├── .gitignore
├── CHANGELOG.md
├── Jenkinsfile
├── LICENSE.md
├── README.md
├── pom.xml
└── src
├── main
└── java
│ └── com
│ └── scorpiac
│ └── javarant
│ ├── ApiEndpoint.java
│ ├── Auth.java
│ ├── Collab.java
│ ├── Comment.java
│ ├── CommentedRant.java
│ ├── DevRant.java
│ ├── DevRantApiException.java
│ ├── DevRantAuth.java
│ ├── DevRantException.java
│ ├── DevRantFeed.java
│ ├── Image.java
│ ├── MinimalUser.java
│ ├── News.java
│ ├── NoSuchRantException.java
│ ├── NoSuchUserException.java
│ ├── NoSuchUserIdException.java
│ ├── NoSuchUsernameException.java
│ ├── Range.java
│ ├── Rant.java
│ ├── RantContent.java
│ ├── Reason.java
│ ├── Sort.java
│ ├── User.java
│ ├── Vote.java
│ ├── VoteState.java
│ ├── responses
│ ├── AbstractRantsFeedResponse.java
│ ├── AuthResponse.java
│ ├── CollabResponse.java
│ ├── CollabsFeedResponse.java
│ ├── CommentResponse.java
│ ├── CommentedRantResponse.java
│ ├── RantResponse.java
│ ├── RantsFeedResponse.java
│ ├── Response.java
│ ├── ResponseWithComments.java
│ ├── ResultsFeedResponse.java
│ ├── UserIdResponse.java
│ └── UserResponse.java
│ └── services
│ ├── ObjectMapperResponseHandlerFactory.java
│ ├── ObjectMapperService.java
│ └── RequestHandler.java
└── test
├── java
└── com
│ └── scorpiac
│ └── javarant
│ ├── DevRantAuthIT.java
│ ├── DevRantFeedIT.java
│ ├── DevRantIT.java
│ ├── DevRantSmokeTestIT.java
│ ├── DevRantTest.java
│ ├── ITHelper.java
│ ├── TestHelper.java
│ └── services
│ ├── MockRequestHandler.java
│ └── RequestHandlerTest.java
└── resources
├── auth-token.json
├── collab-785714.json
├── feed-collabs.json
├── feed-rants.json
├── feed-stories.json
├── feed-weekly.json
├── rant-686001.json
├── rant-invalid.json
├── rant-surprise.json
├── search-wtf.json
├── user-102959.json
├── user-id-LucaScorpion.json
├── user-id-invalid.json
├── user-invalid.json
├── vote-comment-none-843736.json
├── vote-rant-down-843654.json
└── vote-rant-up-843654.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # IntelliJ IDEA
2 | .idea/*
3 | *.iml
4 |
5 | # Maven
6 | target/*
7 |
8 | # Java
9 | *.class
10 | *.jar
11 |
12 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
13 | hs_err_pid*
14 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 2.1.0
4 |
5 | Add an exception model and change the return values of all API methods.
6 | All methods now simply return the value, or throw an exception if something went wrong.
7 |
8 | ## 2.0.0
9 |
10 | Initial version.
--------------------------------------------------------------------------------
/Jenkinsfile:
--------------------------------------------------------------------------------
1 | pipeline {
2 | agent any
3 |
4 | stages {
5 | stage('Clean') {
6 | steps {
7 | mvn 'clean'
8 | }
9 | }
10 | stage('Build') {
11 | steps {
12 | mvn 'install'
13 | }
14 | }
15 | stage('Results') {
16 | steps {
17 | junit '**/target/*-reports/TEST-*.xml'
18 | }
19 | }
20 | }
21 | }
22 |
23 | def mvn(String goals) {
24 | def mvnHome = tool 'Maven 3'
25 | sh "'${mvnHome}/bin/mvn' -Dmaven.test.failure.ignore -Djavarant.test.smoke $goals"
26 | }
27 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017 Scorpiac
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # JavaRant
2 | A devRant API wrapper for Java.
3 |
4 | [![Maven Central][mavenCentralShield]][mavenCentral]
5 | [![Jenkins][jenkinsBuildShield]][jenkins]
6 | [![Jenkins tests][jenkinsTestsShield]][jenkinsTests]
7 |
8 | [mavenCentralShield]: https://img.shields.io/maven-central/v/com.scorpiac.javarant/javarant.svg
9 | [jenkinsBuildShield]: https://img.shields.io/jenkins/s/https/jenkins.scorpiac.com/job/LucaScorpion/job/JavaRant/job/master/.svg
10 | [jenkinsTestsShield]: https://img.shields.io/jenkins/t/https/jenkins.scorpiac.com/job/LucaScorpion/job/JavaRant/job/master/.svg
11 | [mavenCentral]: https://mvnrepository.com/artifact/com.scorpiac.javarant/javarant
12 | [jenkins]: https://jenkins.scorpiac.com/job/LucaScorpion/job/JavaRant/
13 | [jenkinsTests]: https://jenkins.scorpiac.com/job/LucaScorpion/job/JavaRant/job/master/lastCompletedBuild/testReport/
14 |
15 | ## Maven
16 | JavaRant is available on [Maven][mavenCentral], simply add this dependency to your `pom.xml` file:
17 |
18 | ```xml
19 |
20 | com.scorpiac.javarant
21 | javarant
22 | 2.1.0
23 |
24 | ```
25 |
26 | ## Using JavaRant
27 |
28 | To access devRant simply create a new `DevRant` object:
29 |
30 | ```
31 | DevRant devRant = new DevRant();
32 | ```
33 |
34 | The `DevRant` class itself can be used to get specific rants and users.
35 |
36 | ```
37 | // Get a specific rant.
38 | CommentedRant rant = devRant.getRant(686001);
39 |
40 | // Get a user by username.
41 | User me = devRant.getUser("LucaScorpion");
42 | ```
43 |
44 | The `DevRant` class contains 2 methods for getting to specific parts of the api.
45 | First, `getFeed()` which returns a `DevRantFeed` object.
46 | This is used to access the rant and collab feeds.
47 |
48 | ```
49 | // Get the 10 latest rants.
50 | List recent = devRant.getFeed().getRants(Sort.RECENT, 10, 0);
51 |
52 | // Get the 10 best stories.
53 | List stories = devRant.getFeed().getStories(Sort.TOP, 0);
54 |
55 | // Get 10 collabs.
56 | List collabs = devRant.getFeed().getCollabs(10);
57 | ```
58 |
59 | Second, `getAuth()` which returns a `DevRantAuth` object, which is used to access user functionality.
60 | Note that a user needs to be logged in before this can be accessed.
61 |
62 | ```
63 | // Log in to devRant.
64 | char[] password = "".toCharArray();
65 | devRant.login("", password);
66 |
67 | // Upvote a rant.
68 | devRant.getAuth().voteRant(832125, Vote.UP);
69 |
70 | // Clear the vote on a comment.
71 | devRant.getAuth().voteComment(832169, Vote.NONE);
72 |
73 | // Log out to clear the token.
74 | devRant.logout();
75 | ```
76 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.scorpiac.javarant
8 | javarant
9 | 2.1.0
10 | jar
11 |
12 | JavaRant
13 | A devRant wrapper for Java.
14 | https://github.com/LucaScorpion/JavaRant
15 |
16 |
17 |
18 | MIT License
19 | https://opensource.org/licenses/MIT
20 |
21 |
22 |
23 |
24 | https://github.com/LucaScorpion/JavaRant
25 |
26 |
27 |
28 |
29 | Luca Scalzotto
30 | luca@scorpiac.com
31 | Scorpiac
32 | https://scorpiac.com
33 |
34 |
35 |
36 |
37 | 1.8
38 | UTF-8
39 | UTF-8
40 |
41 |
42 | 2.8.9
43 | 4.5.3
44 | 1.7.25
45 | 4.1.0
46 | 6.11
47 | 2.7.1
48 |
49 |
50 | 3.6.1
51 | 3.0.1
52 | 2.10.4
53 | 1.5
54 | 2.20
55 |
56 |
57 |
58 |
59 |
60 | com.fasterxml.jackson.core
61 | jackson-databind
62 | ${jackson.version}
63 |
64 |
65 |
66 | org.apache.httpcomponents
67 | fluent-hc
68 | ${fluent.version}
69 |
70 |
71 |
72 | com.google.inject
73 | guice
74 | ${guice.version}
75 |
76 |
77 |
78 |
79 |
80 |
81 | org.testng
82 | testng
83 | ${testng.version}
84 | test
85 |
86 |
87 |
88 | com.github.tomakehurst
89 | wiremock
90 | ${wiremock.version}
91 | test
92 |
93 |
94 |
95 | org.slf4j
96 | slf4j-simple
97 | ${slf4j.version}
98 | test
99 |
100 |
101 |
102 |
103 |
104 |
105 | ossrh
106 | https://oss.sonatype.org/content/repositories/snapshots
107 |
108 |
109 | ossrh
110 | https://oss.sonatype.org/service/local/staging/deploy/maven2/
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 | org.apache.maven.plugins
119 | maven-compiler-plugin
120 | ${maven.compiler.plugin.version}
121 |
122 | ${java.version}
123 | ${java.version}
124 |
125 |
126 |
127 |
128 | org.apache.maven.plugins
129 | maven-failsafe-plugin
130 | ${maven.failsafe.plugin.version}
131 |
132 |
133 | integration-tests
134 |
135 | integration-test
136 | verify
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 | release
148 |
149 |
150 | release
151 | true
152 |
153 |
154 |
155 |
156 |
157 |
158 | org.apache.maven.plugins
159 | maven-source-plugin
160 | ${maven.source.plugin.version}
161 |
162 |
163 | attach-sources
164 |
165 | jar-no-fork
166 |
167 |
168 |
169 |
170 |
171 |
172 | org.apache.maven.plugins
173 | maven-javadoc-plugin
174 | ${maven.javadoc.plugin.version}
175 |
176 |
177 | attach-javadocs
178 |
179 | jar
180 |
181 |
182 |
183 |
184 | true
185 |
186 |
187 |
188 |
189 | org.apache.maven.plugins
190 | maven-gpg-plugin
191 | ${maven.gpg.plugin.version}
192 |
193 |
194 | sign-artifacts
195 | verify
196 |
197 | sign
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/ApiEndpoint.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | public enum ApiEndpoint {
4 | API("/api"),
5 | API_DEVRANT(API, "devrant"),
6 | COMMENTS(API, "comments"),
7 | VOTE("vote"),
8 | // Rants.
9 | RANTS(API_DEVRANT, "rants"),
10 | SURPRISE(RANTS, "surprise"),
11 | SEARCH(API_DEVRANT, "search"),
12 | WEEKLY(API_DEVRANT, "weekly-rants"),
13 | STORIES(API_DEVRANT, "story-rants"),
14 | COLLABS(API_DEVRANT, "collabs"),
15 | // Users.
16 | USER_ID(API, "get-user-id"),
17 | USERS(API, "users"),
18 | AUTH_TOKEN(USERS, "auth-token");
19 |
20 | private final String endpoint;
21 |
22 | ApiEndpoint(String endpoint) {
23 | this.endpoint = endpoint;
24 | }
25 |
26 | ApiEndpoint(ApiEndpoint base, String endpoint) {
27 | this(base.toString() + '/' + endpoint);
28 | }
29 |
30 | @Override
31 | public String toString() {
32 | return endpoint;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/Auth.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 |
5 | public class Auth {
6 | private final String id;
7 | private final String key;
8 | private final String userId;
9 |
10 | Auth(@JsonProperty("id") String id, @JsonProperty("key") String key, @JsonProperty("user_id") String userId) {
11 | this.id = id;
12 | this.key = key;
13 | this.userId = userId;
14 | }
15 |
16 | String getId() {
17 | return id;
18 | }
19 |
20 | String getKey() {
21 | return key;
22 | }
23 |
24 | String getUserId() {
25 | return userId;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/Collab.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 |
5 | public class Collab extends CommentedRant {
6 | @JsonProperty("c_type_long")
7 | private String projectType;
8 | @JsonProperty("c_tech_stack")
9 | private String techStack;
10 | @JsonProperty("c_team_size")
11 | private String teamSize;
12 | @JsonProperty("c_url")
13 | private String url;
14 | @JsonProperty("c_description")
15 | private String description;
16 |
17 | /**
18 | * Get the project type.
19 | *
20 | * @return The project type.
21 | */
22 | public String getProjectType() {
23 | return projectType;
24 | }
25 |
26 | /**
27 | * Get the project description.
28 | *
29 | * @return The project description.
30 | */
31 | public String getDescription() {
32 | return description;
33 | }
34 |
35 | /**
36 | * Get the project tech stack.
37 | *
38 | * @return The project tech stack.
39 | */
40 | public String getTechStack() {
41 | return techStack;
42 | }
43 |
44 | /**
45 | * Get the team size.
46 | *
47 | * @return The team size.
48 | */
49 | public String getTeamSize() {
50 | return teamSize;
51 | }
52 |
53 | /**
54 | * Get the project url.
55 | *
56 | * @return The project url.
57 | */
58 | public String getUrl() {
59 | return url;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/Comment.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 |
5 | public class Comment extends RantContent {
6 | @Override
7 | public boolean equals(Object obj) {
8 | return super.equals(obj) && obj instanceof Comment;
9 | }
10 |
11 | // For comments the text is called "body" instead of "text".
12 | @JsonProperty("body")
13 | private void setText(String text) {
14 | this.text = text;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/CommentedRant.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 |
5 | import java.util.Collections;
6 | import java.util.List;
7 |
8 | public class CommentedRant extends Rant {
9 | @JsonProperty
10 | private List comments;
11 |
12 | /**
13 | * Get the comments on this rant.
14 | *
15 | * @return The comments.
16 | */
17 | public List getComments() {
18 | return Collections.unmodifiableList(comments);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/DevRant.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | import com.google.inject.Guice;
4 | import com.google.inject.Injector;
5 | import com.scorpiac.javarant.responses.*;
6 | import com.scorpiac.javarant.services.RequestHandler;
7 | import org.apache.http.message.BasicNameValuePair;
8 |
9 | import javax.inject.Inject;
10 |
11 | public class DevRant {
12 | private static final Injector INJECTOR;
13 |
14 | private final DevRantFeed devRantFeed;
15 | private final DevRantAuth devRantAuth;
16 |
17 | private RequestHandler requestHandler;
18 | private Auth auth;
19 |
20 | static {
21 | INJECTOR = Guice.createInjector();
22 | }
23 |
24 | /**
25 | * Create a new DevRant instance.
26 | * Each DevRant instance has their own authenticated user.
27 | */
28 | public DevRant() {
29 | INJECTOR.injectMembers(this);
30 | devRantFeed = new DevRantFeed(this);
31 | devRantAuth = new DevRantAuth(this);
32 | }
33 |
34 | @Inject
35 | void setRequestHandler(RequestHandler requestHandler) {
36 | this.requestHandler = requestHandler;
37 | }
38 |
39 | RequestHandler getRequestHandler() {
40 | return requestHandler;
41 | }
42 |
43 | Auth getAuthObject() {
44 | return auth;
45 | }
46 |
47 | void setAuthObject(Auth auth) {
48 | this.auth = auth;
49 | }
50 |
51 | /**
52 | * Access the devRant feed.
53 | *
54 | * @return A devRant feed class.
55 | */
56 | public DevRantFeed getFeed() {
57 | return devRantFeed;
58 | }
59 |
60 | /**
61 | * Access the user's authenticated devRant.
62 | * Trying to access this when the user is not logged in will throw an {@link IllegalStateException}.
63 | *
64 | * @return An authenticated devRant class.
65 | */
66 | public DevRantAuth getAuth() {
67 | // Check if the user is logged in.
68 | if (!isLoggedIn()) {
69 | throw new IllegalStateException("The user must be logged in to access the DevRantAuth.");
70 | }
71 |
72 | return devRantAuth;
73 | }
74 |
75 | /**
76 | * Get a rant.
77 | *
78 | * @param id The id of the rant.
79 | * @return The rant.
80 | */
81 | public CommentedRant getRant(int id) {
82 | return requestHandler.get(ApiEndpoint.RANTS.toString() + '/' + id, CommentedRantResponse.class)
83 | .getValue().orElseThrow(() -> new NoSuchRantException(id));
84 | }
85 |
86 | /**
87 | * Get a user by username.
88 | *
89 | * @param username The username of the user.
90 | * @return The user.
91 | */
92 | public User getUser(String username) {
93 | Integer result = requestHandler.get(ApiEndpoint.USER_ID, UserIdResponse.class, new BasicNameValuePair("username", username))
94 | .getValue().orElseThrow(() -> new NoSuchUsernameException(username));
95 |
96 | return getUser(result);
97 | }
98 |
99 | /**
100 | * Get a user.
101 | *
102 | * @param id The id of the user.
103 | * @return The user.
104 | */
105 | public User getUser(int id) {
106 | User user = requestHandler.get(ApiEndpoint.USERS.toString() + '/' + id, UserResponse.class)
107 | .getValue().orElseThrow(() -> new NoSuchUserIdException(id));
108 |
109 | // Set the id, as that is not part of the response.
110 | user.setId(id);
111 |
112 | return user;
113 | }
114 |
115 | /**
116 | * Get a random rant.
117 | *
118 | * @return A random rant.
119 | */
120 | public Rant getSurprise() {
121 | return requestHandler.get(ApiEndpoint.SURPRISE, RantResponse.class).getValueOrError();
122 | }
123 |
124 | /**
125 | * Get a collab.
126 | *
127 | * @param id The id of the collab.
128 | * @return The collab.
129 | */
130 | public Collab getCollab(int id) {
131 | return requestHandler.get(ApiEndpoint.RANTS.toString() + '/' + id, CollabResponse.class)
132 | .getValue().orElseThrow(() -> new NoSuchRantException(id));
133 | }
134 |
135 | /**
136 | * Log in to devRant. When a user is already logged in, they will be logged out first.
137 | * Note that this method will clear the characters from the password array.
138 | *
139 | * @param username The username.
140 | * @param password The password.
141 | * @return {@code true} if the user was successfully logged in, or {@code false} otherwise.
142 | */
143 | public boolean login(String username, char[] password) {
144 | logout();
145 |
146 | Auth response = requestHandler.post(ApiEndpoint.AUTH_TOKEN, AuthResponse.class,
147 | new BasicNameValuePair("username", username),
148 | new BasicNameValuePair("password", String.valueOf(password))
149 | ).getValue().orElseThrow(() -> new DevRantException("Could not log in."));
150 |
151 | // Clear the password.
152 | for (int i = 0; i < password.length; i++) {
153 | password[i] = 0;
154 | }
155 |
156 | auth = response;
157 | return isLoggedIn();
158 | }
159 |
160 | /**
161 | * Log out of devRant.
162 | */
163 | public void logout() {
164 | auth = null;
165 | }
166 |
167 | /**
168 | * Check whether a user is logged in.
169 | *
170 | * @return {@code true} if a user is logged in, or {@code false} otherwise.
171 | */
172 | public boolean isLoggedIn() {
173 | return auth != null;
174 | }
175 |
176 | /**
177 | * Set the request timeout in milliseconds, -1 meaning no timeout.
178 | *
179 | * @param timeout The timeout to use.
180 | */
181 | public void setRequestTimeout(int timeout) {
182 | requestHandler.setRequestTimeout(timeout);
183 | }
184 |
185 | /**
186 | * Get the request timeout in milliseconds.
187 | *
188 | * @return The timeout.
189 | */
190 | public int getRequestTimeout() {
191 | return requestHandler.getRequestTimeout();
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/DevRantApiException.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | public class DevRantApiException extends RuntimeException {
4 | public DevRantApiException(String message) {
5 | super("A devRant API exception occurred: " + message);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/DevRantAuth.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | import com.scorpiac.javarant.responses.CommentResponse;
4 | import com.scorpiac.javarant.responses.RantResponse;
5 | import org.apache.http.NameValuePair;
6 | import org.apache.http.message.BasicNameValuePair;
7 |
8 | import java.util.List;
9 |
10 | public class DevRantAuth {
11 | private final DevRant devRant;
12 |
13 | DevRantAuth(DevRant devRant) {
14 | this.devRant = devRant;
15 | }
16 |
17 | /**
18 | * Vote on a rant.
19 | *
20 | * @param id The rant to vote on.
21 | * @param vote The vote to cast.
22 | * @return The rant.
23 | */
24 | public Rant voteRant(int id, Vote vote) {
25 | return devRant.getRequestHandler().post(
26 | ApiEndpoint.RANTS.toString() + '/' + id + '/' + ApiEndpoint.VOTE.toString(),
27 | RantResponse.class,
28 | getParameters(vote.getOptions())
29 | ).getValueOrError();
30 | }
31 |
32 | /**
33 | * Vote on a comment.
34 | *
35 | * @param id The comment to vote on.
36 | * @param vote The vote to cast.
37 | * @return The comment.
38 | */
39 | public Comment voteComment(int id, Vote vote) {
40 | return devRant.getRequestHandler().post(
41 | ApiEndpoint.COMMENTS.toString() + '/' + id + '/' + ApiEndpoint.VOTE.toString(),
42 | CommentResponse.class,
43 | getParameters(vote.getOptions())
44 | ).getValueOrError();
45 | }
46 |
47 | private NameValuePair[] getParameters(List params) {
48 | // Add the auth parameters.
49 | params.add(new BasicNameValuePair("token_id", devRant.getAuthObject().getId()));
50 | params.add(new BasicNameValuePair("token_key", devRant.getAuthObject().getKey()));
51 | params.add(new BasicNameValuePair("user_id", devRant.getAuthObject().getUserId()));
52 |
53 | return params.toArray(new NameValuePair[0]);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/DevRantException.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | public class DevRantException extends RuntimeException {
4 | public DevRantException(String message) {
5 | super(message);
6 | }
7 |
8 | public DevRantException(String message, Throwable cause) {
9 | super(message, cause);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/DevRantFeed.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | import com.scorpiac.javarant.responses.CollabsFeedResponse;
4 | import com.scorpiac.javarant.responses.RantsFeedResponse;
5 | import com.scorpiac.javarant.responses.ResultsFeedResponse;
6 | import org.apache.http.message.BasicNameValuePair;
7 |
8 | import java.util.List;
9 |
10 | public class DevRantFeed {
11 | private final DevRant devRant;
12 |
13 | DevRantFeed(DevRant devRant) {
14 | this.devRant = devRant;
15 | }
16 |
17 | /**
18 | * Get rants from the feed.
19 | *
20 | * @param sort How to sort the feed.
21 | * @param limit How many rants to get.
22 | * @param skip How many rants to skip.
23 | * @return Rants from the feed.
24 | */
25 | public List getRants(Sort sort, int limit, int skip) {
26 | return devRant.getRequestHandler().get(ApiEndpoint.RANTS, RantsFeedResponse.class,
27 | new BasicNameValuePair("sort", sort.toString()),
28 | new BasicNameValuePair("limit", String.valueOf(limit)),
29 | new BasicNameValuePair("skip", String.valueOf(skip))
30 | ).getValueOrError();
31 | }
32 |
33 | /**
34 | * Search for rants.
35 | *
36 | * @param term The term to search for.
37 | * @return The search results.
38 | */
39 | public List search(String term) {
40 | return devRant.getRequestHandler().get(ApiEndpoint.SEARCH, ResultsFeedResponse.class,
41 | new BasicNameValuePair("term", term)
42 | ).getValueOrError();
43 | }
44 |
45 | /**
46 | * Get weekly rants from the feed.
47 | *
48 | * @param sort How to sort the feed.
49 | * @param skip How many rants to skip.
50 | * @return Weekly rants from the feed.
51 | */
52 | public List getWeekly(Sort sort, int skip) {
53 | return devRant.getRequestHandler().get(ApiEndpoint.WEEKLY, RantsFeedResponse.class,
54 | new BasicNameValuePair("sort", sort.toString()),
55 | new BasicNameValuePair("skip", String.valueOf(skip))
56 | ).getValueOrError();
57 | }
58 |
59 | /**
60 | * Get stories from the feed.
61 | *
62 | * @param sort How to sort the feed.
63 | * @param skip How many rants to skip.
64 | * @return Stories from the feed.
65 | */
66 | public List getStories(Sort sort, int skip) {
67 | return devRant.getRequestHandler().get(ApiEndpoint.STORIES, RantsFeedResponse.class,
68 | new BasicNameValuePair("sort", sort.toString()),
69 | new BasicNameValuePair("skip", String.valueOf(skip))
70 | ).getValueOrError();
71 | }
72 |
73 | /**
74 | * Get collabs from the feed.
75 | *
76 | * @param limit How many rants to get.
77 | * @return Collabs from the feed.
78 | */
79 | public List getCollabs(int limit) {
80 | return devRant.getRequestHandler().get(ApiEndpoint.COLLABS, CollabsFeedResponse.class,
81 | new BasicNameValuePair("limit", String.valueOf(limit))
82 | ).getValueOrError();
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/Image.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 |
5 | import java.net.URI;
6 |
7 | public class Image {
8 | @JsonProperty("url")
9 | private URI link;
10 | @JsonProperty("width")
11 | private int width;
12 | @JsonProperty("height")
13 | private int height;
14 |
15 | @Override
16 | public boolean equals(Object obj) {
17 | return obj instanceof Image && ((Image) obj).getLink().equals(link);
18 | }
19 |
20 | @Override
21 | public int hashCode() {
22 | return link.hashCode();
23 | }
24 |
25 | /**
26 | * Get the image link.
27 | *
28 | * @return The image link.
29 | */
30 | public URI getLink() {
31 | return link;
32 | }
33 |
34 | /**
35 | * Get the image width in pixels.
36 | *
37 | * @return The image width.
38 | */
39 | public int getWidth() {
40 | return width;
41 | }
42 |
43 | /**
44 | * Get the image height in pixels.
45 | *
46 | * @return The image height.
47 | */
48 | public int getHeight() {
49 | return height;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/MinimalUser.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 | import com.scorpiac.javarant.services.RequestHandler;
5 |
6 | import java.net.URI;
7 |
8 | public class MinimalUser {
9 | @JsonProperty
10 | private int id;
11 | @JsonProperty
12 | private String username;
13 | @JsonProperty
14 | private int score;
15 |
16 | static MinimalUser create(int id, String username, int score) {
17 | MinimalUser user = new MinimalUser();
18 | user.id = id;
19 | user.username = username;
20 | user.score = score;
21 | return user;
22 | }
23 |
24 | void setId(int id) {
25 | this.id = id;
26 | }
27 |
28 | @Override
29 | public boolean equals(Object obj) {
30 | return obj instanceof MinimalUser && ((MinimalUser) obj).getId() == id;
31 | }
32 |
33 | @Override
34 | public int hashCode() {
35 | return id;
36 | }
37 |
38 | /**
39 | * Get the user id.
40 | *
41 | * @return The user id.
42 | */
43 | public int getId() {
44 | return id;
45 | }
46 |
47 | /**
48 | * Get the username.
49 | *
50 | * @return The username.
51 | */
52 | public String getUsername() {
53 | return username;
54 | }
55 |
56 | /**
57 | * Get the user's overall score.
58 | *
59 | * @return The score.
60 | */
61 | public int getScore() {
62 | return score;
63 | }
64 |
65 | /**
66 | * Get the link to the user's profile.
67 | *
68 | * @return The link to the user's profile.
69 | */
70 | public URI getLink() {
71 | return RequestHandler.BASE_URI.resolve("/users").resolve(username);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/News.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 |
5 | public class News {
6 | @JsonProperty
7 | private int id;
8 | @JsonProperty
9 | private String headline;
10 | @JsonProperty
11 | private String body;
12 | @JsonProperty
13 | private String footer;
14 |
15 | @Override
16 | public String toString() {
17 | return headline + '\n' + body + '\n' + footer;
18 | }
19 |
20 | @Override
21 | public int hashCode() {
22 | return id;
23 | }
24 |
25 | @Override
26 | public boolean equals(Object obj) {
27 | return obj instanceof News && ((News) obj).id == id;
28 | }
29 |
30 | /**
31 | * Get the id.
32 | *
33 | * @return The id.
34 | */
35 | public int getId() {
36 | return id;
37 | }
38 |
39 | /**
40 | * Get the headline.
41 | *
42 | * @return The headline.
43 | */
44 | public String getHeadline() {
45 | return headline;
46 | }
47 |
48 | /**
49 | * Get the body text.
50 | *
51 | * @return The body.
52 | */
53 | public String getBody() {
54 | return body;
55 | }
56 |
57 | /**
58 | * Get the footer.
59 | *
60 | * @return The footer.
61 | */
62 | public String getFooter() {
63 | return footer;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/NoSuchRantException.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | public class NoSuchRantException extends RuntimeException {
4 | private final int id;
5 |
6 | public NoSuchRantException(int id) {
7 | super("A rant with id " + id + " does not exist.");
8 | this.id = id;
9 | }
10 |
11 | public int getId() {
12 | return id;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/NoSuchUserException.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | public abstract class NoSuchUserException extends RuntimeException {
4 | public NoSuchUserException(String message) {
5 | super(message);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/NoSuchUserIdException.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | public class NoSuchUserIdException extends NoSuchUserException {
4 | private final int id;
5 |
6 | public NoSuchUserIdException(int id) {
7 | super("A user with id " + id + " does not exist.");
8 | this.id = id;
9 | }
10 |
11 | public int getId() {
12 | return id;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/NoSuchUsernameException.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | public class NoSuchUsernameException extends NoSuchUserException {
4 | private final String username;
5 |
6 | public NoSuchUsernameException(String username) {
7 | super("The user '" + username + "' does not exist.");
8 | this.username = username;
9 | }
10 |
11 | public String getUsername() {
12 | return username;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/Range.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | public enum Range {
4 | DAY,
5 | WEEK,
6 | MONTH,
7 | ALL;
8 |
9 | @Override
10 | public String toString() {
11 | return name().toLowerCase();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/Rant.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 | import com.scorpiac.javarant.services.RequestHandler;
5 |
6 | import java.net.URI;
7 | import java.util.Collections;
8 | import java.util.List;
9 |
10 | public class Rant extends RantContent {
11 | @JsonProperty
12 | private List tags;
13 | @JsonProperty("num_comments")
14 | private int commentCount;
15 |
16 | @Override
17 | public boolean equals(Object obj) {
18 | return super.equals(obj) && obj instanceof Rant;
19 | }
20 |
21 | /**
22 | * Get the link to the rant.
23 | *
24 | * @return The link to the rant.
25 | */
26 | public URI getLink() {
27 | return RequestHandler.BASE_URI.resolve("/rants/" + getId());
28 | }
29 |
30 | /**
31 | * Get the tags.
32 | *
33 | * @return The tags.
34 | */
35 | public List getTags() {
36 | return Collections.unmodifiableList(tags);
37 | }
38 |
39 | /**
40 | * Get the amount of comments.
41 | *
42 | * @return The amount of comments.
43 | */
44 | public int getCommentCount() {
45 | return commentCount;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/RantContent.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 |
5 | public abstract class RantContent {
6 | @JsonProperty
7 | private int id;
8 | @JsonProperty
9 | private int score;
10 | @JsonProperty("vote_state")
11 | private int voteState;
12 | @JsonProperty
13 | protected String text;
14 | @JsonProperty("attached_image")
15 | private Image image;
16 |
17 | // Minimal user info.
18 | @JsonProperty("user_id")
19 | private int userId;
20 | @JsonProperty("user_username")
21 | private String username;
22 | @JsonProperty("user_score")
23 | private int userScore;
24 |
25 | @Override
26 | public int hashCode() {
27 | return id;
28 | }
29 |
30 | @Override
31 | public boolean equals(Object obj) {
32 | return obj instanceof RantContent && ((RantContent) obj).getId() == id;
33 | }
34 |
35 | /**
36 | * Get the id.
37 | *
38 | * @return The id.
39 | */
40 | public int getId() {
41 | return id;
42 | }
43 |
44 | /**
45 | * Get the user.
46 | * Note that this user only contains the information which was returned with the rant.
47 | * To get the complete {@link User}, use {@link DevRant#getUser}.
48 | *
49 | * @return The user.
50 | */
51 | public MinimalUser getUser() {
52 | return MinimalUser.create(userId, username, userScore);
53 | }
54 |
55 | /**
56 | * Get the score.
57 | *
58 | * @return The score.
59 | */
60 | public int getScore() {
61 | return score;
62 | }
63 |
64 | /**
65 | * Get the vote state of the authenticated user.
66 | * If no user is logged in, the vote state is always {@link VoteState#NONE}.
67 | *
68 | * @return The vote state.
69 | */
70 | public VoteState getVoteState() {
71 | return VoteState.fromValue(voteState);
72 | }
73 |
74 | /**
75 | * Get the text.
76 | *
77 | * @return The text.
78 | */
79 | public String getText() {
80 | return text;
81 | }
82 |
83 | /**
84 | * Get the image, or {@code null} if there is none.
85 | *
86 | * @return The image.
87 | */
88 | public Image getImage() {
89 | return image;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/Reason.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | public enum Reason {
4 | NOT_FOR_ME("0"),
5 | REPOST("1"),
6 | OFFENSIVE_SPAM("2");
7 |
8 | private final String value;
9 |
10 | Reason(String value) {
11 | this.value = value;
12 | }
13 |
14 | @Override
15 | public String toString() {
16 | return value;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/Sort.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | public enum Sort {
4 | ALGO,
5 | RECENT,
6 | TOP;
7 |
8 | @Override
9 | public String toString() {
10 | return name().toLowerCase();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/User.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 |
5 | import java.util.Collections;
6 | import java.util.List;
7 |
8 | public class User extends MinimalUser {
9 | @JsonProperty
10 | private String about;
11 | @JsonProperty
12 | private String location;
13 | @JsonProperty
14 | private String skills;
15 | @JsonProperty
16 | private String github;
17 | @JsonProperty
18 | private String website;
19 | @JsonProperty
20 | private UserContent content;
21 |
22 | /**
23 | * Get information about the user.
24 | *
25 | * @return About the user.
26 | */
27 | public String getAbout() {
28 | return about;
29 | }
30 |
31 | /**
32 | * Get the user's location.
33 | *
34 | * @return The location.
35 | */
36 | public String getLocation() {
37 | return location;
38 | }
39 |
40 | /**
41 | * Get the user's skills.
42 | *
43 | * @return The skills.
44 | */
45 | public String getSkills() {
46 | return skills;
47 | }
48 |
49 | /**
50 | * Get the user's GitHub username.
51 | *
52 | * @return The GitHub username.
53 | */
54 | public String getGithub() {
55 | return github;
56 | }
57 |
58 | /**
59 | * Get the user's website.
60 | *
61 | * @return The website.
62 | */
63 | public String getWebsite() {
64 | return website;
65 | }
66 |
67 | /**
68 | * Get the rants that this user posted.
69 | *
70 | * @return The posted rants.
71 | */
72 | public List getRants() {
73 | return Collections.unmodifiableList(content.content.rants);
74 | }
75 |
76 | /**
77 | * Get the rants that this user upvoted.
78 | *
79 | * @return The upvoted rants.
80 | */
81 | public List getUpvoted() {
82 | return Collections.unmodifiableList(content.content.upvoted);
83 | }
84 |
85 | /**
86 | * Get this user's comments.
87 | *
88 | * @return The posted comments.
89 | */
90 | public List getComments() {
91 | return Collections.unmodifiableList(content.content.comments);
92 | }
93 |
94 | /**
95 | * Get this user's favorites.
96 | *
97 | * @return The favorite rants.
98 | */
99 | public List getFavorites() {
100 | return Collections.unmodifiableList(content.content.favorites);
101 | }
102 |
103 | /**
104 | * Get the amount of rants that this user has posted.
105 | *
106 | * @return The amount of posted rants.
107 | */
108 | public int getRantsCount() {
109 | return content.counts.rants;
110 | }
111 |
112 | /**
113 | * Get the amount of rants that this user has upvoted.
114 | *
115 | * @return The amount of upvoted rants.
116 | */
117 | public int getUpvotedCount() {
118 | return content.counts.upvoted;
119 | }
120 |
121 | /**
122 | * Get the amount of comments that this user has posted.
123 | *
124 | * @return The amount of posted comments.
125 | */
126 | public int getCommentsCount() {
127 | return content.counts.comments;
128 | }
129 |
130 | /**
131 | * Get the amount of rants that this user has favorited.
132 | *
133 | * @return The amount of favorite rants.
134 | */
135 | public int getFavoritesCount() {
136 | return content.counts.favorites;
137 | }
138 |
139 | /**
140 | * Get the amount of collabs that this user has posted.
141 | *
142 | * @return The amount of posted collabs.
143 | */
144 | public int getCollabsCount() {
145 | return content.counts.collabs;
146 | }
147 |
148 | private static class UserContent {
149 | @JsonProperty
150 | private Content content;
151 | @JsonProperty
152 | private Counts counts;
153 | }
154 |
155 | private static class Content {
156 | @JsonProperty
157 | private List rants;
158 | @JsonProperty
159 | private List upvoted;
160 | @JsonProperty
161 | private List comments;
162 | @JsonProperty
163 | private List favorites;
164 | }
165 |
166 | private static class Counts {
167 | @JsonProperty
168 | private int rants;
169 | @JsonProperty
170 | private int upvoted;
171 | @JsonProperty
172 | private int comments;
173 | @JsonProperty
174 | private int favorites;
175 | @JsonProperty
176 | private int collabs;
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/Vote.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | import org.apache.http.NameValuePair;
4 | import org.apache.http.message.BasicNameValuePair;
5 |
6 | import java.util.ArrayList;
7 | import java.util.List;
8 |
9 | public class Vote {
10 | /**
11 | * An upvote.
12 | */
13 | public static final Vote UP = new Vote("1");
14 | /**
15 | * A neutral vote.
16 | */
17 | public static final Vote NONE = new Vote("0");
18 |
19 | private final String value;
20 |
21 | private Vote(String value) {
22 | this.value = value;
23 | }
24 |
25 | /**
26 | * A downvote.
27 | *
28 | * @param reason The reason for the downvote.
29 | * @return A downvote.
30 | */
31 | public static Vote DOWN(Reason reason) {
32 | return new DownVote(reason);
33 | }
34 |
35 | List getOptions() {
36 | List options = new ArrayList<>();
37 | options.add(new BasicNameValuePair("vote", value));
38 | return options;
39 | }
40 |
41 | private static class DownVote extends Vote {
42 | private final Reason reason;
43 |
44 | private DownVote(Reason reason) {
45 | super("-1");
46 | this.reason = reason;
47 | }
48 |
49 | @Override
50 | List getOptions() {
51 | List options = super.getOptions();
52 | options.add(new BasicNameValuePair("reason", reason.toString()));
53 | return options;
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/VoteState.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | public enum VoteState {
7 | UP,
8 | NONE,
9 | DOWN;
10 |
11 | private static final Map states = new HashMap<>();
12 |
13 | static {
14 | states.put(1, UP);
15 | states.put(0, NONE);
16 | states.put(-1, DOWN);
17 | }
18 |
19 | /**
20 | * Get the {@link VoteState} corresponding with a number value.
21 | *
22 | * @param value The value to get the state for.
23 | * @return The {@link VoteState} belonging to the value.
24 | */
25 | public static VoteState fromValue(int value) {
26 | return states.get(value);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/responses/AbstractRantsFeedResponse.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant.responses;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 | import com.scorpiac.javarant.News;
5 | import com.scorpiac.javarant.Rant;
6 |
7 | import java.util.List;
8 |
9 | abstract class AbstractRantsFeedResponse extends Response> {
10 | @JsonProperty
11 | private News news; // TODO: use this.
12 |
13 | @JsonProperty
14 | void setRants(List rants) {
15 | value = rants;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/responses/AuthResponse.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant.responses;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 | import com.scorpiac.javarant.Auth;
5 |
6 | public class AuthResponse extends Response {
7 | @JsonProperty("auth_token")
8 | void setAuth(Auth auth) {
9 | value = auth;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/responses/CollabResponse.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant.responses;
2 |
3 | import com.scorpiac.javarant.Collab;
4 |
5 | public class CollabResponse extends ResponseWithComments {
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/responses/CollabsFeedResponse.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant.responses;
2 |
3 | import com.scorpiac.javarant.Collab;
4 |
5 | public class CollabsFeedResponse extends AbstractRantsFeedResponse {
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/responses/CommentResponse.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant.responses;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 | import com.scorpiac.javarant.Comment;
5 |
6 | public class CommentResponse extends Response {
7 | @JsonProperty
8 | void setComment(Comment comment) {
9 | value = comment;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/responses/CommentedRantResponse.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant.responses;
2 |
3 | import com.scorpiac.javarant.CommentedRant;
4 |
5 | public class CommentedRantResponse extends ResponseWithComments {
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/responses/RantResponse.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant.responses;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 | import com.scorpiac.javarant.Rant;
5 |
6 | public class RantResponse extends Response {
7 | @JsonProperty
8 | void setRant(Rant rant) {
9 | value = rant;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/responses/RantsFeedResponse.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant.responses;
2 |
3 | import com.scorpiac.javarant.Rant;
4 |
5 | public class RantsFeedResponse extends AbstractRantsFeedResponse {
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/responses/Response.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant.responses;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 | import com.scorpiac.javarant.DevRantApiException;
5 |
6 | import java.util.Optional;
7 |
8 | /**
9 | * This class is used internally to create a pojo from a response.
10 | * It contains whether the request was successful, an error message and the actual result value.
11 | *
12 | * @param The type of the value the response contains.
13 | */
14 | public abstract class Response {
15 | @JsonProperty
16 | private boolean success;
17 | @JsonProperty
18 | private String error;
19 |
20 | // The actual result value.
21 | T value;
22 |
23 | /**
24 | * Get the error message.
25 | *
26 | * @return The error message.
27 | */
28 | public String getError() {
29 | return error != null ? error : "An unknown error occurred.";
30 | }
31 |
32 | /**
33 | * Get the result value.
34 | *
35 | * @return An optional containing the result value.
36 | */
37 | public Optional getValue() {
38 | return Optional.ofNullable(value);
39 | }
40 |
41 | public T getValueOrError() {
42 | if (!success) {
43 | throw new DevRantApiException(error);
44 | }
45 | return value;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/responses/ResponseWithComments.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant.responses;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 | import com.scorpiac.javarant.Comment;
5 | import com.scorpiac.javarant.CommentedRant;
6 |
7 | import java.lang.reflect.Field;
8 | import java.util.List;
9 |
10 | abstract class ResponseWithComments extends Response {
11 | private List comments;
12 |
13 | @JsonProperty
14 | void setComments(List comments) {
15 | this.comments = comments;
16 | setCommentsOnRant();
17 | }
18 |
19 | @JsonProperty
20 | void setRant(T rant) {
21 | value = rant;
22 | setCommentsOnRant();
23 | }
24 |
25 | private void setCommentsOnRant() {
26 | // Make sure both properties are set first.
27 | if (value == null || comments == null) {
28 | return;
29 | }
30 |
31 | // Get the comments field.
32 | Field commentsField;
33 | try {
34 | commentsField = CommentedRant.class.getDeclaredField("comments");
35 | } catch (NoSuchFieldException e) {
36 | // This never happens.
37 | throw new IllegalStateException("Could not get comments field from rant.", e);
38 | }
39 |
40 | // Set the comments field.
41 | commentsField.setAccessible(true);
42 | try {
43 | commentsField.set(value, comments);
44 | } catch (IllegalAccessException e) {
45 | // This never happens.
46 | throw new IllegalStateException("Could not set comments field on rant.", e);
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/responses/ResultsFeedResponse.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant.responses;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 | import com.scorpiac.javarant.Rant;
5 |
6 | import java.util.List;
7 |
8 | public class ResultsFeedResponse extends Response> {
9 | @JsonProperty
10 | void setResults(List results) {
11 | value = results;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/responses/UserIdResponse.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant.responses;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 |
5 | public class UserIdResponse extends Response {
6 | @JsonProperty("user_id")
7 | void setId(int id) {
8 | value = id;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/responses/UserResponse.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant.responses;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 | import com.scorpiac.javarant.User;
5 |
6 | public class UserResponse extends Response {
7 | @JsonProperty
8 | void setProfile(User profile) {
9 | value = profile;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/services/ObjectMapperResponseHandlerFactory.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant.services;
2 |
3 | import org.apache.http.client.ResponseHandler;
4 |
5 | import javax.inject.Inject;
6 | import javax.inject.Singleton;
7 | import java.io.InputStream;
8 | import java.util.Scanner;
9 |
10 | @Singleton
11 | class ObjectMapperResponseHandlerFactory {
12 | private final ObjectMapperService mapperService;
13 |
14 | @Inject
15 | ObjectMapperResponseHandlerFactory(ObjectMapperService mapperService) {
16 | this.mapperService = mapperService;
17 | }
18 |
19 | /**
20 | * Get a response handler which maps the response to a class.
21 | *
22 | * @param clazz The class to map the response to.
23 | * @param The type of the class to map the response to.
24 | * @return A response handler.
25 | */
26 | ResponseHandler getResponseHandler(Class clazz) {
27 | return response -> {
28 | InputStream stream = response.getEntity().getContent();
29 | String content = streamToString(stream);
30 | return mapperService.getMapper().readValue(content, clazz);
31 | };
32 | }
33 |
34 | /**
35 | * Read a stream into a string.
36 | *
37 | * @param stream The stream to read.
38 | * @return The contents of the stream as a string.
39 | */
40 | private static String streamToString(InputStream stream) {
41 | try (Scanner s = new Scanner(stream).useDelimiter("\\A")) {
42 | return s.hasNext() ? s.next() : "";
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/services/ObjectMapperService.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant.services;
2 |
3 | import com.fasterxml.jackson.core.JsonGenerator;
4 | import com.fasterxml.jackson.databind.DeserializationFeature;
5 | import com.fasterxml.jackson.databind.ObjectMapper;
6 |
7 | import javax.inject.Singleton;
8 |
9 | @Singleton
10 | class ObjectMapperService {
11 | private static final ObjectMapper MAPPER = new ObjectMapper();
12 |
13 | static {
14 | // Configure the mapper.
15 | MAPPER.configure(JsonGenerator.Feature.IGNORE_UNKNOWN, true);
16 | MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
17 | MAPPER.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
18 | }
19 |
20 | /**
21 | * Get the object mapper.
22 | *
23 | * @return The object mapper.
24 | */
25 | ObjectMapper getMapper() {
26 | return MAPPER;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/com/scorpiac/javarant/services/RequestHandler.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant.services;
2 |
3 | import com.scorpiac.javarant.ApiEndpoint;
4 | import com.scorpiac.javarant.DevRantException;
5 | import com.scorpiac.javarant.responses.Response;
6 | import org.apache.http.NameValuePair;
7 | import org.apache.http.client.fluent.Request;
8 | import org.apache.http.client.utils.URIBuilder;
9 | import org.apache.http.message.BasicNameValuePair;
10 |
11 | import javax.inject.Inject;
12 | import javax.inject.Singleton;
13 | import java.io.IOException;
14 | import java.net.URI;
15 | import java.net.URISyntaxException;
16 | import java.util.*;
17 | import java.util.function.Function;
18 | import java.util.stream.Collectors;
19 |
20 | @Singleton
21 | public class RequestHandler {
22 | public static final URI BASE_URI = URI.create("https://www.devrant.io");
23 | public static final URI AVATARS_URI = URI.create("https://avatars.devrant.io");
24 |
25 | private static final String APP_ID = "3";
26 | private static final String PLAT_ID = "3";
27 |
28 | private final ObjectMapperResponseHandlerFactory responseHandlerFactory;
29 |
30 | private int timeout = 15000;
31 |
32 | @Inject
33 | RequestHandler(ObjectMapperResponseHandlerFactory responseHandlerFactory) {
34 | this.responseHandlerFactory = responseHandlerFactory;
35 | }
36 |
37 | public > R get(ApiEndpoint endpoint, Class clazz, NameValuePair... params) {
38 | return get(endpoint.toString(), clazz, params);
39 | }
40 |
41 | public > R get(String endpoint, Class clazz, NameValuePair... params) {
42 | return handleRequest(
43 | buildRequest(endpoint, Request::Get, getParameters(params)),
44 | clazz
45 | );
46 | }
47 |
48 | public > R post(ApiEndpoint endpoint, Class clazz, NameValuePair... params) {
49 | return post(endpoint.toString(), clazz, params);
50 | }
51 |
52 | public > R post(String endpoint, Class clazz, NameValuePair... params) {
53 | return handleRequest(
54 | buildRequest(endpoint, Request::Post, Collections.emptyList()).bodyForm(getParameters(params)),
55 | clazz
56 | );
57 | }
58 |
59 | /**
60 | * Build a request.
61 | *
62 | * @param endpoint The endpoint to make the request to.
63 | * @param requestFunction The function to create a new request from a URI.
64 | * @param params The request parameters.
65 | * @return A request.
66 | */
67 | private Request buildRequest(String endpoint, Function requestFunction, List params) {
68 | URI uri;
69 |
70 | try {
71 | // Build the URI.
72 | uri = new URIBuilder(resolve(endpoint))
73 | .addParameters(params)
74 | .build();
75 | } catch (URISyntaxException e) {
76 | // This never happens.
77 | throw new IllegalArgumentException("Could not build URI.", e);
78 | }
79 |
80 | return requestFunction.apply(uri)
81 | .connectTimeout(timeout)
82 | .socketTimeout(timeout);
83 | }
84 |
85 | /**
86 | * Resolve an endpoint to a URI.
87 | * This method's main purpose is to be overridden for the tests.
88 | *
89 | * @param endpoint The endpoint to resolve.
90 | * @return A complete URI.
91 | */
92 | URI resolve(String endpoint) {
93 | return BASE_URI.resolve(endpoint);
94 | }
95 |
96 | /**
97 | * Handle a request.
98 | *
99 | * @param request The request to handle.
100 | * @param clazz The class to map the response to.
101 | * @param The type of the class to use in the result.
102 | * @param The type of the class to map the internal response to.
103 | * @return The mapped response.
104 | */
105 | // This is because of the last line, which cannot be checked. Just make sure it is tested properly.
106 | @SuppressWarnings("unchecked")
107 | private > R handleRequest(Request request, Class clazz) {
108 | R response;
109 | try {
110 | response = request.execute().handleResponse(responseHandlerFactory.getResponseHandler(clazz));
111 | } catch (IOException e) {
112 | throw new DevRantException("Failed to execute request.", e);
113 | }
114 |
115 | return response;
116 | }
117 |
118 | /**
119 | * Get a list with all the parameters, including default and auth parameters.
120 | * This also filters out any parameters that are {@code null}.
121 | *
122 | * @param params The parameters to use.
123 | * @return A list containing the given and default parameters.
124 | */
125 | private List getParameters(NameValuePair... params) {
126 | List paramList = new ArrayList<>();
127 |
128 | // Add all non-null parameters.
129 | paramList.addAll(
130 | Arrays.stream(params)
131 | .filter(Objects::nonNull)
132 | .collect(Collectors.toList())
133 | );
134 |
135 | // Add the parameters which always need to be present.
136 | paramList.add(new BasicNameValuePair("app", APP_ID));
137 | paramList.add(new BasicNameValuePair("plat", PLAT_ID));
138 |
139 | return paramList;
140 | }
141 |
142 | /**
143 | * Set the request timeout.
144 | * This timeout will be used for the socket and connection timeout.
145 | *
146 | * @param timeout The timeout in milliseconds to set, or -1 to set no timeout.
147 | */
148 | public void setRequestTimeout(int timeout) {
149 | this.timeout = timeout;
150 | }
151 |
152 | /**
153 | * Get the current request timeout in milliseconds, or -1 if there is no timeout.
154 | *
155 | * @return The request timeout.
156 | */
157 | public int getRequestTimeout() {
158 | return timeout;
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/src/test/java/com/scorpiac/javarant/DevRantAuthIT.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | import org.testng.annotations.BeforeClass;
4 | import org.testng.annotations.Test;
5 |
6 | import java.io.IOException;
7 |
8 | import static com.github.tomakehurst.wiremock.client.WireMock.*;
9 | import static org.testng.Assert.assertEquals;
10 |
11 | public class DevRantAuthIT extends ITHelper {
12 | private String authBody;
13 |
14 | @BeforeClass
15 | public void login() {
16 | devRant.setAuthObject(new Auth("123", "t0k3n", "456"));
17 | authBody = "token_id=123&token_key=t0k3n&user_id=456";
18 | }
19 |
20 | @Test
21 | public void testUpvoteRant() throws IOException {
22 | server.stubFor(stubPost(
23 | post(urlPathEqualTo("/api/devrant/rants/843654/vote"))
24 | .withRequestBody(equalTo("vote=1&" + authBody + "&app=3&plat=3")),
25 | "/vote-rant-up-843654.json"
26 | ));
27 |
28 | Rant rant = devRant.getAuth().voteRant(843654, Vote.UP);
29 | assertEquals(rant.getVoteState(), VoteState.UP);
30 | }
31 |
32 | @Test
33 | public void testDownvoteRant() throws IOException {
34 | server.stubFor(stubPost(
35 | post(urlPathEqualTo("/api/devrant/rants/843654/vote"))
36 | .withRequestBody(equalTo("vote=-1&reason=0&" + authBody + "&app=3&plat=3")),
37 | "/vote-rant-down-843654.json"
38 | ));
39 |
40 | Rant rant = devRant.getAuth().voteRant(843654, Vote.DOWN(Reason.NOT_FOR_ME));
41 | assertEquals(rant.getVoteState(), VoteState.DOWN);
42 | }
43 |
44 | @Test
45 | public void testNonevoteComment() throws IOException {
46 | server.stubFor(stubPost(
47 | post(urlPathEqualTo("/api/comments/843736/vote"))
48 | .withRequestBody(equalTo("vote=0&" + authBody + "&app=3&plat=3")),
49 | "/vote-comment-none-843736.json"
50 | ));
51 |
52 | Comment comment = devRant.getAuth().voteComment(843736, Vote.NONE);
53 | assertEquals(comment.getVoteState(), VoteState.NONE);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/test/java/com/scorpiac/javarant/DevRantFeedIT.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | import org.testng.annotations.Test;
4 |
5 | import java.io.IOException;
6 | import java.util.List;
7 |
8 | import static com.github.tomakehurst.wiremock.client.WireMock.*;
9 | import static org.testng.Assert.assertEquals;
10 |
11 | public class DevRantFeedIT extends ITHelper {
12 | @Test
13 | public void testGetRants() throws IOException {
14 | server.stubFor(stubGet(
15 | get(urlPathEqualTo("/api/devrant/rants"))
16 | .withQueryParam("limit", equalTo("4"))
17 | .withQueryParam("skip", equalTo("1"))
18 | .withQueryParam("sort", equalTo("recent")),
19 | "/feed-rants.json"
20 | ));
21 |
22 | List rants = devRant.getFeed().getRants(Sort.RECENT, 4, 1);
23 | assertEquals(rants.size(), 4);
24 | validateRant(rants.get(0),
25 | 814524,
26 | "Too real...",
27 | 1,
28 | 3,
29 | "tag one", "tag 2"
30 | );
31 | }
32 |
33 | @Test
34 | public void testSearch() throws IOException {
35 | server.stubFor(stubGet(
36 | get(urlPathEqualTo(ApiEndpoint.SEARCH.toString()))
37 | .withQueryParam("term", equalTo("wtf")),
38 | "/search-wtf.json"
39 | ));
40 |
41 | List rants = devRant.getFeed().search("wtf");
42 | validateRant(rants.get(1),
43 | 542296,
44 | "Stages of learning angular js \n1. Wtf \n2. I think I get it. \n3. Wtf",
45 | 327,
46 | 19,
47 | "javascript", "angularjs", "wtf?"
48 | );
49 | }
50 |
51 | @Test
52 | public void testGetWeekly() throws IOException {
53 | server.stubFor(stubGet(
54 | get(urlPathEqualTo("/api/devrant/weekly-rants"))
55 | .withQueryParam("skip", equalTo("2"))
56 | .withQueryParam("sort", equalTo("algo")),
57 | "/feed-weekly.json"
58 | ));
59 |
60 | List rants = devRant.getFeed().getWeekly(Sort.ALGO, 2);
61 | validateRant(rants.get(0),
62 | 843118,
63 | "My own OCR library... so far I haven't found a proper recognizer",
64 | 2,
65 | 0,
66 | "wk69"
67 | );
68 | }
69 |
70 | @Test
71 | public void testGetStories() throws IOException {
72 | server.stubFor(stubGet(
73 | get(urlPathEqualTo("/api/devrant/story-rants"))
74 | .withQueryParam("skip", equalTo("4"))
75 | .withQueryParam("sort", equalTo("top")),
76 | "/feed-stories.json"
77 | ));
78 |
79 | List rants = devRant.getFeed().getStories(Sort.TOP, 4);
80 | validateRant(rants.get(0),
81 | 830647,
82 | "",
83 | 134,
84 | 10,
85 | "mother", "web", "atom", "dreamweaver"
86 | );
87 | }
88 |
89 | @Test
90 | public void testGetCollabs() throws IOException {
91 | server.stubFor(stubGet(
92 | get(urlPathEqualTo("/api/devrant/collabs"))
93 | .withQueryParam("limit", equalTo("2")),
94 | "/feed-collabs.json"
95 | ));
96 |
97 | List rants = devRant.getFeed().getCollabs(2);
98 | validateRant(rants.get(0),
99 | 838945,
100 | "Partnerships Matching App [more details]",
101 | 12,
102 | 6
103 | );
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/test/java/com/scorpiac/javarant/DevRantIT.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | import org.testng.annotations.Test;
4 |
5 | import java.io.IOException;
6 |
7 | import static com.github.tomakehurst.wiremock.client.WireMock.*;
8 | import static org.testng.Assert.*;
9 |
10 | public class DevRantIT extends ITHelper {
11 | @Test
12 | public void testGetRant() throws IOException {
13 | server.stubFor(stubGet(
14 | get(urlPathEqualTo("/api/devrant/rants/686001")),
15 | "/rant-686001.json"
16 | ));
17 |
18 | CommentedRant rant = devRant.getRant(686001);
19 | validateRant(rant,
20 | 686001,
21 | "I only just noticed this is on the git man page :P",
22 | 84,
23 | 5,
24 | "terminal", "manual", "git"
25 | );
26 |
27 | assertEquals(rant.getComments().size(), 5);
28 | assertEquals(rant.getComments().get(0).getId(), 686175);
29 |
30 | validateImage(rant.getImage(), "https://img.devrant.io/devrant/rant/r_686001_VfN7X.jpg", 530, 134);
31 | validateMinimalUser(rant.getUser(), 102959, "LucaScorpion", 3831);
32 | }
33 |
34 | @Test(expectedExceptions = NoSuchRantException.class, expectedExceptionsMessageRegExp = ".*852.*")
35 | public void testGetRantInvalid() throws IOException {
36 | server.stubFor(stubGet(
37 | get(urlPathEqualTo("/api/devrant/rants/852")),
38 | "/rant-invalid.json"
39 | ));
40 |
41 | devRant.getRant(852);
42 | }
43 |
44 | @Test(expectedExceptions = DevRantException.class)
45 | public void testGetRantServerError() throws IOException {
46 | server.stubFor(
47 | get(urlPathEqualTo("/api/devrant/rants/123456"))
48 | .willReturn(serverError().withBody("A server error occurred."))
49 | );
50 |
51 | devRant.getRant(123456);
52 | }
53 |
54 | @Test
55 | public void testGetUserByUsername() throws IOException {
56 | server.stubFor(stubGet(
57 | get(urlPathEqualTo("/api/get-user-id"))
58 | .withQueryParam("username", equalTo("LucaScorpion")),
59 | "/user-id-LucaScorpion.json"
60 | ));
61 | server.stubFor(stubGet(
62 | get(urlPathEqualTo(ApiEndpoint.USERS.toString() + "/102959")),
63 | "/user-102959.json"
64 | ));
65 |
66 | User user = devRant.getUser("LucaScorpion");
67 | validateUser(user,
68 | 102959,
69 | "LucaScorpion",
70 | 3831,
71 | "Software developer, fanatic programmer, hardcore gamer, Linux lover.",
72 | "Netherlands",
73 | "C#, Java, PHP, Javascript, HTML, CSS, SQL, C++ (Arduino), Bash",
74 | "LucaScorpion",
75 | "https://scorpiac.com",
76 | 60,
77 | 5103,
78 | 800,
79 | 36,
80 | 0
81 | );
82 | }
83 |
84 | @Test(expectedExceptions = NoSuchUsernameException.class, expectedExceptionsMessageRegExp = ".*'not-a-name'.*")
85 | public void testGetUserByUsernameInvalid() throws IOException {
86 | server.stubFor(stubGet(
87 | get(urlPathEqualTo("/api/get-user-id"))
88 | .withQueryParam("username", equalTo("not-a-name")),
89 | "/user-id-invalid.json"
90 | ));
91 |
92 | devRant.getUser("not-a-name");
93 | }
94 |
95 | @Test(expectedExceptions = NoSuchUserIdException.class, expectedExceptionsMessageRegExp = ".*123.*")
96 | public void testGetUserInvalid() throws IOException {
97 | server.stubFor(stubGet(
98 | get(urlPathEqualTo("/api/users/123")),
99 | "/user-id-invalid.json"
100 | ));
101 |
102 | devRant.getUser(123);
103 | }
104 |
105 | @Test
106 | public void testGetSurprise() throws IOException {
107 | server.stubFor(stubGet(
108 | get(urlPathEqualTo("/api/devrant/rants/surprise")),
109 | "/rant-surprise.json"
110 | ));
111 |
112 | Rant rant = devRant.getSurprise();
113 |
114 | validateRant(rant,
115 | 26356,
116 | "Life of a software engineer.",
117 | 91,
118 | 1
119 | );
120 |
121 | validateMinimalUser(rant.getUser(),
122 | 18401,
123 | "nhpace",
124 | 218
125 | );
126 |
127 | validateImage(rant.getImage(),
128 | "https://img.devrant.io/devrant/rant/r_26356_N2S4f.jpg",
129 | 504,
130 | 381
131 | );
132 | }
133 |
134 | @Test
135 | public void testGetCollab() throws IOException {
136 | server.stubFor(stubGet(
137 | get(urlPathEqualTo("/api/devrant/rants/785714")),
138 | "/collab-785714.json"
139 | ));
140 |
141 | Collab collab = devRant.getCollab(785714);
142 |
143 | validateCollab(collab,
144 | 785714,
145 | "Desktop Client to Teach Programming",
146 | 54,
147 | 50,
148 | "Existing project",
149 | "",
150 | "Java",
151 | "6",
152 | "github.com/some/project",
153 | "terminal", "manual", "git"
154 | );
155 |
156 | assertEquals(collab.getComments().size(), 50);
157 | assertEquals(collab.getComments().get(0).getId(), 785847);
158 | assertNull(collab.getImage());
159 |
160 | validateMinimalUser(collab.getUser(), 217820, "RuntimeError", 1038);
161 | }
162 |
163 | @Test
164 | public void testLogin() throws IOException {
165 | server.stubFor(stubPost(
166 | post(urlPathEqualTo("/api/users/auth-token"))
167 | .withRequestBody(equalTo("username=LucaScorpion&password=5up3r53cr3tp455w0rd&app=3&plat=3")),
168 | "/auth-token.json"
169 | ));
170 |
171 | char[] password = "5up3r53cr3tp455w0rd".toCharArray();
172 | assertTrue(devRant.login("LucaScorpion", password));
173 | assertNotNull(devRant.getAuth());
174 |
175 | // Ensure the password array is cleared.
176 | for (char c : password) {
177 | assertEquals(c, 0);
178 | }
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/src/test/java/com/scorpiac/javarant/DevRantSmokeTestIT.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | import org.testng.SkipException;
4 | import org.testng.annotations.BeforeClass;
5 | import org.testng.annotations.Test;
6 |
7 | import java.util.UUID;
8 |
9 | import static org.testng.Assert.*;
10 |
11 | public class DevRantSmokeTestIT {
12 | private final DevRant devRant = new DevRant();
13 |
14 | @BeforeClass
15 | public void checkShouldTest() {
16 | if (System.getProperty("javarant.test.smoke") == null) {
17 | throw new SkipException("Property 'javarant.test.smoke' is not set, skipping smoke tests.");
18 | }
19 | }
20 |
21 | @Test
22 | public void testGetRant() {
23 | Rant rant = devRant.getRant(892667);
24 |
25 | assertEquals(rant.getId(), 892667);
26 | assertEquals(rant.getUser().getUsername(), "LucaScorpion");
27 | assertNotNull(rant.getImage());
28 | }
29 |
30 | @Test(expectedExceptions = NoSuchRantException.class, expectedExceptionsMessageRegExp = ".*123.*")
31 | public void testGetRantInvalid() {
32 | devRant.getRant(123);
33 | }
34 |
35 | @Test
36 | public void testGetUserByUsername() {
37 | User user = devRant.getUser("LucaScorpion");
38 |
39 | assertEquals(user.getUsername(), "LucaScorpion");
40 | assertEquals(user.getId(), 102959);
41 | assertFalse(user.getRants().isEmpty());
42 | assertTrue(user.getRantsCount() > 0);
43 | }
44 |
45 | @Test(expectedExceptions = NoSuchUsernameException.class, expectedExceptionsMessageRegExp = ".*'This-is-a-non-existing-username.*'.*")
46 | public void testGetUserByUsernameInvalid() {
47 | // Add some randomness in case someone wants to fuck this test by registering this username.
48 | devRant.getUser("This-is-a-non-existing-username-" + UUID.randomUUID().toString());
49 | }
50 |
51 | @Test(expectedExceptions = NoSuchUserIdException.class, expectedExceptionsMessageRegExp = ".*123.*")
52 | public void testGetUserInvalid() {
53 | devRant.getUser(123);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/test/java/com/scorpiac/javarant/DevRantTest.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | import org.testng.annotations.Test;
4 |
5 | import static org.testng.Assert.assertFalse;
6 | import static org.testng.Assert.assertNotNull;
7 |
8 | public class DevRantTest {
9 | @Test
10 | public void testInit() {
11 | DevRant devRant = new DevRant();
12 |
13 | assertNotNull(devRant.getFeed());
14 | assertFalse(devRant.isLoggedIn());
15 | devRant.logout();
16 | }
17 |
18 | @Test(expectedExceptions = IllegalStateException.class)
19 | public void testGetAuthNotLoggedIn() {
20 | new DevRant().getAuth();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/test/java/com/scorpiac/javarant/ITHelper.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | import com.github.tomakehurst.wiremock.WireMockServer;
4 | import com.github.tomakehurst.wiremock.client.MappingBuilder;
5 | import com.scorpiac.javarant.services.MockRequestHandler;
6 | import org.testng.annotations.AfterClass;
7 | import org.testng.annotations.AfterMethod;
8 | import org.testng.annotations.BeforeClass;
9 |
10 | import java.io.IOException;
11 | import java.util.Scanner;
12 |
13 | import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
14 | import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
15 | import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
16 |
17 | public abstract class ITHelper extends TestHelper {
18 | protected WireMockServer server;
19 | protected DevRant devRant;
20 |
21 | @BeforeClass
22 | public void beforeClass() {
23 | server = new WireMockServer(
24 | options()
25 | .dynamicPort()
26 | );
27 | server.start();
28 |
29 | devRant = new DevRant();
30 | devRant.setRequestHandler(new MockRequestHandler(server.port()));
31 | }
32 |
33 | @AfterClass
34 | public void stopServer() {
35 | server.stop();
36 | }
37 |
38 | @AfterMethod
39 | public void resetServer() {
40 | server.resetAll();
41 | }
42 |
43 | protected MappingBuilder stubGet(MappingBuilder stub, String resource) throws IOException {
44 | return stubPost(stub, resource)
45 | .withQueryParam("app", equalTo("3"))
46 | .withQueryParam("plat", equalTo("3"));
47 | }
48 |
49 | protected MappingBuilder stubPost(MappingBuilder stub, String resource) throws IOException {
50 | String responseString;
51 | try (Scanner scanner = new Scanner(getClass().getResourceAsStream(resource))) {
52 | responseString = scanner.useDelimiter("\\A").next();
53 | }
54 |
55 | return stub.willReturn(aResponse().withBody(responseString));
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/test/java/com/scorpiac/javarant/TestHelper.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant;
2 |
3 | import java.net.URI;
4 | import java.util.Arrays;
5 |
6 | import static org.testng.Assert.assertEquals;
7 |
8 | public abstract class TestHelper {
9 | public void validateRantContent(RantContent rant, int id, String text, int score) {
10 | assertEquals(rant.getId(), id);
11 | assertEquals(rant.getText(), text);
12 | assertEquals(rant.getScore(), score);
13 | }
14 |
15 | public void validateRant(Rant rant, int id, String text, int score, int commentCount, String... tags) {
16 | validateRantContent(rant, id, text, score);
17 |
18 | assertEquals(rant.getCommentCount(), commentCount);
19 | assertEquals(rant.getLink(), URI.create("https://www.devrant.io/rants/" + id));
20 | assertEquals(rant.getTags(), Arrays.asList(tags));
21 | }
22 |
23 | 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) {
24 | validateRant(collab, id, text, score, commentCount);
25 |
26 | assertEquals(collab.getProjectType(), projectType);
27 | assertEquals(collab.getTechStack(), techStack);
28 | assertEquals(collab.getDescription(), description);
29 | assertEquals(collab.getTeamSize(), teamSize);
30 | assertEquals(collab.getUrl(), url);
31 | }
32 |
33 | public void validateImage(Image image, String link, int width, int height) {
34 | assertEquals(image.getLink(), URI.create(link));
35 | assertEquals(image.getWidth(), width);
36 | assertEquals(image.getHeight(), height);
37 | }
38 |
39 | public void validateMinimalUser(MinimalUser user, int id, String username, int score) {
40 | assertEquals(user.getId(), id);
41 | assertEquals(user.getUsername(), username);
42 | assertEquals(user.getScore(), score);
43 | }
44 |
45 | public void validateUser(User user, int id, String username, int score, String about, String location, String skills, String github, String website,
46 | int rants, int upvoted, int comments, int favorites, int collabs) {
47 | validateMinimalUser(user, id, username, score);
48 |
49 | assertEquals(user.getAbout(), about);
50 | assertEquals(user.getLocation(), location);
51 | assertEquals(user.getSkills(), skills);
52 | assertEquals(user.getGithub(), github);
53 | assertEquals(user.getWebsite(), website);
54 |
55 | assertEquals(user.getRantsCount(), rants);
56 | assertEquals(user.getUpvotedCount(), upvoted);
57 | assertEquals(user.getCommentsCount(), comments);
58 | assertEquals(user.getFavoritesCount(), favorites);
59 | assertEquals(user.getCollabsCount(), collabs);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/test/java/com/scorpiac/javarant/services/MockRequestHandler.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant.services;
2 |
3 | import java.net.URI;
4 |
5 | public class MockRequestHandler extends RequestHandler {
6 | private final URI local;
7 |
8 | public MockRequestHandler(int port) {
9 | super(new ObjectMapperResponseHandlerFactory(new ObjectMapperService()));
10 | local = URI.create("http://localhost:" + port);
11 | }
12 |
13 | @Override
14 | URI resolve(String endpoint) {
15 | return local.resolve(endpoint);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/test/java/com/scorpiac/javarant/services/RequestHandlerTest.java:
--------------------------------------------------------------------------------
1 | package com.scorpiac.javarant.services;
2 |
3 | import org.testng.annotations.Test;
4 |
5 | import java.net.URI;
6 |
7 | import static org.testng.Assert.assertEquals;
8 |
9 | public class RequestHandlerTest {
10 | @Test
11 | public void testResolve() {
12 | assertEquals(
13 | new RequestHandler(new ObjectMapperResponseHandlerFactory(new ObjectMapperService()))
14 | .resolve("/some-endpoint"),
15 | URI.create("https://www.devrant.io/some-endpoint")
16 | );
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/test/resources/auth-token.json:
--------------------------------------------------------------------------------
1 | {
2 | "success": true,
3 | "auth_token": {
4 | "id": 630167,
5 | "key": "K3y#h3r3",
6 | "expire_time": 1507537328,
7 | "user_id": 102959
8 | }
9 | }
--------------------------------------------------------------------------------
/src/test/resources/collab-785714.json:
--------------------------------------------------------------------------------
1 | {
2 | "rant": {
3 | "id": 785714,
4 | "text": "Desktop Client to Teach Programming",
5 | "score": 54,
6 | "created_time": 1503083646,
7 | "attached_image": "",
8 | "num_comments": 50,
9 | "tags": [],
10 | "vote_state": 0,
11 | "edited": true,
12 | "link": "collabs/785714/desktop-client-to-teach-programming",
13 | "rt": 2,
14 | "c_type": 4,
15 | "c_type_long": "Existing project",
16 | "c_description": "",
17 | "c_tech_stack": "Java",
18 | "c_team_size": "6",
19 | "c_url": "github.com/some/project",
20 | "user_id": 217820,
21 | "user_username": "RuntimeError",
22 | "user_score": 1038,
23 | "user_avatar": {
24 | "b": "69c9cd",
25 | "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"
26 | },
27 | "user_dpp": 1
28 | },
29 | "comments": [
30 | {
31 | "id": 785847,
32 | "rant_id": 785714,
33 | "body": "I'm in",
34 | "score": 3,
35 | "created_time": 1503089318,
36 | "vote_state": 0,
37 | "rt": 2,
38 | "user_id": 784166,
39 | "user_username": "rozina",
40 | "user_score": 615,
41 | "user_avatar": {
42 | "b": "ecd276",
43 | "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"
44 | }
45 | },
46 | {
47 | "id": 785863,
48 | "rant_id": 785714,
49 | "body": "I'm in to",
50 | "score": 3,
51 | "created_time": 1503090372,
52 | "vote_state": 0,
53 | "rt": 2,
54 | "user_id": 678714,
55 | "user_username": "Trablarer",
56 | "user_score": 391,
57 | "user_avatar": {
58 | "b": "d55161",
59 | "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"
60 | }
61 | },
62 | {
63 | "id": 786067,
64 | "rant_id": 785714,
65 | "body": "I am in as well",
66 | "score": 2,
67 | "created_time": 1503099469,
68 | "vote_state": 0,
69 | "rt": 2,
70 | "user_id": 627273,
71 | "user_username": "404response",
72 | "user_score": 2163,
73 | "user_avatar": {
74 | "b": "2a8b9d",
75 | "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"
76 | },
77 | "user_dpp": 1
78 | },
79 | {
80 | "id": 786070,
81 | "rant_id": 785714,
82 | "body": "I could make something about simple algorithms, like binary search, selection sort, quick sort , etc.",
83 | "score": 1,
84 | "created_time": 1503099604,
85 | "vote_state": 0,
86 | "rt": 2,
87 | "user_id": 627273,
88 | "user_username": "404response",
89 | "user_score": 2163,
90 | "user_avatar": {
91 | "b": "2a8b9d",
92 | "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"
93 | },
94 | "user_dpp": 1
95 | },
96 | {
97 | "id": 786170,
98 | "rant_id": 785714,
99 | "body": "@404response @Trablarer @rozina alright can you guys email me with your skills at dovzhanyn.alex@gmail.com",
100 | "score": 3,
101 | "created_time": 1503104280,
102 | "vote_state": 0,
103 | "rt": 2,
104 | "user_id": 217820,
105 | "user_username": "RuntimeError",
106 | "user_score": 1038,
107 | "user_avatar": {
108 | "b": "69c9cd",
109 | "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"
110 | },
111 | "user_dpp": 1
112 | },
113 | {
114 | "id": 786252,
115 | "rant_id": 785714,
116 | "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.",
117 | "score": 2,
118 | "created_time": 1503108906,
119 | "vote_state": 0,
120 | "rt": 2,
121 | "user_id": 775878,
122 | "user_username": "AkshatB",
123 | "user_score": 64,
124 | "user_avatar": {
125 | "b": "d55161",
126 | "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"
127 | }
128 | },
129 | {
130 | "id": 786312,
131 | "rant_id": 785714,
132 | "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!",
133 | "score": 2,
134 | "created_time": 1503114463,
135 | "vote_state": 0,
136 | "rt": 2,
137 | "user_id": 217820,
138 | "user_username": "RuntimeError",
139 | "user_score": 1038,
140 | "user_avatar": {
141 | "b": "69c9cd",
142 | "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"
143 | },
144 | "user_dpp": 1
145 | },
146 | {
147 | "id": 786329,
148 | "rant_id": 785714,
149 | "body": "@RuntimeError I will send you an email in around 8 to 10 hours. Need to get some sleep now",
150 | "score": 1,
151 | "created_time": 1503115040,
152 | "vote_state": 0,
153 | "rt": 2,
154 | "user_id": 627273,
155 | "user_username": "404response",
156 | "user_score": 2163,
157 | "user_avatar": {
158 | "b": "2a8b9d",
159 | "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"
160 | },
161 | "user_dpp": 1
162 | },
163 | {
164 | "id": 786761,
165 | "rant_id": 785714,
166 | "body": "Why not use electron for this? It'll allow you to make it really pretty :)",
167 | "score": 3,
168 | "created_time": 1503139494,
169 | "vote_state": 0,
170 | "rt": 2,
171 | "edited": true,
172 | "user_id": 161184,
173 | "user_username": "Dacexi",
174 | "user_score": 8054,
175 | "user_avatar": {
176 | "b": "d55161",
177 | "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"
178 | }
179 | },
180 | {
181 | "id": 787127,
182 | "rant_id": 785714,
183 | "body": "@Dacexi its more integrated than electron would allow for",
184 | "score": 1,
185 | "created_time": 1503155025,
186 | "vote_state": 0,
187 | "rt": 2,
188 | "user_id": 217820,
189 | "user_username": "RuntimeError",
190 | "user_score": 1038,
191 | "user_avatar": {
192 | "b": "69c9cd",
193 | "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"
194 | },
195 | "user_dpp": 1
196 | },
197 | {
198 | "id": 787155,
199 | "rant_id": 785714,
200 | "body": "@RuntimeError Sent.",
201 | "score": 1,
202 | "created_time": 1503156522,
203 | "vote_state": 0,
204 | "rt": 2,
205 | "user_id": 775878,
206 | "user_username": "AkshatB",
207 | "user_score": 64,
208 | "user_avatar": {
209 | "b": "d55161",
210 | "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"
211 | }
212 | },
213 | {
214 | "id": 787172,
215 | "rant_id": 785714,
216 | "body": "@AkshatB replied :D",
217 | "score": 2,
218 | "created_time": 1503157311,
219 | "vote_state": 0,
220 | "rt": 2,
221 | "user_id": 217820,
222 | "user_username": "RuntimeError",
223 | "user_score": 1038,
224 | "user_avatar": {
225 | "b": "69c9cd",
226 | "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"
227 | },
228 | "user_dpp": 1
229 | },
230 | {
231 | "id": 787458,
232 | "rant_id": 785714,
233 | "body": "@RuntimeError I have sent you an email",
234 | "score": 1,
235 | "created_time": 1503167526,
236 | "vote_state": 0,
237 | "rt": 2,
238 | "user_id": 627273,
239 | "user_username": "404response",
240 | "user_score": 2163,
241 | "user_avatar": {
242 | "b": "2a8b9d",
243 | "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"
244 | },
245 | "user_dpp": 1
246 | },
247 | {
248 | "id": 787663,
249 | "rant_id": 785714,
250 | "body": "Hey if you make it open source I'll do my best to contribute",
251 | "score": 5,
252 | "created_time": 1503176112,
253 | "vote_state": 0,
254 | "rt": 2,
255 | "user_id": 310584,
256 | "user_username": "dontPanic",
257 | "user_score": 5567,
258 | "user_avatar": {
259 | "b": "2a8b9d",
260 | "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"
261 | }
262 | },
263 | {
264 | "id": 787961,
265 | "rant_id": 785714,
266 | "body": "I’d love to help.",
267 | "score": 1,
268 | "created_time": 1503190874,
269 | "vote_state": 0,
270 | "rt": 2,
271 | "user_id": 364293,
272 | "user_username": "calmyourtities",
273 | "user_score": 8090,
274 | "user_avatar": {
275 | "b": "7bc8a4",
276 | "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"
277 | }
278 | },
279 | {
280 | "id": 788057,
281 | "rant_id": 785714,
282 | "body": "What are you thinking for front end? JavaFX?",
283 | "score": 3,
284 | "created_time": 1503197427,
285 | "vote_state": 0,
286 | "rt": 2,
287 | "user_id": 272568,
288 | "user_username": "ryanmhoffman",
289 | "user_score": 4104,
290 | "user_avatar": {
291 | "b": "a973a2",
292 | "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"
293 | }
294 | },
295 | {
296 | "id": 788096,
297 | "rant_id": 785714,
298 | "body": "@ryanmhoffman yeah as of right now that seems to be the modt logical option",
299 | "score": 1,
300 | "created_time": 1503201353,
301 | "vote_state": 0,
302 | "rt": 2,
303 | "user_id": 217820,
304 | "user_username": "RuntimeError",
305 | "user_score": 1038,
306 | "user_avatar": {
307 | "b": "69c9cd",
308 | "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"
309 | },
310 | "user_dpp": 1
311 | },
312 | {
313 | "id": 788097,
314 | "rant_id": 785714,
315 | "body": "@calmyourtities cool shoot me an email",
316 | "score": 2,
317 | "created_time": 1503201366,
318 | "vote_state": 0,
319 | "rt": 2,
320 | "user_id": 217820,
321 | "user_username": "RuntimeError",
322 | "user_score": 1038,
323 | "user_avatar": {
324 | "b": "69c9cd",
325 | "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"
326 | },
327 | "user_dpp": 1
328 | },
329 | {
330 | "id": 788098,
331 | "rant_id": 785714,
332 | "body": "@dontPanic i think chances are that it will be open source",
333 | "score": 4,
334 | "created_time": 1503201385,
335 | "vote_state": 0,
336 | "rt": 2,
337 | "user_id": 217820,
338 | "user_username": "RuntimeError",
339 | "user_score": 1038,
340 | "user_avatar": {
341 | "b": "69c9cd",
342 | "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"
343 | },
344 | "user_dpp": 1
345 | },
346 | {
347 | "id": 788382,
348 | "rant_id": 785714,
349 | "body": "I'm in as well! ☺",
350 | "score": 1,
351 | "created_time": 1503220565,
352 | "vote_state": 0,
353 | "rt": 2,
354 | "user_id": 12372,
355 | "user_username": "bt141516",
356 | "user_score": 139,
357 | "user_avatar": {
358 | "b": "2a8b9d",
359 | "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"
360 | }
361 | },
362 | {
363 | "id": 788408,
364 | "rant_id": 785714,
365 | "body": "Just out of interest, will this be an open sourced project?",
366 | "score": 0,
367 | "created_time": 1503221454,
368 | "vote_state": 0,
369 | "rt": 2,
370 | "user_id": 784166,
371 | "user_username": "rozina",
372 | "user_score": 615,
373 | "user_avatar": {
374 | "b": "ecd276",
375 | "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"
376 | }
377 | },
378 | {
379 | "id": 788671,
380 | "rant_id": 785714,
381 | "body": "@rozina i imagine so, although i havent fully thought through it yet",
382 | "score": 1,
383 | "created_time": 1503233212,
384 | "vote_state": 0,
385 | "rt": 2,
386 | "user_id": 217820,
387 | "user_username": "RuntimeError",
388 | "user_score": 1038,
389 | "user_avatar": {
390 | "b": "69c9cd",
391 | "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"
392 | },
393 | "user_dpp": 1
394 | },
395 | {
396 | "id": 788971,
397 | "rant_id": 785714,
398 | "body": "Oh my God so many people. \nI think something is gonna to be big! @RuntimeError",
399 | "score": 0,
400 | "created_time": 1503243794,
401 | "vote_state": 0,
402 | "rt": 2,
403 | "user_id": 774806,
404 | "user_username": "itsnameless",
405 | "user_score": 401,
406 | "user_avatar": {
407 | "b": "a973a2",
408 | "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"
409 | }
410 | },
411 | {
412 | "id": 789159,
413 | "rant_id": 785714,
414 | "body": "@itsnameless ahaha yeah its exciting",
415 | "score": 1,
416 | "created_time": 1503253458,
417 | "vote_state": 0,
418 | "rt": 2,
419 | "user_id": 217820,
420 | "user_username": "RuntimeError",
421 | "user_score": 1038,
422 | "user_avatar": {
423 | "b": "69c9cd",
424 | "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"
425 | },
426 | "user_dpp": 1
427 | },
428 | {
429 | "id": 790369,
430 | "rant_id": 785714,
431 | "body": "I am in",
432 | "score": 0,
433 | "created_time": 1503307258,
434 | "vote_state": 0,
435 | "rt": 2,
436 | "user_id": 767233,
437 | "user_username": "engrravijain",
438 | "user_score": 96,
439 | "user_avatar": {
440 | "b": "d55161",
441 | "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"
442 | }
443 | },
444 | {
445 | "id": 791222,
446 | "rant_id": 785714,
447 | "body": "Check out Apples \"sandbox\" app for inspiration. It's quite entertaining.",
448 | "score": 0,
449 | "created_time": 1503332556,
450 | "vote_state": 0,
451 | "rt": 2,
452 | "user_id": 127889,
453 | "user_username": "shdw",
454 | "user_score": 983,
455 | "user_avatar": {
456 | "b": "f99a66"
457 | }
458 | },
459 | {
460 | "id": 791243,
461 | "rant_id": 785714,
462 | "body": "@RuntimeError I’ve sent an email",
463 | "score": 0,
464 | "created_time": 1503333292,
465 | "vote_state": 0,
466 | "rt": 2,
467 | "user_id": 364293,
468 | "user_username": "calmyourtities",
469 | "user_score": 8090,
470 | "user_avatar": {
471 | "b": "7bc8a4",
472 | "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"
473 | }
474 | },
475 | {
476 | "id": 791926,
477 | "rant_id": 785714,
478 | "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?",
479 | "score": 0,
480 | "created_time": 1503353768,
481 | "vote_state": 0,
482 | "rt": 2,
483 | "user_id": 387696,
484 | "user_username": "Skayo",
485 | "user_score": 2949,
486 | "user_avatar": {
487 | "b": "69c9cd",
488 | "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"
489 | },
490 | "user_dpp": 1
491 | },
492 | {
493 | "id": 791988,
494 | "rant_id": 785714,
495 | "body": "@Skayo don’t go for programming. Or maybe the program could install JDK for you.",
496 | "score": 1,
497 | "created_time": 1503356701,
498 | "vote_state": 0,
499 | "rt": 2,
500 | "user_id": 364293,
501 | "user_username": "calmyourtities",
502 | "user_score": 8090,
503 | "user_avatar": {
504 | "b": "7bc8a4",
505 | "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"
506 | }
507 | },
508 | {
509 | "id": 792008,
510 | "rant_id": 785714,
511 | "body": "@calmyourtities ill add you as soon as i can!",
512 | "score": 1,
513 | "created_time": 1503357833,
514 | "vote_state": 0,
515 | "rt": 2,
516 | "user_id": 217820,
517 | "user_username": "RuntimeError",
518 | "user_score": 1038,
519 | "user_avatar": {
520 | "b": "69c9cd",
521 | "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"
522 | },
523 | "user_dpp": 1
524 | },
525 | {
526 | "id": 792009,
527 | "rant_id": 785714,
528 | "body": "@Skayo most systems have java pre installed on em :)",
529 | "score": 1,
530 | "created_time": 1503357848,
531 | "vote_state": 0,
532 | "rt": 2,
533 | "user_id": 217820,
534 | "user_username": "RuntimeError",
535 | "user_score": 1038,
536 | "user_avatar": {
537 | "b": "69c9cd",
538 | "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"
539 | },
540 | "user_dpp": 1
541 | },
542 | {
543 | "id": 792026,
544 | "rant_id": 785714,
545 | "body": "@RuntimeError could you resend me the invitation pls, I fucked up accepting the last one and then it expired",
546 | "score": 0,
547 | "created_time": 1503358308,
548 | "vote_state": 0,
549 | "rt": 2,
550 | "user_id": 627273,
551 | "user_username": "404response",
552 | "user_score": 2163,
553 | "user_avatar": {
554 | "b": "2a8b9d",
555 | "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"
556 | },
557 | "user_dpp": 1
558 | },
559 | {
560 | "id": 792123,
561 | "rant_id": 785714,
562 | "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 :)",
563 | "score": 2,
564 | "created_time": 1503362891,
565 | "vote_state": 0,
566 | "rt": 2,
567 | "user_id": 217820,
568 | "user_username": "RuntimeError",
569 | "user_score": 1038,
570 | "user_avatar": {
571 | "b": "69c9cd",
572 | "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"
573 | },
574 | "user_dpp": 1
575 | },
576 | {
577 | "id": 792748,
578 | "rant_id": 785714,
579 | "body": "<@Dacexi>\nUsing JavaFX you can also build really pretty apps!",
580 | "score": 3,
581 | "created_time": 1503391054,
582 | "vote_state": 0,
583 | "rt": 2,
584 | "user_id": 387696,
585 | "user_username": "Skayo",
586 | "user_score": 2949,
587 | "user_avatar": {
588 | "b": "69c9cd",
589 | "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"
590 | },
591 | "user_dpp": 1
592 | },
593 | {
594 | "id": 792909,
595 | "rant_id": 785714,
596 | "body": "@RuntimeError Nah, Electron allows for pretty deep integration. Though I agree that Java is superior because of resource usage.",
597 | "score": 1,
598 | "created_time": 1503398654,
599 | "vote_state": 0,
600 | "rt": 2,
601 | "edited": true,
602 | "user_id": 397822,
603 | "user_username": "Jop-",
604 | "user_score": 3404,
605 | "user_avatar": {
606 | "b": "d55161",
607 | "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"
608 | }
609 | },
610 | {
611 | "id": 793459,
612 | "rant_id": 785714,
613 | "body": "So how do we do this in practice? Is there a plan? A repository?",
614 | "score": 0,
615 | "created_time": 1503418132,
616 | "vote_state": 0,
617 | "rt": 2,
618 | "user_id": 678714,
619 | "user_username": "Trablarer",
620 | "user_score": 391,
621 | "user_avatar": {
622 | "b": "d55161",
623 | "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"
624 | }
625 | },
626 | {
627 | "id": 793628,
628 | "rant_id": 785714,
629 | "body": "Repo plz",
630 | "score": 1,
631 | "created_time": 1503424527,
632 | "vote_state": 0,
633 | "rt": 2,
634 | "user_id": 47060,
635 | "user_username": "DeveloperACE",
636 | "user_score": 4020,
637 | "user_avatar": {
638 | "b": "2a8b9d",
639 | "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"
640 | }
641 | },
642 | {
643 | "id": 793650,
644 | "rant_id": 785714,
645 | "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",
646 | "score": 2,
647 | "created_time": 1503425489,
648 | "vote_state": 0,
649 | "rt": 2,
650 | "user_id": 217820,
651 | "user_username": "RuntimeError",
652 | "user_score": 1038,
653 | "user_avatar": {
654 | "b": "69c9cd",
655 | "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"
656 | },
657 | "user_dpp": 1
658 | },
659 | {
660 | "id": 793704,
661 | "rant_id": 785714,
662 | "body": "@RuntimeError ok!",
663 | "score": 0,
664 | "created_time": 1503427510,
665 | "vote_state": 0,
666 | "rt": 2,
667 | "user_id": 47060,
668 | "user_username": "DeveloperACE",
669 | "user_score": 4020,
670 | "user_avatar": {
671 | "b": "2a8b9d",
672 | "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"
673 | }
674 | },
675 | {
676 | "id": 794771,
677 | "rant_id": 785714,
678 | "body": "Count me in.",
679 | "score": 0,
680 | "created_time": 1503481384,
681 | "vote_state": 0,
682 | "rt": 2,
683 | "user_id": 722396,
684 | "user_username": "GodHatesMe",
685 | "user_score": 5529,
686 | "user_avatar": {
687 | "b": "ecd276",
688 | "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"
689 | },
690 | "user_dpp": 1
691 | },
692 | {
693 | "id": 804051,
694 | "rant_id": 785714,
695 | "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 ;)",
696 | "score": 0,
697 | "created_time": 1503881884,
698 | "vote_state": 0,
699 | "rt": 2,
700 | "user_id": 734662,
701 | "user_username": "robinofski",
702 | "user_score": 27,
703 | "user_avatar": {
704 | "b": "f99a66",
705 | "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"
706 | }
707 | },
708 | {
709 | "id": 804202,
710 | "rant_id": 785714,
711 | "body": "@robinofski you mean like photoshop designs?",
712 | "score": 1,
713 | "created_time": 1503895167,
714 | "vote_state": 0,
715 | "rt": 2,
716 | "user_id": 217820,
717 | "user_username": "RuntimeError",
718 | "user_score": 1038,
719 | "user_avatar": {
720 | "b": "69c9cd",
721 | "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"
722 | },
723 | "user_dpp": 1
724 | },
725 | {
726 | "id": 804339,
727 | "rant_id": 785714,
728 | "body": "@RuntimeError yes stuff like that.",
729 | "score": 1,
730 | "created_time": 1503901190,
731 | "vote_state": 0,
732 | "rt": 2,
733 | "user_id": 734662,
734 | "user_username": "robinofski",
735 | "user_score": 27,
736 | "user_avatar": {
737 | "b": "f99a66",
738 | "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"
739 | }
740 | },
741 | {
742 | "id": 804951,
743 | "rant_id": 785714,
744 | "body": "@robinofski shoot me an email bro :)",
745 | "score": 0,
746 | "created_time": 1503926062,
747 | "vote_state": 0,
748 | "rt": 2,
749 | "user_id": 217820,
750 | "user_username": "RuntimeError",
751 | "user_score": 1038,
752 | "user_avatar": {
753 | "b": "69c9cd",
754 | "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"
755 | },
756 | "user_dpp": 1
757 | },
758 | {
759 | "id": 804985,
760 | "rant_id": 785714,
761 | "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",
762 | "score": 0,
763 | "created_time": 1503927452,
764 | "vote_state": 0,
765 | "rt": 2,
766 | "user_id": 734662,
767 | "user_username": "robinofski",
768 | "user_score": 27,
769 | "user_avatar": {
770 | "b": "f99a66",
771 | "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"
772 | }
773 | },
774 | {
775 | "id": 805941,
776 | "rant_id": 785714,
777 | "body": "Visual work on the design of the client so far, going well.",
778 | "score": 2,
779 | "created_time": 1503961110,
780 | "vote_state": 0,
781 | "rt": 2,
782 | "user_id": 734662,
783 | "user_username": "robinofski",
784 | "user_score": 27,
785 | "user_avatar": {
786 | "b": "f99a66",
787 | "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"
788 | }
789 | },
790 | {
791 | "id": 807344,
792 | "rant_id": 785714,
793 | "body": "I am in too!\nIs there more room for people?",
794 | "score": 1,
795 | "created_time": 1504023993,
796 | "vote_state": 0,
797 | "rt": 2,
798 | "user_id": 686941,
799 | "user_username": "sagarnar",
800 | "user_score": 36,
801 | "user_avatar": {
802 | "b": "7bc8a4"
803 | }
804 | },
805 | {
806 | "id": 807872,
807 | "rant_id": 785714,
808 | "body": "For Java programmers you should also go alittle bit into Kotlin. I would like to learn how to code in that language. :)",
809 | "score": 2,
810 | "created_time": 1504039483,
811 | "vote_state": 0,
812 | "rt": 2,
813 | "user_id": 748695,
814 | "user_username": "steve-white21",
815 | "user_score": 31,
816 | "user_avatar": {
817 | "b": "f99a66",
818 | "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"
819 | }
820 | },
821 | {
822 | "id": 808090,
823 | "rant_id": 785714,
824 | "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",
825 | "score": 0,
826 | "created_time": 1504049088,
827 | "vote_state": 0,
828 | "rt": 2,
829 | "user_id": 217820,
830 | "user_username": "RuntimeError",
831 | "user_score": 1038,
832 | "user_avatar": {
833 | "b": "69c9cd",
834 | "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"
835 | },
836 | "user_dpp": 1
837 | },
838 | {
839 | "id": 811031,
840 | "rant_id": 785714,
841 | "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.",
842 | "score": 2,
843 | "created_time": 1504183663,
844 | "vote_state": 0,
845 | "rt": 2,
846 | "user_id": 807252,
847 | "user_username": "CodeKill",
848 | "user_score": 677,
849 | "user_avatar": {
850 | "b": "69c9cd",
851 | "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"
852 | },
853 | "user_dpp": 1
854 | }
855 | ],
856 | "success": true
857 | }
--------------------------------------------------------------------------------
/src/test/resources/feed-collabs.json:
--------------------------------------------------------------------------------
1 | {
2 | "success": true,
3 | "rants": [
4 | {
5 | "id": 838945,
6 | "text": "Partnerships Matching App [more details]",
7 | "score": 12,
8 | "created_time": 1505364058,
9 | "attached_image": "",
10 | "num_comments": 6,
11 | "tags": [],
12 | "vote_state": 0,
13 | "edited": false,
14 | "link": "collabs/838945/partnerships-matching-app",
15 | "rt": 2,
16 | "c_type": 3,
17 | "c_type_long": "Project idea",
18 | "user_id": 217820,
19 | "user_username": "RuntimeError",
20 | "user_score": 1038,
21 | "user_avatar": {
22 | "b": "69c9cd",
23 | "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"
24 | },
25 | "user_dpp": 1
26 | },
27 | {
28 | "id": 826092,
29 | "text": "ECS-Concept - a Entity Component System library in C++17/C++20, for learning purposes. [more details]",
30 | "score": 7,
31 | "created_time": 1504808800,
32 | "attached_image": "",
33 | "num_comments": 5,
34 | "tags": [],
35 | "vote_state": 0,
36 | "edited": true,
37 | "link": "collabs/826092/ecs-concept-a-entity-component-system-library-in-c-17-c-20-for-learning-purposes",
38 | "rt": 2,
39 | "c_type": 2,
40 | "c_type_long": "Existing open source project",
41 | "user_id": 518034,
42 | "user_username": "Celes",
43 | "user_score": 505,
44 | "user_avatar": {
45 | "b": "2a8b9d",
46 | "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"
47 | },
48 | "user_dpp": 1
49 | }
50 | ]
51 | }
--------------------------------------------------------------------------------
/src/test/resources/feed-rants.json:
--------------------------------------------------------------------------------
1 | {
2 | "success": true,
3 | "rants": [
4 | {
5 | "id": 814524,
6 | "text": "Too real...",
7 | "score": 1,
8 | "created_time": 1504339145,
9 | "attached_image": {
10 | "url": "https://img.devrant.io/devrant/rant/r_814524_mSXAP.jpg",
11 | "width": 799,
12 | "height": 336
13 | },
14 | "num_comments": 3,
15 | "tags": ["tag one", "tag 2"],
16 | "vote_state": 0,
17 | "edited": false,
18 | "user_id": 759598,
19 | "user_username": "bleachedsleet",
20 | "user_score": 1,
21 | "user_avatar": {
22 | "b": "7bc8a4"
23 | }
24 | },
25 | {
26 | "id": 814516,
27 | "text": "Windows Photo Viewer doesn't support GIF animation. Great!",
28 | "score": 1,
29 | "created_time": 1504338721,
30 | "attached_image": "",
31 | "num_comments": 0,
32 | "tags": [],
33 | "vote_state": 0,
34 | "edited": false,
35 | "user_id": 268318,
36 | "user_username": "error503",
37 | "user_score": 9347,
38 | "user_avatar": {
39 | "b": "2a8b9d",
40 | "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"
41 | }
42 | },
43 | {
44 | "id": 814472,
45 | "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*",
46 | "score": 4,
47 | "created_time": 1504335825,
48 | "attached_image": "",
49 | "num_comments": 1,
50 | "tags": [
51 | "facepalm"
52 | ],
53 | "vote_state": 0,
54 | "edited": false,
55 | "user_id": 410678,
56 | "user_username": "ytho",
57 | "user_score": 714,
58 | "user_avatar": {
59 | "b": "2a8b9d",
60 | "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"
61 | }
62 | },
63 | {
64 | "id": 814432,
65 | "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.",
66 | "score": 2,
67 | "created_time": 1504333286,
68 | "attached_image": "",
69 | "num_comments": 4,
70 | "tags": [],
71 | "vote_state": 0,
72 | "edited": false,
73 | "user_id": 19218,
74 | "user_username": "Haxk20",
75 | "user_score": 6459,
76 | "user_avatar": {
77 | "b": "7bc8a4",
78 | "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"
79 | }
80 | }
81 | ],
82 | "settings": [],
83 | "set": "59aa68e8cdfe8"
84 | }
--------------------------------------------------------------------------------
/src/test/resources/feed-stories.json:
--------------------------------------------------------------------------------
1 | {
2 | "success": true,
3 | "rants": [
4 | {
5 | "id": 830647,
6 | "text": "",
7 | "score": 134,
8 | "created_time": 1505020638,
9 | "attached_image": "",
10 | "num_comments": 10,
11 | "tags": [
12 | "mother",
13 | "web",
14 | "atom",
15 | "dreamweaver"
16 | ],
17 | "vote_state": 0,
18 | "edited": false,
19 | "rt": 1,
20 | "user_id": 732476,
21 | "user_username": "awnz",
22 | "user_score": 412,
23 | "user_avatar": {
24 | "b": "ecd276",
25 | "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"
26 | }
27 | },
28 | {
29 | "id": 836856,
30 | "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.",
31 | "score": 134,
32 | "created_time": 1505293496,
33 | "attached_image": "",
34 | "num_comments": 48,
35 | "tags": [
36 | "suck.my knob",
37 | "i wish they'd finally go bankrupt",
38 | "tainted apple"
39 | ],
40 | "vote_state": 0,
41 | "edited": true,
42 | "rt": 1,
43 | "user_id": 141078,
44 | "user_username": "AlexDeLarge",
45 | "user_score": 43844,
46 | "user_avatar": {
47 | "b": "a973a2",
48 | "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"
49 | },
50 | "user_dpp": 1
51 | },
52 | {
53 | "id": 841073,
54 | "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...!",
55 | "score": 129,
56 | "created_time": 1505449498,
57 | "attached_image": "",
58 | "num_comments": 4,
59 | "tags": [
60 | "mongodb",
61 | "nodejs",
62 | "backend"
63 | ],
64 | "vote_state": 0,
65 | "edited": true,
66 | "rt": 1,
67 | "user_id": 92701,
68 | "user_username": "5hirish",
69 | "user_score": 201,
70 | "user_avatar": {
71 | "b": "69c9cd",
72 | "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"
73 | }
74 | },
75 | {
76 | "id": 832046,
77 | "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...",
78 | "score": 111,
79 | "created_time": 1505103035,
80 | "attached_image": "",
81 | "num_comments": 8,
82 | "tags": [
83 | "procrastination",
84 | "wk69"
85 | ],
86 | "vote_state": 0,
87 | "edited": true,
88 | "rt": 1,
89 | "user_id": 214220,
90 | "user_username": "Lahsen2016",
91 | "user_score": 1830,
92 | "user_avatar": {
93 | "b": "d55161",
94 | "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"
95 | }
96 | },
97 | {
98 | "id": 841401,
99 | "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!",
100 | "score": 101,
101 | "created_time": 1505462382,
102 | "attached_image": "",
103 | "num_comments": 15,
104 | "tags": [
105 | "apple",
106 | "android",
107 | "cnet"
108 | ],
109 | "vote_state": 0,
110 | "edited": false,
111 | "rt": 1,
112 | "user_id": 42079,
113 | "user_username": "tahnik",
114 | "user_score": 35034,
115 | "user_avatar": {
116 | "b": "2a8b9d",
117 | "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"
118 | },
119 | "user_dpp": 1
120 | },
121 | {
122 | "id": 841966,
123 | "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. 😡",
124 | "score": 94,
125 | "created_time": 1505480918,
126 | "attached_image": "",
127 | "num_comments": 39,
128 | "tags": [
129 | "fucking stuttering",
130 | "linux",
131 | "ffs",
132 | "linuxxx"
133 | ],
134 | "vote_state": 0,
135 | "edited": true,
136 | "rt": 1,
137 | "user_id": 22941,
138 | "user_username": "linuxxx",
139 | "user_score": 35032,
140 | "user_avatar": {
141 | "b": "2a8b9d",
142 | "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"
143 | },
144 | "user_dpp": 1
145 | },
146 | {
147 | "id": 842340,
148 | "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🎤",
149 | "score": 85,
150 | "created_time": 1505491005,
151 | "attached_image": "",
152 | "num_comments": 8,
153 | "tags": [
154 | "don't fuck with the sysadmin",
155 | "burn",
156 | "troll"
157 | ],
158 | "vote_state": 0,
159 | "edited": false,
160 | "rt": 1,
161 | "user_id": 503297,
162 | "user_username": "Noob",
163 | "user_score": 3054,
164 | "user_avatar": {
165 | "b": "2a8b9d",
166 | "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"
167 | }
168 | },
169 | {
170 | "id": 840178,
171 | "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.",
172 | "score": 81,
173 | "created_time": 1505408692,
174 | "attached_image": "",
175 | "num_comments": 9,
176 | "tags": [
177 | "random",
178 | "off by 1",
179 | "sweep these things under the carpet"
180 | ],
181 | "vote_state": 0,
182 | "edited": false,
183 | "rt": 1,
184 | "user_id": 798905,
185 | "user_username": "illusion466",
186 | "user_score": 349,
187 | "user_avatar": {
188 | "b": "7bc8a4",
189 | "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"
190 | }
191 | },
192 | {
193 | "id": 832679,
194 | "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.",
195 | "score": 77,
196 | "created_time": 1505130070,
197 | "attached_image": "",
198 | "num_comments": 5,
199 | "tags": [
200 | "tripping",
201 | "in my way",
202 | "upside your head",
203 | "bad",
204 | "rude"
205 | ],
206 | "vote_state": 0,
207 | "edited": false,
208 | "rt": 1,
209 | "user_id": 190725,
210 | "user_username": "practiseSafeHex",
211 | "user_score": 5101,
212 | "user_avatar": {
213 | "b": "2a8b9d",
214 | "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"
215 | }
216 | },
217 | {
218 | "id": 838743,
219 | "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...",
220 | "score": 77,
221 | "created_time": 1505351707,
222 | "attached_image": "",
223 | "num_comments": 10,
224 | "tags": [
225 | "boss",
226 | "git push -f",
227 | "merge conflicts",
228 | "unresolved problems"
229 | ],
230 | "vote_state": 0,
231 | "edited": false,
232 | "rt": 1,
233 | "user_id": 552400,
234 | "user_username": "PonySlaystation",
235 | "user_score": 4961,
236 | "user_avatar": {
237 | "b": "2a8b9d",
238 | "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"
239 | },
240 | "user_dpp": 1
241 | },
242 | {
243 | "id": 835994,
244 | "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.",
245 | "score": 73,
246 | "created_time": 1505252317,
247 | "attached_image": "",
248 | "num_comments": 10,
249 | "tags": [],
250 | "vote_state": 0,
251 | "edited": false,
252 | "rt": 1,
253 | "user_id": 145378,
254 | "user_username": "alcatraz627",
255 | "user_score": 5805,
256 | "user_avatar": {
257 | "b": "2a8b9d",
258 | "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"
259 | }
260 | },
261 | {
262 | "id": 835049,
263 | "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.",
264 | "score": 68,
265 | "created_time": 1505222666,
266 | "attached_image": "",
267 | "num_comments": 28,
268 | "tags": [
269 | "backonfeet"
270 | ],
271 | "vote_state": 0,
272 | "edited": false,
273 | "rt": 1,
274 | "links": [
275 | {
276 | "type": "url",
277 | "url": "https://devrant.io/rants/815062",
278 | "short_url": "https://devrant.io/rants/815062",
279 | "title": "https://devrant.io/rants/815062",
280 | "start": 40,
281 | "end": 71,
282 | "special": 1
283 | }
284 | ],
285 | "special": true,
286 | "user_id": 369518,
287 | "user_username": "ragnarr023",
288 | "user_score": 3091,
289 | "user_avatar": {
290 | "b": "2a8b9d",
291 | "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"
292 | }
293 | },
294 | {
295 | "id": 842343,
296 | "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).",
297 | "score": 67,
298 | "created_time": 1505491203,
299 | "attached_image": "",
300 | "num_comments": 9,
301 | "tags": [],
302 | "vote_state": 0,
303 | "edited": true,
304 | "rt": 1,
305 | "user_id": 396296,
306 | "user_username": "Neotelos",
307 | "user_score": 1409,
308 | "user_avatar": {
309 | "b": "69c9cd",
310 | "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"
311 | }
312 | },
313 | {
314 | "id": 830568,
315 | "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 😎",
316 | "score": 67,
317 | "created_time": 1505011544,
318 | "attached_image": "",
319 | "num_comments": 0,
320 | "tags": [
321 | "interview"
322 | ],
323 | "vote_state": 0,
324 | "edited": false,
325 | "rt": 1,
326 | "user_id": 566882,
327 | "user_username": "scottydevil",
328 | "user_score": 251,
329 | "user_avatar": {
330 | "b": "2a8b9d",
331 | "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"
332 | }
333 | },
334 | {
335 | "id": 839205,
336 | "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!",
337 | "score": 63,
338 | "created_time": 1505374453,
339 | "attached_image": "",
340 | "num_comments": 7,
341 | "tags": [
342 | "it"
343 | ],
344 | "vote_state": 0,
345 | "edited": false,
346 | "rt": 1,
347 | "user_id": 111587,
348 | "user_username": "switchstep",
349 | "user_score": 1543,
350 | "user_avatar": {
351 | "b": "7bc8a4",
352 | "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"
353 | }
354 | },
355 | {
356 | "id": 831031,
357 | "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?",
358 | "score": 57,
359 | "created_time": 1505048499,
360 | "attached_image": "",
361 | "num_comments": 15,
362 | "tags": [
363 | "babylon.js",
364 | "three.js",
365 | "webgl",
366 | "3d",
367 | "canvas"
368 | ],
369 | "vote_state": 0,
370 | "edited": true,
371 | "rt": 1,
372 | "user_id": 141078,
373 | "user_username": "AlexDeLarge",
374 | "user_score": 43844,
375 | "user_avatar": {
376 | "b": "a973a2",
377 | "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"
378 | },
379 | "user_dpp": 1
380 | },
381 | {
382 | "id": 833015,
383 | "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...",
384 | "score": 56,
385 | "created_time": 1505141878,
386 | "attached_image": "",
387 | "num_comments": 5,
388 | "tags": [
389 | "bad design",
390 | "scanning",
391 | "security",
392 | "crash",
393 | "networking"
394 | ],
395 | "vote_state": 0,
396 | "edited": false,
397 | "rt": 1,
398 | "user_id": 38990,
399 | "user_username": "Folkmann",
400 | "user_score": 976,
401 | "user_avatar": {
402 | "b": "2a8b9d",
403 | "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"
404 | }
405 | },
406 | {
407 | "id": 840694,
408 | "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",
409 | "score": 56,
410 | "created_time": 1505425570,
411 | "attached_image": "",
412 | "num_comments": 6,
413 | "tags": [
414 | "open source idiot",
415 | "open source"
416 | ],
417 | "vote_state": 0,
418 | "edited": true,
419 | "rt": 1,
420 | "user_id": 5063,
421 | "user_username": "zshh",
422 | "user_score": 3455,
423 | "user_avatar": {
424 | "b": "d55161",
425 | "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"
426 | },
427 | "user_dpp": 1
428 | },
429 | {
430 | "id": 841779,
431 | "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!!!",
432 | "score": 56,
433 | "created_time": 1505475001,
434 | "attached_image": "",
435 | "num_comments": 26,
436 | "tags": [],
437 | "vote_state": 0,
438 | "edited": false,
439 | "rt": 1,
440 | "user_id": 722396,
441 | "user_username": "GodHatesMe",
442 | "user_score": 5524,
443 | "user_avatar": {
444 | "b": "ecd276",
445 | "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"
446 | },
447 | "user_dpp": 1
448 | },
449 | {
450 | "id": 838826,
451 | "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!",
452 | "score": 54,
453 | "created_time": 1505356730,
454 | "attached_image": "",
455 | "num_comments": 4,
456 | "tags": [
457 | "friend",
458 | "scumbag"
459 | ],
460 | "vote_state": 0,
461 | "edited": false,
462 | "rt": 1,
463 | "user_id": 672442,
464 | "user_username": "bepoXY",
465 | "user_score": 757,
466 | "user_avatar": {
467 | "b": "2a8b9d",
468 | "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"
469 | }
470 | }
471 | ]
472 | }
--------------------------------------------------------------------------------
/src/test/resources/feed-weekly.json:
--------------------------------------------------------------------------------
1 | {
2 | "success": true,
3 | "rants": [
4 | {
5 | "id": 843118,
6 | "text": "My own OCR library... so far I haven't found a proper recognizer",
7 | "score": 2,
8 | "created_time": 1505519016,
9 | "attached_image": "",
10 | "num_comments": 0,
11 | "tags": [
12 | "wk69"
13 | ],
14 | "vote_state": 0,
15 | "edited": false,
16 | "rt": 1,
17 | "user_id": 345752,
18 | "user_username": "davide",
19 | "user_score": 1057,
20 | "user_avatar": {
21 | "b": "7bc8a4",
22 | "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"
23 | }
24 | },
25 | {
26 | "id": 842334,
27 | "text": "Lol. How do I register for {content[0].title}, what happened to all the mails😨",
28 | "score": 1,
29 | "created_time": 1505490675,
30 | "attached_image": {
31 | "url": "https://img.devrant.io/devrant/rant/r_842334_CevLD.jpg",
32 | "width": 540,
33 | "height": 960
34 | },
35 | "num_comments": 0,
36 | "tags": [
37 | "wk69"
38 | ],
39 | "vote_state": 0,
40 | "edited": false,
41 | "rt": 1,
42 | "user_id": 534161,
43 | "user_username": "odusanya",
44 | "user_score": 1,
45 | "user_avatar": {
46 | "b": "7bc8a4"
47 | }
48 | },
49 | {
50 | "id": 841937,
51 | "text": "This.\nI'm not sure yet what I'm going to do with it, but it's going to be awesome.",
52 | "score": 62,
53 | "created_time": 1505479985,
54 | "attached_image": {
55 | "url": "https://img.devrant.io/devrant/rant/r_841937_GGKae.jpg",
56 | "width": 800,
57 | "height": 600
58 | },
59 | "num_comments": 19,
60 | "tags": [
61 | "raspberry pi",
62 | "wk69"
63 | ],
64 | "vote_state": 0,
65 | "edited": false,
66 | "rt": 1,
67 | "user_id": 713663,
68 | "user_username": "karwler",
69 | "user_score": 425,
70 | "user_avatar": {
71 | "b": "2a8b9d",
72 | "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"
73 | }
74 | },
75 | {
76 | "id": 839859,
77 | "text": "The autopilot that detects flooded farm zones. (Crashed it a couple of times😅)",
78 | "score": 32,
79 | "created_time": 1505398354,
80 | "attached_image": {
81 | "url": "https://img.devrant.io/devrant/rant/r_839859_Z1sCU.jpg",
82 | "width": 800,
83 | "height": 600
84 | },
85 | "num_comments": 2,
86 | "tags": [
87 | "wk69"
88 | ],
89 | "vote_state": 0,
90 | "edited": false,
91 | "rt": 1,
92 | "user_id": 839783,
93 | "user_username": "pilobasualdo",
94 | "user_score": 36,
95 | "user_avatar": {
96 | "b": "2a8b9d",
97 | "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"
98 | }
99 | },
100 | {
101 | "id": 836605,
102 | "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 :(",
103 | "score": 54,
104 | "created_time": 1505284462,
105 | "attached_image": {
106 | "url": "https://img.devrant.io/devrant/rant/r_836605_1xkR6.jpg",
107 | "width": 750,
108 | "height": 1000
109 | },
110 | "num_comments": 13,
111 | "tags": [
112 | "wk69",
113 | "side projects",
114 | "arduino",
115 | "moonwalk"
116 | ],
117 | "vote_state": 0,
118 | "edited": false,
119 | "rt": 1,
120 | "user_id": 717749,
121 | "user_username": "marcom",
122 | "user_score": 128,
123 | "user_avatar": {
124 | "b": "69c9cd",
125 | "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"
126 | }
127 | },
128 | {
129 | "id": 842553,
130 | "text": "Side project I wish I could finish... updating my resume and building a portfolio so I can get a new job.",
131 | "score": 6,
132 | "created_time": 1505498330,
133 | "attached_image": "",
134 | "num_comments": 1,
135 | "tags": [
136 | "wk69"
137 | ],
138 | "vote_state": 0,
139 | "edited": false,
140 | "rt": 1,
141 | "user_id": 564853,
142 | "user_username": "amlove32",
143 | "user_score": 246,
144 | "user_avatar": {
145 | "b": "2a8b9d",
146 | "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"
147 | },
148 | "user_dpp": 1
149 | },
150 | {
151 | "id": 842420,
152 | "text": "My dead simple, one button, location based time tracking app. Would help really much...",
153 | "score": 1,
154 | "created_time": 1505494093,
155 | "attached_image": "",
156 | "num_comments": 0,
157 | "tags": [
158 | "wk69"
159 | ],
160 | "vote_state": 0,
161 | "edited": false,
162 | "rt": 1,
163 | "user_id": 757407,
164 | "user_username": "adfr",
165 | "user_score": 9,
166 | "user_avatar": {
167 | "b": "7bc8a4"
168 | }
169 | },
170 | {
171 | "id": 841044,
172 | "text": "My text editor, a stripped down, simpler version of vim",
173 | "score": 7,
174 | "created_time": 1505447807,
175 | "attached_image": "",
176 | "num_comments": 9,
177 | "tags": [
178 | "wk69"
179 | ],
180 | "vote_state": 0,
181 | "edited": false,
182 | "rt": 1,
183 | "user_id": 426559,
184 | "user_username": "leolas95",
185 | "user_score": 227,
186 | "user_avatar": {
187 | "b": "2a8b9d",
188 | "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"
189 | }
190 | },
191 | {
192 | "id": 840841,
193 | "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.",
194 | "score": 6,
195 | "created_time": 1505435185,
196 | "attached_image": "",
197 | "num_comments": 3,
198 | "tags": [
199 | "wk69"
200 | ],
201 | "vote_state": 0,
202 | "edited": false,
203 | "rt": 1,
204 | "user_id": 306082,
205 | "user_username": "stackodev",
206 | "user_score": 3431,
207 | "user_avatar": {
208 | "b": "a973a2",
209 | "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"
210 | }
211 | },
212 | {
213 | "id": 840611,
214 | "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.",
215 | "score": 6,
216 | "created_time": 1505422334,
217 | "attached_image": "",
218 | "num_comments": 0,
219 | "tags": [
220 | "wk69"
221 | ],
222 | "vote_state": 0,
223 | "edited": false,
224 | "rt": 1,
225 | "user_id": 685721,
226 | "user_username": "tonakriz",
227 | "user_score": 6,
228 | "user_avatar": {
229 | "b": "7bc8a4"
230 | }
231 | },
232 | {
233 | "id": 840448,
234 | "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 😥",
235 | "score": 2,
236 | "created_time": 1505417715,
237 | "attached_image": "",
238 | "num_comments": 1,
239 | "tags": [
240 | "wk69"
241 | ],
242 | "vote_state": 0,
243 | "edited": false,
244 | "rt": 1,
245 | "user_id": 839356,
246 | "user_username": "CyclingMatt",
247 | "user_score": 58,
248 | "user_avatar": {
249 | "b": "ecd276",
250 | "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"
251 | }
252 | },
253 | {
254 | "id": 840417,
255 | "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 😀",
256 | "score": 6,
257 | "created_time": 1505416656,
258 | "attached_image": "",
259 | "num_comments": 0,
260 | "tags": [
261 | "69",
262 | "wk69"
263 | ],
264 | "vote_state": 0,
265 | "edited": false,
266 | "rt": 1,
267 | "user_id": 762698,
268 | "user_username": "deltam",
269 | "user_score": 107,
270 | "user_avatar": {
271 | "b": "2a8b9d",
272 | "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"
273 | }
274 | },
275 | {
276 | "id": 843407,
277 | "text": "3d hack & slash game with smooth animation like \"DMC\" xD",
278 | "score": 0,
279 | "created_time": 1505541740,
280 | "attached_image": "",
281 | "num_comments": 0,
282 | "tags": [
283 | "wk69"
284 | ],
285 | "vote_state": 0,
286 | "edited": false,
287 | "rt": 1,
288 | "user_id": 22066,
289 | "user_username": "CSaratakij",
290 | "user_score": 2123,
291 | "user_avatar": {
292 | "b": "7bc8a4",
293 | "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"
294 | }
295 | },
296 | {
297 | "id": 842119,
298 | "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)",
299 | "score": 0,
300 | "created_time": 1505484507,
301 | "attached_image": "",
302 | "num_comments": 0,
303 | "tags": [
304 | "wk69"
305 | ],
306 | "vote_state": 0,
307 | "edited": false,
308 | "rt": 1,
309 | "user_id": 18663,
310 | "user_username": "jester5537",
311 | "user_score": 567,
312 | "user_avatar": {
313 | "b": "a973a2",
314 | "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"
315 | }
316 | },
317 | {
318 | "id": 840690,
319 | "text": "Fucking behemoth(check the collab)",
320 | "score": 1,
321 | "created_time": 1505425449,
322 | "attached_image": "",
323 | "num_comments": 0,
324 | "tags": [
325 | "wk69"
326 | ],
327 | "vote_state": 0,
328 | "edited": true,
329 | "rt": 1,
330 | "user_id": 180116,
331 | "user_username": "Data-Bound",
332 | "user_score": 2833,
333 | "user_avatar": {
334 | "b": "2a8b9d",
335 | "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"
336 | }
337 | },
338 | {
339 | "id": 840378,
340 | "text": "Project I'd like to finish?\nhttps://play.google.com/store/apps/...",
341 | "score": 5,
342 | "created_time": 1505415222,
343 | "attached_image": "",
344 | "num_comments": 3,
345 | "tags": [
346 | "wk69"
347 | ],
348 | "vote_state": 0,
349 | "edited": false,
350 | "rt": 1,
351 | "links": [
352 | {
353 | "type": "url",
354 | "url": "https://play.google.com/store/apps/details?id=at.armino28.tminer",
355 | "short_url": "https://play.google.com/store/apps/...",
356 | "title": "https://play.google.com/store/apps/...",
357 | "start": 28,
358 | "end": 66,
359 | "special": 1
360 | }
361 | ],
362 | "special": true,
363 | "user_id": 686066,
364 | "user_username": "ao28",
365 | "user_score": 140,
366 | "user_avatar": {
367 | "b": "f99a66",
368 | "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"
369 | }
370 | },
371 | {
372 | "id": 840008,
373 | "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.",
374 | "score": 4,
375 | "created_time": 1505402580,
376 | "attached_image": "",
377 | "num_comments": 4,
378 | "tags": [
379 | "cobol",
380 | "wk69",
381 | "cics",
382 | "mainframe"
383 | ],
384 | "vote_state": 0,
385 | "edited": true,
386 | "rt": 1,
387 | "user_id": 578534,
388 | "user_username": "Loeina",
389 | "user_score": 62,
390 | "user_avatar": {
391 | "b": "d55161",
392 | "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"
393 | }
394 | },
395 | {
396 | "id": 839846,
397 | "text": "Remaking my portfolio site using VueJS. Finish that character design sketchbook I started years ago. And move out of my parents' house.",
398 | "score": 7,
399 | "created_time": 1505397975,
400 | "attached_image": "",
401 | "num_comments": 0,
402 | "tags": [
403 | "wk69"
404 | ],
405 | "vote_state": 0,
406 | "edited": false,
407 | "rt": 1,
408 | "user_id": 729633,
409 | "user_username": "KyrePh",
410 | "user_score": 70,
411 | "user_avatar": {
412 | "b": "f99a66",
413 | "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"
414 | }
415 | },
416 | {
417 | "id": 839365,
418 | "text": "My personal resume website. Damn my lazy ass, can't do anything. 😥",
419 | "score": 16,
420 | "created_time": 1505380191,
421 | "attached_image": "",
422 | "num_comments": 3,
423 | "tags": [
424 | "wk69"
425 | ],
426 | "vote_state": 0,
427 | "edited": false,
428 | "rt": 1,
429 | "user_id": 674260,
430 | "user_username": "NoLifeSigns",
431 | "user_score": 394,
432 | "user_avatar": {
433 | "b": "69c9cd",
434 | "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"
435 | }
436 | },
437 | {
438 | "id": 839231,
439 | "text": "A script which spiders hotstar and navigate through the links in command line to obtain links..",
440 | "score": 0,
441 | "created_time": 1505375435,
442 | "attached_image": "",
443 | "num_comments": 0,
444 | "tags": [
445 | "wk69"
446 | ],
447 | "vote_state": 0,
448 | "edited": false,
449 | "rt": 1,
450 | "user_id": 709788,
451 | "user_username": "aEEEdev",
452 | "user_score": 405,
453 | "user_avatar": {
454 | "b": "7bc8a4",
455 | "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"
456 | }
457 | }
458 | ],
459 | "settings": [],
460 | "news": {
461 | "id": 0,
462 | "type": "weekly",
463 | "headline": "Side project you wish you would start/finish?",
464 | "footer": "Week 69 Group Rant - Add tag 'wk69' to your rant",
465 | "height": 65,
466 | "action": "none"
467 | }
468 | }
--------------------------------------------------------------------------------
/src/test/resources/rant-686001.json:
--------------------------------------------------------------------------------
1 | {
2 | "rant": {
3 | "id": 686001,
4 | "text": "I only just noticed this is on the git man page :P",
5 | "score": 84,
6 | "created_time": 1498915304,
7 | "attached_image": {
8 | "url": "https://img.devrant.io/devrant/rant/r_686001_VfN7X.jpg",
9 | "width": 530,
10 | "height": 134
11 | },
12 | "num_comments": 5,
13 | "tags": [
14 | "terminal",
15 | "manual",
16 | "git"
17 | ],
18 | "vote_state": 0,
19 | "edited": false,
20 | "link": "rants/686001/i-only-just-noticed-this-is-on-the-git-man-page-p",
21 | "user_id": 102959,
22 | "user_username": "LucaScorpion",
23 | "user_score": 3831,
24 | "user_avatar": {
25 | "b": "7bc8a4",
26 | "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"
27 | },
28 | "user_dpp": 1
29 | },
30 | "comments": [
31 | {
32 | "id": 686175,
33 | "rant_id": 686001,
34 | "body": "Hahaha funny. I see the Torvals touch.",
35 | "score": 14,
36 | "created_time": 1498921583,
37 | "vote_state": 0,
38 | "user_id": 610004,
39 | "user_username": "edwrodrig",
40 | "user_score": 1710,
41 | "user_avatar": {
42 | "b": "69c9cd",
43 | "i": "v-17_c-3_b-6_g-m_9-1_1-1_16-4_3-3_8-2_7-2_5-1_12-1_6-31_2-27_15-19_11-2_18-1_4-1_19-2.jpg"
44 | },
45 | "attached_image": {
46 | "url": "https://img.devrant.io/devrant/rant/c_686175_yiid2.jpg",
47 | "width": 540,
48 | "height": 960
49 | }
50 | },
51 | {
52 | "id": 686485,
53 | "rant_id": 686001,
54 | "body": "It's so stupid that it requires a genius to understand it 😅",
55 | "score": 2,
56 | "created_time": 1498934323,
57 | "vote_state": 0,
58 | "user_id": 644830,
59 | "user_username": "Condor",
60 | "user_score": 709,
61 | "user_avatar": {
62 | "b": "69c9cd",
63 | "i": "v-17_c-3_b-6_g-m_9-1_1-2_16-3_3-2_8-1_7-1_5-1_12-2_6-98_2-26_15-15_18-2_4-1_19-1.jpg"
64 | },
65 | "user_dpp": 1
66 | },
67 | {
68 | "id": 686655,
69 | "rant_id": 686001,
70 | "body": "lool",
71 | "score": 0,
72 | "created_time": 1498943603,
73 | "vote_state": 0,
74 | "user_id": 433094,
75 | "user_username": "gitpush",
76 | "user_score": 8533,
77 | "user_avatar": {
78 | "b": "2a8b9d",
79 | "i": "v-17_c-3_b-4_g-m_9-1_1-1_16-15_3-1_8-4_7-4_5-4_12-1_6-11_10-8_2-39_15-7_11-7_18-4_4-4_19-3_20-7_21-2.jpg"
80 | }
81 | },
82 | {
83 | "id": 686786,
84 | "rant_id": 686001,
85 | "body": "@edwrodrig \nLol u couldn't even wait to get to a terminal on a pc, you did it on phone.\nI did the same though 🤣",
86 | "score": 1,
87 | "created_time": 1498950365,
88 | "vote_state": 0,
89 | "user_id": 432779,
90 | "user_username": "DonMcCoy",
91 | "user_score": 618,
92 | "user_avatar": {
93 | "b": "2a8b9d",
94 | "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_17-1_6-10_10-4_2-10_15-10_18-1_4-1_19-1.jpg"
95 | }
96 | },
97 | {
98 | "id": 687462,
99 | "rant_id": 686001,
100 | "body": "svn must be the one for geniuses. \nThat's how come I could never wrap my head around it",
101 | "score": 0,
102 | "created_time": 1498990973,
103 | "vote_state": 0,
104 | "user_id": 669377,
105 | "user_username": "fykto",
106 | "user_score": 64,
107 | "user_avatar": {
108 | "b": "2a8b9d",
109 | "i": "v-17_c-3_b-4_g-m_9-1_1-12_16-4_3-3_8-1_7-1_5-1_12-12_6-42_2-39_15-10_11-2_4-1.jpg"
110 | }
111 | }
112 | ],
113 | "success": true
114 | }
115 |
--------------------------------------------------------------------------------
/src/test/resources/rant-invalid.json:
--------------------------------------------------------------------------------
1 | {
2 | "success": false,
3 | "error": "Invalid rant specified in path."
4 | }
--------------------------------------------------------------------------------
/src/test/resources/rant-surprise.json:
--------------------------------------------------------------------------------
1 | {
2 | "success": true,
3 | "rant": {
4 | "id": 26356,
5 | "text": "Life of a software engineer.",
6 | "score": 91,
7 | "created_time": 1463361116,
8 | "attached_image": {
9 | "url": "https://img.devrant.io/devrant/rant/r_26356_N2S4f.jpg",
10 | "width": 504,
11 | "height": 381
12 | },
13 | "num_comments": 1,
14 | "tags": [],
15 | "vote_state": 0,
16 | "edited": false,
17 | "user_id": 18401,
18 | "user_username": "nhpace",
19 | "user_score": 218,
20 | "user_avatar": {
21 | "b": "2a8b9d"
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/src/test/resources/user-102959.json:
--------------------------------------------------------------------------------
1 | {
2 | "success": true,
3 | "profile": {
4 | "username": "LucaScorpion",
5 | "score": 3831,
6 | "about": "Software developer, fanatic programmer, hardcore gamer, Linux lover.",
7 | "location": "Netherlands",
8 | "created_time": 1468222507,
9 | "skills": "C#, Java, PHP, Javascript, HTML, CSS, SQL, C++ (Arduino), Bash",
10 | "github": "LucaScorpion",
11 | "website": "https://scorpiac.com",
12 | "content": {
13 | "content": {
14 | "rants": [
15 | {
16 | "id": 734149,
17 | "text": "Learning something new every day! This is pretty cool.",
18 | "score": 27,
19 | "created_time": 1500923505,
20 | "attached_image": {
21 | "url": "https://img.devrant.io/devrant/rant/r_734149_V8bT3.gif",
22 | "width": 600,
23 | "height": 400,
24 | "frame": "https://img.devrant.io/devrant/rant/frame_r_734149_V8bT3.jpg"
25 | },
26 | "num_comments": 13,
27 | "tags": [
28 | "bash",
29 | "linux"
30 | ],
31 | "vote_state": 0,
32 | "edited": false,
33 | "user_id": 102959,
34 | "user_username": "LucaScorpion",
35 | "user_score": 3831,
36 | "user_avatar": {
37 | "b": "7bc8a4",
38 | "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"
39 | },
40 | "user_dpp": 1
41 | },
42 | {
43 | "id": 731665,
44 | "text": "I'm such a fucking idiot.",
45 | "score": 83,
46 | "created_time": 1500824167,
47 | "attached_image": {
48 | "url": "https://img.devrant.io/devrant/rant/r_731665_BoGHq.jpg",
49 | "width": 505,
50 | "height": 476
51 | },
52 | "num_comments": 22,
53 | "tags": [
54 | "rm -rf",
55 | "linux",
56 | "fuck"
57 | ],
58 | "vote_state": 0,
59 | "edited": false,
60 | "user_id": 102959,
61 | "user_username": "LucaScorpion",
62 | "user_score": 3831,
63 | "user_avatar": {
64 | "b": "7bc8a4",
65 | "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"
66 | },
67 | "user_dpp": 1
68 | }
69 | ],
70 | "upvoted": [
71 | {
72 | "id": 814409,
73 | "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.",
74 | "score": 4,
75 | "created_time": 1504331967,
76 | "attached_image": "",
77 | "num_comments": 3,
78 | "tags": [
79 | "coverrant",
80 | "rantfuscator",
81 | "rantvelop",
82 | "rantinator",
83 | "names are hard"
84 | ],
85 | "vote_state": 0,
86 | "edited": false,
87 | "links": [
88 | {
89 | "type": "url",
90 | "url": "http://jsrant.com",
91 | "short_url": "http://jsrant.com",
92 | "title": "http://jsrant.com",
93 | "start": 86,
94 | "end": 103,
95 | "special": 1
96 | }
97 | ],
98 | "special": true,
99 | "user_id": 99671,
100 | "user_username": "ChappIO",
101 | "user_score": 4177,
102 | "user_avatar": {
103 | "b": "2a8b9d",
104 | "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"
105 | },
106 | "user_dpp": 1
107 | },
108 | {
109 | "id": 805044,
110 | "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.",
111 | "score": 10,
112 | "created_time": 1503929002,
113 | "attached_image": "",
114 | "num_comments": 0,
115 | "tags": [
116 | "late to the party",
117 | "wk65"
118 | ],
119 | "vote_state": 0,
120 | "edited": false,
121 | "user_id": 99671,
122 | "user_username": "ChappIO",
123 | "user_score": 4177,
124 | "user_avatar": {
125 | "b": "2a8b9d",
126 | "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"
127 | },
128 | "user_dpp": 1
129 | }
130 | ],
131 | "comments": [
132 | {
133 | "id": 769281,
134 | "rant_id": 768728,
135 | "body": "Another option is put comments a field that won't be deserialized. But either way it's not a good idea.",
136 | "score": 0,
137 | "created_time": 1502451860,
138 | "vote_state": 0,
139 | "user_id": 102959,
140 | "user_username": "LucaScorpion",
141 | "user_score": 3831,
142 | "user_avatar": {
143 | "b": "7bc8a4",
144 | "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"
145 | },
146 | "user_dpp": 1
147 | },
148 | {
149 | "id": 769277,
150 | "rant_id": 769071,
151 | "body": "Code as if your application is going to be used for years.",
152 | "score": 7,
153 | "created_time": 1502451709,
154 | "vote_state": 0,
155 | "user_id": 102959,
156 | "user_username": "LucaScorpion",
157 | "user_score": 3831,
158 | "user_avatar": {
159 | "b": "7bc8a4",
160 | "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"
161 | },
162 | "user_dpp": 1
163 | }
164 | ],
165 | "favorites": []
166 | },
167 | "counts": {
168 | "rants": 60,
169 | "upvoted": 5103,
170 | "comments": 800,
171 | "favorites": 36,
172 | "collabs": 0
173 | }
174 | },
175 | "avatar": {
176 | "b": "7bc8a4",
177 | "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"
178 | },
179 | "dpp": 1
180 | }
181 | }
--------------------------------------------------------------------------------
/src/test/resources/user-id-LucaScorpion.json:
--------------------------------------------------------------------------------
1 | {
2 | "success": true,
3 | "user_id": 102959
4 | }
--------------------------------------------------------------------------------
/src/test/resources/user-id-invalid.json:
--------------------------------------------------------------------------------
1 | {
2 | "success": false
3 | }
--------------------------------------------------------------------------------
/src/test/resources/user-invalid.json:
--------------------------------------------------------------------------------
1 | {
2 | "success": false,
3 | "error": "Invalid user specified in path."
4 | }
--------------------------------------------------------------------------------
/src/test/resources/vote-comment-none-843736.json:
--------------------------------------------------------------------------------
1 | {
2 | "success": true,
3 | "comment": {
4 | "id": 843736,
5 | "rant_id": 843654,
6 | "body": "Cli|pIt can\nsave your",
7 | "score": 0,
8 | "created_time": 1505556099,
9 | "vote_state": 0,
10 | "user_id": 178022,
11 | "user_username": "filthyranter",
12 | "user_score": 8837,
13 | "user_avatar": {
14 | "b": "2a8b9d",
15 | "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"
16 | },
17 | "user_dpp": 1
18 | }
19 | }
--------------------------------------------------------------------------------
/src/test/resources/vote-rant-down-843654.json:
--------------------------------------------------------------------------------
1 | {
2 | "success": true,
3 | "rant": {
4 | "id": 843654,
5 | "text": "I guess that Devuan LXDE install was kind of a fail 😂",
6 | "score": 1,
7 | "created_time": 1505551627,
8 | "attached_image": {
9 | "url": "https://img.devrant.io/devrant/rant/r_843654_FYnEy.jpg",
10 | "width": 799,
11 | "height": 449
12 | },
13 | "num_comments": 1,
14 | "tags": [
15 | "linux",
16 | "devuan",
17 | "lxde"
18 | ],
19 | "vote_state": -1,
20 | "edited": false,
21 | "rt": 1,
22 | "user_id": 749677,
23 | "user_username": "kenogo",
24 | "user_score": 699,
25 | "user_avatar": {
26 | "b": "f99a66",
27 | "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"
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/src/test/resources/vote-rant-up-843654.json:
--------------------------------------------------------------------------------
1 | {
2 | "success": true,
3 | "rant": {
4 | "id": 843654,
5 | "text": "I guess that Devuan LXDE install was kind of a fail 😂",
6 | "score": 1,
7 | "created_time": 1505551627,
8 | "attached_image": {
9 | "url": "https://img.devrant.io/devrant/rant/r_843654_FYnEy.jpg",
10 | "width": 799,
11 | "height": 449
12 | },
13 | "num_comments": 1,
14 | "tags": [
15 | "linux",
16 | "devuan",
17 | "lxde"
18 | ],
19 | "vote_state": 1,
20 | "edited": false,
21 | "rt": 1,
22 | "user_id": 749677,
23 | "user_username": "kenogo",
24 | "user_score": 699,
25 | "user_avatar": {
26 | "b": "f99a66",
27 | "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"
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------