Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public DatabaseException(ErrorInstance errorInstance) {

public enum Code implements ErrorCode<DatabaseException> {
COUNT_READ_FAILED, // converted from ErrorV1
DOCUMENT_FROM_DB_UNPARSEABLE,
FAILED_CONCURRENT_OPERATIONS,
FAILED_COMPARE_AND_SET,
FAILED_READ_REQUEST,
Expand All @@ -21,6 +22,7 @@ public enum Code implements ErrorCode<DatabaseException> {
TIMEOUT_WRITING_DATA,
UNAUTHORIZED_ACCESS,
UNAVAILABLE_DATABASE,
UNEXPECTED_DOCUMENT_ID_TYPE,
UNEXPECTED_DRIVER_ERROR,
UNKNOWN_KEYSPACE,
UNSUPPORTED_DATABASE_QUERY;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,24 @@ public DocumentException(ErrorInstance errorInstance) {

public enum Code implements ErrorCode<DocumentException> {
DOCUMENT_ALREADY_EXISTS,
// Internal error: does it belong here?
DOCUMENT_FROM_DB_UNPARSEABLE,
DOCUMENT_LEXICAL_CONTENT_TOO_BIG,
DOCUMENT_REPLACE_DIFFERENT_DOCID,

INVALID_COLUMN_VALUES,
MISSING_PRIMARY_KEY_COLUMNS,

SHRED_BAD_BINARY_VECTOR_VALUE,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we dont have any BAD error codes in V2 errors - we can let them through and then I can fix in a follow up ? as in they take some thinking do we want to do that now ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, postpone for now to give us more time to think it through.

SHRED_BAD_DOCID_TYPE,
SHRED_BAD_DOCID_VALUE,
SHRED_BAD_DOCUMENT_TYPE,
SHRED_BAD_DOCUMENT_VECTOR_TYPE,
SHRED_BAD_DOCUMENT_LEXICAL_TYPE,
SHRED_BAD_EJSON_VALUE,
SHRED_BAD_FIELD_NAME, // from ErrorV1.SHRED_DOC_KEY_NAME_VIOLATION
SHRED_BAD_VECTOR_SIZE,
SHRED_BAD_VECTOR_VALUE,
SHRED_DOC_LIMIT_VIOLATION,

UNKNOWN_TABLE_COLUMNS,
UNSUPPORTED_COLUMN_TYPES,
UNSUPPORTED_VECTORIZE_CONFIGURATIONS,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,32 +17,6 @@ public enum ErrorCodeV1 {
EMBEDDING_PROVIDER_UNEXPECTED_RESPONSE("The Embedding Provider returned an unexpected response"),
EMBEDDING_PROVIDER_API_KEY_MISSING("The Embedding Provider API key is missing"),

// Really Bad, generic name: but only used for EmbeddingProvidersConfig validation issues
INVALID_PARAMETER_VALIDATION_TYPE("Invalid Parameter Validation Type"),

SHRED_BAD_BINARY_VECTOR_VALUE("Bad binary vector value to shred"),

SHRED_BAD_DOCID_TYPE("Bad type for '_id' property"),

SHRED_BAD_DOCID_EMPTY_STRING("Bad value for '_id' property: empty String not allowed"),

SHRED_BAD_DOCUMENT_TYPE("Bad document type to shred"),

SHRED_BAD_DOCUMENT_VECTOR_TYPE("Bad $vector document type to shred "),

SHRED_BAD_DOCUMENT_VECTORIZE_TYPE("Bad $vectorize document type to shred "),

SHRED_BAD_DOCUMENT_LEXICAL_TYPE("Bad type for $lexical content to shred"),

SHRED_BAD_EJSON_VALUE("Bad JSON Extension value"),

SHRED_BAD_VECTOR_SIZE("$vector value can't be empty"),

SHRED_BAD_VECTOR_VALUE("$vector value needs to be array of numbers"),

SHRED_DOC_KEY_NAME_VIOLATION("Document field name invalid"),
SHRED_DOC_LIMIT_VIOLATION("Document size limitation violated"),

// CreateCollection error codes:

EXISTING_TABLE_NOT_DATA_API_COLLECTION("Existing table is not a valid Data API collection"),
Expand Down Expand Up @@ -100,9 +74,6 @@ public enum ErrorCodeV1 {
VECTORIZE_CREDENTIAL_INVALID("Invalid credential name for vectorize"),
VECTORIZECONFIG_CHECK_FAIL("Internal server error: VectorizeDefinition check fail"),

LEXICAL_CONTENT_TOO_BIG(
"Lexical content is too big, please use a smaller value for the $lexical field"),

HYBRID_FIELD_CONFLICT(
"The '$hybrid' field cannot be used with '$lexical', '$vector', or '$vectorize'."),
HYBRID_FIELD_UNSUPPORTED_VALUE_TYPE("Unsupported JSON value type for '$hybrid' field"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ public class JsonApiException extends RuntimeException implements Supplier<Comma
ErrorScope.SCHEMA,
new HashSet<>() {
{
add(INVALID_PARAMETER_VALIDATION_TYPE);
add(INVALID_REQUEST);
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,8 @@ private static CommandResult.Error handleQueryValidationException(
// [data-api#2068]: Need to convert Lexical-value-too-big failure to something more meaningful
if (message.contains(
"analyzed size for column query_lexical_value exceeds the cumulative limit for index")) {
return ErrorCodeV1.LEXICAL_CONTENT_TOO_BIG
.toApiException()
return DocumentException.Code.DOCUMENT_LEXICAL_CONTENT_TOO_BIG
.get()
.getCommandResultError(Response.Status.OK);
}
return ErrorCodeV1.INVALID_QUERY
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@
import io.stargate.sgv2.jsonapi.api.model.command.CommandResult;
import io.stargate.sgv2.jsonapi.api.model.command.tracing.RequestTracing;
import io.stargate.sgv2.jsonapi.config.OperationsConfig;
import io.stargate.sgv2.jsonapi.exception.APIException;
import io.stargate.sgv2.jsonapi.exception.APIExceptionCommandErrorBuilder;
import io.stargate.sgv2.jsonapi.exception.ErrorCodeV1;
import io.stargate.sgv2.jsonapi.exception.JsonApiException;
import io.stargate.sgv2.jsonapi.exception.*;
import jakarta.inject.Inject;
import jakarta.ws.rs.NotAllowedException;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.NotSupportedException;
import jakarta.ws.rs.WebApplicationException;
import java.util.Map;
import org.jboss.resteasy.reactive.RestResponse;
import org.jboss.resteasy.reactive.server.ServerExceptionMapper;

Expand All @@ -32,7 +30,9 @@ public RestResponse<CommandResult> webApplicationExceptionMapper(WebApplicationE
// and if we have StreamConstraintsException, re-create as ApiException
if (toReport instanceof StreamConstraintsException) {
// but leave out the root cause, as it is not useful
toReport = ErrorCodeV1.SHRED_DOC_LIMIT_VIOLATION.toApiException(toReport.getMessage());
toReport =
DocumentException.Code.SHRED_DOC_LIMIT_VIOLATION.get(
Map.of("errorMessage", toReport.getMessage()));
}

// V2 Error are returned as APIException, this is required to translate the exception to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import io.smallrye.config.ConfigMapping;
import io.smallrye.config.WithConverter;
import io.smallrye.config.WithDefault;
import io.stargate.sgv2.jsonapi.exception.ErrorCodeV1;
import io.stargate.sgv2.jsonapi.service.provider.ApiModelSupport;
import jakarta.annotation.Nullable;
import jakarta.inject.Inject;
Expand Down Expand Up @@ -207,7 +206,10 @@ public static ValidationType fromString(String type) {
} else if (type.equals("options")) {
return OPTIONS;
}
throw ErrorCodeV1.INVALID_PARAMETER_VALIDATION_TYPE.toApiException(type);
throw new IllegalArgumentException(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One-off failure not to be directly exposed: caller will wrap/convert validation problems in more centralized way.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am guessing we only need this stuff because of the EGW and the trouble we have with configs ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure why this was added: I don't think non-contextual, Enum lookups should throw Data API exceptions; they can't add much context information. I'll see what caller does.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uh. It's called from just one place but that's a Record constructor in ParameterConfigImpl:

      public ParameterConfigImpl(
          EmbeddingGateway.GetSupportedProvidersResponse.ProviderConfig.ParameterConfig
              grpcModelParameter) {
        this(
            grpcModelParameter.getName(),
            ParameterType.valueOf(grpcModelParameter.getType().name()),
            grpcModelParameter.getRequired(),
            Optional.of(grpcModelParameter.getDefaultValue()),
            grpcModelParameter.getValidationMap().entrySet().stream()
                .collect(
                    Collectors.toMap(
                        e -> ValidationType.fromString(e.getKey()),
                        e -> new ArrayList<>(e.getValue().getValuesList()))),

will leave as-is, for now.

"Invalid `ValidationType` value ('"
+ type
+ "') for `EmbeddingProvidersConfig`: expected either 'numericRange' or 'options'");
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,7 @@ public JsonNode get() {
* Helper method to handle details of exactly how much information to include in error message.
*/
static APIException parsingExceptionToApiException(JacksonException e) {
return DocumentException.Code.DOCUMENT_FROM_DB_UNPARSEABLE.get(
Map.of("message", e.getOriginalMessage()));
return DatabaseException.Code.DOCUMENT_FROM_DB_UNPARSEABLE.get(
Map.of("errorMessage", e.getOriginalMessage()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
import io.quarkus.runtime.annotations.RegisterForReflection;
import io.stargate.sgv2.jsonapi.api.model.command.clause.filter.JsonType;
import io.stargate.sgv2.jsonapi.config.constants.DocumentConstants;
import io.stargate.sgv2.jsonapi.exception.ErrorCodeV1;
import io.stargate.sgv2.jsonapi.exception.DatabaseException;
import io.stargate.sgv2.jsonapi.exception.DocumentException;
import io.stargate.sgv2.jsonapi.exception.JsonApiException;
import io.stargate.sgv2.jsonapi.service.shredding.DocRowIdentifer;
import io.stargate.sgv2.jsonapi.util.JsonUtil;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;

Expand Down Expand Up @@ -78,20 +80,26 @@ static DocumentId fromJson(JsonNode node) {
return fromExtensionType(extType, valueNode);
}
}
throw ErrorCodeV1.SHRED_BAD_DOCID_TYPE.toApiException(
"unrecognized JSON extension type '%s'", node.fieldNames().next());
}
throw ErrorCodeV1.SHRED_BAD_DOCID_TYPE.toApiException(
"Document Id must be a JSON String, Number, Boolean, EJSON-Encoded Date Object or NULL instead got %s: %s",
node.getNodeType(), node.toString());
throw DocumentException.Code.SHRED_BAD_DOCID_TYPE.get(
Map.of(
"errorMessage",
"unrecognized JSON extension type '%s'".formatted(node.fieldNames().next())));
}
throw DocumentException.Code.SHRED_BAD_DOCID_TYPE.get(
Map.of(
"errorMessage",
"Document Id must be a JSON String, Number, Boolean, EJSON-Encoded Date Object or null instead got %s: %s"
.formatted(JsonUtil.nodeTypeAsString(node), node.toString())));
}

static DocumentId fromDatabase(int typeId, String documentIdAsText) {
JsonType type = DocumentConstants.KeyTypeId.getJsonType(typeId);
if (type == null) {
throw ErrorCodeV1.SHRED_BAD_DOCID_TYPE.toApiException(
"Document Id must be a JSON String(1), Number(2), Boolean(3), NULL(4) or Date(5) instead got %d",
typeId);
throw DatabaseException.Code.UNEXPECTED_DOCUMENT_ID_TYPE.get(
Map.of(
"errorMessage",
"Document Id must be a JSON String(1), Number(2), Boolean(3), null(4) or Date(5) instead got %d"
.formatted(typeId)));
}
switch (type) {
case BOOLEAN -> {
Expand All @@ -101,9 +109,11 @@ static DocumentId fromDatabase(int typeId, String documentIdAsText) {
case "false":
return fromBoolean(false);
}
throw ErrorCodeV1.SHRED_BAD_DOCID_TYPE.toApiException(
"Document Id type Boolean stored as invalid String '%s' (must be 'true' or 'false')",
documentIdAsText);
throw DatabaseException.Code.UNEXPECTED_DOCUMENT_ID_TYPE.get(
Map.of(
"errorMessage",
"Document Id type Boolean stored as invalid String '%s' (must be 'true' or 'false')"
.formatted(documentIdAsText)));
}
case NULL -> {
return fromNull();
Expand All @@ -112,9 +122,11 @@ static DocumentId fromDatabase(int typeId, String documentIdAsText) {
try {
return fromNumber(new BigDecimal(documentIdAsText));
} catch (NumberFormatException e) {
throw ErrorCodeV1.SHRED_BAD_DOCID_TYPE.toApiException(
"Document Id type Number stored as invalid String '%s' (not a valid Number)",
documentIdAsText);
throw DatabaseException.Code.UNEXPECTED_DOCUMENT_ID_TYPE.get(
Map.of(
"errorMessage",
"Document Id type Number stored as invalid String '%s' (not a valid Number)"
.formatted(documentIdAsText)));
}
}
case STRING -> {
Expand All @@ -125,13 +137,16 @@ static DocumentId fromDatabase(int typeId, String documentIdAsText) {
long ts = Long.parseLong(documentIdAsText);
return fromTimestamp(ts);
} catch (NumberFormatException e) {
throw ErrorCodeV1.SHRED_BAD_DOCID_TYPE.toApiException(
"Document Id type Date stored as invalid String '%s' (needs to be Number)",
documentIdAsText);
throw DatabaseException.Code.UNEXPECTED_DOCUMENT_ID_TYPE.get(
Map.of(
"errorMessage",
"Document Id type Date stored as invalid String '%s' (needs to be Number)"
.formatted(documentIdAsText)));
}
}
}
throw ErrorCodeV1.SHRED_BAD_DOCID_TYPE.toApiException();
throw DatabaseException.Code.UNEXPECTED_DOCUMENT_ID_TYPE.get(
Map.of("errorMessage", "unknown `JsonType`: '%s'".formatted(type)));
}

static DocumentId fromBoolean(boolean key) {
Expand All @@ -150,7 +165,8 @@ static DocumentId fromNumber(BigDecimal key) {
static DocumentId fromString(String key) {
key = Objects.requireNonNull(key);
if (key.isEmpty()) {
throw ErrorCodeV1.SHRED_BAD_DOCID_EMPTY_STRING.toApiException();
throw DocumentException.Code.SHRED_BAD_DOCID_VALUE.get(
Map.of("errorMessage", "empty String not allowed"));
}
return new StringId(key);
}
Expand All @@ -172,7 +188,7 @@ static DocumentId fromExtensionType(JsonExtensionType extType, JsonNode valueNod
Object rawId = JsonUtil.extractExtendedValueUnwrapped(extType, valueNode);
return new ExtensionTypeId(extType, String.valueOf(rawId));
} catch (JsonApiException e) {
throw ErrorCodeV1.SHRED_BAD_DOCID_TYPE.toApiException(e.getMessage());
throw DocumentException.Code.SHRED_BAD_DOCID_TYPE.get(Map.of("errorMessage", e.getMessage()));
}
}

Expand Down
Loading