Skip to content

Commit

Permalink
Use WarningException, expand CommandResultBuilder
Browse files Browse the repository at this point in the history
Changes the warnings associated commands to use the new ApiExcpetions,
and `warnings` in the return status now returns a list of error object V2.

To do that needed to improve the way CommandResult was built, so it always
had a status map so the CommandProcessor could append a warning. To do that
expanded the CommandResultBuilder added for the OperationAttempts, removed
the many overloads used for CommandResult, and updated all creation of the
CommandResult to use the builder.

See also #1518 to continue this
  • Loading branch information
amorton committed Oct 9, 2024
1 parent 1d4f2bd commit 756f615
Show file tree
Hide file tree
Showing 37 changed files with 697 additions and 329 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package io.stargate.sgv2.jsonapi.api.model.command;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import jakarta.ws.rs.core.Response;
import java.util.*;
import org.eclipse.microprofile.openapi.annotations.media.Schema;

/**
* Intended to replace the {@link CommandResult.Error} with something that has all the fields in an
* error.
*
* <p>This super class is the legacy V1 structure, and the subclass {@link CommandErrorV2} is the
* new V2 structure. Once we have fully removed the {@link CommandResult.Error} we can remove this
* class and just have the V2 structure.
*
* <p>This object is intended to be used in the CommandResult, and is serialised into the JSON
* response.
*
* <p>aaron - 9 -oct-2024 - This is initially only used with the Warnings in the CommandResult,
* further work needed to use it in all places. Warnings are in the status, and not described on the
* swagger for the CommandResult. This is why all the decorators from the original class are not
* here yet, but they have things to ignore flagged just incase
*/
public class CommandError {

private final String errorCode;
private final String message;
private final Response.Status httpStatus;
private final String errorClass;
private final Map<String, Object> metricsTags;

public CommandError(
String errorCode,
String message,
Response.Status httpStatus,
String errorClass,
Map<String, Object> metricsTags) {

this.errorCode = requireNoNullOrBlank(errorCode, "errorCode cannot be null or blank");
this.message = requireNoNullOrBlank(message, "message cannot be null or blank");
this.httpStatus = Objects.requireNonNull(httpStatus, "httpStatus cannot be null");

this.metricsTags = metricsTags == null ? Collections.emptyMap() : Map.copyOf(metricsTags);
// errorClass is not required, it is only passed when we are in debug mode
// normalise to null if blank
this.errorClass = errorClass == null || errorClass.isBlank() ? null : errorClass;
}

protected static String requireNoNullOrBlank(String value, String message) {
if (Objects.isNull(value) || value.isBlank()) {
throw new IllegalArgumentException(message);
}
return value;
}

/**
* @return
*/
public String errorCode() {
return errorCode;
}

public String message() {
return message;
}

@JsonIgnore
public Response.Status httpStatus() {
return httpStatus;
}

@JsonIgnore
@Schema(hidden = true)
public Map<String, Object> metricsTags() {
return metricsTags;
}

@Schema(hidden = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public String exceptionClass() {
return errorClass;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package io.stargate.sgv2.jsonapi.api.model.command;

import jakarta.ws.rs.core.Response;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;

/**
* See {@link CommandError} for why this class exists.
*
* <p>Use either {@link #builderV1()} or {@link #builderV2()} to get te builder to create an
* instance of this class.
*/
public class CommandErrorV2 extends CommandError {

private final String family;
private final String scope;
private final String title;
private final UUID id;

public CommandErrorV2(
String errorCode,
String message,
Response.Status httpStatus,
String errorClass,
Map<String, Object> metricsTags,
String family,
String scope,
String title,
UUID id) {
super(errorCode, message, httpStatus, errorClass, metricsTags);
this.family = requireNoNullOrBlank(family, "family cannot be null or blank");
this.scope = scope == null ? "" : scope;
this.title = requireNoNullOrBlank(title, "title cannot be null or blank");
this.id = Objects.requireNonNull(id, "id cannot be null");
}

/** Create a new builder for the {@link CommandError} that represents the V1 error object. */
public static Builder<CommandError> builderV1() {
return new Builder<>(true);
}

/** Create a new builder for the {@link CommandErrorV2} that represents the V2 error object. */
public static Builder<CommandErrorV2> builderV2() {
return new Builder<>(false);
}

public String family() {
return family;
}

public String scope() {
return scope;
}

public String title() {
return title;
}

public UUID id() {
return id;
}

public static class Builder<T extends CommandError> {
private String errorCode;
private String message;
private Response.Status httpStatus;
private String errorClass;
private Map<String, Object> metricsTags;
private String family;
private String scope;
private String title;
private UUID id;

private final boolean v1Error;

Builder(boolean v1Error) {
this.v1Error = v1Error;
}

public Builder<T> errorCode(String errorCode) {
this.errorCode = errorCode;
return this;
}

public Builder<T> message(String message) {
this.message = message;
return this;
}

public Builder<T> httpStatus(Response.Status httpStatus) {
this.httpStatus = httpStatus;
return this;
}

public Builder<T> errorClass(String errorClass) {
this.errorClass = errorClass;
return this;
}

public Builder<T> metricsTags(Map<String, Object> metricsTags) {
this.metricsTags = metricsTags;
return this;
}

public Builder<T> family(String family) {
this.family = family;
return this;
}

public Builder<T> scope(String scope) {
this.scope = scope;
return this;
}

public Builder<T> title(String title) {
this.title = title;
return this;
}

public Builder<T> id(UUID id) {
this.id = id;
return this;
}

public T build() {
return v1Error
? unchecked(new CommandError(errorCode, message, httpStatus, errorClass, metricsTags))
: unchecked(
new CommandErrorV2(
errorCode,
message,
httpStatus,
errorClass,
metricsTags,
family,
scope,
title,
id));
}

@SuppressWarnings("unchecked")
private T unchecked(CommandError error) {
return (T) error;
}
}
}
Loading

0 comments on commit 756f615

Please sign in to comment.