Skip to content

Commit 95b9c80

Browse files
committed
Extended parameter validation and FindBugs annotations. Validation issues will now be handled as RuntimeException.
1 parent 5a480cd commit 95b9c80

23 files changed

+253
-133
lines changed

src/main/java/com/github/m0nk3y2k4/thetvdb/api/QueryParameters.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public interface QueryParameters extends Iterable<QueryParameters.Parameter> {
3939
*
4040
* @return Optional containing the current value for the given key or {@link Optional#empty()} if no parameter with this key exists
4141
*/
42-
Optional<String> getParameterValue(String key);
42+
Optional<String> getParameterValue(@Nonnull String key);
4343

4444
/**
4545
* Returns <code>true</code> if an individual parameter with the given key exists in this very object or <code>false</code> if no such parameter has been added yet.
@@ -48,7 +48,7 @@ public interface QueryParameters extends Iterable<QueryParameters.Parameter> {
4848
*
4949
* @return <code>True</code> if a parameter with the given key has already been added to this object or <code>false</code> if not
5050
*/
51-
boolean containsParameter(String key);
51+
boolean containsParameter(@Nonnull String key);
5252

5353
/**
5454
* Returns a Stream of individual query parameters hold by this class.

src/main/java/com/github/m0nk3y2k4/thetvdb/api/TheTVDBApi.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -399,15 +399,15 @@ public interface TheTVDBApi {
399399

400400
List<Image> queryImages(long seriesId, QueryParameters queryParameters) throws APIException;
401401

402-
List<Image> queryImages(long seriesId, String keyType, String resolution) throws APIException;
402+
List<Image> queryImages(long seriesId, @Nonnull String keyType, @Nonnull String resolution) throws APIException;
403403

404-
List<Image> queryImages(long seriesId, String keyType, String resolution, String subKey) throws APIException;
404+
List<Image> queryImages(long seriesId, @Nonnull String keyType, @Nonnull String resolution, @Nonnull String subKey) throws APIException;
405405

406-
List<Image> queryImagesByKeyType(long seriesId, String keyType) throws APIException;
406+
List<Image> queryImagesByKeyType(long seriesId, @Nonnull String keyType) throws APIException;
407407

408-
List<Image> queryImagesByResolution(long seriesId, String resolution) throws APIException;
408+
List<Image> queryImagesByResolution(long seriesId, @Nonnull String resolution) throws APIException;
409409

410-
List<Image> queryImagesBySubKey(long seriesId, String subKey) throws APIException;
410+
List<Image> queryImagesBySubKey(long seriesId, @Nonnull String subKey) throws APIException;
411411

412412
List<ImageQueryParameter> getAvailableImageQueryParameters(long seriesId) throws APIException;
413413

@@ -851,6 +851,8 @@ interface Extended {
851851

852852
APIResponse<List<String>> getAvailableRatingsQueryParameters() throws APIException;
853853

854+
void deleteFromRatings(@Nonnull String itemType, long itemId) throws APIException;
855+
854856
APIResponse<List<Rating>> addToRatings(@Nonnull String itemType, long itemId, long itemRating) throws APIException;
855857
}
856858
}

src/main/java/com/github/m0nk3y2k4/thetvdb/api/exception/APIException.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44

55
public class APIException extends Exception {
66

7-
public static final String API_NOT_AUTHORIZED_ERROR = "Missing authorization or expired JWT token (HTTP-401). Original API error message: %s";
7+
protected static final String API_NOT_AUTHORIZED_ERROR = "Missing authorization or expired JWT token (HTTP-401). Original API error message: %s";
8+
89
public static final String API_NOT_FOUND_ERROR = "Requested resource could not be found (HTTP-404). Original API error message: %s";
910
public static final String API_CONFLICT_ERROR = "Update request caused a conflict (HTTP-409). Original API error message: %s";
10-
public static final String API_VALIDATION_ERROR = "Request could not be processed due to invalid/missing parameters: %s";
1111
public static final String API_SERVICE_UNAVAILABLE = "API Service is currently unavailable. Please try again later.";
1212
public static final String API_JSON_PARSE_ERROR = "Error while parsing JSON from response";
1313

src/main/java/com/github/m0nk3y2k4/thetvdb/api/exception/APIRuntimeException.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
11
package com.github.m0nk3y2k4.thetvdb.api.exception;
22

3+
import javax.annotation.Nonnull;
4+
35
public class APIRuntimeException extends RuntimeException {
46

7+
protected static final String API_VALIDATION_ERROR = "Request could not be processed due to invalid/missing parameters: %s";
8+
9+
public APIRuntimeException(String message) {
10+
super(message);
11+
}
12+
13+
public APIRuntimeException(@Nonnull String message, @Nonnull String details) {
14+
super(String.format(message, details));
15+
}
16+
517
public APIRuntimeException(String message, Exception ex) {
618
super(message, ex);
719
}

src/main/java/com/github/m0nk3y2k4/thetvdb/internal/api/impl/QueryParametersImpl.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package com.github.m0nk3y2k4.thetvdb.internal.api.impl;
22

33
import com.github.m0nk3y2k4.thetvdb.api.QueryParameters;
4+
import com.github.m0nk3y2k4.thetvdb.internal.resource.validation.ObjectValidator;
45

56
import javax.annotation.Nonnull;
7+
68
import java.util.*;
79
import java.util.stream.Stream;
810
import java.util.stream.StreamSupport;
@@ -31,25 +33,27 @@ public class QueryParametersImpl implements QueryParameters {
3133
public QueryParametersImpl(@Nonnull Map<String, String> parameters) {
3234
super();
3335

34-
Objects.requireNonNull(parameters, "Parameters map must not be NULL");
36+
ObjectValidator.requireNonNull(parameters, "Parameters map must not be NULL");
3537

3638
parameters.forEach(this::addParameter);
3739
}
3840

3941
@Override
4042
public QueryParameters addParameter(@Nonnull String key, @Nonnull String value) {
41-
Objects.requireNonNull(key, "Parameter key must not be NULL");
42-
Objects.requireNonNull(value, "Parameter value must not be NULL");
43+
ObjectValidator.requireNonNull(key, "Parameter key must not be NULL");
44+
ObjectValidator.requireNonNull(value, "Parameter value must not be NULL");
4345

4446
params.put(key, value);
4547
return this;
4648
}
4749

4850
@Override
49-
public Optional<String> getParameterValue(String key) { return Optional.ofNullable(params.get(key)); }
51+
public Optional<String> getParameterValue(@Nonnull String key) {
52+
return Optional.ofNullable(params.get(key));
53+
}
5054

5155
@Override
52-
public boolean containsParameter(String key) {
56+
public boolean containsParameter(@Nonnull String key) {
5357
return params.containsKey(key);
5458
}
5559

src/main/java/com/github/m0nk3y2k4/thetvdb/internal/api/impl/TheTVDBApiImpl.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -242,31 +242,31 @@ public List<Image> queryImages(long seriesId, QueryParameters queryParameters) t
242242
}
243243

244244
@Override
245-
public List<Image> queryImages(long seriesId, String keyType, String resolution) throws APIException {
245+
public List<Image> queryImages(long seriesId, @Nonnull String keyType, @Nonnull String resolution) throws APIException {
246246
validateNotEmpty(keyType, resolution);
247247
return queryImages(seriesId, query(Map.of(Query.Series.KEYTYPE, keyType, Query.Series.RESOLUTION, resolution)));
248248
}
249249

250250
@Override
251-
public List<Image> queryImages(long seriesId, String keyType, String resolution, String subKey) throws APIException {
251+
public List<Image> queryImages(long seriesId, @Nonnull String keyType, @Nonnull String resolution, @Nonnull String subKey) throws APIException {
252252
validateNotEmpty(keyType, resolution, subKey);
253253
return queryImages(seriesId, query(Map.of(Query.Series.KEYTYPE, keyType, Query.Series.RESOLUTION, resolution, Query.Series.SUBKEY, subKey)));
254254
}
255255

256256
@Override
257-
public List<Image> queryImagesByKeyType(long seriesId, String keyType) throws APIException {
257+
public List<Image> queryImagesByKeyType(long seriesId, @Nonnull String keyType) throws APIException {
258258
validateNotEmpty(keyType);
259259
return queryImages(seriesId, query(Map.of(Query.Series.KEYTYPE, keyType)));
260260
}
261261

262262
@Override
263-
public List<Image> queryImagesByResolution(long seriesId, String resolution) throws APIException {
263+
public List<Image> queryImagesByResolution(long seriesId, @Nonnull String resolution) throws APIException {
264264
validateNotEmpty(resolution);
265265
return queryImages(seriesId, query(Map.of(Query.Series.RESOLUTION, resolution)));
266266
}
267267

268268
@Override
269-
public List<Image> queryImagesBySubKey(long seriesId, String subKey) throws APIException {
269+
public List<Image> queryImagesBySubKey(long seriesId, @Nonnull String subKey) throws APIException {
270270
validateNotEmpty(subKey);
271271
return queryImages(seriesId, query(Map.of(Query.Series.SUBKEY, subKey)));
272272
}
@@ -341,7 +341,7 @@ public List<String> getAvailableRatingsQueryParameters() throws APIException {
341341

342342
@Override
343343
public void deleteFromRatings(@Nonnull String itemType, long itemId) throws APIException {
344-
json().deleteFromRatings(itemType, itemId);
344+
extended().deleteFromRatings(itemType, itemId);
345345
}
346346

347347
@Override
@@ -644,6 +644,11 @@ public APIResponse<List<String>> getAvailableRatingsQueryParameters() throws API
644644
return JsonDeserializer.mapQueryParameters(json().getAvailableRatingsQueryParameters());
645645
}
646646

647+
@Override
648+
public void deleteFromRatings(@Nonnull String itemType, long itemId) throws APIException {
649+
json().deleteFromRatings(itemType, itemId);
650+
}
651+
647652
@Override
648653
public APIResponse<List<Rating>> addToRatings(@Nonnull String itemType, long itemId, long itemRating) throws APIException {
649654
return JsonDeserializer.mapRatings(json().addToRatings(itemType, itemId, itemRating));

src/main/java/com/github/m0nk3y2k4/thetvdb/internal/connection/APIConnection.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.github.m0nk3y2k4.thetvdb.api.exception.APIException;
2828
import com.github.m0nk3y2k4.thetvdb.internal.exception.APINotAuthorizedException;
2929
import com.github.m0nk3y2k4.thetvdb.internal.resource.impl.AuthenticationAPI;
30+
import com.github.m0nk3y2k4.thetvdb.internal.resource.validation.ConnectionValidator;
3031

3132
public class APIConnection {
3233

@@ -158,10 +159,13 @@ void setSession(@Nonnull APISession session) {
158159
}
159160

160161
void openConnection(@Nonnull String resource, @Nonnull String requestMethod) throws IOException {
162+
ConnectionValidator.validateResource(resource);
163+
ConnectionValidator.validateRequestMethod(requestMethod);
164+
161165
con = (HttpsURLConnection) new URL(API_URL + resource).openConnection();
162166

163-
// POST or GET
164-
con.setRequestMethod(requestMethod);
167+
// POST, GET, DELETE, PUT,...
168+
con.setRequestMethod(requestMethod.toUpperCase());
165169

166170
// Request properties for API
167171
con.setRequestProperty("Content-Type", "application/json; charset=utf-8");
@@ -241,6 +245,7 @@ JsonNode send() throws APIException {
241245
}
242246

243247
final class PostRequest extends APIRequest {
248+
244249
/** Messages for error/exception handling */
245250
private static final String ERR_POST = "An exception occurred while sending POST request to API";
246251

@@ -268,6 +273,8 @@ JsonNode send() throws APIException {
268273
}
269274

270275
private void writeRequestBody(@Nonnull String data) throws IOException {
276+
ConnectionValidator.validatePayload(data);
277+
271278
con.setDoOutput(true);
272279

273280
try (OutputStream os = con.getOutputStream()) {
@@ -278,6 +285,7 @@ private void writeRequestBody(@Nonnull String data) throws IOException {
278285
}
279286

280287
final class HeadRequest extends APIRequest {
288+
281289
/** Messages for error/exception handling */
282290
private static final String ERR_HEAD = "An exception occurred while sending HEAD request to API";
283291

@@ -326,6 +334,7 @@ private JsonNode createJsonFromHeaderFields() {
326334
}
327335

328336
final class DeleteRequest extends APIRequest {
337+
329338
/** Messages for error/exception handling */
330339
private static final String ERR_DELETE = "An exception occurred while sending DELETE request to API";
331340

@@ -348,6 +357,7 @@ JsonNode send() throws APIException {
348357
}
349358

350359
final class PutRequest extends APIRequest {
360+
351361
/** Messages for error/exception handling */
352362
private static final String ERR_PUT = "An exception occurred while sending PUT request to API";
353363

src/main/java/com/github/m0nk3y2k4/thetvdb/internal/connection/APISession.java

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package com.github.m0nk3y2k4.thetvdb.internal.connection;
22

33
import com.github.m0nk3y2k4.thetvdb.api.exception.APIException;
4+
import com.github.m0nk3y2k4.thetvdb.internal.resource.validation.ObjectValidator;
45
import com.github.m0nk3y2k4.thetvdb.internal.util.APIUtil;
56

67
import javax.annotation.Nonnull;
7-
import java.util.Objects;
88
import java.util.Optional;
99
import java.util.regex.Pattern;
1010

@@ -27,7 +27,16 @@ public final class APISession {
2727
* login/refresh requests may be allowed. During the execution of these kind of requests, the session authorization is in progress.
2828
* In case the login/refresh were successful the session is authorized and ready for general API communication.
2929
*/
30-
public enum Status {NOT_AUTHORIZED, AUTHORIZATION_IN_PROGRESS, AUTHORIZED}
30+
public enum Status {
31+
/** Session authorization is pending */
32+
NOT_AUTHORIZED,
33+
34+
/** Session authorization is currently in progress */
35+
AUTHORIZATION_IN_PROGRESS,
36+
37+
/** Session authorization completed successfully */
38+
AUTHORIZED
39+
}
3140

3241
/** Accept english by default if no language was specified */
3342
private static final String DEFAULT_LANGUAGE = "en";
@@ -62,7 +71,7 @@ public enum Status {NOT_AUTHORIZED, AUTHORIZATION_IN_PROGRESS, AUTHORIZED}
6271
* @param apiKey The API key used to request a session token
6372
*/
6473
APISession(@Nonnull String apiKey) {
65-
Objects.requireNonNull(apiKey, "API key must not be NULL or empty!");
74+
ObjectValidator.requireNonEmpty(apiKey, "API key must not be NULL or empty!");
6675

6776
this.apiKey = apiKey;
6877
this.userKey = null;
@@ -81,9 +90,9 @@ public enum Status {NOT_AUTHORIZED, AUTHORIZATION_IN_PROGRESS, AUTHORIZED}
8190
* @param userName User name for authentication
8291
*/
8392
APISession(@Nonnull String apiKey, @Nonnull String userKey, @Nonnull String userName) {
84-
Objects.requireNonNull(apiKey, "API key must not be NULL or empty!");
85-
Objects.requireNonNull(userKey, "User key must not be NULL or empty!");
86-
Objects.requireNonNull(userName, "User name must not be NULL or empty!");
93+
ObjectValidator.requireNonEmpty(apiKey, "API key must not be NULL or empty!");
94+
ObjectValidator.requireNonEmpty(userKey, "User key must not be NULL or empty!");
95+
ObjectValidator.requireNonEmpty(userName, "User name must not be NULL or empty!");
8796

8897
this.apiKey = apiKey;
8998
this.userKey = userKey;
@@ -140,12 +149,13 @@ void setToken(@Nonnull String token) throws APIException {
140149
}
141150

142151
/**
143-
* Set the preferred language used for API communication. Search results will be based on this language.
152+
* Set the preferred language used for API communication. Search results will be based on this language. If the given
153+
* language parameter is <code>Null</code> the sessions language will be reset to {@link #DEFAULT_LANGUAGE}.
144154
*
145155
* @param language The language for API communication
146156
*/
147157
void setLanguage(String language) {
148-
this.language = language;
158+
this.language = language != null ? language : DEFAULT_LANGUAGE;
149159
}
150160

151161
/**
@@ -158,11 +168,14 @@ String getLanguage() {
158168
}
159169

160170
/**
161-
* Sets the current status of this session
171+
* Sets the current status of this session. If the given status parameter is <code>Null</code> the sessions
172+
* status will be reset to {@link Status#NOT_AUTHORIZED}.
162173
*
163174
* @param status The new session status
164175
*/
165-
void setStatus(Status status) { this.status = status; }
176+
void setStatus(Status status) {
177+
this.status = status != null ? status : Status.NOT_AUTHORIZED;
178+
}
166179
/**
167180
* Returns the current {@link Status} of this session. This status indicates that...
168181
* <p/>

src/main/java/com/github/m0nk3y2k4/thetvdb/internal/exception/APIValidationException.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package com.github.m0nk3y2k4.thetvdb.internal.exception;
22

3-
import com.github.m0nk3y2k4.thetvdb.api.exception.APIException;
3+
import com.github.m0nk3y2k4.thetvdb.api.exception.APIRuntimeException;
44

55
import javax.annotation.Nonnull;
66

7-
public final class APIValidationException extends APIException {
7+
public final class APIValidationException extends APIRuntimeException {
88

99
public APIValidationException(@Nonnull String error) {
1010
super(API_VALIDATION_ERROR, error);

src/main/java/com/github/m0nk3y2k4/thetvdb/internal/resource/QueryResource.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@
1717

1818
public abstract class QueryResource extends Resource {
1919

20+
protected static String createQueryResource(@Nonnull String base, @CheckForNull QueryParameters queryParams) {
21+
return base + createQuery(queryParams);
22+
}
23+
24+
protected static String createQueryResource(@Nonnull String base, @Nonnull String specific, @CheckForNull QueryParameters queryParams) {
25+
return createResource(base, specific) + createQuery(queryParams);
26+
}
27+
2028
private static String createQuery(@CheckForNull QueryParameters queryParams) {
2129
List<QueryParameters.Parameter> validParams = Optional.ofNullable(queryParams).orElse(TheTVDBApiFactory.createQueryParameters())
2230
.stream().filter(QueryResource::isValidQueryParameter).collect(Collectors.toList());
@@ -29,19 +37,11 @@ private static String createQuery(@CheckForNull QueryParameters queryParams) {
2937
return "";
3038
}
3139

32-
protected static String createQueryResource(@Nonnull String base, @CheckForNull QueryParameters queryParams) {
33-
return base + createQuery(queryParams);
34-
}
35-
36-
protected static String createQueryResource(@Nonnull String base, @Nonnull String specific, @CheckForNull QueryParameters queryParams) {
37-
return createResource(base, specific) + createQuery(queryParams);
38-
}
39-
40-
private static Boolean isValidQueryParameter(QueryParameters.Parameter param) {
40+
private static Boolean isValidQueryParameter(@Nonnull QueryParameters.Parameter param) {
4141
return APIUtil.hasValue(param.getKey(), param.getValue());
4242
}
4343

44-
private static String encode(String value) throws APIRuntimeException {
44+
private static String encode(@Nonnull String value) throws APIRuntimeException {
4545
try {
4646
return URLEncoder.encode(value, Charset.defaultCharset().name());
4747
} catch (UnsupportedEncodingException ex) {

0 commit comments

Comments
 (0)