From fbf5457bf008e6fe050f85f431ee41ee7f94e14d Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Sat, 8 Jul 2017 23:04:26 +0200 Subject: [PATCH 01/65] Update pom, add properties for version numbers --- pom.xml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 62d3ba2..a6a0da2 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.scorpiac.javarant javarant - 1.3 + 2.0.0-SNAPSHOT jar JavaRant @@ -33,21 +33,27 @@ + + 2.7 + 4.5.1 + 1.7.21 + + com.google.code.gson gson - 2.7 + ${gson.version} org.apache.httpcomponents fluent-hc - 4.5.1 + ${fluent.version} org.slf4j slf4j-simple - 1.7.21 + ${slf4j.version} From 8d9fde83ba63c7bfb04f1ac95d9ec4b9c600211a Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Sat, 8 Jul 2017 23:21:45 +0200 Subject: [PATCH 02/65] Remove GSON, add Jackson, ripped out stuff until it compiled again --- pom.xml | 12 +- src/main/java/com/scorpiac/javarant/Auth.java | 7 - .../java/com/scorpiac/javarant/Collab.java | 51 --- .../java/com/scorpiac/javarant/Comment.java | 16 - .../java/com/scorpiac/javarant/DevRant.java | 341 ------------------ .../java/com/scorpiac/javarant/Image.java | 19 +- src/main/java/com/scorpiac/javarant/News.java | 10 - src/main/java/com/scorpiac/javarant/Rant.java | 45 --- src/main/java/com/scorpiac/javarant/User.java | 58 +-- src/main/java/com/scorpiac/javarant/Util.java | 38 -- 10 files changed, 8 insertions(+), 589 deletions(-) delete mode 100644 src/main/java/com/scorpiac/javarant/Util.java diff --git a/pom.xml b/pom.xml index a6a0da2..8a7db81 100644 --- a/pom.xml +++ b/pom.xml @@ -34,16 +34,16 @@ - 2.7 - 4.5.1 - 1.7.21 + 2.8.9 + 4.5.3 + 1.7.25 - com.google.code.gson - gson - ${gson.version} + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} org.apache.httpcomponents diff --git a/src/main/java/com/scorpiac/javarant/Auth.java b/src/main/java/com/scorpiac/javarant/Auth.java index 90df9c8..119c6e8 100644 --- a/src/main/java/com/scorpiac/javarant/Auth.java +++ b/src/main/java/com/scorpiac/javarant/Auth.java @@ -1,7 +1,5 @@ package com.scorpiac.javarant; -import com.google.gson.JsonObject; - class Auth { private String id; private String key; @@ -13,11 +11,6 @@ class Auth { this.userId = userId; } - static Auth fromJson(JsonObject json) { - JsonObject token = json.get("auth_token").getAsJsonObject(); - return new Auth(token.get("id").getAsString(), token.get("key").getAsString(), token.get("user_id").getAsString()); - } - String getId() { return id; } diff --git a/src/main/java/com/scorpiac/javarant/Collab.java b/src/main/java/com/scorpiac/javarant/Collab.java index 4ec6244..5971a6a 100644 --- a/src/main/java/com/scorpiac/javarant/Collab.java +++ b/src/main/java/com/scorpiac/javarant/Collab.java @@ -1,9 +1,5 @@ package com.scorpiac.javarant; -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.scorpiac.javarant.exceptions.NotACollabException; - import java.util.Collections; public class Collab extends Rant { @@ -21,40 +17,6 @@ protected Collab(DevRant devRant, int id, User user, int upvotes, int downvotes, this.projectType = projectType; } - static Collab fromJson(DevRant devRant, JsonObject json) { - // Check if the rant is also a collab. - if (!json.has("c_type_long")) - throw new NotACollabException(json.get("id").getAsInt()); - - return new Collab( - devRant, - json.get("id").getAsInt(), - User.fromJson(devRant, json), - json.get("num_upvotes").getAsInt(), - json.get("num_downvotes").getAsInt(), - json.get("score").getAsInt(), - json.get("vote_state").getAsInt(), - json.get("c_type_long").getAsString(), - json.get("text").getAsString(), - json.get("num_comments").getAsInt() - ); - } - - static Collab fromJson(DevRant devRant, JsonObject collab, JsonArray comments) { - Collab result = fromJson(devRant, collab); - result.setData(collab, comments); - result.fetched = true; - return result; - } - - private void setData(JsonObject collab, JsonArray comments) { - commentsFromJson(comments); - description = collab.get("c_description").getAsString(); - techStack = collab.get("c_tech_stack").getAsString(); - teamSize = collab.get("c_team_size").getAsString(); - url = collab.get("c_url").getAsString(); - } - /** * Fetch the data for this collab. If the data is already fetched, it will not be fetched again. * @@ -71,19 +33,6 @@ public boolean fetchData() { * @return Whether the data was fetched successfully. */ public boolean fetchData(boolean force) { - // Check if we already fetched and force is false. - if (fetched && !force) - return true; - - JsonObject json = devRant.get(DevRant.API_RANTS + '/' + getId()); - - // Check for success. - if (!Util.jsonSuccess(json)) - return false; - fetched = true; - - setData(json.get("rant").getAsJsonObject(), json.get("comments").getAsJsonArray()); - return true; } diff --git a/src/main/java/com/scorpiac/javarant/Comment.java b/src/main/java/com/scorpiac/javarant/Comment.java index efabe6b..d065941 100644 --- a/src/main/java/com/scorpiac/javarant/Comment.java +++ b/src/main/java/com/scorpiac/javarant/Comment.java @@ -1,26 +1,10 @@ package com.scorpiac.javarant; -import com.google.gson.JsonObject; - public class Comment extends RantContent { protected Comment(DevRant devRant, int id, User user, int upvotes, int downvotes, int score, int voteState, String content, Image image) { super(devRant, id, user, upvotes, downvotes, score, voteState, content, image); } - static Comment fromJson(DevRant devRant, JsonObject json) { - return new Comment( - devRant, - json.get("id").getAsInt(), - User.fromJson(devRant, json), - json.get("num_upvotes").getAsInt(), - json.get("num_downvotes").getAsInt(), - json.get("score").getAsInt(), - json.get("vote_state").getAsInt(), - json.get("body").getAsString(), - Image.fromJson(json.get("attached_image")) - ); - } - @Override public boolean equals(Object obj) { return super.equals(obj) && obj instanceof Comment; diff --git a/src/main/java/com/scorpiac/javarant/DevRant.java b/src/main/java/com/scorpiac/javarant/DevRant.java index 3c8a73a..765ae62 100644 --- a/src/main/java/com/scorpiac/javarant/DevRant.java +++ b/src/main/java/com/scorpiac/javarant/DevRant.java @@ -1,26 +1,13 @@ package com.scorpiac.javarant; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.google.gson.stream.JsonReader; -import com.scorpiac.javarant.exceptions.AuthenticationException; -import com.scorpiac.javarant.exceptions.NoSuchRantException; -import com.scorpiac.javarant.exceptions.NoSuchUserException; import org.apache.http.NameValuePair; -import org.apache.http.client.fluent.Request; import org.apache.http.message.BasicNameValuePair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.function.Function; import java.util.stream.Collectors; public class DevRant { @@ -59,32 +46,6 @@ public class DevRant { private News news; private int weeklyRantNumber = -1; - /** - * Log in to devRant. - * - * @param username The username. - * @param password The password. - * @throws AuthenticationException If the login data is invalid. - */ - public void login(String username, char[] password) throws AuthenticationException { - if (auth != null) - throw new IllegalStateException("A user is already logged in."); - - JsonObject json = post(API_AUTH_TOKEN, - new BasicNameValuePair("username", username), - new BasicNameValuePair("password", String.valueOf(password)) - ); - - // Clear the password. - for (int i = 0; i < password.length; i++) - password[i] = 0; - - if (!Util.jsonSuccess(json)) - throw new AuthenticationException(); - - auth = Auth.fromJson(json); - } - /** * Log out of devRant. */ @@ -101,268 +62,6 @@ public boolean isLoggedIn() { return auth != null; } - /** - * Get a list of rants. - * - * @param sort The sorting method. - * @param limit How many rants to get. - * @param skip How many rants to skip. - * @return An list of rants. - */ - public List getRants(Sort sort, int limit, int skip) { - return getFeed(API_RANTS, elem -> Rant.fromJson(this, elem), sort, limit, skip); - } - - /** - * Search for rants matching a certain term. - * - * @param term The term to search for. - * @return A list of rants matching the search term. - */ - public List search(String term) { - return getFeed(API_SEARCH, elem -> Rant.fromJson(this, elem), new BasicNameValuePair("term", term)); - } - - /** - * Get a random rant with at least 15 ++'s. - * - * @return A random rant. - */ - public Rant getSurprise() { - JsonObject json = get(API_SURPRISE); - return Util.jsonSuccess(json) ? Rant.fromJson(this, json.get("rant").getAsJsonObject()) : null; - } - - /** - * Get the weekly rants. - * - * @param sort The sorting method. - * @param limit How many rants to get. - * @param skip How many rants to skip. - * @return The weekly rants. - */ - public List getWeekly(Sort sort, int limit, int skip) { - return getFeed(API_WEEKLY, elem -> Rant.fromJson(this, elem), sort, limit, skip); - } - - /** - * Get the collabs. - * - * @param limit How many rants to get. - * @param skip How many rants to skip. - * @return A list of collabs. - */ - public List getCollabs(int limit, int skip) { - return getFeed(API_COLLABS, elem -> Collab.fromJson(this, elem.getAsJsonObject()), - new BasicNameValuePair("limit", String.valueOf(limit)), - new BasicNameValuePair("skip", String.valueOf(skip)) - ); - } - - /** - * Get the story rants. - * - * @param sort The sorting method. - * @param limit How many rants to get. - * @param skip How many rants to skip. - * @return A list of story rants. - */ - public List getStories(Sort sort, int limit, int skip) { - return getFeed(API_STORIES, elem -> Rant.fromJson(this, elem), sort, limit, skip); - } - - private List getFeed(String url, Function converter, Sort sort, int limit, int skip) { - return getFeed(url, converter, - new BasicNameValuePair("sort", sort.toString()), - new BasicNameValuePair("limit", String.valueOf(limit)), - new BasicNameValuePair("skip", String.valueOf(skip)), - sort.getParameter() - ); - } - - private List getFeed(String url, Function converter, NameValuePair... params) { - JsonObject json = get(url, params); - return Util.jsonSuccess(json) ? Util.jsonToList(json.get("rants").getAsJsonArray(), obj -> converter.apply(obj.getAsJsonObject())) : null; - } - - /** - * Get a rant by its id. - * - * @param id The id of the rant to get. - * @return The rant. - */ - public Rant getRant(int id) { - JsonObject json = get(API_RANTS + '/' + id); - - // Check if the rant exists. - if (!Util.jsonSuccess(json)) - throw new NoSuchRantException(id); - - return Rant.fromJson(this, json.get("rant").getAsJsonObject(), json.get("comments").getAsJsonArray()); - } - - /** - * Get a collab by its id. - * - * @param id The id of the collab to get. - * @return The collab. - */ - public Collab getCollab(int id) { - JsonObject json = get(API_RANTS + '/' + id); - - // Check if the collab exists. - if (!Util.jsonSuccess(json)) - throw new NoSuchRantException(id); - - return Collab.fromJson(this, json.get("rant").getAsJsonObject(), json.get("comments").getAsJsonArray()); - } - - /** - * Get a user by their username. - * - * @param username The username of the user to get. - * @return The user. - */ - public User getUser(String username) { - JsonObject json = get(API_USER_ID, new BasicNameValuePair("username", username)); - - // Check if the user exists. - if (!Util.jsonSuccess(json)) - throw new NoSuchUserException(username); - - return getUser(json.get("user_id").getAsInt()); - } - - /** - * Get a user by their id. - * - * @param id The id of the user to get. - * @return The user. - */ - public User getUser(int id) { - return new User(this, id); - } - - /** - * Vote on a rant. - * - * @param rant The rant to vote on. - * @param vote The vote. - * @return Whether the vote was successful. - */ - public boolean vote(Rant rant, Vote vote) { - return voteRant(rant.getId(), vote); - } - - /** - * Vote on a rant. - * - * @param id The id of the rant. - * @param vote The vote. - * @return Whether the vote was successful. - */ - public boolean voteRant(int id, Vote vote) { - // Rants url, id, vote url. - String url = String.format("%1$s/%2$d%3$s", API_RANTS, id, API_VOTE); - return Util.jsonSuccess(post(url, new BasicNameValuePair("vote", vote.toString()), vote.getParameter())); - } - - /** - * Vote on a comment. - * - * @param comment The comment to vote on. - * @param vote The vote. - * @return Whether the vote was successful. - */ - public boolean vote(Comment comment, Vote vote) { - return voteComment(comment.getId(), vote); - } - - /** - * Vote on a comment. - * - * @param id The id of the comment. - * @param vote The vote. - * @return Whether the vote was successful. - */ - public boolean voteComment(int id, Vote vote) { - // API url, comments url, id, vote url. - String url = String.format("%1$s%2$s/%3$d%4$s", API, API_COMMENT, id, API_VOTE); - return Util.jsonSuccess(post(url, new BasicNameValuePair("vote", String.valueOf(vote.toString())), vote.getParameter())); - } - - /** - * Post a rant. - * - * @param rant The content of the rant. - * @param tags The tags. - * @return Whether posting the rant was successful. - */ - public boolean postRant(String rant, String tags) { - return Util.jsonSuccess(post(API_RANTS, - new BasicNameValuePair("rant", rant), - new BasicNameValuePair("tags", tags) - )); - } - - /** - * Post a comment. - * - * @param rant The rant to post the comment on. - * @param comment The content of the comment. - * @return Whether posting the comment was successful. - */ - public boolean postComment(Rant rant, String comment) { - return postComment(rant.getId(), comment); - } - - /** - * Post a comment. - * - * @param rantId The id of the rant to post the comment on. - * @param comment The content of the comment. - * @return Whether posting the comment was successful. - */ - public boolean postComment(int rantId, String comment) { - // Rants url, rant, comments url. - String url = String.format("%1$s/%2$d%3$s", API_RANTS, rantId, API_COMMENT); - return Util.jsonSuccess(post(url, new BasicNameValuePair("comment", comment))); - } - - /** - * Make a POST-request to the devRant server. - * - * @param url The url to make the request to. - * @param params The parameters to post. - * @return A {@link JsonObject} containing the response. - */ - JsonObject post(String url, NameValuePair... params) { - List paramList = getParameters(params); - return executeRequest(Request.Post(BASE_URL + url).bodyForm(paramList)); - } - - /** - * Make a GET-request to the devRant server. - * - * @param url The url to make the request to. - * @return A {@link JsonObject} containing the response. - */ - JsonObject get(String url, NameValuePair... params) { - StringBuilder finalUrl = new StringBuilder(url).append('?'); - List paramList = getParameters(params); - - // Add all parameters. - try { - for (NameValuePair param : paramList) - finalUrl.append('&').append(param.getName()).append('=').append(URLEncoder.encode(param.getValue(), "UTF-8")); - } catch (UnsupportedEncodingException e) { - // This never happens. - LOGGER.error("Unsupported encoding while trying to encode parameter value.", e); - } - - return executeRequest(Request.Get(BASE_URL + finalUrl.toString())); - } - /** * Get a list with all the parameters, including default and auth parameters. * This also filters out any parameters that are {@code null}. @@ -389,46 +88,6 @@ private List getParameters(NameValuePair... params) { return paramList; } - /** - * Execute a request and parse the response. - * - * @param request The request to execute. - * @return A {@link JsonObject} containing the response. - */ - private JsonObject executeRequest(Request request) { - // Make the request and get the returned content as a stream. - InputStream stream; - try { - stream = request.socketTimeout(timeout).connectTimeout(timeout).execute().returnContent().asStream(); - } catch (IOException e) { - LOGGER.warn("Exception while processing request:\n" + request.toString() + '\n' + e.getMessage()); - return null; - } - - // Parse the response as json. - JsonObject json; - try (JsonReader reader = new JsonReader(new InputStreamReader(stream))) { - json = new JsonParser().parse(reader).getAsJsonObject(); - } catch (IOException e) { - LOGGER.error("Exception while trying to create JsonReader.", e); - return null; - } - - // Save the amount of notifs. - if (json.has("num_notifs")) - numNotifs = json.get("num_notifs").getAsInt(); - - // Save the news. - if (json.has("news")) - news = News.fromJson(json.get("news").getAsJsonObject()); - - // Save the weekly rant number. - if (json.has("wrw")) - weeklyRantNumber = json.get("wrw").getAsInt(); - - return json; - } - /** * Set the request timeout. This timeout will be used for the socket and connection timeout. * diff --git a/src/main/java/com/scorpiac/javarant/Image.java b/src/main/java/com/scorpiac/javarant/Image.java index e7bd06f..00f75fd 100644 --- a/src/main/java/com/scorpiac/javarant/Image.java +++ b/src/main/java/com/scorpiac/javarant/Image.java @@ -1,8 +1,5 @@ package com.scorpiac.javarant; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; - public class Image { private String url; private int width; @@ -14,23 +11,9 @@ private Image(String url, int width, int height) { this.height = height; } - /** - * Get the image from the JSON, or {@code null} if there is no image. - * - * @param json The JSON element for the image. - * @return The image, or {@code null}. - */ - static Image fromJson(JsonElement json) { - if (json == null || !json.isJsonObject()) - return null; - - JsonObject obj = json.getAsJsonObject(); - return new Image(obj.get("url").getAsString(), obj.get("width").getAsInt(), obj.get("height").getAsInt()); - } - @Override public boolean equals(Object obj) { - return obj instanceof Image && ((Image) obj).getUrl().equals(url); + return obj instanceof Image && ((Image)obj).getUrl().equals(url); } @Override diff --git a/src/main/java/com/scorpiac/javarant/News.java b/src/main/java/com/scorpiac/javarant/News.java index dab2f83..5507c1b 100644 --- a/src/main/java/com/scorpiac/javarant/News.java +++ b/src/main/java/com/scorpiac/javarant/News.java @@ -1,7 +1,5 @@ package com.scorpiac.javarant; -import com.google.gson.JsonObject; - public class News { private String headline; private String body; @@ -13,14 +11,6 @@ private News(String headline, String body, String footer) { this.footer = footer; } - static News fromJson(JsonObject json) { - return new News( - json.has("headline") ? json.get("headline").getAsString() : "", - json.has("body") ? json.get("body").getAsString() : "", - json.has("footer") ? json.get("footer").getAsString() : "" - ); - } - @Override public String toString() { return headline + '\n' + body + '\n' + footer; diff --git a/src/main/java/com/scorpiac/javarant/Rant.java b/src/main/java/com/scorpiac/javarant/Rant.java index 11022da..5262706 100644 --- a/src/main/java/com/scorpiac/javarant/Rant.java +++ b/src/main/java/com/scorpiac/javarant/Rant.java @@ -1,9 +1,5 @@ package com.scorpiac.javarant; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; - import java.util.Collections; import java.util.List; @@ -18,37 +14,6 @@ protected Rant(DevRant devRant, int id, User user, int upvotes, int downvotes, i this.commentCount = commentCount; } - static Rant fromJson(DevRant devRant, JsonObject json) { - return new Rant( - devRant, - json.get("id").getAsInt(), - User.fromJson(devRant, json), - json.get("num_upvotes").getAsInt(), - json.get("num_downvotes").getAsInt(), - json.get("score").getAsInt(), - json.get("vote_state").getAsInt(), - json.get("text").getAsString(), - Image.fromJson(json.get("attached_image")), - Util.jsonToList(json.getAsJsonArray("tags"), JsonElement::getAsString), - json.get("num_comments").getAsInt() - ); - } - - static Rant fromJson(DevRant devRant, JsonObject rant, JsonArray comments) { - Rant result = fromJson(devRant, rant); - result.commentsFromJson(comments); - return result; - } - - /** - * Set the comments from a JSON array. - * - * @param commentArray The JSON array to get the comments from. - */ - protected void commentsFromJson(JsonArray commentArray) { - comments = Util.jsonToList(commentArray, elem -> Comment.fromJson(devRant, elem.getAsJsonObject())); - } - /** * Get the comments on this rant. If they are not yet retrieved, this will also fetch them. * @@ -75,16 +40,6 @@ public boolean fetchComments() { * @return Whether the data was fetched successfully. */ public boolean fetchComments(boolean force) { - // Check if we already fetched and force is false. - if (comments != null && !force) - return true; - - JsonObject json = devRant.get(DevRant.API_RANTS + '/' + getId()); - - if (!Util.jsonSuccess(json)) - return false; - - commentsFromJson(json.getAsJsonArray("comments")); return true; } diff --git a/src/main/java/com/scorpiac/javarant/User.java b/src/main/java/com/scorpiac/javarant/User.java index 8c9c54e..d46f866 100644 --- a/src/main/java/com/scorpiac/javarant/User.java +++ b/src/main/java/com/scorpiac/javarant/User.java @@ -1,6 +1,5 @@ package com.scorpiac.javarant; -import com.google.gson.JsonObject; import com.scorpiac.javarant.exceptions.NoSuchUserException; import java.util.Collections; @@ -41,23 +40,6 @@ private User(DevRant devRant) { throw new NoSuchUserException(id); } - /** - * Create a user from a JSON object. - * - * @param devRant The devRant client to use. - * @param json The JSON object to create the user from. - * @return The created user. - */ - static User fromJson(DevRant devRant, JsonObject json) { - User result = new User(devRant); - - result.id = json.get("user_id").getAsInt(); - result.username = json.get("user_username").getAsString(); - result.score = json.get("user_score").getAsInt(); - - return result; - } - /** * Fetch the user data from the user profile. If the data is already fetched, it will not be fetched again. * @@ -74,44 +56,6 @@ public boolean fetchData() { * @return Whether the data was fetched successfully. */ public boolean fetchData(boolean force) { - // Check if we already fetched and force is false. - if (fetched && !force) - return true; - - JsonObject json = devRant.get(DevRant.API_USERS + '/' + id); - - // Check for success. - if (!Util.jsonSuccess(json)) - return false; - fetched = true; - - // JSON objects. - JsonObject profileJson = json.getAsJsonObject("profile"); - JsonObject contentJson = profileJson.getAsJsonObject("content"); - JsonObject subContentJson = contentJson.getAsJsonObject("content"); - JsonObject countsJson = contentJson.getAsJsonObject("counts"); - JsonObject avatarJson = profileJson.getAsJsonObject("avatar"); - - // Set all the fields. - username = profileJson.get("username").getAsString(); - score = profileJson.get("score").getAsInt(); - avatar = avatarJson.get("i").getAsString(); - - about = profileJson.get("about").getAsString(); - location = profileJson.get("location").getAsString(); - skills = profileJson.get("skills").getAsString(); - github = profileJson.get("github").getAsString(); - - rantsCount = countsJson.get("rants").getAsInt(); - upvotedCount = countsJson.get("upvoted").getAsInt(); - commentsCount = countsJson.get("comments").getAsInt(); - favoritesCount = countsJson.get("favorites").getAsInt(); - - rants = Util.jsonToList(subContentJson.get("rants").getAsJsonArray(), rant -> Rant.fromJson(devRant, rant.getAsJsonObject())); - upvoted = Util.jsonToList(subContentJson.get("upvoted").getAsJsonArray(), rant -> Rant.fromJson(devRant, rant.getAsJsonObject())); - comments = Util.jsonToList(subContentJson.get("comments").getAsJsonArray(), comment -> Comment.fromJson(devRant, comment.getAsJsonObject())); - favorites = Util.jsonToList(subContentJson.get("favorites").getAsJsonArray(), rant -> Rant.fromJson(devRant, rant.getAsJsonObject())); - return true; } @@ -139,7 +83,7 @@ public String avatarLink() { @Override public boolean equals(Object obj) { - return obj instanceof User && ((User) obj).getId() == id; + return obj instanceof User && ((User)obj).getId() == id; } @Override diff --git a/src/main/java/com/scorpiac/javarant/Util.java b/src/main/java/com/scorpiac/javarant/Util.java deleted file mode 100644 index d022e60..0000000 --- a/src/main/java/com/scorpiac/javarant/Util.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.scorpiac.javarant; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Function; - -class Util { - private Util() { - } - - /** - * Create a list from a JSON array. - * - * @param json The JSON array to convert to a list. - * @param converter The converter for converting individual JSON elements. - * @param The type to convert the elements to. - * @return A list of elements converted from the JSON. - */ - static List jsonToList(JsonArray json, Function converter) { - List result = new ArrayList<>(json.size()); - json.forEach(j -> result.add(converter.apply(j))); - return result; - } - - /** - * Check whether the JSON is not {@code null} and the success member is true. - * - * @param json The JSON to check. - * @return True if the JSON is not {@code null} and has a success member which is true. - */ - static boolean jsonSuccess(JsonObject json) { - return json != null && json.get("success").getAsBoolean(); - } -} From d1e424d3a7512cd6cdca3ab6d0ac619532442edf Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Sun, 9 Jul 2017 00:07:18 +0200 Subject: [PATCH 03/65] Set up basic injection, request handler --- pom.xml | 6 +++ .../java/com/scorpiac/javarant/DevRant.java | 54 +++++-------------- .../scorpiac/javarant/InjectionModule.java | 10 ++++ .../javarant/services/LogFactory.java | 13 +++++ .../javarant/services/RequestHandler.java | 38 +++++++++++++ 5 files changed, 80 insertions(+), 41 deletions(-) create mode 100644 src/main/java/com/scorpiac/javarant/InjectionModule.java create mode 100644 src/main/java/com/scorpiac/javarant/services/LogFactory.java create mode 100644 src/main/java/com/scorpiac/javarant/services/RequestHandler.java diff --git a/pom.xml b/pom.xml index 8a7db81..63f6ed0 100644 --- a/pom.xml +++ b/pom.xml @@ -37,6 +37,7 @@ 2.8.9 4.5.3 1.7.25 + 4.1.0 @@ -55,6 +56,11 @@ slf4j-simple ${slf4j.version} + + com.google.inject + guice + ${guice.version} + diff --git a/src/main/java/com/scorpiac/javarant/DevRant.java b/src/main/java/com/scorpiac/javarant/DevRant.java index 765ae62..16fd969 100644 --- a/src/main/java/com/scorpiac/javarant/DevRant.java +++ b/src/main/java/com/scorpiac/javarant/DevRant.java @@ -1,23 +1,13 @@ package com.scorpiac.javarant; -import org.apache.http.NameValuePair; -import org.apache.http.message.BasicNameValuePair; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; +import com.google.inject.Guice; +import com.google.inject.Injector; public class DevRant { - private static final Logger LOGGER = LoggerFactory.getLogger(DevRant.class); - - static final String APP_ID = "3"; - static final String PLAT_ID = "3"; + private static final Injector INJECTOR; - static final String BASE_URL = "https://www.devrant.io"; - static final String AVATARS_URL = "https://avatars.devrant.io"; + public static final String BASE_URL = "https://www.devrant.io"; + public static final String AVATARS_URL = "https://avatars.devrant.io"; static final String USER_URL = "/users"; static final String RANT_URL = "/rants"; @@ -46,6 +36,14 @@ public class DevRant { private News news; private int weeklyRantNumber = -1; + static { + INJECTOR = Guice.createInjector(new InjectionModule()); + } + + public DevRant() { + INJECTOR.injectMembers(this); + } + /** * Log out of devRant. */ @@ -62,32 +60,6 @@ public boolean isLoggedIn() { return auth != null; } - /** - * Get a list with all the parameters, including default and auth parameters. - * This also filters out any parameters that are {@code null}. - * - * @param params The parameters to use. - * @return A list containing the given parameters, the default parameters, and the auth parameters. - */ - private List getParameters(NameValuePair... params) { - List paramList = new ArrayList<>(params.length + 6); - paramList.addAll(Arrays.stream(params).filter(p -> p != null).collect(Collectors.toList())); - - // Add the parameters which always need to be present. - paramList.add(new BasicNameValuePair("app", APP_ID)); - paramList.add(new BasicNameValuePair("plat", PLAT_ID)); - paramList.add(new BasicNameValuePair("hide_reposts", hideReposts ? "1" : "0")); - - // Add the auth information. - if (isLoggedIn()) { - paramList.add(new BasicNameValuePair("token_id", auth.getId())); - paramList.add(new BasicNameValuePair("token_key", auth.getKey())); - paramList.add(new BasicNameValuePair("user_id", auth.getUserId())); - } - - return paramList; - } - /** * Set the request timeout. This timeout will be used for the socket and connection timeout. * diff --git a/src/main/java/com/scorpiac/javarant/InjectionModule.java b/src/main/java/com/scorpiac/javarant/InjectionModule.java new file mode 100644 index 0000000..c2fb17e --- /dev/null +++ b/src/main/java/com/scorpiac/javarant/InjectionModule.java @@ -0,0 +1,10 @@ +package com.scorpiac.javarant; + +import com.google.inject.AbstractModule; + +class InjectionModule extends AbstractModule { + @Override + protected void configure() { + + } +} diff --git a/src/main/java/com/scorpiac/javarant/services/LogFactory.java b/src/main/java/com/scorpiac/javarant/services/LogFactory.java new file mode 100644 index 0000000..8099811 --- /dev/null +++ b/src/main/java/com/scorpiac/javarant/services/LogFactory.java @@ -0,0 +1,13 @@ +package com.scorpiac.javarant.services; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class LogFactory { + private LogFactory() { + } + + public static Logger getLog() { + return LoggerFactory.getLogger(Thread.currentThread().getStackTrace()[2].getClassName()); + } +} diff --git a/src/main/java/com/scorpiac/javarant/services/RequestHandler.java b/src/main/java/com/scorpiac/javarant/services/RequestHandler.java new file mode 100644 index 0000000..f0e0e40 --- /dev/null +++ b/src/main/java/com/scorpiac/javarant/services/RequestHandler.java @@ -0,0 +1,38 @@ +package com.scorpiac.javarant.services; + +import org.apache.http.NameValuePair; +import org.apache.http.message.BasicNameValuePair; +import org.slf4j.Logger; + +import javax.inject.Singleton; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +@Singleton +public class RequestHandler { + private static final Logger LOGGER = LogFactory.getLog(); + + private static final String APP_ID = "3"; + private static final String PLAT_ID = "3"; + + /** + * Get a list with all the parameters, including default and auth parameters. + * This also filters out any parameters that are {@code null}. + * + * @param params The parameters to use. + * @return A list containing the given parameters, and the default parameters. + */ + private List getParameters(NameValuePair... params) { + List paramList = new ArrayList<>(params.length + 6); + paramList.addAll(Arrays.stream(params).filter(Objects::nonNull).collect(Collectors.toList())); + + // Add the parameters which always need to be present. + paramList.add(new BasicNameValuePair("app", APP_ID)); + paramList.add(new BasicNameValuePair("plat", PLAT_ID)); + + return paramList; + } +} From 9f930ccbb02e343916fb16c074cb2b22974d48ff Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Sun, 9 Jul 2017 00:29:52 +0200 Subject: [PATCH 04/65] Add ObjectMapperService, use URIBuilder, start on request handler --- .../java/com/scorpiac/javarant/Collab.java | 7 +++-- .../java/com/scorpiac/javarant/DevRant.java | 3 -- src/main/java/com/scorpiac/javarant/Rant.java | 7 +++-- src/main/java/com/scorpiac/javarant/User.java | 10 ++++--- .../services/ObjectMapperService.java | 14 +++++++++ .../javarant/services/RequestHandler.java | 29 +++++++++++++++++++ 6 files changed, 59 insertions(+), 11 deletions(-) create mode 100644 src/main/java/com/scorpiac/javarant/services/ObjectMapperService.java diff --git a/src/main/java/com/scorpiac/javarant/Collab.java b/src/main/java/com/scorpiac/javarant/Collab.java index 5971a6a..20e40af 100644 --- a/src/main/java/com/scorpiac/javarant/Collab.java +++ b/src/main/java/com/scorpiac/javarant/Collab.java @@ -1,5 +1,8 @@ package com.scorpiac.javarant; +import com.scorpiac.javarant.services.RequestHandler; + +import java.net.URI; import java.util.Collections; public class Collab extends Rant { @@ -40,8 +43,8 @@ public boolean fetchData(boolean force) { * Get the link to the collab. */ @Override - public String link() { - return DevRant.BASE_URL + DevRant.COLLAB_URL + '/' + getId(); + public URI link() { + return RequestHandler.BASE_URI.resolve(DevRant.COLLAB_URL).resolve(String.valueOf(getId())); } /** diff --git a/src/main/java/com/scorpiac/javarant/DevRant.java b/src/main/java/com/scorpiac/javarant/DevRant.java index 16fd969..5a9e954 100644 --- a/src/main/java/com/scorpiac/javarant/DevRant.java +++ b/src/main/java/com/scorpiac/javarant/DevRant.java @@ -6,9 +6,6 @@ public class DevRant { private static final Injector INJECTOR; - public static final String BASE_URL = "https://www.devrant.io"; - public static final String AVATARS_URL = "https://avatars.devrant.io"; - static final String USER_URL = "/users"; static final String RANT_URL = "/rants"; static final String COLLAB_URL = "/collabs"; diff --git a/src/main/java/com/scorpiac/javarant/Rant.java b/src/main/java/com/scorpiac/javarant/Rant.java index 5262706..5ccc63b 100644 --- a/src/main/java/com/scorpiac/javarant/Rant.java +++ b/src/main/java/com/scorpiac/javarant/Rant.java @@ -1,5 +1,8 @@ package com.scorpiac.javarant; +import com.scorpiac.javarant.services.RequestHandler; + +import java.net.URI; import java.util.Collections; import java.util.List; @@ -46,8 +49,8 @@ public boolean fetchComments(boolean force) { /** * Get the link to the rant. */ - public String link() { - return DevRant.BASE_URL + DevRant.RANT_URL + '/' + getId(); + public URI link() { + return RequestHandler.BASE_URI.resolve(DevRant.RANT_URL).resolve(String.valueOf(getId())); } @Override diff --git a/src/main/java/com/scorpiac/javarant/User.java b/src/main/java/com/scorpiac/javarant/User.java index d46f866..2683f03 100644 --- a/src/main/java/com/scorpiac/javarant/User.java +++ b/src/main/java/com/scorpiac/javarant/User.java @@ -1,7 +1,9 @@ package com.scorpiac.javarant; import com.scorpiac.javarant.exceptions.NoSuchUserException; +import com.scorpiac.javarant.services.RequestHandler; +import java.net.URI; import java.util.Collections; import java.util.List; @@ -69,16 +71,16 @@ public boolean isFetched() { /** * Get the link to the user. */ - public String userLink() { - return DevRant.BASE_URL + DevRant.USER_URL + '/' + username; + public URI userLink() { + return RequestHandler.BASE_URI.resolve(DevRant.USER_URL).resolve(username); } /** * Get the link to the user's avatar. */ - public String avatarLink() { + public URI avatarLink() { fetchData(); - return DevRant.AVATARS_URL + "/" + avatar; + return RequestHandler.AVATARS_URI.resolve(avatar); } @Override diff --git a/src/main/java/com/scorpiac/javarant/services/ObjectMapperService.java b/src/main/java/com/scorpiac/javarant/services/ObjectMapperService.java new file mode 100644 index 0000000..3fb1b77 --- /dev/null +++ b/src/main/java/com/scorpiac/javarant/services/ObjectMapperService.java @@ -0,0 +1,14 @@ +package com.scorpiac.javarant.services; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import javax.inject.Singleton; + +@Singleton +public class ObjectMapperService { + private static final ObjectMapper MAPPER = new ObjectMapper(); + + public ObjectMapper getMapper() { + return MAPPER; + } +} diff --git a/src/main/java/com/scorpiac/javarant/services/RequestHandler.java b/src/main/java/com/scorpiac/javarant/services/RequestHandler.java index f0e0e40..5bdd8cc 100644 --- a/src/main/java/com/scorpiac/javarant/services/RequestHandler.java +++ b/src/main/java/com/scorpiac/javarant/services/RequestHandler.java @@ -1,10 +1,15 @@ package com.scorpiac.javarant.services; +import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; +import org.apache.http.client.fluent.Request; +import org.apache.http.client.utils.URIBuilder; import org.apache.http.message.BasicNameValuePair; import org.slf4j.Logger; import javax.inject.Singleton; +import java.net.URI; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -15,9 +20,33 @@ public class RequestHandler { private static final Logger LOGGER = LogFactory.getLog(); + public static final URI BASE_URI = URI.create("https://www.devrant.io"); + public static final URI AVATARS_URI = URI.create("https://avatars.devrant.io"); + private static final String APP_ID = "3"; private static final String PLAT_ID = "3"; + public T get(String uri, Class resultClass) { + handleRequest(Request.Get(buildUri(uri))); + return null; + } + + private URI buildUri(String uri, NameValuePair... params) { + try { + return new URIBuilder(BASE_URI.resolve(uri)) + .addParameters(getParameters(params)) + .build(); + } catch (URISyntaxException e) { + // This never happens. + LOGGER.error("Could not build URI.", e); + return null; + } + } + + private HttpResponse handleRequest(Request request) { + return null; + } + /** * Get a list with all the parameters, including default and auth parameters. * This also filters out any parameters that are {@code null}. From b9e0b95b6d9eab79df83a49d10f9d72859ce9af8 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Sun, 9 Jul 2017 00:39:17 +0200 Subject: [PATCH 05/65] Improve request handler with object mapper response handler --- .../ObjectMapperResponseHandlerFactory.java | 27 +++++++++++++++++ .../services/ObjectMapperService.java | 3 ++ .../javarant/services/RequestHandler.java | 30 +++++++++++++++---- 3 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/scorpiac/javarant/services/ObjectMapperResponseHandlerFactory.java diff --git a/src/main/java/com/scorpiac/javarant/services/ObjectMapperResponseHandlerFactory.java b/src/main/java/com/scorpiac/javarant/services/ObjectMapperResponseHandlerFactory.java new file mode 100644 index 0000000..09f06cd --- /dev/null +++ b/src/main/java/com/scorpiac/javarant/services/ObjectMapperResponseHandlerFactory.java @@ -0,0 +1,27 @@ +package com.scorpiac.javarant.services; + +import org.apache.http.client.ResponseHandler; + +import javax.inject.Inject; +import javax.inject.Singleton; + +@Singleton +public class ObjectMapperResponseHandlerFactory { + private final ObjectMapperService mapperService; + + @Inject + public ObjectMapperResponseHandlerFactory(ObjectMapperService mapperService) { + this.mapperService = mapperService; + } + + /** + * Get a response handler which maps the response to a class. + * + * @param clazz The class to map the response to. + * @param The type of the class to map the response to. + * @return A response handler. + */ + public ResponseHandler getResponseHandler(Class clazz) { + return response -> mapperService.getMapper().readValue(response.getEntity().getContent(), clazz); + } +} diff --git a/src/main/java/com/scorpiac/javarant/services/ObjectMapperService.java b/src/main/java/com/scorpiac/javarant/services/ObjectMapperService.java index 3fb1b77..15d9faa 100644 --- a/src/main/java/com/scorpiac/javarant/services/ObjectMapperService.java +++ b/src/main/java/com/scorpiac/javarant/services/ObjectMapperService.java @@ -8,6 +8,9 @@ public class ObjectMapperService { private static final ObjectMapper MAPPER = new ObjectMapper(); + /** + * Get the object mapper. + */ public ObjectMapper getMapper() { return MAPPER; } diff --git a/src/main/java/com/scorpiac/javarant/services/RequestHandler.java b/src/main/java/com/scorpiac/javarant/services/RequestHandler.java index 5bdd8cc..3bfc152 100644 --- a/src/main/java/com/scorpiac/javarant/services/RequestHandler.java +++ b/src/main/java/com/scorpiac/javarant/services/RequestHandler.java @@ -1,13 +1,14 @@ package com.scorpiac.javarant.services; -import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.fluent.Request; import org.apache.http.client.utils.URIBuilder; import org.apache.http.message.BasicNameValuePair; import org.slf4j.Logger; +import javax.inject.Inject; import javax.inject.Singleton; +import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; @@ -26,11 +27,25 @@ public class RequestHandler { private static final String APP_ID = "3"; private static final String PLAT_ID = "3"; - public T get(String uri, Class resultClass) { - handleRequest(Request.Get(buildUri(uri))); + private final ObjectMapperResponseHandlerFactory responseHandlerFactory; + + @Inject + public RequestHandler(ObjectMapperResponseHandlerFactory responseHandlerFactory) { + this.responseHandlerFactory = responseHandlerFactory; + } + + public T get(String uri, Class clazz) { + handleRequest(Request.Get(buildUri(uri)), clazz); return null; } + /** + * Build a URI from the given relative URI and parameters. + * + * @param uri The relative URI to use. + * @param params The parameters to use. + * @return A complete URI. + */ private URI buildUri(String uri, NameValuePair... params) { try { return new URIBuilder(BASE_URI.resolve(uri)) @@ -39,11 +54,16 @@ private URI buildUri(String uri, NameValuePair... params) { } catch (URISyntaxException e) { // This never happens. LOGGER.error("Could not build URI.", e); - return null; } + return null; } - private HttpResponse handleRequest(Request request) { + private T handleRequest(Request request, Class clazz) { + try { + request.execute().handleResponse(responseHandlerFactory.getResponseHandler(clazz)); + } catch (IOException e) { + LOGGER.error("Failed to execute request.", e); + } return null; } From f1e360bf890675ce9fef1e8ff0545df1503ad5c2 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Sun, 9 Jul 2017 00:41:47 +0200 Subject: [PATCH 06/65] Remove DevRantHolder class --- src/main/java/com/scorpiac/javarant/Collab.java | 4 ++-- src/main/java/com/scorpiac/javarant/Comment.java | 4 ++-- src/main/java/com/scorpiac/javarant/DevRantHolder.java | 9 --------- src/main/java/com/scorpiac/javarant/Rant.java | 4 ++-- src/main/java/com/scorpiac/javarant/RantContent.java | 7 +++---- src/main/java/com/scorpiac/javarant/User.java | 9 ++------- 6 files changed, 11 insertions(+), 26 deletions(-) delete mode 100644 src/main/java/com/scorpiac/javarant/DevRantHolder.java diff --git a/src/main/java/com/scorpiac/javarant/Collab.java b/src/main/java/com/scorpiac/javarant/Collab.java index 20e40af..841f23e 100644 --- a/src/main/java/com/scorpiac/javarant/Collab.java +++ b/src/main/java/com/scorpiac/javarant/Collab.java @@ -15,8 +15,8 @@ public class Collab extends Rant { private String teamSize; private String url; - protected Collab(DevRant devRant, int id, User user, int upvotes, int downvotes, int score, int voteState, String projectType, String summary, int commentCount) { - super(devRant, id, user, upvotes, downvotes, score, voteState, summary, null, Collections.emptyList(), commentCount); + protected Collab(int id, User user, int upvotes, int downvotes, int score, int voteState, String projectType, String summary, int commentCount) { + super(id, user, upvotes, downvotes, score, voteState, summary, null, Collections.emptyList(), commentCount); this.projectType = projectType; } diff --git a/src/main/java/com/scorpiac/javarant/Comment.java b/src/main/java/com/scorpiac/javarant/Comment.java index d065941..790455a 100644 --- a/src/main/java/com/scorpiac/javarant/Comment.java +++ b/src/main/java/com/scorpiac/javarant/Comment.java @@ -1,8 +1,8 @@ package com.scorpiac.javarant; public class Comment extends RantContent { - protected Comment(DevRant devRant, int id, User user, int upvotes, int downvotes, int score, int voteState, String content, Image image) { - super(devRant, id, user, upvotes, downvotes, score, voteState, content, image); + protected Comment(int id, User user, int upvotes, int downvotes, int score, int voteState, String content, Image image) { + super(id, user, upvotes, downvotes, score, voteState, content, image); } @Override diff --git a/src/main/java/com/scorpiac/javarant/DevRantHolder.java b/src/main/java/com/scorpiac/javarant/DevRantHolder.java deleted file mode 100644 index d5c7848..0000000 --- a/src/main/java/com/scorpiac/javarant/DevRantHolder.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.scorpiac.javarant; - -abstract class DevRantHolder { - protected DevRant devRant; - - DevRantHolder(DevRant devRant) { - this.devRant = devRant; - } -} diff --git a/src/main/java/com/scorpiac/javarant/Rant.java b/src/main/java/com/scorpiac/javarant/Rant.java index 5ccc63b..42d735f 100644 --- a/src/main/java/com/scorpiac/javarant/Rant.java +++ b/src/main/java/com/scorpiac/javarant/Rant.java @@ -11,8 +11,8 @@ public class Rant extends RantContent { private int commentCount; private List comments; - protected Rant(DevRant devRant, int id, User user, int upvotes, int downvotes, int score, int voteState, String text, Image image, List tags, int commentCount) { - super(devRant, id, user, upvotes, downvotes, score, voteState, text, image); + protected Rant(int id, User user, int upvotes, int downvotes, int score, int voteState, String text, Image image, List tags, int commentCount) { + super(id, user, upvotes, downvotes, score, voteState, text, image); this.tags = tags; this.commentCount = commentCount; } diff --git a/src/main/java/com/scorpiac/javarant/RantContent.java b/src/main/java/com/scorpiac/javarant/RantContent.java index fdf7b74..2820a46 100644 --- a/src/main/java/com/scorpiac/javarant/RantContent.java +++ b/src/main/java/com/scorpiac/javarant/RantContent.java @@ -1,6 +1,6 @@ package com.scorpiac.javarant; -public abstract class RantContent extends DevRantHolder { +public abstract class RantContent { private final int id; private final User user; private int upvotes; @@ -10,8 +10,7 @@ public abstract class RantContent extends DevRantHolder { private String content; private Image image; - protected RantContent(DevRant devRant, int id, User user, int upvotes, int downvotes, int score, int voteState, String content, Image image) { - super(devRant); + protected RantContent(int id, User user, int upvotes, int downvotes, int score, int voteState, String content, Image image) { this.id = id; this.user = user; this.upvotes = upvotes; @@ -29,7 +28,7 @@ public int hashCode() { @Override public boolean equals(Object obj) { - return obj instanceof RantContent && ((RantContent) obj).getId() == id; + return obj instanceof RantContent && ((RantContent)obj).getId() == id; } /** diff --git a/src/main/java/com/scorpiac/javarant/User.java b/src/main/java/com/scorpiac/javarant/User.java index 2683f03..cfd2861 100644 --- a/src/main/java/com/scorpiac/javarant/User.java +++ b/src/main/java/com/scorpiac/javarant/User.java @@ -7,7 +7,7 @@ import java.util.Collections; import java.util.List; -public class User extends DevRantHolder { +public class User { // Data that is always available. private int id; private String username; @@ -29,12 +29,7 @@ public class User extends DevRantHolder { private int favoritesCount; private String avatar; - private User(DevRant devRant) { - super(devRant); - } - - User(DevRant devRant, int id) { - super(devRant); + User(int id) { this.id = id; // Fetch the data, check if the user exists. From 5d781d4c1be901e3410c02817f8c26c3fc7ee5a6 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Sun, 9 Jul 2017 00:59:02 +0200 Subject: [PATCH 07/65] Use optionals in request handler --- .../java/com/scorpiac/javarant/DevRant.java | 17 ------- .../javarant/services/RequestHandler.java | 50 +++++++++++++------ 2 files changed, 36 insertions(+), 31 deletions(-) diff --git a/src/main/java/com/scorpiac/javarant/DevRant.java b/src/main/java/com/scorpiac/javarant/DevRant.java index 5a9e954..d9a037a 100644 --- a/src/main/java/com/scorpiac/javarant/DevRant.java +++ b/src/main/java/com/scorpiac/javarant/DevRant.java @@ -27,7 +27,6 @@ public class DevRant { static final String API_NOTIFS = API_USERS + "/me/notif-feed"; private Auth auth; - private int timeout = 15000; private boolean hideReposts = false; private int numNotifs; private News news; @@ -57,22 +56,6 @@ public boolean isLoggedIn() { return auth != null; } - /** - * Set the request timeout. This timeout will be used for the socket and connection timeout. - * - * @param timeout The timeout in milliseconds to set, or -1 to set no timeout. - */ - public void setRequestTimeout(int timeout) { - this.timeout = timeout; - } - - /** - * Get the current request timeout in milliseconds. - */ - public int getRequestTimeout() { - return timeout; - } - /** * Set whether to hide reposts. * diff --git a/src/main/java/com/scorpiac/javarant/services/RequestHandler.java b/src/main/java/com/scorpiac/javarant/services/RequestHandler.java index 3bfc152..6ca8674 100644 --- a/src/main/java/com/scorpiac/javarant/services/RequestHandler.java +++ b/src/main/java/com/scorpiac/javarant/services/RequestHandler.java @@ -11,10 +11,7 @@ import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.stream.Collectors; @Singleton @@ -29,14 +26,19 @@ public class RequestHandler { private final ObjectMapperResponseHandlerFactory responseHandlerFactory; + private int timeout = 15000; + @Inject public RequestHandler(ObjectMapperResponseHandlerFactory responseHandlerFactory) { this.responseHandlerFactory = responseHandlerFactory; } - public T get(String uri, Class clazz) { - handleRequest(Request.Get(buildUri(uri)), clazz); - return null; + public Optional get(String uri, Class clazz) { + return buildUri(uri).flatMap(u -> handleRequest(Request.Get(u), clazz)); + } + + public Optional post(String uri, Class clazz) { + return buildUri(uri).flatMap(u -> handleRequest(Request.Post(u), clazz)); } /** @@ -46,25 +48,29 @@ public T get(String uri, Class clazz) { * @param params The parameters to use. * @return A complete URI. */ - private URI buildUri(String uri, NameValuePair... params) { + private Optional buildUri(String uri, NameValuePair... params) { try { - return new URIBuilder(BASE_URI.resolve(uri)) + return Optional.of(new URIBuilder(BASE_URI.resolve(uri)) .addParameters(getParameters(params)) - .build(); + .build() + ); } catch (URISyntaxException e) { // This never happens. LOGGER.error("Could not build URI.", e); } - return null; + return Optional.empty(); } - private T handleRequest(Request request, Class clazz) { + private Optional handleRequest(Request request, Class clazz) { + request.socketTimeout(timeout).connectTimeout(timeout); + + // Execute the request and handle the response. try { - request.execute().handleResponse(responseHandlerFactory.getResponseHandler(clazz)); + return Optional.of(request.execute().handleResponse(responseHandlerFactory.getResponseHandler(clazz))); } catch (IOException e) { LOGGER.error("Failed to execute request.", e); } - return null; + return Optional.empty(); } /** @@ -84,4 +90,20 @@ private List getParameters(NameValuePair... params) { return paramList; } + + /** + * Set the request timeout. This timeout will be used for the socket and connection timeout. + * + * @param timeout The timeout in milliseconds to set, or -1 to set no timeout. + */ + public void setRequestTimeout(int timeout) { + this.timeout = timeout; + } + + /** + * Get the current request timeout in milliseconds. + */ + public int getRequestTimeout() { + return timeout; + } } From 2cfa3e683a1d4a5184285f53bdf2f87e6aff2e17 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Tue, 11 Jul 2017 13:21:29 +0200 Subject: [PATCH 08/65] Improve RequestHandler structure --- .../javarant/services/RequestHandler.java | 44 ++++++++++++------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/scorpiac/javarant/services/RequestHandler.java b/src/main/java/com/scorpiac/javarant/services/RequestHandler.java index 6ca8674..7a007e5 100644 --- a/src/main/java/com/scorpiac/javarant/services/RequestHandler.java +++ b/src/main/java/com/scorpiac/javarant/services/RequestHandler.java @@ -12,6 +12,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.*; +import java.util.function.Function; import java.util.stream.Collectors; @Singleton @@ -33,37 +34,50 @@ public RequestHandler(ObjectMapperResponseHandlerFactory responseHandlerFactory) this.responseHandlerFactory = responseHandlerFactory; } - public Optional get(String uri, Class clazz) { - return buildUri(uri).flatMap(u -> handleRequest(Request.Get(u), clazz)); + public Optional get(String endpoint, Class clazz) { + return handleRequest(buildRequest(endpoint, Request::Get), clazz); } - public Optional post(String uri, Class clazz) { - return buildUri(uri).flatMap(u -> handleRequest(Request.Post(u), clazz)); + public Optional post(String endpoint, Class clazz) { + return handleRequest(buildRequest(endpoint, Request::Post), clazz); } /** - * Build a URI from the given relative URI and parameters. + * Build a request. * - * @param uri The relative URI to use. - * @param params The parameters to use. - * @return A complete URI. + * @param endpoint The endpoint to make the request to. + * @param requestFunction The function to create a new request from a URI. + * @param params The request parameters. + * @return A request. */ - private Optional buildUri(String uri, NameValuePair... params) { + private Request buildRequest(String endpoint, Function requestFunction, NameValuePair... params) { + URI uri; + try { - return Optional.of(new URIBuilder(BASE_URI.resolve(uri)) + // Build the URI. + uri = new URIBuilder(BASE_URI.resolve(endpoint)) .addParameters(getParameters(params)) - .build() - ); + .build(); } catch (URISyntaxException e) { // This never happens. LOGGER.error("Could not build URI.", e); + throw new IllegalArgumentException("Could not build URI.", e); } - return Optional.empty(); + + return requestFunction.apply(uri) + .connectTimeout(timeout) + .socketTimeout(timeout); } + /** + * Handle a request. + * + * @param request The request to handle. + * @param clazz The class to map the response to. + * @param The type of the class to map the response to. + * @return The mapped response. + */ private Optional handleRequest(Request request, Class clazz) { - request.socketTimeout(timeout).connectTimeout(timeout); - // Execute the request and handle the response. try { return Optional.of(request.execute().handleResponse(responseHandlerFactory.getResponseHandler(clazz))); From 0284f4f0ae4db223a42e303cd40806a10a641ed8 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Tue, 11 Jul 2017 14:13:28 +0200 Subject: [PATCH 09/65] Start on object mapping --- .../java/com/scorpiac/javarant/Collab.java | 6 -- .../java/com/scorpiac/javarant/Comment.java | 4 -- .../java/com/scorpiac/javarant/DevRant.java | 64 ++----------------- .../com/scorpiac/javarant/DevRantFeed.java | 24 +++++++ src/main/java/com/scorpiac/javarant/Rant.java | 6 -- .../com/scorpiac/javarant/RantContent.java | 25 ++------ .../javarant/responses/RantsResponse.java | 14 ++++ .../scorpiac/javarant/responses/Response.java | 12 ++++ .../ObjectMapperResponseHandlerFactory.java | 8 ++- .../services/ObjectMapperService.java | 7 ++ .../javarant/services/RequestHandler.java | 8 +-- 11 files changed, 82 insertions(+), 96 deletions(-) create mode 100644 src/main/java/com/scorpiac/javarant/DevRantFeed.java create mode 100644 src/main/java/com/scorpiac/javarant/responses/RantsResponse.java create mode 100644 src/main/java/com/scorpiac/javarant/responses/Response.java diff --git a/src/main/java/com/scorpiac/javarant/Collab.java b/src/main/java/com/scorpiac/javarant/Collab.java index 841f23e..c71a83a 100644 --- a/src/main/java/com/scorpiac/javarant/Collab.java +++ b/src/main/java/com/scorpiac/javarant/Collab.java @@ -3,7 +3,6 @@ import com.scorpiac.javarant.services.RequestHandler; import java.net.URI; -import java.util.Collections; public class Collab extends Rant { private String projectType; @@ -15,11 +14,6 @@ public class Collab extends Rant { private String teamSize; private String url; - protected Collab(int id, User user, int upvotes, int downvotes, int score, int voteState, String projectType, String summary, int commentCount) { - super(id, user, upvotes, downvotes, score, voteState, summary, null, Collections.emptyList(), commentCount); - this.projectType = projectType; - } - /** * Fetch the data for this collab. If the data is already fetched, it will not be fetched again. * diff --git a/src/main/java/com/scorpiac/javarant/Comment.java b/src/main/java/com/scorpiac/javarant/Comment.java index 790455a..b4a4701 100644 --- a/src/main/java/com/scorpiac/javarant/Comment.java +++ b/src/main/java/com/scorpiac/javarant/Comment.java @@ -1,10 +1,6 @@ package com.scorpiac.javarant; public class Comment extends RantContent { - protected Comment(int id, User user, int upvotes, int downvotes, int score, int voteState, String content, Image image) { - super(id, user, upvotes, downvotes, score, voteState, content, image); - } - @Override public boolean equals(Object obj) { return super.equals(obj) && obj instanceof Comment; diff --git a/src/main/java/com/scorpiac/javarant/DevRant.java b/src/main/java/com/scorpiac/javarant/DevRant.java index d9a037a..341192c 100644 --- a/src/main/java/com/scorpiac/javarant/DevRant.java +++ b/src/main/java/com/scorpiac/javarant/DevRant.java @@ -10,27 +10,9 @@ public class DevRant { static final String RANT_URL = "/rants"; static final String COLLAB_URL = "/collabs"; - // API endpoints. - static final String API = "/api"; - static final String API_DEVRANT = API + "/devrant"; - static final String API_RANTS = API_DEVRANT + "/rants"; - static final String API_SEARCH = API_DEVRANT + "/search"; - static final String API_SURPRISE = API_RANTS + "/surprise"; - static final String API_USERS = API + "/users"; - static final String API_USER_ID = API + "/get-user-id"; - static final String API_WEEKLY = API_DEVRANT + "/weekly-rants"; - static final String API_COLLABS = API_DEVRANT + "/collabs"; - static final String API_STORIES = API_DEVRANT + "/story-rants"; - static final String API_AUTH_TOKEN = API_USERS + "/auth-token"; - static final String API_COMMENT = "/comments"; - static final String API_VOTE = "/vote"; - static final String API_NOTIFS = API_USERS + "/me/notif-feed"; - private Auth auth; - private boolean hideReposts = false; - private int numNotifs; - private News news; - private int weeklyRantNumber = -1; + + private final DevRantFeed feed; static { INJECTOR = Guice.createInjector(new InjectionModule()); @@ -38,6 +20,11 @@ public class DevRant { public DevRant() { INJECTOR.injectMembers(this); + feed = INJECTOR.getInstance(DevRantFeed.class); + } + + public DevRantFeed getFeed() { + return feed; } /** @@ -55,41 +42,4 @@ public void logout() { public boolean isLoggedIn() { return auth != null; } - - /** - * Set whether to hide reposts. - * - * @param hideReposts Whether to hide reposts. - */ - public void setHideReposts(boolean hideReposts) { - this.hideReposts = hideReposts; - } - - /** - * Get whether to hide reposts. - */ - public boolean getHideReposts() { - return hideReposts; - } - - /** - * Get the amount of notifications the user has. - */ - public int getNotifCount() { - return numNotifs; - } - - /** - * Get the news. - */ - public News getNews() { - return news; - } - - /** - * Get the weekly rant number, or -1 if this has not yet been set. - */ - public int getWeeklyRantNumber() { - return weeklyRantNumber; - } } diff --git a/src/main/java/com/scorpiac/javarant/DevRantFeed.java b/src/main/java/com/scorpiac/javarant/DevRantFeed.java new file mode 100644 index 0000000..66a6655 --- /dev/null +++ b/src/main/java/com/scorpiac/javarant/DevRantFeed.java @@ -0,0 +1,24 @@ +package com.scorpiac.javarant; + +import com.scorpiac.javarant.responses.RantsResponse; +import com.scorpiac.javarant.services.RequestHandler; + +import javax.inject.Inject; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +public class DevRantFeed { + private static final String API_RANTS = "/api/devrant/rants"; + + private final RequestHandler requestHandler; + + @Inject + DevRantFeed(RequestHandler requestHandler) { + this.requestHandler = requestHandler; + } + + public Optional> getRants() { + return requestHandler.get(API_RANTS, RantsResponse.class).map(r -> Arrays.asList(r.getRants())); + } +} diff --git a/src/main/java/com/scorpiac/javarant/Rant.java b/src/main/java/com/scorpiac/javarant/Rant.java index 42d735f..460ae2b 100644 --- a/src/main/java/com/scorpiac/javarant/Rant.java +++ b/src/main/java/com/scorpiac/javarant/Rant.java @@ -11,12 +11,6 @@ public class Rant extends RantContent { private int commentCount; private List comments; - protected Rant(int id, User user, int upvotes, int downvotes, int score, int voteState, String text, Image image, List tags, int commentCount) { - super(id, user, upvotes, downvotes, score, voteState, text, image); - this.tags = tags; - this.commentCount = commentCount; - } - /** * Get the comments on this rant. If they are not yet retrieved, this will also fetch them. * diff --git a/src/main/java/com/scorpiac/javarant/RantContent.java b/src/main/java/com/scorpiac/javarant/RantContent.java index 2820a46..20a8d16 100644 --- a/src/main/java/com/scorpiac/javarant/RantContent.java +++ b/src/main/java/com/scorpiac/javarant/RantContent.java @@ -1,26 +1,15 @@ package com.scorpiac.javarant; public abstract class RantContent { - private final int id; - private final User user; + private int id; + private User user; private int upvotes; private int downvotes; private int score; private VoteState voteState; - private String content; + private String text; private Image image; - protected RantContent(int id, User user, int upvotes, int downvotes, int score, int voteState, String content, Image image) { - this.id = id; - this.user = user; - this.upvotes = upvotes; - this.downvotes = downvotes; - this.score = score; - this.voteState = VoteState.fromValue(voteState); - this.content = content; - this.image = image; - } - @Override public int hashCode() { return id; @@ -74,10 +63,10 @@ public VoteState getVoteState() { } /** - * Get the content (text). + * Get the text. */ - public String getContent() { - return content; + public String getText() { + return text; } /** @@ -89,6 +78,6 @@ public Image getImage() { @Override public String toString() { - return content; + return text; } } diff --git a/src/main/java/com/scorpiac/javarant/responses/RantsResponse.java b/src/main/java/com/scorpiac/javarant/responses/RantsResponse.java new file mode 100644 index 0000000..6517e84 --- /dev/null +++ b/src/main/java/com/scorpiac/javarant/responses/RantsResponse.java @@ -0,0 +1,14 @@ +package com.scorpiac.javarant.responses; + +import com.scorpiac.javarant.Rant; + +public class RantsResponse extends Response { + private Rant[] rants; + + public RantsResponse() { + } + + public Rant[] getRants() { + return rants; + } +} diff --git a/src/main/java/com/scorpiac/javarant/responses/Response.java b/src/main/java/com/scorpiac/javarant/responses/Response.java new file mode 100644 index 0000000..3cd8619 --- /dev/null +++ b/src/main/java/com/scorpiac/javarant/responses/Response.java @@ -0,0 +1,12 @@ +package com.scorpiac.javarant.responses; + +class Response { + private boolean success; + + public Response() { + } + + public boolean isSuccess() { + return success; + } +} diff --git a/src/main/java/com/scorpiac/javarant/services/ObjectMapperResponseHandlerFactory.java b/src/main/java/com/scorpiac/javarant/services/ObjectMapperResponseHandlerFactory.java index 09f06cd..733fa7e 100644 --- a/src/main/java/com/scorpiac/javarant/services/ObjectMapperResponseHandlerFactory.java +++ b/src/main/java/com/scorpiac/javarant/services/ObjectMapperResponseHandlerFactory.java @@ -1,9 +1,11 @@ package com.scorpiac.javarant.services; import org.apache.http.client.ResponseHandler; +import sun.misc.IOUtils; import javax.inject.Inject; import javax.inject.Singleton; +import java.io.InputStream; @Singleton public class ObjectMapperResponseHandlerFactory { @@ -22,6 +24,10 @@ public ObjectMapperResponseHandlerFactory(ObjectMapperService mapperService) { * @return A response handler. */ public ResponseHandler getResponseHandler(Class clazz) { - return response -> mapperService.getMapper().readValue(response.getEntity().getContent(), clazz); + return response -> { + InputStream stream = response.getEntity().getContent(); + String content = new String(IOUtils.readFully(stream, -1, true)); + return mapperService.getMapper().readValue(content, clazz); + }; } } diff --git a/src/main/java/com/scorpiac/javarant/services/ObjectMapperService.java b/src/main/java/com/scorpiac/javarant/services/ObjectMapperService.java index 15d9faa..32ce06c 100644 --- a/src/main/java/com/scorpiac/javarant/services/ObjectMapperService.java +++ b/src/main/java/com/scorpiac/javarant/services/ObjectMapperService.java @@ -1,5 +1,7 @@ package com.scorpiac.javarant.services; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import javax.inject.Singleton; @@ -8,6 +10,11 @@ public class ObjectMapperService { private static final ObjectMapper MAPPER = new ObjectMapper(); + static { + MAPPER.configure(JsonGenerator.Feature.IGNORE_UNKNOWN, true); + MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } + /** * Get the object mapper. */ diff --git a/src/main/java/com/scorpiac/javarant/services/RequestHandler.java b/src/main/java/com/scorpiac/javarant/services/RequestHandler.java index 7a007e5..a2fd9f3 100644 --- a/src/main/java/com/scorpiac/javarant/services/RequestHandler.java +++ b/src/main/java/com/scorpiac/javarant/services/RequestHandler.java @@ -34,12 +34,12 @@ public RequestHandler(ObjectMapperResponseHandlerFactory responseHandlerFactory) this.responseHandlerFactory = responseHandlerFactory; } - public Optional get(String endpoint, Class clazz) { - return handleRequest(buildRequest(endpoint, Request::Get), clazz); + public Optional get(String endpoint, Class clazz, NameValuePair... params) { + return handleRequest(buildRequest(endpoint, Request::Get, params), clazz); } - public Optional post(String endpoint, Class clazz) { - return handleRequest(buildRequest(endpoint, Request::Post), clazz); + public Optional post(String endpoint, Class clazz, NameValuePair... params) { + return handleRequest(buildRequest(endpoint, Request::Post, params), clazz); } /** From d26b957c012b5049a8807b170f7e5196f55ef0f2 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Thu, 13 Jul 2017 09:26:57 +0200 Subject: [PATCH 10/65] Add getRant method to DevRant, add enum for Endpoint --- .../java/com/scorpiac/javarant/Comment.java | 1 + .../java/com/scorpiac/javarant/DevRant.java | 32 +++++++++++++++++-- .../com/scorpiac/javarant/DevRantFeed.java | 4 +-- .../java/com/scorpiac/javarant/Endpoint.java | 22 +++++++++++++ src/main/java/com/scorpiac/javarant/Rant.java | 2 ++ .../com/scorpiac/javarant/RantContent.java | 16 ---------- .../exceptions/DevRantApiException.java | 14 ++++++++ .../exceptions/NoSuchRantException.java | 6 ++-- .../javarant/responses/RantResponse.java | 14 ++++++++ .../javarant/responses/RantsResponse.java | 2 +- .../scorpiac/javarant/responses/Response.java | 7 +++- .../ObjectMapperResponseHandlerFactory.java | 9 ++++-- .../javarant/services/RequestHandler.java | 9 ++++++ 13 files changed, 110 insertions(+), 28 deletions(-) create mode 100644 src/main/java/com/scorpiac/javarant/Endpoint.java create mode 100644 src/main/java/com/scorpiac/javarant/exceptions/DevRantApiException.java create mode 100644 src/main/java/com/scorpiac/javarant/responses/RantResponse.java diff --git a/src/main/java/com/scorpiac/javarant/Comment.java b/src/main/java/com/scorpiac/javarant/Comment.java index b4a4701..ad654f9 100644 --- a/src/main/java/com/scorpiac/javarant/Comment.java +++ b/src/main/java/com/scorpiac/javarant/Comment.java @@ -1,6 +1,7 @@ package com.scorpiac.javarant; public class Comment extends RantContent { + @Override public boolean equals(Object obj) { return super.equals(obj) && obj instanceof Comment; diff --git a/src/main/java/com/scorpiac/javarant/DevRant.java b/src/main/java/com/scorpiac/javarant/DevRant.java index 341192c..068499e 100644 --- a/src/main/java/com/scorpiac/javarant/DevRant.java +++ b/src/main/java/com/scorpiac/javarant/DevRant.java @@ -2,6 +2,12 @@ import com.google.inject.Guice; import com.google.inject.Injector; +import com.scorpiac.javarant.exceptions.NoSuchRantException; +import com.scorpiac.javarant.responses.RantResponse; +import com.scorpiac.javarant.services.RequestHandler; + +import javax.inject.Inject; +import java.util.Optional; public class DevRant { private static final Injector INJECTOR; @@ -10,10 +16,11 @@ public class DevRant { static final String RANT_URL = "/rants"; static final String COLLAB_URL = "/collabs"; - private Auth auth; - private final DevRantFeed feed; + private RequestHandler requestHandler; + private Auth auth; + static { INJECTOR = Guice.createInjector(new InjectionModule()); } @@ -27,6 +34,27 @@ public DevRantFeed getFeed() { return feed; } + public Optional getRant(int id) { + // Execute the request. + Optional response = requestHandler.get(Endpoint.RANTS.toString() + '/' + id, RantResponse.class); + if (!response.isPresent()) { + return Optional.empty(); + } + + // Check if the rant exists. + RantResponse rantResponse = response.get(); + if (!rantResponse.isSuccess()) { + throw new NoSuchRantException(id, rantResponse.getError()); + } + + return Optional.of(rantResponse.getRant()); + } + + @Inject + void setRequestHandler(RequestHandler requestHandler) { + this.requestHandler = requestHandler; + } + /** * Log out of devRant. */ diff --git a/src/main/java/com/scorpiac/javarant/DevRantFeed.java b/src/main/java/com/scorpiac/javarant/DevRantFeed.java index 66a6655..f044286 100644 --- a/src/main/java/com/scorpiac/javarant/DevRantFeed.java +++ b/src/main/java/com/scorpiac/javarant/DevRantFeed.java @@ -9,8 +9,6 @@ import java.util.Optional; public class DevRantFeed { - private static final String API_RANTS = "/api/devrant/rants"; - private final RequestHandler requestHandler; @Inject @@ -19,6 +17,6 @@ public class DevRantFeed { } public Optional> getRants() { - return requestHandler.get(API_RANTS, RantsResponse.class).map(r -> Arrays.asList(r.getRants())); + return requestHandler.get(Endpoint.RANTS, RantsResponse.class).map(r -> Arrays.asList(r.getRants())); } } diff --git a/src/main/java/com/scorpiac/javarant/Endpoint.java b/src/main/java/com/scorpiac/javarant/Endpoint.java new file mode 100644 index 0000000..8c76cec --- /dev/null +++ b/src/main/java/com/scorpiac/javarant/Endpoint.java @@ -0,0 +1,22 @@ +package com.scorpiac.javarant; + +public enum Endpoint { + API("/api"), + API_DEVRANT(API, "devrant"), + RANTS(API_DEVRANT, "rants"); + + private final String endpoint; + + Endpoint(String endpoint) { + this.endpoint = endpoint; + } + + Endpoint(Endpoint base, String endpoint) { + this(base.toString() + '/' + endpoint); + } + + @Override + public String toString() { + return endpoint; + } +} diff --git a/src/main/java/com/scorpiac/javarant/Rant.java b/src/main/java/com/scorpiac/javarant/Rant.java index 460ae2b..b1871db 100644 --- a/src/main/java/com/scorpiac/javarant/Rant.java +++ b/src/main/java/com/scorpiac/javarant/Rant.java @@ -1,5 +1,6 @@ package com.scorpiac.javarant; +import com.fasterxml.jackson.annotation.JsonProperty; import com.scorpiac.javarant.services.RequestHandler; import java.net.URI; @@ -8,6 +9,7 @@ public class Rant extends RantContent { private List tags; + @JsonProperty("num_comments") private int commentCount; private List comments; diff --git a/src/main/java/com/scorpiac/javarant/RantContent.java b/src/main/java/com/scorpiac/javarant/RantContent.java index 20a8d16..2fcf56d 100644 --- a/src/main/java/com/scorpiac/javarant/RantContent.java +++ b/src/main/java/com/scorpiac/javarant/RantContent.java @@ -3,8 +3,6 @@ public abstract class RantContent { private int id; private User user; - private int upvotes; - private int downvotes; private int score; private VoteState voteState; private String text; @@ -34,20 +32,6 @@ public User getUser() { return user; } - /** - * Get the amount of upvotes. - */ - public int getUpvotes() { - return upvotes; - } - - /** - * Get the amount of downvotes. - */ - public int getDownvotes() { - return downvotes; - } - /** * Get the score. */ diff --git a/src/main/java/com/scorpiac/javarant/exceptions/DevRantApiException.java b/src/main/java/com/scorpiac/javarant/exceptions/DevRantApiException.java new file mode 100644 index 0000000..24c1c79 --- /dev/null +++ b/src/main/java/com/scorpiac/javarant/exceptions/DevRantApiException.java @@ -0,0 +1,14 @@ +package com.scorpiac.javarant.exceptions; + +public class DevRantApiException extends RuntimeException { + private final String internalError; + + public DevRantApiException(String message, String internalError) { + super(message); + this.internalError = internalError; + } + + public String getInternalError() { + return internalError; + } +} diff --git a/src/main/java/com/scorpiac/javarant/exceptions/NoSuchRantException.java b/src/main/java/com/scorpiac/javarant/exceptions/NoSuchRantException.java index e8f9627..31b02e5 100644 --- a/src/main/java/com/scorpiac/javarant/exceptions/NoSuchRantException.java +++ b/src/main/java/com/scorpiac/javarant/exceptions/NoSuchRantException.java @@ -1,12 +1,12 @@ package com.scorpiac.javarant.exceptions; -public class NoSuchRantException extends RuntimeException { +public class NoSuchRantException extends DevRantApiException { /** * Create a new exception for a non-existent rant. * * @param id The id of the rant. */ - public NoSuchRantException(int id) { - super("Rant with id " + id + " does not exist."); + public NoSuchRantException(int id, String internalError) { + super("Rant with id " + id + " does not exist.", internalError); } } diff --git a/src/main/java/com/scorpiac/javarant/responses/RantResponse.java b/src/main/java/com/scorpiac/javarant/responses/RantResponse.java new file mode 100644 index 0000000..2a9a361 --- /dev/null +++ b/src/main/java/com/scorpiac/javarant/responses/RantResponse.java @@ -0,0 +1,14 @@ +package com.scorpiac.javarant.responses; + +import com.scorpiac.javarant.Rant; + +public class RantResponse extends Response { + private Rant rant; + + RantResponse() { + } + + public Rant getRant() { + return rant; + } +} diff --git a/src/main/java/com/scorpiac/javarant/responses/RantsResponse.java b/src/main/java/com/scorpiac/javarant/responses/RantsResponse.java index 6517e84..f53619d 100644 --- a/src/main/java/com/scorpiac/javarant/responses/RantsResponse.java +++ b/src/main/java/com/scorpiac/javarant/responses/RantsResponse.java @@ -5,7 +5,7 @@ public class RantsResponse extends Response { private Rant[] rants; - public RantsResponse() { + RantsResponse() { } public Rant[] getRants() { diff --git a/src/main/java/com/scorpiac/javarant/responses/Response.java b/src/main/java/com/scorpiac/javarant/responses/Response.java index 3cd8619..1507302 100644 --- a/src/main/java/com/scorpiac/javarant/responses/Response.java +++ b/src/main/java/com/scorpiac/javarant/responses/Response.java @@ -2,11 +2,16 @@ class Response { private boolean success; + private String error; - public Response() { + Response() { } public boolean isSuccess() { return success; } + + public String getError() { + return error; + } } diff --git a/src/main/java/com/scorpiac/javarant/services/ObjectMapperResponseHandlerFactory.java b/src/main/java/com/scorpiac/javarant/services/ObjectMapperResponseHandlerFactory.java index 733fa7e..c23a2a3 100644 --- a/src/main/java/com/scorpiac/javarant/services/ObjectMapperResponseHandlerFactory.java +++ b/src/main/java/com/scorpiac/javarant/services/ObjectMapperResponseHandlerFactory.java @@ -1,11 +1,11 @@ package com.scorpiac.javarant.services; import org.apache.http.client.ResponseHandler; -import sun.misc.IOUtils; import javax.inject.Inject; import javax.inject.Singleton; import java.io.InputStream; +import java.util.Scanner; @Singleton public class ObjectMapperResponseHandlerFactory { @@ -26,8 +26,13 @@ public ObjectMapperResponseHandlerFactory(ObjectMapperService mapperService) { public ResponseHandler getResponseHandler(Class clazz) { return response -> { InputStream stream = response.getEntity().getContent(); - String content = new String(IOUtils.readFully(stream, -1, true)); + String content = streamToString(stream); return mapperService.getMapper().readValue(content, clazz); }; } + + private static String streamToString(InputStream stream) { + java.util.Scanner s = new Scanner(stream).useDelimiter("\\A"); + return s.hasNext() ? s.next() : ""; + } } diff --git a/src/main/java/com/scorpiac/javarant/services/RequestHandler.java b/src/main/java/com/scorpiac/javarant/services/RequestHandler.java index a2fd9f3..f5a6917 100644 --- a/src/main/java/com/scorpiac/javarant/services/RequestHandler.java +++ b/src/main/java/com/scorpiac/javarant/services/RequestHandler.java @@ -1,5 +1,6 @@ package com.scorpiac.javarant.services; +import com.scorpiac.javarant.Endpoint; import org.apache.http.NameValuePair; import org.apache.http.client.fluent.Request; import org.apache.http.client.utils.URIBuilder; @@ -34,10 +35,18 @@ public RequestHandler(ObjectMapperResponseHandlerFactory responseHandlerFactory) this.responseHandlerFactory = responseHandlerFactory; } + public Optional get(Endpoint endpoint, Class clazz, NameValuePair... params) { + return get(endpoint.toString(), clazz, params); + } + public Optional get(String endpoint, Class clazz, NameValuePair... params) { return handleRequest(buildRequest(endpoint, Request::Get, params), clazz); } + public Optional post(Endpoint endpoint, Class clazz, NameValuePair... params) { + return post(endpoint, clazz, params); + } + public Optional post(String endpoint, Class clazz, NameValuePair... params) { return handleRequest(buildRequest(endpoint, Request::Post, params), clazz); } From eaf5399dc4d2bb93b9c7f48006306b39f6118e4c Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Thu, 10 Aug 2017 17:43:31 +0200 Subject: [PATCH 11/65] Update gitignore, license, pom --- .gitattributes | 17 ---------------- .gitignore | 55 +------------------------------------------------- LICENSE.md | 2 +- pom.xml | 5 +++-- 4 files changed, 5 insertions(+), 74 deletions(-) delete mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index bdb0cab..0000000 --- a/.gitattributes +++ /dev/null @@ -1,17 +0,0 @@ -# Auto detect text files and perform LF normalization -* text=auto - -# Custom for Visual Studio -*.cs diff=csharp - -# Standard to msysgit -*.doc diff=astextplain -*.DOC diff=astextplain -*.docx diff=astextplain -*.DOCX diff=astextplain -*.dot diff=astextplain -*.DOT diff=astextplain -*.pdf diff=astextplain -*.PDF diff=astextplain -*.rtf diff=astextplain -*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore index 10bb9bf..ceb6ea8 100644 --- a/.gitignore +++ b/.gitignore @@ -5,62 +5,9 @@ # Maven target/* +# Java *.class - -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # *.jar -*.war -*.ear # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* - -# ========================= -# Operating System Files -# ========================= - -# OSX -# ========================= - -.DS_Store -.AppleDouble -.LSOverride - -# Thumbnails -._* - -# Files that might appear on external disk -.Spotlight-V100 -.Trashes - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -# Windows -# ========================= - -# Windows image file caches -Thumbs.db -ehthumbs.db - -# Folder config file -Desktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msm -*.msp - -# Windows shortcuts -*.lnk diff --git a/LICENSE.md b/LICENSE.md index 71332ef..3554b1b 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2016 Scorpiac +Copyright (c) 2017 Scorpiac Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pom.xml b/pom.xml index 63f6ed0..fec42c0 100644 --- a/pom.xml +++ b/pom.xml @@ -34,6 +34,7 @@ + 1.8 2.8.9 4.5.3 1.7.25 @@ -80,8 +81,8 @@ org.apache.maven.plugins maven-compiler-plugin - 1.8 - 1.8 + ${java.version} + ${java.version} From 573fb9cfd195969bd993f5aa22e6967b459ece33 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Thu, 10 Aug 2017 19:00:30 +0200 Subject: [PATCH 12/65] Fix comments --- .../java/com/scorpiac/javarant/Collab.java | 38 ------------------- .../java/com/scorpiac/javarant/Comment.java | 9 ++++- .../java/com/scorpiac/javarant/DevRant.java | 12 +++--- .../com/scorpiac/javarant/DevRantFeed.java | 3 +- .../java/com/scorpiac/javarant/Image.java | 12 ++---- src/main/java/com/scorpiac/javarant/Rant.java | 38 +++++-------------- .../com/scorpiac/javarant/RantContent.java | 6 +-- .../javarant/responses/RantResponse.java | 26 +++++++++++-- .../javarant/responses/RantsResponse.java | 9 ++--- .../scorpiac/javarant/responses/Response.java | 3 -- .../ObjectMapperResponseHandlerFactory.java | 2 +- 11 files changed, 59 insertions(+), 99 deletions(-) diff --git a/src/main/java/com/scorpiac/javarant/Collab.java b/src/main/java/com/scorpiac/javarant/Collab.java index c71a83a..c81af3a 100644 --- a/src/main/java/com/scorpiac/javarant/Collab.java +++ b/src/main/java/com/scorpiac/javarant/Collab.java @@ -1,46 +1,12 @@ package com.scorpiac.javarant; -import com.scorpiac.javarant.services.RequestHandler; - -import java.net.URI; - public class Collab extends Rant { private String projectType; - - // Data that needs to be fetched. - private boolean fetched = false; private String description; private String techStack; private String teamSize; private String url; - /** - * Fetch the data for this collab. If the data is already fetched, it will not be fetched again. - * - * @return Whether the data was fetched successfully. - */ - public boolean fetchData() { - return fetchData(false); - } - - /** - * Fetch the data for this collab. - * - * @param force Whether to fetch the data even if it was already fetched. - * @return Whether the data was fetched successfully. - */ - public boolean fetchData(boolean force) { - return true; - } - - /** - * Get the link to the collab. - */ - @Override - public URI link() { - return RequestHandler.BASE_URI.resolve(DevRant.COLLAB_URL).resolve(String.valueOf(getId())); - } - /** * Get the project type. */ @@ -52,7 +18,6 @@ public String getProjectType() { * Get the project description. */ public String getDescription() { - fetchData(); return description; } @@ -60,7 +25,6 @@ public String getDescription() { * Get the project tech stack. */ public String getTechStack() { - fetchData(); return techStack; } @@ -68,7 +32,6 @@ public String getTechStack() { * Get the team size. */ public String getTeamSize() { - fetchData(); return teamSize; } @@ -76,7 +39,6 @@ public String getTeamSize() { * Get the project url. */ public String getUrl() { - fetchData(); return url; } } diff --git a/src/main/java/com/scorpiac/javarant/Comment.java b/src/main/java/com/scorpiac/javarant/Comment.java index ad654f9..ce8f5a3 100644 --- a/src/main/java/com/scorpiac/javarant/Comment.java +++ b/src/main/java/com/scorpiac/javarant/Comment.java @@ -1,9 +1,16 @@ package com.scorpiac.javarant; -public class Comment extends RantContent { +import com.fasterxml.jackson.annotation.JsonProperty; +public class Comment extends RantContent { @Override public boolean equals(Object obj) { return super.equals(obj) && obj instanceof Comment; } + + // For comments the text is called "body" instead of "text". + @JsonProperty("body") + private void setText(String text) { + this.text = text; + } } diff --git a/src/main/java/com/scorpiac/javarant/DevRant.java b/src/main/java/com/scorpiac/javarant/DevRant.java index 068499e..79faa45 100644 --- a/src/main/java/com/scorpiac/javarant/DevRant.java +++ b/src/main/java/com/scorpiac/javarant/DevRant.java @@ -35,19 +35,19 @@ public DevRantFeed getFeed() { } public Optional getRant(int id) { - // Execute the request. Optional response = requestHandler.get(Endpoint.RANTS.toString() + '/' + id, RantResponse.class); + + // Check if there is a response. if (!response.isPresent()) { return Optional.empty(); } - // Check if the rant exists. - RantResponse rantResponse = response.get(); - if (!rantResponse.isSuccess()) { - throw new NoSuchRantException(id, rantResponse.getError()); + // Check for success. + if (!response.get().isSuccess()) { + throw new NoSuchRantException(id, response.get().getError()); } - return Optional.of(rantResponse.getRant()); + return Optional.of(response.get().getRant()); } @Inject diff --git a/src/main/java/com/scorpiac/javarant/DevRantFeed.java b/src/main/java/com/scorpiac/javarant/DevRantFeed.java index f044286..88e9834 100644 --- a/src/main/java/com/scorpiac/javarant/DevRantFeed.java +++ b/src/main/java/com/scorpiac/javarant/DevRantFeed.java @@ -4,7 +4,6 @@ import com.scorpiac.javarant.services.RequestHandler; import javax.inject.Inject; -import java.util.Arrays; import java.util.List; import java.util.Optional; @@ -17,6 +16,6 @@ public class DevRantFeed { } public Optional> getRants() { - return requestHandler.get(Endpoint.RANTS, RantsResponse.class).map(r -> Arrays.asList(r.getRants())); + return requestHandler.get(Endpoint.RANTS, RantsResponse.class).map(RantsResponse::getRants); } } diff --git a/src/main/java/com/scorpiac/javarant/Image.java b/src/main/java/com/scorpiac/javarant/Image.java index 00f75fd..55d73b2 100644 --- a/src/main/java/com/scorpiac/javarant/Image.java +++ b/src/main/java/com/scorpiac/javarant/Image.java @@ -5,15 +5,9 @@ public class Image { private int width; private int height; - private Image(String url, int width, int height) { - this.url = url; - this.width = width; - this.height = height; - } - @Override public boolean equals(Object obj) { - return obj instanceof Image && ((Image)obj).getUrl().equals(url); + return obj instanceof Image && ((Image) obj).getLink().equals(url); } @Override @@ -22,9 +16,9 @@ public int hashCode() { } /** - * Get the image url. + * Get the image link. */ - public String getUrl() { + public String getLink() { return url; } diff --git a/src/main/java/com/scorpiac/javarant/Rant.java b/src/main/java/com/scorpiac/javarant/Rant.java index b1871db..a383ad7 100644 --- a/src/main/java/com/scorpiac/javarant/Rant.java +++ b/src/main/java/com/scorpiac/javarant/Rant.java @@ -7,51 +7,33 @@ import java.util.Collections; import java.util.List; + public class Rant extends RantContent { private List tags; @JsonProperty("num_comments") private int commentCount; private List comments; + private String link; + + @Override + public boolean equals(Object obj) { + return super.equals(obj) && obj instanceof Rant; + } /** - * Get the comments on this rant. If they are not yet retrieved, this will also fetch them. + * Get the comments on this rant. * * @return The comments. */ public List getComments() { - fetchComments(); return Collections.unmodifiableList(comments); } - /** - * Fetch the comments on this rant. If the comments are already fetched, they will not be fetched again. - * - * @return Whether the data was fetched successfully. - */ - public boolean fetchComments() { - return fetchComments(false); - } - - /** - * Fetch the comments on this rant. - * - * @param force Whether to fetch the comments even if it they are already fetched. - * @return Whether the data was fetched successfully. - */ - public boolean fetchComments(boolean force) { - return true; - } - /** * Get the link to the rant. */ - public URI link() { - return RequestHandler.BASE_URI.resolve(DevRant.RANT_URL).resolve(String.valueOf(getId())); - } - - @Override - public boolean equals(Object obj) { - return super.equals(obj) && obj instanceof Rant; + public URI getLink() { + return RequestHandler.BASE_URI.resolve('/' + link); } /** diff --git a/src/main/java/com/scorpiac/javarant/RantContent.java b/src/main/java/com/scorpiac/javarant/RantContent.java index 2fcf56d..a965a48 100644 --- a/src/main/java/com/scorpiac/javarant/RantContent.java +++ b/src/main/java/com/scorpiac/javarant/RantContent.java @@ -4,8 +4,8 @@ public abstract class RantContent { private int id; private User user; private int score; - private VoteState voteState; - private String text; + private VoteState voteState = VoteState.NONE; + protected String text; private Image image; @Override @@ -15,7 +15,7 @@ public int hashCode() { @Override public boolean equals(Object obj) { - return obj instanceof RantContent && ((RantContent)obj).getId() == id; + return obj instanceof RantContent && ((RantContent) obj).getId() == id; } /** diff --git a/src/main/java/com/scorpiac/javarant/responses/RantResponse.java b/src/main/java/com/scorpiac/javarant/responses/RantResponse.java index 2a9a361..dd59cdb 100644 --- a/src/main/java/com/scorpiac/javarant/responses/RantResponse.java +++ b/src/main/java/com/scorpiac/javarant/responses/RantResponse.java @@ -1,14 +1,34 @@ package com.scorpiac.javarant.responses; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.scorpiac.javarant.Comment; import com.scorpiac.javarant.Rant; +import java.lang.reflect.Field; +import java.util.List; + public class RantResponse extends Response { private Rant rant; - - RantResponse() { - } + @JsonProperty("comments") + private List comments; public Rant getRant() { + // Get the comments field. + Field commentsField; + try { + commentsField = rant.getClass().getDeclaredField("comments"); + } catch (NoSuchFieldException e) { + throw new IllegalStateException(e); // TODO + } + + // Set the comments field. + commentsField.setAccessible(true); + try { + commentsField.set(rant, comments); + } catch (IllegalAccessException e) { + throw new IllegalStateException(e); // TODO + } + return rant; } } diff --git a/src/main/java/com/scorpiac/javarant/responses/RantsResponse.java b/src/main/java/com/scorpiac/javarant/responses/RantsResponse.java index f53619d..8915cce 100644 --- a/src/main/java/com/scorpiac/javarant/responses/RantsResponse.java +++ b/src/main/java/com/scorpiac/javarant/responses/RantsResponse.java @@ -2,13 +2,12 @@ import com.scorpiac.javarant.Rant; -public class RantsResponse extends Response { - private Rant[] rants; +import java.util.List; - RantsResponse() { - } +public class RantsResponse extends Response { + private List rants; - public Rant[] getRants() { + public List getRants() { return rants; } } diff --git a/src/main/java/com/scorpiac/javarant/responses/Response.java b/src/main/java/com/scorpiac/javarant/responses/Response.java index 1507302..130695f 100644 --- a/src/main/java/com/scorpiac/javarant/responses/Response.java +++ b/src/main/java/com/scorpiac/javarant/responses/Response.java @@ -4,9 +4,6 @@ class Response { private boolean success; private String error; - Response() { - } - public boolean isSuccess() { return success; } diff --git a/src/main/java/com/scorpiac/javarant/services/ObjectMapperResponseHandlerFactory.java b/src/main/java/com/scorpiac/javarant/services/ObjectMapperResponseHandlerFactory.java index c23a2a3..6c9e502 100644 --- a/src/main/java/com/scorpiac/javarant/services/ObjectMapperResponseHandlerFactory.java +++ b/src/main/java/com/scorpiac/javarant/services/ObjectMapperResponseHandlerFactory.java @@ -32,7 +32,7 @@ public ResponseHandler getResponseHandler(Class clazz) { } private static String streamToString(InputStream stream) { - java.util.Scanner s = new Scanner(stream).useDelimiter("\\A"); + Scanner s = new Scanner(stream).useDelimiter("\\A"); return s.hasNext() ? s.next() : ""; } } From 813c6dd05f77f6feeff0b4dd3752d0ba6e463dfb Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Thu, 10 Aug 2017 19:07:48 +0200 Subject: [PATCH 13/65] Fix images --- src/main/java/com/scorpiac/javarant/Image.java | 5 +++++ src/main/java/com/scorpiac/javarant/RantContent.java | 3 +++ .../com/scorpiac/javarant/services/ObjectMapperService.java | 1 + 3 files changed, 9 insertions(+) diff --git a/src/main/java/com/scorpiac/javarant/Image.java b/src/main/java/com/scorpiac/javarant/Image.java index 55d73b2..54ab32b 100644 --- a/src/main/java/com/scorpiac/javarant/Image.java +++ b/src/main/java/com/scorpiac/javarant/Image.java @@ -1,8 +1,13 @@ package com.scorpiac.javarant; +import com.fasterxml.jackson.annotation.JsonProperty; + public class Image { + @JsonProperty("url") private String url; + @JsonProperty("width") private int width; + @JsonProperty("height") private int height; @Override diff --git a/src/main/java/com/scorpiac/javarant/RantContent.java b/src/main/java/com/scorpiac/javarant/RantContent.java index a965a48..ff17a51 100644 --- a/src/main/java/com/scorpiac/javarant/RantContent.java +++ b/src/main/java/com/scorpiac/javarant/RantContent.java @@ -1,11 +1,14 @@ package com.scorpiac.javarant; +import com.fasterxml.jackson.annotation.JsonProperty; + public abstract class RantContent { private int id; private User user; private int score; private VoteState voteState = VoteState.NONE; protected String text; + @JsonProperty("attached_image") private Image image; @Override diff --git a/src/main/java/com/scorpiac/javarant/services/ObjectMapperService.java b/src/main/java/com/scorpiac/javarant/services/ObjectMapperService.java index 32ce06c..d59f72e 100644 --- a/src/main/java/com/scorpiac/javarant/services/ObjectMapperService.java +++ b/src/main/java/com/scorpiac/javarant/services/ObjectMapperService.java @@ -13,6 +13,7 @@ public class ObjectMapperService { static { MAPPER.configure(JsonGenerator.Feature.IGNORE_UNKNOWN, true); MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + MAPPER.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true); } /** From 597cd17b6239ee2a960986f1d65b2bbf6bd8a82d Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Thu, 31 Aug 2017 17:43:36 +0200 Subject: [PATCH 14/65] Add testng and wiremock, fix versions --- pom.xml | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index fec42c0..c2dd063 100644 --- a/pom.xml +++ b/pom.xml @@ -35,10 +35,20 @@ 1.8 + + 2.8.9 4.5.3 1.7.25 4.1.0 + 6.11 + 2.7.1 + + + 3.6.1 + 3.0.1 + 2.10.4 + 1.5 @@ -62,6 +72,20 @@ guice ${guice.version} + + + + org.testng + testng + ${testng.version} + test + + + com.github.tomakehurst + wiremock + ${wiremock.version} + test + @@ -80,6 +104,7 @@ org.apache.maven.plugins maven-compiler-plugin + ${maven.compiler.plugin.version} ${java.version} ${java.version} @@ -88,7 +113,7 @@ org.apache.maven.plugins maven-source-plugin - 3.0.1 + ${maven.source.plugin.version} attach-sources @@ -101,7 +126,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.10.4 + ${maven.javadoc.plugin.version} attach-javadocs @@ -114,7 +139,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.5 + ${maven.gpg.plugin.version} sign-artifacts From d09ca8462989ab93c8b0cc9118765af5d4d7ee8d Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Thu, 31 Aug 2017 17:53:12 +0200 Subject: [PATCH 15/65] Add simple test, fix pom for now --- pom.xml | 9 +++++---- .../javarant/services/LogFactoryTest.java | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 src/main/test/java/com/scorpiac/javarant/services/LogFactoryTest.java diff --git a/pom.xml b/pom.xml index c2dd063..e84067a 100644 --- a/pom.xml +++ b/pom.xml @@ -35,6 +35,7 @@ 1.8 + UTF-8 2.8.9 @@ -123,7 +124,7 @@ - + + diff --git a/src/main/test/java/com/scorpiac/javarant/services/LogFactoryTest.java b/src/main/test/java/com/scorpiac/javarant/services/LogFactoryTest.java new file mode 100644 index 0000000..a83f98d --- /dev/null +++ b/src/main/test/java/com/scorpiac/javarant/services/LogFactoryTest.java @@ -0,0 +1,15 @@ +package com.scorpiac.javarant.services; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +public class LogFactoryTest { + @Test + public void testLoggerName() { + assertEquals( + LogFactory.getLog().getName(), + getClass().getName() + ); + } +} From 7305609a9fcdba52301595aae318b5b3f5c4a405 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Thu, 31 Aug 2017 17:56:00 +0200 Subject: [PATCH 16/65] Move test directory to correct location --- src/test/java/com/scorpiac/javarant/DevRantIT.java | 6 ++++++ .../java/com/scorpiac/javarant/services/LogFactoryTest.java | 0 2 files changed, 6 insertions(+) create mode 100644 src/test/java/com/scorpiac/javarant/DevRantIT.java rename src/{main => }/test/java/com/scorpiac/javarant/services/LogFactoryTest.java (100%) diff --git a/src/test/java/com/scorpiac/javarant/DevRantIT.java b/src/test/java/com/scorpiac/javarant/DevRantIT.java new file mode 100644 index 0000000..c634af1 --- /dev/null +++ b/src/test/java/com/scorpiac/javarant/DevRantIT.java @@ -0,0 +1,6 @@ +package com.scorpiac.javarant; + +import org.testng.annotations.Test; + +public class DevRantIT { +} diff --git a/src/main/test/java/com/scorpiac/javarant/services/LogFactoryTest.java b/src/test/java/com/scorpiac/javarant/services/LogFactoryTest.java similarity index 100% rename from src/main/test/java/com/scorpiac/javarant/services/LogFactoryTest.java rename to src/test/java/com/scorpiac/javarant/services/LogFactoryTest.java From 7db5a3f4626084952a91fc771ee06cfb56fc6a86 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Thu, 31 Aug 2017 18:30:53 +0200 Subject: [PATCH 17/65] Improve DevRantIT --- .../java/com/scorpiac/javarant/DevRantIT.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/test/java/com/scorpiac/javarant/DevRantIT.java b/src/test/java/com/scorpiac/javarant/DevRantIT.java index c634af1..c57eeb8 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantIT.java @@ -1,6 +1,44 @@ package com.scorpiac.javarant; +import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import java.util.Arrays; + +import static org.testng.Assert.assertEquals; + public class DevRantIT { + private DevRant devRant; + + @BeforeClass + public void beforeClass() { + // TODO: use Wiremock. + devRant = new DevRant(); + } + + @Test + public void testGetRant() { + Rant rant = devRant.getRant(686001).get(); + + assertEquals(rant.getId(), 686001); + assertEquals(rant.getText(), "I only just noticed this is on the git man page :P"); + assertEquals(rant.getScore(), 84); + assertEquals(rant.getTags(), Arrays.asList("terminal", "manual", "git")); + assertEquals(rant.getVoteState(), VoteState.NONE); + + // Comments. + assertEquals(rant.getCommentCount(), 5); + assertEquals(rant.getComments().size(), 5); + assertEquals(rant.getComments().get(0).getId(), 686175); + + // Image. + assertEquals(rant.getImage().getWidth(), 530); + assertEquals(rant.getImage().getHeight(), 134); + assertEquals(rant.getImage().getLink(), "https://img.devrant.io/devrant/rant/r_686001_VfN7X.jpg"); + + // User. + /*assertEquals(rant.getUser().getId(), 102959); + assertEquals(rant.getUser().getUsername(), "LucaScorpion"); + assertEquals(rant.getUser().getScore(), 3831);*/ + } } From 06c1503f18ac5f7407d5d6a069a407f1f1572445 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Thu, 31 Aug 2017 18:44:32 +0200 Subject: [PATCH 18/65] Add MinimalUser for RantContent --- .../com/scorpiac/javarant/MinimalUser.java | 65 +++++++++++++ .../com/scorpiac/javarant/RantContent.java | 35 +++++-- src/main/java/com/scorpiac/javarant/User.java | 92 +------------------ .../java/com/scorpiac/javarant/DevRantIT.java | 4 +- 4 files changed, 93 insertions(+), 103 deletions(-) create mode 100644 src/main/java/com/scorpiac/javarant/MinimalUser.java diff --git a/src/main/java/com/scorpiac/javarant/MinimalUser.java b/src/main/java/com/scorpiac/javarant/MinimalUser.java new file mode 100644 index 0000000..9c39d7a --- /dev/null +++ b/src/main/java/com/scorpiac/javarant/MinimalUser.java @@ -0,0 +1,65 @@ +package com.scorpiac.javarant; + +import com.scorpiac.javarant.services.RequestHandler; + +import java.net.URI; + +public class MinimalUser { + private int id; + private String username; + private int score; + + static MinimalUser create(int id, String username, int score) { + MinimalUser user = new MinimalUser(); + user.id = id; + user.username = username; + user.score = score; + return user; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof MinimalUser && ((MinimalUser) obj).getId() == id; + } + + @Override + public int hashCode() { + return id; + } + + /** + * Get the user id. + * + * @return The user id. + */ + public int getId() { + return id; + } + + /** + * Get the username. + * + * @return The username. + */ + public String getUsername() { + return username; + } + + /** + * Get the user's overall score. + * + * @return The score. + */ + public int getScore() { + return score; + } + + /** + * Get the link to the user's profile. + * + * @return A link to the user's profile. + */ + public URI userLink() { + return RequestHandler.BASE_URI.resolve(DevRant.USER_URL).resolve(username); + } +} diff --git a/src/main/java/com/scorpiac/javarant/RantContent.java b/src/main/java/com/scorpiac/javarant/RantContent.java index ff17a51..60949fb 100644 --- a/src/main/java/com/scorpiac/javarant/RantContent.java +++ b/src/main/java/com/scorpiac/javarant/RantContent.java @@ -4,13 +4,20 @@ public abstract class RantContent { private int id; - private User user; private int score; private VoteState voteState = VoteState.NONE; protected String text; @JsonProperty("attached_image") private Image image; + // Minimal user info. + @JsonProperty("user_id") + private int userId; + @JsonProperty("user_username") + private String username; + @JsonProperty("user_score") + private int userScore; + @Override public int hashCode() { return id; @@ -23,27 +30,36 @@ public boolean equals(Object obj) { /** * Get the id. + * + * @return The id. */ public int getId() { return id; } /** - * Get the author. + * Get the user. + * + * @return The user. */ - public User getUser() { - return user; + public MinimalUser getUser() { + return MinimalUser.create(userId, username, userScore); } /** * Get the score. + * + * @return The score. */ public int getScore() { return score; } /** - * Get the vote state. + * Get the vote state of the authenticated user. + * If no user is logged in, the vote state is always {@link VoteState#NONE}. + * + * @return The vote state. */ public VoteState getVoteState() { return voteState; @@ -51,6 +67,8 @@ public VoteState getVoteState() { /** * Get the text. + * + * @return The text. */ public String getText() { return text; @@ -58,13 +76,10 @@ public String getText() { /** * Get the image, or {@code null} if there is none. + * + * @return The image. */ public Image getImage() { return image; } - - @Override - public String toString() { - return text; - } } diff --git a/src/main/java/com/scorpiac/javarant/User.java b/src/main/java/com/scorpiac/javarant/User.java index cfd2861..42ea218 100644 --- a/src/main/java/com/scorpiac/javarant/User.java +++ b/src/main/java/com/scorpiac/javarant/User.java @@ -1,19 +1,12 @@ package com.scorpiac.javarant; -import com.scorpiac.javarant.exceptions.NoSuchUserException; import com.scorpiac.javarant.services.RequestHandler; import java.net.URI; import java.util.Collections; import java.util.List; -public class User { - // Data that is always available. - private int id; - private String username; - private int score; - - // Data that need to be fetched from the user profile. +public class User extends MinimalUser { private boolean fetched = false; private String about; private String location; @@ -29,33 +22,6 @@ public class User { private int favoritesCount; private String avatar; - User(int id) { - this.id = id; - - // Fetch the data, check if the user exists. - if (!fetchData()) - throw new NoSuchUserException(id); - } - - /** - * Fetch the user data from the user profile. If the data is already fetched, it will not be fetched again. - * - * @return Whether the data was fetched successfully. - */ - public boolean fetchData() { - return fetchData(false); - } - - /** - * Fetch the user data from the user profile. - * - * @param force Whether to fetch the data even if it was already fetched. - * @return Whether the data was fetched successfully. - */ - public boolean fetchData(boolean force) { - return true; - } - /** * Get whether the user data is fetched. */ @@ -63,57 +29,17 @@ public boolean isFetched() { return fetched; } - /** - * Get the link to the user. - */ - public URI userLink() { - return RequestHandler.BASE_URI.resolve(DevRant.USER_URL).resolve(username); - } - /** * Get the link to the user's avatar. */ public URI avatarLink() { - fetchData(); return RequestHandler.AVATARS_URI.resolve(avatar); } - @Override - public boolean equals(Object obj) { - return obj instanceof User && ((User)obj).getId() == id; - } - - @Override - public int hashCode() { - return id; - } - - /** - * Get the user id. - */ - public int getId() { - return id; - } - - /** - * Get the username. - */ - public String getUsername() { - return username; - } - - /** - * Get the user's overall score. - */ - public int getScore() { - return score; - } - /** * Get information about the user. */ public String getAbout() { - fetchData(); return about; } @@ -121,7 +47,6 @@ public String getAbout() { * Get the user's location. */ public String getLocation() { - fetchData(); return location; } @@ -129,7 +54,6 @@ public String getLocation() { * Get the user's skills. */ public String getSkills() { - fetchData(); return skills; } @@ -137,7 +61,6 @@ public String getSkills() { * Get the user's GitHub username. */ public String getGithub() { - fetchData(); return github; } @@ -145,7 +68,6 @@ public String getGithub() { * Get the rants that this user posted. */ public List getRants() { - fetchData(); return Collections.unmodifiableList(rants); } @@ -153,7 +75,6 @@ public List getRants() { * Get the rants that this user upvoted. */ public List getUpvoted() { - fetchData(); return Collections.unmodifiableList(upvoted); } @@ -161,7 +82,6 @@ public List getUpvoted() { * Get this user's comments. */ public List getComments() { - fetchData(); return Collections.unmodifiableList(comments); } @@ -169,7 +89,6 @@ public List getComments() { * Get this user's favorites. */ public List getFavorites() { - fetchData(); return Collections.unmodifiableList(favorites); } @@ -177,7 +96,6 @@ public List getFavorites() { * Get the amount of rants that this user has posted. */ public int getRantsCount() { - fetchData(); return rantsCount; } @@ -185,7 +103,6 @@ public int getRantsCount() { * Get the amount of rants that this user has upvoted. */ public int getUpvotedCount() { - fetchData(); return upvotedCount; } @@ -193,7 +110,6 @@ public int getUpvotedCount() { * Get the amount of comments that this user has posted. */ public int getCommentsCount() { - fetchData(); return commentsCount; } @@ -201,12 +117,6 @@ public int getCommentsCount() { * Get the amount of rants that this user has favorited. */ public int getFavoritesCount() { - fetchData(); return favoritesCount; } - - @Override - public String toString() { - return username; - } } diff --git a/src/test/java/com/scorpiac/javarant/DevRantIT.java b/src/test/java/com/scorpiac/javarant/DevRantIT.java index c57eeb8..210ab83 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantIT.java @@ -37,8 +37,8 @@ public void testGetRant() { assertEquals(rant.getImage().getLink(), "https://img.devrant.io/devrant/rant/r_686001_VfN7X.jpg"); // User. - /*assertEquals(rant.getUser().getId(), 102959); + assertEquals(rant.getUser().getId(), 102959); assertEquals(rant.getUser().getUsername(), "LucaScorpion"); - assertEquals(rant.getUser().getScore(), 3831);*/ + assertEquals(rant.getUser().getScore(), 3831); } } From a4f4c2cf55f7207664bfc99c6096086f894fb3b2 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Thu, 31 Aug 2017 19:09:19 +0200 Subject: [PATCH 19/65] Remove InjectionModule, fix DevRantTest --- src/main/java/com/scorpiac/javarant/DevRant.java | 14 +++++++------- .../com/scorpiac/javarant/InjectionModule.java | 10 ---------- .../javarant/{DevRantIT.java => DevRantTest.java} | 2 +- 3 files changed, 8 insertions(+), 18 deletions(-) delete mode 100644 src/main/java/com/scorpiac/javarant/InjectionModule.java rename src/test/java/com/scorpiac/javarant/{DevRantIT.java => DevRantTest.java} (98%) diff --git a/src/main/java/com/scorpiac/javarant/DevRant.java b/src/main/java/com/scorpiac/javarant/DevRant.java index 79faa45..eb3d838 100644 --- a/src/main/java/com/scorpiac/javarant/DevRant.java +++ b/src/main/java/com/scorpiac/javarant/DevRant.java @@ -22,7 +22,7 @@ public class DevRant { private Auth auth; static { - INJECTOR = Guice.createInjector(new InjectionModule()); + INJECTOR = Guice.createInjector(); } public DevRant() { @@ -30,6 +30,11 @@ public DevRant() { feed = INJECTOR.getInstance(DevRantFeed.class); } + @Inject + void setRequestHandler(RequestHandler requestHandler) { + this.requestHandler = requestHandler; + } + public DevRantFeed getFeed() { return feed; } @@ -50,11 +55,6 @@ public Optional getRant(int id) { return Optional.of(response.get().getRant()); } - @Inject - void setRequestHandler(RequestHandler requestHandler) { - this.requestHandler = requestHandler; - } - /** * Log out of devRant. */ @@ -65,7 +65,7 @@ public void logout() { /** * Check whether a user is logged in. * - * @return {@code true} if a user is logged in. + * @return {@code true} if a user is logged in, or {@code false} otherwise. */ public boolean isLoggedIn() { return auth != null; diff --git a/src/main/java/com/scorpiac/javarant/InjectionModule.java b/src/main/java/com/scorpiac/javarant/InjectionModule.java deleted file mode 100644 index c2fb17e..0000000 --- a/src/main/java/com/scorpiac/javarant/InjectionModule.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.scorpiac.javarant; - -import com.google.inject.AbstractModule; - -class InjectionModule extends AbstractModule { - @Override - protected void configure() { - - } -} diff --git a/src/test/java/com/scorpiac/javarant/DevRantIT.java b/src/test/java/com/scorpiac/javarant/DevRantTest.java similarity index 98% rename from src/test/java/com/scorpiac/javarant/DevRantIT.java rename to src/test/java/com/scorpiac/javarant/DevRantTest.java index 210ab83..5746a34 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantTest.java @@ -7,7 +7,7 @@ import static org.testng.Assert.assertEquals; -public class DevRantIT { +public class DevRantTest { private DevRant devRant; @BeforeClass From 26387b3866b14cb684b4135018b1a7c27ff04860 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Thu, 31 Aug 2017 22:39:21 +0200 Subject: [PATCH 20/65] Fix all javadoc, add json example for test, redo Sort and Vote --- pom.xml | 4 +- .../java/com/scorpiac/javarant/Collab.java | 10 ++ .../java/com/scorpiac/javarant/DevRant.java | 4 + .../java/com/scorpiac/javarant/Image.java | 10 +- .../com/scorpiac/javarant/MinimalUser.java | 4 +- src/main/java/com/scorpiac/javarant/News.java | 12 +- .../java/com/scorpiac/javarant/Option.java | 25 ---- src/main/java/com/scorpiac/javarant/Rant.java | 6 + .../com/scorpiac/javarant/RantContent.java | 2 + src/main/java/com/scorpiac/javarant/Sort.java | 18 +-- src/main/java/com/scorpiac/javarant/User.java | 34 ++++-- src/main/java/com/scorpiac/javarant/Vote.java | 23 ++-- .../exceptions/NoSuchRantException.java | 3 +- .../javarant/services/LogFactory.java | 5 + .../services/ObjectMapperService.java | 2 + .../javarant/services/RequestHandler.java | 4 +- src/main/resources/rant-686001.json | 114 ++++++++++++++++++ .../{DevRantTest.java => DevRantITest.java} | 2 +- 18 files changed, 211 insertions(+), 71 deletions(-) delete mode 100644 src/main/java/com/scorpiac/javarant/Option.java create mode 100644 src/main/resources/rant-686001.json rename src/test/java/com/scorpiac/javarant/{DevRantTest.java => DevRantITest.java} (98%) diff --git a/pom.xml b/pom.xml index e84067a..b6dcaf3 100644 --- a/pom.xml +++ b/pom.xml @@ -124,7 +124,7 @@ - + + + + + release + + + release + true + + + + + + org.apache.maven.plugins + maven-gpg-plugin + ${maven.gpg.plugin.version} + + + sign-artifacts + verify + + sign + + + + + + + + From bac70bd6234775ebff2f4b8c99ed743c69c54183 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Thu, 31 Aug 2017 23:08:59 +0200 Subject: [PATCH 23/65] Also move sources and javadocs plugins to release profile --- pom.xml | 60 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/pom.xml b/pom.xml index 255b615..793d6ea 100644 --- a/pom.xml +++ b/pom.xml @@ -89,6 +89,7 @@ + ossrh @@ -111,39 +112,11 @@ ${java.version} - - org.apache.maven.plugins - maven-source-plugin - ${maven.source.plugin.version} - - - attach-sources - - jar-no-fork - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - ${maven.javadoc.plugin.version} - - - attach-javadocs - - jar - - - - - true - - + release @@ -154,6 +127,35 @@ + + org.apache.maven.plugins + maven-source-plugin + ${maven.source.plugin.version} + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + ${maven.javadoc.plugin.version} + + + attach-javadocs + + jar + + + + + true + + org.apache.maven.plugins maven-gpg-plugin From 1d4e8c07d90ad32f123cd23dccf3ba3e162a9de2 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Fri, 1 Sep 2017 00:10:00 +0200 Subject: [PATCH 24/65] Use Wiremock in integration test --- .../javarant/services/RequestHandler.java | 6 ++- .../com/scorpiac/javarant/DevRantITest.java | 48 ++++++++++++++++++- .../javarant/services/MockRequestHandler.java | 17 +++++++ src/{main => test}/resources/rant-686001.json | 0 4 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 src/test/java/com/scorpiac/javarant/services/MockRequestHandler.java rename src/{main => test}/resources/rant-686001.json (100%) diff --git a/src/main/java/com/scorpiac/javarant/services/RequestHandler.java b/src/main/java/com/scorpiac/javarant/services/RequestHandler.java index c43e4b0..30c9f22 100644 --- a/src/main/java/com/scorpiac/javarant/services/RequestHandler.java +++ b/src/main/java/com/scorpiac/javarant/services/RequestHandler.java @@ -64,7 +64,7 @@ private Request buildRequest(String endpoint, Function requestFunc try { // Build the URI. - uri = new URIBuilder(BASE_URI.resolve(endpoint)) + uri = new URIBuilder(resolve(endpoint)) .addParameters(getParameters(params)) .build(); } catch (URISyntaxException e) { @@ -78,6 +78,10 @@ private Request buildRequest(String endpoint, Function requestFunc .socketTimeout(timeout); } + URI resolve(String endpoint) { + return BASE_URI.resolve(endpoint); + } + /** * Handle a request. * diff --git a/src/test/java/com/scorpiac/javarant/DevRantITest.java b/src/test/java/com/scorpiac/javarant/DevRantITest.java index b693e5a..8e4c230 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantITest.java +++ b/src/test/java/com/scorpiac/javarant/DevRantITest.java @@ -1,23 +1,67 @@ package com.scorpiac.javarant; +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.MappingBuilder; +import com.scorpiac.javarant.services.MockRequestHandler; +import org.testng.annotations.AfterClass; +import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import java.io.IOException; import java.util.Arrays; +import java.util.Scanner; +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; import static org.testng.Assert.assertEquals; public class DevRantITest { private DevRant devRant; + private WireMockServer server; + @BeforeClass public void beforeClass() { - // TODO: use Wiremock. + server = new WireMockServer( + options() + .dynamicPort() + ); + server.start(); + devRant = new DevRant(); + devRant.setRequestHandler(new MockRequestHandler(server.port())); + } + + @AfterClass + public void afterClass() { + server.stop(); + } + + @AfterMethod + public void resetServer() { + server.resetAll(); + } + + private MappingBuilder stubResponse(MappingBuilder stub, String resource) throws IOException { + String responseString; + try (Scanner scanner = new Scanner(getClass().getResourceAsStream(resource))) { + responseString = scanner.useDelimiter("\\A").next(); + } + + return stub + .withQueryParam("app", equalTo("3")) + .withQueryParam("plat", equalTo("3")) + .willReturn(aResponse().withBody(responseString)); } @Test - public void testGetRant() { + public void testGetRant() throws IOException { + server.stubFor(stubResponse( + get(Endpoint.RANTS.toString() + '/' + 686001 + "?app=3&plat=3"), + "/rant-686001.json" + )); + Rant rant = devRant.getRant(686001).get(); assertEquals(rant.getId(), 686001); diff --git a/src/test/java/com/scorpiac/javarant/services/MockRequestHandler.java b/src/test/java/com/scorpiac/javarant/services/MockRequestHandler.java new file mode 100644 index 0000000..17de667 --- /dev/null +++ b/src/test/java/com/scorpiac/javarant/services/MockRequestHandler.java @@ -0,0 +1,17 @@ +package com.scorpiac.javarant.services; + +import java.net.URI; + +public class MockRequestHandler extends RequestHandler { + private final URI local; + + public MockRequestHandler(int port) { + super(new ObjectMapperResponseHandlerFactory(new ObjectMapperService())); + local = URI.create("http://localhost:" + port); + } + + @Override + URI resolve(String endpoint) { + return local.resolve(endpoint); + } +} diff --git a/src/main/resources/rant-686001.json b/src/test/resources/rant-686001.json similarity index 100% rename from src/main/resources/rant-686001.json rename to src/test/resources/rant-686001.json From 47799aca600760017baf94f3897c96957988c31f Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Fri, 1 Sep 2017 00:11:46 +0200 Subject: [PATCH 25/65] Remove query params from stub url --- src/test/java/com/scorpiac/javarant/DevRantITest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/scorpiac/javarant/DevRantITest.java b/src/test/java/com/scorpiac/javarant/DevRantITest.java index 8e4c230..45774fc 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantITest.java +++ b/src/test/java/com/scorpiac/javarant/DevRantITest.java @@ -58,7 +58,7 @@ private MappingBuilder stubResponse(MappingBuilder stub, String resource) throws @Test public void testGetRant() throws IOException { server.stubFor(stubResponse( - get(Endpoint.RANTS.toString() + '/' + 686001 + "?app=3&plat=3"), + get(urlPathEqualTo(Endpoint.RANTS.toString() + '/' + 686001)), "/rant-686001.json" )); From d3a6481eb5f6449563a9c3418ab3f39dbe342b3a Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Sat, 2 Sep 2017 09:47:17 +0200 Subject: [PATCH 26/65] Add getUser from id and username --- .../java/com/scorpiac/javarant/DevRant.java | 30 +-- .../java/com/scorpiac/javarant/Endpoint.java | 5 +- .../com/scorpiac/javarant/MinimalUser.java | 8 + src/main/java/com/scorpiac/javarant/User.java | 94 ++++++--- .../exceptions/NoSuchRantException.java | 13 -- .../exceptions/NoSuchUserException.java | 21 -- .../exceptions/NotACollabException.java | 12 -- .../javarant/responses/RantResponse.java | 12 +- .../javarant/responses/RantsResponse.java | 2 + .../scorpiac/javarant/responses/Response.java | 4 + .../javarant/responses/UserIdResponse.java | 12 ++ .../javarant/responses/UserResponse.java | 15 ++ .../com/scorpiac/javarant/DevRantITest.java | 45 ++++- src/test/resources/rant-invalid.json | 4 + src/test/resources/user-102959.json | 181 ++++++++++++++++++ src/test/resources/user-id-LucaScorpion.json | 4 + 16 files changed, 368 insertions(+), 94 deletions(-) delete mode 100644 src/main/java/com/scorpiac/javarant/exceptions/NoSuchRantException.java delete mode 100644 src/main/java/com/scorpiac/javarant/exceptions/NoSuchUserException.java delete mode 100644 src/main/java/com/scorpiac/javarant/exceptions/NotACollabException.java create mode 100644 src/main/java/com/scorpiac/javarant/responses/UserIdResponse.java create mode 100644 src/main/java/com/scorpiac/javarant/responses/UserResponse.java create mode 100644 src/test/resources/rant-invalid.json create mode 100644 src/test/resources/user-102959.json create mode 100644 src/test/resources/user-id-LucaScorpion.json diff --git a/src/main/java/com/scorpiac/javarant/DevRant.java b/src/main/java/com/scorpiac/javarant/DevRant.java index 4039799..f8ba35a 100644 --- a/src/main/java/com/scorpiac/javarant/DevRant.java +++ b/src/main/java/com/scorpiac/javarant/DevRant.java @@ -2,9 +2,11 @@ import com.google.inject.Guice; import com.google.inject.Injector; -import com.scorpiac.javarant.exceptions.NoSuchRantException; import com.scorpiac.javarant.responses.RantResponse; +import com.scorpiac.javarant.responses.UserIdResponse; +import com.scorpiac.javarant.responses.UserResponse; import com.scorpiac.javarant.services.RequestHandler; +import org.apache.http.message.BasicNameValuePair; import javax.inject.Inject; import java.util.Optional; @@ -40,23 +42,23 @@ public DevRantFeed getFeed() { } public Optional getRant(int id) { - Optional response = requestHandler.get(Endpoint.RANTS.toString() + '/' + id, RantResponse.class); - - // Check if there is a response. - if (!response.isPresent()) { - return Optional.empty(); - } - - // Check for success. - if (!response.get().isSuccess()) { - throw new NoSuchRantException(id, response.get().getError()); - } + return requestHandler.get(Endpoint.RANTS.toString() + '/' + id, RantResponse.class) + .flatMap(RantResponse::getRant); + } - return Optional.of(response.get().getRant()); + public Optional getUser(String username) { + return requestHandler.get(Endpoint.USER_ID, UserIdResponse.class, new BasicNameValuePair("username", username)) + .flatMap(u -> getUser(u.getId())); } public Optional getUser(int id) { - throw new IllegalStateException("This method is not yet implemented."); + Optional user = requestHandler.get(Endpoint.USERS.toString() + '/' + id, UserResponse.class) + .flatMap(UserResponse::getUser); + + // Set the id, as that is not part of the response. + user.ifPresent(u -> u.setId(id)); + + return user; } /** diff --git a/src/main/java/com/scorpiac/javarant/Endpoint.java b/src/main/java/com/scorpiac/javarant/Endpoint.java index 8c76cec..7919cfa 100644 --- a/src/main/java/com/scorpiac/javarant/Endpoint.java +++ b/src/main/java/com/scorpiac/javarant/Endpoint.java @@ -3,7 +3,10 @@ public enum Endpoint { API("/api"), API_DEVRANT(API, "devrant"), - RANTS(API_DEVRANT, "rants"); + USER_ID(API, "get-user-id"), + USERS(API, "users"), + RANTS(API_DEVRANT, "rants") + ; private final String endpoint; diff --git a/src/main/java/com/scorpiac/javarant/MinimalUser.java b/src/main/java/com/scorpiac/javarant/MinimalUser.java index 586515f..7e0474a 100644 --- a/src/main/java/com/scorpiac/javarant/MinimalUser.java +++ b/src/main/java/com/scorpiac/javarant/MinimalUser.java @@ -1,12 +1,16 @@ package com.scorpiac.javarant; +import com.fasterxml.jackson.annotation.JsonProperty; import com.scorpiac.javarant.services.RequestHandler; import java.net.URI; public class MinimalUser { + @JsonProperty private int id; + @JsonProperty private String username; + @JsonProperty private int score; static MinimalUser create(int id, String username, int score) { @@ -17,6 +21,10 @@ static MinimalUser create(int id, String username, int score) { return user; } + void setId(int id) { + this.id = id; + } + @Override public boolean equals(Object obj) { return obj instanceof MinimalUser && ((MinimalUser) obj).getId() == id; diff --git a/src/main/java/com/scorpiac/javarant/User.java b/src/main/java/com/scorpiac/javarant/User.java index 4b605d1..1d5e3ab 100644 --- a/src/main/java/com/scorpiac/javarant/User.java +++ b/src/main/java/com/scorpiac/javarant/User.java @@ -1,34 +1,23 @@ package com.scorpiac.javarant; -import com.scorpiac.javarant.services.RequestHandler; +import com.fasterxml.jackson.annotation.JsonProperty; -import java.net.URI; import java.util.Collections; import java.util.List; public class User extends MinimalUser { + @JsonProperty private String about; + @JsonProperty private String location; + @JsonProperty private String skills; + @JsonProperty private String github; - private List rants; - private List upvoted; - private List comments; - private List favorites; - private int rantsCount; - private int upvotedCount; - private int commentsCount; - private int favoritesCount; - private String avatar; - - /** - * Get the link to the user's avatar. - * - * @return A link to the avatar. - */ - public URI avatarLink() { - return RequestHandler.AVATARS_URI.resolve(avatar); - } + @JsonProperty + private String website; + @JsonProperty + private UserContent content; /** * Get information about the user. @@ -66,13 +55,22 @@ public String getGithub() { return github; } + /** + * Get the user's website. + * + * @return The website. + */ + public String getWebsite() { + return website; + } + /** * Get the rants that this user posted. * * @return The posted rants. */ public List getRants() { - return Collections.unmodifiableList(rants); + return Collections.unmodifiableList(content.content.rants); } /** @@ -81,7 +79,7 @@ public List getRants() { * @return The upvoted rants. */ public List getUpvoted() { - return Collections.unmodifiableList(upvoted); + return Collections.unmodifiableList(content.content.upvoted); } /** @@ -90,7 +88,7 @@ public List getUpvoted() { * @return The posted comments. */ public List getComments() { - return Collections.unmodifiableList(comments); + return Collections.unmodifiableList(content.content.comments); } /** @@ -99,7 +97,7 @@ public List getComments() { * @return The favorite rants. */ public List getFavorites() { - return Collections.unmodifiableList(favorites); + return Collections.unmodifiableList(content.content.favorites); } /** @@ -108,7 +106,7 @@ public List getFavorites() { * @return The amount of posted rants. */ public int getRantsCount() { - return rantsCount; + return content.counts.rants; } /** @@ -117,7 +115,7 @@ public int getRantsCount() { * @return The amount of upvoted rants. */ public int getUpvotedCount() { - return upvotedCount; + return content.counts.upvoted; } /** @@ -126,7 +124,7 @@ public int getUpvotedCount() { * @return The amount of posted comments. */ public int getCommentsCount() { - return commentsCount; + return content.counts.comments; } /** @@ -135,6 +133,46 @@ public int getCommentsCount() { * @return The amount of favorite rants. */ public int getFavoritesCount() { - return favoritesCount; + return content.counts.favorites; + } + + /** + * Get the amount of collabs that this user has posted. + * + * @return The amount of posted collabs. + */ + public int getCollabsCount() { + return content.counts.collabs; + } + + private static class UserContent { + @JsonProperty + private Content content; + @JsonProperty + private Counts counts; + } + + private static class Content { + @JsonProperty + private List rants; + @JsonProperty + private List upvoted; + @JsonProperty + private List comments; + @JsonProperty + private List favorites; + } + + private static class Counts { + @JsonProperty + private int rants; + @JsonProperty + private int upvoted; + @JsonProperty + private int comments; + @JsonProperty + private int favorites; + @JsonProperty + private int collabs; } } diff --git a/src/main/java/com/scorpiac/javarant/exceptions/NoSuchRantException.java b/src/main/java/com/scorpiac/javarant/exceptions/NoSuchRantException.java deleted file mode 100644 index 5a98254..0000000 --- a/src/main/java/com/scorpiac/javarant/exceptions/NoSuchRantException.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.scorpiac.javarant.exceptions; - -public class NoSuchRantException extends DevRantApiException { - /** - * Create a new exception for a non-existent rant. - * - * @param id The id of the rant. - * @param internalError The internal error. - */ - public NoSuchRantException(int id, String internalError) { - super("Rant with id " + id + " does not exist.", internalError); - } -} diff --git a/src/main/java/com/scorpiac/javarant/exceptions/NoSuchUserException.java b/src/main/java/com/scorpiac/javarant/exceptions/NoSuchUserException.java deleted file mode 100644 index da5b866..0000000 --- a/src/main/java/com/scorpiac/javarant/exceptions/NoSuchUserException.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.scorpiac.javarant.exceptions; - -public class NoSuchUserException extends RuntimeException { - /** - * Create a new exception for a non-existent user. - * - * @param username The username of the user. - */ - public NoSuchUserException(String username) { - super("User \"" + username + "\" does not exist."); - } - - /** - * Create a new exception for a non-existent user. - * - * @param id The id of the user. - */ - public NoSuchUserException(int id) { - super("User with id " + id + " does not exist."); - } -} diff --git a/src/main/java/com/scorpiac/javarant/exceptions/NotACollabException.java b/src/main/java/com/scorpiac/javarant/exceptions/NotACollabException.java deleted file mode 100644 index 23bb3a9..0000000 --- a/src/main/java/com/scorpiac/javarant/exceptions/NotACollabException.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.scorpiac.javarant.exceptions; - -public class NotACollabException extends RuntimeException { - /** - * Create a new exception for a rant that is not a collab. - * - * @param id The id of the rant. - */ - public NotACollabException(int id) { - super("Rant with id " + id + " is not a collab."); - } -} diff --git a/src/main/java/com/scorpiac/javarant/responses/RantResponse.java b/src/main/java/com/scorpiac/javarant/responses/RantResponse.java index dd59cdb..2697076 100644 --- a/src/main/java/com/scorpiac/javarant/responses/RantResponse.java +++ b/src/main/java/com/scorpiac/javarant/responses/RantResponse.java @@ -6,13 +6,19 @@ import java.lang.reflect.Field; import java.util.List; +import java.util.Optional; public class RantResponse extends Response { + @JsonProperty private Rant rant; - @JsonProperty("comments") + @JsonProperty private List comments; - public Rant getRant() { + public Optional getRant() { + if (!isSuccess()) { + return Optional.empty(); + } + // Get the comments field. Field commentsField; try { @@ -29,6 +35,6 @@ public Rant getRant() { throw new IllegalStateException(e); // TODO } - return rant; + return Optional.of(rant); } } diff --git a/src/main/java/com/scorpiac/javarant/responses/RantsResponse.java b/src/main/java/com/scorpiac/javarant/responses/RantsResponse.java index 8915cce..81031f5 100644 --- a/src/main/java/com/scorpiac/javarant/responses/RantsResponse.java +++ b/src/main/java/com/scorpiac/javarant/responses/RantsResponse.java @@ -1,10 +1,12 @@ package com.scorpiac.javarant.responses; +import com.fasterxml.jackson.annotation.JsonProperty; import com.scorpiac.javarant.Rant; import java.util.List; public class RantsResponse extends Response { + @JsonProperty private List rants; public List getRants() { diff --git a/src/main/java/com/scorpiac/javarant/responses/Response.java b/src/main/java/com/scorpiac/javarant/responses/Response.java index 130695f..8244830 100644 --- a/src/main/java/com/scorpiac/javarant/responses/Response.java +++ b/src/main/java/com/scorpiac/javarant/responses/Response.java @@ -1,7 +1,11 @@ package com.scorpiac.javarant.responses; +import com.fasterxml.jackson.annotation.JsonProperty; + class Response { + @JsonProperty private boolean success; + @JsonProperty private String error; public boolean isSuccess() { diff --git a/src/main/java/com/scorpiac/javarant/responses/UserIdResponse.java b/src/main/java/com/scorpiac/javarant/responses/UserIdResponse.java new file mode 100644 index 0000000..5d8a6b1 --- /dev/null +++ b/src/main/java/com/scorpiac/javarant/responses/UserIdResponse.java @@ -0,0 +1,12 @@ +package com.scorpiac.javarant.responses; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class UserIdResponse extends Response { + @JsonProperty("user_id") + private int id; + + public int getId() { + return id; + } +} diff --git a/src/main/java/com/scorpiac/javarant/responses/UserResponse.java b/src/main/java/com/scorpiac/javarant/responses/UserResponse.java new file mode 100644 index 0000000..4ac1444 --- /dev/null +++ b/src/main/java/com/scorpiac/javarant/responses/UserResponse.java @@ -0,0 +1,15 @@ +package com.scorpiac.javarant.responses; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.scorpiac.javarant.User; + +import java.util.Optional; + +public class UserResponse extends Response { + @JsonProperty + private User profile; + + public Optional getUser() { + return Optional.ofNullable(profile); + } +} diff --git a/src/test/java/com/scorpiac/javarant/DevRantITest.java b/src/test/java/com/scorpiac/javarant/DevRantITest.java index 45774fc..ae29c59 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantITest.java +++ b/src/test/java/com/scorpiac/javarant/DevRantITest.java @@ -15,10 +15,10 @@ import static com.github.tomakehurst.wiremock.client.WireMock.*; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; public class DevRantITest { private DevRant devRant; - private WireMockServer server; @BeforeClass @@ -58,7 +58,7 @@ private MappingBuilder stubResponse(MappingBuilder stub, String resource) throws @Test public void testGetRant() throws IOException { server.stubFor(stubResponse( - get(urlPathEqualTo(Endpoint.RANTS.toString() + '/' + 686001)), + get(urlPathEqualTo(Endpoint.RANTS.toString() + "/686001")), "/rant-686001.json" )); @@ -85,4 +85,45 @@ public void testGetRant() throws IOException { assertEquals(rant.getUser().getUsername(), "LucaScorpion"); assertEquals(rant.getUser().getScore(), 3831); } + + @Test + public void testGetInvalidRant() throws IOException { + server.stubFor(stubResponse( + get(urlPathEqualTo(Endpoint.RANTS.toString() + "/0")), + "/rant-invalid.json" + )); + + assertFalse(devRant.getRant(0).isPresent()); + } + + @Test + public void testGetUserByUsername() throws IOException { + server.stubFor(stubResponse( + get(urlPathEqualTo(Endpoint.USER_ID.toString())) + .withQueryParam("username", equalTo("LucaScorpion")), + "/user-id-LucaScorpion.json" + )); + server.stubFor(stubResponse( + get(urlPathEqualTo(Endpoint.USERS.toString() + "/102959")), + "/user-102959.json" + )); + + User user = devRant.getUser("LucaScorpion").get(); + + assertEquals(user.getId(), 102959); + assertEquals(user.getUsername(), "LucaScorpion"); + assertEquals(user.getScore(), 3831); + assertEquals(user.getAbout(), "Software developer, fanatic programmer, hardcore gamer, Linux lover."); + assertEquals(user.getLocation(), "Netherlands"); + assertEquals(user.getSkills(), "C#, Java, PHP, Javascript, HTML, CSS, SQL, C++ (Arduino), Bash"); + assertEquals(user.getGithub(), "LucaScorpion"); + assertEquals(user.getWebsite(), "https://scorpiac.com"); + + // Counts. + assertEquals(user.getRantsCount(), 60); + assertEquals(user.getUpvotedCount(), 5103); + assertEquals(user.getCommentsCount(), 800); + assertEquals(user.getFavoritesCount(), 36); + assertEquals(user.getCollabsCount(), 0); + } } diff --git a/src/test/resources/rant-invalid.json b/src/test/resources/rant-invalid.json new file mode 100644 index 0000000..a0a2399 --- /dev/null +++ b/src/test/resources/rant-invalid.json @@ -0,0 +1,4 @@ +{ + "success": false, + "error": "Invalid rant specified in path." +} \ No newline at end of file diff --git a/src/test/resources/user-102959.json b/src/test/resources/user-102959.json new file mode 100644 index 0000000..4795e91 --- /dev/null +++ b/src/test/resources/user-102959.json @@ -0,0 +1,181 @@ +{ + "success": true, + "profile": { + "username": "LucaScorpion", + "score": 3831, + "about": "Software developer, fanatic programmer, hardcore gamer, Linux lover.", + "location": "Netherlands", + "created_time": 1468222507, + "skills": "C#, Java, PHP, Javascript, HTML, CSS, SQL, C++ (Arduino), Bash", + "github": "LucaScorpion", + "website": "https://scorpiac.com", + "content": { + "content": { + "rants": [ + { + "id": 734149, + "text": "Learning something new every day! This is pretty cool.", + "score": 27, + "created_time": 1500923505, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_734149_V8bT3.gif", + "width": 600, + "height": 400, + "frame": "https://img.devrant.io/devrant/rant/frame_r_734149_V8bT3.jpg" + }, + "num_comments": 13, + "tags": [ + "bash", + "linux" + ], + "vote_state": 0, + "edited": false, + "user_id": 102959, + "user_username": "LucaScorpion", + "user_score": 3831, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-2_16-15_3-3_8-3_7-3_5-3_12-2_6-36_10-9_2-36_15-20_18-4_4-3_19-3_20-6_21-2.jpg" + }, + "user_dpp": 1 + }, + { + "id": 731665, + "text": "I'm such a fucking idiot.", + "score": 83, + "created_time": 1500824167, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_731665_BoGHq.jpg", + "width": 505, + "height": 476 + }, + "num_comments": 22, + "tags": [ + "rm -rf", + "linux", + "fuck" + ], + "vote_state": 0, + "edited": false, + "user_id": 102959, + "user_username": "LucaScorpion", + "user_score": 3831, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-2_16-15_3-3_8-3_7-3_5-3_12-2_6-36_10-9_2-36_15-20_18-4_4-3_19-3_20-6_21-2.jpg" + }, + "user_dpp": 1 + } + ], + "upvoted": [ + { + "id": 814409, + "text": "Tomorrow I will be on a long train trip again so here goes!\n\nMy last train project is http://jsrant.com and people seem to enjoy it. Every time I am mentioned in a rant related to it people also mention the idea of a similar application but for in the terminal. So I intend to build that tomorrow.\n\nTo build the best thing for you I want to ask you some questions:\n\n- What operating system are you running?\n\n- Why (or how) would you like to use a devrant terminal reader?\n\n- Why would you NOT want to use a devrant terminal reader?\n\n- Would your use-case required obfuscated output? (Hiding it from someone)\n\n- If so, what formats do you use on a daily basis or are you most comfortable with?\n\n- Anything else you would like to mention or for me to consider?\n\nI will be developing the larger part of this tomorrow, but the sources will be made available to the public.", + "score": 4, + "created_time": 1504331967, + "attached_image": "", + "num_comments": 3, + "tags": [ + "coverrant", + "rantfuscator", + "rantvelop", + "rantinator", + "names are hard" + ], + "vote_state": 0, + "edited": false, + "links": [ + { + "type": "url", + "url": "http://jsrant.com", + "short_url": "http://jsrant.com", + "title": "http://jsrant.com", + "start": 86, + "end": 103, + "special": 1 + } + ], + "special": true, + "user_id": 99671, + "user_username": "ChappIO", + "user_score": 4177, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-3_3-1_8-4_7-4_5-3_12-1_6-4_2-92_15-16_18-4_4-3_19-3_20-7_21-2.jpg" + }, + "user_dpp": 1 + }, + { + "id": 805044, + "text": "There was this project where a bunch a scripts had been running for three weeks analysing a bunch of fileshares. The project was in overrun and the analysis wasn't anywhere near done. \n\nI was given complete isolation and a team of people who were instructed to do anything I needed. I had them replicate the data to as many machines as possible and I started scripting the analysis with some sample data. After half a day of collecting laptops, desktops and severs I transfered my scripts to those machines and ran the analysis in 5 hours. \n\nI felt like I saved the project.", + "score": 10, + "created_time": 1503929002, + "attached_image": "", + "num_comments": 0, + "tags": [ + "late to the party", + "wk65" + ], + "vote_state": 0, + "edited": false, + "user_id": 99671, + "user_username": "ChappIO", + "user_score": 4177, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-3_3-1_8-4_7-4_5-3_12-1_6-4_2-92_15-16_18-4_4-3_19-3_20-7_21-2.jpg" + }, + "user_dpp": 1 + } + ], + "comments": [ + { + "id": 769281, + "rant_id": 768728, + "body": "Another option is put comments a field that won't be deserialized. But either way it's not a good idea.", + "score": 0, + "created_time": 1502451860, + "vote_state": 0, + "user_id": 102959, + "user_username": "LucaScorpion", + "user_score": 3831, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-2_16-15_3-3_8-3_7-3_5-3_12-2_6-36_10-9_2-36_15-20_18-4_4-3_19-3_20-6_21-2.jpg" + }, + "user_dpp": 1 + }, + { + "id": 769277, + "rant_id": 769071, + "body": "Code as if your application is going to be used for years.", + "score": 7, + "created_time": 1502451709, + "vote_state": 0, + "user_id": 102959, + "user_username": "LucaScorpion", + "user_score": 3831, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-2_16-15_3-3_8-3_7-3_5-3_12-2_6-36_10-9_2-36_15-20_18-4_4-3_19-3_20-6_21-2.jpg" + }, + "user_dpp": 1 + } + ], + "favorites": [] + }, + "counts": { + "rants": 60, + "upvoted": 5103, + "comments": 800, + "favorites": 36, + "collabs": 0 + } + }, + "avatar": { + "b": "7bc8a4", + "i": "v-17_c-1_b-1_g-m_9-1_1-2_16-15_3-3_8-3_7-3_5-3_12-2_6-36_10-9_2-36_15-20_18-4_4-3_19-3_20-6_21-2.png" + }, + "dpp": 1 + } +} \ No newline at end of file diff --git a/src/test/resources/user-id-LucaScorpion.json b/src/test/resources/user-id-LucaScorpion.json new file mode 100644 index 0000000..1254b83 --- /dev/null +++ b/src/test/resources/user-id-LucaScorpion.json @@ -0,0 +1,4 @@ +{ + "success": true, + "user_id": 102959 +} \ No newline at end of file From 688572069914c4ed21561cdaa9c4b5d00e0c9e5d Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Sat, 2 Sep 2017 09:50:07 +0200 Subject: [PATCH 27/65] Move integration tests to verify phase --- pom.xml | 16 ++++++++++++++++ .../{DevRantITest.java => DevRantIT.java} | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) rename src/test/java/com/scorpiac/javarant/{DevRantITest.java => DevRantIT.java} (99%) diff --git a/pom.xml b/pom.xml index 793d6ea..078a2eb 100644 --- a/pom.xml +++ b/pom.xml @@ -36,6 +36,7 @@ 1.8 UTF-8 + UTF-8 2.8.9 @@ -50,6 +51,7 @@ 3.0.1 2.10.4 1.5 + 2.20 @@ -112,6 +114,20 @@ ${java.version} + + org.apache.maven.plugins + maven-failsafe-plugin + ${maven.failsafe.plugin.version} + + + integration-tests + + integration-test + verify + + + + diff --git a/src/test/java/com/scorpiac/javarant/DevRantITest.java b/src/test/java/com/scorpiac/javarant/DevRantIT.java similarity index 99% rename from src/test/java/com/scorpiac/javarant/DevRantITest.java rename to src/test/java/com/scorpiac/javarant/DevRantIT.java index ae29c59..73b2556 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantITest.java +++ b/src/test/java/com/scorpiac/javarant/DevRantIT.java @@ -17,7 +17,7 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; -public class DevRantITest { +public class DevRantIT { private DevRant devRant; private WireMockServer server; From b09b46e3adad83e35e7e23e8280a51a86498bdc5 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Sat, 2 Sep 2017 10:23:02 +0200 Subject: [PATCH 28/65] Add rant feed --- .../com/scorpiac/javarant/DevRantFeed.java | 10 ++- src/main/java/com/scorpiac/javarant/News.java | 26 ++++++ .../java/com/scorpiac/javarant/Range.java | 5 +- src/main/java/com/scorpiac/javarant/Sort.java | 7 +- .../javarant/responses/RantsResponse.java | 3 + .../com/scorpiac/javarant/DevRantFeedIT.java | 25 ++++++ .../java/com/scorpiac/javarant/DevRantIT.java | 47 +---------- .../com/scorpiac/javarant/DevRantTest.java | 17 ++++ .../java/com/scorpiac/javarant/ITHelper.java | 54 ++++++++++++ src/test/resources/feed-rants.json | 84 +++++++++++++++++++ 10 files changed, 228 insertions(+), 50 deletions(-) create mode 100644 src/test/java/com/scorpiac/javarant/DevRantFeedIT.java create mode 100644 src/test/java/com/scorpiac/javarant/DevRantTest.java create mode 100644 src/test/java/com/scorpiac/javarant/ITHelper.java create mode 100644 src/test/resources/feed-rants.json diff --git a/src/main/java/com/scorpiac/javarant/DevRantFeed.java b/src/main/java/com/scorpiac/javarant/DevRantFeed.java index 88e9834..b336f96 100644 --- a/src/main/java/com/scorpiac/javarant/DevRantFeed.java +++ b/src/main/java/com/scorpiac/javarant/DevRantFeed.java @@ -2,6 +2,7 @@ import com.scorpiac.javarant.responses.RantsResponse; import com.scorpiac.javarant.services.RequestHandler; +import org.apache.http.message.BasicNameValuePair; import javax.inject.Inject; import java.util.List; @@ -15,7 +16,12 @@ public class DevRantFeed { this.requestHandler = requestHandler; } - public Optional> getRants() { - return requestHandler.get(Endpoint.RANTS, RantsResponse.class).map(RantsResponse::getRants); + public Optional> getRants(Sort sort, int limit, int skip) { + return requestHandler.get(Endpoint.RANTS, RantsResponse.class, + new BasicNameValuePair("sort", sort.toString()), + new BasicNameValuePair("limit", String.valueOf(limit)), + new BasicNameValuePair("skip", String.valueOf(skip)) + ) + .map(RantsResponse::getRants); } } diff --git a/src/main/java/com/scorpiac/javarant/News.java b/src/main/java/com/scorpiac/javarant/News.java index 7c22c5d..af2e138 100644 --- a/src/main/java/com/scorpiac/javarant/News.java +++ b/src/main/java/com/scorpiac/javarant/News.java @@ -1,8 +1,15 @@ package com.scorpiac.javarant; +import com.fasterxml.jackson.annotation.JsonProperty; + public class News { + @JsonProperty + private int id; + @JsonProperty private String headline; + @JsonProperty private String body; + @JsonProperty private String footer; @Override @@ -10,6 +17,25 @@ public String toString() { return headline + '\n' + body + '\n' + footer; } + @Override + public int hashCode() { + return id; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof News && ((News) obj).id == id; + } + + /** + * Get the id. + * + * @return The id. + */ + public int getId() { + return id; + } + /** * Get the headline. * diff --git a/src/main/java/com/scorpiac/javarant/Range.java b/src/main/java/com/scorpiac/javarant/Range.java index b7b5495..0eb9f47 100644 --- a/src/main/java/com/scorpiac/javarant/Range.java +++ b/src/main/java/com/scorpiac/javarant/Range.java @@ -1,7 +1,10 @@ package com.scorpiac.javarant; public enum Range { - DAY, WEEK, MONTH, ALL; + DAY, + WEEK, + MONTH, + ALL; @Override public String toString() { diff --git a/src/main/java/com/scorpiac/javarant/Sort.java b/src/main/java/com/scorpiac/javarant/Sort.java index 6db12ab..f068a54 100644 --- a/src/main/java/com/scorpiac/javarant/Sort.java +++ b/src/main/java/com/scorpiac/javarant/Sort.java @@ -3,5 +3,10 @@ public enum Sort { ALGO, RECENT, - TOP + TOP; + + @Override + public String toString() { + return name().toLowerCase(); + } } diff --git a/src/main/java/com/scorpiac/javarant/responses/RantsResponse.java b/src/main/java/com/scorpiac/javarant/responses/RantsResponse.java index 81031f5..d257be3 100644 --- a/src/main/java/com/scorpiac/javarant/responses/RantsResponse.java +++ b/src/main/java/com/scorpiac/javarant/responses/RantsResponse.java @@ -1,6 +1,7 @@ package com.scorpiac.javarant.responses; import com.fasterxml.jackson.annotation.JsonProperty; +import com.scorpiac.javarant.News; import com.scorpiac.javarant.Rant; import java.util.List; @@ -8,6 +9,8 @@ public class RantsResponse extends Response { @JsonProperty private List rants; + @JsonProperty + private News news; public List getRants() { return rants; diff --git a/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java b/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java new file mode 100644 index 0000000..eb91aa1 --- /dev/null +++ b/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java @@ -0,0 +1,25 @@ +package com.scorpiac.javarant; + +import org.testng.annotations.Test; + +import java.io.IOException; +import java.util.List; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static org.testng.Assert.assertEquals; + +public class DevRantFeedIT extends ITHelper { + @Test + public void testGetRants() throws IOException { + server.stubFor(stubResponse( + get(urlPathEqualTo(Endpoint.RANTS.toString())) + .withQueryParam("limit", equalTo("4")) + .withQueryParam("skip", equalTo("1")) + .withQueryParam("sort", equalTo("recent")), + "/feed-rants.json" + )); + + List rants = devRant.getFeed().getRants(Sort.RECENT, 4, 1).get(); + assertEquals(rants.size(), 4); + } +} diff --git a/src/test/java/com/scorpiac/javarant/DevRantIT.java b/src/test/java/com/scorpiac/javarant/DevRantIT.java index 73b2556..766a6e0 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantIT.java @@ -1,60 +1,15 @@ package com.scorpiac.javarant; -import com.github.tomakehurst.wiremock.WireMockServer; -import com.github.tomakehurst.wiremock.client.MappingBuilder; -import com.scorpiac.javarant.services.MockRequestHandler; -import org.testng.annotations.AfterClass; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.io.IOException; import java.util.Arrays; -import java.util.Scanner; import static com.github.tomakehurst.wiremock.client.WireMock.*; -import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; -public class DevRantIT { - private DevRant devRant; - private WireMockServer server; - - @BeforeClass - public void beforeClass() { - server = new WireMockServer( - options() - .dynamicPort() - ); - server.start(); - - devRant = new DevRant(); - devRant.setRequestHandler(new MockRequestHandler(server.port())); - } - - @AfterClass - public void afterClass() { - server.stop(); - } - - @AfterMethod - public void resetServer() { - server.resetAll(); - } - - private MappingBuilder stubResponse(MappingBuilder stub, String resource) throws IOException { - String responseString; - try (Scanner scanner = new Scanner(getClass().getResourceAsStream(resource))) { - responseString = scanner.useDelimiter("\\A").next(); - } - - return stub - .withQueryParam("app", equalTo("3")) - .withQueryParam("plat", equalTo("3")) - .willReturn(aResponse().withBody(responseString)); - } - +public class DevRantIT extends ITHelper { @Test public void testGetRant() throws IOException { server.stubFor(stubResponse( diff --git a/src/test/java/com/scorpiac/javarant/DevRantTest.java b/src/test/java/com/scorpiac/javarant/DevRantTest.java new file mode 100644 index 0000000..6bdd5bd --- /dev/null +++ b/src/test/java/com/scorpiac/javarant/DevRantTest.java @@ -0,0 +1,17 @@ +package com.scorpiac.javarant; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; + +public class DevRantTest { + @Test + public void testInit() { + DevRant devRant = new DevRant(); + + assertNotNull(devRant.getFeed()); + assertFalse(devRant.isLoggedIn()); + devRant.logout(); + } +} diff --git a/src/test/java/com/scorpiac/javarant/ITHelper.java b/src/test/java/com/scorpiac/javarant/ITHelper.java new file mode 100644 index 0000000..4c53c44 --- /dev/null +++ b/src/test/java/com/scorpiac/javarant/ITHelper.java @@ -0,0 +1,54 @@ +package com.scorpiac.javarant; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.MappingBuilder; +import com.scorpiac.javarant.services.MockRequestHandler; +import org.testng.annotations.AfterClass; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeClass; + +import java.io.IOException; +import java.util.Scanner; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; + +public class ITHelper { + protected WireMockServer server; + protected DevRant devRant; + + @BeforeClass + public void beforeClass() { + server = new WireMockServer( + options() + .dynamicPort() + ); + server.start(); + + devRant = new DevRant(); + devRant.setRequestHandler(new MockRequestHandler(server.port())); + } + + @AfterClass + public void stopServer() { + server.stop(); + } + + @AfterMethod + public void resetServer() { + server.resetAll(); + } + + protected MappingBuilder stubResponse(MappingBuilder stub, String resource) throws IOException { + String responseString; + try (Scanner scanner = new Scanner(getClass().getResourceAsStream(resource))) { + responseString = scanner.useDelimiter("\\A").next(); + } + + return stub + .withQueryParam("app", equalTo("3")) + .withQueryParam("plat", equalTo("3")) + .willReturn(aResponse().withBody(responseString)); + } +} diff --git a/src/test/resources/feed-rants.json b/src/test/resources/feed-rants.json new file mode 100644 index 0000000..1ae3a2f --- /dev/null +++ b/src/test/resources/feed-rants.json @@ -0,0 +1,84 @@ +{ + "success": true, + "rants": [ + { + "id": 814524, + "text": "Too real...", + "score": 1, + "created_time": 1504339145, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_814524_mSXAP.jpg", + "width": 799, + "height": 336 + }, + "num_comments": 0, + "tags": [], + "vote_state": 0, + "edited": false, + "user_id": 759598, + "user_username": "bleachedsleet", + "user_score": 1, + "user_avatar": { + "b": "7bc8a4" + } + }, + { + "id": 814516, + "text": "Windows Photo Viewer doesn't support GIF animation. Great!", + "score": 1, + "created_time": 1504338721, + "attached_image": "", + "num_comments": 0, + "tags": [], + "vote_state": 0, + "edited": false, + "user_id": 268318, + "user_username": "error503", + "user_score": 9347, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-15_3-11_8-3_7-3_5-4_12-1_6-97_2-1_15-14_11-2_4-4_19-3_20-9.jpg" + } + }, + { + "id": 814472, + "text": "Installed an SSD in my Linux box. Installed fresh distro, tried to log in via SSH on localhost. Didn't work. Tried like three times, turned off firewalls, restarted ssh servers, nothing.\n\nLooked at username. Typo in username when setting things up. *facepalm*", + "score": 4, + "created_time": 1504335825, + "attached_image": "", + "num_comments": 1, + "tags": [ + "facepalm" + ], + "vote_state": 0, + "edited": false, + "user_id": 410678, + "user_username": "ytho", + "user_score": 714, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-3_16-6_3-6_8-1_7-1_5-1_12-3_6-12_10-3_2-86_18-2_4-1_19-1.jpg" + } + }, + { + "id": 814432, + "text": "Why the fuck are people word fighting what should they drink tea or coffee.\nFrom chemical look it doesn't matter you are still getting coffein with tea just in smaller quantity.\nSo it doesn't matter I drink both when I'm OK but need small bzzzz then tea and when I need to stop sleeping then coffee.", + "score": 2, + "created_time": 1504333286, + "attached_image": "", + "num_comments": 4, + "tags": [], + "vote_state": 0, + "edited": false, + "user_id": 19218, + "user_username": "Haxk20", + "user_score": 6459, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-2_16-13_3-3_8-4_7-4_5-4_12-2_6-3_10-2_2-41_18-4_4-4_19-2_20-4_21-1.jpg" + } + } + ], + "settings": [], + "set": "59aa68e8cdfe8" +} \ No newline at end of file From 33b3a49832a5f4a860aa1926b2493be571daad1e Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Mon, 4 Sep 2017 17:31:33 +0200 Subject: [PATCH 29/65] Expose timeout, improve javadoc --- .../java/com/scorpiac/javarant/DevRant.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/main/java/com/scorpiac/javarant/DevRant.java b/src/main/java/com/scorpiac/javarant/DevRant.java index f8ba35a..5829e94 100644 --- a/src/main/java/com/scorpiac/javarant/DevRant.java +++ b/src/main/java/com/scorpiac/javarant/DevRant.java @@ -41,16 +41,34 @@ public DevRantFeed getFeed() { return feed; } + /** + * Get a rant. + * + * @param id The id of the rant. + * @return The rant. + */ public Optional getRant(int id) { return requestHandler.get(Endpoint.RANTS.toString() + '/' + id, RantResponse.class) .flatMap(RantResponse::getRant); } + /** + * Get a user by username. + * + * @param username The username of the user. + * @return The user. + */ public Optional getUser(String username) { return requestHandler.get(Endpoint.USER_ID, UserIdResponse.class, new BasicNameValuePair("username", username)) .flatMap(u -> getUser(u.getId())); } + /** + * Get a user. + * + * @param id The id of the user. + * @return The user. + */ public Optional getUser(int id) { Optional user = requestHandler.get(Endpoint.USERS.toString() + '/' + id, UserResponse.class) .flatMap(UserResponse::getUser); @@ -76,4 +94,22 @@ public void logout() { public boolean isLoggedIn() { return auth != null; } + + /** + * Set the request timeout in milliseconds, -1 meaning no timeout. + * + * @param timeout The timeout to use. + */ + public void setRequestTimeout(int timeout) { + requestHandler.setRequestTimeout(timeout); + } + + /** + * Get the request timeout in milliseconds. + * + * @return The timeout. + */ + public int getRequestTimeout() { + return requestHandler.getRequestTimeout(); + } } From 16b205da6ffeaf2c4da019128a99675eff4dde16 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Mon, 4 Sep 2017 21:21:45 +0200 Subject: [PATCH 30/65] Improve tests, add MinimalRant without comments --- .../java/com/scorpiac/javarant/DevRant.java | 3 +- .../com/scorpiac/javarant/DevRantFeed.java | 12 ++-- .../java/com/scorpiac/javarant/Image.java | 12 ++-- .../com/scorpiac/javarant/MinimalRant.java | 47 ++++++++++++++++ src/main/java/com/scorpiac/javarant/Rant.java | 43 +------------- .../com/scorpiac/javarant/RantContent.java | 4 ++ .../exceptions/DevRantApiException.java | 14 ----- ...ntsResponse.java => RantFeedResponse.java} | 8 +-- .../com/scorpiac/javarant/DevRantFeedIT.java | 13 ++++- .../java/com/scorpiac/javarant/DevRantIT.java | 56 +++++++++---------- .../java/com/scorpiac/javarant/ITHelper.java | 2 +- .../com/scorpiac/javarant/TestHelper.java | 51 +++++++++++++++++ src/test/resources/feed-rants.json | 4 +- 13 files changed, 162 insertions(+), 107 deletions(-) create mode 100644 src/main/java/com/scorpiac/javarant/MinimalRant.java delete mode 100644 src/main/java/com/scorpiac/javarant/exceptions/DevRantApiException.java rename src/main/java/com/scorpiac/javarant/responses/{RantsResponse.java => RantFeedResponse.java} (59%) create mode 100644 src/test/java/com/scorpiac/javarant/TestHelper.java diff --git a/src/main/java/com/scorpiac/javarant/DevRant.java b/src/main/java/com/scorpiac/javarant/DevRant.java index 5829e94..d5e362f 100644 --- a/src/main/java/com/scorpiac/javarant/DevRant.java +++ b/src/main/java/com/scorpiac/javarant/DevRant.java @@ -28,13 +28,14 @@ public class DevRant { } public DevRant() { + feed = new DevRantFeed(); INJECTOR.injectMembers(this); - feed = INJECTOR.getInstance(DevRantFeed.class); } @Inject void setRequestHandler(RequestHandler requestHandler) { this.requestHandler = requestHandler; + feed.setRequestHandler(requestHandler); } public DevRantFeed getFeed() { diff --git a/src/main/java/com/scorpiac/javarant/DevRantFeed.java b/src/main/java/com/scorpiac/javarant/DevRantFeed.java index b336f96..6775fa9 100644 --- a/src/main/java/com/scorpiac/javarant/DevRantFeed.java +++ b/src/main/java/com/scorpiac/javarant/DevRantFeed.java @@ -1,6 +1,6 @@ package com.scorpiac.javarant; -import com.scorpiac.javarant.responses.RantsResponse; +import com.scorpiac.javarant.responses.RantFeedResponse; import com.scorpiac.javarant.services.RequestHandler; import org.apache.http.message.BasicNameValuePair; @@ -9,19 +9,19 @@ import java.util.Optional; public class DevRantFeed { - private final RequestHandler requestHandler; + private RequestHandler requestHandler; @Inject - DevRantFeed(RequestHandler requestHandler) { + void setRequestHandler(RequestHandler requestHandler) { this.requestHandler = requestHandler; } - public Optional> getRants(Sort sort, int limit, int skip) { - return requestHandler.get(Endpoint.RANTS, RantsResponse.class, + public Optional> getRants(Sort sort, int limit, int skip) { + return requestHandler.get(Endpoint.RANTS, RantFeedResponse.class, new BasicNameValuePair("sort", sort.toString()), new BasicNameValuePair("limit", String.valueOf(limit)), new BasicNameValuePair("skip", String.valueOf(skip)) ) - .map(RantsResponse::getRants); + .map(RantFeedResponse::getRants); } } diff --git a/src/main/java/com/scorpiac/javarant/Image.java b/src/main/java/com/scorpiac/javarant/Image.java index 609d3df..7e9dcec 100644 --- a/src/main/java/com/scorpiac/javarant/Image.java +++ b/src/main/java/com/scorpiac/javarant/Image.java @@ -2,9 +2,11 @@ import com.fasterxml.jackson.annotation.JsonProperty; +import java.net.URI; + public class Image { @JsonProperty("url") - private String url; + private URI link; @JsonProperty("width") private int width; @JsonProperty("height") @@ -12,12 +14,12 @@ public class Image { @Override public boolean equals(Object obj) { - return obj instanceof Image && ((Image) obj).getLink().equals(url); + return obj instanceof Image && ((Image) obj).getLink().equals(link); } @Override public int hashCode() { - return url.hashCode(); + return link.hashCode(); } /** @@ -25,8 +27,8 @@ public int hashCode() { * * @return The image link. */ - public String getLink() { - return url; + public URI getLink() { + return link; } /** diff --git a/src/main/java/com/scorpiac/javarant/MinimalRant.java b/src/main/java/com/scorpiac/javarant/MinimalRant.java new file mode 100644 index 0000000..9d0bc96 --- /dev/null +++ b/src/main/java/com/scorpiac/javarant/MinimalRant.java @@ -0,0 +1,47 @@ +package com.scorpiac.javarant; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.scorpiac.javarant.services.RequestHandler; + +import java.net.URI; +import java.util.Collections; +import java.util.List; + +public class MinimalRant extends RantContent { + @JsonProperty + private List tags; + @JsonProperty("num_comments") + private int commentCount; + + @Override + public boolean equals(Object obj) { + return super.equals(obj) && obj instanceof MinimalRant; + } + + /** + * Get the link to the rant. + * + * @return The link to the rant. + */ + public URI getLink() { + return RequestHandler.BASE_URI.resolve("/rants/" + getId()); + } + + /** + * Get the tags. + * + * @return The tags. + */ + public List getTags() { + return Collections.unmodifiableList(tags); + } + + /** + * Get the amount of comments. + * + * @return The amount of comments. + */ + public int getCommentCount() { + return commentCount; + } +} diff --git a/src/main/java/com/scorpiac/javarant/Rant.java b/src/main/java/com/scorpiac/javarant/Rant.java index c399f39..3d0df3a 100644 --- a/src/main/java/com/scorpiac/javarant/Rant.java +++ b/src/main/java/com/scorpiac/javarant/Rant.java @@ -1,24 +1,10 @@ package com.scorpiac.javarant; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.scorpiac.javarant.services.RequestHandler; - -import java.net.URI; import java.util.Collections; import java.util.List; - -public class Rant extends RantContent { - private List tags; - @JsonProperty("num_comments") - private int commentCount; +public class Rant extends MinimalRant { private List comments; - private String link; - - @Override - public boolean equals(Object obj) { - return super.equals(obj) && obj instanceof Rant; - } /** * Get the comments on this rant. @@ -28,31 +14,4 @@ public boolean equals(Object obj) { public List getComments() { return Collections.unmodifiableList(comments); } - - /** - * Get the link to the rant. - * - * @return The link to the rant. - */ - public URI getLink() { - return RequestHandler.BASE_URI.resolve('/' + link); - } - - /** - * Get the tags. - * - * @return The tags. - */ - public List getTags() { - return Collections.unmodifiableList(tags); - } - - /** - * Get the amount of comments. - * - * @return The amount of comments. - */ - public int getCommentCount() { - return commentCount; - } } diff --git a/src/main/java/com/scorpiac/javarant/RantContent.java b/src/main/java/com/scorpiac/javarant/RantContent.java index 6c7728b..e7150b0 100644 --- a/src/main/java/com/scorpiac/javarant/RantContent.java +++ b/src/main/java/com/scorpiac/javarant/RantContent.java @@ -3,9 +3,13 @@ import com.fasterxml.jackson.annotation.JsonProperty; public abstract class RantContent { + @JsonProperty private int id; + @JsonProperty private int score; + @JsonProperty private VoteState voteState = VoteState.NONE; + @JsonProperty protected String text; @JsonProperty("attached_image") private Image image; diff --git a/src/main/java/com/scorpiac/javarant/exceptions/DevRantApiException.java b/src/main/java/com/scorpiac/javarant/exceptions/DevRantApiException.java deleted file mode 100644 index 24c1c79..0000000 --- a/src/main/java/com/scorpiac/javarant/exceptions/DevRantApiException.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.scorpiac.javarant.exceptions; - -public class DevRantApiException extends RuntimeException { - private final String internalError; - - public DevRantApiException(String message, String internalError) { - super(message); - this.internalError = internalError; - } - - public String getInternalError() { - return internalError; - } -} diff --git a/src/main/java/com/scorpiac/javarant/responses/RantsResponse.java b/src/main/java/com/scorpiac/javarant/responses/RantFeedResponse.java similarity index 59% rename from src/main/java/com/scorpiac/javarant/responses/RantsResponse.java rename to src/main/java/com/scorpiac/javarant/responses/RantFeedResponse.java index d257be3..de6accf 100644 --- a/src/main/java/com/scorpiac/javarant/responses/RantsResponse.java +++ b/src/main/java/com/scorpiac/javarant/responses/RantFeedResponse.java @@ -1,18 +1,18 @@ package com.scorpiac.javarant.responses; import com.fasterxml.jackson.annotation.JsonProperty; +import com.scorpiac.javarant.MinimalRant; import com.scorpiac.javarant.News; -import com.scorpiac.javarant.Rant; import java.util.List; -public class RantsResponse extends Response { +public class RantFeedResponse extends Response { @JsonProperty - private List rants; + private List rants; @JsonProperty private News news; - public List getRants() { + public List getRants() { return rants; } } diff --git a/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java b/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java index eb91aa1..5e9e528 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java @@ -1,5 +1,6 @@ package com.scorpiac.javarant; +import com.scorpiac.javarant.services.MockRequestHandler; import org.testng.annotations.Test; import java.io.IOException; @@ -11,6 +12,8 @@ public class DevRantFeedIT extends ITHelper { @Test public void testGetRants() throws IOException { + devRant.setRequestHandler(new MockRequestHandler(server.port())); + server.stubFor(stubResponse( get(urlPathEqualTo(Endpoint.RANTS.toString())) .withQueryParam("limit", equalTo("4")) @@ -19,7 +22,15 @@ public void testGetRants() throws IOException { "/feed-rants.json" )); - List rants = devRant.getFeed().getRants(Sort.RECENT, 4, 1).get(); + List rants = devRant.getFeed().getRants(Sort.RECENT, 4, 1).get(); assertEquals(rants.size(), 4); + + validateRant(rants.get(0), + 814524, + "Too real...", + 1, + 3, + "tag one", "tag 2" + ); } } diff --git a/src/test/java/com/scorpiac/javarant/DevRantIT.java b/src/test/java/com/scorpiac/javarant/DevRantIT.java index 766a6e0..8565cec 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantIT.java @@ -3,7 +3,6 @@ import org.testng.annotations.Test; import java.io.IOException; -import java.util.Arrays; import static com.github.tomakehurst.wiremock.client.WireMock.*; import static org.testng.Assert.assertEquals; @@ -19,26 +18,21 @@ public void testGetRant() throws IOException { Rant rant = devRant.getRant(686001).get(); - assertEquals(rant.getId(), 686001); - assertEquals(rant.getText(), "I only just noticed this is on the git man page :P"); - assertEquals(rant.getScore(), 84); - assertEquals(rant.getTags(), Arrays.asList("terminal", "manual", "git")); - assertEquals(rant.getVoteState(), VoteState.NONE); + validateRant( + rant, + 686001, + "I only just noticed this is on the git man page :P", + 84, + 5, + "terminal", "manual", "git" + ); - // Comments. assertEquals(rant.getCommentCount(), 5); assertEquals(rant.getComments().size(), 5); assertEquals(rant.getComments().get(0).getId(), 686175); - // Image. - assertEquals(rant.getImage().getWidth(), 530); - assertEquals(rant.getImage().getHeight(), 134); - assertEquals(rant.getImage().getLink(), "https://img.devrant.io/devrant/rant/r_686001_VfN7X.jpg"); - - // User. - assertEquals(rant.getUser().getId(), 102959); - assertEquals(rant.getUser().getUsername(), "LucaScorpion"); - assertEquals(rant.getUser().getScore(), 3831); + validateImage(rant.getImage(), "https://img.devrant.io/devrant/rant/r_686001_VfN7X.jpg", 530, 134); + validateMinimalUser(rant.getUser(), 102959, "LucaScorpion", 3831); } @Test @@ -65,20 +59,20 @@ public void testGetUserByUsername() throws IOException { User user = devRant.getUser("LucaScorpion").get(); - assertEquals(user.getId(), 102959); - assertEquals(user.getUsername(), "LucaScorpion"); - assertEquals(user.getScore(), 3831); - assertEquals(user.getAbout(), "Software developer, fanatic programmer, hardcore gamer, Linux lover."); - assertEquals(user.getLocation(), "Netherlands"); - assertEquals(user.getSkills(), "C#, Java, PHP, Javascript, HTML, CSS, SQL, C++ (Arduino), Bash"); - assertEquals(user.getGithub(), "LucaScorpion"); - assertEquals(user.getWebsite(), "https://scorpiac.com"); - - // Counts. - assertEquals(user.getRantsCount(), 60); - assertEquals(user.getUpvotedCount(), 5103); - assertEquals(user.getCommentsCount(), 800); - assertEquals(user.getFavoritesCount(), 36); - assertEquals(user.getCollabsCount(), 0); + validateUser(user, + 102959, + "LucaScorpion", + 3831, + "Software developer, fanatic programmer, hardcore gamer, Linux lover.", + "Netherlands", + "C#, Java, PHP, Javascript, HTML, CSS, SQL, C++ (Arduino), Bash", + "LucaScorpion", + "https://scorpiac.com", + 60, + 5103, + 800, + 36, + 0 + ); } } diff --git a/src/test/java/com/scorpiac/javarant/ITHelper.java b/src/test/java/com/scorpiac/javarant/ITHelper.java index 4c53c44..9aabd4a 100644 --- a/src/test/java/com/scorpiac/javarant/ITHelper.java +++ b/src/test/java/com/scorpiac/javarant/ITHelper.java @@ -14,7 +14,7 @@ import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; -public class ITHelper { +public abstract class ITHelper extends TestHelper { protected WireMockServer server; protected DevRant devRant; diff --git a/src/test/java/com/scorpiac/javarant/TestHelper.java b/src/test/java/com/scorpiac/javarant/TestHelper.java new file mode 100644 index 0000000..f99589d --- /dev/null +++ b/src/test/java/com/scorpiac/javarant/TestHelper.java @@ -0,0 +1,51 @@ +package com.scorpiac.javarant; + +import java.net.URI; +import java.util.Arrays; + +import static org.testng.Assert.assertEquals; + +public abstract class TestHelper { + public void validateRantContent(RantContent rant, int id, String text, int score) { + assertEquals(rant.getId(), id); + assertEquals(rant.getText(), text); + assertEquals(rant.getScore(), score); + } + + public void validateRant(MinimalRant rant, int id, String text, int score, int commentCount, String... tags) { + validateRantContent(rant, id, text, score); + + assertEquals(rant.getCommentCount(), commentCount); + assertEquals(rant.getLink(), URI.create("https://www.devrant.io/rants/" + id)); + assertEquals(rant.getTags(), Arrays.asList(tags)); + } + + public void validateImage(Image image, String link, int width, int height) { + assertEquals(image.getLink(), URI.create(link)); + assertEquals(image.getWidth(), width); + assertEquals(image.getHeight(), height); + } + + public void validateMinimalUser(MinimalUser user, int id, String username, int score) { + assertEquals(user.getId(), id); + assertEquals(user.getUsername(), username); + assertEquals(user.getScore(), score); + } + + public void validateUser(User user, int id, String username, int score, String about, String location, String skills, String github, String website, + int rants, int upvoted, int comments, int favorites, int collabs) { + validateMinimalUser(user, id, username, score); + + assertEquals(user.getAbout(), about); + assertEquals(user.getLocation(), location); + assertEquals(user.getSkills(), skills); + assertEquals(user.getGithub(), github); + assertEquals(user.getWebsite(), website); + + assertEquals(user.getRantsCount(), rants); + assertEquals(user.getUpvotedCount(), upvoted); + assertEquals(user.getCommentsCount(), comments); + assertEquals(user.getFavoritesCount(), favorites); + assertEquals(user.getCollabsCount(), collabs); + } +} diff --git a/src/test/resources/feed-rants.json b/src/test/resources/feed-rants.json index 1ae3a2f..b27e0ad 100644 --- a/src/test/resources/feed-rants.json +++ b/src/test/resources/feed-rants.json @@ -11,8 +11,8 @@ "width": 799, "height": 336 }, - "num_comments": 0, - "tags": [], + "num_comments": 3, + "tags": ["tag one", "tag 2"], "vote_state": 0, "edited": false, "user_id": 759598, From c87c91441a27068746e18e123d026db38f51b70a Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Tue, 5 Sep 2017 20:46:28 +0200 Subject: [PATCH 31/65] Jenkinsify --- Jenkinsfile | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 Jenkinsfile diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..caecc49 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,21 @@ +pipeline { + agent any + + stages { + stage('Clean') { + steps { + mvn 'clean' + } + } + stage('Build') { + steps { + mvn 'install' + } + } + } +} + +def mvn(String goals) { + def mvnHome = tool 'Maven 3' + sh "'${mvnHome}/bin/mvn' -Dmaven.test.failure.ignore $goals" +} From 85d3b34fd8b092bc66e874065a00206788f24fc2 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Tue, 5 Sep 2017 21:41:37 +0200 Subject: [PATCH 32/65] Simplify readme, add shields --- README.md | 140 +++++------------------------------------------------- 1 file changed, 13 insertions(+), 127 deletions(-) diff --git a/README.md b/README.md index 88ac227..de3791c 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,11 @@ # JavaRant A devRant API wrapper for Java. +[![Maven Central](https://img.shields.io/maven-central/v/com.scorpiac.javarant/javarant.svg)](https://mvnrepository.com/artifact/com.scorpiac.javarant/javarant) +[![Jenkins](https://img.shields.io/jenkins/s/https/jenkins.scorpiac.com/job/JavaRant/job/rework-to-2.0.svg)]() + ## Using JavaRant -JavaRant is available on Maven, simply add this dependency to your `pom.xml` file: +JavaRant is available on [Maven](http://mvnrepository.com/artifact/com.scorpiac.javarant/javarant), simply add this dependency to your `pom.xml` file: ``` @@ -12,140 +15,23 @@ JavaRant is available on Maven, simply add this dependency to your `pom.xml` fil ``` -## Class overview +## Getting started -### DevRant -This is the main class to interact with devRant. -To use it, you first need to create an instance of it. +To access devRant simply create a new `DevRant` object: ``` DevRant devRant = new DevRant(); ``` -It can then be used to get rants and collabs from the feed or by searching. - -``` -List rants = devRant.getRants(Sort.ALGO, 10, 0); -List wtf = devRant.search("wtf"); -List weekly = devRant.getWeekly(Sort.TOP(Range.ALL), 20, 0); -List stories = devRant.getStories(Sort.RECENT, 20, 0); - -List collabs = devRant.getCollabs(10, 0); - -Rant random = devRant.getSurprise(); -``` - -It can also be used to get specific items based on an id (or username). -These methods will throw an exception if the specified item does not exist (`NoSuchRantException` for rants and `NoSuchUserException` for users). -Additionally, `getCollab` will also throw a `NotACollabException` if the requested rant is a normal rant instead of a collab. - -``` -Rant rant = devRant.getRant(422850); -Collab collab = devRant.getCollab(420025); - -User me = devRant.getUser("LucaScorpion"); -User me2 = devRant.getUser(102959); -``` - -To post or vote anything we first need to log in. -This will make a request to get an authentication token, which is stored for later use. -Your username and password will _not_ be stored. -The login method uses `char[]` instead of `String` for the password parameter for security reasons (and most of the times you have a password it should already be in the form of a `char[]`). - -``` -devRant.login("username", "password".toCharArray()); -``` - -Voting on rants and comments can be done by passing the `Rant` or `Comment` object to vote on, or by directly passing the id. - -``` -devRant.vote(rant, Vote.DOWN(Reason.NOT_FOR_ME)); -devRant.vote(comment, Vote.NONE); - -devRant.voteRant(429863, Vote.UP); -devRant.voteComment(424558, Vote.UP); -``` - -To post a rant you simply need the content and tags. -To post a comment you need the text and a `Rant` object, or the id of the rant to comment on. - -``` -devRant.postRant("Hello world!", "hello, not a rant"); +You can then use it to get specific rants and users, or access the feed: -devRant.postComment(rant, "This is a comment."); -devRant.postComment(424553, "And another one."); ``` +// Get a specific rant. +Optional rant = devRant.getRant(686001); -You can log out to clear the authorization token. +// Get a user by username. +Optional me = devRant.getUser("LucaScorpion"); +// Get the 10 newest rants. +List recent = devRant.getFeed().getRants(Sort.RECENT); ``` -devRant.logout(); -``` - -### RantContent -This is the base class for rants and comments, which have the following attributes: - -- id -- user -- upvotes -- downvotes -- score -- voteState -- content -- image - -### Rant -In addition to the `RantContent` attributes, rants also contain: - -- tags -- commentCount -- comments - -The comments can be accessed by calling `getComments()`. -This will also fetch them if that has not been done yet. -Alternatively you can call `fetchComments()` to fetch the comments, or `fetchComments(force)` to force fetch them (i.e. fetch them again). - -### Collab -Collabs are an extension of rants. -Besides the attributes from a rant, collabs contain the following: - -- projectType -- description -- techStack -- teamSize -- url - -Like with a rant the comments can be fetched or force fetched. -There is also more data which needs to be fetched, similar to the comments this is done by calling `fetchData()` or `fetchData(force)` to force fetch it. - -### Comment -A comment only contains the attributes from `RantContent`. - -### Image -An image has the following attributes: - -- url -- width -- height - -### User -Users can be get from rants, comments, by id or username. -When they are get from a rant or comment, only the id, username and score are given. -The other data will be fetched and stored as soon as it's accessed. -Alternatively you can fetch the data by calling `fetchData()`, or `fetchData(force)` to force fetch the data (i.e. fetch it again). - -### Sort -These are the different sort options, which are used when getting rants from the feed. -You can pick between `ALGO`, `RECENT` or `TOP`. - -### Range -The range is used for the `TOP` sorting method. -The ranges are `DAY`, `WEEK`, `MONTH`, and `ALL`. - -### Vote -These are the different vote options, which are used to vote on rants and comments. -The vote options are `UP`, `NONE` and `DOWN`. - -### Reason -These are the reasons for a downvote. -The options are `NOT_FOR_ME`, `REPOST` and `OFFENSIVE_SPAM`. From 0659f067a2202b93a0c7781d235f1b31703a2f8e Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Tue, 5 Sep 2017 21:57:15 +0200 Subject: [PATCH 33/65] Add results stage for test reports --- Jenkinsfile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index caecc49..b208665 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -12,6 +12,11 @@ pipeline { mvn 'install' } } + stage('Results') { + steps { + junit '**/target/*-reports/TEST-*.xml' + } + } } } From 3f9fb0902af124a21cf4c0bdd1390e9423f730ab Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Tue, 5 Sep 2017 21:59:51 +0200 Subject: [PATCH 34/65] Add tests badge to readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index de3791c..ba450c9 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ A devRant API wrapper for Java. [![Maven Central](https://img.shields.io/maven-central/v/com.scorpiac.javarant/javarant.svg)](https://mvnrepository.com/artifact/com.scorpiac.javarant/javarant) [![Jenkins](https://img.shields.io/jenkins/s/https/jenkins.scorpiac.com/job/JavaRant/job/rework-to-2.0.svg)]() +[![Jenkins tests](https://img.shields.io/jenkins/t/https/jenkins.scorpiac.com/job/JavaRant/job/rework-to-2.0.svg)]() ## Using JavaRant JavaRant is available on [Maven](http://mvnrepository.com/artifact/com.scorpiac.javarant/javarant), simply add this dependency to your `pom.xml` file: From 4618dca86a482c24fd061d585c531144b653d5f7 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Tue, 5 Sep 2017 22:11:13 +0200 Subject: [PATCH 35/65] Update version in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ba450c9..1d6c7d0 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ JavaRant is available on [Maven](http://mvnrepository.com/artifact/com.scorpiac. com.scorpiac.javarant javarant - 1.3 + 2.0.0 ``` From a1e7dd0c41f580eaf7377e3d61a05c60f47f0898 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Wed, 6 Sep 2017 10:32:42 +0200 Subject: [PATCH 36/65] Rename Rant to CommentedRant, MinimalRant to Rant --- .../java/com/scorpiac/javarant/Collab.java | 2 +- .../com/scorpiac/javarant/CommentedRant.java | 17 +++++++ .../java/com/scorpiac/javarant/DevRant.java | 2 +- .../com/scorpiac/javarant/DevRantFeed.java | 2 +- .../com/scorpiac/javarant/MinimalRant.java | 47 ------------------- src/main/java/com/scorpiac/javarant/Rant.java | 42 ++++++++++++++--- src/main/java/com/scorpiac/javarant/User.java | 12 ++--- .../javarant/responses/RantFeedResponse.java | 6 +-- .../javarant/responses/RantResponse.java | 6 +-- .../com/scorpiac/javarant/DevRantFeedIT.java | 2 +- .../java/com/scorpiac/javarant/DevRantIT.java | 2 +- .../com/scorpiac/javarant/TestHelper.java | 2 +- 12 files changed, 71 insertions(+), 71 deletions(-) create mode 100644 src/main/java/com/scorpiac/javarant/CommentedRant.java delete mode 100644 src/main/java/com/scorpiac/javarant/MinimalRant.java diff --git a/src/main/java/com/scorpiac/javarant/Collab.java b/src/main/java/com/scorpiac/javarant/Collab.java index a7d0b71..25ded1b 100644 --- a/src/main/java/com/scorpiac/javarant/Collab.java +++ b/src/main/java/com/scorpiac/javarant/Collab.java @@ -1,6 +1,6 @@ package com.scorpiac.javarant; -public class Collab extends Rant { +public class Collab extends CommentedRant { private String projectType; private String description; private String techStack; diff --git a/src/main/java/com/scorpiac/javarant/CommentedRant.java b/src/main/java/com/scorpiac/javarant/CommentedRant.java new file mode 100644 index 0000000..5f0d995 --- /dev/null +++ b/src/main/java/com/scorpiac/javarant/CommentedRant.java @@ -0,0 +1,17 @@ +package com.scorpiac.javarant; + +import java.util.Collections; +import java.util.List; + +public class CommentedRant extends Rant { + private List comments; + + /** + * Get the comments on this rant. + * + * @return The comments. + */ + public List getComments() { + return Collections.unmodifiableList(comments); + } +} diff --git a/src/main/java/com/scorpiac/javarant/DevRant.java b/src/main/java/com/scorpiac/javarant/DevRant.java index d5e362f..13d7b0a 100644 --- a/src/main/java/com/scorpiac/javarant/DevRant.java +++ b/src/main/java/com/scorpiac/javarant/DevRant.java @@ -48,7 +48,7 @@ public DevRantFeed getFeed() { * @param id The id of the rant. * @return The rant. */ - public Optional getRant(int id) { + public Optional getRant(int id) { return requestHandler.get(Endpoint.RANTS.toString() + '/' + id, RantResponse.class) .flatMap(RantResponse::getRant); } diff --git a/src/main/java/com/scorpiac/javarant/DevRantFeed.java b/src/main/java/com/scorpiac/javarant/DevRantFeed.java index 6775fa9..6f7eb2d 100644 --- a/src/main/java/com/scorpiac/javarant/DevRantFeed.java +++ b/src/main/java/com/scorpiac/javarant/DevRantFeed.java @@ -16,7 +16,7 @@ void setRequestHandler(RequestHandler requestHandler) { this.requestHandler = requestHandler; } - public Optional> getRants(Sort sort, int limit, int skip) { + public Optional> getRants(Sort sort, int limit, int skip) { return requestHandler.get(Endpoint.RANTS, RantFeedResponse.class, new BasicNameValuePair("sort", sort.toString()), new BasicNameValuePair("limit", String.valueOf(limit)), diff --git a/src/main/java/com/scorpiac/javarant/MinimalRant.java b/src/main/java/com/scorpiac/javarant/MinimalRant.java deleted file mode 100644 index 9d0bc96..0000000 --- a/src/main/java/com/scorpiac/javarant/MinimalRant.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.scorpiac.javarant; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.scorpiac.javarant.services.RequestHandler; - -import java.net.URI; -import java.util.Collections; -import java.util.List; - -public class MinimalRant extends RantContent { - @JsonProperty - private List tags; - @JsonProperty("num_comments") - private int commentCount; - - @Override - public boolean equals(Object obj) { - return super.equals(obj) && obj instanceof MinimalRant; - } - - /** - * Get the link to the rant. - * - * @return The link to the rant. - */ - public URI getLink() { - return RequestHandler.BASE_URI.resolve("/rants/" + getId()); - } - - /** - * Get the tags. - * - * @return The tags. - */ - public List getTags() { - return Collections.unmodifiableList(tags); - } - - /** - * Get the amount of comments. - * - * @return The amount of comments. - */ - public int getCommentCount() { - return commentCount; - } -} diff --git a/src/main/java/com/scorpiac/javarant/Rant.java b/src/main/java/com/scorpiac/javarant/Rant.java index 3d0df3a..5f49c80 100644 --- a/src/main/java/com/scorpiac/javarant/Rant.java +++ b/src/main/java/com/scorpiac/javarant/Rant.java @@ -1,17 +1,47 @@ package com.scorpiac.javarant; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.scorpiac.javarant.services.RequestHandler; + +import java.net.URI; import java.util.Collections; import java.util.List; -public class Rant extends MinimalRant { - private List comments; +public class Rant extends RantContent { + @JsonProperty + private List tags; + @JsonProperty("num_comments") + private int commentCount; + + @Override + public boolean equals(Object obj) { + return super.equals(obj) && obj instanceof Rant; + } + + /** + * Get the link to the rant. + * + * @return The link to the rant. + */ + public URI getLink() { + return RequestHandler.BASE_URI.resolve("/rants/" + getId()); + } + + /** + * Get the tags. + * + * @return The tags. + */ + public List getTags() { + return Collections.unmodifiableList(tags); + } /** - * Get the comments on this rant. + * Get the amount of comments. * - * @return The comments. + * @return The amount of comments. */ - public List getComments() { - return Collections.unmodifiableList(comments); + public int getCommentCount() { + return commentCount; } } diff --git a/src/main/java/com/scorpiac/javarant/User.java b/src/main/java/com/scorpiac/javarant/User.java index 1d5e3ab..d5eff47 100644 --- a/src/main/java/com/scorpiac/javarant/User.java +++ b/src/main/java/com/scorpiac/javarant/User.java @@ -69,7 +69,7 @@ public String getWebsite() { * * @return The posted rants. */ - public List getRants() { + public List getRants() { return Collections.unmodifiableList(content.content.rants); } @@ -78,7 +78,7 @@ public List getRants() { * * @return The upvoted rants. */ - public List getUpvoted() { + public List getUpvoted() { return Collections.unmodifiableList(content.content.upvoted); } @@ -96,7 +96,7 @@ public List getComments() { * * @return The favorite rants. */ - public List getFavorites() { + public List getFavorites() { return Collections.unmodifiableList(content.content.favorites); } @@ -154,13 +154,13 @@ private static class UserContent { private static class Content { @JsonProperty - private List rants; + private List rants; @JsonProperty - private List upvoted; + private List upvoted; @JsonProperty private List comments; @JsonProperty - private List favorites; + private List favorites; } private static class Counts { diff --git a/src/main/java/com/scorpiac/javarant/responses/RantFeedResponse.java b/src/main/java/com/scorpiac/javarant/responses/RantFeedResponse.java index de6accf..1c15e94 100644 --- a/src/main/java/com/scorpiac/javarant/responses/RantFeedResponse.java +++ b/src/main/java/com/scorpiac/javarant/responses/RantFeedResponse.java @@ -1,18 +1,18 @@ package com.scorpiac.javarant.responses; import com.fasterxml.jackson.annotation.JsonProperty; -import com.scorpiac.javarant.MinimalRant; +import com.scorpiac.javarant.Rant; import com.scorpiac.javarant.News; import java.util.List; public class RantFeedResponse extends Response { @JsonProperty - private List rants; + private List rants; @JsonProperty private News news; - public List getRants() { + public List getRants() { return rants; } } diff --git a/src/main/java/com/scorpiac/javarant/responses/RantResponse.java b/src/main/java/com/scorpiac/javarant/responses/RantResponse.java index 2697076..cd28e8a 100644 --- a/src/main/java/com/scorpiac/javarant/responses/RantResponse.java +++ b/src/main/java/com/scorpiac/javarant/responses/RantResponse.java @@ -2,7 +2,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.scorpiac.javarant.Comment; -import com.scorpiac.javarant.Rant; +import com.scorpiac.javarant.CommentedRant; import java.lang.reflect.Field; import java.util.List; @@ -10,11 +10,11 @@ public class RantResponse extends Response { @JsonProperty - private Rant rant; + private CommentedRant rant; @JsonProperty private List comments; - public Optional getRant() { + public Optional getRant() { if (!isSuccess()) { return Optional.empty(); } diff --git a/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java b/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java index 5e9e528..a985f8f 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java @@ -22,7 +22,7 @@ public void testGetRants() throws IOException { "/feed-rants.json" )); - List rants = devRant.getFeed().getRants(Sort.RECENT, 4, 1).get(); + List rants = devRant.getFeed().getRants(Sort.RECENT, 4, 1).get(); assertEquals(rants.size(), 4); validateRant(rants.get(0), diff --git a/src/test/java/com/scorpiac/javarant/DevRantIT.java b/src/test/java/com/scorpiac/javarant/DevRantIT.java index 8565cec..2268197 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantIT.java @@ -16,7 +16,7 @@ public void testGetRant() throws IOException { "/rant-686001.json" )); - Rant rant = devRant.getRant(686001).get(); + CommentedRant rant = devRant.getRant(686001).get(); validateRant( rant, diff --git a/src/test/java/com/scorpiac/javarant/TestHelper.java b/src/test/java/com/scorpiac/javarant/TestHelper.java index f99589d..100e090 100644 --- a/src/test/java/com/scorpiac/javarant/TestHelper.java +++ b/src/test/java/com/scorpiac/javarant/TestHelper.java @@ -12,7 +12,7 @@ public void validateRantContent(RantContent rant, int id, String text, int score assertEquals(rant.getScore(), score); } - public void validateRant(MinimalRant rant, int id, String text, int score, int commentCount, String... tags) { + public void validateRant(Rant rant, int id, String text, int score, int commentCount, String... tags) { validateRantContent(rant, id, text, score); assertEquals(rant.getCommentCount(), commentCount); From 9ed38697c2c55e76fca2b501aed331f67afb7941 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Wed, 6 Sep 2017 10:49:43 +0200 Subject: [PATCH 37/65] Add search to feed --- README.md | 2 +- .../com/scorpiac/javarant/DevRantFeed.java | 30 + .../java/com/scorpiac/javarant/Endpoint.java | 4 +- .../responses/ResultsFeedResponse.java | 15 + .../com/scorpiac/javarant/DevRantFeedIT.java | 22 +- src/test/resources/search-wtf.json | 6734 +++++++++++++++++ 6 files changed, 6801 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/scorpiac/javarant/responses/ResultsFeedResponse.java create mode 100644 src/test/resources/search-wtf.json diff --git a/README.md b/README.md index 1d6c7d0..bb4374d 100644 --- a/README.md +++ b/README.md @@ -34,5 +34,5 @@ Optional rant = devRant.getRant(686001); Optional me = devRant.getUser("LucaScorpion"); // Get the 10 newest rants. -List recent = devRant.getFeed().getRants(Sort.RECENT); +Optional> recent = devRant.getFeed().getRants(Sort.RECENT); ``` diff --git a/src/main/java/com/scorpiac/javarant/DevRantFeed.java b/src/main/java/com/scorpiac/javarant/DevRantFeed.java index 6f7eb2d..6d23393 100644 --- a/src/main/java/com/scorpiac/javarant/DevRantFeed.java +++ b/src/main/java/com/scorpiac/javarant/DevRantFeed.java @@ -1,6 +1,7 @@ package com.scorpiac.javarant; import com.scorpiac.javarant.responses.RantFeedResponse; +import com.scorpiac.javarant.responses.ResultsFeedResponse; import com.scorpiac.javarant.services.RequestHandler; import org.apache.http.message.BasicNameValuePair; @@ -16,6 +17,22 @@ void setRequestHandler(RequestHandler requestHandler) { this.requestHandler = requestHandler; } + public Optional> getRants(Sort sort) { + return getRants(sort, 20, 0); + } + + public Optional> getRants(Sort sort, int limit) { + return getRants(sort, limit, 0); + } + + /** + * Get rants from the feed. + * + * @param sort How to sort the feed. + * @param limit How many rants to get. + * @param skip How many rants to skip. + * @return Rants from the feed. + */ public Optional> getRants(Sort sort, int limit, int skip) { return requestHandler.get(Endpoint.RANTS, RantFeedResponse.class, new BasicNameValuePair("sort", sort.toString()), @@ -24,4 +41,17 @@ public Optional> getRants(Sort sort, int limit, int skip) { ) .map(RantFeedResponse::getRants); } + + /** + * Search for rants. + * + * @param term The term to search for. + * @return The search results. + */ + public Optional> search(String term) { + return requestHandler.get(Endpoint.SEARCH, ResultsFeedResponse.class, + new BasicNameValuePair("term", term) + ) + .map(ResultsFeedResponse::getResults); + } } diff --git a/src/main/java/com/scorpiac/javarant/Endpoint.java b/src/main/java/com/scorpiac/javarant/Endpoint.java index 7919cfa..51ab764 100644 --- a/src/main/java/com/scorpiac/javarant/Endpoint.java +++ b/src/main/java/com/scorpiac/javarant/Endpoint.java @@ -5,8 +5,8 @@ public enum Endpoint { API_DEVRANT(API, "devrant"), USER_ID(API, "get-user-id"), USERS(API, "users"), - RANTS(API_DEVRANT, "rants") - ; + RANTS(API_DEVRANT, "rants"), + SEARCH(API_DEVRANT, "search"); private final String endpoint; diff --git a/src/main/java/com/scorpiac/javarant/responses/ResultsFeedResponse.java b/src/main/java/com/scorpiac/javarant/responses/ResultsFeedResponse.java new file mode 100644 index 0000000..bf779b0 --- /dev/null +++ b/src/main/java/com/scorpiac/javarant/responses/ResultsFeedResponse.java @@ -0,0 +1,15 @@ +package com.scorpiac.javarant.responses; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.scorpiac.javarant.Rant; + +import java.util.List; + +public class ResultsFeedResponse extends Response { + @JsonProperty + private List results; + + public List getResults() { + return results; + } +} diff --git a/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java b/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java index a985f8f..536c035 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java @@ -1,6 +1,5 @@ package com.scorpiac.javarant; -import com.scorpiac.javarant.services.MockRequestHandler; import org.testng.annotations.Test; import java.io.IOException; @@ -12,8 +11,6 @@ public class DevRantFeedIT extends ITHelper { @Test public void testGetRants() throws IOException { - devRant.setRequestHandler(new MockRequestHandler(server.port())); - server.stubFor(stubResponse( get(urlPathEqualTo(Endpoint.RANTS.toString())) .withQueryParam("limit", equalTo("4")) @@ -33,4 +30,23 @@ public void testGetRants() throws IOException { "tag one", "tag 2" ); } + + @Test + public void testSearch() throws IOException { + server.stubFor(stubResponse( + get(urlPathEqualTo(Endpoint.SEARCH.toString())) + .withQueryParam("term", equalTo("wtf")), + "/search-wtf.json" + )); + + List rants = devRant.getFeed().search("wtf").get(); + + validateRant(rants.get(1), + 542296, + "Stages of learning angular js \n1. Wtf \n2. I think I get it. \n3. Wtf", + 327, + 19, + "javascript", "angularjs", "wtf?" + ); + } } diff --git a/src/test/resources/search-wtf.json b/src/test/resources/search-wtf.json new file mode 100644 index 0000000..0d4d128 --- /dev/null +++ b/src/test/resources/search-wtf.json @@ -0,0 +1,6734 @@ +{ + "success": true, + "results": [ + { + "id": 206702, + "text": "Writing some code on a flight\n\n\"ARE YOU HACKING?!?!\"\n\"Ugh... Well yeah but not in the way you're thinking\"\n\"Omg I'm getting a flight attendant\"\n\nNeedless to say I'm still en route because the flight attendant realized that I'm not Mr Robot, nor do I have ZeroCool scribbled on my laptop anywhere.", + "score": 354, + "created_time": 1475080266, + "attached_image": "", + "num_comments": 14, + "tags": [ + "wtf", + "hacking" + ], + "vote_state": 0, + "edited": false, + "user_id": 178684, + "user_username": "molynerd", + "user_score": 1965, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-12_3-7_8-2_7-2_5-1_12-1_6-11_10-7_2-39_4-1.jpg" + } + }, + { + "id": 542296, + "text": "Stages of learning angular js \n1. Wtf \n2. I think I get it. \n3. Wtf", + "score": 327, + "created_time": 1492688613, + "attached_image": "", + "num_comments": 19, + "tags": [ + "javascript", + "angularjs", + "wtf?" + ], + "vote_state": 0, + "edited": false, + "user_id": 499185, + "user_username": "byt3cod3", + "user_score": 2041, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-2_16-1_3-4_8-1_7-1_5-1_12-2_6-10_10-1_2-34_15-18_4-1.jpg" + } + }, + { + "id": 371010, + "text": "Yesterday, in a meeting with project stakeholders and a dev was demoing his software when an un-handled exception occurred, causing the app to crash.\n\nDev: โ€œOh..thatโ€™s weird. Doesnโ€™t do that on my machine. Better look at the logโ€\r\n- Dev looks at the log and sees the exception was a divide by zero error.\r\nDev: โ€œOhhhโ€ฆyeaโ€ฆthe average price calculation, itโ€™s a bug in the database.โ€\r\n \r\nMe: โ€œThatโ€™s funny.โ€\r\n\r\nDevMgr: โ€œWhatโ€™s funny about bugs in the database?โ€\r\nMe: โ€œDivide by zero exceptions are not an indication of a data error, itโ€™s a bug in the code.โ€\r\nDev: โ€œUhhโ€ฆhow so? The price factor is zero, which comes from a table, so thatโ€™s a bug in the databaseโ€\r\nMe: โ€œJim, will you have sales with a price factor of zero?โ€\r\nStakeholderJim: โ€œYea, for add-on items that weโ€™re not putting on sale. Hats, gloves, things like that.โ€\r\nDev: โ€œSteve, did anyone tell you the factor could be zero?โ€\r\nDBA-Steve: โ€œUh...noโ€ฆjust that the value couldnโ€™t be null. You guys can put whatever you want.โ€\r\nDevMgr: โ€œSo, how will you fix this bug?โ€\r\nDBA-Steve: โ€œBug? โ€ฆohโ€ฆumโ€ฆI guess I could default the value to 1.โ€\r\nDev: โ€œWhat if the user types in a zero? Can you switch it to a 1?โ€\r\nMe: โ€œOr you check the factor value before you try to divide. That will fix the exception and Steve wonโ€™t have to do anything.โ€\r\n\r\nDevMgr: โ€œLets wrap this up. Steve, go ahead and make the necessary database changes to make sure the factor is never zero.โ€\r\nStakeholderJim: โ€œThat doesnโ€™t sound right. Add-on items should never have a factor. A value of 1 could screw up the average.โ€\r\nDev: โ€œDonโ€™t worry, weโ€™ll know the difference.โ€\r\n\n\nI completely lost any sort of brain power to say anything after Dev said that. All the little voices kept saying were โ€˜WTF? WTF just happened? No reallyโ€ฆW T F just happened!?โ€™ over and over. I still have no idea on how to articulate to anyone with any sort of sense about what happened. Thanks DevRant for letting me rant.", + "score": 308, + "created_time": 1483737176, + "attached_image": "", + "num_comments": 15, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 27522, + "user_username": "PaperTrail", + "user_score": 4978, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-1_1-1_16-1_3-9_8-3_7-3_5-3_12-1_6-65_10-7_2-19_4-3_19-3.jpg" + } + }, + { + "id": 118234, + "text": "Boss: \"I looked at a testing suite. It is $2,500 a license and I'm buying 60 licenses. You should probably get familiar with it.\"\r\nLeadDev: \"Um, we already use NUnit, and it's free.\"\r\nBoss: \"Hmm...I'd better add Pluralsight training in the budget so you can learn about the new program.\"\r\nLeadDev: \"Oh, no...we need new laptops more than we need software.\"\r\nBoss:\"New laptops? Not my budget. When we buy this new software, everyone is going to use it\"\r\nLeadDev: \"Everyone? How will you monitor it's usage?\"\r\nBoss: \"I'll have networking send me captures of all the running tasks on the dev machines. The test suite better be running. Writing good tests will be our #1 priority.\"\r\nLeadDev: \"Um, we already write tests using NUnit.\"\r\nBoss: \"I don't understand what you are saying. I need something I can visualize. This UI testing suite is exactly what I need.\"\r\nLeadDev: \"Maybe the testing suite would be better suited for you and QA?\"\r\n\r\nBoss: \"Submitted the budget. There will be a test server available for you to configure. This whole project costs over $100,000, so don't screw it up. Any questions?\"\r\nLeadDev: \"Oh...well...what server ...\"\r\nBoss: \"Dang...sorry, I'm taking off the rest of the afternoon. We'll talk about this more on Monday. Get started on those Pluralsight videos. I'll expect a full training and deployments by next week. Have a great weekend!\"", + "score": 307, + "created_time": 1469217034, + "attached_image": "", + "num_comments": 16, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 27522, + "user_username": "PaperTrail", + "user_score": 4978, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-1_1-1_16-1_3-9_8-3_7-3_5-3_12-1_6-65_10-7_2-19_4-3_19-3.jpg" + } + }, + { + "id": 215104, + "text": "Words cannot describe this...", + "score": 217, + "created_time": 1475502406, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_215104_FPbij.jpg", + "width": 799, + "height": 561 + }, + "num_comments": 26, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 202624, + "user_username": "franga2000", + "user_score": 3081, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-6_3-2_8-2_7-2_5-2_12-1_6-4_10-1_2-27_18-3_4-2_19-2_21-2.jpg" + } + }, + { + "id": 561750, + "text": "So are scammers using GitHub now? Like wtf is this ๐Ÿ˜‚", + "score": 202, + "created_time": 1493539811, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_561750_QHUZd.jpg", + "width": 562, + "height": 999 + }, + "num_comments": 33, + "tags": [ + "github", + "wtf", + "scam" + ], + "vote_state": 0, + "edited": false, + "user_id": 35661, + "user_username": "liammartens", + "user_score": 5017, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-1_16-3_3-4_8-1_7-1_5-3_12-1_6-2_10-5_2-39_11-1_18-1_4-3_19-2_20-5.jpg" + } + }, + { + "id": 242137, + "text": "*Looking at other studentโ€™s code*\nMe: Why do you have a line that only says โ€œvar != var2โ€? That doesnโ€™t do anything.\nThem: That tells the computer to set var to anything except var2, right?\nMe:", + "score": 160, + "created_time": 1476852842, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_242137_yn5zT.jpg", + "width": 685, + "height": 1000 + }, + "num_comments": 8, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 149649, + "user_username": "Minnow", + "user_score": 2873, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-f_9-1_1-2_16-6_3-5_8-3_7-3_5-2_12-2_17-2_6-6_10-9_2-47_11-4_18-4_4-2_19-3_20-5_21-2.jpg" + } + }, + { + "id": 556061, + "text": "Boss: make this thing\nMe: yeah no worries. Where is the spec?\nBoss: We don't have enough one but we outsourced the design so call him\n\nDesigner: haven't started yet\nMe: excellent\n\nBoss: I'm going on holiday. I'll leave this to you.\nMe: erm ok. I'm having a few problems getting stuff out of the designer though.\n\n*2 weeks later and still no designs*\n\nBoss: I'm back. Where is the progress?!\nMe: indeed.\n\n*1 week later i get half designs that sort of make sense*\n\nBoss: hurry up!\n\n*1 week later*\n\nMe: designer you're busting my balls here\nDesigner: yeah lol\n\nMe to boss: still having problems. No idea what I'm doing.\nBoss: deal with it\n\n*2 days later*\nPM: we are demoing it to clients tomorrow\nMe: brilliant. I'll become a magician then.\n\n* Meeting goes well and no one notices the thing is a bit buggy*\n\n*2 days later*\nMe to boss and pm: you already know whats going on but I'll keep trying.\nBoss: ok it's just a proof of concept anyway.\nDesigner: yeah here's the rest of the designs lol\n\n*1 week later, the designs made no sense, no idea what they wanted but hey it's a proof of concept so I'll just do my best...*\n\n*suddenly again, hey you have 1 week before we sell it. Lol. smashes a product together as fast as humanly possible, due to half designs and no time to do it right even html classes and CSS aren't right - didn't know things would be repeated at the time. No time to fix entire thing. Luckily just a proof of concept*\n\nNew senior developer: hey boss just said this is being sold tomorrow.\nMe: wtf..It's a proof of concept and i was given longer...\nNew senior developer: no\nMe: :(\n\nSenior developer and all colleagues: it's full of bugs and doesn't work\nMe: yes that will happen without specs, random tight deadlines, no designs that made sense and a total of about a week and a half to make an entire system for multiple user types to make applications, send messages, post jobs, handle all paperwork and move paperwork among different user types as they go through applications. I told everyone what was going on but i get no support...\n\n*Silence*\n\nBoss: wtf i gave you so long! All i know is my entire staff is working on a product that should be done ages ago\nMe: ok, however i have said almost every day i need-\nBoss: I'm not interested\n\n*I finish my placement year and never get any promised work or the job offer*\n\nSeems legit?", + "score": 149, + "created_time": 1493255175, + "attached_image": "", + "num_comments": 16, + "tags": [ + "work hell", + "bad boss", + "maybe i'm retarded", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 37343, + "user_username": "craig939393", + "user_score": 808, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-3_16-7_3-1_8-1_7-1_5-1_12-3_6-15_2-10_15-11_11-2_18-2_4-1_19-1.jpg" + } + }, + { + "id": 545595, + "text": "WTF!?!?!?!?....", + "score": 149, + "created_time": 1492817906, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_545595_GQnTW.jpg", + "width": 720, + "height": 899 + }, + "num_comments": 7, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 527845, + "user_username": "garnakolegovitc", + "user_score": 574, + "user_avatar": { + "b": "7bc8a4" + } + }, + { + "id": 733731, + "text": "A guy in the office just bursted out \"9gag is getting boring... I wish there was a community for devs and stuff...\". The entire team just stared at him xD", + "score": 148, + "created_time": 1500909386, + "attached_image": "", + "num_comments": 8, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 53357, + "user_username": "webbu", + "user_score": 350, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-3_16-1_3-2_8-1_7-1_5-1_12-3_6-65_2-12_4-1.jpg" + } + }, + { + "id": 766948, + "text": "friend: Hey you program right?\r\nme: yea sure. \r\nfriend: What kind of phone should I get?\r\nme: ....", + "score": 133, + "created_time": 1502365898, + "attached_image": "", + "num_comments": 13, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 521989, + "user_username": "Jeremiahlukus", + "user_score": 491, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-2_16-1_3-5_8-1_7-1_5-1_12-2_6-1_10-5_2-18_11-1_18-1_4-1_19-1.jpg" + } + }, + { + "id": 206650, + "text": "My company doesn't allows or uses Wi-Fi. It's too dangerous... Like fucking really. We also don't use a repository. We version stuff by dates folders. Yes we are a ecommerce business.", + "score": 133, + "created_time": 1475077759, + "attached_image": "", + "num_comments": 26, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 34836, + "user_username": "xociety", + "user_score": 662, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-5_16-4_3-4_8-1_7-1_5-1_12-5_17-1_6-50_10-2_2-40_15-10_11-2_18-2_4-1_19-1.jpg" + } + }, + { + "id": 487705, + "text": "IT Teacher, high school's first year: \"To write in Word, you must use the keyboard.\"\n\nSHIT, REALLY!?", + "score": 111, + "created_time": 1490046935, + "attached_image": "", + "num_comments": 9, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": true, + "user_id": 471309, + "user_username": "codanimex", + "user_score": 803, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-1_16-15_3-2_8-1_7-1_5-1_12-1_6-98_10-3_2-91_15-11_11-2_18-2_4-1_19-2.jpg" + } + }, + { + "id": 616493, + "text": "Google is a bit confused about what \"wtf\" mean :-)", + "score": 102, + "created_time": 1495996613, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_616493_NPygy.jpg", + "width": 562, + "height": 1000 + }, + "num_comments": 5, + "tags": [ + "android log wtf what a terrible faliture" + ], + "vote_state": 0, + "edited": false, + "user_id": 363501, + "user_username": "shelladdicted", + "user_score": 2295, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-1_16-7_3-4_8-1_7-1_5-1_12-1_6-10_10-9_2-76_18-2_4-1_19-2.jpg" + }, + "user_dpp": 1 + }, + { + "id": 79665, + "text": "My friend said, \"Java is better than Linux.\"", + "score": 91, + "created_time": 1466483108, + "attached_image": "", + "num_comments": 13, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 77738, + "user_username": "itch96", + "user_score": 2657, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-11_16-16_3-2_8-2_7-2_5-2_12-11_6-2_2-47_11-4_18-3_4-2_19-3_20-8_21-2.jpg" + } + }, + { + "id": 603264, + "text": "WTF GOOGLE, WHY...", + "score": 90, + "created_time": 1495295133, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_603264_RbzuS.jpg", + "width": 728, + "height": 400 + }, + "num_comments": 26, + "tags": [ + "wtf", + "emoji", + "google being google" + ], + "vote_state": 0, + "edited": false, + "user_id": 159002, + "user_username": "n1had", + "user_score": 2631, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-2_16-17_3-13_8-2_7-2_5-2_12-2_6-2_10-9_2-51_18-4_4-2_19-3_20-7_21-2.jpg" + } + }, + { + "id": 653741, + "text": "So a guy wants to start a company with me. He wants to be CEO ๐Ÿ‘จโ€๐Ÿ’ผ I'm fine with it but now he also wants me to obey every one of his orders such as attend long unplanned meetings, go do market research, code the app and for what, a mere 5%. He gets a bigger cut of the income because he came up with the idea ๐Ÿ’ก, he also gets to sit and order me around because he's read a few books on business and economics. People don't seem to understand the difference between having an idea and implementation. I just left and said no to every offer he had the highest of which was 10%, don't be worried about the money he says this is a real opportunity for you. I mean wtf is wrong with some people.", + "score": 85, + "created_time": 1497597131, + "attached_image": "", + "num_comments": 16, + "tags": [ + "people don't understand the cost of websites", + "wtf?" + ], + "vote_state": 0, + "edited": false, + "user_id": 24092, + "user_username": "sesh", + "user_score": 2413, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-13_16-18_3-12_8-2_7-2_5-2_12-13_6-102_10-9_2-76_15-10_11-14_18-4_4-2_19-3_20-8_21-2.jpg" + } + }, + { + "id": 460517, + "text": "He was saved..", + "score": 85, + "created_time": 1488648275, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_460517_LTyzv.jpg", + "width": 750, + "height": 1000 + }, + "num_comments": 8, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": true, + "user_id": 101429, + "user_username": "ReliantFever735", + "user_score": 6709, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-4_16-1_3-1_8-1_7-1_5-4_12-4_6-14_10-1_2-47_15-10_18-3_4-4_19-3_20-7_21-1.jpg" + } + }, + { + "id": 166537, + "text": "My girlfriend just got a new job and the contract says that masturbation is allowed on the toilet.", + "score": 79, + "created_time": 1472943301, + "attached_image": "", + "num_comments": 9, + "tags": [ + "weird", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 149362, + "user_username": "Paramite", + "user_score": 4993, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-3_16-6_3-3_8-4_7-4_5-3_12-3_6-82_10-9_2-54_15-10_11-2_18-4_4-3_19-3_20-3_21-2.jpg" + }, + "user_dpp": 1 + }, + { + "id": 373910, + "text": "Thanks for such useful information, PHPMyAdmin!", + "score": 77, + "created_time": 1483902222, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_373910_SNGET.jpg", + "width": 379, + "height": 109 + }, + "num_comments": 3, + "tags": [ + "phpmyadmin", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 22941, + "user_username": "linuxxx", + "user_score": 32147, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-3_3-3_8-3_7-3_5-4_12-2_6-7_10-9_2-48_15-11_18-4_4-4_19-3_20-14.jpg" + }, + "user_dpp": 1 + }, + { + "id": 753217, + "text": "I read your CV and i called you for this interview but now i see that you are not qualified for this position.\nYou have to be certified by CISCO as a Software Developer.\n\nThat was the moment I knew he is a fucktard.", + "score": 76, + "created_time": 1501787368, + "attached_image": "", + "num_comments": 2, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 21823, + "user_username": "nofckingcluedev", + "user_score": 490, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-5_3-2_8-1_7-1_5-1_12-1_6-3_2-4_15-19_4-1.jpg" + } + }, + { + "id": 610982, + "text": "Today I was searching Android docs and found something interesting:\n\nTo write Android log statements, you use the android.util.Log class with the following methods:\n\nLog.v()\n\nLog.d()\n\n...\n\nLog.wtf() ๐Ÿ˜ฎ๐Ÿ˜ฎ๐Ÿ˜ฎ\n\nAccording to Google it seems to mean \"What a Terrible Failure\".\n\nWTF?!?!?!", + "score": 74, + "created_time": 1495700931, + "attached_image": "", + "num_comments": 13, + "tags": [ + "java", + "android", + "google i know what you wanna do", + "log.wtf()", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 393537, + "user_username": "spneshaei", + "user_score": 1042, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-2_16-6_3-14_8-1_7-1_5-1_12-2_6-2_10-1_2-87_4-1_19-1.jpg" + } + }, + { + "id": 246439, + "text": "Me: Junior, do not touch this server\nJunior: ok\n\n-- some weeks later -- \n\nJunior: the server does not boot!\nMe: What!?\nJunior: i have tried to make x and y changes\nMe: What!? In a production server?\nJunior: Yes \n\nWTF Junior!?", + "score": 73, + "created_time": 1477063636, + "attached_image": "", + "num_comments": 10, + "tags": [ + "wtf junior", + "production server", + "junior", + "server" + ], + "vote_state": 0, + "edited": false, + "user_id": 25750, + "user_username": "marodok", + "user_score": 3774, + "user_avatar": { + "b": "a973a2", + "i": "v-17_c-3_b-2_g-m_9-1_1-11_16-8_3-11_8-4_7-4_5-3_12-11_6-100_2-57_11-4_18-4_4-3_19-3_20-12_21-1.jpg" + }, + "user_dpp": 1 + }, + { + "id": 332994, + "text": "So how's your Monday going, devRant?", + "score": 73, + "created_time": 1481570550, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_332994_cGLCo.jpg", + "width": 747, + "height": 999 + }, + "num_comments": 15, + "tags": [ + "wtf", + "monday" + ], + "vote_state": 0, + "edited": false, + "user_id": 193256, + "user_username": "chenb0x", + "user_score": 1424, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-4_16-6_3-3_8-1_7-1_5-1_12-4_6-54_10-1_2-39_15-27_11-2_18-2_4-1_19-3.jpg" + } + }, + { + "id": 554615, + "text": "Someone fucking scratched my personal laptop. No one at home ready to take the responsibility. ๐Ÿ˜’", + "score": 70, + "created_time": 1493197410, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_554615_H1ziJ.jpg", + "width": 750, + "height": 1000 + }, + "num_comments": 25, + "tags": [ + "wtf guys" + ], + "vote_state": 0, + "edited": false, + "user_id": 538863, + "user_username": "TheLazyGoan", + "user_score": 2009, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-7_16-13_3-1_8-2_7-2_5-2_12-7_6-2_10-9_2-41_15-10_11-3_18-4_4-2_19-3_20-8_21-2.jpg" + } + }, + { + "id": 719138, + "text": "What the actual...?", + "score": 69, + "created_time": 1500298107, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_719138_GoiTV.jpg", + "width": 720, + "height": 590 + }, + "num_comments": 35, + "tags": [ + "wtf?" + ], + "vote_state": 0, + "edited": false, + "user_id": 234356, + "user_username": "elonmusk", + "user_score": 14074, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-2_1-1_16-13_3-3_8-4_7-4_5-4_12-1_6-14_10-9_2-49_15-10_11-3_18-4_4-4_19-3_20-13_21-1.jpg" + } + }, + { + "id": 456953, + "text": "That facepalm moment when the client rejects your code saying it's too small, and it might contain bugs.", + "score": 67, + "created_time": 1488467458, + "attached_image": "", + "num_comments": 9, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 439374, + "user_username": "aayusharyan", + "user_score": 4267, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-3_16-17_3-6_8-3_7-3_5-3_12-3_6-2_2-39_15-18_11-2_18-3_4-3_19-3.jpg" + } + }, + { + "id": 599194, + "text": "The guy in front of me is coding in C And is using BING\n\nLike wtf?!", + "score": 65, + "created_time": 1495100270, + "attached_image": "", + "num_comments": 12, + "tags": [ + "wtfisthis", + "wtf?", + "bing", + "bing?!", + "coding" + ], + "vote_state": 0, + "edited": false, + "user_id": 209145, + "user_username": "ThoughtfulDev", + "user_score": 830, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-1_3-1_8-1_7-1_5-1_12-2_6-1_10-5_2-71_18-1_4-1_19-1.jpg" + }, + "user_dpp": 1 + }, + { + "id": 504661, + "text": "Had this with a relative. His laptop wasn't turning on, with or without charger so he brought it back to the store to fix it. It ran elementary os by the way (detail for later). Then he got it back after a week and we booted it and it had windows 8 installed (wtf indeed). So we called the service desk to ask about it since the issue was a broken charger (!!!). Their reply: oh yeah there was a weird system installed on it so we thought we'd reset it as well for you. \n\nSERIOUSLY, THAT'S NOT YOUR FUCKING JOB!! \n\nHe is not tech savvy and he didn't know much about backups so that was literally about one year of work GONE. Yeah, I setup a cloud backup sync thingy for him right after that.", + "score": 64, + "created_time": 1490906567, + "attached_image": "", + "num_comments": 8, + "tags": [ + "tech service", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 22941, + "user_username": "linuxxx", + "user_score": 32147, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-3_3-3_8-3_7-3_5-4_12-2_6-7_10-9_2-48_15-11_18-4_4-4_19-3_20-14.jpg" + }, + "user_dpp": 1 + }, + { + "id": 696865, + "text": "Colegue could not find data when running a very important report.\nI did some research and found out that there was no data for for the month they searched . They ensured my boss that they did upload the data and that the program just does not work.\nI spent two days of work trying to find out what the problem could be, under boss's pressure.\nStill there was no data in the database or a record that there was ever any for that month.\nThe deadline passed. We got fined and only after that the colegue reilised that she had never generated the data, so there was not even data to upload. \nNow it is my fault cause I never told her that she needed to do that. \nI am the new guy and she has been working the same job for 7 years now. Like WTF", + "score": 64, + "created_time": 1499407895, + "attached_image": "", + "num_comments": 1, + "tags": [ + "report", + "rant", + "wtf", + "i can't even" + ], + "vote_state": 0, + "edited": false, + "user_id": 696801, + "user_username": "cimbaman", + "user_score": 239, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-2_16-3_3-1_8-1_7-1_5-1_12-2_6-3_2-14_15-11_11-2_18-1_4-1_19-1.jpg" + } + }, + { + "id": 789752, + "text": "JavaScript WTF #1", + "score": 63, + "created_time": 1503279890, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_789752_SmxX6.jpg", + "width": 431, + "height": 173 + }, + "num_comments": 18, + "tags": [ + "javascript", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 757528, + "user_username": "dimitarnestorov", + "user_score": 157, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-1_1-9_16-12_3-12_8-1_7-1_5-1_12-9_6-3_2-10_15-11_11-1_4-1.jpg" + } + }, + { + "id": 344274, + "text": "A app that will figure out what goes on in the mind of a woman", + "score": 58, + "created_time": 1482160601, + "attached_image": "", + "num_comments": 11, + "tags": [ + "wk31", + "wtf" + ], + "vote_state": 0, + "edited": true, + "weekly": { + "week": 31, + "topic": "Your dream project? (unlimited time/money)", + "date": "12/19/16", + "height": 50 + }, + "user_id": 344266, + "user_username": "Leeroy0011", + "user_score": 354, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-3_16-3_3-3_8-1_7-1_5-1_12-3_6-97_2-31_15-10_4-1.jpg" + } + }, + { + "id": 385758, + "text": "My company just asked me make a 4k VR headset in 30 days , i said ok and then went to bathroom and cried for like 5 mins straight", + "score": 58, + "created_time": 1484589891, + "attached_image": "", + "num_comments": 8, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 66981, + "user_username": "mohit3112", + "user_score": 314, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-3_16-1_3-13_8-1_7-1_5-1_12-3_6-9_2-12_4-1.jpg" + } + }, + { + "id": 225313, + "text": "International quality of code measurement? WTFs/minute", + "score": 57, + "created_time": 1476025755, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_225313_5QBY7.jpg", + "width": 500, + "height": 453 + }, + "num_comments": 2, + "tags": [ + "wtf code" + ], + "vote_state": 0, + "edited": false, + "user_id": 94733, + "user_username": "Rossko", + "user_score": 188, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-5_3-2_8-1_7-1_5-1_12-2_6-11_2-20_15-15_4-1.jpg" + } + }, + { + "id": 402579, + "text": "Who the fuck is able to not understand the basics of git. With ten years, supposedly , of front end experience. How in the hell do you not understand version control.", + "score": 55, + "created_time": 1485568290, + "attached_image": "", + "num_comments": 18, + "tags": [ + "wtf git" + ], + "vote_state": 0, + "edited": false, + "user_id": 136228, + "user_username": "Caprico", + "user_score": 1455, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-3_16-15_3-4_8-1_7-1_5-1_12-3_6-23_10-6_2-76_15-11_11-2_18-2_4-1_19-2_20-8.jpg" + } + }, + { + "id": 216791, + "text": "Not sure whether that's a good thing or not...", + "score": 55, + "created_time": 1475580639, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_216791_dkvXn.jpg", + "width": 798, + "height": 304 + }, + "num_comments": 8, + "tags": [ + "wtf", + "ubuntu" + ], + "vote_state": 0, + "edited": false, + "user_id": 158295, + "user_username": "codelis", + "user_score": 1152, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-3_3-2_8-2_7-2_5-1_12-2_6-3_10-5_2-12_11-1_4-1.jpg" + } + }, + { + "id": 44720, + "text": "I don't even...", + "score": 54, + "created_time": 1464686208, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_44720_uZpus.jpg", + "width": 650, + "height": 490 + }, + "num_comments": 9, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 27091, + "user_username": "chrome", + "user_score": 2798, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-1_1-3_16-14_3-1_8-3_7-3_5-1_12-3_6-98_10-9_2-50_18-4_4-1_19-3.jpg" + } + }, + { + "id": 680072, + "text": "WTF! ahahahahahah", + "score": 53, + "created_time": 1498659230, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_680072_JD4Xd.jpg", + "width": 680, + "height": 382 + }, + "num_comments": 8, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 527845, + "user_username": "garnakolegovitc", + "user_score": 574, + "user_avatar": { + "b": "7bc8a4" + } + }, + { + "id": 282809, + "text": "Me reviewing some high school level exams after an Excel course.\r\r\"hmmm the next question is 'what does the symbol $ mean when found inside a FORMULA in Excel' ... Let's see what they answered...\"\r\r* \"it's the symbol for DOLLARS\" <-- well, he tried\r* \"I don't know\" <-- mmh ok, he doesn't know\r* \"it can be either a plus or a minus\" <-- mmmh maybe the interpreter will just figure out the correct one\r* \"it's used to keep an index fixed when you copy/drag the formula\" <-- nice, someone who actually followed the lesson or at least knows how to google things when the teacher doesn't see\r* \"it's the symbol for POUNDS\" <-- WTF!! Wait a moment: POUNDS???? Have you ever lived a single moment in this world?", + "score": 53, + "created_time": 1478936396, + "attached_image": "", + "num_comments": 0, + "tags": [ + "excel wtf dollar" + ], + "vote_state": 0, + "edited": false, + "user_id": 37075, + "user_username": "ste09", + "user_score": 1210, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-1_16-16_3-11_8-2_7-2_5-1_12-1_6-3_10-9_2-42_15-19_18-2_4-1_19-2_20-1.jpg" + } + }, + { + "id": 754804, + "text": "I just had the most bizarre experience of my life. I handed in my resignation letter today. About an hour later, the CEO comes and collects me, takes me into his office, and rants about me leaving and other random not even tangentially related bullshit. Accusing me of not believing in the company and not talking to him. I have no idea if I'm fired.", + "score": 53, + "created_time": 1501858059, + "attached_image": "", + "num_comments": 11, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 368132, + "user_username": "ComradeEevee", + "user_score": 967, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-f_9-1_1-1_16-1_3-4_8-1_7-1_5-1_12-1_6-38_10-1_2-1_4-1.jpg" + } + }, + { + "id": 722145, + "text": "I had a weird dream last night where people communicated by using log statements.\n\nLike if I wanted to say something very loud I'd think Log.wtf(\"WTF!\") and it would appear in front of me like subtitles. Log levels were equivalent to tone level.\n\nThe scene was basically a bunch of people with log statements of various levels in front of them and their lips moving with no sound.\n\nI think I need a vacation.", + "score": 53, + "created_time": 1500410455, + "attached_image": "", + "num_comments": 2, + "tags": [ + "brain not ok", + "wtf", + "irl", + "logs" + ], + "vote_state": 0, + "edited": false, + "user_id": 591238, + "user_username": "UltimateZero", + "user_score": 336, + "user_avatar": { + "b": "a973a2", + "i": "v-17_c-3_b-2_g-m_9-1_1-2_16-1_3-6_8-1_7-1_5-1_12-2_6-31_10-1_2-13_15-43_18-1_4-1_19-1.jpg" + } + }, + { + "id": 87408, + "text": "the official Android documentation says it stands for \"What a Terrible Failure\"...sure it does Google, sure it does....", + "score": 52, + "created_time": 1467083596, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_87408_8W4YE.jpg", + "width": 800, + "height": 600 + }, + "num_comments": 0, + "tags": [ + "wtf", + "android" + ], + "vote_state": 0, + "edited": false, + "user_id": 67254, + "user_username": "uniplatapus", + "user_score": 453, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-15_3-2_8-1_7-1_5-1_12-1_6-3_10-5_2-14_15-53_11-1_4-1.jpg" + } + }, + { + "id": 284004, + "text": "Just checked my Google Analytics, WTF?", + "score": 52, + "created_time": 1479000543, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_284004_16igQ.jpg", + "width": 800, + "height": 368 + }, + "num_comments": 8, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 254841, + "user_username": "blauesocke", + "user_score": 2633, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-8_3-3_8-2_7-2_5-1_12-1_6-3_10-9_2-27_15-15_11-1_18-2_4-1_19-2.jpg" + } + }, + { + "id": 558414, + "text": "Is it just me, or is using --> for indentation just dumb?", + "score": 52, + "created_time": 1493375274, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_558414_dZ6PA.jpg", + "width": 407, + "height": 115 + }, + "num_comments": 7, + "tags": [ + "wtf", + "indentation" + ], + "vote_state": 0, + "edited": false, + "user_id": 543558, + "user_username": "penguin", + "user_score": 2425, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-1_1-1_16-6_3-3_8-2_7-2_5-2_12-1_6-10_2-18_18-3_4-2_19-3_20-10.jpg" + } + }, + { + "id": 294956, + "text": "When the entire platform mysteriously goes down for a half hour at 11pm ON A SATURDAY AND YOU'RE THE ONLY PERSON WHO WORKS ON IT GOOD GOD SERIOUSLY YOU'VE GOT TO BE SHITTING ME I JUST WANT TO SLEEP\n\nWHOEVER DID THIS I WILL FIND YOU AND I WILL KILL YOU", + "score": 52, + "created_time": 1479627140, + "attached_image": "", + "num_comments": 5, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 19955, + "user_username": "luminous-flux", + "user_score": 305, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-6_3-2_8-1_7-1_5-1_12-2_6-7_2-40_15-11_18-1_4-1_19-1.jpg" + } + }, + { + "id": 786502, + "text": "So my friend, who owns a restaurant, asked me over 6 months ago, if i could redesign his homepage. I told him \"sure why not\" and since we're friends i didn't want him to pay me any money.\n\nHe told me what his thoughts about the design were and i told him that i needed the menu, some decent pictures of the restaurant, the \"about us\" story and the credentials to the server. \n\nHe didn't know the credentials to his server and i told him to ask the person, who made that page to send me the information i needed, but he kept on saying \"could you call her because blah blah\". Well, i did but she couldn't give me that info without asking the owner. So i met him and told him \"hey i told you so, because it's completely normal not give sensible information to unknown people and besides that she told me to tell you that you should give her a call, because she hasn't got your new phone number\". Two months later i got an email with the credentials, but still no menu and no pictures.\n\nFour days ago i made a transition page, because i didn't want to publish the page with stock images and without menu, so i wrote him again whether he wanted design #1 or #2. Got a text at ~21:00 saying \"design 2, but you need to publish it at 22:00\".\n\nI mean wtf?! He assured me he would call some people he knows to get those things. I told him, that it would be free, because of our friendship, but no support from him and he keeps stressing?! He knows i've got a full-time job and my studies going on, so my time is really limited and he keeps fking around like that?! Man it pisses me really off...", + "score": 50, + "created_time": 1503126379, + "attached_image": "", + "num_comments": 13, + "tags": [ + "wtf moments" + ], + "vote_state": 0, + "edited": false, + "user_id": 783361, + "user_username": "k3sh", + "user_score": 62, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-6_3-2_8-1_7-1_5-1_12-1_6-3_10-1_2-19_15-19_11-2_4-1.jpg" + } + }, + { + "id": 568387, + "text": "whatsapp down, HELP!... LOL ahahahaha", + "score": 49, + "created_time": 1493848855, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_568387_uRbRn.jpg", + "width": 673, + "height": 799 + }, + "num_comments": 3, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 527845, + "user_username": "garnakolegovitc", + "user_score": 574, + "user_avatar": { + "b": "7bc8a4" + } + }, + { + "id": 275616, + "text": "Sure. Just unplug my ethernet cable without asking. Not like I'm trying to work or anything, right?", + "score": 48, + "created_time": 1478537280, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 16085, + "user_username": "martin", + "user_score": 416, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-3_16-3_3-2_8-1_7-1_5-1_12-3_6-77_10-1_2-1_4-1.jpg" + } + }, + { + "id": 426580, + "text": "My brother, as a 23 year old trying to apply for mechanical engineering jobs, came across this posting! \n\nThat's why they say, start young, kids :P", + "score": 46, + "created_time": 1486964153, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_426580_aFtCh.jpg", + "width": 750, + "height": 1000 + }, + "num_comments": 9, + "tags": [ + "wtf", + "ayfwm" + ], + "vote_state": 0, + "edited": false, + "user_id": 241425, + "user_username": "srshah19", + "user_score": 936, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-6_3-2_8-1_7-1_5-1_12-2_6-6_10-1_2-3_15-18_4-1.jpg" + } + }, + { + "id": 177232, + "text": "The upside of having to go through code you wrote years ago (while mumbling WTFs) is that you realize how far you've come in those years.", + "score": 44, + "created_time": 1473531523, + "attached_image": "", + "num_comments": 1, + "tags": [ + "legacy", + "neverfullylearned", + "wtf", + "givemeanavatar" + ], + "vote_state": 0, + "edited": true, + "user_id": 50974, + "user_username": "slugger", + "user_score": 317, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-1_1-2_16-5_3-3_8-1_7-1_5-1_12-2_17-1_6-55_10-1_2-10_15-15_18-1_4-1_19-1.jpg" + } + }, + { + "id": 128168, + "text": "A guy asked me to develop an uber-like app... he offered me 1000 bucks to do it", + "score": 42, + "created_time": 1470107722, + "attached_image": "", + "num_comments": 10, + "tags": [ + "really", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 121049, + "user_username": "chekostyle", + "user_score": 154, + "user_avatar": { + "b": "a973a2", + "i": "v-17_c-3_b-2_g-m_9-1_1-4_16-6_3-6_8-1_7-1_5-1_12-4_6-10_2-31_15-42_4-1.jpg" + } + }, + { + "id": 531195, + "text": "Boss:\"Build templates that convert our 1000+ pages built for desktop, into responsive ones that work on large screen and phones. \"\n\nFinished...Run the company's code scanner and it reports violations of development rules (which was expected)\n\nI go to request exemptions....\n\n\"Oh, no you can't use JavaScript or CSS that uses position.\"", + "score": 40, + "created_time": 1492220528, + "attached_image": "", + "num_comments": 1, + "tags": [ + "#wtf #fml" + ], + "vote_state": 0, + "edited": false, + "user_id": 14984, + "user_username": "rantmaster", + "user_score": 210, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-2_16-1_3-1_8-1_7-1_5-1_12-2_6-15_10-1_2-18_15-19_4-1.jpg" + } + }, + { + "id": 166581, + "text": "While drinking in Lisbon a dude asks me if I'm more of front-end or back-end and winks... Wtf?!!", + "score": 40, + "created_time": 1472947207, + "attached_image": "", + "num_comments": 4, + "tags": [ + "wtf", + "back-end", + "front-end", + "nohomo" + ], + "vote_state": 0, + "edited": false, + "user_id": 156754, + "user_username": "fdomingues", + "user_score": 105, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-1_16-4_3-3_8-1_7-1_5-1_12-1_6-10_10-1_2-36_15-14_11-1_4-1.jpg" + } + }, + { + "id": 169491, + "text": "On my first day at work realising that I would be working on a code base with 1.5 million+ lines of code and the only documentation is half a paragraph some guy wrote the day before he left ๐Ÿ˜‘", + "score": 40, + "created_time": 1473101958, + "attached_image": "", + "num_comments": 3, + "tags": [ + "wtf", + "wk16" + ], + "vote_state": 0, + "edited": false, + "weekly": { + "week": 16, + "topic": "Worst documentation you've dealt with?", + "date": "9/5/16", + "height": 50 + }, + "user_id": 12372, + "user_username": "bt141516", + "user_score": 139, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-2_3-3_8-1_7-1_5-1_12-2_6-3_10-1_2-1_11-2_4-1.jpg" + } + }, + { + "id": 246534, + "text": "What in fuck!!! Production better not break, cause I can't push and pull shit.", + "score": 38, + "created_time": 1477067453, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_246534_uAx17.jpg", + "width": 750, + "height": 1000 + }, + "num_comments": 6, + "tags": [ + "#wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 3563, + "user_username": "reticentroot", + "user_score": 190, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-3_3-1_8-1_7-1_5-1_12-2_6-10_10-1_2-39_15-27_11-2_18-1_4-1_19-1.jpg" + } + }, + { + "id": 502947, + "text": "Get this, at college we were told to use PHP7 for development right? All good, PHP7 is awesome and all, so we have to make a big project for college and put it on the school's server... now here's the pickle...\n\nTHEIR SERVER RUNS FUCKING PHP5.X\n\nWTF!\n\nARE YOU FUCKING KIDDING ME? DON'T FUCKING TELL US WE SHOULD USE PHP7 AND THEN DON'T SUPPORT PHP7 FUCK FUCK FUCK FUCK", + "score": 38, + "created_time": 1490819785, + "attached_image": "", + "num_comments": 9, + "tags": [ + "college", + "php5", + "this shit", + "wtf", + "php7", + "schools suck", + "shit" + ], + "vote_state": 0, + "edited": false, + "user_id": 35661, + "user_username": "liammartens", + "user_score": 5017, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-1_16-3_3-4_8-1_7-1_5-3_12-1_6-2_10-5_2-39_11-1_18-1_4-3_19-2_20-5.jpg" + } + }, + { + "id": 668338, + "text": "Came back from vacation today to find out that some FUCKTURD PIECE OF SHIT deleted my virtual server!! Tried to find any traces on who that SHITFACED NUTSACK was without luck. This server is hosting several websites, some having files and data stretching over more than 10 years! Spent the day praying to GOD that my equally old backup scripts had run and where the FLYING FUCK those files were saved. Luckily the script had worked and I found a recent backup so now I can start the restore process on another machine. But still. WTF!!??", + "score": 37, + "created_time": 1498160185, + "attached_image": "", + "num_comments": 5, + "tags": [ + "server deleted wtf backup" + ], + "vote_state": 0, + "edited": false, + "user_id": 54767, + "user_username": "obia", + "user_score": 874, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-6_3-6_8-1_7-1_5-1_12-2_17-1_6-53_10-1_2-56_15-26_18-1_4-1_19-2.jpg" + }, + "user_dpp": 1 + }, + { + "id": 572361, + "text": "When you are watching porn on MacOS and suddenly Android UI pops up! \n\nTurn Off!!!", + "score": 36, + "created_time": 1494009970, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_572361_h5JJy.jpg", + "width": 602, + "height": 502 + }, + "num_comments": 3, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 422162, + "user_username": "ganapativs", + "user_score": 1503, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-2_16-9_3-6_8-2_7-2_5-1_12-2_6-14_10-7_2-42_15-37_11-1_18-2_4-1_19-2.jpg" + } + }, + { + "id": 50148, + "text": "That moment when you are at the hospital and your wife just gave birth. Boss is calling:\n-Hi! congratulations on a baby!\n-Thanks.\n-I need something quick from you...\n*connects me to live presentation with a client*\n\nI said \"Hi everyone!\" and ended the call...\nDidn't pick up since..", + "score": 36, + "created_time": 1464950672, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_50148_ExaHD.jpg", + "width": 659, + "height": 593 + }, + "num_comments": 6, + "tags": [ + "wtf???" + ], + "vote_state": 0, + "edited": false, + "user_id": 16888, + "user_username": "Raspik", + "user_score": 2380, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-11_3-4_8-2_7-2_5-2_12-2_6-82_10-5_2-49_15-6_11-3_18-4_4-2_19-3.jpg" + } + }, + { + "id": 111229, + "text": "Government website only open certain hours of the day? WTF?", + "score": 35, + "created_time": 1468707452, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_111229_yx1vP.jpg", + "width": 765, + "height": 283 + }, + "num_comments": 5, + "tags": [ + "wtf", + "government", + "irs" + ], + "vote_state": 0, + "edited": false, + "user_id": 108133, + "user_username": "spo81ty", + "user_score": 34, + "user_avatar": { + "b": "a973a2" + } + }, + { + "id": 222060, + "text": "Starting a third year computer science course. Lesson 1 of web API production. Git. Lecturer tells us to do a different branch for every commit so it's easy to roll back. \n\nNice.", + "score": 35, + "created_time": 1475843041, + "attached_image": "", + "num_comments": 9, + "tags": [ + "wtf", + "git" + ], + "vote_state": 0, + "edited": false, + "user_id": 37343, + "user_username": "craig939393", + "user_score": 808, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-3_16-7_3-1_8-1_7-1_5-1_12-3_6-15_2-10_15-11_11-2_18-2_4-1_19-1.jpg" + } + }, + { + "id": 229362, + "text": "Who the fuck doesn't use responsive design. Oh yeah my Job. Thank God I quit Friday. Fuck this old ass dirt company", + "score": 35, + "created_time": 1476218214, + "attached_image": "", + "num_comments": 4, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": true, + "user_id": 34836, + "user_username": "xociety", + "user_score": 662, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-5_16-4_3-4_8-1_7-1_5-1_12-5_17-1_6-50_10-2_2-40_15-10_11-2_18-2_4-1_19-1.jpg" + } + }, + { + "id": 276773, + "text": "So my school network is blocking Google Drive, but not OneDrive... Okay, I guess?", + "score": 35, + "created_time": 1478595790, + "attached_image": "", + "num_comments": 24, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 264365, + "user_username": "athlon", + "user_score": 4577, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-1_16-8_3-1_8-4_7-4_5-3_12-1_6-3_10-9_2-54_15-11_18-4_4-3_19-3_20-3.jpg" + } + }, + { + "id": 353928, + "text": "My study's logic every fucking time: (I'm a senior by the way)\n\nJunior: Sir, could you help me out for a minute?\n\nTeacher: I'm busy right now, please fill out the support request form and go ask one of the seniors (yeah, not even kidding)\n\nJunior: Alright, hey dude, could you help me out maybe?\n\nMe: yeah of course, just get your laptop and go sit here next to me!\n\nOther Teacher: Hey you, leave the seniors alone, they've got their own work!", + "score": 35, + "created_time": 1482748953, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_353928_c1a6f.jpg", + "width": 251, + "height": 201 + }, + "num_comments": 2, + "tags": [ + "wtf", + "study" + ], + "vote_state": 0, + "edited": false, + "user_id": 22941, + "user_username": "linuxxx", + "user_score": 32147, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-3_3-3_8-3_7-3_5-4_12-2_6-7_10-9_2-48_15-11_18-4_4-4_19-3_20-14.jpg" + }, + "user_dpp": 1 + }, + { + "id": 385458, + "text": "Why are modern apps so god damn huge? A fucking messaging app doesn't require 500000000000000 MB!!! I wonder what shit load of fuck they're packing in those apps.", + "score": 35, + "created_time": 1484578030, + "attached_image": "", + "num_comments": 15, + "tags": [ + "app", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 95042, + "user_username": "compilergeek", + "user_score": 624, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-2_16-1_3-1_8-1_7-1_5-1_12-2_6-2_10-3_2-18_11-2_18-2_4-1_19-1.jpg" + } + }, + { + "id": 298534, + "text": "Client: \"My proposed deadline can't be met? Maybe we should go to your office and sit right next to you to see how you work.\"\nYeah right, that surely speeds things up. Even if the ever-changing requirements require at least triple of the time allowed.", + "score": 34, + "created_time": 1479817097, + "attached_image": "", + "num_comments": 4, + "tags": [ + "wtf", + "clients" + ], + "vote_state": 0, + "edited": false, + "user_id": 292119, + "user_username": "whocareswhoiam", + "user_score": 356, + "user_avatar": { + "b": "a973a2", + "i": "v-17_c-3_b-2_g-f_9-1_1-2_16-15_3-3_8-1_7-1_5-1_12-2_6-2_10-1_2-10_11-2_4-1.jpg" + } + }, + { + "id": 736402, + "text": "var { name: x } = person\nDay 1 : that's some good ES6 code man, I'm so 2017\nDay 5 : Oh yeah I think it works, dont really remember \nDay 17 : WTF is that ? Is that even Javascript ?", + "score": 34, + "created_time": 1501018075, + "attached_image": "", + "num_comments": 10, + "tags": [ + "refactoring", + "javascript", + "es6", + "wtf?" + ], + "vote_state": 0, + "edited": false, + "user_id": 720820, + "user_username": "AdrienITTS", + "user_score": 699, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-1_16-10_3-1_8-1_7-1_5-1_12-1_6-3_10-7_2-28_15-19_18-2_4-1_19-1.jpg" + } + }, + { + "id": 665433, + "text": "Didn't do anything today, so someone referred to me as \"more useless than a knitted condom\"", + "score": 34, + "created_time": 1498053078, + "attached_image": "", + "num_comments": 3, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 429783, + "user_username": "aile11", + "user_score": 1594, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-17_3-2_8-2_7-2_5-2_12-2_6-3_2-12_15-19_11-4_18-3_4-2_19-1_20-5.jpg" + } + }, + { + "id": 63745, + "text": "When you develop top-notch software but it won't perform well because your server is like", + "score": 33, + "created_time": 1465495243, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_63745_tQVwA.jpg", + "width": 500, + "height": 430 + }, + "num_comments": 2, + "tags": [ + "server", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 63042, + "user_username": "dennie170", + "user_score": 306, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-1_16-10_3-7_8-1_7-1_5-1_12-1_6-1_10-1_2-1_15-9_4-1.jpg" + } + }, + { + "id": 794889, + "text": "When I'm programming, I wonder how many times I say \"what the fuck\" in an hour", + "score": 32, + "created_time": 1503487170, + "attached_image": "", + "num_comments": 8, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 100806, + "user_username": "jelleken", + "user_score": 570, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-1_16-6_3-1_8-1_7-1_5-1_12-1_6-81_10-5_2-82_11-1_18-1_4-1_19-1.jpg" + } + }, + { + "id": 60028, + "text": "When your program is well packaged and runs on even the 13 year old XP machine you keep around for testing these things, but somehow a customer has a Windows PC so shitty it won't run it.", + "score": 31, + "created_time": 1465329510, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_60028_g7UiQ.jpg", + "width": 400, + "height": 341 + }, + "num_comments": 0, + "tags": [ + "wtf", + "windows" + ], + "vote_state": 0, + "edited": false, + "user_id": 9046, + "user_username": "jeeper", + "user_score": 1572, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-2_16-9_3-1_8-1_7-1_5-1_12-2_6-11_10-7_2-52_15-19_18-2_4-1_19-2_20-7.jpg" + } + }, + { + "id": 442623, + "text": "What if we're all expanding with the universe but no one notices cause we all scale up together?", + "score": 31, + "created_time": 1487768714, + "attached_image": "", + "num_comments": 10, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 321452, + "user_username": "nikolatesla", + "user_score": 11405, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-2_1-3_16-6_3-2_8-4_7-4_5-2_12-3_6-10_10-9_2-40_18-4_4-2_19-3_20-8.jpg" + } + }, + { + "id": 297551, + "text": "MFW broken stuff automagically worked.", + "score": 30, + "created_time": 1479759189, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_297551_Ryj1o.jpg", + "width": 220, + "height": 200 + }, + "num_comments": 1, + "tags": [ + "wtf happened" + ], + "vote_state": 0, + "edited": false, + "user_id": 232702, + "user_username": "Xanaxiel", + "user_score": 141, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-3_16-6_3-1_8-1_7-1_5-1_12-3_6-38_2-1_15-10_11-2_4-1.jpg" + } + }, + { + "id": 192466, + "text": "The other day I was looking for my wallet and the first thing that crossed my mind was \"Ctrl + F\" please tell me I'm not crazy", + "score": 29, + "created_time": 1474381454, + "attached_image": "", + "num_comments": 5, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 100600, + "user_username": "cube5", + "user_score": 786, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-4_16-10_3-13_8-1_7-1_5-1_12-4_17-1_6-10_10-3_2-91_15-14_18-1_4-1_19-1.jpg" + } + }, + { + "id": 609880, + "text": "In 2017, who the hell goes to market with an app written in VB using SQL?????? Especially in the IoT space.\nAre you kidding me? Even back when this project started, it was a dead language already.\n\nI can't even.", + "score": 29, + "created_time": 1495640825, + "attached_image": "", + "num_comments": 6, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 5939, + "user_username": "lewdogg", + "user_score": 813, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-1_16-1_3-1_8-1_7-1_5-1_12-1_6-60_2-17_4-1.jpg" + } + }, + { + "id": 515207, + "text": "Who the fuck just restarted the wifi without notice?", + "score": 28, + "created_time": 1491474821, + "attached_image": "", + "num_comments": 7, + "tags": [ + "wtf man" + ], + "vote_state": 0, + "edited": false, + "user_id": 178420, + "user_username": "bdhobare", + "user_score": 10753, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-2_16-10_3-11_8-1_7-1_5-4_12-2_6-11_10-5_2-14_18-1_4-4_19-2_20-9.jpg" + } + }, + { + "id": 726103, + "text": "A LOT of this article makes me fairly upset. (Second screenshot in comments). Sure, Java is difficult, especially as an introductory language, but fuck me, replace it with ANYTHING OTHER THAN JAVASCRIPT PLEASE. JavaScript is not a good language to learn from - it is cheaty and makes script kiddies, not programmers. Fuck, they went from a strong-typed, verbose language to a shit show where you can turn an integer into a function without so much as a peep from the interpreter.\n\nAnd fUCK ME WHY NOT PYTHON?? It's a weak typed but dynamic language that FORCES good indentation and actually has ACCESS TO THE FILE SYSTEM instead of just the web APIs that don't let you do SHIT compared to what you SHOULD learn.\n\nOH AND TO PUT THE ICING ON THE CAKE, the article was comparing hello worlds, and they did the whole Java thing right but used ALERT instead of CONSOLE.LOG for JavaScript??? Sure, you can communicate with the user that way too but if you're comparing the languages, write text to the console in both languages, don't write text to the console in Java and use the alert api in JavaScript.\n\nFuck you Stanford, I expected better you shitty cockmunchers.", + "score": 28, + "created_time": 1500563991, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_726103_Pwpas.jpg", + "width": 708, + "height": 999 + }, + "num_comments": 33, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 374269, + "user_username": "AlgoRythm", + "user_score": 11273, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-2_16-9_3-1_8-3_7-3_5-4_12-2_6-11_2-47_4-4_19-1_20-11.jpg" + }, + "user_dpp": 1 + }, + { + "id": 401996, + "text": "When you're working on a very complex client/server project in 7 open Solutions and changes in about 100 classes and your project manger gives you a \"very important text align issue\" that you have to fix immediately ๐Ÿ˜’", + "score": 27, + "created_time": 1485534646, + "attached_image": "", + "num_comments": 1, + "tags": [ + "#wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 321442, + "user_username": "Mescon", + "user_score": 37, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-1_16-1_3-1_8-1_7-1_5-1_12-1_6-11_2-26_4-1.jpg" + } + }, + { + "id": 352875, + "text": "Windows 10 just notified me that my laptop battery is at 6% and shut it down immediately. No warning before that.", + "score": 27, + "created_time": 1482675932, + "attached_image": "", + "num_comments": 3, + "tags": [ + "wtf microsoft" + ], + "vote_state": 0, + "edited": false, + "user_id": 202624, + "user_username": "franga2000", + "user_score": 3081, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-6_3-2_8-2_7-2_5-2_12-1_6-4_10-1_2-27_18-3_4-2_19-2_21-2.jpg" + } + }, + { + "id": 32970, + "text": "\"The aim is to develop highly robust data streams so we have the flexibility to build and evolve the user interface without having to change code in the API\" \n\nOh, is that all you need?", + "score": 27, + "created_time": 1463760439, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_32970_H7KY6.jpg", + "width": 500, + "height": 414 + }, + "num_comments": 4, + "tags": [ + "wtf", + "requirements" + ], + "vote_state": 0, + "edited": false, + "user_id": 19563, + "user_username": "tdev4life", + "user_score": 52, + "user_avatar": { + "b": "f99a66" + } + }, + { + "id": 130638, + "text": "javascript... the language where your code works even if you forget ';' and declaring your variables.\n\nwtf", + "score": 26, + "created_time": 1470322903, + "attached_image": "", + "num_comments": 7, + "tags": [ + "wtf", + "iwanttodie", + "js" + ], + "vote_state": 0, + "edited": false, + "user_id": 102282, + "user_username": "liverreich", + "user_score": 859, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-1_3-1_8-1_7-1_5-1_12-2_6-31_2-10_15-19_18-1_4-1_19-2.jpg" + } + }, + { + "id": 174795, + "text": "API returns two date time in json (x-date-time, y-date-time) both of which are coming wrong.\nInstructions for consuming API :\nTake date part from y-date-time and time part from x-date-time and combine them so you would have the desired result", + "score": 26, + "created_time": 1473392779, + "attached_image": "", + "num_comments": 1, + "tags": [ + "wtf", + "fml" + ], + "vote_state": 0, + "edited": false, + "user_id": 66108, + "user_username": "humble-progrmr", + "user_score": 230, + "user_avatar": { + "b": "7bc8a4" + } + }, + { + "id": 153181, + "text": "just found this in some code running in production.\n\n// This is a single comment.", + "score": 25, + "created_time": 1472073502, + "attached_image": "", + "num_comments": 5, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 152430, + "user_username": "GooseNinja", + "user_score": 46, + "user_avatar": { + "b": "d55161" + } + }, + { + "id": 295348, + "text": "Been programming Java for a few weeks now, and WTF is this, Java?!\n\"Example\".equals(\"Example\")\n\nWhat is wrong with the form that a dozen of other languages use?\n\"Example\" == \"Example\"\n\nP. S. If you don't know Java, the latter one compares for the type of objects and always returns true in this case.", + "score": 24, + "created_time": 1479649058, + "attached_image": "", + "num_comments": 19, + "tags": [ + "java", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 271612, + "user_username": "DRSDavidSoft", + "user_score": 3548, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-6_3-8_8-1_7-1_5-1_12-1_6-4_10-5_2-17_11-1_4-1_19-1.jpg" + } + }, + { + "id": 213945, + "text": "When you start inspecting code of the page you are using, and you find something like this... \r\nNot sure if I want to make this reservation anymore...", + "score": 24, + "created_time": 1475440163, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_213945_NfsuP.jpg", + "width": 777, + "height": 21 + }, + "num_comments": 1, + "tags": [ + "wtf", + "js" + ], + "vote_state": 0, + "edited": false, + "user_id": 78976, + "user_username": "hayz", + "user_score": 924, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-1_1-1_16-1_3-4_8-1_7-1_5-1_12-1_6-52_10-5_2-18_15-12_11-2_18-1_4-1_19-2.jpg" + } + }, + { + "id": 182017, + "text": "What the literal fuck? This is an ad targeting devs. They don't even know what a fucking programming language is.\n\nhttps://youtube.com/watch/...", + "score": 24, + "created_time": 1473816656, + "attached_image": "", + "num_comments": 4, + "tags": [ + "wtf?" + ], + "vote_state": 0, + "edited": false, + "links": [ + { + "type": "url", + "url": "https://www.youtube.com/watch?v=Me3B1cqZfW0", + "short_url": "https://youtube.com/watch/...", + "title": "https://youtube.com/watch/...", + "start": 115, + "end": 144, + "special": 1 + } + ], + "special": true, + "user_id": 121120, + "user_username": "FelisPhasma", + "user_score": 3783, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-1_16-14_3-12_8-3_7-3_5-3_12-1_17-2_6-3_10-5_2-39_11-4_4-3.jpg" + } + }, + { + "id": 527787, + "text": "I can't change the installation path?!\nMicrosoft i hate you so God damn hard...WTF", + "score": 24, + "created_time": 1492049233, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_527787_cqZkE.jpg", + "width": 750, + "height": 1000 + }, + "num_comments": 15, + "tags": [ + "rant", + "microsoft", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 169243, + "user_username": "Liz3", + "user_score": 675, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-9_16-6_3-14_8-1_7-1_5-1_12-9_6-4_10-1_2-92_11-2_18-2_4-1_19-1.jpg" + } + }, + { + "id": 688547, + "text": "Hum, I'm quite new here, wtf is going on with these rubber ducks", + "score": 23, + "created_time": 1499034118, + "attached_image": "", + "num_comments": 6, + "tags": [ + "wtf", + "nice butt", + "lol", + "rubber duck" + ], + "vote_state": 0, + "edited": false, + "user_id": 666268, + "user_username": "Eyra", + "user_score": 31, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-7_16-11_3-4_8-1_7-1_5-1_12-7_6-10_10-1_2-36_15-2_11-1_4-1.jpg" + } + }, + { + "id": 309282, + "text": "It rains today thus university's server goes offline again... \n#precisionengineering\n", + "score": 23, + "created_time": 1480332851, + "attached_image": "", + "num_comments": 1, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 150541, + "user_username": "zarkopafilis", + "user_score": 1280, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-2_3-1_8-1_7-1_5-1_12-2_6-3_10-8_2-25_15-19_18-1_4-1_19-2_20-11.jpg" + } + }, + { + "id": 653894, + "text": "In my country, almost every college student is expected to finish their degree and apply for an internship, with some universities forcing them to do it and making it a requirement to finish their studies. \n\nNow, this wouldn't be so bad if almost every internship employer in the country didn't expect you to work for free. Seriously, I can estimate 80% of the internships pay you NOTHING. WTF. \n\nFortunately this is not the case for CS, but every time I tell somebody I recently started an internship, they will ask me: \"Oh, but they don't pay you anything, do they?\". Of course they pay me! I wouldn't be going to an office every day for 4 hours to do someone else's work if they didn't!! \n\nWhy the fuck is it even legal to employ somebody and not pay them a cent, just because \"it will look good on your resume\"?? And why do people still accept this shit?? \n\nIs is like that on other countries as well?", + "score": 23, + "created_time": 1497603509, + "attached_image": "", + "num_comments": 3, + "tags": [ + "internships", + "wtf is up with spain" + ], + "vote_state": 0, + "edited": false, + "user_id": 368626, + "user_username": "ocab19", + "user_score": 1335, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-6_3-3_8-2_7-2_5-1_12-2_6-43_10-6_2-54_18-2_4-1_19-2.jpg" + }, + "user_dpp": 1 + }, + { + "id": 502061, + "text": "@Csaki My Friend's dual monitor system...", + "score": 22, + "created_time": 1490786276, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_502061_cyUqX.jpg", + "width": 473, + "height": 841 + }, + "num_comments": 14, + "tags": [ + "wtf?" + ], + "vote_state": 0, + "edited": false, + "user_id": 500781, + "user_username": "Linux1230", + "user_score": 63, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-1_1-1_16-1_3-1_8-1_7-1_5-1_12-1_6-10_10-1_2-10_15-14_11-2_4-1.jpg" + } + }, + { + "id": 685411, + "text": "แบž, capital sharp s, in whatsapp for android..", + "score": 22, + "created_time": 1498890151, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_685411_mf4vU.jpg", + "width": 799, + "height": 449 + }, + "num_comments": 13, + "tags": [ + "wtf!" + ], + "vote_state": 0, + "edited": false, + "user_id": 527845, + "user_username": "garnakolegovitc", + "user_score": 574, + "user_avatar": { + "b": "7bc8a4" + } + }, + { + "id": 558822, + "text": "I'm currently developing an integration to an API and I found out a bug. \n\nSo I asked the contact person for the API if this feature was intentional. I did this on their forum for the API and I sent it as a private message to him.\n\nI got a response telling me that they dont have the \"sufficient details\" to answer my question and that they would investigate the feature \"comprehensivley\".\n\n5 minutes later I see a post on the forum in the developer section from the contact dude and it was my fucking question. \n\nSo now he's asking the users of the API if his and his companys own bug is intentional. \nWhat the actual fuck?! \nIs this how you investigate things \"comprehensivley\"?", + "score": 22, + "created_time": 1493389323, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wtf?" + ], + "vote_state": 0, + "edited": false, + "user_id": 50263, + "user_username": "biglars", + "user_score": 94, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-3_16-5_3-5_8-1_7-1_5-1_12-3_6-3_2-10_15-19_11-1_4-1.jpg" + } + }, + { + "id": 232884, + "text": "So I went with a friend to a printing company today and asked this gem: \"so , will you print the white too?\" \n\nBefore he answered I realized my momentary retardness and said: \"wtf, what a stupid question.\"\n\nI think I need some rest ๐Ÿ˜‚๐Ÿ˜‚", + "score": 22, + "created_time": 1476403010, + "attached_image": "", + "num_comments": 2, + "tags": [ + "trump", + "sleep", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 26258, + "user_username": "azous", + "user_score": 5762, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-3_16-6_3-1_8-1_7-1_5-4_12-3_6-52_10-5_2-50_11-4_18-4_4-4_19-3_20-5_21-2.jpg" + } + }, + { + "id": 172743, + "text": "Apple: rebranding our competitors features as brand new since we can't think of anything new. this keynote is making me so angry.", + "score": 22, + "created_time": 1473273691, + "attached_image": "", + "num_comments": 1, + "tags": [ + "apple", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 141846, + "user_username": "bassclarinet42", + "user_score": 114, + "user_avatar": { + "b": "a973a2", + "i": "v-17_c-3_b-2_g-m_9-1_1-3_16-2_3-1_8-1_7-1_5-1_12-3_6-16_10-1_2-3_15-16_11-1_4-1.jpg" + } + }, + { + "id": 201200, + "text": "Why do so many people use macs for developing? \nI really don't get it.", + "score": 21, + "created_time": 1474812578, + "attached_image": "", + "num_comments": 20, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": true, + "user_id": 199725, + "user_username": "Yast2", + "user_score": 304, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-3_16-1_3-1_8-1_7-1_5-1_12-3_6-11_10-1_2-26_15-11_4-1.jpg" + } + }, + { + "id": 474088, + "text": "Code Quality: Metrics that Matter (via CodeChef)", + "score": 21, + "created_time": 1489393955, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_474088_WCnhz.jpg", + "width": 500, + "height": 471 + }, + "num_comments": 1, + "tags": [ + "wtf", + "metrics" + ], + "vote_state": 0, + "edited": false, + "user_id": 16188, + "user_username": "armaged", + "user_score": 285, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-2_16-1_3-1_8-1_7-1_5-1_12-2_6-97_2-19_15-10_4-1.jpg" + } + }, + { + "id": 739820, + "text": "fck you visual studio!!! seriously what is wrong with you?!? \n~me peacfully writing some code ~ \nok let's see what we did \nvs: I can't compile that. The key whateverKey in line 15 is not defined. \nme: ok let's investigate... \nnowhere in line 15 use whateverKey.... ok.... \nwait I didn't change that file at all. \n~me clicking rebuild solution~ \nvs: can't build that because of whateverKey in line 15. \nme : WTF?!? \nchecking git diff -> file not changed \nme okkkkkkk...... \nclosing visual studio and reopening solution. \nBuild succeeded. \nWhat the actuall hell?!? \nI'm spending way too much time trying to get that shity peace of software to do what it is supposed to do!", + "score": 21, + "created_time": 1501168637, + "attached_image": "", + "num_comments": 6, + "tags": [ + "build", + "wtf", + "vs", + "visual studio" + ], + "vote_state": 0, + "edited": false, + "user_id": 721912, + "user_username": "JinaJita", + "user_score": 190, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-f_9-1_1-9_16-6_3-1_8-1_7-1_5-1_12-9_6-38_2-14_11-1_18-1_4-1_19-1.jpg" + } + }, + { + "id": 162857, + "text": "The moment you enable the PHP warnings in your company and you want to delete all your coworkers code. \n\n#why #you #do #this #to #me", + "score": 20, + "created_time": 1472730181, + "attached_image": "", + "num_comments": 3, + "tags": [ + "php", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 162846, + "user_username": "markus", + "user_score": 178, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-1_1-1_16-6_3-3_8-1_7-1_5-1_12-1_6-19_10-1_2-12_15-15_4-1.jpg" + } + }, + { + "id": 72666, + "text": "Teaching the apprentice how to use Microsoft Word. \n\nFaith in humanity deleted.", + "score": 20, + "created_time": 1466028497, + "attached_image": "", + "num_comments": 0, + "tags": [ + "boring", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 59755, + "user_username": "0xH4lcyon", + "user_score": 1453, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-7_3-3_8-1_7-1_5-1_12-1_6-14_2-42_15-18_4-1.jpg" + } + }, + { + "id": 634097, + "text": "Client doesn't have any idea what pages/links they want for their website. Same client told me to call the guy who recommended me to him for the project brief...wtf...I mean WTF!!!", + "score": 20, + "created_time": 1496788037, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_634097_Jnkft.jpg", + "width": 540, + "height": 853 + }, + "num_comments": 6, + "tags": [ + "wtf", + "clients from hell", + "smh" + ], + "vote_state": 0, + "edited": false, + "user_id": 192292, + "user_username": "kwameboame", + "user_score": 534, + "user_avatar": { + "b": "ecd276", + "i": "v-17_c-3_b-7_g-m_9-1_1-6_16-6_3-1_8-1_7-1_5-1_12-6_6-97_10-1_2-8_15-2_11-2_18-2_4-1_19-1.jpg" + } + }, + { + "id": 271792, + "text": "My mum just had to call me to change our home WiFi password, because she can't change it on her laptop.\n\nShe's pretty bad with technology, but she had to call me to do it, because no one else knew how to. Including my brother-in-law who designs IT systems for major banks and system admins working for the government.\n\nShe had to call me, from Hungary to do it. I live in Scotland.", + "score": 20, + "created_time": 1478346252, + "attached_image": "", + "num_comments": 4, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": true, + "user_id": 148803, + "user_username": "620hun", + "user_score": 5046, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-5_3-3_8-2_7-2_5-2_12-2_6-11_2-76_15-15_4-2_19-1_20-9.jpg" + } + }, + { + "id": 331063, + "text": "wtf! http://wtfmobileweb.com/", + "score": 20, + "created_time": 1481463361, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wtf", + "web", + "mobile" + ], + "vote_state": 0, + "edited": false, + "links": [ + { + "type": "url", + "url": "http://wtfmobileweb.com/", + "short_url": "http://wtfmobileweb.com/", + "title": "http://wtfmobileweb.com/", + "start": 5, + "end": 29, + "special": 0 + } + ], + "user_id": 331053, + "user_username": "fariz", + "user_score": 233, + "user_avatar": { + "b": "ecd276", + "i": "v-17_c-3_b-7_g-m_9-1_1-1_16-4_3-5_8-1_7-1_5-1_12-1_6-10_2-40_4-1.jpg" + } + }, + { + "id": 156279, + "text": "O RLY and their books.\nAnd this was printed in 2015.\nTo be continued...", + "score": 19, + "created_time": 1472306034, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_156279_oeg7b.jpg", + "width": 750, + "height": 1000 + }, + "num_comments": 5, + "tags": [ + "lutz wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 144116, + "user_username": "light2yellow", + "user_score": 524, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-3_3-3_8-1_7-1_5-1_12-1_17-1_6-3_10-5_2-26_4-1.jpg" + } + }, + { + "id": 11246, + "text": "ARGH. The moment you realise the previous developer centralised ALL of the current software around a M$ access dBase. WTF. Same dude also stores day of year, week of year, month number and year as seperate fields in a MySQL dBase. Alongside a datetime stamp. OMFG!!!!", + "score": 19, + "created_time": 1461954752, + "attached_image": "", + "num_comments": 10, + "tags": [ + "wtf", + "a bloody mazing.", + "dbase noob" + ], + "vote_state": 0, + "edited": false, + "user_id": 4758, + "user_username": "Wabby", + "user_score": 653, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-3_16-1_3-1_8-1_7-1_5-1_12-3_6-76_10-1_2-18_15-24_4-1.jpg" + } + }, + { + "id": 460421, + "text": "How on EARTH is visual studios c compiler SO BAD?!?!\n\nFOR FUCKS SAKE the size of a integer and a long integer are the SAME (4 bytes). WHATS THE FUCKING POINT TO THAT?!??", + "score": 19, + "created_time": 1488644201, + "attached_image": "", + "num_comments": 5, + "tags": [ + "wtf?" + ], + "vote_state": 0, + "edited": false, + "user_id": 374269, + "user_username": "AlgoRythm", + "user_score": 11273, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-2_16-9_3-1_8-3_7-3_5-4_12-2_6-11_2-47_4-4_19-1_20-11.jpg" + }, + "user_dpp": 1 + }, + { + "id": 486216, + "text": "Yeah good luck fitting a Modern system on a Floppy Disk...wtf", + "score": 19, + "created_time": 1489971977, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_486216_SWQsH.jpg", + "width": 800, + "height": 600 + }, + "num_comments": 1, + "tags": [ + "stoneage", + "wtf", + "floppy" + ], + "vote_state": 0, + "edited": false, + "user_id": 481727, + "user_username": "SHA-256", + "user_score": 1715, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-1_16-6_3-3_8-2_7-2_5-2_12-1_6-98_10-9_2-76_18-3_4-2_19-3_20-4.jpg" + } + }, + { + "id": 146784, + "text": "THOSE PEOPLE/TEACHERS WHO PUT TWO SPACES BETWEEN SENTENCES, WTF IS WRONG WITH YOU. ITS NOT THE FUCKING TYPEWRITER AGE, THE COMPUTER DOES SPACING FOR YOU. MIGHT AS WELL DRAW SERIFS ON WITH A PEN AFTER PRINTING BY THAT LOGIC.", + "score": 19, + "created_time": 1471572710, + "attached_image": "", + "num_comments": 10, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 104297, + "user_username": "0xcaff", + "user_score": 364, + "user_avatar": { + "b": "a973a2", + "i": "v-17_c-3_b-2_g-m_9-1_1-4_16-15_3-14_8-1_7-1_5-1_12-4_6-68_10-1_2-20_15-50_4-1.jpg" + } + }, + { + "id": 199066, + "text": "So...the internet is a beautiful place..", + "score": 18, + "created_time": 1474697296, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_199066_9U9uK.jpg", + "width": 562, + "height": 1000 + }, + "num_comments": 12, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 94700, + "user_username": "Ben24", + "user_score": 1001, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-3_3-6_8-1_7-1_5-1_12-2_6-10_10-1_2-36_15-14_11-2_18-1_4-1.jpg" + } + }, + { + "id": 620039, + "text": "If this isn't the worst thing.\n\nI was asked to develop a WordPress plugin as an intern developer and I've been on it since last week. I got stuck when i finally had the loops running but couldn't find a way to format the output without overwriting the existing values on each iteration.\n\nFor the last one week I've been showing the progress on my code to the CTO and this is how it has been.\n\nMe: Hello. Everything is coming along fine, I have most of the functions running properly, do you mind looking into the algorithm?\n\nCTO: Oh not at all, let's see what you got. Omg great code for an intern. I think you should add a new variable there and maybe clean up that function over there because it's deprecated now and yeah HaHa, Great work.\n\nMe: Thanks xD I'll have it finished latest next week. \n\nCTO: Oh great. I can't wait to see what you'd have by next week so we can install it on our WordPress.\n\n*Next finally week comes and I'm done with the code. \n\nMe: Hello, I'm done with the entire code! Want to take a look? The plugin works just exactly as described. \n\n*CTO takes a look \n\nCTO: Omg?\n\nMe: Omg? \n\nCTO: This is completely bad programming practice, so you are running 4 nested loops that all send queries to our data base and make changes to data. This would have a very drastic effect on the server considering the traffic we get. \n\nMe: But you saw this exact code last week and said it was okay, I only changed some CSS since the last time.\n\nCTO: Omg, we can't accept this, you have to develop it again from the scratch without using those loops and queries.\n\nMe: What? Okay, fine. Any hints?\n\nCTO: Yes.\n\nMe: What? \n\nCTO: Just start. That's the greatest hint I could ever give. And also, always have a plan before you begin. \n\nMe: Yeah, thanks for those. It's the first time I'm hearing them and they would totally be applicable to building this thing.", + "score": 18, + "created_time": 1496155442, + "attached_image": "", + "num_comments": 2, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 552905, + "user_username": "Castillo", + "user_score": 402, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-3_16-5_3-1_8-1_7-1_5-1_12-3_6-68_10-1_2-5_15-24_11-1_4-1.jpg" + } + }, + { + "id": 129447, + "text": "The game we have all been waiting for is here!!\n\nhttp://store.steampowered.com/agech...\n\no wait....wtf", + "score": 18, + "created_time": 1470223313, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_129447_WSqUe.jpg", + "width": 562, + "height": 1000 + }, + "num_comments": 1, + "tags": [ + "shower dad wtf game" + ], + "vote_state": 0, + "edited": false, + "links": [ + { + "type": "url", + "url": "http://store.steampowered.com/agecheck/app/359050/", + "short_url": "http://store.steampowered.com/agech...", + "title": "http://store.steampowered.com/agech...", + "start": 49, + "end": 87, + "special": 1 + } + ], + "special": true, + "user_id": 91142, + "user_username": "boyney123", + "user_score": 358, + "user_avatar": { + "b": "2a8b9d" + } + }, + { + "id": 344802, + "text": "Hur dur, java is so bad that I'm gonna rant about it on a social network backed by a java database...", + "score": 18, + "created_time": 1482183813, + "attached_image": "", + "num_comments": 1, + "tags": [ + "wtf", + "really?" + ], + "vote_state": 0, + "edited": false, + "user_id": 26258, + "user_username": "azous", + "user_score": 5762, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-3_16-6_3-1_8-1_7-1_5-4_12-3_6-52_10-5_2-50_11-4_18-4_4-4_19-3_20-5_21-2.jpg" + } + }, + { + "id": 501354, + "text": "TL;DR;\n\nIdiot hard coded database host on the app... Pushed to prod and suddenly shit wasn't working... Took me 10 minutes to figure out what was going on...\n\nWrote a passive aggressive git message and commited.\nBefore updating prod my boss turns around to me and the following took place:\n\nBoss: is there any problem with the server?\n\nMe: yes, someone (i know who was ) hard coded the test db IP and it broke the backend.\n\nBoss: oh, but will it affect the mobile app? \n\nMe: well, it won't work but I'm already pushing the fix.\n\nBoss: no..err.. I mean... Will I have to make any change to the mobile app?\n\nMe inside: wtf dude... For real?! Get your shit together... \n\nMe: no. It good, I already fixed it.\n\nBoss: OK. Thanks\n\nTL;DR;\n\nMoron hard coded dB's host and stupid boss can't get shit together nor ask who did it to take precautions...", + "score": 18, + "created_time": 1490737789, + "attached_image": "", + "num_comments": 13, + "tags": [ + "hard code", + "stupid", + "fuck", + "git", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 26258, + "user_username": "azous", + "user_score": 5762, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-3_16-6_3-1_8-1_7-1_5-4_12-3_6-52_10-5_2-50_11-4_18-4_4-4_19-3_20-5_21-2.jpg" + } + }, + { + "id": 143251, + "text": "When i saw that notigs i was thinking hmm ok devRant have some weird bug but it wasnt bug wooow", + "score": 17, + "created_time": 1471326984, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_143251_SEPK7.jpg", + "width": 540, + "height": 960 + }, + "num_comments": 3, + "tags": [ + "wtf :)" + ], + "vote_state": 0, + "edited": false, + "user_id": 19218, + "user_username": "Haxk20", + "user_score": 6483, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-2_16-13_3-3_8-4_7-4_5-4_12-2_6-3_10-2_2-41_18-4_4-4_19-2_20-4_21-1.jpg" + } + }, + { + "id": 489311, + "text": "\"Hey, remember when we said we didn't need any of that data, and it was totally okay to design the app without any expectation of needing it, and that it would actually put us behind to add even hooks 'just in case' like you begged? Well, turns out we need it. And we need it right now.\"\r\n...\r\n\"Oh, and this is totally not our fault because you should have just gone ahead and done the thing we told you not to do anyway, on your own time.\"", + "score": 17, + "created_time": 1490123539, + "attached_image": "", + "num_comments": 1, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 262660, + "user_username": "tachoknight", + "user_score": 606, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-3_16-10_3-2_8-1_7-1_5-1_12-3_6-80_10-1_2-1_15-23_11-1_18-1_4-1_19-1.jpg" + } + }, + { + "id": 439832, + "text": "when MySQL opens its updater terminal but does nothing but sit there in your toolbar", + "score": 17, + "created_time": 1487653323, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_439832_fANmm.jpg", + "width": 417, + "height": 388 + }, + "num_comments": 5, + "tags": [ + "mysql", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 439680, + "user_username": "chrisx84", + "user_score": 177, + "user_avatar": { + "b": "a973a2", + "i": "v-17_c-3_b-2_g-m_9-1_1-1_16-3_3-3_8-1_7-1_5-1_12-1_6-101_2-10_11-2_4-1.jpg" + } + }, + { + "id": 438976, + "text": "Found this gem in a popular frontend framework...\n\n...the fuck?!", + "score": 17, + "created_time": 1487611439, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_438976_X4qpK.jpg", + "width": 720, + "height": 492 + }, + "num_comments": 16, + "tags": [ + "wtf", + "fml" + ], + "vote_state": 0, + "edited": false, + "user_id": 1150, + "user_username": "nblackburn", + "user_score": 9017, + "user_avatar": { + "b": "a973a2", + "i": "v-17_c-3_b-2_g-m_9-1_1-2_16-3_3-7_8-2_7-2_5-4_12-2_6-3_2-71_18-1_4-4_19-3.jpg" + }, + "user_dpp": 1 + }, + { + "id": 136212, + "text": "My boss insists in develop the most unmaintainable ways possible like hard coding everything... EVERYTHING....\nDDD (despair driven design)\n๐Ÿ˜ต๐Ÿ˜จ", + "score": 17, + "created_time": 1470794329, + "attached_image": "", + "num_comments": 5, + "tags": [ + "wtf", + "fml" + ], + "vote_state": 0, + "edited": false, + "user_id": 26258, + "user_username": "azous", + "user_score": 5762, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-3_16-6_3-1_8-1_7-1_5-4_12-3_6-52_10-5_2-50_11-4_18-4_4-4_19-3_20-5_21-2.jpg" + } + }, + { + "id": 791739, + "text": "Extract from a quite old recruiter email (excluding the formalities):\n\nPosition: junior dev\nWorkplace: office for first year, can be changed to remote after (salary will be 50% lower if you go remote though) *suspicion levels starts raising*\nPayment: 1โ‚ฌ per line of code (empty lines/comments excluded) or page of documentation *alarms start ringing*\nAdditional info: on high alert for 4 weeks every 6 weeks *wtf is going on*, salary bonus is 10โ‚ฌ per week *I stopped reading here and moved the email directly to the spam folder*", + "score": 17, + "created_time": 1503348252, + "attached_image": "", + "num_comments": 8, + "tags": [ + "gtfo", + "nope", + "wtf is this payment", + "dev on high alert?", + "wk2" + ], + "vote_state": 0, + "edited": false, + "weekly": { + "week": 2, + "topic": "Your most ridiculous recruiter experience?", + "date": "5/30/16", + "height": 50 + }, + "user_id": 352531, + "user_username": "redstonetehnik", + "user_score": 3375, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-1_16-11_3-3_8-4_7-4_5-3_12-1_6-10_10-3_2-47_15-10_11-8_18-4_4-3_19-3_20-12_21-2.jpg" + } + }, + { + "id": 378701, + "text": "A few days ago I had a party with a big part of my good ol' highschool classmates who I almost never spoke to. Let the stories begin:\n\n- Guy who made fun me in when I said I wanted to do computer science: \"Man, I wish I had done the same study. It looks fun.\"\n\n- Guy who has a startup for like 1 year: \"Sooo what are you good at, ios/android development? webdevelopment? contact me if you want to work with us.(for free)\"\n\n- One of the friendly guys: \"Do you have any sites where I can learn some basic programming or something?\" \n\nWhat I thought: WTF HAPPEND IN THOSE 3 YEARS, WHY THE SUDDEN INTEREST IN PROGRAMMING AND STUFF?! ESPECIALLY YOU FIRST GUY!", + "score": 17, + "created_time": 1484166594, + "attached_image": "", + "num_comments": 3, + "tags": [ + "higschool reunion", + "startuppitchtalks", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 164361, + "user_username": "Stukongeluk", + "user_score": 406, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-3_16-6_3-6_8-1_7-1_5-1_12-3_6-14_10-1_2-10_11-1_18-1_4-1_19-1.jpg" + } + }, + { + "id": 560127, + "text": "The WTF moment when I realized that the main production DB server was configured with **dynamic** private IP. After maintenance upgrade and reboot the rest of environment stopped. When I explained to sys admin what caused the production breakdown hi still did not get that :/", + "score": 16, + "created_time": 1493455289, + "attached_image": "", + "num_comments": 3, + "tags": [ + "wtf?", + "production breakdown" + ], + "vote_state": 0, + "edited": false, + "user_id": 539658, + "user_username": "LMtx", + "user_score": 342, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-10_3-6_8-1_7-1_5-1_12-2_6-76_2-20_4-1_19-1.jpg" + } + }, + { + "id": 239207, + "text": "JavaScript for beginners...", + "score": 16, + "created_time": 1476717447, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_239207_Rwec1.jpg", + "width": 231, + "height": 78 + }, + "num_comments": 4, + "tags": [ + "wtf", + "javascript" + ], + "vote_state": 0, + "edited": true, + "user_id": 230055, + "user_username": "Devvi", + "user_score": 643, + "user_avatar": { + "b": "7bc8a4" + } + }, + { + "id": 157837, + "text": "When you get 'last weeks backup' but the files have a date of NOVEMBER....", + "score": 16, + "created_time": 1472422379, + "attached_image": "", + "num_comments": 1, + "tags": [ + "backups wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 90430, + "user_username": "spl0", + "user_score": 2276, + "user_avatar": { + "b": "f99a66" + } + }, + { + "id": 461809, + "text": "Slack is cool and all... But do we really have to have an \"account per team\" ? Damn I cringed so hard when I was setting up two-factor authentication and realized it was this way... Wtf...", + "score": 15, + "created_time": 1488731111, + "attached_image": "", + "num_comments": 9, + "tags": [ + "slack", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 26258, + "user_username": "azous", + "user_score": 5762, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-3_16-6_3-1_8-1_7-1_5-4_12-3_6-52_10-5_2-50_11-4_18-4_4-4_19-3_20-5_21-2.jpg" + } + }, + { + "id": 489085, + "text": "Just learnt perfectly what the below joke means:\n\n'I wanted to improve the world, but they wouldn't give me the source code'\n\nI really don't understand why the world is full of obsolete processes that people fight against daily when changing things ever so slightly could take the weight of the world off their shoulders. The same thing goes for my work, I work in finance, and we use a remote app built in Windows forms (not xaml or wpf, the original forms) and it's insecure, slow, buggy, and crashes whenever you press ESC (yes, really). Even worse, I've offered to rewrite their whole network for nothing, just the improvement to people's lives. And they say no! WELL FUCK YOU FOR BEING A PLAGUE ON THE FUCKING WORLD! Why do people insist on staying behind the times when the world could be such a beautiful place?!?", + "score": 15, + "created_time": 1490116186, + "attached_image": "", + "num_comments": 3, + "tags": [ + "fml", + "pointless", + "improvement", + "wtf", + "stupid" + ], + "vote_state": 0, + "edited": false, + "user_id": 362982, + "user_username": "DucksCanCode", + "user_score": 2379, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-9_3-1_8-2_7-2_5-2_12-2_17-1_6-2_10-9_2-41_11-3_18-4_4-2_19-3_20-7_21-1.jpg" + }, + "user_dpp": 1 + }, + { + "id": 537356, + "text": "First day in new job, decided to take the train because city is so overloaded with cars, it is end of april and snow outside wtf. Best weather for coding :D", + "score": 15, + "created_time": 1492493055, + "attached_image": "", + "num_comments": 4, + "tags": [ + "weather", + "coding", + "wtf", + "job" + ], + "vote_state": 0, + "edited": false, + "user_id": 433163, + "user_username": "WhoCode", + "user_score": 597, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-3_16-6_3-7_8-1_7-1_5-1_12-3_6-98_10-3_2-18_15-15_11-2_18-1_4-1_19-1.jpg" + } + }, + { + "id": 738742, + "text": "(I'll give some context before the rant: I'm part if the IT department of a manufacturing company (actually I'm 1/2 of the department), and all the applications (old an new - except the ones used on production line) used in the company are my responsibility, that including most of databases too... Also, English isn't my native language so there will be some words or phrases that I'll probably write wrong... Sorry for that, if there are any corrections, I'll be glad to hear them)\n\nSo...\n\nThere will be an implementation of new \"control point\" on the \"shipping department\" which consists on a electromechanical equipment controlled by a PLC. And despite the original concept was a collaboration between 2 departments (we, IT, and Production Control), I was never taken in consideration about anything of the project... To be fair, I forget about its existence until two weeks ago.\n\nSo, a few days I learned that there are a huge delay regarding the original deadline (mainly because the supplier was delayed with the delivery of their system), and since two weeks (less, actually, because some holydays in between) I'm learning how to integrate that \"P.o.S\" into an existing application on a PC using a serial communication (not the main problem, as I've done that before... With another brand of PLC's) while avoiding buying any additional software (to get the communication done and in a easy way) and that sort of things... But discovering in the process that it will be necessary to acquire such additional SW in order to finish the job ASAP.\n\nWhen suddenly I get the \"news\" that it's almost all my duty (and responsibility) to meet the original deadline, because it doesn't matter how the other departments screw all the schedule, it's the job of IT to get the shit done in time... And what is worst: they didn't said that in such straight manner, no, the implied it while making a quick test with the general manager.\n\nI mean, WTF? Besides doing a \"respectable\" number of \"user support\" activities in a dialy basis, I also need to manage the activities of other departments? And also fix their screw ups on a schedule that I just learned days before?\n\nAnd also there is a coworker (one of whom screwed up) that, almost every time she see me, is asking \"how much until you'll finish?\"\n\nAs I read on a meme years ago: \"please, give patience, because if you give strength, I'll need bail money too...\"\n\nDamn... I don't know of the benefits of this work are worth all this nonsense", + "score": 15, + "created_time": 1501127439, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wtf", + "fml", + "i need a new job" + ], + "vote_state": 0, + "edited": false, + "user_id": 727919, + "user_username": "araxhiel", + "user_score": 53, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-3_3-4_8-1_7-1_5-1_12-2_6-10_10-1_2-26_15-14_11-2_4-1.jpg" + } + }, + { + "id": 347670, + "text": "Code quality as measured in WTFs/minute.", + "score": 15, + "created_time": 1482341228, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_347670_BPUKP.jpg", + "width": 800, + "height": 800 + }, + "num_comments": 1, + "tags": [ + "code reviews", + "good code", + "wtf", + "wtfs/minute" + ], + "vote_state": 0, + "edited": false, + "user_id": 334809, + "user_username": "aStewartDev", + "user_score": 953, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-2_3-2_8-1_7-1_5-1_12-1_6-98_10-5_2-75_15-11_18-1_4-1_19-2.jpg" + } + }, + { + "id": 28706, + "text": "Friend: oh so you are a programmer? \nMe: yeaaa..\nFriend: great! Im having problem with the wifi connection on my phone.. Can you help me fix it?\nMe: (( _ _ ))..zzzZZ", + "score": 15, + "created_time": 1463483132, + "attached_image": "", + "num_comments": 2, + "tags": [ + "wtf fml" + ], + "vote_state": 0, + "edited": false, + "user_id": 28371, + "user_username": "mdhilwan", + "user_score": 262, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-4_16-6_3-4_8-1_7-1_5-1_12-4_6-97_10-1_2-1_15-27_11-1_4-1.jpg" + } + }, + { + "id": 82549, + "text": "Just had a client ring me to check I'll be voting the \"right way\" in the EU Referendum. WTF??", + "score": 15, + "created_time": 1466675270, + "attached_image": "", + "num_comments": 9, + "tags": [ + "referendum", + "none of your business", + "vote", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 50254, + "user_username": "fridaytypo", + "user_score": 138, + "user_avatar": { + "b": "a973a2" + } + }, + { + "id": 801533, + "text": "Uhhhh !!! No standard way for electron apps to auto-update on Linux.\n\nWTF !!!", + "score": 14, + "created_time": 1503760907, + "attached_image": "", + "num_comments": 4, + "tags": [ + "annoying", + "electron", + "update", + "wtf?", + "linux" + ], + "vote_state": 0, + "edited": false, + "user_id": 801511, + "user_username": "gswebspace", + "user_score": 48, + "user_avatar": { + "b": "7bc8a4" + } + }, + { + "id": 58074, + "text": "Because we don't have a static IP at the office, why don't we implement two factor auth for SSH? \n\nNo that's too complicated, let's just update the IP every week and we'll leave webmin available as a backdoor for ourselves to do so.", + "score": 14, + "created_time": 1465233194, + "attached_image": "", + "num_comments": 8, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 26392, + "user_username": "devRantUser42", + "user_score": 133, + "user_avatar": { + "b": "d55161" + } + }, + { + "id": 407507, + "text": "*Runs MySQL benchmark on a new server*\r\nResult: 30 read queries a second.\r\n*Hmm.... that can't be right....\"\r\n*Creates simple node benchmark script and runs it*\r\nResult: 400-500 read queries a second.\n\nWtf mysql...", + "score": 14, + "created_time": 1485895718, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_407507_sywNt.jpg", + "width": 500, + "height": 375 + }, + "num_comments": 8, + "tags": [ + "wtf", + "y u do dis", + "benchmark", + "mysql" + ], + "vote_state": 0, + "edited": false, + "user_id": 22941, + "user_username": "linuxxx", + "user_score": 32147, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-3_3-3_8-3_7-3_5-4_12-2_6-7_10-9_2-48_15-11_18-4_4-4_19-3_20-14.jpg" + }, + "user_dpp": 1 + }, + { + "id": 380802, + "text": "So a client of mine who I've known a long time referred me to a friend of his for a project. OK great! Quoted the guy a price got the OK but didn't know my client had told this friend they would be paying me for the job. Well I found this out when I went to get payment from the guy. I call my client and he proceeds to tell me \"yes we told him we would pay for it but thought it would just be included in our project fee which was already paid\".. WTF", + "score": 14, + "created_time": 1484275168, + "attached_image": "", + "num_comments": 2, + "tags": [ + "don't trust anyone", + "wtf", + "pissed" + ], + "vote_state": 0, + "edited": false, + "user_id": 201011, + "user_username": "wglenn", + "user_score": 144, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-1_16-6_3-1_8-1_7-1_5-1_12-1_6-14_10-1_2-10_15-42_4-1.jpg" + } + }, + { + "id": 59296, + "text": "When i started my work I encoubtered this db(one of 4): more than 20 tables, some with 200 columns literally... EVERYTHING is a varchar ๐Ÿ˜“. \n\nI'm slowly designing some normalized tables with real fk on new features and projects and people are like: how the fuck did you implement this feature so fast? the other guy spent 3 months designing this form (and I'm just speechless):\n\n The form was some sort of crazyness shit passing input names as \"name-of-property\" and a file only to check if(name=\"string\") then store a number value to an array and save it as a \"number\" (actually varchar) on the db. literally more than 50 if statements to do this. \n\nEverything on a single table that made no sense at all.\n\nJust wtf... At least my boss let me start if from scratch cause he we were always having panick attacks every time he needed to do something with it. ๐Ÿ˜‚๐Ÿ˜‚", + "score": 13, + "created_time": 1465299226, + "attached_image": "", + "num_comments": 6, + "tags": [ + "crazy", + "db", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 26258, + "user_username": "azous", + "user_score": 5762, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-3_16-6_3-1_8-1_7-1_5-4_12-3_6-52_10-5_2-50_11-4_18-4_4-4_19-3_20-5_21-2.jpg" + } + }, + { + "id": 460107, + "text": "Started further developing an app (that I had left dormant) for retail outlets in my town. Spoken to the managers and they said they like my idea, and want to trial run it and then if it works properly, they want to start having the other branches around the country use it. \n\nNow to sit by ass behind my screen, and finish developing my app (meaning tackling the many bugs that made me pull my hair out last time)", + "score": 13, + "created_time": 1488626160, + "attached_image": "", + "num_comments": 2, + "tags": [ + "whoohoo", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 369518, + "user_username": "ragnarr023", + "user_score": 2939, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-2_3-9_8-2_7-2_5-2_12-1_6-83_10-7_2-48_15-11_18-2_4-2_19-2_20-7.jpg" + } + }, + { + "id": 376462, + "text": "what grinds my balls ? DATA INCONSISTENCIES!!! what the fuck did people think proliferating bad data futher into the abyss of existence!!!", + "score": 13, + "created_time": 1484047598, + "attached_image": "", + "num_comments": 3, + "tags": [ + "wtf", + "getitoffmychest" + ], + "vote_state": 0, + "edited": false, + "user_id": 210385, + "user_username": "naughtyelf", + "user_score": 265, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-3_16-1_3-2_8-1_7-1_5-1_12-3_6-47_10-3_2-9_15-19_11-2_18-1_4-1_19-1.jpg" + } + }, + { + "id": 113229, + "text": "Just found out that our front-end intern that has been here for more than 4 months don't know about \"onchange\" events!!!\n\n wtf man? I'm back end and know this shit...", + "score": 13, + "created_time": 1468850799, + "attached_image": "", + "num_comments": 2, + "tags": [ + "intern", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 26258, + "user_username": "azous", + "user_score": 5762, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-3_16-6_3-1_8-1_7-1_5-4_12-3_6-52_10-5_2-50_11-4_18-4_4-4_19-3_20-5_21-2.jpg" + } + }, + { + "id": 117168, + "text": "Just realized that I'm at page 508 of \n http://clientsfromhell.net ... what am I doing with my life ๐Ÿ˜“", + "score": 13, + "created_time": 1469140398, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "links": [ + { + "type": "url", + "url": "http://clientsfromhell.net", + "short_url": "http://clientsfromhell.net", + "title": "http://clientsfromhell.net", + "start": 41, + "end": 67, + "special": 1 + } + ], + "special": true, + "user_id": 26258, + "user_username": "azous", + "user_score": 5762, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-3_16-6_3-1_8-1_7-1_5-4_12-3_6-52_10-5_2-50_11-4_18-4_4-4_19-3_20-5_21-2.jpg" + } + }, + { + "id": 216474, + "text": "Ya'll have been typing your code wrong", + "score": 13, + "created_time": 1475563949, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_216474_xtMEk.gif", + "width": 800, + "height": 600 + }, + "num_comments": 3, + "tags": [ + "fml", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 157883, + "user_username": "varundey", + "user_score": 5508, + "user_avatar": { + "b": "a973a2", + "i": "v-17_c-3_b-2_g-m_9-1_1-2_16-6_3-6_8-4_7-4_5-4_12-2_6-2_10-9_2-76_15-27_11-2_18-4_4-4_19-3_20-10_21-1.jpg" + } + }, + { + "id": 98123, + "text": "I'm literally the only one who locks the screen here at work.\n\nAlways makes me wanna do something to teach then.\n\nMy boss always leaves the screen unlocked with sublime opened and goes to lunch! \nI think someday he was logged into production also...\n\nAnd I'm like: seriously? wtf...\n\nI lock my screen even when I'm home alone... yes I'm that paranoid...\n\nNo one is gonna \"Greek question mark\" me ๐Ÿ˜‚", + "score": 13, + "created_time": 1467911233, + "attached_image": "", + "num_comments": 18, + "tags": [ + "security", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 26258, + "user_username": "azous", + "user_score": 5762, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-3_16-6_3-1_8-1_7-1_5-4_12-3_6-52_10-5_2-50_11-4_18-4_4-4_19-3_20-5_21-2.jpg" + } + }, + { + "id": 786970, + "text": "I have a Yahoo app on my phone for some legacy purposes. I just allowed the storage access permissions when it was asking for it during the installation or something, cause like, who doesn't? I checked my Yahoo Mail on the browser tonight and saw copies of my mobile photos in it! It's through the Attach Icon > Insert animated GIF. WTF? So that's how you can easily get hacked from apps?", + "score": 13, + "created_time": 1503148198, + "attached_image": "", + "num_comments": 0, + "tags": [ + "anonymous", + "sync", + "fsociety", + "storage", + "allow", + "hack", + "access", + "wtf", + "yahoo", + "pussy" + ], + "vote_state": 0, + "edited": false, + "user_id": 321452, + "user_username": "nikolatesla", + "user_score": 11405, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-2_1-3_16-6_3-2_8-4_7-4_5-2_12-3_6-10_10-9_2-40_18-4_4-2_19-3_20-8.jpg" + } + }, + { + "id": 205839, + "text": "No man's sky: I can imagine the devs fighting management and marketing letting them know that what they were selling was bullshit and they be like 0 fucks given.", + "score": 13, + "created_time": 1475040334, + "attached_image": "", + "num_comments": 2, + "tags": [ + "managers wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 185208, + "user_username": "tass", + "user_score": 396, + "user_avatar": { + "b": "a973a2", + "i": "v-17_c-3_b-2_g-m_9-1_1-1_16-1_3-1_8-1_7-1_5-1_12-1_6-25_2-1_4-1.jpg" + } + }, + { + "id": 180362, + "text": "so I'm here watching my boss' whatsapp blinking \"typing...\" for 5 minutes wondering of it's bugged, he's drunk or I have to update my CV...", + "score": 13, + "created_time": 1473720083, + "attached_image": "", + "num_comments": 3, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 26258, + "user_username": "azous", + "user_score": 5762, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-3_16-6_3-1_8-1_7-1_5-4_12-3_6-52_10-5_2-50_11-4_18-4_4-4_19-3_20-5_21-2.jpg" + } + }, + { + "id": 192064, + "text": "Try and Google for 'Apple iBag'. No, really, I wish I was kidding...", + "score": 13, + "created_time": 1474360739, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_192064_sdsbc.jpg", + "width": 400, + "height": 320 + }, + "num_comments": 4, + "tags": [ + "apple", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 22941, + "user_username": "linuxxx", + "user_score": 32147, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-3_3-3_8-3_7-3_5-4_12-2_6-7_10-9_2-48_15-11_18-4_4-4_19-3_20-14.jpg" + }, + "user_dpp": 1 + }, + { + "id": 658829, + "text": "In a lecture hall \n1. Try Listening to lecture\n2. Finds it uninteresting\n3. Think that you can later learn from video tutorials\n4. One day before exam -- No time to learn..", + "score": 13, + "created_time": 1497785613, + "attached_image": "", + "num_comments": 3, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 604964, + "user_username": "whatsinusername", + "user_score": 333, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-1_1-1_16-6_3-3_8-1_7-1_5-1_12-1_6-6_2-18_15-14_11-1_4-1.jpg" + } + }, + { + "id": 741964, + "text": "Fucking fuck shit monkeycocksucking gargling wtf!\n\nI was getting some stuff done in my accounting software and it bugged me that the fields were dark and the fonts as well, thus seeing fucking shit. This was clearly a bad choice of a gtk3 dark theme, thus i switched to the fucking default adwaita, suddenly gnome session crashes. \nOk, i just log out and log back in.\nLogout.... Nothing happens.... Ctrl-alt-backspace , nothing happens (and i knew i enabled that in the settings)\nOk let's do it a bit more forceful and restart the display manager... Gdm starts... I insert my credentials... It fucking crashes.\n\nWTF!!!\n\nI desperately try to debug it, xsession error msg'es? Nope. Something in /var/log/messages? Nope. Something, anything at all, nope sherlock nopedinope! \n\nAbout to go batshit crazy, purging and reinstalling all of gnome, thibking that, what ever setting lust have broke it, it will be fixed now. \n\nNo fucking fuck desktop!!!\n\nI lost my nerve and replaced gdm with lightdm, and i finally, after three hours wasted on my machine, i get my gnome desktop back... But in a state of mess! Extensions don't work and make it crash again, user themes? Nope, go fuck yourself with plain default.\n\nI'm really losing my shit, business is almost non-existant, and now ly FUCKING desktop refuses to work like i want to. Everything is fucking broken to shits !!\n\nI'm gon a go to my gf, and relax a little, at least i still have a working laptop.\n\nQuestion is, for how long???\n\nFml", + "score": 13, + "created_time": 1501259980, + "attached_image": "", + "num_comments": 4, + "tags": [ + "wtf how did this happen?", + "gdm", + "themes", + "gnome", + "crash" + ], + "vote_state": 0, + "edited": false, + "user_id": 22704, + "user_username": "NeatNerdPrime", + "user_score": 1705, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-6_16-3_3-3_8-1_7-1_5-1_12-6_6-34_2-39_15-10_18-1_4-1_19-1.jpg" + } + }, + { + "id": 37078, + "text": "Why do clients wait for 17:55 before asking you to fix that nuclear reaction bug or that new \"little insignificant feature i can't live without\" ? Seriously, what the hell is your daily job ? Because i really want to switch to it.", + "score": 12, + "created_time": 1464075339, + "attached_image": "", + "num_comments": 3, + "tags": [ + "wtf", + "clients" + ], + "vote_state": 0, + "edited": false, + "user_id": 37075, + "user_username": "ste09", + "user_score": 1210, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-1_16-16_3-11_8-2_7-2_5-1_12-1_6-3_10-9_2-42_15-19_18-2_4-1_19-2_20-1.jpg" + } + }, + { + "id": 519454, + "text": "So.. There are about 4 jobs in my country that even mention golang, and only as an \"advantage\"\n\n๐Ÿ˜Ÿ Wow.. learning Go really isn't helping..\n\nWhy isn't it used widely as the main language of a dev team?? Too young? Or are there serious issues, that most companies prefer using c#/ruby/python for web dev backend?", + "score": 12, + "created_time": 1491680413, + "attached_image": "", + "num_comments": 8, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 202381, + "user_username": "vortex", + "user_score": 3754, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-f_9-1_1-1_16-3_3-15_8-2_7-2_5-2_12-1_6-38_2-26_18-1_4-2_19-3.jpg" + } + }, + { + "id": 536459, + "text": "I struggled to find the interview location as the company as they were using another companies offices. As I sit down, sweating, feeling rushed for barely making it on time the interviewer says: \"Tell us a joke\"\n\nI should have got up and walked out, but since I was there already I pulled this one out:\n\nOne day, a mechanical engineer, electrical engineer, chemical engineer, and computer engineer were driving down the street in the same car when it broke down.\n\nThe mechanical engineer said, I think a rod broke.\n\nThe chemical engineer said, The way it sputtered at the end, I think it's not getting enough gas.\n\nThe electrical engineer said, I think there was a spark and something's wrong with the electrical system.\n\nAll three turned to the computer engineer and asked, What do you think?\n\nThe computer engineer said, I think we should all get out and then get back in.", + "score": 12, + "created_time": 1492451477, + "attached_image": "", + "num_comments": 4, + "tags": [ + "wk48", + "wtf?" + ], + "vote_state": 0, + "edited": false, + "weekly": { + "week": 48, + "topic": "Best or worst developer interview question?", + "date": "4/17/17", + "height": 50 + }, + "user_id": 376515, + "user_username": "qbalsdon", + "user_score": 1891, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-3_16-2_3-4_8-1_7-1_5-1_12-3_6-11_10-1_2-37_15-19_11-1_18-2_4-1_19-3.jpg" + } + }, + { + "id": 130893, + "text": "#!/bin/TableFlip\n\necho ${profanity} > /dev/null\nrm -rf ~/projects\nsudo shutdown -h now", + "score": 12, + "created_time": 1470336746, + "attached_image": "", + "num_comments": 0, + "tags": [ + "tableflip", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 5451, + "user_username": "juneeighteen", + "user_score": 3899, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-1_16-17_3-10_8-4_7-4_5-3_12-1_6-11_10-9_2-81_11-2_4-3_19-2_20-10.jpg" + } + }, + { + "id": 206492, + "text": "bosses doesn't care about the scope of the projject they just want it to be done fast as possible. It's burning me out. Fuck this..", + "score": 12, + "created_time": 1475071165, + "attached_image": "", + "num_comments": 6, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 34836, + "user_username": "xociety", + "user_score": 662, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-5_16-4_3-4_8-1_7-1_5-1_12-5_17-1_6-50_10-2_2-40_15-10_11-2_18-2_4-1_19-1.jpg" + } + }, + { + "id": 133835, + "text": "WTF ..?!\n:/", + "score": 11, + "created_time": 1470591611, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_133835_a7puH.jpg", + "width": 597, + "height": 314 + }, + "num_comments": 0, + "tags": [ + "exit", + "dialog", + "buttons", + "quit", + "funny" + ], + "vote_state": 0, + "edited": false, + "user_id": 75906, + "user_username": "Alireza6677", + "user_score": 5333, + "user_avatar": { + "b": "ecd276", + "i": "v-17_c-3_b-7_g-m_9-1_1-3_16-13_3-5_8-4_7-4_5-4_12-3_6-82_10-9_2-91_18-4_4-4_19-3_20-6_21-1.jpg" + } + }, + { + "id": 282562, + "text": "A===B\nB===C\nA!===C for some reason. \n\nUghhh, am I just being dumb?", + "score": 11, + "created_time": 1478912106, + "attached_image": "", + "num_comments": 17, + "tags": [ + "help", + "wtf" + ], + "vote_state": 0, + "edited": true, + "user_id": 222295, + "user_username": "Player2", + "user_score": 2384, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-11_3-4_8-2_7-2_5-1_12-1_6-2_10-5_2-51_11-2_18-4_4-1_19-3_20-3.jpg" + } + }, + { + "id": 9978, + "text": "The moment you realize your co workers don't really know how to use git....", + "score": 11, + "created_time": 1461639553, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_9978_DVR65.jpg", + "width": 800, + "height": 490 + }, + "num_comments": 4, + "tags": [ + "git wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 4425, + "user_username": "kevdev", + "user_score": 42, + "user_avatar": { + "b": "2a8b9d" + } + }, + { + "id": 8150, + "text": "I'm a tech lead for a new agile project for a manager who knows nothing about agile. Having to work on a chart that shows exactly how many sprints each milestone will be and when it will complete for like, 17 sprints from now when requirements aren't even set. Wtf?", + "score": 11, + "created_time": 1461114018, + "attached_image": "", + "num_comments": 7, + "tags": [ + "badmanager", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 8141, + "user_username": "NotABugAFeature", + "user_score": 1578, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-3_16-3_3-1_8-2_7-2_5-1_12-3_6-43_10-4_2-27_11-7_18-1_4-1_19-1.jpg" + } + }, + { + "id": 220681, + "text": "Client email: so you know that custom thing I wanted on my website that you made and I refused to pay for? Could you just send me the code, I'm gonna write my own.\n\n*Staring at screen in disbelief and laughing*\n\nEven if you were an awesome client, and you had paid, I still wouldn't just \"give you my code\" so you can write your own. You hired me to do it because you CAN'T not because you didn't want to, not because you're lazy, but because you are not mentally capable of making your own.\n\nMe: I'm sorry, but I'm not sure you would even know where to start with this, what was wrong with the one built?\n\nClient: I wanna put the code in my Salesforce so it can run the API request and automatically make a lead for me.\n\nMy code takes fields from a form, runs the data over to the API, gets the response, allows for user confirmation that the information is correct, and then sends all the relevant data to Salesforce as a lead.\n\nMe: But that's exactly what mine does, just does it from your website where users will be entering the info, and once they've confirmed it you get a Salesforce lead.\n\nClient: well some of my leads come from other places and I want to simplify everything.\n\nMe: no, not possible, sorry.\n\nIf you didn't have 25 different websites for one company then all your leads would come from the same place and it would be simple.", + "score": 11, + "created_time": 1475771614, + "attached_image": "", + "num_comments": 2, + "tags": [ + "wtf", + "nope" + ], + "vote_state": 0, + "edited": false, + "user_id": 183114, + "user_username": "sylar182", + "user_score": 1729, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-3_16-9_3-6_8-2_7-2_5-1_12-3_6-12_10-5_2-42_11-4_18-2_4-1_19-2.jpg" + } + }, + { + "id": 469371, + "text": "I've refrained from commenting on which IDE is best and which isn't, what simply works for me is eclipse. So far it handles stuff for me pretty nicely... until today.\n\nLook at this screenshot, can you spot the wtf? Like, SRSLY, WTF.... Might it really be just that DOT at the end or is it just the dot concatenated at the end of the error message?", + "score": 11, + "created_time": 1489143006, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_469371_kxzQh.jpg", + "width": 428, + "height": 337 + }, + "num_comments": 14, + "tags": [ + "error msg", + "doubtingcomputingcapabilitiesforthefirsttime", + "notsurewhattodo", + "wtf eclipse?" + ], + "vote_state": 0, + "edited": false, + "user_id": 22704, + "user_username": "NeatNerdPrime", + "user_score": 1705, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-6_16-3_3-3_8-1_7-1_5-1_12-6_6-34_2-39_15-10_18-1_4-1_19-1.jpg" + } + }, + { + "id": 152115, + "text": "WPF\n\nmore like WTF.", + "score": 11, + "created_time": 1471994131, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wfpwtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 104297, + "user_username": "0xcaff", + "user_score": 364, + "user_avatar": { + "b": "a973a2", + "i": "v-17_c-3_b-2_g-m_9-1_1-4_16-15_3-14_8-1_7-1_5-1_12-4_6-68_10-1_2-20_15-50_4-1.jpg" + } + }, + { + "id": 604566, + "text": "If I have $100000 budget for a Steelcase chair, which one should I buy?", + "score": 11, + "created_time": 1495371816, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_604566_6z3yq.jpg", + "width": 800, + "height": 500 + }, + "num_comments": 9, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 314770, + "user_username": "JaredDunn", + "user_score": 1277, + "user_avatar": { + "b": "a973a2", + "i": "v-17_c-3_b-2_g-m_9-1_1-2_16-10_3-12_8-1_7-1_5-1_12-2_6-2_10-1_2-1_4-1_19-2.jpg" + }, + "user_dpp": 1 + }, + { + "id": 160639, + "text": "That feeling when you spend hours trying to fix something, only to give up.\n\nThen you come back he next day and find you were missing a comma...", + "score": 11, + "created_time": 1472617349, + "attached_image": "", + "num_comments": 1, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 47060, + "user_username": "DeveloperACE", + "user_score": 3985, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-6_16-15_3-10_8-4_7-4_5-3_12-6_6-33_10-3_2-10_18-1_4-3_19-3.jpg" + } + }, + { + "id": 319550, + "text": "Still using xp in school \nWTF WHY AM I STILL LIVING ON THIS PLANET ouch", + "score": 11, + "created_time": 1480843871, + "attached_image": "", + "num_comments": 14, + "tags": [ + "school", + "wtf are you doing!", + "fuck this shit", + "why me", + "xp" + ], + "vote_state": 0, + "edited": false, + "user_id": 315507, + "user_username": "H1ghTech", + "user_score": 226, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-6_3-2_8-1_7-1_5-1_12-2_6-11_10-3_2-3_18-1_4-1_19-1.jpg" + } + }, + { + "id": 566645, + "text": "Monday \npm: anon we need this by Friday I will get send you the requirements. \n\nTuesday\nPm no show.\n \nWednesday afternoon\nPm: here are the requirements anon(literally only one sentence) wtf \n\nThrusday \nPm: can I test it anon.", + "score": 11, + "created_time": 1493773599, + "attached_image": "", + "num_comments": 1, + "tags": [ + "imoutofhere", + "icantworkwiththisguy", + "pmrant", + "wtf?", + "pms" + ], + "vote_state": 0, + "edited": false, + "user_id": 563663, + "user_username": "ChrundelDeGreat", + "user_score": 117, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-1_1-4_16-6_3-4_8-1_7-1_5-1_12-4_6-14_10-1_2-18_15-47_11-2_4-1.jpg" + } + }, + { + "id": 569069, + "text": "Is this how i should sanitize my inputs?", + "score": 10, + "created_time": 1493887757, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_569069_xrBcf.jpg", + "width": 796, + "height": 91 + }, + "num_comments": 2, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 542210, + "user_username": "Gielert", + "user_score": 32, + "user_avatar": { + "b": "a973a2", + "i": "v-17_c-3_b-2_g-m_9-1_1-6_16-6_3-4_8-1_7-1_5-1_12-6_6-11_2-26_15-11_4-1.jpg" + } + }, + { + "id": 26272, + "text": "Network manager: administration just canceled our domain registration 2 months before schedule.\n\nBoss: uh.. OK... but can't we make a script to route traffic from www.canceled.com to our server?\n\nMe: that's not how DNS works ( proceed to explain how DNS works ) \n\nPS: boss is lead developer... wtf?", + "score": 10, + "created_time": 1463355861, + "attached_image": "", + "num_comments": 2, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "links": [ + { + "type": "url", + "url": "http://www.canceled.com", + "short_url": "www.canceled.com", + "title": "www.canceled.com", + "start": 164, + "end": 180, + "special": 1 + } + ], + "special": true, + "user_id": 26258, + "user_username": "azous", + "user_score": 5762, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-3_16-6_3-1_8-1_7-1_5-4_12-3_6-52_10-5_2-50_11-4_18-4_4-4_19-3_20-5_21-2.jpg" + } + }, + { + "id": 646986, + "text": "> Developer in a company that builds Enterprise Applications with web client and stuff. \r\n> Had an really nice website a which colleague made with joomla as base.\r\n> New CMO thinks it would be cool if he could change more details.\r\n> Company now uses some cheap ugly website builder.\n\nWTF is wrong with u O.o", + "score": 10, + "created_time": 1497338836, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wtf", + "company", + "website", + "joomla" + ], + "vote_state": 0, + "edited": false, + "user_id": 646969, + "user_username": "DROPTABLE", + "user_score": 26, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-2_16-1_3-5_8-1_7-1_5-1_12-2_6-99_2-12_15-20_4-1.jpg" + } + }, + { + "id": 250353, + "text": "Installing COSU devices. Need to setup 200 Androids. I boot up number 43 and it's set to Chinese language. I switch to English. It registers with the network.. WTF there is a sim card inside. =D And it was in a sealed package.\n\nNow I am a proud owner of some poor bastards China Unicom WO sim card. =D", + "score": 10, + "created_time": 1477290230, + "attached_image": "", + "num_comments": 0, + "tags": [ + "#wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 164065, + "user_username": "xios", + "user_score": 1802, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-2_16-15_3-1_8-2_7-2_5-2_12-2_6-7_10-9_2-54_18-3_4-2_19-3_20-5.jpg" + }, + "user_dpp": 1 + }, + { + "id": 159004, + "text": "Soon we enter the WTF part of the week...", + "score": 10, + "created_time": 1472500122, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_159004_qfZSe.jpg", + "width": 800, + "height": 800 + }, + "num_comments": 0, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 51967, + "user_username": "overtune", + "user_score": 1470, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-1_1-1_16-15_3-2_8-2_7-2_5-1_12-1_6-4_10-5_2-14_11-2_4-1.jpg" + } + }, + { + "id": 81634, + "text": "was handed a new .NET project (im not a C# guy). i go to spin up a windows 10 machine, i have a 20 gig SSD thinking that would be enough - Windows 10 + Visual studio is 24 gigs???? WTF!!!!", + "score": 10, + "created_time": 1466609360, + "attached_image": "", + "num_comments": 2, + "tags": [ + "wtf", + "c#", + ".net", + "fail", + "windows" + ], + "vote_state": 0, + "edited": false, + "user_id": 27533, + "user_username": "mattwebdev", + "user_score": 1281, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-3_16-16_3-10_8-1_7-1_5-1_12-3_6-14_2-43_15-27_4-1.jpg" + } + }, + { + "id": 143381, + "text": "Client: Can you have a look at this website template for me?\nMe: Sure, what's the address?\nClient: You'll need to modify your hosts file to see it...\nMe: ๐Ÿ˜ก๐Ÿ˜ท", + "score": 9, + "created_time": 1471340360, + "attached_image": "", + "num_comments": 5, + "tags": [ + "seriously?", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 135193, + "user_username": "pixeltherapy", + "user_score": 2993, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-3_16-9_3-2_8-2_7-2_5-2_12-3_6-2_10-9_2-41_15-6_11-4_4-2.jpg" + } + }, + { + "id": 163019, + "text": "If the codebase quality drops below levels measurable by way of \"WTFs per minute\", determine further negative code quality by counting the amount of times per hour you wonder if you're in Hell and what exactly you did to deserve this torment", + "score": 9, + "created_time": 1472739668, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wtf per minute", + "hell", + "badcode", + "poor code" + ], + "vote_state": 0, + "edited": false, + "user_id": 138232, + "user_username": "comradebeaglsky", + "user_score": 494, + "user_avatar": { + "b": "2a8b9d" + } + }, + { + "id": 758037, + "text": "WTF...!!!", + "score": 9, + "created_time": 1501994640, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_758037_YnKcP.jpg", + "width": 612, + "height": 476 + }, + "num_comments": 8, + "tags": [ + "show password", + "username", + "password" + ], + "vote_state": 0, + "edited": false, + "user_id": 754703, + "user_username": "alen-francis", + "user_score": 932, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-1_16-15_3-3_8-1_7-1_5-1_12-1_6-10_10-7_2-10_15-47_11-1_18-2_4-1_19-1.jpg" + } + }, + { + "id": 131552, + "text": "WTF Google?", + "score": 9, + "created_time": 1470391916, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_131552_NM6oA.jpg", + "width": 799, + "height": 477 + }, + "num_comments": 3, + "tags": [ + "google", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 68889, + "user_username": "crisz", + "user_score": 4394, + "user_avatar": { + "b": "ecd276", + "i": "v-17_c-3_b-7_g-m_9-1_1-9_16-9_3-7_8-4_7-4_5-3_12-9_6-3_10-8_2-74_15-19_18-4_4-3_19-3_20-4.jpg" + } + }, + { + "id": 391010, + "text": "So I've been maintaining our company's web products for a few years now with a great senior dev, but why would it ever make sense to have a \n\nbool somebool = returnsBool();\nif (somebool == true)\n ...\n\nWTF?!?! I still fine them in the code to this day.", + "score": 9, + "created_time": 1484882944, + "attached_image": "", + "num_comments": 6, + "tags": [ + "code", + "c#", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 388808, + "user_username": "aquacash5", + "user_score": 206, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-1_16-3_3-1_8-1_7-1_5-1_12-1_6-43_2-76_15-48_4-1_19-1.jpg" + } + }, + { + "id": 163106, + "text": "When someone sends you a link to a 10.X.X.X address....", + "score": 9, + "created_time": 1472744262, + "attached_image": "", + "num_comments": 2, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 90430, + "user_username": "spl0", + "user_score": 2276, + "user_avatar": { + "b": "f99a66" + } + }, + { + "id": 175936, + "text": "Hi devRant, meet the unresettable computer. When you try to reset it, it asks you for a keyboard layout...BUT YOU HAVE NO INPUT DEVICE TO ANSWER! Fucking great...", + "score": 9, + "created_time": 1473445031, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_175936_zQHtw.jpg", + "width": 799, + "height": 449 + }, + "num_comments": 3, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 18681, + "user_username": "Lukas", + "user_score": 1934, + "user_avatar": { + "b": "a973a2", + "i": "v-17_c-3_b-2_g-m_9-1_1-1_16-9_3-1_8-2_7-2_5-2_12-1_6-2_2-42_18-3_4-2_19-3.jpg" + } + }, + { + "id": 13367, + "text": "When someone merged in unformatted code and then they put up a pull request saying that they changed these ten things and formatted the code and the entire file is different...", + "score": 9, + "created_time": 1462564902, + "attached_image": "", + "num_comments": 1, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 6682, + "user_username": "alias", + "user_score": 166, + "user_avatar": { + "b": "a973a2" + } + }, + { + "id": 254572, + "text": "So today's conversation with my co-worker who built our build system...\n\nMe:OS X build server is not building valid installs.\nHim:What's the problem?\nMe:The KEXT is not rebuild... I think that Jenkins isn't capable of updating the file because of the permissions the script set when you test compiled it manually... Could you please add Jenkins user to sudoers file or something?\nHim:Yes of course, but what should I google?\n\nWTF dude? Do you even think yourself? And for some reason no-one has acces to the build servers configs exept for him and he shows up like 3 times a week...", + "score": 9, + "created_time": 1477507006, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wtf", + "what should i google" + ], + "vote_state": 0, + "edited": false, + "user_id": 36375, + "user_username": "jandans1", + "user_score": 325, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-1_1-2_16-1_3-14_8-1_7-1_5-1_12-2_6-9_2-39_4-1.jpg" + } + }, + { + "id": 196613, + "text": "Keep this in mind: I don't like WordPress and PHP at all!!!\nSo a couple of days ago my boss asked me if I could extend a custom made WordPress plugin made by our intern. First thought: sure why not? Boss says: it has to be done in less than 100 hours of work (an estimate done by my boss and the intern). Me: I can't tell you that before I have seen the code and what functionality has to be in the extension. Boss: Cool, look it over this weekend and tell me if you want to do it or not. \nI looked it through and my answer will probably be: NO WHERE IN HELL am I gonna are this in less that 100 hours! 1. no tests has been performed so I have absolutely no clue if his code works. \n2. variable names are mostly: $string_query (whatever that means?), $result, $string_temp and so on. \n3. Methods and functions are more than 250 lines long, with shitty formatting, and more comments than code. WTF? \n4. The estimate has been made by an intern and my boss (doesn't know much about programming). I haven't been consulted about it....\n5. No version control. No branches, no commits other than initial commit. Great. \n6. Most comments in the code just tells me what I can read from the code. What it returns and what it takes as params. Can I please know wtf your method call named $booking->run () does? I still haven't found this method in the code after 1 hour of intensively looking for it...\n\nFFS man... Not gonna do this, even though I thought it would have been an interesting project initially.\n\nSorry for the long rant... I just wish the intern would have consulted me about all this shit, since he obviously have bad practices. *sigh*", + "score": 9, + "created_time": 1474581482, + "attached_image": "", + "num_comments": 6, + "tags": [ + "killmeslowly", + "intern", + "longrant", + "wtf" + ], + "vote_state": 0, + "edited": true, + "user_id": 23618, + "user_username": "aaxa", + "user_score": 1092, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-9_3-2_8-2_7-2_5-1_12-1_6-11_10-9_2-42_15-19_11-3_18-2_4-1_19-2_20-11.jpg" + }, + "user_dpp": 1 + }, + { + "id": 816403, + "text": "Whaaaat theeeee actual fuuuuuuuuck. So basically I've got a server running and everything is fine. All services are working and I can access the webserver running on it over every browser. But randomly my ssh access stopped working (can connect but doesn't return shit after last login message) and when loading the web config thingie from my provider it gives me an empty response (all other pages from the provider are working). So basically I've got a working server I cannot access. But I'd like to access it and cannot even restart that shitty thing.\n\nAnybody else had a problem like that or has any idea wtf is going on?", + "score": 9, + "created_time": 1504435539, + "attached_image": "", + "num_comments": 5, + "tags": [ + "hatemylife", + "server", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 664776, + "user_username": "b3b3", + "user_score": 568, + "user_avatar": { + "b": "a973a2", + "i": "v-17_c-3_b-2_g-m_9-1_1-9_16-4_3-13_8-1_7-1_5-1_12-9_6-32_2-33_15-59_4-1.jpg" + } + }, + { + "id": 516875, + "text": "To day at scool a strange teacher tell me to have a software on paper !? WTF", + "score": 9, + "created_time": 1491549128, + "attached_image": "", + "num_comments": 5, + "tags": [ + "wtf scool paper software teacher pen" + ], + "vote_state": 0, + "edited": false, + "user_id": 516194, + "user_username": "PrincesseLulu", + "user_score": 25, + "user_avatar": { + "b": "a973a2", + "i": "v-17_c-3_b-2_g-m_9-1_1-2_16-3_3-3_8-1_7-1_5-1_12-2_6-7_2-1_4-1.jpg" + } + }, + { + "id": 344085, + "text": "Can anybody here tell wtf answer of this question is?\n\nMost of the quizzes in this test are terrible. Answer to some options don't get displayed because it gets parsed by the browser. I think the person who developed this website and questions suck at this.", + "score": 9, + "created_time": 1482153117, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_344085_xE75o.jpg", + "width": 799, + "height": 449 + }, + "num_comments": 5, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 232103, + "user_username": "HoloDreamer", + "user_score": 4189, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-2_16-6_3-4_8-1_7-1_5-1_12-2_6-10_10-1_2-31_15-14_4-1_20-10_21-2.jpg" + } + }, + { + "id": 70628, + "text": "Tye branch manager just made me spend 1.5 hours rearranging a huge dropdown list because he wanted to be able to sort it differently. We are deleting that dropdown list tomorrow.", + "score": 9, + "created_time": 1465917329, + "attached_image": "", + "num_comments": 1, + "tags": [ + "wtf?? wastingmydamntime" + ], + "vote_state": 0, + "edited": false, + "user_id": 3359, + "user_username": "MightybeforeGod", + "user_score": 383, + "user_avatar": { + "b": "f99a66" + } + }, + { + "id": 159045, + "text": "why the fuck do these mother fuckers have to give information to the ones responsible for designing the fucking application on the approval fase so I have to redesign some areas and spend a lot of time doing shit I could do once...\nand that's not new information... no... I could handle it... it's fucking things you know since your fucking birth!!! fuck you and your shit talk!!!", + "score": 9, + "created_time": 1472502192, + "attached_image": "", + "num_comments": 1, + "tags": [ + "wtf", + "fml" + ], + "vote_state": 0, + "edited": false, + "user_id": 26258, + "user_username": "azous", + "user_score": 5762, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-3_16-6_3-1_8-1_7-1_5-4_12-3_6-52_10-5_2-50_11-4_18-4_4-4_19-3_20-5_21-2.jpg" + } + }, + { + "id": 6293, + "text": "When the IT shop tells you they are running low on disk and can't give your shop any additional storage...", + "score": 8, + "created_time": 1460596988, + "attached_image": "", + "num_comments": 2, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 4665, + "user_username": "wbuchanan", + "user_score": 296, + "user_avatar": { + "b": "7bc8a4" + } + }, + { + "id": 775787, + "text": "WTF, I've just written 3 lines of code, I'm gonna check devrant and social media.", + "score": 8, + "created_time": 1502712953, + "attached_image": "", + "num_comments": 5, + "tags": [ + "lazy", + "devrant", + "youtube", + "facebook", + "twitter", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 605966, + "user_username": "develalfy", + "user_score": 62, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-3_3-4_8-1_7-1_5-1_12-2_6-54_10-1_2-10_15-14_4-1.jpg" + } + }, + { + "id": 652529, + "text": "You have never experienced hate and anger until you have to migrate your asp.net application to a SharePoint server (with all server-side scripting disabled) and being told \"Just make it work\"\n\nbecause developers are, you know....magicians", + "score": 8, + "created_time": 1497548502, + "attached_image": "", + "num_comments": 2, + "tags": [ + "wtf??" + ], + "vote_state": 0, + "edited": false, + "user_id": 18596, + "user_username": "needsmoreimages", + "user_score": 1337, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-5_16-1_3-13_8-1_7-1_5-1_12-5_6-61_2-39_15-13_4-1.jpg" + } + }, + { + "id": 155304, + "text": "Run test \neverything ok\nRun again \neverything ok\nRun again \nerror \nRun again \neverything ok\n\n*wtaf is happening?*\nI googled for the cause of a \"double callback\" error and found out that supertest was throwing it when I was attaching a file to the API request. found no conclusive way to solve it, just that \"supertest does it from time to time\". \n\nTrying to investigate further, I made a function to repeat the API request 5 times in a row. Everything passed. The run it again. Two failed. Again and everything passed.\n\nthis is bullshit \n\nfml", + "score": 8, + "created_time": 1472228081, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_155304_nNiNN.jpg", + "width": 209, + "height": 273 + }, + "num_comments": 0, + "tags": [ + "wtf", + "api", + "supertest", + "js" + ], + "vote_state": 0, + "edited": false, + "user_id": 63587, + "user_username": "thassiov", + "user_score": 1100, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-2_16-3_3-2_8-2_7-2_5-1_12-2_6-7_2-14_15-48_18-1_4-1_19-1.jpg" + } + }, + { + "id": 166859, + "text": "So I was using Defraggler to defragment one of the partitions on my computer, and somehow ended up with more fragmentation than there were to start with. WTF?!", + "score": 8, + "created_time": 1472972405, + "attached_image": "", + "num_comments": 12, + "tags": [ + "defrag", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 164544, + "user_username": "darthtigris", + "user_score": 2149, + "user_avatar": { + "b": "a973a2", + "i": "v-17_c-3_b-2_g-m_9-1_1-2_16-6_3-4_8-2_7-2_5-2_12-2_6-2_10-9_2-30_15-14_11-7_18-4_4-2_19-3_21-1.jpg" + } + }, + { + "id": 548497, + "text": "Junior asks me to help him with his Microprocessors project. I was like cool mail it to me I'll check it out. He sends me a .s assembly file and tells me this is machine generated code, can you make it look like it's written by a human. I was like wtf dude -_-.", + "score": 8, + "created_time": 1492959224, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wtf", + "juniors", + "assembly" + ], + "vote_state": 0, + "edited": false, + "user_id": 538863, + "user_username": "TheLazyGoan", + "user_score": 2009, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-7_16-13_3-1_8-2_7-2_5-2_12-7_6-2_10-9_2-41_15-10_11-3_18-4_4-2_19-3_20-8_21-2.jpg" + } + }, + { + "id": 241983, + "text": "Cleaning up a CSS file that was becoming out of hand... Found this gem:\n\n.main-content .panel-body .alert {\n margin: -19px -16px 20px -16px;\n margin: -19px -16px 00px -16px;\n}", + "score": 8, + "created_time": 1476842696, + "attached_image": "", + "num_comments": 4, + "tags": [ + "wtf", + "css" + ], + "vote_state": 0, + "edited": false, + "user_id": 79504, + "user_username": "zareef", + "user_score": 137, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-4_16-1_3-3_8-1_7-1_5-1_12-4_6-2_10-1_2-12_15-14_4-1.jpg" + } + }, + { + "id": 209096, + "text": "from ... import *\n\nFor one function. Why.", + "score": 8, + "created_time": 1475180428, + "attached_image": "", + "num_comments": 3, + "tags": [ + "wtf", + "python" + ], + "vote_state": 0, + "edited": false, + "user_id": 156434, + "user_username": "corscheid", + "user_score": 2310, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-6_3-1_8-1_7-1_5-1_12-2_6-2_10-1_2-10_11-2_18-1_4-1_19-1.jpg" + }, + "user_dpp": 1 + }, + { + "id": 511144, + "text": "I am not even at our office yet the PM already sent us multiple emails asking us to do trivial stuff like update excel sheets, file reports, etc... WTF!? Can't you fucking wait till I get to work!?", + "score": 8, + "created_time": 1491263083, + "attached_image": "", + "num_comments": 1, + "tags": [ + "bad pm", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 244100, + "user_username": "silly-symphony", + "user_score": 2978, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-2_3-3_8-2_7-2_5-2_12-1_6-2_10-5_2-94_11-2_18-4_4-2_19-3_20-11.jpg" + } + }, + { + "id": 166635, + "text": "When you have friends that have no idea how to use a computer.\n\nDave: Dude, I need some RAM\n\nNathan: Ram? You need a goat?\n\nDave: Sigh, Back to square one...", + "score": 8, + "created_time": 1472952152, + "attached_image": "", + "num_comments": 1, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 166595, + "user_username": "Nova54517", + "user_score": 138, + "user_avatar": { + "b": "7bc8a4" + } + }, + { + "id": 62484, + "text": "โ€œThis value must be shorter than 20 characters in length.โ€ โ€ฆ password field, bank website, 2016, wtf ยฏ\\_(ใƒ„)_/ยฏ", + "score": 7, + "created_time": 1465430904, + "attached_image": "", + "num_comments": 2, + "tags": [ + "security", + "passwords", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 16699, + "user_username": "maiis", + "user_score": 401, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-10_3-6_8-1_7-1_5-1_12-1_6-11_10-1_2-5_15-19_11-2_4-1.jpg" + } + }, + { + "id": 94860, + "text": "for the last 6 consecutive Wednesdays, I've gotten some form of an email stating \"sorry, venue super swamped, we definite want to find a way to work with you. we're still is talks with other developers, but we really like your knowledge of the project and want to work with you in some capacity\". The only stated reason for not going with me in the first place was I told them the project would take 8-12 weeks, but they needed it turned around in 10 weeks max.... you do the math...", + "score": 7, + "created_time": 1467710863, + "attached_image": "", + "num_comments": 1, + "tags": [ + "wk7", + "wtf" + ], + "vote_state": 0, + "edited": false, + "weekly": { + "week": 7, + "topic": "Worst interview experience?", + "date": "7/4/16", + "height": 50 + }, + "user_id": 67254, + "user_username": "uniplatapus", + "user_score": 453, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-15_3-2_8-1_7-1_5-1_12-1_6-3_10-5_2-14_15-53_11-1_4-1.jpg" + } + }, + { + "id": 390035, + "text": "4 months ago, my team had the task of redesigning the login page of our main app. Really nice design. Since it was fairly simple, it was given to one of our summer camp guys to do something useful. After he finished, it was stuck on merge request and no one bothered to check it, as it was not important for our PO's, it simply got forgotten...\nLast week, since I was bored and remembered about it, I decided to check it and fix the small issues it had, without telling anything to our PO, just did it, asked for code review and added it to our latest release.\n\nToday I overheard 2 guys from analytics team:\n\"Hey, have you seen our new login page?\"\n\"There is a new WordPress developer so he just does his job well\"\n\nOur application is not in WordPress, only our company's website is!\nOur application is in Angular!\nThere is no new WordPress developer! We only have an offer looking for one!\n\nWTF", + "score": 7, + "created_time": 1484831514, + "attached_image": "", + "num_comments": 2, + "tags": [ + "angularjs", + "wordpress", + "wtf?" + ], + "vote_state": 0, + "edited": false, + "user_id": 290842, + "user_username": "shizpi", + "user_score": 88, + "user_avatar": { + "b": "ecd276", + "i": "v-17_c-3_b-7_g-m_9-1_1-4_16-1_3-8_8-1_7-1_5-1_12-4_6-14_10-1_2-8_4-1.jpg" + } + }, + { + "id": 50356, + "text": "Saw an add for a dev job in which to be considered you had to build a social media network. Seriously.", + "score": 7, + "created_time": 1464953386, + "attached_image": "", + "num_comments": 2, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 50257, + "user_username": "georgeliquor", + "user_score": 501, + "user_avatar": { + "b": "d55161" + } + }, + { + "id": 265149, + "text": "16 Lines of code.\r\nNearly 600 words of documentation.", + "score": 7, + "created_time": 1478006128, + "attached_image": "", + "num_comments": 5, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 225103, + "user_username": "verkruemelt", + "user_score": 223, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-6_3-4_8-1_7-1_5-1_12-1_6-96_2-18_11-2_18-1_4-1_19-1.jpg" + } + }, + { + "id": 179766, + "text": "I've just read iPhone 7 has 2 GB RAM.\nWait, wat?\nOnePlus has 6.", + "score": 7, + "created_time": 1473695687, + "attached_image": "", + "num_comments": 5, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": true, + "user_id": 144116, + "user_username": "light2yellow", + "user_score": 524, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-3_3-3_8-1_7-1_5-1_12-1_17-1_6-3_10-5_2-26_4-1.jpg" + } + }, + { + "id": 541400, + "text": "HOW HAVE I NEVER RUN INTO THE SIGNED ZERO (-0) PROBLEM BEFORE?!?!", + "score": 7, + "created_time": 1492641757, + "attached_image": "", + "num_comments": 1, + "tags": [ + "wtf js" + ], + "vote_state": 0, + "edited": true, + "user_id": 9713, + "user_username": "ExcellentSP", + "user_score": 901, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-3_16-16_3-13_8-1_7-1_5-1_12-3_17-1_6-3_10-9_2-43_15-19_11-3_18-2_4-1_19-2.jpg" + } + }, + { + "id": 227644, + "text": "Wtf youtube", + "score": 7, + "created_time": 1476133546, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_227644_9jRU2.jpg", + "width": 800, + "height": 449 + }, + "num_comments": 4, + "tags": [ + "wtf youtube" + ], + "vote_state": 0, + "edited": false, + "user_id": 166659, + "user_username": "Decabyte", + "user_score": 122, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-1_1-2_16-1_3-5_8-1_7-1_5-1_12-2_6-50_2-14_15-18_18-1_4-1_19-1.jpg" + } + }, + { + "id": 178228, + "text": "Was there a recent CSS update or something? My website just broke all of a sudden. I haven't touched anything since last year WTF", + "score": 7, + "created_time": 1473596783, + "attached_image": "", + "num_comments": 4, + "tags": [ + "wtf", + "css" + ], + "vote_state": 0, + "edited": false, + "user_id": 42079, + "user_username": "tahnik", + "user_score": 34697, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-2_1-4_16-15_3-2_8-4_7-4_5-3_12-4_6-10_10-9_2-39_15-18_11-4_18-4_4-3_19-1_20-14_21-1.jpg" + }, + "user_dpp": 1 + }, + { + "id": 248316, + "text": "So I decided to download OS X iso from a third party website as I don't have a mac. After downloading I couldn't extract the iso file as it showed the zip file was corrupted.\n\nFrom the comment I found out that the zip file can be only extracted in a mac :/\n\nHow is that even possible :/", + "score": 7, + "created_time": 1477167036, + "attached_image": "", + "num_comments": 4, + "tags": [ + "mac", + "wtf" + ], + "vote_state": 0, + "edited": true, + "user_id": 42079, + "user_username": "tahnik", + "user_score": 34697, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-2_1-4_16-15_3-2_8-4_7-4_5-3_12-4_6-10_10-9_2-39_15-18_11-4_18-4_4-3_19-1_20-14_21-1.jpg" + }, + "user_dpp": 1 + }, + { + "id": 83888, + "text": "Doing a copy and paste (within the same file) and the IDE stops responding.", + "score": 6, + "created_time": 1466781470, + "attached_image": "", + "num_comments": 7, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 40485, + "user_username": "host", + "user_score": 4343, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-6_3-3_8-1_7-1_5-1_12-2_6-31_2-18_11-2_4-1.jpg" + } + }, + { + "id": 748117, + "text": "Watched Tom Scott's video on FizzBuzz today. Quite interesting. Did some further reading and apparently some people either fail completely or it takes them forever to get to a solution.\nAnd this is Computer science graduates we're talking about. Like wtf? \n\nFor those of you who doesn't know what FizzBuzz is, just Google it.", + "score": 6, + "created_time": 1501549252, + "attached_image": "", + "num_comments": 3, + "tags": [ + "fizzbuzz", + "easy", + "fizz", + "buzz", + "why do you even go to school", + "wtf", + "lmao" + ], + "vote_state": 0, + "edited": false, + "user_id": 524493, + "user_username": "olback", + "user_score": 2351, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-6_16-3_3-3_8-3_7-3_5-4_12-6_6-3_10-5_2-26_18-1_4-4_19-2_20-5.jpg" + }, + "user_dpp": 1 + }, + { + "id": 289874, + "text": "\"Instead of using languages we all master lets choose language none of us knows and lets use it to build our time constrained business critical platform. Don't worry about the fact we are about a year past deadline\" argued leads of another project.\n\nYeah. About that...", + "score": 6, + "created_time": 1479322081, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_289874_cBjGP.jpg", + "width": 799, + "height": 267 + }, + "num_comments": 2, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 238036, + "user_username": "addvilz", + "user_score": 525, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-15_3-1_8-1_7-1_5-1_12-1_6-4_2-10_15-8_11-1_4-1.jpg" + } + }, + { + "id": 82780, + "text": "It happened again:\nObject reference not set to an instance of an object", + "score": 6, + "created_time": 1466690355, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_82780_J5JBF.jpg", + "width": 800, + "height": 533 + }, + "num_comments": 1, + "tags": [ + "wtf", + "fml" + ], + "vote_state": 0, + "edited": false, + "user_id": 82736, + "user_username": "navi", + "user_score": 721, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-1_1-3_16-4_3-1_8-1_7-1_5-1_12-3_6-10_10-1_2-8_18-2_4-1_19-1.jpg" + } + }, + { + "id": 449007, + "text": "In romania's parlament, the opposition lit their phone's flashlights as the street's protesters did every evening within public square.\nOne of majority's members (leds were headed toward them) said that carriers should provide detailed bills with costs and the senators who lighted up their leds must be charged with spending and abusing public funds because flashlights consume monthly carrier subscription.", + "score": 6, + "created_time": 1488055641, + "attached_image": "", + "num_comments": 4, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 392723, + "user_username": "tomeb", + "user_score": 1075, + "user_avatar": { + "b": "a973a2", + "i": "v-17_c-3_b-2_g-m_9-1_1-2_16-1_3-1_8-1_7-1_5-1_12-2_6-31_10-1_2-1_4-1.jpg" + } + }, + { + "id": 487758, + "text": "Programming in Delphi without any concepz. Done in CS class in school. Like wtf did anyone see delphi even near production?! Teacher did not know any other programming language", + "score": 6, + "created_time": 1490049012, + "attached_image": "", + "num_comments": 2, + "tags": [ + "wtf", + "wk44 bad teacher" + ], + "vote_state": 0, + "edited": false, + "user_id": 405471, + "user_username": "dmin", + "user_score": 39, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-3_16-6_3-1_8-1_7-1_5-1_12-3_6-97_2-32_15-10_4-1.jpg" + } + }, + { + "id": 226696, + "text": "Is it asking too much that a state agency use any kind of naming convention in their data management? I've come across about 7 different column names that all contain a value representing the number of students. Trying to decide whether I should send a deluge of tickets at their help system by submitting a ticket for every single row that contains any type of error.", + "score": 6, + "created_time": 1476094834, + "attached_image": "", + "num_comments": 1, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 4665, + "user_username": "wbuchanan", + "user_score": 296, + "user_avatar": { + "b": "7bc8a4" + } + }, + { + "id": 42496, + "text": "Someone just opened Jira about other open Jira.. WTF!?!?", + "score": 6, + "created_time": 1464523721, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_42496_DKTFm.jpg", + "width": 550, + "height": 382 + }, + "num_comments": 0, + "tags": [ + "jira", + "r u serious?", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 16888, + "user_username": "Raspik", + "user_score": 2380, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-11_3-4_8-2_7-2_5-2_12-2_6-82_10-5_2-49_15-6_11-3_18-4_4-2_19-3.jpg" + } + }, + { + "id": 667391, + "text": "Fucking backend devs who changes the endpoint specs without telling anyone and breaks all clients applications.", + "score": 6, + "created_time": 1498129236, + "attached_image": "", + "num_comments": 1, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 666656, + "user_username": "wakazors", + "user_score": 363, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-2_16-11_3-2_8-1_7-1_5-1_12-2_6-6_10-1_2-1_15-14_18-1_4-1_19-1.jpg" + } + }, + { + "id": 39413, + "text": "That monent when you find out that the US nuclear arsenal controlled by 1970s computers with 8in floppy disks:\n\nhttp://gu.com/p/4jjx2/...", + "score": 6, + "created_time": 1464266681, + "attached_image": "", + "num_comments": 5, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "links": [ + { + "type": "url", + "url": "http://gu.com/p/4jjx2?CMP=Share_AndroidApp_Copy_to_clipboard", + "short_url": "http://gu.com/p/4jjx2/...", + "title": "http://gu.com/p/4jjx2/...", + "start": 112, + "end": 137, + "special": 1 + } + ], + "special": true, + "user_id": 6750, + "user_username": "g-m-f", + "user_score": 19401, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-13_3-5_8-1_7-1_5-4_12-2_6-7_10-5_2-42_4-4.jpg" + } + }, + { + "id": 526508, + "text": "\n\nPlot: we need to release our website in two weeks which holds at least a thousand pages. All these pages are manually migrated from the old website, which doesn't have a database. Current status: 650 pages/1000 are completed, 40 different templates need to be adapted. I'm alone on these templates, my colleagues create the pages and fill the new database \n\nSo I'm working on the templates a WebDev coded for our website on a licensed CMS, and had this decently simple html block that looks like a square and consisting of roughly this (Emmet style): \n\na.area > blockquote > strong.title + p \n\nAfter adding another element inside the p, I noticed that my wouldn't display and bust the whole look of the square. \n\nJust for more details, the CSS the dev made is ultra specified (meaning each element is too precisely \"described\" : div.class .child .child2 { /* styles */ } when it could be .class .child2 for example). Also, the templates he made need to be compatible with any \"module\" the website has, thus the need of this high specificity\n\nSo I fired up the DevTools to check what happened, and had: \n\nExpected: a.area > blockquote > strong.title + p > a\nActual result: some new a.area were wrapping the , the

and the I just added. The source code was not showing any of this but just the rules I initially wrote - the expected result\n\nWtf?! I thought the JS the dev made was adding elements. I disabled said JS, and bam, these a.area were still wrapping everything!! What black magic would add these stupid tags I never asked for.\n\nSo I went looking in the CSS files in case some wizardry was happening, but everything was OK.\n\nI tried changing my structure, changing tag (swapping a.area to p.area or without .area), HTML just said \"nope, have those please\".\n\nEventually I rewrote my own module out of frustration after three quarters of an hour fiddling with this stupid \"module\". I hate losing time for such shenanigans and under a lot of pressure because of deadlines. \n\nStill haven't figured why those .area would wrap everything out of nowhere...", + "score": 6, + "created_time": 1491993734, + "attached_image": "", + "num_comments": 3, + "tags": [ + "gohomehtmlyouredrunk", + "wtf", + "html black magic", + "specified css" + ], + "vote_state": 0, + "edited": false, + "user_id": 475858, + "user_username": "Phlisg", + "user_score": 121, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-3_16-1_3-4_8-1_7-1_5-1_12-3_17-1_6-16_2-12_4-1.jpg" + } + }, + { + "id": 226483, + "text": "In a startup WE: \"I want to create airdrone who can detect agression and survey people by flying on the street. I'll call it Big Brother.\"\nWTF dude ?!", + "score": 5, + "created_time": 1476085182, + "attached_image": "", + "num_comments": 1, + "tags": [ + "startupwe", + "wtf", + "wk21" + ], + "vote_state": 0, + "edited": false, + "weekly": { + "week": 21, + "topic": "Dumbest project/startup idea pitched to you?", + "date": "10/10/16", + "height": 50 + }, + "user_id": 177835, + "user_username": "FlyingSocket", + "user_score": 145, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-6_3-4_8-1_7-1_5-1_12-1_6-11_10-1_2-12_4-1.jpg" + } + }, + { + "id": 503252, + "text": "I`m new to coding. So i`m also new at ranting.\n\nI know i have something to rant about. But my nerd culture is just not yet at the level. \n\nI have been taught by a mate to used linux and started vanilla javascript. We use intellj as IDE. \n\nSo i have to speak to this client whose previous IT provider was gonna code his thing with ASP and visual studio!!!\nRight?! WTF?!!! But that`s all i got!!!!\n\nIm pretty sure its a wtf?! But i don`t have the rock solid reasons why. \n\nPlease ranters help me become better at rantong and tell me i`m not wrong and why ;)", + "score": 5, + "created_time": 1490835167, + "attached_image": "", + "num_comments": 9, + "tags": [ + "wtf?", + "l2rant", + "linux" + ], + "vote_state": 0, + "edited": false, + "user_id": 487895, + "user_username": "Santaclauze", + "user_score": 892, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-1_1-3_16-7_3-12_8-1_7-1_5-1_12-3_17-2_6-47_2-75_15-15_18-2_4-1_19-1.jpg" + } + }, + { + "id": 417091, + "text": "Database assisted Dependency Injection... How does that sound?", + "score": 5, + "created_time": 1486476245, + "attached_image": "", + "num_comments": 1, + "tags": [ + "wtf?!?!" + ], + "vote_state": 0, + "edited": false, + "user_id": 345578, + "user_username": "SnafuAI", + "user_score": 676, + "user_avatar": { + "b": "a973a2", + "i": "v-17_c-3_b-2_g-m_9-1_1-1_16-15_3-2_8-1_7-1_5-1_12-1_6-30_10-5_2-91_15-6_18-2_4-1_19-1.jpg" + } + }, + { + "id": 131055, + "text": "IIS curse you and your nuances!\n\nI launch my local web application (which was working fine) and now get CORS errors and 404 not found. Wtf. I clean the solution rebuild, same thing. Then I restart my PC and try again. Same thing. \n\nThen I use Firefox instead if chrome and it magically works. Wtf!\n\nIt's hard to fix broken things when they fix themeselves afyer trial and error", + "score": 5, + "created_time": 1470348298, + "attached_image": "", + "num_comments": 1, + "tags": [ + "iis hosting", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 4072, + "user_username": "champion01", + "user_score": 1099, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-1_3-1_8-1_7-1_5-1_12-1_6-1_2-1_4-1.jpg" + } + }, + { + "id": 138530, + "text": "Dramatic question on Quora - \"Should I quit programming as languages are going to be dead?\"", + "score": 5, + "created_time": 1470947312, + "attached_image": "", + "num_comments": 8, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 132166, + "user_username": "firusvg", + "user_score": 1398, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-2_3-1_8-1_7-1_5-1_12-2_6-47_2-3_15-19_11-1_18-1_4-1.jpg" + } + }, + { + "id": 242208, + "text": "WTF What??", + "score": 5, + "created_time": 1476855888, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_242208_RHsxr.jpg", + "width": 480, + "height": 800 + }, + "num_comments": 0, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 234356, + "user_username": "elonmusk", + "user_score": 14074, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-2_1-1_16-13_3-3_8-4_7-4_5-4_12-1_6-14_10-9_2-49_15-10_11-3_18-4_4-4_19-3_20-13_21-1.jpg" + } + }, + { + "id": 228628, + "text": "3 diffrent versions of jquery on page...", + "score": 5, + "created_time": 1476187783, + "attached_image": "", + "num_comments": 0, + "tags": [ + "#wtf #javascript" + ], + "vote_state": 0, + "edited": false, + "user_id": 199563, + "user_username": "macbury", + "user_score": 446, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-2_16-2_3-1_8-1_7-1_5-1_12-2_6-16_10-1_2-10_15-12_11-2_4-1.jpg" + } + }, + { + "id": 277507, + "text": "And here I am at the hospital thinking about that stupid deadline... Wtf", + "score": 5, + "created_time": 1478629532, + "attached_image": "", + "num_comments": 1, + "tags": [ + "death line", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 26258, + "user_username": "azous", + "user_score": 5762, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-3_16-6_3-1_8-1_7-1_5-4_12-3_6-52_10-5_2-50_11-4_18-4_4-4_19-3_20-5_21-2.jpg" + } + }, + { + "id": 484989, + "text": "gettype(this_is_a_valid_string_in_php) === 'string';", + "score": 5, + "created_time": 1489905863, + "attached_image": "", + "num_comments": 2, + "tags": [ + "php", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 113667, + "user_username": "faisalhakim47", + "user_score": 842, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-1_1-2_16-6_3-5_8-1_7-1_5-1_12-2_6-14_10-4_2-28_11-2_18-2_4-1_19-1.jpg" + } + }, + { + "id": 160543, + "text": "Made a fresh Windows 7 install and directly wanted to update it. I didn't install or modify anything and the updater is corrupted. Wtf windows?", + "score": 5, + "created_time": 1472609877, + "attached_image": "", + "num_comments": 4, + "tags": [ + "wtf", + "windows update", + "windows" + ], + "vote_state": 0, + "edited": false, + "user_id": 149362, + "user_username": "Paramite", + "user_score": 4993, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-3_16-6_3-3_8-4_7-4_5-3_12-3_6-82_10-9_2-54_15-10_11-2_18-4_4-3_19-3_20-3_21-2.jpg" + }, + "user_dpp": 1 + }, + { + "id": 62055, + "text": "if you're a dev, WHY on earth would you send me a blind LinkedIn invite with no message? who the !@#$ are you??", + "score": 5, + "created_time": 1465411126, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_62055_WhFxN.gif", + "width": 480, + "height": 270 + }, + "num_comments": 1, + "tags": [ + "wtf", + "linkedin" + ], + "vote_state": 0, + "edited": false, + "user_id": 60175, + "user_username": "kindohm", + "user_score": 47, + "user_avatar": { + "b": "7bc8a4" + } + }, + { + "id": 361522, + "text": "Always shut down atleast once a month :/\nJust wanted to work a little :(", + "score": 5, + "created_time": 1483187926, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_361522_aJfsn.jpg", + "width": 750, + "height": 1000 + }, + "num_comments": 3, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 93523, + "user_username": "GurpreetSK95", + "user_score": 1105, + "user_avatar": { + "b": "ecd276", + "i": "v-17_c-3_b-7_g-m_9-1_1-2_16-12_3-1_8-1_7-1_5-1_12-2_17-2_6-30_10-1_2-25_15-47_11-1_18-2_4-1_19-2.jpg" + } + }, + { + "id": 565700, + "text": "So i was putting finishing touches on an android game i was building in Unity3D as a fun side project - implementing google play leaderboards and google ads. \nLuckily google is nice enough to include 2 unity plugins for ads and leaderboards/achievements however, THE FUCKING PLUGINS AREN'T COMPATIBLE. TWO PIECES OF SOFTWARE THAT ARE LITERALLY MEANT TO WORK TOGETHER HAVE COLLIDING .JAR FILES PREVENTING ME FROM BUILDING THE APK.\n\ni had to spend fucking days trying to figure out how to unpack the jar files and what to remove from them so they would stop colliding.\n\nWTF GOOGLE?", + "score": 5, + "created_time": 1493732990, + "attached_image": "", + "num_comments": 3, + "tags": [ + "wtf google go home you're drunk", + "unity3d", + "google play", + "android" + ], + "vote_state": 0, + "edited": false, + "user_id": 563052, + "user_username": "hellvetica", + "user_score": 629, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-1_16-6_3-1_8-1_7-1_5-1_12-1_6-11_2-67_15-11_18-2_4-1_19-1.jpg" + } + }, + { + "id": 49455, + "text": "Wtf looking at a Pull Request today for styling 4 buttons...\n750 lines of of CSS and not even able to style an anchor!! \nHow the hell is that even possible!!!", + "score": 5, + "created_time": 1464938435, + "attached_image": "", + "num_comments": 1, + "tags": [ + "wtf", + "scss" + ], + "vote_state": 0, + "edited": false, + "user_id": 49052, + "user_username": "gintom", + "user_score": 61, + "user_avatar": { + "b": "d55161" + } + }, + { + "id": 224455, + "text": "console(config)#ip ssh port 22\n\nInCorrect Port-Number : Port-Number Should be in the Range <1025 - 65535>\n\nconsole(config)#", + "score": 5, + "created_time": 1475969729, + "attached_image": "", + "num_comments": 4, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 90430, + "user_username": "spl0", + "user_score": 2276, + "user_avatar": { + "b": "f99a66" + } + }, + { + "id": 298367, + "text": "Finding this in the documentation - //skipping index that are multiples of 10 (multiples of 10 create problems as the input IDs) if ( $input_id % 10 == 0 ) { $input_id++; }", + "score": 5, + "created_time": 1479807675, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wtf", + "gravityforms" + ], + "vote_state": 0, + "edited": false, + "user_id": 16616, + "user_username": "wgroenewold", + "user_score": 840, + "user_avatar": { + "b": "f99a66" + } + }, + { + "id": 116832, + "text": "wtf boss!!!! Is it so hard to understand that having one column per year is a horrible bad practice?", + "score": 5, + "created_time": 1469119638, + "attached_image": "", + "num_comments": 9, + "tags": [ + "db", + "wtf", + "boss", + "fml", + "bad practice" + ], + "vote_state": 0, + "edited": false, + "user_id": 26258, + "user_username": "azous", + "user_score": 5762, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-3_16-6_3-1_8-1_7-1_5-4_12-3_6-52_10-5_2-50_11-4_18-4_4-4_19-3_20-5_21-2.jpg" + } + }, + { + "id": 761103, + "text": "Upwork isn't allowing new freelancers . Wtf", + "score": 5, + "created_time": 1502124807, + "attached_image": "", + "num_comments": 3, + "tags": [ + "upwork", + "freelance", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 638811, + "user_username": "mrSomeone", + "user_score": 676, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-10_16-10_3-2_8-1_7-1_5-1_12-10_6-2_10-1_2-26_15-10_11-1_4-1.jpg" + } + }, + { + "id": 212600, + "text": "WTF?!?! Just opened thrones amino app and I got this error. Something is strange. The night is dark and full of terrors.", + "score": 4, + "created_time": 1475355772, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_212600_wn6Wq.jpg", + "width": 563, + "height": 999 + }, + "num_comments": 0, + "tags": [ + "wtf?", + "thrones", + "amino" + ], + "vote_state": 0, + "edited": false, + "user_id": 18977, + "user_username": "oxkipo", + "user_score": 844, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-2_16-1_3-5_8-1_7-1_5-1_12-2_6-97_10-1_2-10_4-1.jpg" + } + }, + { + "id": 476969, + "text": "I said Medusa... I said Medusa.", + "score": 4, + "created_time": 1489516294, + "attached_image": "", + "num_comments": 1, + "tags": [ + "medusa", + "wtf?" + ], + "vote_state": 0, + "edited": false, + "user_id": 416381, + "user_username": "Peekaboo", + "user_score": 1027, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-5_16-8_3-14_8-2_7-2_5-1_12-5_17-2_6-36_10-9_2-23_15-64_11-4_18-2_4-1_19-2_20-11.jpg" + } + }, + { + "id": 51117, + "text": "In Sweden the word for computer is \"dator\" and the word for data is \"data\". \n\nHowever \"data\" is commonly used as slang for computer by muggles. So when people tell me that they have problems with their \"data\" I always get the wtf face.", + "score": 4, + "created_time": 1464964736, + "attached_image": "", + "num_comments": 0, + "tags": [ + "it", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 50263, + "user_username": "biglars", + "user_score": 94, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-3_16-5_3-5_8-1_7-1_5-1_12-3_6-3_2-10_15-19_11-1_4-1.jpg" + } + }, + { + "id": 551674, + "text": "i want to post something here but I have literally no effing idea about what to post... agrrr ๐Ÿ˜‘", + "score": 4, + "created_time": 1493072243, + "attached_image": "", + "num_comments": 0, + "tags": [ + "#noidea #wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 300535, + "user_username": "MunifTanjim", + "user_score": 18, + "user_avatar": { + "b": "ecd276", + "i": "v-17_c-3_b-7_g-m_9-1_1-2_16-6_3-4_8-1_7-1_5-1_12-2_6-30_2-10_15-6_11-2_4-1.jpg" + } + }, + { + "id": 564090, + "text": "friend : can you help me modifying my client's website? It's a company profile website so no complicated stuff. \nme : let me see... \n\n*it's bulit on opencart*\n\nme : wtf\nfriend : exactly ๐Ÿ˜‚", + "score": 4, + "created_time": 1493654451, + "attached_image": "", + "num_comments": 2, + "tags": [ + "wtf", + "opencart" + ], + "vote_state": 0, + "edited": false, + "user_id": 274779, + "user_username": "falmesino", + "user_score": 814, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-6_3-3_8-1_7-1_5-1_12-2_6-38_2-10_15-47_11-1_18-1_4-1_19-1.jpg" + } + }, + { + "id": 375347, + "text": "I wonder, what coding on acid is like...", + "score": 4, + "created_time": 1483986053, + "attached_image": "", + "num_comments": 4, + "tags": [ + "wtf", + "dude" + ], + "vote_state": 0, + "edited": true, + "user_id": 375335, + "user_username": "thallosaurus", + "user_score": 86, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-3_16-6_3-1_8-1_7-1_5-1_12-3_6-34_10-1_2-1_15-42_4-1.jpg" + } + }, + { + "id": 488302, + "text": "Long one\nSo our newest team-mate has made a channel dedicated to make fun of or scrum master, then I send him a pm saying to cut out the childs play, and we need to act professional.\nThen he tells me that our scrum master is okay with it, and really looks like he is!\nSM said this making jokes about him brings the whole team together and makes us a better team!!\nWTF...\nReally WTF ... am I the bad guy for caring about my team??", + "score": 4, + "created_time": 1490082898, + "attached_image": "", + "num_comments": 3, + "tags": [ + "wtf?", + "scrum", + "teammates" + ], + "vote_state": 0, + "edited": false, + "user_id": 487556, + "user_username": "tinybyte", + "user_score": 1368, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-2_16-16_3-1_8-2_7-2_5-1_12-2_6-7_10-9_2-54_11-3_18-2_4-1_19-2_20-7.jpg" + } + }, + { + "id": 487027, + "text": "Almost like a ritual I see the time 3:33 way too regularly. I need some brain science on this.", + "score": 4, + "created_time": 1490017087, + "attached_image": "", + "num_comments": 5, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 410146, + "user_username": "ohmylacooon", + "user_score": 299, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-1_16-1_3-3_8-1_7-1_5-1_12-1_6-44_10-3_2-37_15-12_18-1_4-1_19-1.jpg" + } + }, + { + "id": 21570, + "text": "Irrational Programmer's Response to Problems - \"I tried, if I couldn't find a solution to it, then it's impossible.\"", + "score": 4, + "created_time": 1463206873, + "attached_image": "", + "num_comments": 0, + "tags": [ + "notarealprogrammer", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 21330, + "user_username": "JDev", + "user_score": 60, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-3_16-6_3-3_8-1_7-1_5-1_12-3_6-10_10-1_2-26_11-2_4-1.jpg" + } + }, + { + "id": 551295, + "text": "Wtf\nA website just prevented me from opening or closing tabs in Chrome by opening a message saying it was unable to connect to the server. I couldn't even see the message as it was displayed on the small screen I didn't look at.\nIt wouldn't even let me close Chrome!", + "score": 4, + "created_time": 1493058595, + "attached_image": "", + "num_comments": 2, + "tags": [ + "stupid message", + "wtf", + "chrome" + ], + "vote_state": 0, + "edited": true, + "user_id": 418647, + "user_username": "Niddam", + "user_score": 158, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-3_3-6_8-1_7-1_5-1_12-2_6-3_2-10_4-1.jpg" + } + }, + { + "id": 106309, + "text": "CamanJS is a nice library and such but why does it's vignette function take a String as it's parameter for percentage!? It parses it by removing the '%' character and doing a parseInt()!", + "score": 4, + "created_time": 1468403756, + "attached_image": "", + "num_comments": 2, + "tags": [ + "camanjs wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 97387, + "user_username": "debug", + "user_score": 290, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-f_9-1_1-2_16-6_3-15_8-1_7-1_5-1_12-2_6-2_10-1_2-26_4-1.jpg" + } + }, + { + "id": 194549, + "text": "Manager informed me that I need to go on business trip abroad (only 2000+ kilometers, two, perhaps three flight exchanges) in three days at customer's plant for (at least) two weeks. Of course, he only hinted vague details what I should/would do there. I must admit that I'm a bit baffled but ...\n\nPlease note that, because of health issues, i didn't leave home town for more than 5 years and didn't leave country for more than 20 years. ;)\n\nOh, joy ...", + "score": 4, + "created_time": 1474483191, + "attached_image": "", + "num_comments": 4, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 132166, + "user_username": "firusvg", + "user_score": 1398, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-2_3-1_8-1_7-1_5-1_12-2_6-47_2-3_15-19_11-1_18-1_4-1.jpg" + } + }, + { + "id": 475185, + "text": "WTF OPPO? \n\nWhy assume it's \"malicious\" when I'm the one that rooted my own phone? \n\nIt's even more ridiculous when I'm prompted to SIGN IN OPPO account to maintain the root status. Who's the fucking malicious one now?\n\nThis piece of shitty pink notification permanently stuck onto my drawer and I can't seem remove it. GG fucking WP but I'm just going to live with it.", + "score": 4, + "created_time": 1489438790, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_475185_UMtgN.jpg", + "width": 562, + "height": 1000 + }, + "num_comments": 4, + "tags": [ + "wtf", + "dont wanna update", + "coloros", + "oppo", + "android", + "root" + ], + "vote_state": 0, + "edited": false, + "user_id": 399253, + "user_username": "whitekoffee", + "user_score": 1220, + "user_avatar": { + "b": "ecd276", + "i": "v-17_c-3_b-7_g-m_9-1_1-2_16-10_3-6_8-1_7-1_5-1_12-2_6-97_10-1_2-42_11-7_18-2_4-1_19-2_20-7.jpg" + } + }, + { + "id": 65890, + "text": "The issue with working in a small company is that is that talking to operations is so fucking hard sometimes. How the fuck can you dispute the validity of a if true statement. If it's true it's fucking true.", + "score": 4, + "created_time": 1465610952, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_65890_B9MAb.jpg", + "width": 451, + "height": 400 + }, + "num_comments": 1, + "tags": [ + "wtf", + "logic" + ], + "vote_state": 0, + "edited": false, + "user_id": 3563, + "user_username": "reticentroot", + "user_score": 190, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-3_3-1_8-1_7-1_5-1_12-2_6-10_10-1_2-39_15-27_11-2_18-1_4-1_19-1.jpg" + } + }, + { + "id": 550308, + "text": "Start coding a project for version 7.x, everything works in tests. Deploy it on the server and it doesn't work. Checks version and turns out it is 6.x, ask my manager why didn't he tell me before, he goes like so what, do it for all versions.\n\nI go wtf", + "score": 4, + "created_time": 1493025760, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wtf", + "mondays", + "fuck version", + "requirements" + ], + "vote_state": 0, + "edited": false, + "user_id": 538863, + "user_username": "TheLazyGoan", + "user_score": 2009, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-7_16-13_3-1_8-2_7-2_5-2_12-7_6-2_10-9_2-41_15-10_11-3_18-4_4-2_19-3_20-8_21-2.jpg" + } + }, + { + "id": 211495, + "text": "iCloud it's that thing that if you haven't a Mac you can't download your own app files either in iOS or Windows.. I had to run a VM and search for an hour for 40-50 family photos...", + "score": 3, + "created_time": 1475295368, + "attached_image": "", + "num_comments": 6, + "tags": [ + "icloud", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 206142, + "user_username": "teoval8", + "user_score": 712, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-6_3-3_8-1_7-1_5-1_12-1_6-11_10-1_2-39_15-28_4-1.jpg" + } + }, + { + "id": 577322, + "text": "Getting feedback from a client like: \"on mobile left is right and right is left\"\n\nWtf am i supposed to make out of this", + "score": 3, + "created_time": 1494233542, + "attached_image": "", + "num_comments": 1, + "tags": [ + "wtf", + "client" + ], + "vote_state": 0, + "edited": false, + "user_id": 276763, + "user_username": "daredevil", + "user_score": 1541, + "user_avatar": { + "b": "ecd276", + "i": "v-17_c-3_b-7_g-m_9-1_1-1_16-8_3-2_8-1_7-1_5-1_12-1_6-7_2-31_15-11_11-1_18-2_4-1_19-2_20-3.jpg" + } + }, + { + "id": 683190, + "text": "Release estimated in one week. \nBoss adds a whole bunch of new tickets. \nBoss gets surprised/angry that the release is not one week anymore.", + "score": 3, + "created_time": 1498788673, + "attached_image": "", + "num_comments": 2, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 666656, + "user_username": "wakazors", + "user_score": 363, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-2_16-11_3-2_8-1_7-1_5-1_12-2_6-6_10-1_2-1_15-14_18-1_4-1_19-1.jpg" + } + }, + { + "id": 442398, + "text": "Jira is asking to answer 2 short questions. Rating scale 1 to 7 what the fuck.", + "score": 3, + "created_time": 1487760523, + "attached_image": "", + "num_comments": 4, + "tags": [ + "wtf?", + "jira" + ], + "vote_state": 0, + "edited": false, + "user_id": 5167, + "user_username": "BrianK", + "user_score": 667, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-2_16-6_3-1_8-1_7-1_5-1_12-2_6-98_2-10_15-19_18-1_4-1_19-1.jpg" + } + }, + { + "id": 409096, + "text": "What is gitlab and what hapuened? I only use github", + "score": 3, + "created_time": 1486001026, + "attached_image": "", + "num_comments": 9, + "tags": [ + "toostupid4theworld", + "wtf?" + ], + "vote_state": 0, + "edited": false, + "user_id": 279630, + "user_username": "linuxer4fun", + "user_score": 7136, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-1_1-1_16-15_3-1_8-2_7-2_5-4_12-1_6-6_10-9_2-50_15-18_11-5_18-4_4-4_19-3_20-11.jpg" + } + }, + { + "id": 179843, + "text": "Firmware updates with no README at all!", + "score": 3, + "created_time": 1473699175, + "attached_image": "", + "num_comments": 1, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 90430, + "user_username": "spl0", + "user_score": 2276, + "user_avatar": { + "b": "f99a66" + } + }, + { + "id": 50127, + "text": "Coders who don't check return values should be forced to rewrite their 500GB WebApp using nothing but VI and assembler language.", + "score": 3, + "created_time": 1464950217, + "attached_image": "", + "num_comments": 2, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 50083, + "user_username": "joho", + "user_score": 15, + "user_avatar": { + "b": "7bc8a4" + } + }, + { + "id": 424864, + "text": "We all know it's true...", + "score": 3, + "created_time": 1486869688, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_424864_mB7FZ.jpg", + "width": 500, + "height": 471 + }, + "num_comments": 0, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 402643, + "user_username": "thpoul", + "user_score": 77, + "user_avatar": { + "b": "a973a2", + "i": "v-17_c-3_b-2_g-m_9-1_1-3_16-6_3-2_8-1_7-1_5-1_12-3_6-2_10-1_2-10_4-1.jpg" + } + }, + { + "id": 93948, + "text": "Got a call from a British phone number (that I missed as I was at work), turned out to be a recruitment agency that wanted me on a video interview with company_x...\n\nHow did they even get my number? (it's a Norwegian phone number!)", + "score": 3, + "created_time": 1467639478, + "attached_image": "", + "num_comments": 6, + "tags": [ + "wk7", + "wtf" + ], + "vote_state": 0, + "edited": false, + "weekly": { + "week": 7, + "topic": "Worst interview experience?", + "date": "7/4/16", + "height": 50 + }, + "user_id": 78002, + "user_username": "hareland", + "user_score": 286, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-1_3-1_8-1_7-1_5-1_12-1_6-2_2-10_4-1.jpg" + } + }, + { + "id": 339508, + "text": "You send out an email to your client with corrections on the requirements they have sent you (you know, those crappy requirements we all get), basically explaining how their application should work. They thank you for being there for them. An hour later, you get an email from the client with questions on why the development is going so slow...\n\nWow.", + "score": 3, + "created_time": 1481881290, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 317231, + "user_username": "ex0dm3nt", + "user_score": 131, + "user_avatar": { + "b": "7bc8a4" + } + }, + { + "id": 59280, + "text": "What is the point of having SElinux at all if every single product I install requires it to be turned off?", + "score": 3, + "created_time": 1465298147, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wtf security" + ], + "vote_state": 0, + "edited": false, + "user_id": 33949, + "user_username": "jawidr", + "user_score": 61, + "user_avatar": { + "b": "d55161" + } + }, + { + "id": 708779, + "text": "Just watched the first season of betas on Amazon prime - looking forward after the cliffhanger.... \n\nAfter a quick search I realized there will be not any additional seasons ๐Ÿ˜ก wtf can't describe how I am feeling right now", + "score": 3, + "created_time": 1499889163, + "attached_image": "", + "num_comments": 0, + "tags": [ + "cliffhanger", + "season", + "wtf", + "betaseries" + ], + "vote_state": 0, + "edited": false, + "user_id": 684431, + "user_username": "M0dev", + "user_score": 73, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-2_16-11_3-2_8-1_7-1_5-1_12-2_6-3_10-1_2-18_15-15_11-1_4-1.jpg" + } + }, + { + "id": 488114, + "text": "How's your code?", + "score": 3, + "created_time": 1490072655, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_488114_M3cyN.jpg", + "width": 750, + "height": 1000 + }, + "num_comments": 0, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 240271, + "user_username": "curthulhu", + "user_score": 140, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-3_3-1_8-1_7-1_5-1_12-2_6-2_10-1_2-40_15-6_11-2_4-1.jpg" + } + }, + { + "id": 208500, + "text": "Wtf? \nWho the hell puts a recaptcha on a login page? Ecomdash, that's who. \nhttps://dashboard.ecomdash.com\nAny Ecomdash devs want to explain this?", + "score": 3, + "created_time": 1475164086, + "attached_image": "", + "num_comments": 5, + "tags": [ + "ui", + "ecomdash", + "ux", + "wtf", + "uxsux" + ], + "vote_state": 0, + "edited": true, + "links": [ + { + "type": "url", + "url": "https://dashboard.ecomdash.com", + "short_url": "https://dashboard.ecomdash.com", + "title": "https://dashboard.ecomdash.com", + "start": 76, + "end": 106, + "special": 1 + } + ], + "special": true, + "user_id": 198063, + "user_username": "drewdevvin", + "user_score": 16, + "user_avatar": { + "b": "7bc8a4" + } + }, + { + "id": 409714, + "text": "Gitlab as a product is awesome, the real wtf is the processes (manual, automated or otherwise) and people supporting their cloud offering.", + "score": 3, + "created_time": 1486043686, + "attached_image": "", + "num_comments": 3, + "tags": [ + "gitlab", + "cloud", + "wtf", + "sysadmin", + "devops" + ], + "vote_state": 0, + "edited": true, + "user_id": 248222, + "user_username": "n002213f", + "user_score": 25, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-5_16-1_3-1_8-1_7-1_5-1_12-5_6-42_2-8_4-1.jpg" + } + }, + { + "id": 57064, + "text": "Some high quality rendering here, iOS / Twitter - you'd think these bugs would've been caught in alpha or beta, but noooooo...", + "score": 3, + "created_time": 1465187675, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_57064_GxwAb.jpg", + "width": 750, + "height": 472 + }, + "num_comments": 0, + "tags": [ + "wtf", + "ui" + ], + "vote_state": 0, + "edited": false, + "user_id": 55695, + "user_username": "darkhorse166", + "user_score": 3, + "user_avatar": { + "b": "7bc8a4" + } + }, + { + "id": 597617, + "text": "Think im going to cry,\n\nJust got asked to help set up visual studio, \n\nthis person is not even a junior dev,\r\ndont think thats even an excuse tbh,\n\nThankfully im outa here in a week,", + "score": 3, + "created_time": 1495030671, + "attached_image": "", + "num_comments": 5, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 83780, + "user_username": "Lenyct", + "user_score": 710, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-2_16-7_3-11_8-1_7-1_5-1_12-2_6-98_10-8_2-26_15-19_18-2_4-1_19-1.jpg" + } + }, + { + "id": 492823, + "text": "Im proud to present blessing.js (look it up in npm.js)", + "score": 2, + "created_time": 1490287950, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wtf", + "bored" + ], + "vote_state": 0, + "edited": false, + "user_id": 323612, + "user_username": "jsmrcaga", + "user_score": 286, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-2_3-1_8-1_7-1_5-1_12-2_6-2_2-10_15-18_18-1_4-1_19-1.jpg" + } + }, + { + "id": 486390, + "text": "RANTCEPTION", + "score": 2, + "created_time": 1489991033, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_486390_5dtwn.jpg", + "width": 562, + "height": 1000 + }, + "num_comments": 0, + "tags": [ + "wtf", + "rantception" + ], + "vote_state": 0, + "edited": false, + "user_id": 204414, + "user_username": "Kickflip", + "user_score": 296, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-11_16-6_3-1_8-1_7-1_5-1_12-11_6-10_10-1_2-1_11-1_18-1_4-1_19-1.jpg" + } + }, + { + "id": 564286, + "text": "Made an app for a client on both iOS and Android, published the the app on app store and google play at the same time week ago, google published the app within 2 hours, apple still though, WHY THE FUCK WE PAY THEM 99$ LER YEAR BRUH, THEY NEED A CERTIFICATE OF OWNER SHIP ON THE APP BRAND NAME SMH, CHANGE THIS AND THAT, FUCK THIS SHIT 2 WEEKS TO PUBLISH AN APP AND MY CLIENT IS YELLING WTF", + "score": 2, + "created_time": 1493660199, + "attached_image": "", + "num_comments": 5, + "tags": [ + "apple", + "app store", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 564268, + "user_username": "Chionophile", + "user_score": 2, + "user_avatar": { + "b": "7bc8a4" + } + }, + { + "id": 332230, + "text": "(Disclaimer: Normaly a Java guy)Dipping my toes into JavaScript Waters... Want to write an that generates shift plans. I'm working in the Domain Model and go wtf like every 5 minutes...", + "score": 2, + "created_time": 1481536172, + "attached_image": "", + "num_comments": 0, + "tags": [ + "java", + "javascript", + "noob", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 321630, + "user_username": "taijidude", + "user_score": 83, + "user_avatar": { + "b": "7bc8a4" + } + }, + { + "id": 69252, + "text": "\"Values for NLS_LANG differs between the system (UTF8) and the database (UTF-8)\" really? REALLY?", + "score": 2, + "created_time": 1465822373, + "attached_image": "", + "num_comments": 0, + "tags": [ + "fail", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 33949, + "user_username": "jawidr", + "user_score": 61, + "user_avatar": { + "b": "d55161" + } + }, + { + "id": 131706, + "text": "When you are trying to install pow for like 2 hours unsuccessfully previous day and today in like 5 mins finally made it....", + "score": 2, + "created_time": 1470402580, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wtf moment" + ], + "vote_state": 0, + "edited": false, + "user_id": 124237, + "user_username": "Epineas", + "user_score": 725, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-1_16-6_3-12_8-1_7-1_5-1_12-1_6-42_2-3_15-14_11-1_4-1.jpg" + } + }, + { + "id": 395910, + "text": "Typical Monday where I log in to find an alarming amount of stupid to deal with.", + "score": 2, + "created_time": 1485186334, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wtf", + "mondays" + ], + "vote_state": 0, + "edited": false, + "user_id": 204807, + "user_username": "Rabb", + "user_score": 778, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-3_16-10_3-1_8-1_7-1_5-1_12-3_6-3_2-14_15-19_11-2_18-1_4-1_19-1.jpg" + } + }, + { + "id": 742505, + "text": "Wtf devRant? Get your shit together. My phone is literally hot!!", + "score": 2, + "created_time": 1501283076, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_742505_zAGDP.jpg", + "width": 799, + "height": 557 + }, + "num_comments": 10, + "tags": [ + "wtf?", + "devrant" + ], + "vote_state": 0, + "edited": true, + "user_id": 527654, + "user_username": "codesutra", + "user_score": 40, + "user_avatar": { + "b": "7bc8a4" + } + }, + { + "id": 256870, + "text": "I was talking to someone recently who organizes a conference and told me that some people complained that the speaker list wasn't diverse enough because he had only 2 women and no racial minorities. Apparently the numerous Asian (Indian and Oriental don't count), nor the the organizer who is in a minority. Only African (specifically black) counts a d's minority nowadays. We now have a world with I finite gender identities and only 2 races. ๐Ÿ˜ฐ", + "score": 2, + "created_time": 1477618895, + "attached_image": "", + "num_comments": 6, + "tags": [ + "diversity", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 240276, + "user_username": "davejlong", + "user_score": 447, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-3_16-6_3-1_8-1_7-1_5-1_12-3_17-1_6-7_10-4_2-14_15-19_18-1_4-1_19-1.jpg" + } + }, + { + "id": 448168, + "text": "Today, I saw a car has NationalGeographic tag on it but drives just like Dakar Rally, wtf with this people :/", + "score": 2, + "created_time": 1488017627, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wtf?" + ], + "vote_state": 0, + "edited": false, + "user_id": 434287, + "user_username": "seven5689", + "user_score": 496, + "user_avatar": { + "b": "a973a2", + "i": "v-17_c-3_b-2_g-m_9-1_1-2_16-6_3-5_8-1_7-1_5-1_12-2_6-97_2-12_15-18_11-2_4-1.jpg" + } + }, + { + "id": 711245, + "text": "AHAHAHAHAHAHAHAHAHAH OMG FUCK!", + "score": 2, + "created_time": 1499977361, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_711245_RkCCy.jpg", + "width": 500, + "height": 358 + }, + "num_comments": 1, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 527845, + "user_username": "garnakolegovitc", + "user_score": 574, + "user_avatar": { + "b": "7bc8a4" + } + }, + { + "id": 94827, + "text": "Every couple of hours a certain request from our web app gets a CORS error from our server. Refresh the page and everything works perfectly. WTF...", + "score": 2, + "created_time": 1467707168, + "attached_image": "", + "num_comments": 1, + "tags": [ + "wtf", + "voodoo", + "black magic" + ], + "vote_state": 0, + "edited": false, + "user_id": 10031, + "user_username": "4sight", + "user_score": 163, + "user_avatar": { + "b": "7bc8a4" + } + }, + { + "id": 581472, + "text": "The fuck is up with r/web_programming? At first I thought all the completely uneducated questions were funny, but now it's just frustrating", + "score": 1, + "created_time": 1494378500, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wtf?" + ], + "vote_state": 0, + "edited": false, + "user_id": 2714, + "user_username": "Treighton", + "user_score": 2007, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-11_3-2_8-2_7-2_5-2_12-1_17-2_6-83_2-71_15-19_11-3_18-3_4-2_19-3_20-12.jpg" + }, + "user_dpp": 1 + }, + { + "id": 254007, + "text": "Why does everyone keep 'dying a bit inside'? And how come they are still alive?", + "score": 1, + "created_time": 1477487856, + "attached_image": "", + "num_comments": 1, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 90430, + "user_username": "spl0", + "user_score": 2276, + "user_avatar": { + "b": "f99a66" + } + }, + { + "id": 50251, + "text": "i hate stored procedures! wtf!!!", + "score": 1, + "created_time": 1464951968, + "attached_image": "", + "num_comments": 2, + "tags": [ + "wk2", + "wtf", + "storedprocedures", + "mysql" + ], + "vote_state": 0, + "edited": false, + "weekly": { + "week": 2, + "topic": "Your most ridiculous recruiter experience?", + "date": "5/30/16", + "height": 50 + }, + "user_id": 49709, + "user_username": "ryuzaki17", + "user_score": 129, + "user_avatar": { + "b": "d55161" + } + }, + { + "id": 231085, + "text": "After messing around with Laravel 5 for hours on end I finally got it working on production. Turns out everything was correct, but the random key generator command decided to literally put \"RandomString\" as the 32 character website secret... Wtf Laravel?", + "score": 1, + "created_time": 1476307989, + "attached_image": "", + "num_comments": 5, + "tags": [ + "laravel", + "php", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 217675, + "user_username": "kerrermanisNL", + "user_score": 931, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-1_1-2_16-6_3-4_8-1_7-1_5-1_12-2_6-3_2-34_18-1_4-1_19-1.jpg" + } + }, + { + "id": 582293, + "text": "Anyone knows wtf has happend with Azure DocumentDB??? Someone renamed it to CosmosDB or what?", + "score": 1, + "created_time": 1494420170, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wtf?", + "azure", + "cosmosdb" + ], + "vote_state": 0, + "edited": false, + "user_id": 539658, + "user_username": "LMtx", + "user_score": 342, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-10_3-6_8-1_7-1_5-1_12-2_6-76_2-20_4-1_19-1.jpg" + } + }, + { + "id": 208340, + "text": "I need help trying to explain to my boss. Iframes are going to load slow no matter what. Then he shows a page where a iframe loads pretty. decently well. He fucking doesn't understand that even a blank iframe can slow everything.", + "score": 1, + "created_time": 1475157474, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 34836, + "user_username": "xociety", + "user_score": 662, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-5_16-4_3-4_8-1_7-1_5-1_12-5_17-1_6-50_10-2_2-40_15-10_11-2_18-2_4-1_19-1.jpg" + } + }, + { + "id": 59601, + "text": "Just cloned a repo over 600MB in size. WTF.", + "score": 1, + "created_time": 1465309231, + "attached_image": "", + "num_comments": 5, + "tags": [ + "wtf", + "git", + "repo" + ], + "vote_state": 0, + "edited": false, + "user_id": 49772, + "user_username": "patoncrispy", + "user_score": 212, + "user_avatar": { + "b": "2a8b9d" + } + }, + { + "id": 722246, + "text": "Ok. Kill me now. WTF! This thing gets to 100% and starts all over again? Probably going to mess up Grub. I hate these updates. Restarted twice now!", + "score": 1, + "created_time": 1500414811, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_722246_13sEs.jpg", + "width": 800, + "height": 600 + }, + "num_comments": 4, + "tags": [ + "wtf", + "windows", + "win10" + ], + "vote_state": 0, + "edited": false, + "user_id": 17538, + "user_username": "Yawks", + "user_score": 213, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-1_1-5_16-1_3-12_8-1_7-1_5-1_12-5_6-82_10-1_2-10_4-1.jpg" + } + }, + { + "id": 183390, + "text": "When you try and compile for iOS using TACO and you get an error message about an android icon file missing *wears the wtf hat*", + "score": 1, + "created_time": 1473884392, + "attached_image": "", + "num_comments": 0, + "tags": [ + "ios", + "taco", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 183277, + "user_username": "navster", + "user_score": 700, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-6_16-5_3-1_8-1_7-1_5-1_12-6_6-54_2-26_15-18_4-1.jpg" + } + }, + { + "id": 396961, + "text": "Yesterday when I came into the office my laptop (in a workstation) could not connect to the internet because of bad ip config. The auto configuration just didn't work...\r\nSolved the problem by opening the laptop-lid for about a second, just wtf", + "score": 1, + "created_time": 1485244451, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wtf", + "wifi", + "ip" + ], + "vote_state": 0, + "edited": false, + "user_id": 340051, + "user_username": "moars42", + "user_score": 631, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-1_16-11_3-6_8-1_7-1_5-1_12-1_6-2_10-1_2-5_15-14_18-1_4-1_19-1.jpg" + } + }, + { + "id": 740185, + "text": "I just watched Bahubali, recomended by my Indian Colleague (wth about so many indians on programming, nice coding btw). Its not a big deal (long and boring most of times) but the WAR SCENE! OMG! INSANE hahahha loved it", + "score": 1, + "created_time": 1501180352, + "attached_image": "", + "num_comments": 0, + "tags": [ + "bahubali; indian; wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 726572, + "user_username": "LinuxPauling", + "user_score": 109, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-1_16-4_3-1_8-1_7-1_5-1_12-1_6-1_10-1_2-36_15-13_4-1.jpg" + } + }, + { + "id": 346415, + "text": "WTF?! Why is devRant taking up more of my battery than the screen? It shouldn't be like this:", + "score": 1, + "created_time": 1482267423, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_346415_caDEi.jpg", + "width": 562, + "height": 1000 + }, + "num_comments": 2, + "tags": [ + "wtf", + "devrant", + "android", + "battery life", + "seriously wtf?" + ], + "vote_state": 0, + "edited": false, + "user_id": 345033, + "user_username": "psudo", + "user_score": 406, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-2_16-1_3-6_8-1_7-1_5-1_12-2_6-2_2-30_15-14_18-1_4-1_19-1.jpg" + } + }, + { + "id": 274831, + "text": "When your government's trusted CA doesn't trust its own certificate.", + "score": 0, + "created_time": 1478503183, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_274831_66w3R.jpg", + "width": 799, + "height": 449 + }, + "num_comments": 1, + "tags": [ + "ssl", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 266302, + "user_username": "varevarao", + "user_score": 107, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-4_16-1_3-2_8-1_7-1_5-1_12-4_6-2_10-1_2-38_15-10_4-1.jpg" + } + }, + { + "id": 566454, + "text": "So they built a ionic app for compatibility between android and iOS now they are gonna keep ionic for android and build from scratch with react native for iOS... Am I missing something? Is that right? Am I sleeping?", + "score": 0, + "created_time": 1493762721, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 26258, + "user_username": "azous", + "user_score": 5762, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-3_16-6_3-1_8-1_7-1_5-4_12-3_6-52_10-5_2-50_11-4_18-4_4-4_19-3_20-5_21-2.jpg" + } + }, + { + "id": 48303, + "text": "That awkward moment when you see an ad for an app similar to devRant. ๐Ÿ˜ฆ", + "score": 0, + "created_time": 1464907551, + "attached_image": "", + "num_comments": 2, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 34751, + "user_username": "jdmkaan", + "user_score": 2156, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-2_16-7_3-8_8-2_7-2_5-2_12-2_6-3_10-2_2-39_11-2_4-2.jpg" + } + }, + { + "id": 244109, + "text": "My gitkraken is being funny... when I press commit after I've staged files and wrote the message the commit button just does nothing I have to restart the program and write the message and commit again but files are staged already\n\nWtf?", + "score": 0, + "created_time": 1476955312, + "attached_image": "", + "num_comments": 0, + "tags": [ + "rant", + "fml", + "git", + "commit", + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 217394, + "user_username": "thenoobdev", + "user_score": 181, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-3_16-6_3-4_8-1_7-1_5-1_12-3_6-4_2-1_11-2_18-1_4-1_19-1.jpg" + } + }, + { + "id": 215439, + "text": "That moment when you get to work in the morning, realize that you can't connect to the network, plug in a monitor and keyboard to the server, only to find that my goddamn dhcpd went missing for some reason..", + "score": 0, + "created_time": 1475514389, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 158295, + "user_username": "codelis", + "user_score": 1152, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-3_3-2_8-2_7-2_5-1_12-2_6-3_10-5_2-12_11-1_4-1.jpg" + } + }, + { + "id": 475133, + "text": "Whiskey Tango Foxtrot.", + "score": 0, + "created_time": 1489436124, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_475133_5hgL3.jpg", + "width": 500, + "height": 471 + }, + "num_comments": 0, + "tags": [ + "wtf", + "testing" + ], + "vote_state": 0, + "edited": true, + "user_id": 312958, + "user_username": "arriagar", + "user_score": 228, + "user_avatar": { + "b": "7bc8a4" + } + }, + { + "id": 232516, + "text": "String displayTextEatDay = cheatDay == day ? \"Your cheating has begun! Enjoy your day and make sure you're getting back soon!\" : Values.isCheatMode ? Constants.CHEATDAY_PREFIX + \"\\n\\n\" + Constants.EATDAY_TEXT : Constants.EATDAY_TEXT;", + "score": 0, + "created_time": 1476385667, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wtf plzno" + ], + "vote_state": 0, + "edited": false, + "user_id": 96670, + "user_username": "iceman", + "user_score": 636, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-1_3-10_8-1_7-1_5-1_12-1_6-91_10-1_2-13_15-1_18-2_4-1_19-1.jpg" + } + }, + { + "id": 111230, + "text": "Government website only open certain hours of the day? WTF?", + "score": 0, + "created_time": 1468707468, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_111230_qVrHA.jpg", + "width": 765, + "height": 283 + }, + "num_comments": 0, + "tags": [ + "wtf", + "government", + "irs" + ], + "vote_state": 0, + "edited": false, + "user_id": 108133, + "user_username": "spo81ty", + "user_score": 34, + "user_avatar": { + "b": "a973a2" + } + }, + { + "id": 351629, + "text": "Haven't seen this since windows 7.\n\nNow it has a sad face and a QR?", + "score": 0, + "created_time": 1482591229, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_351629_LrJCe.jpg", + "width": 799, + "height": 449 + }, + "num_comments": 1, + "tags": [ + "bsod wtf" + ], + "vote_state": 0, + "edited": false, + "user_id": 100693, + "user_username": "Pelayin5", + "user_score": 281, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-3_16-6_3-1_8-1_7-1_5-1_12-3_6-2_10-2_2-40_15-18_4-1_19-1.jpg" + } + } + ] +} \ No newline at end of file From be54068b594b8f835d4db75f40ad1c50d0f00b06 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Wed, 6 Sep 2017 11:02:07 +0200 Subject: [PATCH 38/65] Add surprise method --- .../java/com/scorpiac/javarant/DevRant.java | 15 ++++++- .../java/com/scorpiac/javarant/Endpoint.java | 3 +- .../responses/CommentedRantResponse.java | 40 +++++++++++++++++++ .../javarant/responses/RantResponse.java | 33 ++------------- .../java/com/scorpiac/javarant/DevRantIT.java | 29 ++++++++++++++ src/test/resources/rant-surprise.json | 24 +++++++++++ 6 files changed, 112 insertions(+), 32 deletions(-) create mode 100644 src/main/java/com/scorpiac/javarant/responses/CommentedRantResponse.java create mode 100644 src/test/resources/rant-surprise.json diff --git a/src/main/java/com/scorpiac/javarant/DevRant.java b/src/main/java/com/scorpiac/javarant/DevRant.java index 13d7b0a..cfd2e6a 100644 --- a/src/main/java/com/scorpiac/javarant/DevRant.java +++ b/src/main/java/com/scorpiac/javarant/DevRant.java @@ -2,6 +2,7 @@ import com.google.inject.Guice; import com.google.inject.Injector; +import com.scorpiac.javarant.responses.CommentedRantResponse; import com.scorpiac.javarant.responses.RantResponse; import com.scorpiac.javarant.responses.UserIdResponse; import com.scorpiac.javarant.responses.UserResponse; @@ -49,8 +50,8 @@ public DevRantFeed getFeed() { * @return The rant. */ public Optional getRant(int id) { - return requestHandler.get(Endpoint.RANTS.toString() + '/' + id, RantResponse.class) - .flatMap(RantResponse::getRant); + return requestHandler.get(Endpoint.RANTS.toString() + '/' + id, CommentedRantResponse.class) + .flatMap(CommentedRantResponse::getRant); } /** @@ -80,6 +81,16 @@ public Optional getUser(int id) { return user; } + /** + * Get a random rant. + * + * @return A random rant. + */ + public Optional getSurprise() { + return requestHandler.get(Endpoint.SURPRISE, RantResponse.class) + .flatMap(RantResponse::getRant); + } + /** * Log out of devRant. */ diff --git a/src/main/java/com/scorpiac/javarant/Endpoint.java b/src/main/java/com/scorpiac/javarant/Endpoint.java index 51ab764..5930081 100644 --- a/src/main/java/com/scorpiac/javarant/Endpoint.java +++ b/src/main/java/com/scorpiac/javarant/Endpoint.java @@ -6,7 +6,8 @@ public enum Endpoint { USER_ID(API, "get-user-id"), USERS(API, "users"), RANTS(API_DEVRANT, "rants"), - SEARCH(API_DEVRANT, "search"); + SEARCH(API_DEVRANT, "search"), + SURPRISE(RANTS, "surprise"); private final String endpoint; diff --git a/src/main/java/com/scorpiac/javarant/responses/CommentedRantResponse.java b/src/main/java/com/scorpiac/javarant/responses/CommentedRantResponse.java new file mode 100644 index 0000000..a1309b0 --- /dev/null +++ b/src/main/java/com/scorpiac/javarant/responses/CommentedRantResponse.java @@ -0,0 +1,40 @@ +package com.scorpiac.javarant.responses; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.scorpiac.javarant.Comment; +import com.scorpiac.javarant.CommentedRant; + +import java.lang.reflect.Field; +import java.util.List; +import java.util.Optional; + +public class CommentedRantResponse extends Response { + @JsonProperty + private CommentedRant rant; + @JsonProperty + private List comments; + + public Optional getRant() { + if (!isSuccess()) { + return Optional.empty(); + } + + // Get the comments field. + Field commentsField; + try { + commentsField = rant.getClass().getDeclaredField("comments"); + } catch (NoSuchFieldException e) { + throw new IllegalStateException(e); // TODO + } + + // Set the comments field. + commentsField.setAccessible(true); + try { + commentsField.set(rant, comments); + } catch (IllegalAccessException e) { + throw new IllegalStateException(e); // TODO + } + + return Optional.of(rant); + } +} diff --git a/src/main/java/com/scorpiac/javarant/responses/RantResponse.java b/src/main/java/com/scorpiac/javarant/responses/RantResponse.java index cd28e8a..67ba0c1 100644 --- a/src/main/java/com/scorpiac/javarant/responses/RantResponse.java +++ b/src/main/java/com/scorpiac/javarant/responses/RantResponse.java @@ -1,40 +1,15 @@ package com.scorpiac.javarant.responses; import com.fasterxml.jackson.annotation.JsonProperty; -import com.scorpiac.javarant.Comment; -import com.scorpiac.javarant.CommentedRant; +import com.scorpiac.javarant.Rant; -import java.lang.reflect.Field; -import java.util.List; import java.util.Optional; public class RantResponse extends Response { @JsonProperty - private CommentedRant rant; - @JsonProperty - private List comments; - - public Optional getRant() { - if (!isSuccess()) { - return Optional.empty(); - } - - // Get the comments field. - Field commentsField; - try { - commentsField = rant.getClass().getDeclaredField("comments"); - } catch (NoSuchFieldException e) { - throw new IllegalStateException(e); // TODO - } - - // Set the comments field. - commentsField.setAccessible(true); - try { - commentsField.set(rant, comments); - } catch (IllegalAccessException e) { - throw new IllegalStateException(e); // TODO - } + private Rant rant; - return Optional.of(rant); + public Optional getRant() { + return Optional.ofNullable(rant); } } diff --git a/src/test/java/com/scorpiac/javarant/DevRantIT.java b/src/test/java/com/scorpiac/javarant/DevRantIT.java index 2268197..49680fe 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantIT.java @@ -75,4 +75,33 @@ public void testGetUserByUsername() throws IOException { 0 ); } + + @Test + public void testGetSurprise() throws IOException { + server.stubFor(stubResponse( + get(urlPathEqualTo(Endpoint.SURPRISE.toString())), + "/rant-surprise.json" + )); + + Rant rant = devRant.getSurprise().get(); + + validateRant(rant, + 26356, + "Life of a software engineer.", + 91, + 1 + ); + + validateMinimalUser(rant.getUser(), + 18401, + "nhpace", + 218 + ); + + validateImage(rant.getImage(), + "https://img.devrant.io/devrant/rant/r_26356_N2S4f.jpg", + 504, + 381 + ); + } } diff --git a/src/test/resources/rant-surprise.json b/src/test/resources/rant-surprise.json new file mode 100644 index 0000000..541c917 --- /dev/null +++ b/src/test/resources/rant-surprise.json @@ -0,0 +1,24 @@ +{ + "success": true, + "rant": { + "id": 26356, + "text": "Life of a software engineer.", + "score": 91, + "created_time": 1463361116, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_26356_N2S4f.jpg", + "width": 504, + "height": 381 + }, + "num_comments": 1, + "tags": [], + "vote_state": 0, + "edited": false, + "user_id": 18401, + "user_username": "nhpace", + "user_score": 218, + "user_avatar": { + "b": "2a8b9d" + } + } +} \ No newline at end of file From 3d020dcd5072387a377ee25d7e1a7fccefc4b138 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Wed, 6 Sep 2017 11:20:37 +0200 Subject: [PATCH 39/65] Simplify responses, add user id invalid test --- .../java/com/scorpiac/javarant/DevRant.java | 6 ++--- .../responses/CommentedRantResponse.java | 9 ++----- .../javarant/responses/RantResponse.java | 6 ++--- .../scorpiac/javarant/responses/Response.java | 2 +- .../javarant/responses/UserResponse.java | 6 ++--- .../javarant/services/RequestHandler.java | 24 ++++++++++++------- .../java/com/scorpiac/javarant/DevRantIT.java | 11 +++++++++ src/test/resources/user-id-invalid.json | 3 +++ 8 files changed, 40 insertions(+), 27 deletions(-) create mode 100644 src/test/resources/user-id-invalid.json diff --git a/src/main/java/com/scorpiac/javarant/DevRant.java b/src/main/java/com/scorpiac/javarant/DevRant.java index cfd2e6a..b33b00a 100644 --- a/src/main/java/com/scorpiac/javarant/DevRant.java +++ b/src/main/java/com/scorpiac/javarant/DevRant.java @@ -51,7 +51,7 @@ public DevRantFeed getFeed() { */ public Optional getRant(int id) { return requestHandler.get(Endpoint.RANTS.toString() + '/' + id, CommentedRantResponse.class) - .flatMap(CommentedRantResponse::getRant); + .map(CommentedRantResponse::getRant); } /** @@ -73,7 +73,7 @@ public Optional getUser(String username) { */ public Optional getUser(int id) { Optional user = requestHandler.get(Endpoint.USERS.toString() + '/' + id, UserResponse.class) - .flatMap(UserResponse::getUser); + .map(UserResponse::getUser); // Set the id, as that is not part of the response. user.ifPresent(u -> u.setId(id)); @@ -88,7 +88,7 @@ public Optional getUser(int id) { */ public Optional getSurprise() { return requestHandler.get(Endpoint.SURPRISE, RantResponse.class) - .flatMap(RantResponse::getRant); + .map(RantResponse::getRant); } /** diff --git a/src/main/java/com/scorpiac/javarant/responses/CommentedRantResponse.java b/src/main/java/com/scorpiac/javarant/responses/CommentedRantResponse.java index a1309b0..90b0bc5 100644 --- a/src/main/java/com/scorpiac/javarant/responses/CommentedRantResponse.java +++ b/src/main/java/com/scorpiac/javarant/responses/CommentedRantResponse.java @@ -6,7 +6,6 @@ import java.lang.reflect.Field; import java.util.List; -import java.util.Optional; public class CommentedRantResponse extends Response { @JsonProperty @@ -14,11 +13,7 @@ public class CommentedRantResponse extends Response { @JsonProperty private List comments; - public Optional getRant() { - if (!isSuccess()) { - return Optional.empty(); - } - + public CommentedRant getRant() { // Get the comments field. Field commentsField; try { @@ -35,6 +30,6 @@ public Optional getRant() { throw new IllegalStateException(e); // TODO } - return Optional.of(rant); + return rant; } } diff --git a/src/main/java/com/scorpiac/javarant/responses/RantResponse.java b/src/main/java/com/scorpiac/javarant/responses/RantResponse.java index 67ba0c1..730ce7b 100644 --- a/src/main/java/com/scorpiac/javarant/responses/RantResponse.java +++ b/src/main/java/com/scorpiac/javarant/responses/RantResponse.java @@ -3,13 +3,11 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.scorpiac.javarant.Rant; -import java.util.Optional; - public class RantResponse extends Response { @JsonProperty private Rant rant; - public Optional getRant() { - return Optional.ofNullable(rant); + public Rant getRant() { + return rant; } } diff --git a/src/main/java/com/scorpiac/javarant/responses/Response.java b/src/main/java/com/scorpiac/javarant/responses/Response.java index 8244830..9f30f6d 100644 --- a/src/main/java/com/scorpiac/javarant/responses/Response.java +++ b/src/main/java/com/scorpiac/javarant/responses/Response.java @@ -2,7 +2,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; -class Response { +public class Response { @JsonProperty private boolean success; @JsonProperty diff --git a/src/main/java/com/scorpiac/javarant/responses/UserResponse.java b/src/main/java/com/scorpiac/javarant/responses/UserResponse.java index 4ac1444..6f2d281 100644 --- a/src/main/java/com/scorpiac/javarant/responses/UserResponse.java +++ b/src/main/java/com/scorpiac/javarant/responses/UserResponse.java @@ -3,13 +3,11 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.scorpiac.javarant.User; -import java.util.Optional; - public class UserResponse extends Response { @JsonProperty private User profile; - public Optional getUser() { - return Optional.ofNullable(profile); + public User getUser() { + return profile; } } diff --git a/src/main/java/com/scorpiac/javarant/services/RequestHandler.java b/src/main/java/com/scorpiac/javarant/services/RequestHandler.java index 30c9f22..95a998d 100644 --- a/src/main/java/com/scorpiac/javarant/services/RequestHandler.java +++ b/src/main/java/com/scorpiac/javarant/services/RequestHandler.java @@ -1,6 +1,7 @@ package com.scorpiac.javarant.services; import com.scorpiac.javarant.Endpoint; +import com.scorpiac.javarant.responses.Response; import org.apache.http.NameValuePair; import org.apache.http.client.fluent.Request; import org.apache.http.client.utils.URIBuilder; @@ -35,19 +36,19 @@ public RequestHandler(ObjectMapperResponseHandlerFactory responseHandlerFactory) this.responseHandlerFactory = responseHandlerFactory; } - public Optional get(Endpoint endpoint, Class clazz, NameValuePair... params) { + public Optional get(Endpoint endpoint, Class clazz, NameValuePair... params) { return get(endpoint.toString(), clazz, params); } - public Optional get(String endpoint, Class clazz, NameValuePair... params) { + public Optional get(String endpoint, Class clazz, NameValuePair... params) { return handleRequest(buildRequest(endpoint, Request::Get, params), clazz); } - public Optional post(Endpoint endpoint, Class clazz, NameValuePair... params) { + public Optional post(Endpoint endpoint, Class clazz, NameValuePair... params) { return post(endpoint, clazz, params); } - public Optional post(String endpoint, Class clazz, NameValuePair... params) { + public Optional post(String endpoint, Class clazz, NameValuePair... params) { return handleRequest(buildRequest(endpoint, Request::Post, params), clazz); } @@ -90,14 +91,21 @@ URI resolve(String endpoint) { * @param The type of the class to map the response to. * @return The mapped response. */ - private Optional handleRequest(Request request, Class clazz) { - // Execute the request and handle the response. + private Optional handleRequest(Request request, Class clazz) { + T response; try { - return Optional.of(request.execute().handleResponse(responseHandlerFactory.getResponseHandler(clazz))); + response = request.execute().handleResponse(responseHandlerFactory.getResponseHandler(clazz)); } catch (IOException e) { LOGGER.error("Failed to execute request.", e); + return Optional.empty(); } - return Optional.empty(); + + // Check if there was an error. + if (response.getError() != null) { + LOGGER.error("A devRant API error occurred: " + response.getError()); + } + + return response.isSuccess() ? Optional.of(response) : Optional.empty(); } /** diff --git a/src/test/java/com/scorpiac/javarant/DevRantIT.java b/src/test/java/com/scorpiac/javarant/DevRantIT.java index 49680fe..4310792 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantIT.java @@ -76,6 +76,17 @@ public void testGetUserByUsername() throws IOException { ); } + @Test + public void testGetUserByUsernameInvalid() throws IOException { + server.stubFor(stubResponse( + get(urlPathEqualTo(Endpoint.USER_ID.toString())) + .withQueryParam("username", equalTo("invalid")), + "/user-id-invalid.json" + )); + + assertFalse(devRant.getUser("invalid").isPresent()); + } + @Test public void testGetSurprise() throws IOException { server.stubFor(stubResponse( diff --git a/src/test/resources/user-id-invalid.json b/src/test/resources/user-id-invalid.json new file mode 100644 index 0000000..0a1450b --- /dev/null +++ b/src/test/resources/user-id-invalid.json @@ -0,0 +1,3 @@ +{ + "success": false +} \ No newline at end of file From 5f3f9a6977be5aacad10720b9da0c079957fb655 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Wed, 6 Sep 2017 11:24:04 +0200 Subject: [PATCH 40/65] Add test for invalid user id --- src/test/java/com/scorpiac/javarant/DevRantIT.java | 10 ++++++++++ src/test/resources/user-invalid.json | 4 ++++ 2 files changed, 14 insertions(+) create mode 100644 src/test/resources/user-invalid.json diff --git a/src/test/java/com/scorpiac/javarant/DevRantIT.java b/src/test/java/com/scorpiac/javarant/DevRantIT.java index 4310792..68453f5 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantIT.java @@ -87,6 +87,16 @@ public void testGetUserByUsernameInvalid() throws IOException { assertFalse(devRant.getUser("invalid").isPresent()); } + @Test + public void testGetUserInvalid() throws IOException { + server.stubFor(stubResponse( + get(urlPathEqualTo(Endpoint.USERS.toString() + "/123")), + "/user-id-invalid.json" + )); + + assertFalse(devRant.getUser(123).isPresent()); + } + @Test public void testGetSurprise() throws IOException { server.stubFor(stubResponse( diff --git a/src/test/resources/user-invalid.json b/src/test/resources/user-invalid.json new file mode 100644 index 0000000..a65417c --- /dev/null +++ b/src/test/resources/user-invalid.json @@ -0,0 +1,4 @@ +{ + "success": false, + "error": "Invalid user specified in path." +} \ No newline at end of file From f5782acdb011d65c2b837920bdef9dec7b17cc40 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Fri, 8 Sep 2017 09:18:29 +0200 Subject: [PATCH 41/65] Let DevRantFeed use DevRant --- src/main/java/com/scorpiac/javarant/DevRant.java | 7 +++++-- .../java/com/scorpiac/javarant/DevRantFeed.java | 13 +++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/scorpiac/javarant/DevRant.java b/src/main/java/com/scorpiac/javarant/DevRant.java index b33b00a..bab5f58 100644 --- a/src/main/java/com/scorpiac/javarant/DevRant.java +++ b/src/main/java/com/scorpiac/javarant/DevRant.java @@ -29,14 +29,17 @@ public class DevRant { } public DevRant() { - feed = new DevRantFeed(); INJECTOR.injectMembers(this); + feed = new DevRantFeed(this); } @Inject void setRequestHandler(RequestHandler requestHandler) { this.requestHandler = requestHandler; - feed.setRequestHandler(requestHandler); + } + + RequestHandler getRequestHandler() { + return requestHandler; } public DevRantFeed getFeed() { diff --git a/src/main/java/com/scorpiac/javarant/DevRantFeed.java b/src/main/java/com/scorpiac/javarant/DevRantFeed.java index 6d23393..332711f 100644 --- a/src/main/java/com/scorpiac/javarant/DevRantFeed.java +++ b/src/main/java/com/scorpiac/javarant/DevRantFeed.java @@ -2,19 +2,16 @@ import com.scorpiac.javarant.responses.RantFeedResponse; import com.scorpiac.javarant.responses.ResultsFeedResponse; -import com.scorpiac.javarant.services.RequestHandler; import org.apache.http.message.BasicNameValuePair; -import javax.inject.Inject; import java.util.List; import java.util.Optional; public class DevRantFeed { - private RequestHandler requestHandler; + private final DevRant devRant; - @Inject - void setRequestHandler(RequestHandler requestHandler) { - this.requestHandler = requestHandler; + DevRantFeed(DevRant devRant) { + this.devRant = devRant; } public Optional> getRants(Sort sort) { @@ -34,7 +31,7 @@ public Optional> getRants(Sort sort, int limit) { * @return Rants from the feed. */ public Optional> getRants(Sort sort, int limit, int skip) { - return requestHandler.get(Endpoint.RANTS, RantFeedResponse.class, + return devRant.getRequestHandler().get(Endpoint.RANTS, RantFeedResponse.class, new BasicNameValuePair("sort", sort.toString()), new BasicNameValuePair("limit", String.valueOf(limit)), new BasicNameValuePair("skip", String.valueOf(skip)) @@ -49,7 +46,7 @@ public Optional> getRants(Sort sort, int limit, int skip) { * @return The search results. */ public Optional> search(String term) { - return requestHandler.get(Endpoint.SEARCH, ResultsFeedResponse.class, + return devRant.getRequestHandler().get(Endpoint.SEARCH, ResultsFeedResponse.class, new BasicNameValuePair("term", term) ) .map(ResultsFeedResponse::getResults); From 398bc5778aab4189a79d10a539184c5af977c47c Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Fri, 8 Sep 2017 09:29:58 +0200 Subject: [PATCH 42/65] Add test for request with server error --- src/test/java/com/scorpiac/javarant/DevRantIT.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/scorpiac/javarant/DevRantIT.java b/src/test/java/com/scorpiac/javarant/DevRantIT.java index 68453f5..1d19192 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantIT.java @@ -36,7 +36,7 @@ public void testGetRant() throws IOException { } @Test - public void testGetInvalidRant() throws IOException { + public void testGetRantInvalid() throws IOException { server.stubFor(stubResponse( get(urlPathEqualTo(Endpoint.RANTS.toString() + "/0")), "/rant-invalid.json" @@ -45,6 +45,16 @@ public void testGetInvalidRant() throws IOException { assertFalse(devRant.getRant(0).isPresent()); } + @Test + public void testGetRantServerError() throws IOException { + server.stubFor( + get(urlPathEqualTo(Endpoint.RANTS.toString() + "/123456")) + .willReturn(serverError().withBody("An unknown error occurred.")) + ); + + assertFalse(devRant.getRant(123456).isPresent()); + } + @Test public void testGetUserByUsername() throws IOException { server.stubFor(stubResponse( From 3654a62f080b0f12944a6e4fe737c93e6ca49152 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Fri, 8 Sep 2017 10:24:53 +0200 Subject: [PATCH 43/65] Add login --- src/main/java/com/scorpiac/javarant/Auth.java | 13 ++++--- .../java/com/scorpiac/javarant/DevRant.java | 34 ++++++++++++++++--- .../com/scorpiac/javarant/DevRantFeed.java | 11 +++--- .../java/com/scorpiac/javarant/Endpoint.java | 9 +++-- .../javarant/responses/AuthResponse.java | 13 +++++++ 5 files changed, 62 insertions(+), 18 deletions(-) create mode 100644 src/main/java/com/scorpiac/javarant/responses/AuthResponse.java diff --git a/src/main/java/com/scorpiac/javarant/Auth.java b/src/main/java/com/scorpiac/javarant/Auth.java index 119c6e8..18aad84 100644 --- a/src/main/java/com/scorpiac/javarant/Auth.java +++ b/src/main/java/com/scorpiac/javarant/Auth.java @@ -1,16 +1,15 @@ package com.scorpiac.javarant; -class Auth { +import com.fasterxml.jackson.annotation.JsonProperty; + +public class Auth { + @JsonProperty private String id; + @JsonProperty private String key; + @JsonProperty("user_id") private String userId; - Auth(String id, String key, String userId) { - this.id = id; - this.key = key; - this.userId = userId; - } - String getId() { return id; } diff --git a/src/main/java/com/scorpiac/javarant/DevRant.java b/src/main/java/com/scorpiac/javarant/DevRant.java index bab5f58..d9d9553 100644 --- a/src/main/java/com/scorpiac/javarant/DevRant.java +++ b/src/main/java/com/scorpiac/javarant/DevRant.java @@ -2,10 +2,8 @@ import com.google.inject.Guice; import com.google.inject.Injector; -import com.scorpiac.javarant.responses.CommentedRantResponse; -import com.scorpiac.javarant.responses.RantResponse; -import com.scorpiac.javarant.responses.UserIdResponse; -import com.scorpiac.javarant.responses.UserResponse; +import com.scorpiac.javarant.exceptions.AuthenticationException; +import com.scorpiac.javarant.responses.*; import com.scorpiac.javarant.services.RequestHandler; import org.apache.http.message.BasicNameValuePair; @@ -94,6 +92,34 @@ public Optional getSurprise() { .map(RantResponse::getRant); } + /** + * Log in to devRant. + * Note that this method will clear the characters from the password array. + * + * @param username The username. + * @param password The password. + * @throws AuthenticationException If the credentials are invalid. + */ + public void login(String username, char[] password) throws AuthenticationException { + Optional response = requestHandler.post(Endpoint.AUTH_TOKEN, AuthResponse.class, + new BasicNameValuePair("username", username), + new BasicNameValuePair("password", String.valueOf(password)) + ); + + // Clear the password. + for (int i = 0; i < password.length; i++) { + password[i] = 0; + } + + // Check for success. + AuthResponse authResponse = response.orElseThrow(AuthenticationException::new); + if (!authResponse.isSuccess()) { + throw new AuthenticationException(); + } + + auth = authResponse.getAuth(); + } + /** * Log out of devRant. */ diff --git a/src/main/java/com/scorpiac/javarant/DevRantFeed.java b/src/main/java/com/scorpiac/javarant/DevRantFeed.java index 332711f..1412eb6 100644 --- a/src/main/java/com/scorpiac/javarant/DevRantFeed.java +++ b/src/main/java/com/scorpiac/javarant/DevRantFeed.java @@ -14,10 +14,13 @@ public class DevRantFeed { this.devRant = devRant; } - public Optional> getRants(Sort sort) { - return getRants(sort, 20, 0); - } - + /** + * Get rants from the feed. + * + * @param sort How to sort the feed. + * @param limit How many rants to get. + * @return Rants from the feed. + */ public Optional> getRants(Sort sort, int limit) { return getRants(sort, limit, 0); } diff --git a/src/main/java/com/scorpiac/javarant/Endpoint.java b/src/main/java/com/scorpiac/javarant/Endpoint.java index 5930081..e6732a1 100644 --- a/src/main/java/com/scorpiac/javarant/Endpoint.java +++ b/src/main/java/com/scorpiac/javarant/Endpoint.java @@ -3,11 +3,14 @@ public enum Endpoint { API("/api"), API_DEVRANT(API, "devrant"), - USER_ID(API, "get-user-id"), - USERS(API, "users"), + // Rants. RANTS(API_DEVRANT, "rants"), SEARCH(API_DEVRANT, "search"), - SURPRISE(RANTS, "surprise"); + SURPRISE(RANTS, "surprise"), + // Users. + USER_ID(API, "get-user-id"), + USERS(API, "users"), + AUTH_TOKEN(USERS, "auth-token"); private final String endpoint; diff --git a/src/main/java/com/scorpiac/javarant/responses/AuthResponse.java b/src/main/java/com/scorpiac/javarant/responses/AuthResponse.java new file mode 100644 index 0000000..872142c --- /dev/null +++ b/src/main/java/com/scorpiac/javarant/responses/AuthResponse.java @@ -0,0 +1,13 @@ +package com.scorpiac.javarant.responses; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.scorpiac.javarant.Auth; + +public class AuthResponse extends Response { + @JsonProperty("auth_token") + private Auth auth; + + public Auth getAuth() { + return auth; + } +} From 20cc741bc2625053921ed2fd31c37021046b3059 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Sat, 9 Sep 2017 10:12:55 +0200 Subject: [PATCH 44/65] Separate authenticated calls to DevRantAuth --- .../java/com/scorpiac/javarant/DevRant.java | 46 +++++++++++++------ .../com/scorpiac/javarant/DevRantAuth.java | 9 ++++ .../exceptions/AuthenticationException.java | 7 --- .../com/scorpiac/javarant/DevRantTest.java | 5 ++ 4 files changed, 46 insertions(+), 21 deletions(-) create mode 100644 src/main/java/com/scorpiac/javarant/DevRantAuth.java delete mode 100644 src/main/java/com/scorpiac/javarant/exceptions/AuthenticationException.java diff --git a/src/main/java/com/scorpiac/javarant/DevRant.java b/src/main/java/com/scorpiac/javarant/DevRant.java index d9d9553..4548799 100644 --- a/src/main/java/com/scorpiac/javarant/DevRant.java +++ b/src/main/java/com/scorpiac/javarant/DevRant.java @@ -2,7 +2,6 @@ import com.google.inject.Guice; import com.google.inject.Injector; -import com.scorpiac.javarant.exceptions.AuthenticationException; import com.scorpiac.javarant.responses.*; import com.scorpiac.javarant.services.RequestHandler; import org.apache.http.message.BasicNameValuePair; @@ -17,7 +16,8 @@ public class DevRant { static final String RANT_URL = "/rants"; static final String COLLAB_URL = "/collabs"; - private final DevRantFeed feed; + private final DevRantFeed devRantFeed; + private final DevRantAuth devRantAuth; private RequestHandler requestHandler; private Auth auth; @@ -28,7 +28,8 @@ public class DevRant { public DevRant() { INJECTOR.injectMembers(this); - feed = new DevRantFeed(this); + devRantFeed = new DevRantFeed(this); + devRantAuth = new DevRantAuth(this); } @Inject @@ -40,8 +41,28 @@ RequestHandler getRequestHandler() { return requestHandler; } + /** + * Access the devRant feed. + * + * @return A devRant feed class. + */ public DevRantFeed getFeed() { - return feed; + return devRantFeed; + } + + /** + * Access the user's authenticated devRant. + * Trying to access this when the user is not logged in will throw an {@link IllegalStateException}. + * + * @return An authenticated devRant class. + */ + public DevRantAuth getAuth() { + // Check if the user is logged in. + if (!isLoggedIn()) { + throw new IllegalStateException("The user must be logged in to access the DevRantAuth."); + } + + return devRantAuth; } /** @@ -93,14 +114,16 @@ public Optional getSurprise() { } /** - * Log in to devRant. + * Log in to devRant. When a user is already logged in, they will be logged out first. * Note that this method will clear the characters from the password array. * * @param username The username. * @param password The password. - * @throws AuthenticationException If the credentials are invalid. + * @return {@code true} if the user was successfully logged in, or {@code false} otherwise. */ - public void login(String username, char[] password) throws AuthenticationException { + public boolean login(String username, char[] password) { + logout(); + Optional response = requestHandler.post(Endpoint.AUTH_TOKEN, AuthResponse.class, new BasicNameValuePair("username", username), new BasicNameValuePair("password", String.valueOf(password)) @@ -111,13 +134,8 @@ public void login(String username, char[] password) throws AuthenticationExcepti password[i] = 0; } - // Check for success. - AuthResponse authResponse = response.orElseThrow(AuthenticationException::new); - if (!authResponse.isSuccess()) { - throw new AuthenticationException(); - } - - auth = authResponse.getAuth(); + response.ifPresent(r -> auth = r.getAuth()); + return isLoggedIn(); } /** diff --git a/src/main/java/com/scorpiac/javarant/DevRantAuth.java b/src/main/java/com/scorpiac/javarant/DevRantAuth.java new file mode 100644 index 0000000..7a8c25e --- /dev/null +++ b/src/main/java/com/scorpiac/javarant/DevRantAuth.java @@ -0,0 +1,9 @@ +package com.scorpiac.javarant; + +public class DevRantAuth { + private final DevRant devRant; + + DevRantAuth(DevRant devRant) { + this.devRant = devRant; + } +} diff --git a/src/main/java/com/scorpiac/javarant/exceptions/AuthenticationException.java b/src/main/java/com/scorpiac/javarant/exceptions/AuthenticationException.java deleted file mode 100644 index 5c56ef6..0000000 --- a/src/main/java/com/scorpiac/javarant/exceptions/AuthenticationException.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.scorpiac.javarant.exceptions; - -public class AuthenticationException extends Exception { - public AuthenticationException() { - super("Invalid login data specified."); - } -} diff --git a/src/test/java/com/scorpiac/javarant/DevRantTest.java b/src/test/java/com/scorpiac/javarant/DevRantTest.java index 6bdd5bd..c04e081 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantTest.java +++ b/src/test/java/com/scorpiac/javarant/DevRantTest.java @@ -14,4 +14,9 @@ public void testInit() { assertFalse(devRant.isLoggedIn()); devRant.logout(); } + + @Test(expectedExceptions = IllegalStateException.class) + public void testGetAuthNotLoggedIn() { + new DevRant().getAuth(); + } } From 16f92ee0e3f36e13139e023d1783b18cb1bb3157 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Sat, 9 Sep 2017 10:57:40 +0200 Subject: [PATCH 45/65] Fix request handler, implement login with tests --- .../javarant/services/RequestHandler.java | 27 +++++++++----- .../com/scorpiac/javarant/DevRantFeedIT.java | 4 +-- .../java/com/scorpiac/javarant/DevRantIT.java | 35 ++++++++++++++----- .../java/com/scorpiac/javarant/ITHelper.java | 13 ++++--- src/test/resources/auth-token.json | 9 +++++ 5 files changed, 64 insertions(+), 24 deletions(-) create mode 100644 src/test/resources/auth-token.json diff --git a/src/main/java/com/scorpiac/javarant/services/RequestHandler.java b/src/main/java/com/scorpiac/javarant/services/RequestHandler.java index 95a998d..1d42c2f 100644 --- a/src/main/java/com/scorpiac/javarant/services/RequestHandler.java +++ b/src/main/java/com/scorpiac/javarant/services/RequestHandler.java @@ -41,15 +41,21 @@ public Optional get(Endpoint endpoint, Class clazz, N } public Optional get(String endpoint, Class clazz, NameValuePair... params) { - return handleRequest(buildRequest(endpoint, Request::Get, params), clazz); + return handleRequest( + buildRequest(endpoint, Request::Get, getParameters(params)), + clazz + ); } public Optional post(Endpoint endpoint, Class clazz, NameValuePair... params) { - return post(endpoint, clazz, params); + return post(endpoint.toString(), clazz, params); } public Optional post(String endpoint, Class clazz, NameValuePair... params) { - return handleRequest(buildRequest(endpoint, Request::Post, params), clazz); + return handleRequest( + buildRequest(endpoint, Request::Post, Collections.emptyList()).bodyForm(getParameters(params)), + clazz + ); } /** @@ -60,17 +66,16 @@ public Optional post(String endpoint, Class clazz, Na * @param params The request parameters. * @return A request. */ - private Request buildRequest(String endpoint, Function requestFunction, NameValuePair... params) { + private Request buildRequest(String endpoint, Function requestFunction, List params) { URI uri; try { // Build the URI. uri = new URIBuilder(resolve(endpoint)) - .addParameters(getParameters(params)) + .addParameters(params) .build(); } catch (URISyntaxException e) { // This never happens. - LOGGER.error("Could not build URI.", e); throw new IllegalArgumentException("Could not build URI.", e); } @@ -116,8 +121,14 @@ private Optional handleRequest(Request request, Class * @return A list containing the given parameters, and the default parameters. */ private List getParameters(NameValuePair... params) { - List paramList = new ArrayList<>(params.length + 6); - paramList.addAll(Arrays.stream(params).filter(Objects::nonNull).collect(Collectors.toList())); + List paramList = new ArrayList<>(); + + // Add all non-null parameters. + paramList.addAll( + Arrays.stream(params) + .filter(Objects::nonNull) + .collect(Collectors.toList()) + ); // Add the parameters which always need to be present. paramList.add(new BasicNameValuePair("app", APP_ID)); diff --git a/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java b/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java index 536c035..94bc897 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java @@ -11,7 +11,7 @@ public class DevRantFeedIT extends ITHelper { @Test public void testGetRants() throws IOException { - server.stubFor(stubResponse( + server.stubFor(stubGet( get(urlPathEqualTo(Endpoint.RANTS.toString())) .withQueryParam("limit", equalTo("4")) .withQueryParam("skip", equalTo("1")) @@ -33,7 +33,7 @@ public void testGetRants() throws IOException { @Test public void testSearch() throws IOException { - server.stubFor(stubResponse( + server.stubFor(stubGet( get(urlPathEqualTo(Endpoint.SEARCH.toString())) .withQueryParam("term", equalTo("wtf")), "/search-wtf.json" diff --git a/src/test/java/com/scorpiac/javarant/DevRantIT.java b/src/test/java/com/scorpiac/javarant/DevRantIT.java index 1d19192..bd908fc 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantIT.java @@ -5,13 +5,12 @@ import java.io.IOException; import static com.github.tomakehurst.wiremock.client.WireMock.*; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; +import static org.testng.Assert.*; public class DevRantIT extends ITHelper { @Test public void testGetRant() throws IOException { - server.stubFor(stubResponse( + server.stubFor(stubGet( get(urlPathEqualTo(Endpoint.RANTS.toString() + "/686001")), "/rant-686001.json" )); @@ -37,7 +36,7 @@ public void testGetRant() throws IOException { @Test public void testGetRantInvalid() throws IOException { - server.stubFor(stubResponse( + server.stubFor(stubGet( get(urlPathEqualTo(Endpoint.RANTS.toString() + "/0")), "/rant-invalid.json" )); @@ -57,12 +56,12 @@ public void testGetRantServerError() throws IOException { @Test public void testGetUserByUsername() throws IOException { - server.stubFor(stubResponse( + server.stubFor(stubGet( get(urlPathEqualTo(Endpoint.USER_ID.toString())) .withQueryParam("username", equalTo("LucaScorpion")), "/user-id-LucaScorpion.json" )); - server.stubFor(stubResponse( + server.stubFor(stubGet( get(urlPathEqualTo(Endpoint.USERS.toString() + "/102959")), "/user-102959.json" )); @@ -88,7 +87,7 @@ public void testGetUserByUsername() throws IOException { @Test public void testGetUserByUsernameInvalid() throws IOException { - server.stubFor(stubResponse( + server.stubFor(stubGet( get(urlPathEqualTo(Endpoint.USER_ID.toString())) .withQueryParam("username", equalTo("invalid")), "/user-id-invalid.json" @@ -99,7 +98,7 @@ public void testGetUserByUsernameInvalid() throws IOException { @Test public void testGetUserInvalid() throws IOException { - server.stubFor(stubResponse( + server.stubFor(stubGet( get(urlPathEqualTo(Endpoint.USERS.toString() + "/123")), "/user-id-invalid.json" )); @@ -109,7 +108,7 @@ public void testGetUserInvalid() throws IOException { @Test public void testGetSurprise() throws IOException { - server.stubFor(stubResponse( + server.stubFor(stubGet( get(urlPathEqualTo(Endpoint.SURPRISE.toString())), "/rant-surprise.json" )); @@ -135,4 +134,22 @@ public void testGetSurprise() throws IOException { 381 ); } + + @Test + public void testLogin() throws IOException { + server.stubFor(stubPost( + post(urlPathEqualTo(Endpoint.AUTH_TOKEN.toString())) + .withRequestBody(equalTo("username=LucaScorpion&password=5up3r53cr3tp455w0rd&app=3&plat=3")), + "/auth-token.json" + )); + + char[] password = "5up3r53cr3tp455w0rd".toCharArray(); + assertTrue(devRant.login("LucaScorpion", password)); + assertNotNull(devRant.getAuth()); + + // Ensure the password array is cleared. + for (char c : password) { + assertEquals(c, 0); + } + } } diff --git a/src/test/java/com/scorpiac/javarant/ITHelper.java b/src/test/java/com/scorpiac/javarant/ITHelper.java index 9aabd4a..e94f963 100644 --- a/src/test/java/com/scorpiac/javarant/ITHelper.java +++ b/src/test/java/com/scorpiac/javarant/ITHelper.java @@ -40,15 +40,18 @@ public void resetServer() { server.resetAll(); } - protected MappingBuilder stubResponse(MappingBuilder stub, String resource) throws IOException { + protected MappingBuilder stubGet(MappingBuilder stub, String resource) throws IOException { + return stubPost(stub, resource) + .withQueryParam("app", equalTo("3")) + .withQueryParam("plat", equalTo("3")); + } + + protected MappingBuilder stubPost(MappingBuilder stub, String resource) throws IOException { String responseString; try (Scanner scanner = new Scanner(getClass().getResourceAsStream(resource))) { responseString = scanner.useDelimiter("\\A").next(); } - return stub - .withQueryParam("app", equalTo("3")) - .withQueryParam("plat", equalTo("3")) - .willReturn(aResponse().withBody(responseString)); + return stub.willReturn(aResponse().withBody(responseString)); } } diff --git a/src/test/resources/auth-token.json b/src/test/resources/auth-token.json new file mode 100644 index 0000000..bb9b223 --- /dev/null +++ b/src/test/resources/auth-token.json @@ -0,0 +1,9 @@ +{ + "success": true, + "auth_token": { + "id": 630167, + "key": "K3y#h3r3", + "expire_time": 1507537328, + "user_id": 102959 + } +} \ No newline at end of file From 3d46ffe33aa3149bec8b6831d5409b20b13632fc Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Sat, 9 Sep 2017 12:34:05 +0200 Subject: [PATCH 46/65] Rename Endpoint to ApiEndpoint --- .../{Endpoint.java => ApiEndpoint.java} | 6 +++--- .../java/com/scorpiac/javarant/DevRant.java | 14 +++++--------- .../com/scorpiac/javarant/DevRantFeed.java | 4 ++-- .../com/scorpiac/javarant/MinimalUser.java | 2 +- .../javarant/services/RequestHandler.java | 6 +++--- .../com/scorpiac/javarant/DevRantFeedIT.java | 4 ++-- .../java/com/scorpiac/javarant/DevRantIT.java | 18 +++++++++--------- 7 files changed, 25 insertions(+), 29 deletions(-) rename src/main/java/com/scorpiac/javarant/{Endpoint.java => ApiEndpoint.java} (81%) diff --git a/src/main/java/com/scorpiac/javarant/Endpoint.java b/src/main/java/com/scorpiac/javarant/ApiEndpoint.java similarity index 81% rename from src/main/java/com/scorpiac/javarant/Endpoint.java rename to src/main/java/com/scorpiac/javarant/ApiEndpoint.java index e6732a1..c0eda4e 100644 --- a/src/main/java/com/scorpiac/javarant/Endpoint.java +++ b/src/main/java/com/scorpiac/javarant/ApiEndpoint.java @@ -1,6 +1,6 @@ package com.scorpiac.javarant; -public enum Endpoint { +public enum ApiEndpoint { API("/api"), API_DEVRANT(API, "devrant"), // Rants. @@ -14,11 +14,11 @@ public enum Endpoint { private final String endpoint; - Endpoint(String endpoint) { + ApiEndpoint(String endpoint) { this.endpoint = endpoint; } - Endpoint(Endpoint base, String endpoint) { + ApiEndpoint(ApiEndpoint base, String endpoint) { this(base.toString() + '/' + endpoint); } diff --git a/src/main/java/com/scorpiac/javarant/DevRant.java b/src/main/java/com/scorpiac/javarant/DevRant.java index 4548799..0235f28 100644 --- a/src/main/java/com/scorpiac/javarant/DevRant.java +++ b/src/main/java/com/scorpiac/javarant/DevRant.java @@ -12,10 +12,6 @@ public class DevRant { private static final Injector INJECTOR; - static final String USER_URL = "/users"; - static final String RANT_URL = "/rants"; - static final String COLLAB_URL = "/collabs"; - private final DevRantFeed devRantFeed; private final DevRantAuth devRantAuth; @@ -72,7 +68,7 @@ public DevRantAuth getAuth() { * @return The rant. */ public Optional getRant(int id) { - return requestHandler.get(Endpoint.RANTS.toString() + '/' + id, CommentedRantResponse.class) + return requestHandler.get(ApiEndpoint.RANTS.toString() + '/' + id, CommentedRantResponse.class) .map(CommentedRantResponse::getRant); } @@ -83,7 +79,7 @@ public Optional getRant(int id) { * @return The user. */ public Optional getUser(String username) { - return requestHandler.get(Endpoint.USER_ID, UserIdResponse.class, new BasicNameValuePair("username", username)) + return requestHandler.get(ApiEndpoint.USER_ID, UserIdResponse.class, new BasicNameValuePair("username", username)) .flatMap(u -> getUser(u.getId())); } @@ -94,7 +90,7 @@ public Optional getUser(String username) { * @return The user. */ public Optional getUser(int id) { - Optional user = requestHandler.get(Endpoint.USERS.toString() + '/' + id, UserResponse.class) + Optional user = requestHandler.get(ApiEndpoint.USERS.toString() + '/' + id, UserResponse.class) .map(UserResponse::getUser); // Set the id, as that is not part of the response. @@ -109,7 +105,7 @@ public Optional getUser(int id) { * @return A random rant. */ public Optional getSurprise() { - return requestHandler.get(Endpoint.SURPRISE, RantResponse.class) + return requestHandler.get(ApiEndpoint.SURPRISE, RantResponse.class) .map(RantResponse::getRant); } @@ -124,7 +120,7 @@ public Optional getSurprise() { public boolean login(String username, char[] password) { logout(); - Optional response = requestHandler.post(Endpoint.AUTH_TOKEN, AuthResponse.class, + Optional response = requestHandler.post(ApiEndpoint.AUTH_TOKEN, AuthResponse.class, new BasicNameValuePair("username", username), new BasicNameValuePair("password", String.valueOf(password)) ); diff --git a/src/main/java/com/scorpiac/javarant/DevRantFeed.java b/src/main/java/com/scorpiac/javarant/DevRantFeed.java index 1412eb6..376efc2 100644 --- a/src/main/java/com/scorpiac/javarant/DevRantFeed.java +++ b/src/main/java/com/scorpiac/javarant/DevRantFeed.java @@ -34,7 +34,7 @@ public Optional> getRants(Sort sort, int limit) { * @return Rants from the feed. */ public Optional> getRants(Sort sort, int limit, int skip) { - return devRant.getRequestHandler().get(Endpoint.RANTS, RantFeedResponse.class, + return devRant.getRequestHandler().get(ApiEndpoint.RANTS, RantFeedResponse.class, new BasicNameValuePair("sort", sort.toString()), new BasicNameValuePair("limit", String.valueOf(limit)), new BasicNameValuePair("skip", String.valueOf(skip)) @@ -49,7 +49,7 @@ public Optional> getRants(Sort sort, int limit, int skip) { * @return The search results. */ public Optional> search(String term) { - return devRant.getRequestHandler().get(Endpoint.SEARCH, ResultsFeedResponse.class, + return devRant.getRequestHandler().get(ApiEndpoint.SEARCH, ResultsFeedResponse.class, new BasicNameValuePair("term", term) ) .map(ResultsFeedResponse::getResults); diff --git a/src/main/java/com/scorpiac/javarant/MinimalUser.java b/src/main/java/com/scorpiac/javarant/MinimalUser.java index 7e0474a..e8dde6d 100644 --- a/src/main/java/com/scorpiac/javarant/MinimalUser.java +++ b/src/main/java/com/scorpiac/javarant/MinimalUser.java @@ -68,6 +68,6 @@ public int getScore() { * @return The link to the user's profile. */ public URI getLink() { - return RequestHandler.BASE_URI.resolve(DevRant.USER_URL).resolve(username); + return RequestHandler.BASE_URI.resolve("/users").resolve(username); } } diff --git a/src/main/java/com/scorpiac/javarant/services/RequestHandler.java b/src/main/java/com/scorpiac/javarant/services/RequestHandler.java index 1d42c2f..1e0ebcc 100644 --- a/src/main/java/com/scorpiac/javarant/services/RequestHandler.java +++ b/src/main/java/com/scorpiac/javarant/services/RequestHandler.java @@ -1,6 +1,6 @@ package com.scorpiac.javarant.services; -import com.scorpiac.javarant.Endpoint; +import com.scorpiac.javarant.ApiEndpoint; import com.scorpiac.javarant.responses.Response; import org.apache.http.NameValuePair; import org.apache.http.client.fluent.Request; @@ -36,7 +36,7 @@ public RequestHandler(ObjectMapperResponseHandlerFactory responseHandlerFactory) this.responseHandlerFactory = responseHandlerFactory; } - public Optional get(Endpoint endpoint, Class clazz, NameValuePair... params) { + public Optional get(ApiEndpoint endpoint, Class clazz, NameValuePair... params) { return get(endpoint.toString(), clazz, params); } @@ -47,7 +47,7 @@ public Optional get(String endpoint, Class clazz, Nam ); } - public Optional post(Endpoint endpoint, Class clazz, NameValuePair... params) { + public Optional post(ApiEndpoint endpoint, Class clazz, NameValuePair... params) { return post(endpoint.toString(), clazz, params); } diff --git a/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java b/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java index 94bc897..909e830 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java @@ -12,7 +12,7 @@ public class DevRantFeedIT extends ITHelper { @Test public void testGetRants() throws IOException { server.stubFor(stubGet( - get(urlPathEqualTo(Endpoint.RANTS.toString())) + get(urlPathEqualTo(ApiEndpoint.RANTS.toString())) .withQueryParam("limit", equalTo("4")) .withQueryParam("skip", equalTo("1")) .withQueryParam("sort", equalTo("recent")), @@ -34,7 +34,7 @@ public void testGetRants() throws IOException { @Test public void testSearch() throws IOException { server.stubFor(stubGet( - get(urlPathEqualTo(Endpoint.SEARCH.toString())) + get(urlPathEqualTo(ApiEndpoint.SEARCH.toString())) .withQueryParam("term", equalTo("wtf")), "/search-wtf.json" )); diff --git a/src/test/java/com/scorpiac/javarant/DevRantIT.java b/src/test/java/com/scorpiac/javarant/DevRantIT.java index bd908fc..b17039e 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantIT.java @@ -11,7 +11,7 @@ public class DevRantIT extends ITHelper { @Test public void testGetRant() throws IOException { server.stubFor(stubGet( - get(urlPathEqualTo(Endpoint.RANTS.toString() + "/686001")), + get(urlPathEqualTo(ApiEndpoint.RANTS.toString() + "/686001")), "/rant-686001.json" )); @@ -37,7 +37,7 @@ public void testGetRant() throws IOException { @Test public void testGetRantInvalid() throws IOException { server.stubFor(stubGet( - get(urlPathEqualTo(Endpoint.RANTS.toString() + "/0")), + get(urlPathEqualTo(ApiEndpoint.RANTS.toString() + "/0")), "/rant-invalid.json" )); @@ -47,7 +47,7 @@ public void testGetRantInvalid() throws IOException { @Test public void testGetRantServerError() throws IOException { server.stubFor( - get(urlPathEqualTo(Endpoint.RANTS.toString() + "/123456")) + get(urlPathEqualTo(ApiEndpoint.RANTS.toString() + "/123456")) .willReturn(serverError().withBody("An unknown error occurred.")) ); @@ -57,12 +57,12 @@ public void testGetRantServerError() throws IOException { @Test public void testGetUserByUsername() throws IOException { server.stubFor(stubGet( - get(urlPathEqualTo(Endpoint.USER_ID.toString())) + get(urlPathEqualTo(ApiEndpoint.USER_ID.toString())) .withQueryParam("username", equalTo("LucaScorpion")), "/user-id-LucaScorpion.json" )); server.stubFor(stubGet( - get(urlPathEqualTo(Endpoint.USERS.toString() + "/102959")), + get(urlPathEqualTo(ApiEndpoint.USERS.toString() + "/102959")), "/user-102959.json" )); @@ -88,7 +88,7 @@ public void testGetUserByUsername() throws IOException { @Test public void testGetUserByUsernameInvalid() throws IOException { server.stubFor(stubGet( - get(urlPathEqualTo(Endpoint.USER_ID.toString())) + get(urlPathEqualTo(ApiEndpoint.USER_ID.toString())) .withQueryParam("username", equalTo("invalid")), "/user-id-invalid.json" )); @@ -99,7 +99,7 @@ public void testGetUserByUsernameInvalid() throws IOException { @Test public void testGetUserInvalid() throws IOException { server.stubFor(stubGet( - get(urlPathEqualTo(Endpoint.USERS.toString() + "/123")), + get(urlPathEqualTo(ApiEndpoint.USERS.toString() + "/123")), "/user-id-invalid.json" )); @@ -109,7 +109,7 @@ public void testGetUserInvalid() throws IOException { @Test public void testGetSurprise() throws IOException { server.stubFor(stubGet( - get(urlPathEqualTo(Endpoint.SURPRISE.toString())), + get(urlPathEqualTo(ApiEndpoint.SURPRISE.toString())), "/rant-surprise.json" )); @@ -138,7 +138,7 @@ public void testGetSurprise() throws IOException { @Test public void testLogin() throws IOException { server.stubFor(stubPost( - post(urlPathEqualTo(Endpoint.AUTH_TOKEN.toString())) + post(urlPathEqualTo(ApiEndpoint.AUTH_TOKEN.toString())) .withRequestBody(equalTo("username=LucaScorpion&password=5up3r53cr3tp455w0rd&app=3&plat=3")), "/auth-token.json" )); From 3529f2e2f6d2766aa811a8e1a91fa699c2eeb21b Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Sat, 9 Sep 2017 12:43:06 +0200 Subject: [PATCH 47/65] Clean up code --- .../scorpiac/javarant/responses/CommentedRantResponse.java | 6 ++++-- .../java/com/scorpiac/javarant/services/LogFactory.java | 2 +- .../services/ObjectMapperResponseHandlerFactory.java | 6 +++--- .../com/scorpiac/javarant/services/ObjectMapperService.java | 4 ++-- .../java/com/scorpiac/javarant/services/RequestHandler.java | 2 +- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/scorpiac/javarant/responses/CommentedRantResponse.java b/src/main/java/com/scorpiac/javarant/responses/CommentedRantResponse.java index 90b0bc5..5c61678 100644 --- a/src/main/java/com/scorpiac/javarant/responses/CommentedRantResponse.java +++ b/src/main/java/com/scorpiac/javarant/responses/CommentedRantResponse.java @@ -19,7 +19,8 @@ public CommentedRant getRant() { try { commentsField = rant.getClass().getDeclaredField("comments"); } catch (NoSuchFieldException e) { - throw new IllegalStateException(e); // TODO + // This never happens. + throw new IllegalStateException("Could not get comments field from rant.", e); } // Set the comments field. @@ -27,7 +28,8 @@ public CommentedRant getRant() { try { commentsField.set(rant, comments); } catch (IllegalAccessException e) { - throw new IllegalStateException(e); // TODO + // This never happens. + throw new IllegalStateException("Could not set comments field on rant.", e); } return rant; diff --git a/src/main/java/com/scorpiac/javarant/services/LogFactory.java b/src/main/java/com/scorpiac/javarant/services/LogFactory.java index b321f88..1383bd9 100644 --- a/src/main/java/com/scorpiac/javarant/services/LogFactory.java +++ b/src/main/java/com/scorpiac/javarant/services/LogFactory.java @@ -12,7 +12,7 @@ private LogFactory() { * * @return A logger. */ - public static Logger getLog() { + static Logger getLog() { return LoggerFactory.getLogger(Thread.currentThread().getStackTrace()[2].getClassName()); } } diff --git a/src/main/java/com/scorpiac/javarant/services/ObjectMapperResponseHandlerFactory.java b/src/main/java/com/scorpiac/javarant/services/ObjectMapperResponseHandlerFactory.java index 6c9e502..9e9d0d6 100644 --- a/src/main/java/com/scorpiac/javarant/services/ObjectMapperResponseHandlerFactory.java +++ b/src/main/java/com/scorpiac/javarant/services/ObjectMapperResponseHandlerFactory.java @@ -8,11 +8,11 @@ import java.util.Scanner; @Singleton -public class ObjectMapperResponseHandlerFactory { +class ObjectMapperResponseHandlerFactory { private final ObjectMapperService mapperService; @Inject - public ObjectMapperResponseHandlerFactory(ObjectMapperService mapperService) { + ObjectMapperResponseHandlerFactory(ObjectMapperService mapperService) { this.mapperService = mapperService; } @@ -23,7 +23,7 @@ public ObjectMapperResponseHandlerFactory(ObjectMapperService mapperService) { * @param The type of the class to map the response to. * @return A response handler. */ - public ResponseHandler getResponseHandler(Class clazz) { + ResponseHandler getResponseHandler(Class clazz) { return response -> { InputStream stream = response.getEntity().getContent(); String content = streamToString(stream); diff --git a/src/main/java/com/scorpiac/javarant/services/ObjectMapperService.java b/src/main/java/com/scorpiac/javarant/services/ObjectMapperService.java index e8cff24..8efbc71 100644 --- a/src/main/java/com/scorpiac/javarant/services/ObjectMapperService.java +++ b/src/main/java/com/scorpiac/javarant/services/ObjectMapperService.java @@ -7,7 +7,7 @@ import javax.inject.Singleton; @Singleton -public class ObjectMapperService { +class ObjectMapperService { private static final ObjectMapper MAPPER = new ObjectMapper(); static { @@ -21,7 +21,7 @@ public class ObjectMapperService { * * @return The object mapper. */ - public ObjectMapper getMapper() { + ObjectMapper getMapper() { return MAPPER; } } diff --git a/src/main/java/com/scorpiac/javarant/services/RequestHandler.java b/src/main/java/com/scorpiac/javarant/services/RequestHandler.java index 1e0ebcc..09ab3f7 100644 --- a/src/main/java/com/scorpiac/javarant/services/RequestHandler.java +++ b/src/main/java/com/scorpiac/javarant/services/RequestHandler.java @@ -32,7 +32,7 @@ public class RequestHandler { private int timeout = 15000; @Inject - public RequestHandler(ObjectMapperResponseHandlerFactory responseHandlerFactory) { + RequestHandler(ObjectMapperResponseHandlerFactory responseHandlerFactory) { this.responseHandlerFactory = responseHandlerFactory; } From 093ae2d664d1b7bebcce4961ca4b75ce6f711294 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Fri, 15 Sep 2017 22:47:58 +0200 Subject: [PATCH 48/65] Close scanner for response stream --- .../services/ObjectMapperResponseHandlerFactory.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/scorpiac/javarant/services/ObjectMapperResponseHandlerFactory.java b/src/main/java/com/scorpiac/javarant/services/ObjectMapperResponseHandlerFactory.java index 9e9d0d6..f4e3df9 100644 --- a/src/main/java/com/scorpiac/javarant/services/ObjectMapperResponseHandlerFactory.java +++ b/src/main/java/com/scorpiac/javarant/services/ObjectMapperResponseHandlerFactory.java @@ -32,7 +32,8 @@ ResponseHandler getResponseHandler(Class clazz) { } private static String streamToString(InputStream stream) { - Scanner s = new Scanner(stream).useDelimiter("\\A"); - return s.hasNext() ? s.next() : ""; + try (Scanner s = new Scanner(stream).useDelimiter("\\A")) { + return s.hasNext() ? s.next() : ""; + } } } From 2fb6a84fe898659ff7b238f6cefb64288e0a9460 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Fri, 15 Sep 2017 23:00:22 +0200 Subject: [PATCH 49/65] Return Result object instead of Optional for all request methods. --- .../java/com/scorpiac/javarant/DevRant.java | 37 ++++++++++--------- .../com/scorpiac/javarant/DevRantFeed.java | 13 +++---- .../java/com/scorpiac/javarant/Result.java | 35 ++++++++++++++++++ .../javarant/responses/AuthResponse.java | 8 ++-- .../responses/CommentedRantResponse.java | 5 ++- .../javarant/responses/RantFeedResponse.java | 13 +++---- .../javarant/responses/RantResponse.java | 8 ++-- .../scorpiac/javarant/responses/Response.java | 8 +++- .../responses/ResultsFeedResponse.java | 8 ++-- .../javarant/responses/UserIdResponse.java | 6 +-- .../javarant/responses/UserResponse.java | 6 +-- .../javarant/services/RequestHandler.java | 27 +++++++------- .../com/scorpiac/javarant/DevRantFeedIT.java | 4 +- .../java/com/scorpiac/javarant/DevRantIT.java | 16 ++++---- 14 files changed, 113 insertions(+), 81 deletions(-) create mode 100644 src/main/java/com/scorpiac/javarant/Result.java diff --git a/src/main/java/com/scorpiac/javarant/DevRant.java b/src/main/java/com/scorpiac/javarant/DevRant.java index 0235f28..3debf4c 100644 --- a/src/main/java/com/scorpiac/javarant/DevRant.java +++ b/src/main/java/com/scorpiac/javarant/DevRant.java @@ -7,7 +7,6 @@ import org.apache.http.message.BasicNameValuePair; import javax.inject.Inject; -import java.util.Optional; public class DevRant { private static final Injector INJECTOR; @@ -67,9 +66,8 @@ public DevRantAuth getAuth() { * @param id The id of the rant. * @return The rant. */ - public Optional getRant(int id) { - return requestHandler.get(ApiEndpoint.RANTS.toString() + '/' + id, CommentedRantResponse.class) - .map(CommentedRantResponse::getRant); + public Result getRant(int id) { + return requestHandler.get(ApiEndpoint.RANTS.toString() + '/' + id, CommentedRantResponse.class); } /** @@ -78,9 +76,16 @@ public Optional getRant(int id) { * @param username The username of the user. * @return The user. */ - public Optional getUser(String username) { - return requestHandler.get(ApiEndpoint.USER_ID, UserIdResponse.class, new BasicNameValuePair("username", username)) - .flatMap(u -> getUser(u.getId())); + public Result getUser(String username) { + Result id = requestHandler.get(ApiEndpoint.USER_ID, UserIdResponse.class, new BasicNameValuePair("username", username)); + + // Check the result. + if (id.getError().isPresent() || !id.getValue().isPresent()) { + // When the username is invalid, no error message is returned by the API. + return new Result<>(id.getError().isPresent() ? id.getError().get() : "Invalid username specified."); + } + + return getUser(id.getValue().get()); } /** @@ -89,14 +94,13 @@ public Optional getUser(String username) { * @param id The id of the user. * @return The user. */ - public Optional getUser(int id) { - Optional user = requestHandler.get(ApiEndpoint.USERS.toString() + '/' + id, UserResponse.class) - .map(UserResponse::getUser); + public Result getUser(int id) { + Result result = requestHandler.get(ApiEndpoint.USERS.toString() + '/' + id, UserResponse.class); // Set the id, as that is not part of the response. - user.ifPresent(u -> u.setId(id)); + result.getValue().ifPresent(u -> u.setId(id)); - return user; + return result; } /** @@ -104,9 +108,8 @@ public Optional getUser(int id) { * * @return A random rant. */ - public Optional getSurprise() { - return requestHandler.get(ApiEndpoint.SURPRISE, RantResponse.class) - .map(RantResponse::getRant); + public Result getSurprise() { + return requestHandler.get(ApiEndpoint.SURPRISE, RantResponse.class); } /** @@ -120,7 +123,7 @@ public Optional getSurprise() { public boolean login(String username, char[] password) { logout(); - Optional response = requestHandler.post(ApiEndpoint.AUTH_TOKEN, AuthResponse.class, + Result response = requestHandler.post(ApiEndpoint.AUTH_TOKEN, AuthResponse.class, new BasicNameValuePair("username", username), new BasicNameValuePair("password", String.valueOf(password)) ); @@ -130,7 +133,7 @@ public boolean login(String username, char[] password) { password[i] = 0; } - response.ifPresent(r -> auth = r.getAuth()); + response.getValue().ifPresent(r -> auth = r); return isLoggedIn(); } diff --git a/src/main/java/com/scorpiac/javarant/DevRantFeed.java b/src/main/java/com/scorpiac/javarant/DevRantFeed.java index 376efc2..d5198fe 100644 --- a/src/main/java/com/scorpiac/javarant/DevRantFeed.java +++ b/src/main/java/com/scorpiac/javarant/DevRantFeed.java @@ -5,7 +5,6 @@ import org.apache.http.message.BasicNameValuePair; import java.util.List; -import java.util.Optional; public class DevRantFeed { private final DevRant devRant; @@ -21,7 +20,7 @@ public class DevRantFeed { * @param limit How many rants to get. * @return Rants from the feed. */ - public Optional> getRants(Sort sort, int limit) { + public Result> getRants(Sort sort, int limit) { return getRants(sort, limit, 0); } @@ -33,13 +32,12 @@ public Optional> getRants(Sort sort, int limit) { * @param skip How many rants to skip. * @return Rants from the feed. */ - public Optional> getRants(Sort sort, int limit, int skip) { + public Result> getRants(Sort sort, int limit, int skip) { return devRant.getRequestHandler().get(ApiEndpoint.RANTS, RantFeedResponse.class, new BasicNameValuePair("sort", sort.toString()), new BasicNameValuePair("limit", String.valueOf(limit)), new BasicNameValuePair("skip", String.valueOf(skip)) - ) - .map(RantFeedResponse::getRants); + ); } /** @@ -48,10 +46,9 @@ public Optional> getRants(Sort sort, int limit, int skip) { * @param term The term to search for. * @return The search results. */ - public Optional> search(String term) { + public Result> search(String term) { return devRant.getRequestHandler().get(ApiEndpoint.SEARCH, ResultsFeedResponse.class, new BasicNameValuePair("term", term) - ) - .map(ResultsFeedResponse::getResults); + ); } } diff --git a/src/main/java/com/scorpiac/javarant/Result.java b/src/main/java/com/scorpiac/javarant/Result.java new file mode 100644 index 0000000..2f349d1 --- /dev/null +++ b/src/main/java/com/scorpiac/javarant/Result.java @@ -0,0 +1,35 @@ +package com.scorpiac.javarant; + +import com.scorpiac.javarant.responses.Response; + +import java.util.Optional; + +public class Result { + private final T value; + private final String error; + + public Result(String error) { + this.error = error; + value = null; + } + + public Result(Response response) { + if (!response.isSuccess() || (response.getError() != null && !response.getError().isEmpty())) { + // An error occurred. + error = response.getError(); + value = null; + } else { + // Success. + error = null; + value = response.getValue(); + } + } + + public Optional getValue() { + return Optional.ofNullable(value); + } + + public Optional getError() { + return Optional.ofNullable(error); + } +} diff --git a/src/main/java/com/scorpiac/javarant/responses/AuthResponse.java b/src/main/java/com/scorpiac/javarant/responses/AuthResponse.java index 872142c..c412508 100644 --- a/src/main/java/com/scorpiac/javarant/responses/AuthResponse.java +++ b/src/main/java/com/scorpiac/javarant/responses/AuthResponse.java @@ -3,11 +3,9 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.scorpiac.javarant.Auth; -public class AuthResponse extends Response { +public class AuthResponse extends Response { @JsonProperty("auth_token") - private Auth auth; - - public Auth getAuth() { - return auth; + void setAuth(Auth auth) { + value = auth; } } diff --git a/src/main/java/com/scorpiac/javarant/responses/CommentedRantResponse.java b/src/main/java/com/scorpiac/javarant/responses/CommentedRantResponse.java index 5c61678..73eebba 100644 --- a/src/main/java/com/scorpiac/javarant/responses/CommentedRantResponse.java +++ b/src/main/java/com/scorpiac/javarant/responses/CommentedRantResponse.java @@ -7,13 +7,14 @@ import java.lang.reflect.Field; import java.util.List; -public class CommentedRantResponse extends Response { +public class CommentedRantResponse extends Response { @JsonProperty private CommentedRant rant; @JsonProperty private List comments; - public CommentedRant getRant() { + @Override + public CommentedRant getValue() { // Get the comments field. Field commentsField; try { diff --git a/src/main/java/com/scorpiac/javarant/responses/RantFeedResponse.java b/src/main/java/com/scorpiac/javarant/responses/RantFeedResponse.java index 1c15e94..4700a8a 100644 --- a/src/main/java/com/scorpiac/javarant/responses/RantFeedResponse.java +++ b/src/main/java/com/scorpiac/javarant/responses/RantFeedResponse.java @@ -1,18 +1,17 @@ package com.scorpiac.javarant.responses; import com.fasterxml.jackson.annotation.JsonProperty; -import com.scorpiac.javarant.Rant; import com.scorpiac.javarant.News; +import com.scorpiac.javarant.Rant; import java.util.List; -public class RantFeedResponse extends Response { +public class RantFeedResponse extends Response> { @JsonProperty - private List rants; - @JsonProperty - private News news; + private News news; // TODO: use this. - public List getRants() { - return rants; + @JsonProperty + void setRants(List rants) { + value = rants; } } diff --git a/src/main/java/com/scorpiac/javarant/responses/RantResponse.java b/src/main/java/com/scorpiac/javarant/responses/RantResponse.java index 730ce7b..a50b9c9 100644 --- a/src/main/java/com/scorpiac/javarant/responses/RantResponse.java +++ b/src/main/java/com/scorpiac/javarant/responses/RantResponse.java @@ -3,11 +3,9 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.scorpiac.javarant.Rant; -public class RantResponse extends Response { +public class RantResponse extends Response { @JsonProperty - private Rant rant; - - public Rant getRant() { - return rant; + void setRant(Rant rant) { + value = rant; } } diff --git a/src/main/java/com/scorpiac/javarant/responses/Response.java b/src/main/java/com/scorpiac/javarant/responses/Response.java index 9f30f6d..3bee77a 100644 --- a/src/main/java/com/scorpiac/javarant/responses/Response.java +++ b/src/main/java/com/scorpiac/javarant/responses/Response.java @@ -2,12 +2,14 @@ import com.fasterxml.jackson.annotation.JsonProperty; -public class Response { +public abstract class Response { @JsonProperty private boolean success; @JsonProperty private String error; + T value; + public boolean isSuccess() { return success; } @@ -15,4 +17,8 @@ public boolean isSuccess() { public String getError() { return error; } + + public T getValue() { + return value; + } } diff --git a/src/main/java/com/scorpiac/javarant/responses/ResultsFeedResponse.java b/src/main/java/com/scorpiac/javarant/responses/ResultsFeedResponse.java index bf779b0..ce6e174 100644 --- a/src/main/java/com/scorpiac/javarant/responses/ResultsFeedResponse.java +++ b/src/main/java/com/scorpiac/javarant/responses/ResultsFeedResponse.java @@ -5,11 +5,9 @@ import java.util.List; -public class ResultsFeedResponse extends Response { +public class ResultsFeedResponse extends Response> { @JsonProperty - private List results; - - public List getResults() { - return results; + void setResults(List results) { + value = results; } } diff --git a/src/main/java/com/scorpiac/javarant/responses/UserIdResponse.java b/src/main/java/com/scorpiac/javarant/responses/UserIdResponse.java index 5d8a6b1..eb1eaeb 100644 --- a/src/main/java/com/scorpiac/javarant/responses/UserIdResponse.java +++ b/src/main/java/com/scorpiac/javarant/responses/UserIdResponse.java @@ -4,9 +4,7 @@ public class UserIdResponse extends Response { @JsonProperty("user_id") - private int id; - - public int getId() { - return id; + void setId(int id) { + value = id; } } diff --git a/src/main/java/com/scorpiac/javarant/responses/UserResponse.java b/src/main/java/com/scorpiac/javarant/responses/UserResponse.java index 6f2d281..ec69a05 100644 --- a/src/main/java/com/scorpiac/javarant/responses/UserResponse.java +++ b/src/main/java/com/scorpiac/javarant/responses/UserResponse.java @@ -5,9 +5,7 @@ public class UserResponse extends Response { @JsonProperty - private User profile; - - public User getUser() { - return profile; + void setProfile(User profile) { + value = profile; } } diff --git a/src/main/java/com/scorpiac/javarant/services/RequestHandler.java b/src/main/java/com/scorpiac/javarant/services/RequestHandler.java index 09ab3f7..aa2d3d3 100644 --- a/src/main/java/com/scorpiac/javarant/services/RequestHandler.java +++ b/src/main/java/com/scorpiac/javarant/services/RequestHandler.java @@ -1,6 +1,7 @@ package com.scorpiac.javarant.services; import com.scorpiac.javarant.ApiEndpoint; +import com.scorpiac.javarant.Result; import com.scorpiac.javarant.responses.Response; import org.apache.http.NameValuePair; import org.apache.http.client.fluent.Request; @@ -36,22 +37,22 @@ public class RequestHandler { this.responseHandlerFactory = responseHandlerFactory; } - public Optional get(ApiEndpoint endpoint, Class clazz, NameValuePair... params) { + public Result get(ApiEndpoint endpoint, Class clazz, NameValuePair... params) { return get(endpoint.toString(), clazz, params); } - public Optional get(String endpoint, Class clazz, NameValuePair... params) { + public Result get(String endpoint, Class clazz, NameValuePair... params) { return handleRequest( buildRequest(endpoint, Request::Get, getParameters(params)), clazz ); } - public Optional post(ApiEndpoint endpoint, Class clazz, NameValuePair... params) { + public Result post(ApiEndpoint endpoint, Class clazz, NameValuePair... params) { return post(endpoint.toString(), clazz, params); } - public Optional post(String endpoint, Class clazz, NameValuePair... params) { + public Result post(String endpoint, Class clazz, NameValuePair... params) { return handleRequest( buildRequest(endpoint, Request::Post, Collections.emptyList()).bodyForm(getParameters(params)), clazz @@ -93,24 +94,22 @@ URI resolve(String endpoint) { * * @param request The request to handle. * @param clazz The class to map the response to. - * @param The type of the class to map the response to. + * @param The type of the class to use in the result. + * @param The type of the class to map the internal response to. * @return The mapped response. */ - private Optional handleRequest(Request request, Class clazz) { - T response; + // This is because of the last line, which cannot be checked. Just make sure it is tested properly. + @SuppressWarnings("unchecked") + private Result handleRequest(Request request, Class clazz) { + R response; try { response = request.execute().handleResponse(responseHandlerFactory.getResponseHandler(clazz)); } catch (IOException e) { LOGGER.error("Failed to execute request.", e); - return Optional.empty(); + return new Result<>(e.getMessage()); } - // Check if there was an error. - if (response.getError() != null) { - LOGGER.error("A devRant API error occurred: " + response.getError()); - } - - return response.isSuccess() ? Optional.of(response) : Optional.empty(); + return new Result(response); } /** diff --git a/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java b/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java index 909e830..eada65e 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java @@ -19,7 +19,7 @@ public void testGetRants() throws IOException { "/feed-rants.json" )); - List rants = devRant.getFeed().getRants(Sort.RECENT, 4, 1).get(); + List rants = devRant.getFeed().getRants(Sort.RECENT, 4, 1).getValue().get(); assertEquals(rants.size(), 4); validateRant(rants.get(0), @@ -39,7 +39,7 @@ public void testSearch() throws IOException { "/search-wtf.json" )); - List rants = devRant.getFeed().search("wtf").get(); + List rants = devRant.getFeed().search("wtf").getValue().get(); validateRant(rants.get(1), 542296, diff --git a/src/test/java/com/scorpiac/javarant/DevRantIT.java b/src/test/java/com/scorpiac/javarant/DevRantIT.java index b17039e..068b024 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantIT.java @@ -15,7 +15,7 @@ public void testGetRant() throws IOException { "/rant-686001.json" )); - CommentedRant rant = devRant.getRant(686001).get(); + CommentedRant rant = devRant.getRant(686001).getValue().get(); validateRant( rant, @@ -41,7 +41,7 @@ public void testGetRantInvalid() throws IOException { "/rant-invalid.json" )); - assertFalse(devRant.getRant(0).isPresent()); + assertFalse(devRant.getRant(0).getValue().isPresent()); } @Test @@ -51,7 +51,7 @@ public void testGetRantServerError() throws IOException { .willReturn(serverError().withBody("An unknown error occurred.")) ); - assertFalse(devRant.getRant(123456).isPresent()); + assertFalse(devRant.getRant(123456).getValue().isPresent()); } @Test @@ -66,7 +66,7 @@ public void testGetUserByUsername() throws IOException { "/user-102959.json" )); - User user = devRant.getUser("LucaScorpion").get(); + User user = devRant.getUser("LucaScorpion").getValue().get(); validateUser(user, 102959, @@ -93,7 +93,9 @@ public void testGetUserByUsernameInvalid() throws IOException { "/user-id-invalid.json" )); - assertFalse(devRant.getUser("invalid").isPresent()); + Result user = devRant.getUser("invalid"); + assertFalse(user.getValue().isPresent()); + assertTrue(user.getError().isPresent()); } @Test @@ -103,7 +105,7 @@ public void testGetUserInvalid() throws IOException { "/user-id-invalid.json" )); - assertFalse(devRant.getUser(123).isPresent()); + assertFalse(devRant.getUser(123).getValue().isPresent()); } @Test @@ -113,7 +115,7 @@ public void testGetSurprise() throws IOException { "/rant-surprise.json" )); - Rant rant = devRant.getSurprise().get(); + Rant rant = devRant.getSurprise().getValue().get(); validateRant(rant, 26356, From 43a1b6e2b326e95e9fe5ccef2ce584eca0a5b035 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Fri, 15 Sep 2017 23:09:30 +0200 Subject: [PATCH 50/65] Improve tests to also check for error messages --- .../java/com/scorpiac/javarant/DevRant.java | 8 ++++++- .../com/scorpiac/javarant/DevRantFeedIT.java | 10 +++++--- .../java/com/scorpiac/javarant/DevRantIT.java | 24 +++++++++++++------ 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/scorpiac/javarant/DevRant.java b/src/main/java/com/scorpiac/javarant/DevRant.java index 3debf4c..95d074f 100644 --- a/src/main/java/com/scorpiac/javarant/DevRant.java +++ b/src/main/java/com/scorpiac/javarant/DevRant.java @@ -97,8 +97,14 @@ public Result getUser(String username) { public Result getUser(int id) { Result result = requestHandler.get(ApiEndpoint.USERS.toString() + '/' + id, UserResponse.class); + // Check the result. + if (result.getError().isPresent() || !result.getValue().isPresent()) { + // When the user id is invalid, no error message is returned by the API. + return new Result<>(result.getError().isPresent() ? result.getError().get() : "Invalid user id specified."); + } + // Set the id, as that is not part of the response. - result.getValue().ifPresent(u -> u.setId(id)); + result.getValue().get().setId(id); return result; } diff --git a/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java b/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java index eada65e..813e541 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java @@ -7,6 +7,7 @@ import static com.github.tomakehurst.wiremock.client.WireMock.*; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; public class DevRantFeedIT extends ITHelper { @Test @@ -19,7 +20,9 @@ public void testGetRants() throws IOException { "/feed-rants.json" )); - List rants = devRant.getFeed().getRants(Sort.RECENT, 4, 1).getValue().get(); + Result> result = devRant.getFeed().getRants(Sort.RECENT, 4, 1); + assertFalse(result.getError().isPresent()); + List rants = result.getValue().get(); assertEquals(rants.size(), 4); validateRant(rants.get(0), @@ -39,9 +42,10 @@ public void testSearch() throws IOException { "/search-wtf.json" )); - List rants = devRant.getFeed().search("wtf").getValue().get(); + Result> result = devRant.getFeed().search("wtf"); + assertFalse(result.getError().isPresent()); - validateRant(rants.get(1), + validateRant(result.getValue().get().get(1), 542296, "Stages of learning angular js \n1. Wtf \n2. I think I get it. \n3. Wtf", 327, diff --git a/src/test/java/com/scorpiac/javarant/DevRantIT.java b/src/test/java/com/scorpiac/javarant/DevRantIT.java index 068b024..00a9a45 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantIT.java @@ -15,7 +15,9 @@ public void testGetRant() throws IOException { "/rant-686001.json" )); - CommentedRant rant = devRant.getRant(686001).getValue().get(); + Result result = devRant.getRant(686001); + assertFalse(result.getError().isPresent()); + CommentedRant rant = result.getValue().get(); validateRant( rant, @@ -41,7 +43,9 @@ public void testGetRantInvalid() throws IOException { "/rant-invalid.json" )); - assertFalse(devRant.getRant(0).getValue().isPresent()); + Result result = devRant.getRant(0); + assertFalse(result.getValue().isPresent()); + assertTrue(result.getError().isPresent()); } @Test @@ -51,7 +55,9 @@ public void testGetRantServerError() throws IOException { .willReturn(serverError().withBody("An unknown error occurred.")) ); - assertFalse(devRant.getRant(123456).getValue().isPresent()); + Result result = devRant.getRant(123456); + assertFalse(result.getValue().isPresent()); + assertTrue(result.getError().isPresent()); } @Test @@ -66,9 +72,10 @@ public void testGetUserByUsername() throws IOException { "/user-102959.json" )); - User user = devRant.getUser("LucaScorpion").getValue().get(); + Result result = devRant.getUser("LucaScorpion"); + assertFalse(result.getError().isPresent()); - validateUser(user, + validateUser(result.getValue().get(), 102959, "LucaScorpion", 3831, @@ -105,7 +112,9 @@ public void testGetUserInvalid() throws IOException { "/user-id-invalid.json" )); - assertFalse(devRant.getUser(123).getValue().isPresent()); + Result result = devRant.getUser(123); + assertFalse(result.getValue().isPresent()); + assertTrue(result.getError().isPresent()); } @Test @@ -115,7 +124,8 @@ public void testGetSurprise() throws IOException { "/rant-surprise.json" )); - Rant rant = devRant.getSurprise().getValue().get(); + Result result = devRant.getSurprise(); + Rant rant = result.getValue().get(); validateRant(rant, 26356, From 9b2df7bdf2650cb91dbacea49bb981605cbc8531 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Fri, 15 Sep 2017 23:32:49 +0200 Subject: [PATCH 51/65] Add javadoc, small code improvements --- .../java/com/scorpiac/javarant/DevRant.java | 8 ++++-- .../java/com/scorpiac/javarant/Result.java | 15 +++------- .../responses/CommentedRantResponse.java | 4 +++ .../scorpiac/javarant/responses/Response.java | 28 +++++++++++++++---- .../ObjectMapperResponseHandlerFactory.java | 6 ++++ .../services/ObjectMapperService.java | 1 + .../javarant/services/RequestHandler.java | 12 ++++++-- 7 files changed, 54 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/scorpiac/javarant/DevRant.java b/src/main/java/com/scorpiac/javarant/DevRant.java index 95d074f..395ae26 100644 --- a/src/main/java/com/scorpiac/javarant/DevRant.java +++ b/src/main/java/com/scorpiac/javarant/DevRant.java @@ -21,6 +21,10 @@ public class DevRant { INJECTOR = Guice.createInjector(); } + /** + * Create a new DevRant instance. + * Each DevRant instance has their own authenticated user. + */ public DevRant() { INJECTOR.injectMembers(this); devRantFeed = new DevRantFeed(this); @@ -82,7 +86,7 @@ public Result getUser(String username) { // Check the result. if (id.getError().isPresent() || !id.getValue().isPresent()) { // When the username is invalid, no error message is returned by the API. - return new Result<>(id.getError().isPresent() ? id.getError().get() : "Invalid username specified."); + return new Result<>("Invalid username specified."); } return getUser(id.getValue().get()); @@ -100,7 +104,7 @@ public Result getUser(int id) { // Check the result. if (result.getError().isPresent() || !result.getValue().isPresent()) { // When the user id is invalid, no error message is returned by the API. - return new Result<>(result.getError().isPresent() ? result.getError().get() : "Invalid user id specified."); + return new Result<>("Invalid user id specified."); } // Set the id, as that is not part of the response. diff --git a/src/main/java/com/scorpiac/javarant/Result.java b/src/main/java/com/scorpiac/javarant/Result.java index 2f349d1..a6ad89d 100644 --- a/src/main/java/com/scorpiac/javarant/Result.java +++ b/src/main/java/com/scorpiac/javarant/Result.java @@ -5,8 +5,8 @@ import java.util.Optional; public class Result { - private final T value; - private final String error; + private T value; + private String error; public Result(String error) { this.error = error; @@ -14,15 +14,8 @@ public Result(String error) { } public Result(Response response) { - if (!response.isSuccess() || (response.getError() != null && !response.getError().isEmpty())) { - // An error occurred. - error = response.getError(); - value = null; - } else { - // Success. - error = null; - value = response.getValue(); - } + error = response.getError(); + value = response.getValue(); } public Optional getValue() { diff --git a/src/main/java/com/scorpiac/javarant/responses/CommentedRantResponse.java b/src/main/java/com/scorpiac/javarant/responses/CommentedRantResponse.java index 73eebba..b8d0a5c 100644 --- a/src/main/java/com/scorpiac/javarant/responses/CommentedRantResponse.java +++ b/src/main/java/com/scorpiac/javarant/responses/CommentedRantResponse.java @@ -15,6 +15,10 @@ public class CommentedRantResponse extends Response { @Override public CommentedRant getValue() { + if (rant == null) { + return null; + } + // Get the comments field. Field commentsField; try { diff --git a/src/main/java/com/scorpiac/javarant/responses/Response.java b/src/main/java/com/scorpiac/javarant/responses/Response.java index 3bee77a..d6ecaf9 100644 --- a/src/main/java/com/scorpiac/javarant/responses/Response.java +++ b/src/main/java/com/scorpiac/javarant/responses/Response.java @@ -2,22 +2,40 @@ import com.fasterxml.jackson.annotation.JsonProperty; +/** + * This class is used internally to create a pojo from a response. + * It contains whether the request was successful, an error message and the actual result value. + * + * @param The type of the value the response contains. + */ public abstract class Response { @JsonProperty private boolean success; @JsonProperty private String error; + // The actual result value. T value; - public boolean isSuccess() { - return success; - } - + /** + * Get the error. + * Returns {@code null} if there is no error. + * + * @return The error, or {@code null} if there is no error. + */ public String getError() { - return error; + if (success) { + return null; + } + + return error != null ? error : "An unknown error occurred."; } + /** + * Get the result value. + * + * @return The result value, or {@code null} if there was an error. + */ public T getValue() { return value; } diff --git a/src/main/java/com/scorpiac/javarant/services/ObjectMapperResponseHandlerFactory.java b/src/main/java/com/scorpiac/javarant/services/ObjectMapperResponseHandlerFactory.java index f4e3df9..7b1f959 100644 --- a/src/main/java/com/scorpiac/javarant/services/ObjectMapperResponseHandlerFactory.java +++ b/src/main/java/com/scorpiac/javarant/services/ObjectMapperResponseHandlerFactory.java @@ -31,6 +31,12 @@ ResponseHandler getResponseHandler(Class clazz) { }; } + /** + * Read a stream into a string. + * + * @param stream The stream to read. + * @return The contents of the stream as a string. + */ private static String streamToString(InputStream stream) { try (Scanner s = new Scanner(stream).useDelimiter("\\A")) { return s.hasNext() ? s.next() : ""; diff --git a/src/main/java/com/scorpiac/javarant/services/ObjectMapperService.java b/src/main/java/com/scorpiac/javarant/services/ObjectMapperService.java index 8efbc71..6191ca5 100644 --- a/src/main/java/com/scorpiac/javarant/services/ObjectMapperService.java +++ b/src/main/java/com/scorpiac/javarant/services/ObjectMapperService.java @@ -11,6 +11,7 @@ class ObjectMapperService { private static final ObjectMapper MAPPER = new ObjectMapper(); static { + // Configure the mapper. MAPPER.configure(JsonGenerator.Feature.IGNORE_UNKNOWN, true); MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); MAPPER.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true); diff --git a/src/main/java/com/scorpiac/javarant/services/RequestHandler.java b/src/main/java/com/scorpiac/javarant/services/RequestHandler.java index aa2d3d3..19fd010 100644 --- a/src/main/java/com/scorpiac/javarant/services/RequestHandler.java +++ b/src/main/java/com/scorpiac/javarant/services/RequestHandler.java @@ -85,6 +85,13 @@ private Request buildRequest(String endpoint, Function requestFunc .socketTimeout(timeout); } + /** + * Resolve an endpoint to a URI. + * This method's main purpose is to be overridden for the tests. + * + * @param endpoint The endpoint to resolve. + * @return A complete URI. + */ URI resolve(String endpoint) { return BASE_URI.resolve(endpoint); } @@ -117,7 +124,7 @@ private Result handleRequest(Request request, Class getParameters(NameValuePair... params) { List paramList = new ArrayList<>(); @@ -137,7 +144,8 @@ private List getParameters(NameValuePair... params) { } /** - * Set the request timeout. This timeout will be used for the socket and connection timeout. + * Set the request timeout. + * This timeout will be used for the socket and connection timeout. * * @param timeout The timeout in milliseconds to set, or -1 to set no timeout. */ From 11597a129472c6e3f5ecf0f09a48e956d56d070b Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Fri, 15 Sep 2017 23:35:27 +0200 Subject: [PATCH 52/65] Improve CommentedRantResponse --- .../responses/CommentedRantResponse.java | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/scorpiac/javarant/responses/CommentedRantResponse.java b/src/main/java/com/scorpiac/javarant/responses/CommentedRantResponse.java index b8d0a5c..531a1ae 100644 --- a/src/main/java/com/scorpiac/javarant/responses/CommentedRantResponse.java +++ b/src/main/java/com/scorpiac/javarant/responses/CommentedRantResponse.java @@ -8,21 +8,30 @@ import java.util.List; public class CommentedRantResponse extends Response { + private List comments; + @JsonProperty - private CommentedRant rant; + void setComments(List comments) { + this.comments = comments; + setCommentsOnRant(); + } + @JsonProperty - private List comments; + void setRant(CommentedRant rant) { + value = rant; + setCommentsOnRant(); + } - @Override - public CommentedRant getValue() { - if (rant == null) { - return null; + private void setCommentsOnRant() { + // Make sure both properties are set first. + if (value == null || comments == null) { + return; } // Get the comments field. Field commentsField; try { - commentsField = rant.getClass().getDeclaredField("comments"); + commentsField = value.getClass().getDeclaredField("comments"); } catch (NoSuchFieldException e) { // This never happens. throw new IllegalStateException("Could not get comments field from rant.", e); @@ -31,12 +40,10 @@ public CommentedRant getValue() { // Set the comments field. commentsField.setAccessible(true); try { - commentsField.set(rant, comments); + commentsField.set(value, comments); } catch (IllegalAccessException e) { // This never happens. throw new IllegalStateException("Could not set comments field on rant.", e); } - - return rant; } } From 7634e00ee4905130fd3f38282b400f6220d20273 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Sat, 16 Sep 2017 10:34:36 +0200 Subject: [PATCH 53/65] Add weekly rants to feed --- .../com/scorpiac/javarant/ApiEndpoint.java | 1 + .../com/scorpiac/javarant/DevRantFeed.java | 29 +- ...edResponse.java => RantsFeedResponse.java} | 2 +- .../com/scorpiac/javarant/DevRantFeedIT.java | 22 + src/test/resources/feed-weekly.json | 468 ++++++++++++++++++ 5 files changed, 508 insertions(+), 14 deletions(-) rename src/main/java/com/scorpiac/javarant/responses/{RantFeedResponse.java => RantsFeedResponse.java} (84%) create mode 100644 src/test/resources/feed-weekly.json diff --git a/src/main/java/com/scorpiac/javarant/ApiEndpoint.java b/src/main/java/com/scorpiac/javarant/ApiEndpoint.java index c0eda4e..bc9ae16 100644 --- a/src/main/java/com/scorpiac/javarant/ApiEndpoint.java +++ b/src/main/java/com/scorpiac/javarant/ApiEndpoint.java @@ -7,6 +7,7 @@ public enum ApiEndpoint { RANTS(API_DEVRANT, "rants"), SEARCH(API_DEVRANT, "search"), SURPRISE(RANTS, "surprise"), + WEEKLY(API_DEVRANT, "weekly-rants"), // Users. USER_ID(API, "get-user-id"), USERS(API, "users"), diff --git a/src/main/java/com/scorpiac/javarant/DevRantFeed.java b/src/main/java/com/scorpiac/javarant/DevRantFeed.java index d5198fe..0dc6a98 100644 --- a/src/main/java/com/scorpiac/javarant/DevRantFeed.java +++ b/src/main/java/com/scorpiac/javarant/DevRantFeed.java @@ -1,6 +1,6 @@ package com.scorpiac.javarant; -import com.scorpiac.javarant.responses.RantFeedResponse; +import com.scorpiac.javarant.responses.RantsFeedResponse; import com.scorpiac.javarant.responses.ResultsFeedResponse; import org.apache.http.message.BasicNameValuePair; @@ -13,17 +13,6 @@ public class DevRantFeed { this.devRant = devRant; } - /** - * Get rants from the feed. - * - * @param sort How to sort the feed. - * @param limit How many rants to get. - * @return Rants from the feed. - */ - public Result> getRants(Sort sort, int limit) { - return getRants(sort, limit, 0); - } - /** * Get rants from the feed. * @@ -33,7 +22,7 @@ public Result> getRants(Sort sort, int limit) { * @return Rants from the feed. */ public Result> getRants(Sort sort, int limit, int skip) { - return devRant.getRequestHandler().get(ApiEndpoint.RANTS, RantFeedResponse.class, + return devRant.getRequestHandler().get(ApiEndpoint.RANTS, RantsFeedResponse.class, new BasicNameValuePair("sort", sort.toString()), new BasicNameValuePair("limit", String.valueOf(limit)), new BasicNameValuePair("skip", String.valueOf(skip)) @@ -51,4 +40,18 @@ public Result> search(String term) { new BasicNameValuePair("term", term) ); } + + /** + * Get weekly rants from the feed. + * + * @param sort How to sort the feed. + * @param skip How many rants to skip. + * @return Weekly rants from the feed. + */ + public Result> getWeekly(Sort sort, int skip) { + return devRant.getRequestHandler().get(ApiEndpoint.WEEKLY, RantsFeedResponse.class, + new BasicNameValuePair("sort", sort.toString()), + new BasicNameValuePair("skip", String.valueOf(skip)) + ); + } } diff --git a/src/main/java/com/scorpiac/javarant/responses/RantFeedResponse.java b/src/main/java/com/scorpiac/javarant/responses/RantsFeedResponse.java similarity index 84% rename from src/main/java/com/scorpiac/javarant/responses/RantFeedResponse.java rename to src/main/java/com/scorpiac/javarant/responses/RantsFeedResponse.java index 4700a8a..078eaf3 100644 --- a/src/main/java/com/scorpiac/javarant/responses/RantFeedResponse.java +++ b/src/main/java/com/scorpiac/javarant/responses/RantsFeedResponse.java @@ -6,7 +6,7 @@ import java.util.List; -public class RantFeedResponse extends Response> { +public class RantsFeedResponse extends Response> { @JsonProperty private News news; // TODO: use this. diff --git a/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java b/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java index 813e541..b6df051 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java @@ -53,4 +53,26 @@ public void testSearch() throws IOException { "javascript", "angularjs", "wtf?" ); } + + @Test + public void testGetWeekly() throws IOException { + server.stubFor(stubGet( + get(urlPathEqualTo(ApiEndpoint.WEEKLY.toString())) + .withQueryParam("skip", equalTo("2")) + .withQueryParam("sort", equalTo("algo")), + "/feed-weekly.json" + )); + + Result> result = devRant.getFeed().getWeekly(Sort.ALGO, 2); + assertFalse(result.getError().isPresent()); + List rants = result.getValue().get(); + + validateRant(rants.get(0), + 843118, + "My own OCR library... so far I haven't found a proper recognizer", + 2, + 0, + "wk69" + ); + } } diff --git a/src/test/resources/feed-weekly.json b/src/test/resources/feed-weekly.json new file mode 100644 index 0000000..e5b8c1f --- /dev/null +++ b/src/test/resources/feed-weekly.json @@ -0,0 +1,468 @@ +{ + "success": true, + "rants": [ + { + "id": 843118, + "text": "My own OCR library... so far I haven't found a proper recognizer", + "score": 2, + "created_time": 1505519016, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wk69" + ], + "vote_state": 0, + "edited": false, + "rt": 1, + "user_id": 345752, + "user_username": "davide", + "user_score": 1057, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-2_16-1_3-14_8-1_7-1_5-1_12-2_6-3_10-1_2-13_4-1.jpg" + } + }, + { + "id": 842334, + "text": "Lol. How do I register for {content[0].title}, what happened to all the mails๐Ÿ˜จ", + "score": 1, + "created_time": 1505490675, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_842334_CevLD.jpg", + "width": 540, + "height": 960 + }, + "num_comments": 0, + "tags": [ + "wk69" + ], + "vote_state": 0, + "edited": false, + "rt": 1, + "user_id": 534161, + "user_username": "odusanya", + "user_score": 1, + "user_avatar": { + "b": "7bc8a4" + } + }, + { + "id": 841937, + "text": "This.\nI'm not sure yet what I'm going to do with it, but it's going to be awesome.", + "score": 62, + "created_time": 1505479985, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_841937_GGKae.jpg", + "width": 800, + "height": 600 + }, + "num_comments": 19, + "tags": [ + "raspberry pi", + "wk69" + ], + "vote_state": 0, + "edited": false, + "rt": 1, + "user_id": 713663, + "user_username": "karwler", + "user_score": 425, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-3_3-2_8-1_7-1_5-1_12-2_6-15_10-1_2-1_15-19_18-1_4-1_19-1.jpg" + } + }, + { + "id": 839859, + "text": "The autopilot that detects flooded farm zones. (Crashed it a couple of times๐Ÿ˜…)", + "score": 32, + "created_time": 1505398354, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_839859_Z1sCU.jpg", + "width": 800, + "height": 600 + }, + "num_comments": 2, + "tags": [ + "wk69" + ], + "vote_state": 0, + "edited": false, + "rt": 1, + "user_id": 839783, + "user_username": "pilobasualdo", + "user_score": 36, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-1_3-1_8-1_7-1_5-1_12-1_6-2_2-1_4-1.jpg" + } + }, + { + "id": 836605, + "text": "Want to finish my little friend for a long time now. He can dance Michael Jackson's moonwalk for now.\nIdea was to use the sonar sensor to switch dancing styles but he is still blind :(", + "score": 54, + "created_time": 1505284462, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_836605_1xkR6.jpg", + "width": 750, + "height": 1000 + }, + "num_comments": 13, + "tags": [ + "wk69", + "side projects", + "arduino", + "moonwalk" + ], + "vote_state": 0, + "edited": false, + "rt": 1, + "user_id": 717749, + "user_username": "marcom", + "user_score": 128, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-2_16-6_3-11_8-1_7-1_5-1_12-2_6-3_10-1_2-5_15-7_4-1.jpg" + } + }, + { + "id": 842553, + "text": "Side project I wish I could finish... updating my resume and building a portfolio so I can get a new job.", + "score": 6, + "created_time": 1505498330, + "attached_image": "", + "num_comments": 1, + "tags": [ + "wk69" + ], + "vote_state": 0, + "edited": false, + "rt": 1, + "user_id": 564853, + "user_username": "amlove32", + "user_score": 246, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-f_9-1_1-9_16-6_3-2_8-1_7-1_5-1_12-9_6-37_10-1_2-1_4-1.jpg" + }, + "user_dpp": 1 + }, + { + "id": 842420, + "text": "My dead simple, one button, location based time tracking app. Would help really much...", + "score": 1, + "created_time": 1505494093, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wk69" + ], + "vote_state": 0, + "edited": false, + "rt": 1, + "user_id": 757407, + "user_username": "adfr", + "user_score": 9, + "user_avatar": { + "b": "7bc8a4" + } + }, + { + "id": 841044, + "text": "My text editor, a stripped down, simpler version of vim", + "score": 7, + "created_time": 1505447807, + "attached_image": "", + "num_comments": 9, + "tags": [ + "wk69" + ], + "vote_state": 0, + "edited": false, + "rt": 1, + "user_id": 426559, + "user_username": "leolas95", + "user_score": 227, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-6_3-13_8-1_7-1_5-1_12-2_6-2_10-1_2-10_15-10_11-2_4-1_19-1.jpg" + } + }, + { + "id": 840841, + "text": "Finishing at least one novel so I can jumpstart a new career and get out of web development while I'm still ahead. That or get better at math/physics so I can solve anti-gravity/free energy and become a legend. Neither of these things is ever likely to happen.", + "score": 6, + "created_time": 1505435185, + "attached_image": "", + "num_comments": 3, + "tags": [ + "wk69" + ], + "vote_state": 0, + "edited": false, + "rt": 1, + "user_id": 306082, + "user_username": "stackodev", + "user_score": 3431, + "user_avatar": { + "b": "a973a2", + "i": "v-17_c-3_b-2_g-m_9-1_1-2_16-1_3-13_8-1_7-1_5-1_12-2_6-3_2-3_11-2_4-1.jpg" + } + }, + { + "id": 840611, + "text": "I am creating an app for my school which displays timetables, supplementations and other stuff. I think I am overengineering it a bit, hope I will finish in a week.", + "score": 6, + "created_time": 1505422334, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wk69" + ], + "vote_state": 0, + "edited": false, + "rt": 1, + "user_id": 685721, + "user_username": "tonakriz", + "user_score": 6, + "user_avatar": { + "b": "7bc8a4" + } + }, + { + "id": 840448, + "text": "Finishing my software to predict ice-hoceky results... so I would finally have a portfolio to show for, just in case I decide to drop off of academia one day ๐Ÿ˜ฅ", + "score": 2, + "created_time": 1505417715, + "attached_image": "", + "num_comments": 1, + "tags": [ + "wk69" + ], + "vote_state": 0, + "edited": false, + "rt": 1, + "user_id": 839356, + "user_username": "CyclingMatt", + "user_score": 58, + "user_avatar": { + "b": "ecd276", + "i": "v-17_c-3_b-7_g-m_9-1_1-2_16-6_3-1_8-1_7-1_5-1_12-2_6-11_10-1_2-8_15-19_11-1_4-1.jpg" + } + }, + { + "id": 840417, + "text": "I have already started the process of a side project by desiging the software, the architecture, the 3d model, ordered all the electronics of a pet 'smart' stable for my guinea pigs. \n\nWhich would automatically feed them and refill their water tanks silently but for me the point on playing around with dozens of sensors for like different water levels, water quality, hay, temperature, water quality (you get the point) ... Building a nice looking web interface or an App to control everything and get a live feed from different angles ( sounds a bit crazy altogether but it looked like a cool project ) \n\nI even started a instructable and had a github repo for sharing the source of the app/web interface and the whole micro service based server \n\nI'm still at it and hopefully will start to build the ***ing wood and acrylic parts in the next month's but currently and for the last month's free time ist my archenemy\n\nKeep you posted if you are interested ๐Ÿ˜€", + "score": 6, + "created_time": 1505416656, + "attached_image": "", + "num_comments": 0, + "tags": [ + "69", + "wk69" + ], + "vote_state": 0, + "edited": false, + "rt": 1, + "user_id": 762698, + "user_username": "deltam", + "user_score": 107, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-1_3-1_8-1_7-1_5-1_12-2_6-15_10-1_2-14_4-1.jpg" + } + }, + { + "id": 843407, + "text": "3d hack & slash game with smooth animation like \"DMC\" xD", + "score": 0, + "created_time": 1505541740, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wk69" + ], + "vote_state": 0, + "edited": false, + "rt": 1, + "user_id": 22066, + "user_username": "CSaratakij", + "user_score": 2123, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-4_16-3_3-5_8-2_7-2_5-2_12-4_6-14_2-47_15-6_18-4_4-2_19-3_20-2_21-2.jpg" + } + }, + { + "id": 842119, + "text": "A yugioh themed text to ydk converter with a few other features I have yet to implement. (Need to write the converter gui first before adding anything else and I'm stuck on that)", + "score": 0, + "created_time": 1505484507, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wk69" + ], + "vote_state": 0, + "edited": false, + "rt": 1, + "user_id": 18663, + "user_username": "jester5537", + "user_score": 567, + "user_avatar": { + "b": "a973a2", + "i": "v-17_c-3_b-2_g-m_9-1_1-2_16-3_3-3_8-1_7-1_5-1_12-2_6-3_10-1_2-39_15-19_11-2_4-1.jpg" + } + }, + { + "id": 840690, + "text": "Fucking behemoth(check the collab)", + "score": 1, + "created_time": 1505425449, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wk69" + ], + "vote_state": 0, + "edited": true, + "rt": 1, + "user_id": 180116, + "user_username": "Data-Bound", + "user_score": 2833, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-4_16-1_3-1_8-1_7-1_5-2_12-4_6-11_2-10_15-19_18-4_4-2_19-3_21-2.jpg" + } + }, + { + "id": 840378, + "text": "Project I'd like to finish?\nhttps://play.google.com/store/apps/...", + "score": 5, + "created_time": 1505415222, + "attached_image": "", + "num_comments": 3, + "tags": [ + "wk69" + ], + "vote_state": 0, + "edited": false, + "rt": 1, + "links": [ + { + "type": "url", + "url": "https://play.google.com/store/apps/details?id=at.armino28.tminer", + "short_url": "https://play.google.com/store/apps/...", + "title": "https://play.google.com/store/apps/...", + "start": 28, + "end": 66, + "special": 1 + } + ], + "special": true, + "user_id": 686066, + "user_username": "ao28", + "user_score": 140, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-1_1-1_16-1_3-2_8-1_7-1_5-1_12-1_6-98_10-1_2-14_4-1.jpg" + } + }, + { + "id": 840008, + "text": "I want to finnish modifying a chess game written in Cobol.\n\nModification would be a CICS module so that it can be played live on a mainframe.", + "score": 4, + "created_time": 1505402580, + "attached_image": "", + "num_comments": 4, + "tags": [ + "cobol", + "wk69", + "cics", + "mainframe" + ], + "vote_state": 0, + "edited": true, + "rt": 1, + "user_id": 578534, + "user_username": "Loeina", + "user_score": 62, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-1_16-1_3-2_8-1_7-1_5-1_12-1_6-17_10-1_2-1_15-56_4-1.jpg" + } + }, + { + "id": 839846, + "text": "Remaking my portfolio site using VueJS. Finish that character design sketchbook I started years ago. And move out of my parents' house.", + "score": 7, + "created_time": 1505397975, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wk69" + ], + "vote_state": 0, + "edited": false, + "rt": 1, + "user_id": 729633, + "user_username": "KyrePh", + "user_score": 70, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-1_1-6_16-6_3-3_8-1_7-1_5-1_12-6_6-6_10-1_2-19_15-42_11-2_4-1.jpg" + } + }, + { + "id": 839365, + "text": "My personal resume website. Damn my lazy ass, can't do anything. ๐Ÿ˜ฅ", + "score": 16, + "created_time": 1505380191, + "attached_image": "", + "num_comments": 3, + "tags": [ + "wk69" + ], + "vote_state": 0, + "edited": false, + "rt": 1, + "user_id": 674260, + "user_username": "NoLifeSigns", + "user_score": 394, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-6_16-6_3-1_8-1_7-1_5-1_12-6_6-2_2-4_15-18_11-1_4-1.jpg" + } + }, + { + "id": 839231, + "text": "A script which spiders hotstar and navigate through the links in command line to obtain links..", + "score": 0, + "created_time": 1505375435, + "attached_image": "", + "num_comments": 0, + "tags": [ + "wk69" + ], + "vote_state": 0, + "edited": false, + "rt": 1, + "user_id": 709788, + "user_username": "aEEEdev", + "user_score": 405, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-1_1-6_16-2_3-1_8-1_7-1_5-1_12-6_6-2_2-3_15-2_4-1.jpg" + } + } + ], + "settings": [], + "news": { + "id": 0, + "type": "weekly", + "headline": "Side project you wish you would start/finish?", + "footer": "Week 69 Group Rant - Add tag 'wk69' to your rant", + "height": 65, + "action": "none" + } +} \ No newline at end of file From d199b8efa76ebc1ed1d37c4f303b379f9deaaef5 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Sat, 16 Sep 2017 10:41:59 +0200 Subject: [PATCH 54/65] Add stories to feed --- .../com/scorpiac/javarant/ApiEndpoint.java | 3 +- .../com/scorpiac/javarant/DevRantFeed.java | 14 + .../com/scorpiac/javarant/DevRantFeedIT.java | 22 + src/test/resources/feed-stories.json | 472 ++++++++++++++++++ 4 files changed, 510 insertions(+), 1 deletion(-) create mode 100644 src/test/resources/feed-stories.json diff --git a/src/main/java/com/scorpiac/javarant/ApiEndpoint.java b/src/main/java/com/scorpiac/javarant/ApiEndpoint.java index bc9ae16..553b446 100644 --- a/src/main/java/com/scorpiac/javarant/ApiEndpoint.java +++ b/src/main/java/com/scorpiac/javarant/ApiEndpoint.java @@ -5,9 +5,10 @@ public enum ApiEndpoint { API_DEVRANT(API, "devrant"), // Rants. RANTS(API_DEVRANT, "rants"), - SEARCH(API_DEVRANT, "search"), SURPRISE(RANTS, "surprise"), + SEARCH(API_DEVRANT, "search"), WEEKLY(API_DEVRANT, "weekly-rants"), + STORIES(API_DEVRANT, "story-rants"), // Users. USER_ID(API, "get-user-id"), USERS(API, "users"), diff --git a/src/main/java/com/scorpiac/javarant/DevRantFeed.java b/src/main/java/com/scorpiac/javarant/DevRantFeed.java index 0dc6a98..511f875 100644 --- a/src/main/java/com/scorpiac/javarant/DevRantFeed.java +++ b/src/main/java/com/scorpiac/javarant/DevRantFeed.java @@ -54,4 +54,18 @@ public Result> getWeekly(Sort sort, int skip) { new BasicNameValuePair("skip", String.valueOf(skip)) ); } + + /** + * Get stories from the feed. + * + * @param sort How to sort the feed. + * @param skip How many rants to skip. + * @return Stories from the feed. + */ + public Result> getStories(Sort sort, int skip) { + return devRant.getRequestHandler().get(ApiEndpoint.STORIES, RantsFeedResponse.class, + new BasicNameValuePair("sort", sort.toString()), + new BasicNameValuePair("skip", String.valueOf(skip)) + ); + } } diff --git a/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java b/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java index b6df051..1853b72 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java @@ -75,4 +75,26 @@ public void testGetWeekly() throws IOException { "wk69" ); } + + @Test + public void testGetStories() throws IOException { + server.stubFor(stubGet( + get(urlPathEqualTo(ApiEndpoint.STORIES.toString())) + .withQueryParam("skip", equalTo("4")) + .withQueryParam("sort", equalTo("top")), + "/feed-stories.json" + )); + + Result> result = devRant.getFeed().getStories(Sort.TOP, 4); + assertFalse(result.getError().isPresent()); + List rants = result.getValue().get(); + + validateRant(rants.get(0), + 830647, + "", + 134, + 10, + "mother", "web", "atom", "dreamweaver" + ); + } } diff --git a/src/test/resources/feed-stories.json b/src/test/resources/feed-stories.json new file mode 100644 index 0000000..35165fb --- /dev/null +++ b/src/test/resources/feed-stories.json @@ -0,0 +1,472 @@ +{ + "success": true, + "rants": [ + { + "id": 830647, + "text": "", + "score": 134, + "created_time": 1505020638, + "attached_image": "", + "num_comments": 10, + "tags": [ + "mother", + "web", + "atom", + "dreamweaver" + ], + "vote_state": 0, + "edited": false, + "rt": 1, + "user_id": 732476, + "user_username": "awnz", + "user_score": 412, + "user_avatar": { + "b": "ecd276", + "i": "v-17_c-3_b-7_g-m_9-1_1-11_16-4_3-3_8-1_7-1_5-1_12-11_6-2_2-10_4-1.jpg" + } + }, + { + "id": 836856, + "text": "Fuck. I'm trying hard not to react to Apple related rants, but I can't fucking refrain from it. Apple are pissing me off to no fucking end with their greed, lies and pseudo-artistic edge, still pretending to be 'innovative', 'hip' and offering top notch technology. What a stinking, steaming, fucking pile of bullshit.\n\nSuck my knob, Apple! I hope you crash into the ground at top speed and make place for something truly good and innovative that can hopefully feast on your rotting remains without getting tainted by your greed, arrogance and lies.", + "score": 134, + "created_time": 1505293496, + "attached_image": "", + "num_comments": 48, + "tags": [ + "suck.my knob", + "i wish they'd finally go bankrupt", + "tainted apple" + ], + "vote_state": 0, + "edited": true, + "rt": 1, + "user_id": 141078, + "user_username": "AlexDeLarge", + "user_score": 43844, + "user_avatar": { + "b": "a973a2", + "i": "v-17_c-3_b-2_g-m_9-1_1-9_16-6_3-11_8-2_7-2_5-2_12-9_6-54_2-50_15-27_11-4_18-4_4-2_19-3_20-14_21-2.jpg" + }, + "user_dpp": 1 + }, + { + "id": 841073, + "text": "Hired a new backend Dev. He writes a script and sends it for testing...\nTester: \"It's not working...\"\nBackend Dev: Goes to Mongo and deletes the tester's whole profile...\n\nI cant control my laughter every time I remember this incident...He claimed it was a mistake, I don't think that it was a mistake...the tester had it coming...\n\"It's not working\" that's all he says every time...I mean at least give me something to start with...!", + "score": 129, + "created_time": 1505449498, + "attached_image": "", + "num_comments": 4, + "tags": [ + "mongodb", + "nodejs", + "backend" + ], + "vote_state": 0, + "edited": true, + "rt": 1, + "user_id": 92701, + "user_username": "5hirish", + "user_score": 201, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-2_16-10_3-1_8-1_7-1_5-1_12-2_6-2_10-1_2-10_15-37_4-1.jpg" + } + }, + { + "id": 832046, + "text": "Ok Santa, for Christmas I'd like some free time to finish :\n-My Android 2D pixel game\n-My Android app\n-My Winux (Windows+Linux) adventure game.\n-My own Brainfuck compiler.\n-My C code generator.\n-My C++ code generator.\n-My \"\"\"AI\"\"\" that plays Super Hexagon and Rocket League for me.\n-My own programming language.\n-My Android app for people who look for internships.\n-My C++ e-mail client.\n-My Telegram bot.\n-My CLI chat client.\n-My C/C++ automatic code obfuscator.\n\nI guess that's it...", + "score": 111, + "created_time": 1505103035, + "attached_image": "", + "num_comments": 8, + "tags": [ + "procrastination", + "wk69" + ], + "vote_state": 0, + "edited": true, + "rt": 1, + "user_id": 214220, + "user_username": "Lahsen2016", + "user_score": 1830, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-10_16-11_3-4_8-2_7-2_5-2_12-10_6-10_10-5_2-49_11-4_18-2_4-2_19-3_20-1.jpg" + } + }, + { + "id": 841401, + "text": "I fucking hate CNET already. I mean who likes a website which autoplays a video everytime you visit them, with 200% volume.\n\nBut this time, I am just so fucking annoyed. Here is the title of an article: \n\n\"iPhone 8, X's wireless charging is a game changer for Android\"\n\nAnd the subtitle:\n\n\"When it comes to Apple, plenty of Android phone makers are monkey see, monkey do.\"\n\nFUck you motherfucker. \"Monkey see, monkey do\". Are you fucking kidding me you cunt?\n\nRemeber your 3D touch bullshit? Your fucking wireless charging will be bullshit too.\n\n\"the rest of the phone users make do with messy cables.\"\n\nMaybe you're a fucking imbecile who doesn't know how to manage simple cables and ends up with broken wires.\n\nYou know who looks like a monkey? Some apple users who uses that shitty looking wireless earphone, which looks like monkey's dick you asshole.\n\nFuck off!", + "score": 101, + "created_time": 1505462382, + "attached_image": "", + "num_comments": 15, + "tags": [ + "apple", + "android", + "cnet" + ], + "vote_state": 0, + "edited": false, + "rt": 1, + "user_id": 42079, + "user_username": "tahnik", + "user_score": 35034, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-2_1-4_16-15_3-2_8-4_7-4_5-3_12-4_6-10_10-9_2-39_15-18_11-4_18-4_4-3_19-1_20-14_21-1.jpg" + }, + "user_dpp": 1 + }, + { + "id": 841966, + "text": "So I've got a Linux related job (or, starting at monday). When people ask me what my new position is called, I'll of course tell! Well, I stutter sometimes in my native language. Especially with the letter L. \n\n\"so what's your new position?\" \n\"Lllllll-lllll-llllllllllllllllllll\"\n*mother of god* \n\"lllllllllllllllllllllll-llllllllllllll* \n*OH FFS* \n\"Llllllllllll-llllllllllllllllllll-lllllllinux support engineer!\"\n*FUCKING FINALLY!* \n\n\"Hey man, you got a new job I heard, what's your new position?\"\n*please work* \n\"Lllllllllll-lllllllllllllllll-llllllllllllllllllllll* \n*MOTHERFUCKER* \n\"Lllllllll-lllllllllllllllllllinux support engineer!\" \n\n\"ey dude, what's your new position? Heard you got a new job!\" \n*alright let's do this better* \n\"gonna do stuff with servers and customer service!\" \n\"Ah cool! What system do they run on their servers?\"\n\nNo. ๐Ÿ˜ก", + "score": 94, + "created_time": 1505480918, + "attached_image": "", + "num_comments": 39, + "tags": [ + "fucking stuttering", + "linux", + "ffs", + "linuxxx" + ], + "vote_state": 0, + "edited": true, + "rt": 1, + "user_id": 22941, + "user_username": "linuxxx", + "user_score": 35032, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-3_3-3_8-3_7-3_5-4_12-2_6-7_10-9_2-48_15-11_18-4_4-4_19-3_20-14.jpg" + }, + "user_dpp": 1 + }, + { + "id": 842340, + "text": "Senior IT engineer enters the room and quietly talks to a coworker about a job related issue. \n\nAnother coworker decided to troll the sysadmin. \n\nCW: *yells* \"Open a ticket!\" (That's the sysadmin's regular reply)\nIT: *ignores*\nCW: *trying to get his attention* \"Open a ticket first! Then come back\"\nIT: *gives him the stare of death*\nCW: \"Go away and open a ticket!\"\nIT: *silently leaves the room*\n\nAfter no more than a minute CW gets a reject from all networks outside the company's VPN.\n\nIT comes back into the room, get's intimately close to CW's ear and says \"Now open a ticket\".\n\n๐Ÿ‘‹\n\n๐ŸŽค", + "score": 85, + "created_time": 1505491005, + "attached_image": "", + "num_comments": 8, + "tags": [ + "don't fuck with the sysadmin", + "burn", + "troll" + ], + "vote_state": 0, + "edited": false, + "rt": 1, + "user_id": 503297, + "user_username": "Noob", + "user_score": 3054, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-3_16-8_3-1_8-2_7-2_5-2_12-3_6-7_10-9_2-40_15-15_11-2_18-4_4-2_19-3_20-8_21-2.jpg" + } + }, + { + "id": 840178, + "text": "We have this stupid library at work. Its called Randomness, and its basically just a wrapper over the standard .NET System.Random class to help our devs generate random data for unit tests easier.\n\nDebate about random data in unit tests aside.\n\nI came across a bug in randomness. Theres a PickFrom function which gets passed an array, and returns a random element from the array. Problem is, it uses Random.Next to do this, and the max value was set to the array length, minus one.\n\nRandom.Next generates values inclusive of the min value, and exclusive of the max value. Arr.Length-1 as a max value, is wrong, the last element in the array would never be selected. \n\nSo i fixed it.\n\nAnd proceeded to break dozens upon dozens of unit tests that were now testing from their full sets of data, and had actually been faulty for god knows how long.", + "score": 81, + "created_time": 1505408692, + "attached_image": "", + "num_comments": 9, + "tags": [ + "random", + "off by 1", + "sweep these things under the carpet" + ], + "vote_state": 0, + "edited": false, + "rt": 1, + "user_id": 798905, + "user_username": "illusion466", + "user_score": 349, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-f_9-1_1-1_16-1_3-1_8-1_7-1_5-1_12-1_6-31_2-1_4-1.jpg" + } + }, + { + "id": 832679, + "text": "Acceptable places to leave your bag when you get in, in the morning:\n\n- Under your desk\r\n- On your desk\r\n- Infront of your locker\r\n- On the back of your chair\r\n- etc.\n\nUnacceptable, is to throw your bag behind you and to the right, so it ends up in the middle of the floor and behind my chair.\n\nConsistent use of this space, and me tripping over it will result in 2 things:\n\n1. I will intentionally run over your bag, back and forth until I am satisfied everything is broken.\n\n2. I will then pickup said bag and throw it, with force, at your head.", + "score": 77, + "created_time": 1505130070, + "attached_image": "", + "num_comments": 5, + "tags": [ + "tripping", + "in my way", + "upside your head", + "bad", + "rude" + ], + "vote_state": 0, + "edited": false, + "rt": 1, + "user_id": 190725, + "user_username": "practiseSafeHex", + "user_score": 5101, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-6_3-1_8-3_7-3_5-3_12-1_6-2_10-1_2-47_18-3_4-3_19-3_20-8.jpg" + } + }, + { + "id": 838743, + "text": "Thank you dear mr. boss for fucking up our master branch by adding local changes to a 2 months outdated master branch (250 FUCKING COMMITS BEHIND), pull the remote and then just push without resolving any conflicts!!!1!!!\n\nBut thank you so much for sending me an email at 10pm asking me to resolve the conflicts.\n\nIt is 3 in the morning and it took 1 hour to get it clean.\n\nSometimes I want to break some necks...", + "score": 77, + "created_time": 1505351707, + "attached_image": "", + "num_comments": 10, + "tags": [ + "boss", + "git push -f", + "merge conflicts", + "unresolved problems" + ], + "vote_state": 0, + "edited": false, + "rt": 1, + "user_id": 552400, + "user_username": "PonySlaystation", + "user_score": 4961, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-8_3-13_8-2_7-2_5-4_12-1_6-3_10-9_2-81_15-11_11-4_18-4_4-4_19-3_20-5_21-2.jpg" + }, + "user_dpp": 1 + }, + { + "id": 835994, + "text": "To people who don't know how to use Linux: Just because I use nano instead of gedit or any other GUI text editor does not mean I'm showing off. Why can't you understand that ssh-ing into a server and opening a file in the terminal itself to edit three lines of configuration is much easier than opening FileZilla, connecting, downloading the file, making the changes and uploading it again. It's fine if you want to do it that way. But please don't judge me for doing it my way.\n\nTo people who are good with Linux: Can you please stop suggesting me to use vim instead, EVERY FUCKING TIME? I know it's more powerful, but I haven't been using Linux enough to have surpassed it's learning curve. Plus I google up how to use it and do use it when I have the need. Please let me be?\n\nTo people who tell me to use Windows for everything: Go suck a fat dick, you uncultured morons.", + "score": 73, + "created_time": 1505252317, + "attached_image": "", + "num_comments": 10, + "tags": [], + "vote_state": 0, + "edited": false, + "rt": 1, + "user_id": 145378, + "user_username": "alcatraz627", + "user_score": 5805, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-11_16-13_3-4_8-4_7-4_5-2_12-11_17-1_6-15_10-9_2-59_15-11_11-1_18-4_4-2_19-3_20-8_21-1.jpg" + } + }, + { + "id": 835049, + "text": "This is a follow up on my previous rant https://devrant.io/rants/815062\n\nI confronted her again. \n\nI was told that I am useless and worth noting to this world, worth more dead than alive.\n\nI was told that I will never get anywhere in life, and that the time I have spent watching Elon Musk interviews (amongst other ones, I do this for fun) is fucking useless, as I will never get anywhere ini life. Only low-life pieces of shit such as myself deserve nothing apparently.\n\nI had to organise a place to stay with my family, but I couldn't for a week. I slept on the floor outside my workplace, and bathed at friends.\n\nI have moved out, had to go get my own place. I have nothing, but I have my motivation back. I have my coding behind me, I have my motivation, I have my mind clear, and I have plans for the future.\n\nI plan to fucking make a name for myself, and fuck everyone who has a fucking issue with it.\n\nWill distribute the app sometime.\n\nFuck people who fuck you around.", + "score": 68, + "created_time": 1505222666, + "attached_image": "", + "num_comments": 28, + "tags": [ + "backonfeet" + ], + "vote_state": 0, + "edited": false, + "rt": 1, + "links": [ + { + "type": "url", + "url": "https://devrant.io/rants/815062", + "short_url": "https://devrant.io/rants/815062", + "title": "https://devrant.io/rants/815062", + "start": 40, + "end": 71, + "special": 1 + } + ], + "special": true, + "user_id": 369518, + "user_username": "ragnarr023", + "user_score": 3091, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-2_3-9_8-2_7-2_5-2_12-1_6-83_10-7_2-48_15-11_18-2_4-2_19-2_20-7.jpg" + } + }, + { + "id": 842343, + "text": "I have this one major pet peeve - getting interrupted on any messenger by \"hey\".\n\nQ: Hey\nA: Hey, what's up?\n\n-minutes pass, I try to resume work-\n\nQ: Do you have a second?\nA: Sure, what's up?\n\n-minutes pass, I try to resume work... Again-\n\nQ: Do you know anything about #feature#?\nA: Yeah, I wrote most of it, what do you need?\n\n-minutes pass, I try to resume work... AGAIN-\n\n(goes on same pattern, takes half an hour for a 10 second question/answer)\n\nLike... Come on!!! Don't do this to me\n\nI get it, I like to be cordial and friendly - but there is absolutely nothing stopping you from getting your message across without making me have to go back and forth (interrupting my work).", + "score": 67, + "created_time": 1505491203, + "attached_image": "", + "num_comments": 9, + "tags": [], + "vote_state": 0, + "edited": true, + "rt": 1, + "user_id": 396296, + "user_username": "Neotelos", + "user_score": 1409, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-3_16-2_3-2_8-2_7-2_5-1_12-3_6-2_10-8_2-38_15-18_4-1_19-2.jpg" + } + }, + { + "id": 830568, + "text": "So I just had an interview a couple weeks ago at one of the largest employers of software engineers in the country. After multiple stages of group interviews it came to the 2 on 1 individual interview with some project leads (picture devs using a MacBook with Github stickers - lads). Among the questions they asked me we both had a laugh at 2 of them:\n\nQ: Explain how deadlocks work?\nA: Hire me and I will explain how they work.\n\nQ: Where do you see yourself in 5 years?\nA: Celebrating 22s 22m 22h 22/12/2022.\n\nStrangely enough they hired me ๐Ÿ˜Ž", + "score": 67, + "created_time": 1505011544, + "attached_image": "", + "num_comments": 0, + "tags": [ + "interview" + ], + "vote_state": 0, + "edited": false, + "rt": 1, + "user_id": 566882, + "user_username": "scottydevil", + "user_score": 251, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-1_3-3_8-1_7-1_5-1_12-2_6-2_10-1_2-31_11-2_4-1.jpg" + } + }, + { + "id": 839205, + "text": "!rant\n\n*in slack*\nMe: Team's gonna watch IT. Wanna come?\nFriend from other team: Yeah, sure!\n\n*in cinema*\nFriend: WTF! Why is this I.T? This is scary as fuck!\nMe: I-T? It's \"it\". HAHAHAHA It's a remake of the It 1990 movie.\nFriend: I don't know anything about that. I'm scared as hell! I thought this would be some tech stuff and crying in the server room or something!", + "score": 63, + "created_time": 1505374453, + "attached_image": "", + "num_comments": 7, + "tags": [ + "it" + ], + "vote_state": 0, + "edited": false, + "rt": 1, + "user_id": 111587, + "user_username": "switchstep", + "user_score": 1543, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-f_9-1_1-2_16-6_3-1_8-1_7-1_5-1_12-2_6-6_10-1_2-18_18-2_4-1_19-2.jpg" + } + }, + { + "id": 831031, + "text": "!rant\n\nDamn, I'm totally immerged in WebGL right now. Oh, the possibilities! And it's so much faster than I thought it would be, that's fucking amazing. I'm currently experimenting with three.js and babylon.js and it's so much fun. I love particle effects and 3D animations. Even 2D rendering is damn cool!\n\nI also just learned you can import models from Blender. I never liked Blender's UI, because it seems so bloated and complicated to me. But then again, Blender is amazingly powerful and free, so there is no real reason to complain. I guess I have to learn it know, in order to use WebGL for most of the things I'm envisioning right now.\n\nJust thinking about the amazing possibilities makes my head spin in excitement: interactive 3D infographics, futuristic UIs, games, movies and galleries, โ€ฆ so. fucking. cool. Why the fuck didn't I try out WebGL earlier?", + "score": 57, + "created_time": 1505048499, + "attached_image": "", + "num_comments": 15, + "tags": [ + "babylon.js", + "three.js", + "webgl", + "3d", + "canvas" + ], + "vote_state": 0, + "edited": true, + "rt": 1, + "user_id": 141078, + "user_username": "AlexDeLarge", + "user_score": 43844, + "user_avatar": { + "b": "a973a2", + "i": "v-17_c-3_b-2_g-m_9-1_1-9_16-6_3-11_8-2_7-2_5-2_12-9_6-54_2-50_15-27_11-4_18-4_4-2_19-3_20-14_21-2.jpg" + }, + "user_dpp": 1 + }, + { + "id": 833015, + "text": "So...\nI'm penetrationtesting a network and the servers on said network\nThe network administrator and IT security officer knows this, because they hired me..\n\nTL;DR a scan caused the network to crash. \n\nToday I received a very angry email going \"Stop scanning NOW!\" from one of the IT departments.\n\nApparently I crashed their login server and thus their entire network...\nIt happened d the first time I scanned the network from the outside and they had spend an entire day figuring out how and repairing the service they thought was the problem, but then it crashed again, when I scanned from within the network.\n\nNow they want to send me a list of IP's that I'm not allowed to scan and want to know exactly what and when I'm scanning...\n\nHow crap can they be at their job, if they weren't able to spot a scan... The only reason they found out it was me was because the NA had whitelistet my IP, so that I could scan in peace...", + "score": 56, + "created_time": 1505141878, + "attached_image": "", + "num_comments": 5, + "tags": [ + "bad design", + "scanning", + "security", + "crash", + "networking" + ], + "vote_state": 0, + "edited": false, + "rt": 1, + "user_id": 38990, + "user_username": "Folkmann", + "user_score": 976, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-10_3-5_8-1_7-1_5-1_12-2_6-33_10-5_2-56_15-9_11-2_18-2_4-1_19-2.jpg" + } + }, + { + "id": 840694, + "text": "I found a cool project on GitHub. I forked it and added a simple dev server with the intent of making it more accessible which could lead to more activity = improved project. I created a PR with small concise commits with very informative messages.\n\nThe guy who owns the project comments and says \"I don't want your dev server, I have an apache instance locally on my computer\". I tell him \"Ok sure, but wouldn't it be nice if everyone else also had a nice dev server which can be started with a single command?\", and other people join the PR and agree with me that we should make it available for everyone.\n\nBut the fucking idiot doesn't care, \"No, I prefer to use my apache server\". YOU FUCKING ASS WIPE, why do you even put it up on GitHub if you don't want contributions to make your project better and more available? I saw other open PRs where he basically did the same thing, left a snarky comment without merging it. What a fucking tool. Worst spent time ever.\n\nFUCK YOU", + "score": 56, + "created_time": 1505425570, + "attached_image": "", + "num_comments": 6, + "tags": [ + "open source idiot", + "open source" + ], + "vote_state": 0, + "edited": true, + "rt": 1, + "user_id": 5063, + "user_username": "zshh", + "user_score": 3455, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-1_16-7_3-2_8-4_7-4_5-3_12-1_6-8_10-6_2-40_18-4_4-3_19-3_20-12.jpg" + }, + "user_dpp": 1 + }, + { + "id": 841779, + "text": "HOW THE FUCK DID PDF JUST BECOME THE DEFAULT FOR DOCUMENTS ANYWAY? ARE YOU FUCKING KIDDING ME?!\n\nWHY IS IT WO FYCKING DIFFICULT TO OPEN A PDF, PLACE A FUCKING HAND DRAWN SUGNATURE ON A DOTTED LINE AND EMAIL IT BACK TO SOMEONE?\n\nWHY DO I NEES TO PAY SOME COMPANY FOR A THIRD OARTY PDF APP JUST TO FUCKING PLACE 128 PIXELS ON A DOCUMENT?\n\nAND WHY DO I HAVE TO KEEP PAYING THEM EVERY MONYH?\n\nHIW IS IT THAT AFTER ANDEOID BEING AROUND FOR PROBABLY MORE THAN 10 YEARS NOW, THAT AUTOCORRECT ON EVERY KEYWORD IS ACTUALLY WORSE?!\n\nWHY ARE DOCTORS SO SHIT? WHAT IS WRONG WITH THIS WORLD?!\n\nFYCK! DUCK!\n\nFUCK!!!", + "score": 56, + "created_time": 1505475001, + "attached_image": "", + "num_comments": 26, + "tags": [], + "vote_state": 0, + "edited": false, + "rt": 1, + "user_id": 722396, + "user_username": "GodHatesMe", + "user_score": 5524, + "user_avatar": { + "b": "ecd276", + "i": "v-17_c-3_b-7_g-m_9-1_1-2_16-15_3-2_8-1_7-1_5-3_12-2_6-98_2-8_4-3_21-2.jpg" + }, + "user_dpp": 1 + }, + { + "id": 838826, + "text": "There are things that i wish i didn't see.\n\nYesterday, i went to a coffee shop to relax and reviewing my works. And suddenly a college friend of mine approach me and we started talking about work. \n\nMe: So, What do you do at work? What's your stack?\r\nHim: Not much of a new. Still working with wordpress, html,css and jquery.\n\nSo he started talking about how cool wordpress is and how he generates money doing sites.\n\nMe: Can i see your sample works? \r\nHim: Sure, *opens his shitty windows laptop with Web Tech stickers*. and handover his laptop to me.\r\nMe: Woah. the design is so neat (I'm lying). But it's freaking slow man(REALLY FVCKING SLOW).\n\n* I decided to open the devTools and inspected the source code. And I can't believe what i saw.\n\n- 20+ images with 2~4mb file size\r\n- 13 unminified javascript files with variable declarations that looks like minified.\r\n- CDN's of bootstrap, foundation and semantic UI\r\n- LOTS OF FVCKING PLUGINS\n\n* I didn't told him what i saw. I just turn over the laptop to him and finish my coffee. \n\nHim: My sites are cool right? I have a lot of pending projects right now. Easy money Bruh!\r\nMe: Wow. *sips* coffee. and say goodbye to him and walkout.\n\nI FEEL BAD FOR HIS CLIENTS!", + "score": 54, + "created_time": 1505356730, + "attached_image": "", + "num_comments": 4, + "tags": [ + "friend", + "scumbag" + ], + "vote_state": 0, + "edited": false, + "rt": 1, + "user_id": 672442, + "user_username": "bepoXY", + "user_score": 757, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-7_3-1_8-1_7-1_5-1_12-2_17-2_6-2_10-5_2-73_15-52_11-3_18-2_4-1_19-1.jpg" + } + } + ] +} \ No newline at end of file From a5655cd16476db76a03d3db451a9af9d0b600984 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Sat, 16 Sep 2017 10:48:38 +0200 Subject: [PATCH 55/65] Add collabs to feed --- .../com/scorpiac/javarant/ApiEndpoint.java | 1 + .../com/scorpiac/javarant/DevRantFeed.java | 12 +++++ .../com/scorpiac/javarant/DevRantFeedIT.java | 20 ++++++++ src/test/resources/feed-collabs.json | 51 +++++++++++++++++++ 4 files changed, 84 insertions(+) create mode 100644 src/test/resources/feed-collabs.json diff --git a/src/main/java/com/scorpiac/javarant/ApiEndpoint.java b/src/main/java/com/scorpiac/javarant/ApiEndpoint.java index 553b446..580c41f 100644 --- a/src/main/java/com/scorpiac/javarant/ApiEndpoint.java +++ b/src/main/java/com/scorpiac/javarant/ApiEndpoint.java @@ -9,6 +9,7 @@ public enum ApiEndpoint { SEARCH(API_DEVRANT, "search"), WEEKLY(API_DEVRANT, "weekly-rants"), STORIES(API_DEVRANT, "story-rants"), + COLLABS(API_DEVRANT, "collabs"), // Users. USER_ID(API, "get-user-id"), USERS(API, "users"), diff --git a/src/main/java/com/scorpiac/javarant/DevRantFeed.java b/src/main/java/com/scorpiac/javarant/DevRantFeed.java index 511f875..f5292c0 100644 --- a/src/main/java/com/scorpiac/javarant/DevRantFeed.java +++ b/src/main/java/com/scorpiac/javarant/DevRantFeed.java @@ -68,4 +68,16 @@ public Result> getStories(Sort sort, int skip) { new BasicNameValuePair("skip", String.valueOf(skip)) ); } + + /** + * Get collabs from the feed. + * + * @param limit How many rants to get. + * @return Collabs from the feed. + */ + public Result> getCollabs(int limit) { + return devRant.getRequestHandler().get(ApiEndpoint.COLLABS, RantsFeedResponse.class, + new BasicNameValuePair("limit", String.valueOf(limit)) + ); + } } diff --git a/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java b/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java index 1853b72..fc70eb2 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java @@ -97,4 +97,24 @@ public void testGetStories() throws IOException { "mother", "web", "atom", "dreamweaver" ); } + + @Test + public void testGetCollabs() throws IOException { + server.stubFor(stubGet( + get(urlPathEqualTo(ApiEndpoint.COLLABS.toString())) + .withQueryParam("limit", equalTo("2")), + "/feed-collabs.json" + )); + + Result> result = devRant.getFeed().getCollabs(2); + assertFalse(result.getError().isPresent()); + List rants = result.getValue().get(); + + validateRant(rants.get(0), + 838945, + "Partnerships Matching App [more details]", + 12, + 6 + ); + } } diff --git a/src/test/resources/feed-collabs.json b/src/test/resources/feed-collabs.json new file mode 100644 index 0000000..9e1e2cb --- /dev/null +++ b/src/test/resources/feed-collabs.json @@ -0,0 +1,51 @@ +{ + "success": true, + "rants": [ + { + "id": 838945, + "text": "Partnerships Matching App [more details]", + "score": 12, + "created_time": 1505364058, + "attached_image": "", + "num_comments": 6, + "tags": [], + "vote_state": 0, + "edited": false, + "link": "collabs/838945/partnerships-matching-app", + "rt": 2, + "c_type": 3, + "c_type_long": "Project idea", + "user_id": 217820, + "user_username": "RuntimeError", + "user_score": 1038, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-6_16-8_3-4_8-2_7-2_5-1_12-6_17-2_6-3_10-9_2-39_15-11_18-2_4-1_19-2_20-8.jpg" + }, + "user_dpp": 1 + }, + { + "id": 826092, + "text": "ECS-Concept - a Entity Component System library in C++17/C++20, for learning purposes. [more details]", + "score": 7, + "created_time": 1504808800, + "attached_image": "", + "num_comments": 5, + "tags": [], + "vote_state": 0, + "edited": true, + "link": "collabs/826092/ecs-concept-a-entity-component-system-library-in-c-17-c-20-for-learning-purposes", + "rt": 2, + "c_type": 2, + "c_type_long": "Existing open source project", + "user_id": 518034, + "user_username": "Celes", + "user_score": 505, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-6_16-11_3-3_8-1_7-1_5-1_12-6_6-63_10-4_2-25_15-11_11-7_4-1_19-1.jpg" + }, + "user_dpp": 1 + } + ] +} \ No newline at end of file From 29b0daa9dcf44a665e86f3e1178eefb26550372d Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Sat, 16 Sep 2017 10:54:31 +0200 Subject: [PATCH 56/65] Add test for request handler --- .../javarant/services/RequestHandlerTest.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/test/java/com/scorpiac/javarant/services/RequestHandlerTest.java diff --git a/src/test/java/com/scorpiac/javarant/services/RequestHandlerTest.java b/src/test/java/com/scorpiac/javarant/services/RequestHandlerTest.java new file mode 100644 index 0000000..3d38451 --- /dev/null +++ b/src/test/java/com/scorpiac/javarant/services/RequestHandlerTest.java @@ -0,0 +1,18 @@ +package com.scorpiac.javarant.services; + +import org.testng.annotations.Test; + +import java.net.URI; + +import static org.testng.Assert.assertEquals; + +public class RequestHandlerTest { + @Test + public void testResolve() { + assertEquals( + new RequestHandler(new ObjectMapperResponseHandlerFactory(new ObjectMapperService())) + .resolve("/some-endpoint"), + URI.create("https://www.devrant.io/some-endpoint") + ); + } +} From 91bed53c0ec4ecdbb9dde1abe9505d6e2288d13a Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Sat, 16 Sep 2017 11:10:57 +0200 Subject: [PATCH 57/65] Add getCollab --- .../java/com/scorpiac/javarant/Collab.java | 9 +- .../java/com/scorpiac/javarant/DevRant.java | 10 + .../javarant/responses/CollabResponse.java | 50 + .../java/com/scorpiac/javarant/DevRantIT.java | 35 +- .../com/scorpiac/javarant/TestHelper.java | 10 + src/test/resources/collab-785714.json | 857 ++++++++++++++++++ 6 files changed, 967 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/scorpiac/javarant/responses/CollabResponse.java create mode 100644 src/test/resources/collab-785714.json diff --git a/src/main/java/com/scorpiac/javarant/Collab.java b/src/main/java/com/scorpiac/javarant/Collab.java index 25ded1b..d300d0f 100644 --- a/src/main/java/com/scorpiac/javarant/Collab.java +++ b/src/main/java/com/scorpiac/javarant/Collab.java @@ -1,11 +1,18 @@ package com.scorpiac.javarant; +import com.fasterxml.jackson.annotation.JsonProperty; + public class Collab extends CommentedRant { + @JsonProperty("c_type_long") private String projectType; - private String description; + @JsonProperty("c_tech_stack") private String techStack; + @JsonProperty("c_team_size") private String teamSize; + @JsonProperty("c_url") private String url; + @JsonProperty("c_description") + private String description; /** * Get the project type. diff --git a/src/main/java/com/scorpiac/javarant/DevRant.java b/src/main/java/com/scorpiac/javarant/DevRant.java index 395ae26..79a8603 100644 --- a/src/main/java/com/scorpiac/javarant/DevRant.java +++ b/src/main/java/com/scorpiac/javarant/DevRant.java @@ -122,6 +122,16 @@ public Result getSurprise() { return requestHandler.get(ApiEndpoint.SURPRISE, RantResponse.class); } + /** + * Get a collab. + * + * @param id The id of the collab. + * @return The collab. + */ + public Result getCollab(int id) { + return requestHandler.get(ApiEndpoint.RANTS.toString() + '/' + id, CollabResponse.class); + } + /** * Log in to devRant. When a user is already logged in, they will be logged out first. * Note that this method will clear the characters from the password array. diff --git a/src/main/java/com/scorpiac/javarant/responses/CollabResponse.java b/src/main/java/com/scorpiac/javarant/responses/CollabResponse.java new file mode 100644 index 0000000..a364899 --- /dev/null +++ b/src/main/java/com/scorpiac/javarant/responses/CollabResponse.java @@ -0,0 +1,50 @@ +package com.scorpiac.javarant.responses; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.scorpiac.javarant.Collab; +import com.scorpiac.javarant.Comment; +import com.scorpiac.javarant.CommentedRant; + +import java.lang.reflect.Field; +import java.util.List; + +public class CollabResponse extends Response { + private List comments; + + @JsonProperty + void setComments(List comments) { + this.comments = comments; + setCommentsOnRant(); + } + + @JsonProperty + void setRant(Collab collab) { + value = collab; + setCommentsOnRant(); + } + + private void setCommentsOnRant() { + // Make sure both properties are set first. + if (value == null || comments == null) { + return; + } + + // Get the comments field. + Field commentsField; + try { + commentsField = CommentedRant.class.getDeclaredField("comments"); + } catch (NoSuchFieldException e) { + // This never happens. + throw new IllegalStateException("Could not get comments field from rant.", e); + } + + // Set the comments field. + commentsField.setAccessible(true); + try { + commentsField.set(value, comments); + } catch (IllegalAccessException e) { + // This never happens. + throw new IllegalStateException("Could not set comments field on rant.", e); + } + } +} diff --git a/src/test/java/com/scorpiac/javarant/DevRantIT.java b/src/test/java/com/scorpiac/javarant/DevRantIT.java index 00a9a45..e3c8d3a 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantIT.java @@ -19,8 +19,7 @@ public void testGetRant() throws IOException { assertFalse(result.getError().isPresent()); CommentedRant rant = result.getValue().get(); - validateRant( - rant, + validateRant(rant, 686001, "I only just noticed this is on the git man page :P", 84, @@ -28,7 +27,6 @@ public void testGetRant() throws IOException { "terminal", "manual", "git" ); - assertEquals(rant.getCommentCount(), 5); assertEquals(rant.getComments().size(), 5); assertEquals(rant.getComments().get(0).getId(), 686175); @@ -147,6 +145,37 @@ public void testGetSurprise() throws IOException { ); } + @Test + public void testGetCollab() throws IOException { + server.stubFor(stubGet( + get(urlPathEqualTo(ApiEndpoint.RANTS.toString() + "/785714")), + "/collab-785714.json" + )); + + Result result = devRant.getCollab(785714); + assertFalse(result.getError().isPresent()); + Collab collab = result.getValue().get(); + + validateCollab(collab, + 785714, + "Desktop Client to Teach Programming", + 54, + 50, + "Existing project", + "", + "Java", + "6", + "github.com/some/project", + "terminal", "manual", "git" + ); + + assertEquals(collab.getComments().size(), 50); + assertEquals(collab.getComments().get(0).getId(), 785847); + assertNull(collab.getImage()); + + validateMinimalUser(collab.getUser(), 217820, "RuntimeError", 1038); + } + @Test public void testLogin() throws IOException { server.stubFor(stubPost( diff --git a/src/test/java/com/scorpiac/javarant/TestHelper.java b/src/test/java/com/scorpiac/javarant/TestHelper.java index 100e090..29f12fd 100644 --- a/src/test/java/com/scorpiac/javarant/TestHelper.java +++ b/src/test/java/com/scorpiac/javarant/TestHelper.java @@ -20,6 +20,16 @@ public void validateRant(Rant rant, int id, String text, int score, int commentC assertEquals(rant.getTags(), Arrays.asList(tags)); } + public void validateCollab(Collab collab, int id, String text, int score, int commentCount, String projectType, String description, String techStack, String teamSize, String url, String... tags) { + validateRant(collab, id, text, score, commentCount); + + assertEquals(collab.getProjectType(), projectType); + assertEquals(collab.getTechStack(), techStack); + assertEquals(collab.getDescription(), description); + assertEquals(collab.getTeamSize(), teamSize); + assertEquals(collab.getUrl(), url); + } + public void validateImage(Image image, String link, int width, int height) { assertEquals(image.getLink(), URI.create(link)); assertEquals(image.getWidth(), width); diff --git a/src/test/resources/collab-785714.json b/src/test/resources/collab-785714.json new file mode 100644 index 0000000..f143b36 --- /dev/null +++ b/src/test/resources/collab-785714.json @@ -0,0 +1,857 @@ +{ + "rant": { + "id": 785714, + "text": "Desktop Client to Teach Programming", + "score": 54, + "created_time": 1503083646, + "attached_image": "", + "num_comments": 50, + "tags": [], + "vote_state": 0, + "edited": true, + "link": "collabs/785714/desktop-client-to-teach-programming", + "rt": 2, + "c_type": 4, + "c_type_long": "Existing project", + "c_description": "", + "c_tech_stack": "Java", + "c_team_size": "6", + "c_url": "github.com/some/project", + "user_id": 217820, + "user_username": "RuntimeError", + "user_score": 1038, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-6_16-8_3-4_8-2_7-2_5-1_12-6_17-2_6-3_10-9_2-39_15-11_18-2_4-1_19-2_20-8.jpg" + }, + "user_dpp": 1 + }, + "comments": [ + { + "id": 785847, + "rant_id": 785714, + "body": "I'm in", + "score": 3, + "created_time": 1503089318, + "vote_state": 0, + "rt": 2, + "user_id": 784166, + "user_username": "rozina", + "user_score": 615, + "user_avatar": { + "b": "ecd276", + "i": "v-17_c-3_b-7_g-f_9-1_1-2_16-3_3-4_8-1_7-1_5-1_12-2_6-37_10-1_2-10_11-1_18-1_4-1_19-1.jpg" + } + }, + { + "id": 785863, + "rant_id": 785714, + "body": "I'm in to", + "score": 3, + "created_time": 1503090372, + "vote_state": 0, + "rt": 2, + "user_id": 678714, + "user_username": "Trablarer", + "user_score": 391, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-2_16-1_3-5_8-1_7-1_5-1_12-2_6-2_2-2_15-14_11-1_18-1_4-1_19-1.jpg" + } + }, + { + "id": 786067, + "rant_id": 785714, + "body": "I am in as well", + "score": 2, + "created_time": 1503099469, + "vote_state": 0, + "rt": 2, + "user_id": 627273, + "user_username": "404response", + "user_score": 2163, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-8_3-1_8-2_7-2_5-2_12-2_6-1_2-54_18-4_4-2_19-3_20-1_21-2.jpg" + }, + "user_dpp": 1 + }, + { + "id": 786070, + "rant_id": 785714, + "body": "I could make something about simple algorithms, like binary search, selection sort, quick sort , etc.", + "score": 1, + "created_time": 1503099604, + "vote_state": 0, + "rt": 2, + "user_id": 627273, + "user_username": "404response", + "user_score": 2163, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-8_3-1_8-2_7-2_5-2_12-2_6-1_2-54_18-4_4-2_19-3_20-1_21-2.jpg" + }, + "user_dpp": 1 + }, + { + "id": 786170, + "rant_id": 785714, + "body": "@404response @Trablarer @rozina alright can you guys email me with your skills at dovzhanyn.alex@gmail.com", + "score": 3, + "created_time": 1503104280, + "vote_state": 0, + "rt": 2, + "user_id": 217820, + "user_username": "RuntimeError", + "user_score": 1038, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-6_16-8_3-4_8-2_7-2_5-1_12-6_17-2_6-3_10-9_2-39_15-11_18-2_4-1_19-2_20-8.jpg" + }, + "user_dpp": 1 + }, + { + "id": 786252, + "rant_id": 785714, + "body": "I don't know how much I could contribute I know Java did a couple of projects using Java and MySQL but I would love to help if I can.", + "score": 2, + "created_time": 1503108906, + "vote_state": 0, + "rt": 2, + "user_id": 775878, + "user_username": "AkshatB", + "user_score": 64, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-2_16-6_3-3_8-1_7-1_5-1_12-2_6-2_10-1_2-10_4-1.jpg" + } + }, + { + "id": 786312, + "rant_id": 785714, + "body": "@AkshatB sure man, email me at dovzhanyn.alex@gmail.com and we can talk further. anyone else who wants to helo out, email me as well!", + "score": 2, + "created_time": 1503114463, + "vote_state": 0, + "rt": 2, + "user_id": 217820, + "user_username": "RuntimeError", + "user_score": 1038, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-6_16-8_3-4_8-2_7-2_5-1_12-6_17-2_6-3_10-9_2-39_15-11_18-2_4-1_19-2_20-8.jpg" + }, + "user_dpp": 1 + }, + { + "id": 786329, + "rant_id": 785714, + "body": "@RuntimeError I will send you an email in around 8 to 10 hours. Need to get some sleep now", + "score": 1, + "created_time": 1503115040, + "vote_state": 0, + "rt": 2, + "user_id": 627273, + "user_username": "404response", + "user_score": 2163, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-8_3-1_8-2_7-2_5-2_12-2_6-1_2-54_18-4_4-2_19-3_20-1_21-2.jpg" + }, + "user_dpp": 1 + }, + { + "id": 786761, + "rant_id": 785714, + "body": "Why not use electron for this? It'll allow you to make it really pretty :)", + "score": 3, + "created_time": 1503139494, + "vote_state": 0, + "rt": 2, + "edited": true, + "user_id": 161184, + "user_username": "Dacexi", + "user_score": 8054, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-1_16-14_3-2_8-3_7-3_5-4_12-1_6-3_10-9_2-54_11-2_4-4_19-2_21-2.jpg" + } + }, + { + "id": 787127, + "rant_id": 785714, + "body": "@Dacexi its more integrated than electron would allow for", + "score": 1, + "created_time": 1503155025, + "vote_state": 0, + "rt": 2, + "user_id": 217820, + "user_username": "RuntimeError", + "user_score": 1038, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-6_16-8_3-4_8-2_7-2_5-1_12-6_17-2_6-3_10-9_2-39_15-11_18-2_4-1_19-2_20-8.jpg" + }, + "user_dpp": 1 + }, + { + "id": 787155, + "rant_id": 785714, + "body": "@RuntimeError Sent.", + "score": 1, + "created_time": 1503156522, + "vote_state": 0, + "rt": 2, + "user_id": 775878, + "user_username": "AkshatB", + "user_score": 64, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-2_16-6_3-3_8-1_7-1_5-1_12-2_6-2_10-1_2-10_4-1.jpg" + } + }, + { + "id": 787172, + "rant_id": 785714, + "body": "@AkshatB replied :D", + "score": 2, + "created_time": 1503157311, + "vote_state": 0, + "rt": 2, + "user_id": 217820, + "user_username": "RuntimeError", + "user_score": 1038, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-6_16-8_3-4_8-2_7-2_5-1_12-6_17-2_6-3_10-9_2-39_15-11_18-2_4-1_19-2_20-8.jpg" + }, + "user_dpp": 1 + }, + { + "id": 787458, + "rant_id": 785714, + "body": "@RuntimeError I have sent you an email", + "score": 1, + "created_time": 1503167526, + "vote_state": 0, + "rt": 2, + "user_id": 627273, + "user_username": "404response", + "user_score": 2163, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-8_3-1_8-2_7-2_5-2_12-2_6-1_2-54_18-4_4-2_19-3_20-1_21-2.jpg" + }, + "user_dpp": 1 + }, + { + "id": 787663, + "rant_id": 785714, + "body": "Hey if you make it open source I'll do my best to contribute", + "score": 5, + "created_time": 1503176112, + "vote_state": 0, + "rt": 2, + "user_id": 310584, + "user_username": "dontPanic", + "user_score": 5567, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-7_3-2_8-3_7-3_5-1_12-2_6-3_10-8_2-17_11-15_18-1_4-1_19-1_21-1.jpg" + } + }, + { + "id": 787961, + "rant_id": 785714, + "body": "Iโ€™d love to help.", + "score": 1, + "created_time": 1503190874, + "vote_state": 0, + "rt": 2, + "user_id": 364293, + "user_username": "calmyourtities", + "user_score": 8090, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-2_1-2_16-13_3-2_8-4_7-4_5-3_12-2_6-3_10-8_2-92_18-4_4-3_19-3_20-8_21-1.jpg" + } + }, + { + "id": 788057, + "rant_id": 785714, + "body": "What are you thinking for front end? JavaFX?", + "score": 3, + "created_time": 1503197427, + "vote_state": 0, + "rt": 2, + "user_id": 272568, + "user_username": "ryanmhoffman", + "user_score": 4104, + "user_avatar": { + "b": "a973a2", + "i": "v-17_c-3_b-2_g-m_9-1_1-1_16-9_3-1_8-4_7-4_5-3_12-1_17-2_6-96_10-5_2-44_15-9_18-1_4-3_19-2_20-8.jpg" + } + }, + { + "id": 788096, + "rant_id": 785714, + "body": "@ryanmhoffman yeah as of right now that seems to be the modt logical option", + "score": 1, + "created_time": 1503201353, + "vote_state": 0, + "rt": 2, + "user_id": 217820, + "user_username": "RuntimeError", + "user_score": 1038, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-6_16-8_3-4_8-2_7-2_5-1_12-6_17-2_6-3_10-9_2-39_15-11_18-2_4-1_19-2_20-8.jpg" + }, + "user_dpp": 1 + }, + { + "id": 788097, + "rant_id": 785714, + "body": "@calmyourtities cool shoot me an email", + "score": 2, + "created_time": 1503201366, + "vote_state": 0, + "rt": 2, + "user_id": 217820, + "user_username": "RuntimeError", + "user_score": 1038, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-6_16-8_3-4_8-2_7-2_5-1_12-6_17-2_6-3_10-9_2-39_15-11_18-2_4-1_19-2_20-8.jpg" + }, + "user_dpp": 1 + }, + { + "id": 788098, + "rant_id": 785714, + "body": "@dontPanic i think chances are that it will be open source", + "score": 4, + "created_time": 1503201385, + "vote_state": 0, + "rt": 2, + "user_id": 217820, + "user_username": "RuntimeError", + "user_score": 1038, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-6_16-8_3-4_8-2_7-2_5-1_12-6_17-2_6-3_10-9_2-39_15-11_18-2_4-1_19-2_20-8.jpg" + }, + "user_dpp": 1 + }, + { + "id": 788382, + "rant_id": 785714, + "body": "I'm in as well! โ˜บ", + "score": 1, + "created_time": 1503220565, + "vote_state": 0, + "rt": 2, + "user_id": 12372, + "user_username": "bt141516", + "user_score": 139, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-2_3-3_8-1_7-1_5-1_12-2_6-3_10-1_2-1_11-2_4-1.jpg" + } + }, + { + "id": 788408, + "rant_id": 785714, + "body": "Just out of interest, will this be an open sourced project?", + "score": 0, + "created_time": 1503221454, + "vote_state": 0, + "rt": 2, + "user_id": 784166, + "user_username": "rozina", + "user_score": 615, + "user_avatar": { + "b": "ecd276", + "i": "v-17_c-3_b-7_g-f_9-1_1-2_16-3_3-4_8-1_7-1_5-1_12-2_6-37_10-1_2-10_11-1_18-1_4-1_19-1.jpg" + } + }, + { + "id": 788671, + "rant_id": 785714, + "body": "@rozina i imagine so, although i havent fully thought through it yet", + "score": 1, + "created_time": 1503233212, + "vote_state": 0, + "rt": 2, + "user_id": 217820, + "user_username": "RuntimeError", + "user_score": 1038, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-6_16-8_3-4_8-2_7-2_5-1_12-6_17-2_6-3_10-9_2-39_15-11_18-2_4-1_19-2_20-8.jpg" + }, + "user_dpp": 1 + }, + { + "id": 788971, + "rant_id": 785714, + "body": "Oh my God so many people. \nI think something is gonna to be big! @RuntimeError", + "score": 0, + "created_time": 1503243794, + "vote_state": 0, + "rt": 2, + "user_id": 774806, + "user_username": "itsnameless", + "user_score": 401, + "user_avatar": { + "b": "a973a2", + "i": "v-17_c-3_b-2_g-f_9-1_1-9_16-7_3-15_8-1_7-1_5-1_12-9_17-1_6-2_10-2_2-7_11-1_18-1_4-1_19-1.jpg" + } + }, + { + "id": 789159, + "rant_id": 785714, + "body": "@itsnameless ahaha yeah its exciting", + "score": 1, + "created_time": 1503253458, + "vote_state": 0, + "rt": 2, + "user_id": 217820, + "user_username": "RuntimeError", + "user_score": 1038, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-6_16-8_3-4_8-2_7-2_5-1_12-6_17-2_6-3_10-9_2-39_15-11_18-2_4-1_19-2_20-8.jpg" + }, + "user_dpp": 1 + }, + { + "id": 790369, + "rant_id": 785714, + "body": "I am in", + "score": 0, + "created_time": 1503307258, + "vote_state": 0, + "rt": 2, + "user_id": 767233, + "user_username": "engrravijain", + "user_score": 96, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-2_16-15_3-4_8-1_7-1_5-1_12-2_6-2_10-1_2-10_15-14_4-1.jpg" + } + }, + { + "id": 791222, + "rant_id": 785714, + "body": "Check out Apples \"sandbox\" app for inspiration. It's quite entertaining.", + "score": 0, + "created_time": 1503332556, + "vote_state": 0, + "rt": 2, + "user_id": 127889, + "user_username": "shdw", + "user_score": 983, + "user_avatar": { + "b": "f99a66" + } + }, + { + "id": 791243, + "rant_id": 785714, + "body": "@RuntimeError Iโ€™ve sent an email", + "score": 0, + "created_time": 1503333292, + "vote_state": 0, + "rt": 2, + "user_id": 364293, + "user_username": "calmyourtities", + "user_score": 8090, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-2_1-2_16-13_3-2_8-4_7-4_5-3_12-2_6-3_10-8_2-92_18-4_4-3_19-3_20-8_21-1.jpg" + } + }, + { + "id": 791926, + "rant_id": 785714, + "body": "Great idea! Too bad I'm not very experienced in Java...\nWait. This program uses Java. So I need Java to download other Languages? What if I am too dumb to download Java?", + "score": 0, + "created_time": 1503353768, + "vote_state": 0, + "rt": 2, + "user_id": 387696, + "user_username": "Skayo", + "user_score": 2949, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-3_16-8_3-11_8-3_7-3_5-2_12-3_6-12_10-4_2-18_18-4_4-2_19-3_21-2.jpg" + }, + "user_dpp": 1 + }, + { + "id": 791988, + "rant_id": 785714, + "body": "@Skayo donโ€™t go for programming. Or maybe the program could install JDK for you.", + "score": 1, + "created_time": 1503356701, + "vote_state": 0, + "rt": 2, + "user_id": 364293, + "user_username": "calmyourtities", + "user_score": 8090, + "user_avatar": { + "b": "7bc8a4", + "i": "v-17_c-3_b-1_g-m_9-2_1-2_16-13_3-2_8-4_7-4_5-3_12-2_6-3_10-8_2-92_18-4_4-3_19-3_20-8_21-1.jpg" + } + }, + { + "id": 792008, + "rant_id": 785714, + "body": "@calmyourtities ill add you as soon as i can!", + "score": 1, + "created_time": 1503357833, + "vote_state": 0, + "rt": 2, + "user_id": 217820, + "user_username": "RuntimeError", + "user_score": 1038, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-6_16-8_3-4_8-2_7-2_5-1_12-6_17-2_6-3_10-9_2-39_15-11_18-2_4-1_19-2_20-8.jpg" + }, + "user_dpp": 1 + }, + { + "id": 792009, + "rant_id": 785714, + "body": "@Skayo most systems have java pre installed on em :)", + "score": 1, + "created_time": 1503357848, + "vote_state": 0, + "rt": 2, + "user_id": 217820, + "user_username": "RuntimeError", + "user_score": 1038, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-6_16-8_3-4_8-2_7-2_5-1_12-6_17-2_6-3_10-9_2-39_15-11_18-2_4-1_19-2_20-8.jpg" + }, + "user_dpp": 1 + }, + { + "id": 792026, + "rant_id": 785714, + "body": "@RuntimeError could you resend me the invitation pls, I fucked up accepting the last one and then it expired", + "score": 0, + "created_time": 1503358308, + "vote_state": 0, + "rt": 2, + "user_id": 627273, + "user_username": "404response", + "user_score": 2163, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-8_3-1_8-2_7-2_5-2_12-2_6-1_2-54_18-4_4-2_19-3_20-1_21-2.jpg" + }, + "user_dpp": 1 + }, + { + "id": 792123, + "rant_id": 785714, + "body": "thanks everyone for your interest! because of the huge amount of interest you all have shown we've filled the project quickly with people who are eager to build this :)\n\nfor now, in order to not have too many people working on this and have everyone tripping over each other, im going to close off the 'admissions' until further notice.\n\nvery excited to build this, im sure we'll keep you all posted with how things are going :)", + "score": 2, + "created_time": 1503362891, + "vote_state": 0, + "rt": 2, + "user_id": 217820, + "user_username": "RuntimeError", + "user_score": 1038, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-6_16-8_3-4_8-2_7-2_5-1_12-6_17-2_6-3_10-9_2-39_15-11_18-2_4-1_19-2_20-8.jpg" + }, + "user_dpp": 1 + }, + { + "id": 792748, + "rant_id": 785714, + "body": "<@Dacexi>\nUsing JavaFX you can also build really pretty apps!", + "score": 3, + "created_time": 1503391054, + "vote_state": 0, + "rt": 2, + "user_id": 387696, + "user_username": "Skayo", + "user_score": 2949, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-3_16-8_3-11_8-3_7-3_5-2_12-3_6-12_10-4_2-18_18-4_4-2_19-3_21-2.jpg" + }, + "user_dpp": 1 + }, + { + "id": 792909, + "rant_id": 785714, + "body": "@RuntimeError Nah, Electron allows for pretty deep integration. Though I agree that Java is superior because of resource usage.", + "score": 1, + "created_time": 1503398654, + "vote_state": 0, + "rt": 2, + "edited": true, + "user_id": 397822, + "user_username": "Jop-", + "user_score": 3404, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-1_16-7_3-1_8-1_7-1_5-1_12-1_6-7_10-2_2-51_18-1_4-1_19-1.jpg" + } + }, + { + "id": 793459, + "rant_id": 785714, + "body": "So how do we do this in practice? Is there a plan? A repository?", + "score": 0, + "created_time": 1503418132, + "vote_state": 0, + "rt": 2, + "user_id": 678714, + "user_username": "Trablarer", + "user_score": 391, + "user_avatar": { + "b": "d55161", + "i": "v-17_c-3_b-5_g-m_9-1_1-2_16-1_3-5_8-1_7-1_5-1_12-2_6-2_2-2_15-14_11-1_18-1_4-1_19-1.jpg" + } + }, + { + "id": 793628, + "rant_id": 785714, + "body": "Repo plz", + "score": 1, + "created_time": 1503424527, + "vote_state": 0, + "rt": 2, + "user_id": 47060, + "user_username": "DeveloperACE", + "user_score": 4020, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-6_16-15_3-10_8-4_7-4_5-3_12-6_6-33_10-3_2-10_18-1_4-3_19-3.jpg" + } + }, + { + "id": 793650, + "rant_id": 785714, + "body": "@Trablarer @DeveloperACE as of right now, we have a private repo that were working on, but will likely make it public when we feel weve made a good amount of groundwork", + "score": 2, + "created_time": 1503425489, + "vote_state": 0, + "rt": 2, + "user_id": 217820, + "user_username": "RuntimeError", + "user_score": 1038, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-6_16-8_3-4_8-2_7-2_5-1_12-6_17-2_6-3_10-9_2-39_15-11_18-2_4-1_19-2_20-8.jpg" + }, + "user_dpp": 1 + }, + { + "id": 793704, + "rant_id": 785714, + "body": "@RuntimeError ok!", + "score": 0, + "created_time": 1503427510, + "vote_state": 0, + "rt": 2, + "user_id": 47060, + "user_username": "DeveloperACE", + "user_score": 4020, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-6_16-15_3-10_8-4_7-4_5-3_12-6_6-33_10-3_2-10_18-1_4-3_19-3.jpg" + } + }, + { + "id": 794771, + "rant_id": 785714, + "body": "Count me in.", + "score": 0, + "created_time": 1503481384, + "vote_state": 0, + "rt": 2, + "user_id": 722396, + "user_username": "GodHatesMe", + "user_score": 5529, + "user_avatar": { + "b": "ecd276", + "i": "v-17_c-3_b-7_g-m_9-1_1-2_16-15_3-2_8-1_7-1_5-3_12-2_6-98_2-8_4-3_21-2.jpg" + }, + "user_dpp": 1 + }, + { + "id": 804051, + "rant_id": 785714, + "body": "If you are looking for some visual work I'd love to contribute. I'm a media design student and could use portfolio additions ;)", + "score": 0, + "created_time": 1503881884, + "vote_state": 0, + "rt": 2, + "user_id": 734662, + "user_username": "robinofski", + "user_score": 27, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-1_1-2_16-10_3-3_8-1_7-1_5-1_12-2_6-3_10-1_2-18_15-19_4-1.jpg" + } + }, + { + "id": 804202, + "rant_id": 785714, + "body": "@robinofski you mean like photoshop designs?", + "score": 1, + "created_time": 1503895167, + "vote_state": 0, + "rt": 2, + "user_id": 217820, + "user_username": "RuntimeError", + "user_score": 1038, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-6_16-8_3-4_8-2_7-2_5-1_12-6_17-2_6-3_10-9_2-39_15-11_18-2_4-1_19-2_20-8.jpg" + }, + "user_dpp": 1 + }, + { + "id": 804339, + "rant_id": 785714, + "body": "@RuntimeError yes stuff like that.", + "score": 1, + "created_time": 1503901190, + "vote_state": 0, + "rt": 2, + "user_id": 734662, + "user_username": "robinofski", + "user_score": 27, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-1_1-2_16-10_3-3_8-1_7-1_5-1_12-2_6-3_10-1_2-18_15-19_4-1.jpg" + } + }, + { + "id": 804951, + "rant_id": 785714, + "body": "@robinofski shoot me an email bro :)", + "score": 0, + "created_time": 1503926062, + "vote_state": 0, + "rt": 2, + "user_id": 217820, + "user_username": "RuntimeError", + "user_score": 1038, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-6_16-8_3-4_8-2_7-2_5-1_12-6_17-2_6-3_10-9_2-39_15-11_18-2_4-1_19-2_20-8.jpg" + }, + "user_dpp": 1 + }, + { + "id": 804985, + "rant_id": 785714, + "body": "@RuntimeError I've put some basic info in the mail about what kind of things I do for school and how this would benefit me and your project :D", + "score": 0, + "created_time": 1503927452, + "vote_state": 0, + "rt": 2, + "user_id": 734662, + "user_username": "robinofski", + "user_score": 27, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-1_1-2_16-10_3-3_8-1_7-1_5-1_12-2_6-3_10-1_2-18_15-19_4-1.jpg" + } + }, + { + "id": 805941, + "rant_id": 785714, + "body": "Visual work on the design of the client so far, going well.", + "score": 2, + "created_time": 1503961110, + "vote_state": 0, + "rt": 2, + "user_id": 734662, + "user_username": "robinofski", + "user_score": 27, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-1_1-2_16-10_3-3_8-1_7-1_5-1_12-2_6-3_10-1_2-18_15-19_4-1.jpg" + } + }, + { + "id": 807344, + "rant_id": 785714, + "body": "I am in too!\nIs there more room for people?", + "score": 1, + "created_time": 1504023993, + "vote_state": 0, + "rt": 2, + "user_id": 686941, + "user_username": "sagarnar", + "user_score": 36, + "user_avatar": { + "b": "7bc8a4" + } + }, + { + "id": 807872, + "rant_id": 785714, + "body": "For Java programmers you should also go alittle bit into Kotlin. I would like to learn how to code in that language. :)", + "score": 2, + "created_time": 1504039483, + "vote_state": 0, + "rt": 2, + "user_id": 748695, + "user_username": "steve-white21", + "user_score": 31, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-1_1-6_16-3_3-6_8-1_7-1_5-1_12-6_6-83_10-1_2-8_15-15_4-1.jpg" + } + }, + { + "id": 808090, + "rant_id": 785714, + "body": "@sagarnar as of right now we're pretty full on people, but in the future we might open source it so people can add more languages to it", + "score": 0, + "created_time": 1504049088, + "vote_state": 0, + "rt": 2, + "user_id": 217820, + "user_username": "RuntimeError", + "user_score": 1038, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-6_16-8_3-4_8-2_7-2_5-1_12-6_17-2_6-3_10-9_2-39_15-11_18-2_4-1_19-2_20-8.jpg" + }, + "user_dpp": 1 + }, + { + "id": 811031, + "rant_id": 785714, + "body": "That escalated quickly!\n\nWould love to help but still a newbie. Good luck to you all - drop a link to source, I would love to keep an eye on it.", + "score": 2, + "created_time": 1504183663, + "vote_state": 0, + "rt": 2, + "user_id": 807252, + "user_username": "CodeKill", + "user_score": 677, + "user_avatar": { + "b": "69c9cd", + "i": "v-17_c-3_b-6_g-m_9-1_1-6_16-15_3-14_8-1_7-1_5-1_12-6_6-11_2-12_15-15_11-2_18-1_4-1_19-1.jpg" + }, + "user_dpp": 1 + } + ], + "success": true +} \ No newline at end of file From dda89fb1de5a5cc7ff0fa1065dcd81d6f3a921c2 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Sat, 16 Sep 2017 11:14:53 +0200 Subject: [PATCH 58/65] Improve internal response with comments --- .../javarant/responses/CollabResponse.java | 46 +---------------- .../responses/CommentedRantResponse.java | 45 +---------------- .../responses/ResponseWithComments.java | 49 +++++++++++++++++++ 3 files changed, 51 insertions(+), 89 deletions(-) create mode 100644 src/main/java/com/scorpiac/javarant/responses/ResponseWithComments.java diff --git a/src/main/java/com/scorpiac/javarant/responses/CollabResponse.java b/src/main/java/com/scorpiac/javarant/responses/CollabResponse.java index a364899..4e75b4e 100644 --- a/src/main/java/com/scorpiac/javarant/responses/CollabResponse.java +++ b/src/main/java/com/scorpiac/javarant/responses/CollabResponse.java @@ -1,50 +1,6 @@ package com.scorpiac.javarant.responses; -import com.fasterxml.jackson.annotation.JsonProperty; import com.scorpiac.javarant.Collab; -import com.scorpiac.javarant.Comment; -import com.scorpiac.javarant.CommentedRant; -import java.lang.reflect.Field; -import java.util.List; - -public class CollabResponse extends Response { - private List comments; - - @JsonProperty - void setComments(List comments) { - this.comments = comments; - setCommentsOnRant(); - } - - @JsonProperty - void setRant(Collab collab) { - value = collab; - setCommentsOnRant(); - } - - private void setCommentsOnRant() { - // Make sure both properties are set first. - if (value == null || comments == null) { - return; - } - - // Get the comments field. - Field commentsField; - try { - commentsField = CommentedRant.class.getDeclaredField("comments"); - } catch (NoSuchFieldException e) { - // This never happens. - throw new IllegalStateException("Could not get comments field from rant.", e); - } - - // Set the comments field. - commentsField.setAccessible(true); - try { - commentsField.set(value, comments); - } catch (IllegalAccessException e) { - // This never happens. - throw new IllegalStateException("Could not set comments field on rant.", e); - } - } +public class CollabResponse extends ResponseWithComments { } diff --git a/src/main/java/com/scorpiac/javarant/responses/CommentedRantResponse.java b/src/main/java/com/scorpiac/javarant/responses/CommentedRantResponse.java index 531a1ae..6dfcd0a 100644 --- a/src/main/java/com/scorpiac/javarant/responses/CommentedRantResponse.java +++ b/src/main/java/com/scorpiac/javarant/responses/CommentedRantResponse.java @@ -1,49 +1,6 @@ package com.scorpiac.javarant.responses; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.scorpiac.javarant.Comment; import com.scorpiac.javarant.CommentedRant; -import java.lang.reflect.Field; -import java.util.List; - -public class CommentedRantResponse extends Response { - private List comments; - - @JsonProperty - void setComments(List comments) { - this.comments = comments; - setCommentsOnRant(); - } - - @JsonProperty - void setRant(CommentedRant rant) { - value = rant; - setCommentsOnRant(); - } - - private void setCommentsOnRant() { - // Make sure both properties are set first. - if (value == null || comments == null) { - return; - } - - // Get the comments field. - Field commentsField; - try { - commentsField = value.getClass().getDeclaredField("comments"); - } catch (NoSuchFieldException e) { - // This never happens. - throw new IllegalStateException("Could not get comments field from rant.", e); - } - - // Set the comments field. - commentsField.setAccessible(true); - try { - commentsField.set(value, comments); - } catch (IllegalAccessException e) { - // This never happens. - throw new IllegalStateException("Could not set comments field on rant.", e); - } - } +public class CommentedRantResponse extends ResponseWithComments { } diff --git a/src/main/java/com/scorpiac/javarant/responses/ResponseWithComments.java b/src/main/java/com/scorpiac/javarant/responses/ResponseWithComments.java new file mode 100644 index 0000000..45a7e5f --- /dev/null +++ b/src/main/java/com/scorpiac/javarant/responses/ResponseWithComments.java @@ -0,0 +1,49 @@ +package com.scorpiac.javarant.responses; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.scorpiac.javarant.Comment; +import com.scorpiac.javarant.CommentedRant; + +import java.lang.reflect.Field; +import java.util.List; + +abstract class ResponseWithComments extends Response { + private List comments; + + @JsonProperty + void setComments(List comments) { + this.comments = comments; + setCommentsOnRant(); + } + + @JsonProperty + void setRant(T rant) { + value = rant; + setCommentsOnRant(); + } + + private void setCommentsOnRant() { + // Make sure both properties are set first. + if (value == null || comments == null) { + return; + } + + // Get the comments field. + Field commentsField; + try { + commentsField = CommentedRant.class.getDeclaredField("comments"); + } catch (NoSuchFieldException e) { + // This never happens. + throw new IllegalStateException("Could not get comments field from rant.", e); + } + + // Set the comments field. + commentsField.setAccessible(true); + try { + commentsField.set(value, comments); + } catch (IllegalAccessException e) { + // This never happens. + throw new IllegalStateException("Could not set comments field on rant.", e); + } + } +} From e80e950ba8e928b55f4275a482edbefaf4a15185 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Sat, 16 Sep 2017 11:45:05 +0200 Subject: [PATCH 59/65] Make Vote a class instead of enum, use the Reason --- src/main/java/com/scorpiac/javarant/Vote.java | 56 +++++++++++++++---- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/scorpiac/javarant/Vote.java b/src/main/java/com/scorpiac/javarant/Vote.java index 282cf00..606dd37 100644 --- a/src/main/java/com/scorpiac/javarant/Vote.java +++ b/src/main/java/com/scorpiac/javarant/Vote.java @@ -1,22 +1,56 @@ package com.scorpiac.javarant; -public enum Vote { - UP(1), - NONE(0), - DOWN(-1); +import org.apache.http.NameValuePair; +import org.apache.http.message.BasicNameValuePair; - private final int number; +import java.util.ArrayList; +import java.util.List; - Vote(int number) { - this.number = number; +public class Vote { + /** + * An upvote. + */ + public static final Vote UP = new Vote("1"); + /** + * A neutral vote. + */ + public static final Vote NONE = new Vote("0"); + + private final String value; + + private Vote(String value) { + this.value = value; } /** - * Get the number value of this vote. + * A downvote. * - * @return The number value. + * @param reason The reason for the downvote. + * @return A downvote. */ - public int getNumber() { - return number; + public static Vote DOWN(Reason reason) { + return new DownVote(reason); + } + + List getOptions() { + List options = new ArrayList<>(); + options.add(new BasicNameValuePair("vote", value)); + return options; + } + + private static class DownVote extends Vote { + private final Reason reason; + + private DownVote(Reason reason) { + super("-1"); + this.reason = reason; + } + + @Override + List getOptions() { + List options = super.getOptions(); + options.add(new BasicNameValuePair("reason", reason.toString())); + return options; + } } } From fe91af2dfea9001accb3ff392bdca327d838650b Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Sat, 16 Sep 2017 12:30:07 +0200 Subject: [PATCH 60/65] Add rant vote method --- .../com/scorpiac/javarant/ApiEndpoint.java | 1 + src/main/java/com/scorpiac/javarant/Auth.java | 15 +++++---- .../java/com/scorpiac/javarant/DevRant.java | 2 +- .../com/scorpiac/javarant/DevRantAuth.java | 23 +++++++++++++ .../com/scorpiac/javarant/RantContent.java | 6 ++-- .../java/com/scorpiac/javarant/VoteState.java | 6 ---- .../com/scorpiac/javarant/DevRantAuthIT.java | 33 +++++++++++++++++++ src/test/resources/vote-rant-up-843654.json | 30 +++++++++++++++++ 8 files changed, 100 insertions(+), 16 deletions(-) create mode 100644 src/test/java/com/scorpiac/javarant/DevRantAuthIT.java create mode 100644 src/test/resources/vote-rant-up-843654.json diff --git a/src/main/java/com/scorpiac/javarant/ApiEndpoint.java b/src/main/java/com/scorpiac/javarant/ApiEndpoint.java index 580c41f..b1f7cf3 100644 --- a/src/main/java/com/scorpiac/javarant/ApiEndpoint.java +++ b/src/main/java/com/scorpiac/javarant/ApiEndpoint.java @@ -3,6 +3,7 @@ public enum ApiEndpoint { API("/api"), API_DEVRANT(API, "devrant"), + VOTE("vote"), // Rants. RANTS(API_DEVRANT, "rants"), SURPRISE(RANTS, "surprise"), diff --git a/src/main/java/com/scorpiac/javarant/Auth.java b/src/main/java/com/scorpiac/javarant/Auth.java index 18aad84..330b0cf 100644 --- a/src/main/java/com/scorpiac/javarant/Auth.java +++ b/src/main/java/com/scorpiac/javarant/Auth.java @@ -3,12 +3,15 @@ import com.fasterxml.jackson.annotation.JsonProperty; public class Auth { - @JsonProperty - private String id; - @JsonProperty - private String key; - @JsonProperty("user_id") - private String userId; + private final String id; + private final String key; + private final String userId; + + Auth(@JsonProperty("id") String id, @JsonProperty("key") String key, @JsonProperty("user_id") String userId) { + this.id = id; + this.key = key; + this.userId = userId; + } String getId() { return id; diff --git a/src/main/java/com/scorpiac/javarant/DevRant.java b/src/main/java/com/scorpiac/javarant/DevRant.java index 79a8603..266195a 100644 --- a/src/main/java/com/scorpiac/javarant/DevRant.java +++ b/src/main/java/com/scorpiac/javarant/DevRant.java @@ -15,7 +15,7 @@ public class DevRant { private final DevRantAuth devRantAuth; private RequestHandler requestHandler; - private Auth auth; + Auth auth; static { INJECTOR = Guice.createInjector(); diff --git a/src/main/java/com/scorpiac/javarant/DevRantAuth.java b/src/main/java/com/scorpiac/javarant/DevRantAuth.java index 7a8c25e..8257425 100644 --- a/src/main/java/com/scorpiac/javarant/DevRantAuth.java +++ b/src/main/java/com/scorpiac/javarant/DevRantAuth.java @@ -1,9 +1,32 @@ package com.scorpiac.javarant; +import com.scorpiac.javarant.responses.RantResponse; +import org.apache.http.NameValuePair; +import org.apache.http.message.BasicNameValuePair; + +import java.util.List; + public class DevRantAuth { private final DevRant devRant; DevRantAuth(DevRant devRant) { this.devRant = devRant; } + + public Result voteRant(int id, Vote vote) { + return devRant.getRequestHandler().post( + ApiEndpoint.RANTS.toString() + '/' + id + '/' + ApiEndpoint.VOTE.toString(), + RantResponse.class, + getParameters(vote.getOptions()) + ); + } + + private NameValuePair[] getParameters(List params) { + // Add the auth parameters. + params.add(new BasicNameValuePair("token_id", devRant.auth.getId())); + params.add(new BasicNameValuePair("token_key", devRant.auth.getKey())); + params.add(new BasicNameValuePair("user_id", devRant.auth.getUserId())); + + return params.toArray(new NameValuePair[0]); + } } diff --git a/src/main/java/com/scorpiac/javarant/RantContent.java b/src/main/java/com/scorpiac/javarant/RantContent.java index e7150b0..8fbb400 100644 --- a/src/main/java/com/scorpiac/javarant/RantContent.java +++ b/src/main/java/com/scorpiac/javarant/RantContent.java @@ -7,8 +7,8 @@ public abstract class RantContent { private int id; @JsonProperty private int score; - @JsonProperty - private VoteState voteState = VoteState.NONE; + @JsonProperty("vote_state") + private int voteState; @JsonProperty protected String text; @JsonProperty("attached_image") @@ -68,7 +68,7 @@ public int getScore() { * @return The vote state. */ public VoteState getVoteState() { - return voteState; + return VoteState.fromValue(voteState); } /** diff --git a/src/main/java/com/scorpiac/javarant/VoteState.java b/src/main/java/com/scorpiac/javarant/VoteState.java index 2dcf44e..b252b9d 100644 --- a/src/main/java/com/scorpiac/javarant/VoteState.java +++ b/src/main/java/com/scorpiac/javarant/VoteState.java @@ -23,12 +23,6 @@ public enum VoteState { * @return The {@link VoteState} belonging to the value. */ public static VoteState fromValue(int value) { - // Clamp the value between -1 and 1. - if (value > 1) - value = 1; - if (value < -1) - value = -1; - return states.get(value); } } diff --git a/src/test/java/com/scorpiac/javarant/DevRantAuthIT.java b/src/test/java/com/scorpiac/javarant/DevRantAuthIT.java new file mode 100644 index 0000000..9b9b45d --- /dev/null +++ b/src/test/java/com/scorpiac/javarant/DevRantAuthIT.java @@ -0,0 +1,33 @@ +package com.scorpiac.javarant; + +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import java.io.IOException; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; + +public class DevRantAuthIT extends ITHelper { + private String authBody; + + @BeforeClass + public void login() { + devRant.auth = new Auth("123", "t0k3n", "456"); + authBody = "token_id=123&token_key=t0k3n&user_id=456"; + } + + @Test + public void testUpvoteRant() throws IOException { + server.stubFor(stubPost( + post(urlPathEqualTo(ApiEndpoint.RANTS.toString() + "/843654/" + ApiEndpoint.VOTE.toString())) + .withRequestBody(equalTo("vote=1&" + authBody + "&app=3&plat=3")), + "/vote-rant-up-843654.json" + )); + + Result result = devRant.getAuth().voteRant(843654, Vote.UP); + assertFalse(result.getError().isPresent()); + assertEquals(result.getValue().get().getVoteState(), VoteState.UP); + } +} diff --git a/src/test/resources/vote-rant-up-843654.json b/src/test/resources/vote-rant-up-843654.json new file mode 100644 index 0000000..843f85b --- /dev/null +++ b/src/test/resources/vote-rant-up-843654.json @@ -0,0 +1,30 @@ +{ + "success": true, + "rant": { + "id": 843654, + "text": "I guess that Devuan LXDE install was kind of a fail ๐Ÿ˜‚", + "score": 1, + "created_time": 1505551627, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_843654_FYnEy.jpg", + "width": 799, + "height": 449 + }, + "num_comments": 1, + "tags": [ + "linux", + "devuan", + "lxde" + ], + "vote_state": 1, + "edited": false, + "rt": 1, + "user_id": 749677, + "user_username": "kenogo", + "user_score": 699, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-1_1-9_16-3_3-3_8-1_7-1_5-1_12-9_6-29_10-7_2-76_18-2_4-1_19-1.jpg" + } + } +} \ No newline at end of file From f3db5bfb6433aa8fb1db0389e99963dcc7f73c54 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Sat, 16 Sep 2017 12:33:42 +0200 Subject: [PATCH 61/65] Add extra test for downvote --- .../com/scorpiac/javarant/DevRantAuthIT.java | 13 ++++++++ src/test/resources/vote-rant-down-843654.json | 30 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 src/test/resources/vote-rant-down-843654.json diff --git a/src/test/java/com/scorpiac/javarant/DevRantAuthIT.java b/src/test/java/com/scorpiac/javarant/DevRantAuthIT.java index 9b9b45d..bd6106d 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantAuthIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantAuthIT.java @@ -30,4 +30,17 @@ public void testUpvoteRant() throws IOException { assertFalse(result.getError().isPresent()); assertEquals(result.getValue().get().getVoteState(), VoteState.UP); } + + @Test + public void testDownvoteRant() throws IOException { + server.stubFor(stubPost( + post(urlPathEqualTo(ApiEndpoint.RANTS.toString() + "/843654/" + ApiEndpoint.VOTE.toString())) + .withRequestBody(equalTo("vote=-1&reason=0&" + authBody + "&app=3&plat=3")), + "/vote-rant-down-843654.json" + )); + + Result result = devRant.getAuth().voteRant(843654, Vote.DOWN(Reason.NOT_FOR_ME)); + assertFalse(result.getError().isPresent()); + assertEquals(result.getValue().get().getVoteState(), VoteState.DOWN); + } } diff --git a/src/test/resources/vote-rant-down-843654.json b/src/test/resources/vote-rant-down-843654.json new file mode 100644 index 0000000..48b4458 --- /dev/null +++ b/src/test/resources/vote-rant-down-843654.json @@ -0,0 +1,30 @@ +{ + "success": true, + "rant": { + "id": 843654, + "text": "I guess that Devuan LXDE install was kind of a fail ๐Ÿ˜‚", + "score": 1, + "created_time": 1505551627, + "attached_image": { + "url": "https://img.devrant.io/devrant/rant/r_843654_FYnEy.jpg", + "width": 799, + "height": 449 + }, + "num_comments": 1, + "tags": [ + "linux", + "devuan", + "lxde" + ], + "vote_state": -1, + "edited": false, + "rt": 1, + "user_id": 749677, + "user_username": "kenogo", + "user_score": 699, + "user_avatar": { + "b": "f99a66", + "i": "v-17_c-3_b-3_g-m_9-1_1-9_16-3_3-3_8-1_7-1_5-1_12-9_6-29_10-7_2-76_18-2_4-1_19-1.jpg" + } + } +} \ No newline at end of file From bcea56bd74136f18c691b2b2e8bfe2fbd7fcbeae Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Sat, 16 Sep 2017 12:43:47 +0200 Subject: [PATCH 62/65] Add voteComment method --- .../com/scorpiac/javarant/ApiEndpoint.java | 1 + .../com/scorpiac/javarant/DevRantAuth.java | 9 +++++++++ .../javarant/responses/CommentResponse.java | 11 +++++++++++ .../com/scorpiac/javarant/DevRantAuthIT.java | 13 +++++++++++++ .../resources/vote-comment-none-843736.json | 19 +++++++++++++++++++ 5 files changed, 53 insertions(+) create mode 100644 src/main/java/com/scorpiac/javarant/responses/CommentResponse.java create mode 100644 src/test/resources/vote-comment-none-843736.json diff --git a/src/main/java/com/scorpiac/javarant/ApiEndpoint.java b/src/main/java/com/scorpiac/javarant/ApiEndpoint.java index b1f7cf3..35a0a83 100644 --- a/src/main/java/com/scorpiac/javarant/ApiEndpoint.java +++ b/src/main/java/com/scorpiac/javarant/ApiEndpoint.java @@ -3,6 +3,7 @@ public enum ApiEndpoint { API("/api"), API_DEVRANT(API, "devrant"), + COMMENTS(API, "comments"), VOTE("vote"), // Rants. RANTS(API_DEVRANT, "rants"), diff --git a/src/main/java/com/scorpiac/javarant/DevRantAuth.java b/src/main/java/com/scorpiac/javarant/DevRantAuth.java index 8257425..da3947f 100644 --- a/src/main/java/com/scorpiac/javarant/DevRantAuth.java +++ b/src/main/java/com/scorpiac/javarant/DevRantAuth.java @@ -1,5 +1,6 @@ package com.scorpiac.javarant; +import com.scorpiac.javarant.responses.CommentResponse; import com.scorpiac.javarant.responses.RantResponse; import org.apache.http.NameValuePair; import org.apache.http.message.BasicNameValuePair; @@ -21,6 +22,14 @@ public Result voteRant(int id, Vote vote) { ); } + public Result voteComment(int id, Vote vote) { + return devRant.getRequestHandler().post( + ApiEndpoint.COMMENTS.toString() + '/' + id + '/' + ApiEndpoint.VOTE.toString(), + CommentResponse.class, + getParameters(vote.getOptions()) + ); + } + private NameValuePair[] getParameters(List params) { // Add the auth parameters. params.add(new BasicNameValuePair("token_id", devRant.auth.getId())); diff --git a/src/main/java/com/scorpiac/javarant/responses/CommentResponse.java b/src/main/java/com/scorpiac/javarant/responses/CommentResponse.java new file mode 100644 index 0000000..0457b2c --- /dev/null +++ b/src/main/java/com/scorpiac/javarant/responses/CommentResponse.java @@ -0,0 +1,11 @@ +package com.scorpiac.javarant.responses; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.scorpiac.javarant.Comment; + +public class CommentResponse extends Response { + @JsonProperty + void setComment(Comment comment) { + value = comment; + } +} diff --git a/src/test/java/com/scorpiac/javarant/DevRantAuthIT.java b/src/test/java/com/scorpiac/javarant/DevRantAuthIT.java index bd6106d..9ab14fc 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantAuthIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantAuthIT.java @@ -43,4 +43,17 @@ public void testDownvoteRant() throws IOException { assertFalse(result.getError().isPresent()); assertEquals(result.getValue().get().getVoteState(), VoteState.DOWN); } + + @Test + public void testNonevoteComment() throws IOException { + server.stubFor(stubPost( + post(urlPathEqualTo("/api/comments/843736/vote")) + .withRequestBody(equalTo("vote=0&" + authBody + "&app=3&plat=3")), + "/vote-comment-none-843736.json" + )); + + Result result = devRant.getAuth().voteComment(843736, Vote.NONE); + assertFalse(result.getError().isPresent()); + assertEquals(result.getValue().get().getVoteState(), VoteState.NONE); + } } diff --git a/src/test/resources/vote-comment-none-843736.json b/src/test/resources/vote-comment-none-843736.json new file mode 100644 index 0000000..a74d476 --- /dev/null +++ b/src/test/resources/vote-comment-none-843736.json @@ -0,0 +1,19 @@ +{ + "success": true, + "comment": { + "id": 843736, + "rant_id": 843654, + "body": "Cli|pIt can\nsave your", + "score": 0, + "created_time": 1505556099, + "vote_state": 0, + "user_id": 178022, + "user_username": "filthyranter", + "user_score": 8837, + "user_avatar": { + "b": "2a8b9d", + "i": "v-17_c-3_b-4_g-m_9-1_1-2_16-17_3-10_8-2_7-2_5-4_12-2_6-8_10-1_2-67_4-4_19-3_20-2.jpg" + }, + "user_dpp": 1 + } +} \ No newline at end of file From b50291158ba483e95c51006c3c5da2a72ca740bc Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Sat, 16 Sep 2017 12:47:52 +0200 Subject: [PATCH 63/65] Use string in urlPath matcher in tests instead of api endpoints --- .../com/scorpiac/javarant/DevRantAuthIT.java | 4 ++-- .../com/scorpiac/javarant/DevRantFeedIT.java | 8 ++++---- .../java/com/scorpiac/javarant/DevRantIT.java | 18 +++++++++--------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/test/java/com/scorpiac/javarant/DevRantAuthIT.java b/src/test/java/com/scorpiac/javarant/DevRantAuthIT.java index 9ab14fc..6bea6a9 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantAuthIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantAuthIT.java @@ -21,7 +21,7 @@ public void login() { @Test public void testUpvoteRant() throws IOException { server.stubFor(stubPost( - post(urlPathEqualTo(ApiEndpoint.RANTS.toString() + "/843654/" + ApiEndpoint.VOTE.toString())) + post(urlPathEqualTo("/api/devrant/rants/843654/vote")) .withRequestBody(equalTo("vote=1&" + authBody + "&app=3&plat=3")), "/vote-rant-up-843654.json" )); @@ -34,7 +34,7 @@ public void testUpvoteRant() throws IOException { @Test public void testDownvoteRant() throws IOException { server.stubFor(stubPost( - post(urlPathEqualTo(ApiEndpoint.RANTS.toString() + "/843654/" + ApiEndpoint.VOTE.toString())) + post(urlPathEqualTo("/api/devrant/rants/843654/vote")) .withRequestBody(equalTo("vote=-1&reason=0&" + authBody + "&app=3&plat=3")), "/vote-rant-down-843654.json" )); diff --git a/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java b/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java index fc70eb2..fd24dc6 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java @@ -13,7 +13,7 @@ public class DevRantFeedIT extends ITHelper { @Test public void testGetRants() throws IOException { server.stubFor(stubGet( - get(urlPathEqualTo(ApiEndpoint.RANTS.toString())) + get(urlPathEqualTo("/api/devrant/rants")) .withQueryParam("limit", equalTo("4")) .withQueryParam("skip", equalTo("1")) .withQueryParam("sort", equalTo("recent")), @@ -57,7 +57,7 @@ public void testSearch() throws IOException { @Test public void testGetWeekly() throws IOException { server.stubFor(stubGet( - get(urlPathEqualTo(ApiEndpoint.WEEKLY.toString())) + get(urlPathEqualTo("/api/devrant/weekly-rants")) .withQueryParam("skip", equalTo("2")) .withQueryParam("sort", equalTo("algo")), "/feed-weekly.json" @@ -79,7 +79,7 @@ public void testGetWeekly() throws IOException { @Test public void testGetStories() throws IOException { server.stubFor(stubGet( - get(urlPathEqualTo(ApiEndpoint.STORIES.toString())) + get(urlPathEqualTo("/api/devrant/story-rants")) .withQueryParam("skip", equalTo("4")) .withQueryParam("sort", equalTo("top")), "/feed-stories.json" @@ -101,7 +101,7 @@ public void testGetStories() throws IOException { @Test public void testGetCollabs() throws IOException { server.stubFor(stubGet( - get(urlPathEqualTo(ApiEndpoint.COLLABS.toString())) + get(urlPathEqualTo("/api/devrant/collabs")) .withQueryParam("limit", equalTo("2")), "/feed-collabs.json" )); diff --git a/src/test/java/com/scorpiac/javarant/DevRantIT.java b/src/test/java/com/scorpiac/javarant/DevRantIT.java index e3c8d3a..e30d5c2 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantIT.java @@ -11,7 +11,7 @@ public class DevRantIT extends ITHelper { @Test public void testGetRant() throws IOException { server.stubFor(stubGet( - get(urlPathEqualTo(ApiEndpoint.RANTS.toString() + "/686001")), + get(urlPathEqualTo("/api/devrant/rants/686001")), "/rant-686001.json" )); @@ -37,7 +37,7 @@ public void testGetRant() throws IOException { @Test public void testGetRantInvalid() throws IOException { server.stubFor(stubGet( - get(urlPathEqualTo(ApiEndpoint.RANTS.toString() + "/0")), + get(urlPathEqualTo("/api/devrant/rants/0")), "/rant-invalid.json" )); @@ -49,7 +49,7 @@ public void testGetRantInvalid() throws IOException { @Test public void testGetRantServerError() throws IOException { server.stubFor( - get(urlPathEqualTo(ApiEndpoint.RANTS.toString() + "/123456")) + get(urlPathEqualTo("/api/devrant/rants/123456")) .willReturn(serverError().withBody("An unknown error occurred.")) ); @@ -61,7 +61,7 @@ public void testGetRantServerError() throws IOException { @Test public void testGetUserByUsername() throws IOException { server.stubFor(stubGet( - get(urlPathEqualTo(ApiEndpoint.USER_ID.toString())) + get(urlPathEqualTo("/api/get-user-id")) .withQueryParam("username", equalTo("LucaScorpion")), "/user-id-LucaScorpion.json" )); @@ -93,7 +93,7 @@ public void testGetUserByUsername() throws IOException { @Test public void testGetUserByUsernameInvalid() throws IOException { server.stubFor(stubGet( - get(urlPathEqualTo(ApiEndpoint.USER_ID.toString())) + get(urlPathEqualTo("/api/get-user-id")) .withQueryParam("username", equalTo("invalid")), "/user-id-invalid.json" )); @@ -106,7 +106,7 @@ public void testGetUserByUsernameInvalid() throws IOException { @Test public void testGetUserInvalid() throws IOException { server.stubFor(stubGet( - get(urlPathEqualTo(ApiEndpoint.USERS.toString() + "/123")), + get(urlPathEqualTo("/api/users/123")), "/user-id-invalid.json" )); @@ -118,7 +118,7 @@ public void testGetUserInvalid() throws IOException { @Test public void testGetSurprise() throws IOException { server.stubFor(stubGet( - get(urlPathEqualTo(ApiEndpoint.SURPRISE.toString())), + get(urlPathEqualTo("/api/devrant/rants/surprise")), "/rant-surprise.json" )); @@ -148,7 +148,7 @@ public void testGetSurprise() throws IOException { @Test public void testGetCollab() throws IOException { server.stubFor(stubGet( - get(urlPathEqualTo(ApiEndpoint.RANTS.toString() + "/785714")), + get(urlPathEqualTo("/api/devrant/rants/785714")), "/collab-785714.json" )); @@ -179,7 +179,7 @@ public void testGetCollab() throws IOException { @Test public void testLogin() throws IOException { server.stubFor(stubPost( - post(urlPathEqualTo(ApiEndpoint.AUTH_TOKEN.toString())) + post(urlPathEqualTo("/api/users/auth-token")) .withRequestBody(equalTo("username=LucaScorpion&password=5up3r53cr3tp455w0rd&app=3&plat=3")), "/auth-token.json" )); From eefbdc38f5a1ddb7cd3111f7a2b2c5838ffc718f Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Sat, 16 Sep 2017 12:48:49 +0200 Subject: [PATCH 64/65] Add javadoc --- .../java/com/scorpiac/javarant/DevRantAuth.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/java/com/scorpiac/javarant/DevRantAuth.java b/src/main/java/com/scorpiac/javarant/DevRantAuth.java index da3947f..cf29ef2 100644 --- a/src/main/java/com/scorpiac/javarant/DevRantAuth.java +++ b/src/main/java/com/scorpiac/javarant/DevRantAuth.java @@ -14,6 +14,13 @@ public class DevRantAuth { this.devRant = devRant; } + /** + * Vote on a rant. + * + * @param id The rant to vote on. + * @param vote The vote to cast. + * @return The rant. + */ public Result voteRant(int id, Vote vote) { return devRant.getRequestHandler().post( ApiEndpoint.RANTS.toString() + '/' + id + '/' + ApiEndpoint.VOTE.toString(), @@ -22,6 +29,13 @@ public Result voteRant(int id, Vote vote) { ); } + /** + * Vote on a comment. + * + * @param id The comment to vote on. + * @param vote The vote to cast. + * @return The comment. + */ public Result voteComment(int id, Vote vote) { return devRant.getRequestHandler().post( ApiEndpoint.COMMENTS.toString() + '/' + id + '/' + ApiEndpoint.VOTE.toString(), From 53f6a153e344c638c6c56d772b16f03f86872016 Mon Sep 17 00:00:00 2001 From: Luca Scalzotto Date: Sat, 16 Sep 2017 17:29:29 +0200 Subject: [PATCH 65/65] Update readme, make error from result not optional --- README.md | 58 +++++++++++++++++-- pom.xml | 2 +- .../java/com/scorpiac/javarant/DevRant.java | 8 +-- .../com/scorpiac/javarant/DevRantFeed.java | 2 +- .../java/com/scorpiac/javarant/Result.java | 16 ++++- .../com/scorpiac/javarant/DevRantAuthIT.java | 7 ++- .../com/scorpiac/javarant/DevRantFeedIT.java | 16 ++--- .../java/com/scorpiac/javarant/DevRantIT.java | 18 +++--- 8 files changed, 93 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index bb4374d..3db1097 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ A devRant API wrapper for Java. ## Using JavaRant JavaRant is available on [Maven](http://mvnrepository.com/artifact/com.scorpiac.javarant/javarant), simply add this dependency to your `pom.xml` file: -``` +```xml com.scorpiac.javarant javarant @@ -24,15 +24,61 @@ To access devRant simply create a new `DevRant` object: DevRant devRant = new DevRant(); ``` -You can then use it to get specific rants and users, or access the feed: +Most object that are returned from `DevRant` are wrapped in a `Result` object. +This will contain an optional result value, along with an error message. +If the optional result is empty, then an error occurred and the error message will be set. +For example: + +``` +Result result = devRant.getRant(832125); + +if (!result.getValue().isPresent()) { + System.out.println("An error occurred: " + result.getError()); +} else { + CommentedRant rant = result.getValue().get(); + System.out.println(rant.getUser().getUsername() + '\n' + rant.getText()); +} +``` + +The `DevRant` class itself can be used to get specific rants and users. ``` // Get a specific rant. -Optional rant = devRant.getRant(686001); +Result rant = devRant.getRant(686001); // Get a user by username. -Optional me = devRant.getUser("LucaScorpion"); +Result me = devRant.getUser("LucaScorpion"); +``` + +The `DevRant` class contains 2 methods for getting to specific parts of the api. +First, `getFeed()` which returns a `DevRantFeed` object. +This is used to access the rant and collab feeds. + +``` +// Get the 10 latest rants. +Result> recent = devRant.getFeed().getRants(Sort.RECENT, 10, 0); + +// Get the 10 best stories. +Result> stories = devRant.getFeed().getStories(Sort.TOP, 0); + +// Get 10 collabs. +Result> collabs = devRant.getFeed().getCollabs(10); +``` + +Second, `getAuth()` which returns a `DevRantAuth` object, which is used to access user functionality. +Note that a user needs to be logged in before this can be accessed. + +``` +// Log in to devRant. +char[] password = "".toCharArray(); +devRant.login("", password); + +// Upvote a rant. +devRant.getAuth().voteRant(832125, Vote.UP); + +// Clear the vote on a comment. +devRant.getAuth().voteComment(832169, Vote.NONE); -// Get the 10 newest rants. -Optional> recent = devRant.getFeed().getRants(Sort.RECENT); +// Log out to clear the token. +devRant.logout(); ``` diff --git a/pom.xml b/pom.xml index 078a2eb..a728406 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.scorpiac.javarant javarant - 2.0.0-SNAPSHOT + 2.0.0 jar JavaRant diff --git a/src/main/java/com/scorpiac/javarant/DevRant.java b/src/main/java/com/scorpiac/javarant/DevRant.java index 266195a..7fb4dfc 100644 --- a/src/main/java/com/scorpiac/javarant/DevRant.java +++ b/src/main/java/com/scorpiac/javarant/DevRant.java @@ -81,15 +81,15 @@ public Result getRant(int id) { * @return The user. */ public Result getUser(String username) { - Result id = requestHandler.get(ApiEndpoint.USER_ID, UserIdResponse.class, new BasicNameValuePair("username", username)); + Result result = requestHandler.get(ApiEndpoint.USER_ID, UserIdResponse.class, new BasicNameValuePair("username", username)); // Check the result. - if (id.getError().isPresent() || !id.getValue().isPresent()) { + if (!result.getValue().isPresent()) { // When the username is invalid, no error message is returned by the API. return new Result<>("Invalid username specified."); } - return getUser(id.getValue().get()); + return getUser(result.getValue().get()); } /** @@ -102,7 +102,7 @@ public Result getUser(int id) { Result result = requestHandler.get(ApiEndpoint.USERS.toString() + '/' + id, UserResponse.class); // Check the result. - if (result.getError().isPresent() || !result.getValue().isPresent()) { + if (!result.getValue().isPresent()) { // When the user id is invalid, no error message is returned by the API. return new Result<>("Invalid user id specified."); } diff --git a/src/main/java/com/scorpiac/javarant/DevRantFeed.java b/src/main/java/com/scorpiac/javarant/DevRantFeed.java index f5292c0..9007ec7 100644 --- a/src/main/java/com/scorpiac/javarant/DevRantFeed.java +++ b/src/main/java/com/scorpiac/javarant/DevRantFeed.java @@ -75,7 +75,7 @@ public Result> getStories(Sort sort, int skip) { * @param limit How many rants to get. * @return Collabs from the feed. */ - public Result> getCollabs(int limit) { + public Result> getCollabs(int limit) { return devRant.getRequestHandler().get(ApiEndpoint.COLLABS, RantsFeedResponse.class, new BasicNameValuePair("limit", String.valueOf(limit)) ); diff --git a/src/main/java/com/scorpiac/javarant/Result.java b/src/main/java/com/scorpiac/javarant/Result.java index a6ad89d..9af566e 100644 --- a/src/main/java/com/scorpiac/javarant/Result.java +++ b/src/main/java/com/scorpiac/javarant/Result.java @@ -18,11 +18,23 @@ public Result(Response response) { value = response.getValue(); } + /** + * Get the result value. + * If an error occurred, this will be empty and the error will be set. + * + * @return The value. + */ public Optional getValue() { return Optional.ofNullable(value); } - public Optional getError() { - return Optional.ofNullable(error); + /** + * Get the error message. + * If there was no error, this returns {@code null}. + * + * @return The error, or {@code null} if there was none. + */ + public String getError() { + return error; } } diff --git a/src/test/java/com/scorpiac/javarant/DevRantAuthIT.java b/src/test/java/com/scorpiac/javarant/DevRantAuthIT.java index 6bea6a9..65b1115 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantAuthIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantAuthIT.java @@ -8,6 +8,7 @@ import static com.github.tomakehurst.wiremock.client.WireMock.*; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNull; public class DevRantAuthIT extends ITHelper { private String authBody; @@ -27,7 +28,7 @@ public void testUpvoteRant() throws IOException { )); Result result = devRant.getAuth().voteRant(843654, Vote.UP); - assertFalse(result.getError().isPresent()); + assertNull(result.getError()); assertEquals(result.getValue().get().getVoteState(), VoteState.UP); } @@ -40,7 +41,7 @@ public void testDownvoteRant() throws IOException { )); Result result = devRant.getAuth().voteRant(843654, Vote.DOWN(Reason.NOT_FOR_ME)); - assertFalse(result.getError().isPresent()); + assertNull(result.getError()); assertEquals(result.getValue().get().getVoteState(), VoteState.DOWN); } @@ -53,7 +54,7 @@ public void testNonevoteComment() throws IOException { )); Result result = devRant.getAuth().voteComment(843736, Vote.NONE); - assertFalse(result.getError().isPresent()); + assertNull(result.getError()); assertEquals(result.getValue().get().getVoteState(), VoteState.NONE); } } diff --git a/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java b/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java index fd24dc6..607f8a3 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java @@ -7,7 +7,7 @@ import static com.github.tomakehurst.wiremock.client.WireMock.*; import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNull; public class DevRantFeedIT extends ITHelper { @Test @@ -21,7 +21,7 @@ public void testGetRants() throws IOException { )); Result> result = devRant.getFeed().getRants(Sort.RECENT, 4, 1); - assertFalse(result.getError().isPresent()); + assertNull(result.getError()); List rants = result.getValue().get(); assertEquals(rants.size(), 4); @@ -43,7 +43,7 @@ public void testSearch() throws IOException { )); Result> result = devRant.getFeed().search("wtf"); - assertFalse(result.getError().isPresent()); + assertNull(result.getError()); validateRant(result.getValue().get().get(1), 542296, @@ -64,7 +64,7 @@ public void testGetWeekly() throws IOException { )); Result> result = devRant.getFeed().getWeekly(Sort.ALGO, 2); - assertFalse(result.getError().isPresent()); + assertNull(result.getError()); List rants = result.getValue().get(); validateRant(rants.get(0), @@ -86,7 +86,7 @@ public void testGetStories() throws IOException { )); Result> result = devRant.getFeed().getStories(Sort.TOP, 4); - assertFalse(result.getError().isPresent()); + assertNull(result.getError()); List rants = result.getValue().get(); validateRant(rants.get(0), @@ -106,9 +106,9 @@ public void testGetCollabs() throws IOException { "/feed-collabs.json" )); - Result> result = devRant.getFeed().getCollabs(2); - assertFalse(result.getError().isPresent()); - List rants = result.getValue().get(); + Result> result = devRant.getFeed().getCollabs(2); + assertNull(result.getError()); + List rants = result.getValue().get(); validateRant(rants.get(0), 838945, diff --git a/src/test/java/com/scorpiac/javarant/DevRantIT.java b/src/test/java/com/scorpiac/javarant/DevRantIT.java index e30d5c2..6c3543e 100644 --- a/src/test/java/com/scorpiac/javarant/DevRantIT.java +++ b/src/test/java/com/scorpiac/javarant/DevRantIT.java @@ -16,7 +16,7 @@ public void testGetRant() throws IOException { )); Result result = devRant.getRant(686001); - assertFalse(result.getError().isPresent()); + assertNull(result.getError()); CommentedRant rant = result.getValue().get(); validateRant(rant, @@ -43,7 +43,7 @@ public void testGetRantInvalid() throws IOException { Result result = devRant.getRant(0); assertFalse(result.getValue().isPresent()); - assertTrue(result.getError().isPresent()); + assertNotNull(result.getError()); } @Test @@ -55,7 +55,7 @@ public void testGetRantServerError() throws IOException { Result result = devRant.getRant(123456); assertFalse(result.getValue().isPresent()); - assertTrue(result.getError().isPresent()); + assertNotNull(result.getError()); } @Test @@ -71,7 +71,7 @@ public void testGetUserByUsername() throws IOException { )); Result result = devRant.getUser("LucaScorpion"); - assertFalse(result.getError().isPresent()); + assertNull(result.getError()); validateUser(result.getValue().get(), 102959, @@ -98,9 +98,9 @@ public void testGetUserByUsernameInvalid() throws IOException { "/user-id-invalid.json" )); - Result user = devRant.getUser("invalid"); - assertFalse(user.getValue().isPresent()); - assertTrue(user.getError().isPresent()); + Result result = devRant.getUser("invalid"); + assertFalse(result.getValue().isPresent()); + assertNotNull(result.getError()); } @Test @@ -112,7 +112,7 @@ public void testGetUserInvalid() throws IOException { Result result = devRant.getUser(123); assertFalse(result.getValue().isPresent()); - assertTrue(result.getError().isPresent()); + assertNotNull(result.getError()); } @Test @@ -153,7 +153,7 @@ public void testGetCollab() throws IOException { )); Result result = devRant.getCollab(785714); - assertFalse(result.getError().isPresent()); + assertNull(result.getError()); Collab collab = result.getValue().get(); validateCollab(collab,