All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.dv8tion.jda.api.exceptions.ErrorResponseException Maven / Gradle / Ivy

Go to download

Java wrapper for the popular chat & VOIP service: Discord https://discord.com

The newest version!
/*
 * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.dv8tion.jda.api.exceptions;

import net.dv8tion.jda.api.requests.ErrorResponse;
import net.dv8tion.jda.api.requests.Response;
import net.dv8tion.jda.api.requests.RestAction;
import net.dv8tion.jda.api.utils.data.DataArray;
import net.dv8tion.jda.api.utils.data.DataObject;
import net.dv8tion.jda.internal.utils.Checks;
import net.dv8tion.jda.internal.utils.Helpers;
import net.dv8tion.jda.internal.utils.JDALogger;

import javax.annotation.Nonnull;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * Indicates an unhandled error that is returned by Discord API Request using {@link net.dv8tion.jda.api.requests.RestAction RestAction}
 * 
It holds an {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponse} * * @see net.dv8tion.jda.api.exceptions.ErrorHandler */ public class ErrorResponseException extends RuntimeException { private final ErrorResponse errorResponse; private final Response response; private final String meaning; private final int code; private final List schemaErrors; /** * Creates a new ErrorResponseException instance * * @param errorResponse * The {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponse} corresponding * for the received error response from Discord * @param response * The Discord Response causing the ErrorResponse */ private ErrorResponseException(ErrorResponse errorResponse, Response response, int code, String meaning, List schemaErrors) { super(code + ": " + meaning + (schemaErrors.isEmpty() ? "" : "\n" + schemaErrors.stream().map(SchemaError::toString).collect(Collectors.joining("\n")))); this.response = response; if (response != null && response.getException() != null) initCause(response.getException()); this.errorResponse = errorResponse; this.code = code; this.meaning = meaning; this.schemaErrors = Collections.unmodifiableList(schemaErrors); } private ErrorResponseException(String message, ErrorResponseException cause) { super(cause.code + ": " + message, cause); this.response = cause.response; this.errorResponse = cause.errorResponse; this.code = cause.code; this.meaning = cause.meaning; this.schemaErrors = cause.schemaErrors; } /** * Whether this is an internal server error from discord (status 500) * * @return True, if this is an internal server error * {@link net.dv8tion.jda.api.requests.ErrorResponse#SERVER_ERROR ErrorResponse.SERVER_ERROR} */ public boolean isServerError() { return errorResponse == ErrorResponse.SERVER_ERROR; } /** * The meaning for this error. *
It is possible that the value from this method is different for {@link #isServerError() server errors} * * @return Never-null meaning of this error. */ @Nonnull public String getMeaning() { return meaning; } /** * The discord error code for this error response. * * @return The discord error code. * * @see Discord Error Codes */ public int getErrorCode() { return code; } /** * The {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponse} corresponding * for the received error response from Discord * * @return {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponse} */ @Nonnull public ErrorResponse getErrorResponse() { return errorResponse; } /** * The Discord Response causing the ErrorResponse * * @return {@link net.dv8tion.jda.api.requests.Response Response} */ @Nonnull public Response getResponse() { return response; } /** * The {@link SchemaError SchemaErrors} for this error response. *
These errors provide more context of what part in the body caused the error, and more explanation for the error itself. * * @return Possibly-empty list of {@link SchemaError SchemaError} */ @Nonnull public List getSchemaErrors() { return schemaErrors; } @Nonnull public static ErrorResponseException create(@Nonnull String message, @Nonnull ErrorResponseException cause) { return new ErrorResponseException(message, cause); } @Nonnull public static ErrorResponseException create(@Nonnull ErrorResponse errorResponse, @Nonnull Response response) { String meaning = errorResponse.getMeaning(); int code = errorResponse.getCode(); List schemaErrors = new ArrayList<>(); try { Optional optObj = response.optObject(); if (response.isError() && response.getException() != null) { // this generally means that an exception occurred trying to //make an http request. e.g.: //SocketTimeoutException/ UnknownHostException code = response.code; meaning = response.getException().getClass().getName(); } else if (optObj.isPresent()) { DataObject obj = optObj.get(); if (!obj.isNull("code") || !obj.isNull("message")) { if (!obj.isNull("code")) code = obj.getInt("code"); if (!obj.isNull("message")) meaning = obj.getString("message"); } else { // This means that neither code or message is provided //In that case we simply put the raw response in place! code = response.code; meaning = obj.toString(); } obj.optObject("errors").ifPresent(schema -> parseSchema(schemaErrors, "", schema)); } else { // error response body is not JSON code = response.code; meaning = response.getString(); } } catch (Exception e) { JDALogger.getLog(ErrorResponseException.class).error("Failed to parse parts of error response. Body: {}", response.getString(), e); } return new ErrorResponseException(errorResponse, response, code, meaning, schemaErrors); } private static void parseSchema(List schemaErrors, String currentLocation, DataObject errors) { // check what kind of errors we are dealing with for (String name : errors.keys()) { if (name.equals("_errors")) { schemaErrors.add(parseSchemaError(currentLocation, errors)); continue; } DataObject schemaError = errors.getObject(name); if (!schemaError.isNull("_errors")) { // We are dealing with an Object Error schemaErrors.add(parseSchemaError(currentLocation + name, schemaError)); } else if (schemaError.keys().stream().allMatch(Helpers::isNumeric)) { // We have an Array Error for (String index : schemaError.keys()) { DataObject properties = schemaError.getObject(index); String location = String.format("%s%s[%s].", currentLocation, name, index); if (properties.hasKey("_errors")) schemaErrors.add(parseSchemaError(location.substring(0, location.length()-1), properties)); else parseSchema(schemaErrors, location, properties); } } else { // We have a nested schema error, use recursion! String location = String.format("%s%s.", currentLocation, name); parseSchema(schemaErrors, location, schemaError); } } } private static SchemaError parseSchemaError(String location, DataObject obj) { List codes = obj.getArray("_errors") .stream(DataArray::getObject) .map(json -> new ErrorCode(json.getString("code"), json.getString("message"))) .collect(Collectors.toList()); return new SchemaError(location, codes); } /** * Ignore the specified set of error responses. * *

Example
*

{@code
     * // Creates a message with the provided content and deletes it 30 seconds later
     * public static void selfDestruct(MessageChannel channel, String content) {
     *     channel.sendMessage(content).queue((message) ->
     *         message.delete().queueAfter(30, SECONDS, null, ignore(EnumSet.of(UNKNOWN_MESSAGE)))
     *     );
     * }
     * }
* * @param set * Set of ignored error responses * * @throws IllegalArgumentException * If provided with null or an empty collection * * @return {@link Consumer} decorator for {@link RestAction#getDefaultFailure()} * which ignores the specified {@link ErrorResponse ErrorResponses} */ @Nonnull public static Consumer ignore(@Nonnull Collection set) { return ignore(RestAction.getDefaultFailure(), set); } /** * Ignore the specified set of error responses. * *

Example
*

{@code
     * // Creates a message with the provided content and deletes it 30 seconds later
     * public static void selfDestruct(MessageChannel channel, String content) {
     *     channel.sendMessage(content).queue((message) ->
     *         message.delete().queueAfter(30, SECONDS, null, ignore(UNKNOWN_MESSAGE))
     *     );
     * }
     * }
* * @param ignored * Ignored error response * @param errorResponses * Additional error responses to ignore * * @throws IllegalArgumentException * If provided with null * * @return {@link Consumer} decorator for {@link RestAction#getDefaultFailure()} * which ignores the specified {@link ErrorResponse ErrorResponses} */ @Nonnull public static Consumer ignore(@Nonnull ErrorResponse ignored, @Nonnull ErrorResponse... errorResponses) { return ignore(RestAction.getDefaultFailure(), ignored, errorResponses); } /** * Ignore the specified set of error responses. * *

Example
*

{@code
     * // Creates a message with the provided content and deletes it 30 seconds later
     * public static void selfDestruct(MessageChannel channel, String content) {
     *     channel.sendMessage(content).queue((message) ->
     *         message.delete().queueAfter(30, SECONDS, null, ignore(Throwable::printStackTrace, UNKNOWN_MESSAGE))
     *     );
     * }
     * }
* * @param orElse * Behavior to default to if the error response is not ignored * @param ignored * Ignored error response * @param errorResponses * Additional error responses to ignore * * @throws IllegalArgumentException * If provided with null * * @return {@link Consumer} decorator for the provided callback * which ignores the specified {@link ErrorResponse ErrorResponses} */ @Nonnull public static Consumer ignore(@Nonnull Consumer orElse, @Nonnull ErrorResponse ignored, @Nonnull ErrorResponse... errorResponses) { return ignore(orElse, EnumSet.of(ignored, errorResponses)); } /** * Ignore the specified set of error responses. * *

Example
*

{@code
     * // Creates a message with the provided content and deletes it 30 seconds later
     * public static void selfDestruct(MessageChannel channel, String content) {
     *     channel.sendMessage(content).queue((message) ->
     *         message.delete().queueAfter(30, SECONDS, null, ignore(Throwable::printStackTrace, EnumSet.of(UNKNOWN_MESSAGE)))
     *     );
     * }
     * }
* * @param orElse * Behavior to default to if the error response is not ignored * @param set * Set of ignored error responses * * @throws IllegalArgumentException * If provided with null or an empty collection * * @return {@link Consumer} decorator for the provided callback * which ignores the specified {@link ErrorResponse ErrorResponses} */ @Nonnull public static Consumer ignore(@Nonnull Consumer orElse, @Nonnull Collection set) { Checks.notNull(orElse, "Callback"); Checks.notEmpty(set, "Ignored collection"); // Make an enum set copy (for performance, memory efficiency, and thread-safety) final EnumSet ignored = EnumSet.copyOf(set); return new ErrorHandler(orElse).ignore(ignored); } /** * An error for a {@link SchemaError}. *
This provides the machine parsable error code name and the human readable message. */ public static class ErrorCode { private final String code; private final String message; ErrorCode(String code, String message) { this.code = code; this.message = message; } /** * The machine parsable error code * * @return The error code */ @Nonnull public String getCode() { return code; } /** * The human readable explanation message for this error * * @return The message */ @Nonnull public String getMessage() { return message; } @Override public String toString() { return code + ": " + message; } } /** * Schema error which supplies more context to a ErrorResponse. *
This provides a list of {@link ErrorCode ErrorCodes} and a {@link #getLocation() location} for the errors. */ public static class SchemaError { private final String location; private final List errors; private SchemaError(String location, List codes) { this.location = location; this.errors = codes; } /** * The JSON-path for the error. *
This path describes the location of the error, within the request json body. * *

Example: {@code embed.fields[3].name} * * @return The JSON-path location */ @Nonnull public String getLocation() { return location; } /** * The list of {@link ErrorCode ErrorCodes} associated with this schema error. * * @return The error codes */ @Nonnull public List getErrors() { return errors; } @Override public String toString() { return (location.isEmpty() ? "" : location+"\n") + "\t- " + errors.stream().map(Object::toString).collect(Collectors.joining("\n\t- ")); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy