diff --git a/.gitignore b/.gitignore index 49dee7762..5403f0327 100644 --- a/.gitignore +++ b/.gitignore @@ -81,6 +81,7 @@ client/shade/dependency-reduced-pom.xml # mkdocs output site + # agents .claude .playwright-mcp diff --git a/CLAUDE.md b/CLAUDE.md index 3279d857e..0052041b8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -118,6 +118,6 @@ ui/graphiql/ -> GraphiQL UI component - Jakarta namespace only — the build enforces a ban on `javax.*` dependencies via maven-enforcer-plugin - Logging uses `jboss-logging` with message localization (`SmallRyeGraphQLServerLogging`, `SmallRyeGraphQLServerMessages`) -- JSON binding uses JSON-B (Yasson implementation), not Jackson +- JSON processing uses Jackson 3.x (tools.jackson). JSON-B annotations are supported via the jackson-jsonb-compat module - The `.mvn/` directory is gitignored and may contain local Maven settings (repo path, mirrors) - The graphql-java library (currently v21.1) is the underlying GraphQL execution engine diff --git a/client/implementation/src/main/java/io/smallrye/graphql/client/impl/typesafe/reflection/MethodInvocation.java b/client/implementation/src/main/java/io/smallrye/graphql/client/impl/typesafe/reflection/MethodInvocation.java index 5f44dc2a4..b4419ee70 100644 --- a/client/implementation/src/main/java/io/smallrye/graphql/client/impl/typesafe/reflection/MethodInvocation.java +++ b/client/implementation/src/main/java/io/smallrye/graphql/client/impl/typesafe/reflection/MethodInvocation.java @@ -25,7 +25,6 @@ import org.eclipse.microprofile.graphql.Query; import io.smallrye.graphql.api.Namespace; -import io.smallrye.graphql.api.Subscription; import io.smallrye.graphql.client.core.OperationType; import io.smallrye.graphql.client.model.MethodKey; import io.smallrye.graphql.client.typesafe.api.Multiple; @@ -67,7 +66,8 @@ public OperationType getOperationType() { if (method.isAnnotationPresent(Mutation.class)) { return OperationType.MUTATION; } - if (method.isAnnotationPresent(Subscription.class)) { + if (method.isAnnotationPresent(org.eclipse.microprofile.graphql.Subscription.class) + || method.isAnnotationPresent(io.smallrye.graphql.api.Subscription.class)) { return OperationType.SUBSCRIPTION; } return OperationType.QUERY; @@ -99,9 +99,18 @@ private Optional mutationName() { } private Optional subscriptionName() { - Subscription annotation = method.getAnnotation(Subscription.class); - if (annotation != null && !annotation.value().isEmpty()) - return Optional.of(annotation.value()); + // Check MicroProfile annotation first (preferred) + org.eclipse.microprofile.graphql.Subscription mpAnnotation = method + .getAnnotation(org.eclipse.microprofile.graphql.Subscription.class); + if (mpAnnotation != null && !mpAnnotation.value().isEmpty()) + return Optional.of(mpAnnotation.value()); + + // Fall back to deprecated SmallRye annotation + io.smallrye.graphql.api.Subscription srAnnotation = method + .getAnnotation(io.smallrye.graphql.api.Subscription.class); + if (srAnnotation != null && !srAnnotation.value().isEmpty()) + return Optional.of(srAnnotation.value()); + return Optional.empty(); } diff --git a/client/model-builder/src/main/java/io/smallrye/graphql/client/modelbuilder/Annotations.java b/client/model-builder/src/main/java/io/smallrye/graphql/client/modelbuilder/Annotations.java index af76831e6..26262c906 100644 --- a/client/model-builder/src/main/java/io/smallrye/graphql/client/modelbuilder/Annotations.java +++ b/client/model-builder/src/main/java/io/smallrye/graphql/client/modelbuilder/Annotations.java @@ -542,6 +542,7 @@ private static Map getAnnotationsWithFilter(Type ty .createSimple("io.smallrye.graphql.client.typesafe.api.GraphQLClientApi"); public static final DotName QUERY = DotName.createSimple("org.eclipse.microprofile.graphql.Query"); public static final DotName MUTATION = DotName.createSimple("org.eclipse.microprofile.graphql.Mutation"); + public static final DotName SUBSCRIPTION = DotName.createSimple("org.eclipse.microprofile.graphql.Subscription"); public static final DotName INPUT = DotName.createSimple("org.eclipse.microprofile.graphql.Input"); public static final DotName TYPE = DotName.createSimple("org.eclipse.microprofile.graphql.Type"); public static final DotName INTERFACE = DotName.createSimple("org.eclipse.microprofile.graphql.Interface"); diff --git a/client/model-builder/src/main/java/io/smallrye/graphql/client/modelbuilder/Scalars.java b/client/model-builder/src/main/java/io/smallrye/graphql/client/modelbuilder/Scalars.java index 611c14045..4374c3244 100644 --- a/client/model-builder/src/main/java/io/smallrye/graphql/client/modelbuilder/Scalars.java +++ b/client/model-builder/src/main/java/io/smallrye/graphql/client/modelbuilder/Scalars.java @@ -72,10 +72,6 @@ public static boolean isStringScalar(String className) { populateScalar(URL.class.getName(), STRING); populateScalar(URI.class.getName(), STRING); populateScalar("org.bson.types.ObjectId", STRING); - populateScalar("javax.json.JsonObject", STRING); - populateScalar("javax.json.JsonArray", STRING); - populateScalar("jakarta.json.JsonObject", STRING); - populateScalar("jakarta.json.JsonArray", STRING); // Boolean populateScalar(Boolean.class.getName(), BOOLEAN); diff --git a/client/model-builder/src/main/java/io/smallrye/graphql/client/modelbuilder/helper/OperationModel.java b/client/model-builder/src/main/java/io/smallrye/graphql/client/modelbuilder/helper/OperationModel.java index 94336097a..fb185b0e7 100644 --- a/client/model-builder/src/main/java/io/smallrye/graphql/client/modelbuilder/helper/OperationModel.java +++ b/client/model-builder/src/main/java/io/smallrye/graphql/client/modelbuilder/helper/OperationModel.java @@ -6,6 +6,7 @@ import static io.smallrye.graphql.client.modelbuilder.Annotations.NAMESPACE; import static io.smallrye.graphql.client.modelbuilder.Annotations.QUERY; import static io.smallrye.graphql.client.modelbuilder.Annotations.SUBCRIPTION; +import static io.smallrye.graphql.client.modelbuilder.Annotations.SUBSCRIPTION; import static io.smallrye.graphql.client.modelbuilder.ScanningContext.getIndex; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; @@ -201,7 +202,7 @@ public OperationType getOperationType() { if (method.hasAnnotation(MUTATION)) { return OperationType.MUTATION; } - if (method.hasAnnotation(SUBCRIPTION)) { + if (method.hasAnnotation(SUBSCRIPTION) || method.hasAnnotation(SUBCRIPTION)) { return OperationType.SUBSCRIPTION; } return OperationType.QUERY; @@ -236,14 +237,23 @@ public Optional mutationName() { } /** - * Gets the name of the GraphQL subscription, considering any io.smallrye.graphql.api.Subscription annotation. + * Gets the name of the GraphQL subscription, considering any {@code @Subscription} annotation + * (either {@link org.eclipse.microprofile.graphql.Subscription} or + * {@link io.smallrye.graphql.api.Subscription}). * * @return An optional containing the subscription name if specified, otherwise empty. */ public Optional subscriptionName() { - Optional subscriptionAnnotation = getMethodAnnotation(SUBCRIPTION); + // Check MicroProfile annotation first (preferred) + Optional subscriptionAnnotation = getMethodAnnotation(SUBSCRIPTION); if (subscriptionAnnotation.isPresent() && subscriptionAnnotation.orElseThrow().value() != null) return Optional.of(subscriptionAnnotation.orElseThrow().value().asString()); + + // Fall back to deprecated SmallRye annotation + subscriptionAnnotation = getMethodAnnotation(SUBCRIPTION); + if (subscriptionAnnotation.isPresent() && subscriptionAnnotation.orElseThrow().value() != null) + return Optional.of(subscriptionAnnotation.orElseThrow().value().asString()); + return Optional.empty(); } diff --git a/common/schema-builder/src/main/java/io/smallrye/graphql/schema/Annotations.java b/common/schema-builder/src/main/java/io/smallrye/graphql/schema/Annotations.java index e513951c2..481080ae5 100644 --- a/common/schema-builder/src/main/java/io/smallrye/graphql/schema/Annotations.java +++ b/common/schema-builder/src/main/java/io/smallrye/graphql/schema/Annotations.java @@ -604,6 +604,7 @@ private static Map getAnnotationsWithFilter(org.jbo public static final DotName GRAPHQL_API = DotName.createSimple("org.eclipse.microprofile.graphql.GraphQLApi"); public static final DotName QUERY = DotName.createSimple("org.eclipse.microprofile.graphql.Query"); public static final DotName MUTATION = DotName.createSimple("org.eclipse.microprofile.graphql.Mutation"); + public static final DotName SUBSCRIPTION = DotName.createSimple("org.eclipse.microprofile.graphql.Subscription"); public static final DotName INPUT = DotName.createSimple("org.eclipse.microprofile.graphql.Input"); public static final DotName TYPE = DotName.createSimple("org.eclipse.microprofile.graphql.Type"); public static final DotName INTERFACE = DotName.createSimple("org.eclipse.microprofile.graphql.Interface"); diff --git a/common/schema-builder/src/main/java/io/smallrye/graphql/schema/Classes.java b/common/schema-builder/src/main/java/io/smallrye/graphql/schema/Classes.java index f6b6e3467..7343c9785 100644 --- a/common/schema-builder/src/main/java/io/smallrye/graphql/schema/Classes.java +++ b/common/schema-builder/src/main/java/io/smallrye/graphql/schema/Classes.java @@ -156,7 +156,8 @@ private static boolean isAsyncType(Type type) { || type.name().equals(COMPLETION_STAGE) || type.name().equals(UNI) || type.name().equals(MULTI) - || type.name().equals(PUBLISHER); + || type.name().equals(PUBLISHER) + || type.name().equals(FLOW_PUBLISHER); } /** @@ -267,6 +268,7 @@ public static boolean isUnwrappedType(Type type) { private static final DotName MULTI = DotName.createSimple("io.smallrye.mutiny.Multi"); @Deprecated private static final DotName PUBLISHER = DotName.createSimple("org.reactivestreams.Publisher"); + private static final DotName FLOW_PUBLISHER = DotName.createSimple("java.util.concurrent.Flow$Publisher"); public static final DotName SERIALIZABLE = DotName.createSimple(Serializable.class.getName()); public static final DotName OBJECT = DotName.createSimple(Object.class.getName()); diff --git a/common/schema-builder/src/main/java/io/smallrye/graphql/schema/SchemaBuilder.java b/common/schema-builder/src/main/java/io/smallrye/graphql/schema/SchemaBuilder.java index c57b113c7..b2823f13a 100644 --- a/common/schema-builder/src/main/java/io/smallrye/graphql/schema/SchemaBuilder.java +++ b/common/schema-builder/src/main/java/io/smallrye/graphql/schema/SchemaBuilder.java @@ -207,7 +207,7 @@ private void validateSubscriptions(Collection graphQLApiAnno List methods = getAllMethodsIncludingFromSuperClasses(apiClass); for (MethodInfo methodInfo : methods) { Annotations annotationsForMethod = Annotations.getAnnotationsForMethod(methodInfo); - if (annotationsForMethod.containsOneOfTheseAnnotations(Annotations.SUBCRIPTION)) { + if (annotationsForMethod.containsOneOfTheseAnnotations(Annotations.SUBSCRIPTION, Annotations.SUBCRIPTION)) { errors.add("class: " + apiClass.name().toString() + ", method: " + methodInfo.name()); } } @@ -450,7 +450,7 @@ private void addOperations(Schema schema, List methodInfoList) { } else if (annotationsForMethod.containsOneOfTheseAnnotations(Annotations.MUTATION)) { Operation mutation = operationCreator.createOperation(methodInfo, OperationType.MUTATION, null); schema.addMutation(mutation); - } else if (annotationsForMethod.containsOneOfTheseAnnotations(Annotations.SUBCRIPTION)) { + } else if (annotationsForMethod.containsOneOfTheseAnnotations(Annotations.SUBSCRIPTION, Annotations.SUBCRIPTION)) { Operation subscription = operationCreator.createOperation(methodInfo, OperationType.SUBSCRIPTION, null); schema.addSubscription(subscription); } diff --git a/common/schema-builder/src/main/java/io/smallrye/graphql/schema/creator/OperationCreator.java b/common/schema-builder/src/main/java/io/smallrye/graphql/schema/creator/OperationCreator.java index 3ac0d66fe..4551bb89a 100644 --- a/common/schema-builder/src/main/java/io/smallrye/graphql/schema/creator/OperationCreator.java +++ b/common/schema-builder/src/main/java/io/smallrye/graphql/schema/creator/OperationCreator.java @@ -79,7 +79,18 @@ public Operation createOperation(MethodInfo methodInfo, OperationType operationT Type fieldType = getReturnType(methodInfo); // Name - String name = getOperationName(methodInfo, operationType, annotationsForMethod); + // For source operations, find the @Source parameter to extract custom field name + Annotations sourceParameterAnnotations = null; + if (type != null) { // This is a source operation + for (short i = 0; i < methodInfo.parametersCount(); i++) { + Annotations paramAnnotations = Annotations.getAnnotationsForArgument(methodInfo, i); + if (paramAnnotations.containsOneOfTheseAnnotations(Annotations.SOURCE)) { + sourceParameterAnnotations = paramAnnotations; + break; + } + } + } + String name = getOperationName(methodInfo, operationType, annotationsForMethod, sourceParameterAnnotations); // Field Type validateFieldType(methodInfo, operationType); @@ -273,25 +284,56 @@ private static void validateFieldType(MethodInfo methodInfo, OperationType opera /** * Get the name from annotation(s) or default. - * This is for operations (query, mutation and source) * * @param methodInfo the java method - * @param operationType the type (query, mutation) + * @param operationType the type (query, mutation, subscription) * @param annotations the annotations on this method + * @param sourceParameterAnnotations annotations for the @Source parameter (null for top-level operations) * @return the operation name */ - private static String getOperationName(MethodInfo methodInfo, OperationType operationType, Annotations annotations) { + private static String getOperationName(MethodInfo methodInfo, OperationType operationType, Annotations annotations, + Annotations sourceParameterAnnotations) { + // For source operations, check the @Source annotation's value() and name() attributes first + if (sourceParameterAnnotations != null) { + Optional sourceAnnotation = sourceParameterAnnotations + .getOneOfTheseAnnotations(Annotations.SOURCE); + if (sourceAnnotation.isPresent()) { + AnnotationInstance source = sourceAnnotation.get(); + + // Try value() first (current preferred way per MicroProfile GraphQL 2.1) + if (source.value("value") != null) { + String valueAttr = source.value("value").asString(); + if (valueAttr != null && !valueAttr.isEmpty()) { + return valueAttr; + } + } + + // Fall back to name() (deprecated but still supported) + if (source.value("name") != null) { + String nameAttr = source.value("name").asString(); + if (nameAttr != null && !nameAttr.isEmpty()) { + return nameAttr; + } + } + } + } + DotName operationAnnotation = getOperationAnnotation(operationType); - // If the @Query or @Mutation annotation has a value, use that, else use name or jsonb property - return annotations.getOneOfTheseMethodAnnotationsValue( + // If the @Query, @Mutation, or @Subscription annotation has a value, use that, else use name or jsonb property + Optional name = annotations.getOneOfTheseMethodAnnotationsValue( operationAnnotation, Annotations.NAME, Annotations.JAKARTA_JSONB_PROPERTY, Annotations.JAVAX_JSONB_PROPERTY, - Annotations.JACKSON_PROPERTY) - .orElse(getDefaultExecutionTypeName(methodInfo, operationType)); + Annotations.JACKSON_PROPERTY); + + // For subscriptions, also check the deprecated SmallRye annotation + if (name.isEmpty() && operationType == OperationType.SUBSCRIPTION) { + name = annotations.getOneOfTheseMethodAnnotationsValue(Annotations.SUBCRIPTION); + } + return name.orElse(getDefaultExecutionTypeName(methodInfo, operationType)); } private static DotName getOperationAnnotation(OperationType operationType) { @@ -301,7 +343,7 @@ private static DotName getOperationAnnotation(OperationType operationType) { case MUTATION: return Annotations.MUTATION; case SUBSCRIPTION: - return Annotations.SUBCRIPTION; + return Annotations.SUBSCRIPTION; case RESOLVER: return Annotations.RESOLVER; default: diff --git a/common/schema-model/src/main/java/io/smallrye/graphql/schema/model/Scalars.java b/common/schema-model/src/main/java/io/smallrye/graphql/schema/model/Scalars.java index fb6e8ba85..e27925cab 100644 --- a/common/schema-model/src/main/java/io/smallrye/graphql/schema/model/Scalars.java +++ b/common/schema-model/src/main/java/io/smallrye/graphql/schema/model/Scalars.java @@ -121,10 +121,9 @@ public static void addObject() { } public static void addJson() { - populateScalar("jakarta.json.JsonValue", JSON, Object.class.getName()); - populateScalar("jakarta.json.JsonObject", JSON, Object.class.getName()); populateScalar("tools.jackson.databind.JsonNode", JSON, Object.class.getName()); populateScalar("tools.jackson.databind.node.ObjectNode", JSON, Object.class.getName()); + populateScalar("tools.jackson.databind.node.ArrayNode", JSON, Object.class.getName()); } static { @@ -138,10 +137,6 @@ public static void addJson() { populateScalar(URL.class.getName(), STRING, String.class.getName()); populateScalar(URI.class.getName(), STRING, String.class.getName()); populateScalar("org.bson.types.ObjectId", STRING, String.class.getName()); - populateScalar("javax.json.JsonObject", STRING, String.class.getName()); - populateScalar("javax.json.JsonArray", STRING, String.class.getName()); - populateScalar("jakarta.json.JsonObject", STRING, String.class.getName()); - populateScalar("jakarta.json.JsonArray", STRING, String.class.getName()); // Boolean populateScalar(Boolean.class.getName(), BOOLEAN); diff --git a/pom.xml b/pom.xml index 297141b97..f1c1797c7 100644 --- a/pom.xml +++ b/pom.xml @@ -17,8 +17,7 @@ 3.1.1 - 2.0 - + 2.1-SNAPSHOT 1.3 3.5.3 3.17.2 @@ -108,31 +107,12 @@ - - org.eclipse.microprofile.graphql microprofile-graphql-tck ${version.eclipse.microprofile.graphql} - - - org.eclipse.microprofile.graphql - microprofile-graphql-server-tck - ${version.eclipse.microprofile.graphql} - - + org.eclipse.microprofile.config microprofile-config-api diff --git a/server/api/pom.xml b/server/api/pom.xml index 3392190fd..45fe4d62f 100644 --- a/server/api/pom.xml +++ b/server/api/pom.xml @@ -45,10 +45,10 @@ smallrye-common-annotation - + - jakarta.json - jakarta.json-api + tools.jackson.core + jackson-databind provided diff --git a/server/api/src/main/java/io/smallrye/graphql/api/Context.java b/server/api/src/main/java/io/smallrye/graphql/api/Context.java index 917ffebe9..6adadb689 100644 --- a/server/api/src/main/java/io/smallrye/graphql/api/Context.java +++ b/server/api/src/main/java/io/smallrye/graphql/api/Context.java @@ -18,18 +18,15 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Set; - -import jakarta.json.JsonArray; -import jakarta.json.JsonNumber; -import jakarta.json.JsonObject; -import jakarta.json.JsonString; -import jakarta.json.JsonValue; import io.smallrye.common.annotation.Experimental; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.node.ArrayNode; +import tools.jackson.databind.node.ObjectNode; /** * Holing context for the current request @@ -48,9 +45,9 @@ public interface Context { * Get the full body of the request. * This includes the query, variables and operation name * - * @return JsonObject + * @return ObjectNode */ - public JsonObject getRequest(); + public ObjectNode getRequest(); /** * Check if there is a request set @@ -58,7 +55,7 @@ public interface Context { * @return */ default boolean hasRequest() { - JsonObject request = getRequest(); + ObjectNode request = getRequest(); return request != null; } @@ -69,7 +66,8 @@ default boolean hasRequest() { * @return raw string query */ default String getQuery() { - return getRequest().getString(QUERY, null); + JsonNode node = getRequest().get(QUERY); + return (node != null && !node.isNull()) ? node.asText() : null; } /** @@ -96,11 +94,11 @@ default boolean hasOperationName() { * @return */ default Optional> getVariables() { - if (getRequest().containsKey(VARIABLES) + if (getRequest().has(VARIABLES) && getRequest().get(VARIABLES) != null - && !getRequest().get(VARIABLES).getValueType().equals(JsonValue.ValueType.NULL)) { - JsonValue jsonValue = getRequest().get(VARIABLES); - return VariablesParser.toMap(jsonValue); + && !getRequest().get(VARIABLES).isNull()) { + JsonNode node = getRequest().get(VARIABLES); + return VariablesParser.toMap(node); } return Optional.empty(); } @@ -197,14 +195,14 @@ default boolean hasSource() { * * @return JsonArray of fields selected */ - public JsonArray getSelectedFields(); + public ArrayNode getSelectedFields(); /** * Return the fields and source fields in the request * * @return JsonArray of fields selected */ - public JsonArray getSelectedAndSourceFields(); + public ArrayNode getSelectedAndSourceFields(); /** * Return the current type (Query, Mutation ext) @@ -245,61 +243,44 @@ default boolean hasSource() { */ class VariablesParser { - public static Optional> toMap(JsonValue jsonValue) { - if (null != jsonValue - && !JsonValue.NULL.equals(jsonValue) - && !JsonValue.EMPTY_JSON_OBJECT.equals(jsonValue) - && !JsonValue.EMPTY_JSON_ARRAY.equals(jsonValue)) { - return Optional.of(toMap(jsonValue.asJsonObject())); + public static Optional> toMap(JsonNode node) { + if (node != null && !node.isNull() && node.isObject() && !node.isEmpty()) { + return Optional.of(toMap((ObjectNode) node)); } return Optional.empty(); } - private static Map toMap(JsonObject jo) { + private static Map toMap(ObjectNode obj) { Map ro = new HashMap<>(); - if (jo != null) { - Set> entrySet = jo.entrySet(); - for (Map.Entry es : entrySet) { - ro.put(es.getKey(), toObject(es.getValue())); + if (obj != null) { + Iterator> fields = obj.properties().iterator(); + while (fields.hasNext()) { + Map.Entry entry = fields.next(); + ro.put(entry.getKey(), toObject(entry.getValue())); } } return ro; } - private static Object toObject(JsonValue jsonValue) { - Object ret = null; - JsonValue.ValueType typ = jsonValue.getValueType(); - if (null != typ) - switch (typ) { - case NUMBER: - ret = ((JsonNumber) jsonValue).bigDecimalValue(); - break; - case STRING: - ret = ((JsonString) jsonValue).getString(); - break; - case FALSE: - ret = Boolean.FALSE; - break; - case TRUE: - ret = Boolean.TRUE; - break; - case ARRAY: - JsonArray arr = (JsonArray) jsonValue; - List vals = new ArrayList<>(); - int sz = arr.size(); - for (int i = 0; i < sz; i++) { - JsonValue v = arr.get(i); - vals.add(toObject(v)); - } - ret = vals; - break; - case OBJECT: - ret = toMap((JsonObject) jsonValue); - break; - default: - break; + private static Object toObject(JsonNode node) { + if (node == null || node.isNull()) { + return null; + } else if (node.isNumber()) { + return node.decimalValue(); + } else if (node.isTextual()) { + return node.asText(); + } else if (node.isBoolean()) { + return node.booleanValue(); + } else if (node.isArray()) { + List vals = new ArrayList<>(); + for (int i = 0; i < node.size(); i++) { + vals.add(toObject(node.get(i))); } - return ret; + return vals; + } else if (node.isObject()) { + return toMap((ObjectNode) node); + } + return null; } } -} \ No newline at end of file +} diff --git a/server/api/src/main/java/io/smallrye/graphql/api/ErrorExtensionProvider.java b/server/api/src/main/java/io/smallrye/graphql/api/ErrorExtensionProvider.java index f0f11033c..fcf4ab66a 100644 --- a/server/api/src/main/java/io/smallrye/graphql/api/ErrorExtensionProvider.java +++ b/server/api/src/main/java/io/smallrye/graphql/api/ErrorExtensionProvider.java @@ -1,6 +1,6 @@ package io.smallrye.graphql.api; -import jakarta.json.JsonValue; +import tools.jackson.databind.JsonNode; /** * To add you own GraphQL error extension fields, you can add your own implementations @@ -9,5 +9,5 @@ public interface ErrorExtensionProvider { String getKey(); - JsonValue mapValueFrom(Throwable exception); + JsonNode mapValueFrom(Throwable exception); } diff --git a/server/api/src/main/java/io/smallrye/graphql/api/Subscription.java b/server/api/src/main/java/io/smallrye/graphql/api/Subscription.java index 2e2854cac..6d363f9c0 100644 --- a/server/api/src/main/java/io/smallrye/graphql/api/Subscription.java +++ b/server/api/src/main/java/io/smallrye/graphql/api/Subscription.java @@ -46,6 +46,10 @@ * stockQuote(stockCode: string): [Stock] * } * + * + * @deprecated Use {@link org.eclipse.microprofile.graphql.Subscription} instead. + * This annotation has been added to the MicroProfile GraphQL specification + * and this SmallRye-specific version will be removed in a future release. */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) diff --git a/server/implementation-cdi/pom.xml b/server/implementation-cdi/pom.xml index 450feb975..96e1b6f4d 100644 --- a/server/implementation-cdi/pom.xml +++ b/server/implementation-cdi/pom.xml @@ -60,11 +60,6 @@ provided - - jakarta.json - jakarta.json-api - - org.junit.jupiter diff --git a/server/implementation-cdi/src/main/java/io/smallrye/graphql/cdi/context/CDISmallRyeContext.java b/server/implementation-cdi/src/main/java/io/smallrye/graphql/cdi/context/CDISmallRyeContext.java index f2238a625..3146de311 100644 --- a/server/implementation-cdi/src/main/java/io/smallrye/graphql/cdi/context/CDISmallRyeContext.java +++ b/server/implementation-cdi/src/main/java/io/smallrye/graphql/cdi/context/CDISmallRyeContext.java @@ -4,9 +4,6 @@ import java.util.Map; import java.util.Optional; -import jakarta.json.JsonArray; -import jakarta.json.JsonObject; - import graphql.ExecutionInput; import graphql.schema.DataFetchingEnvironment; import io.smallrye.graphql.execution.QueryCache; @@ -14,6 +11,8 @@ import io.smallrye.graphql.execution.context.SmallRyeContext; import io.smallrye.graphql.execution.context.SmallRyeContextManager; import io.smallrye.graphql.schema.model.Field; +import tools.jackson.databind.node.ArrayNode; +import tools.jackson.databind.node.ObjectNode; /** * Class that serves as a proxy decorator for obtaining the current SmallRyeContext. It always calls @@ -105,22 +104,22 @@ public String getOperationType() { } @Override - public void setSelectedAndSourceFields(JsonArray selectedAndSourceFields) { + public void setSelectedAndSourceFields(ArrayNode selectedAndSourceFields) { SmallRyeContextManager.getCurrentSmallRyeContext().setSelectedAndSourceFields(selectedAndSourceFields); } @Override - public JsonArray getSelectedAndSourceFields() { + public ArrayNode getSelectedAndSourceFields() { return SmallRyeContextManager.getCurrentSmallRyeContext().getSelectedAndSourceFields(); } @Override - public void setSelectedFields(JsonArray selectedFields) { + public void setSelectedFields(ArrayNode selectedFields) { SmallRyeContextManager.getCurrentSmallRyeContext().setSelectedFields(selectedFields); } @Override - public JsonArray getSelectedFields() { + public ArrayNode getSelectedFields() { return SmallRyeContextManager.getCurrentSmallRyeContext().getSelectedFields(); } @@ -185,12 +184,12 @@ public String getExecutionId() { } @Override - public void setRequest(JsonObject request) { + public void setRequest(ObjectNode request) { SmallRyeContextManager.getCurrentSmallRyeContext().setRequest(request); } @Override - public JsonObject getRequest() { + public ObjectNode getRequest() { return SmallRyeContextManager.getCurrentSmallRyeContext().getRequest(); } diff --git a/server/implementation-cdi/src/test/java/io/smallrye/graphql/execution/CdiExecutionTest.java b/server/implementation-cdi/src/test/java/io/smallrye/graphql/execution/CdiExecutionTest.java index 8b8aa0b69..04d36d55c 100644 --- a/server/implementation-cdi/src/test/java/io/smallrye/graphql/execution/CdiExecutionTest.java +++ b/server/implementation-cdi/src/test/java/io/smallrye/graphql/execution/CdiExecutionTest.java @@ -1,6 +1,5 @@ package io.smallrye.graphql.execution; -import static io.smallrye.graphql.JsonProviderHolder.JSON_PROVIDER; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -9,8 +8,6 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.RequestScoped; import jakarta.inject.Inject; -import jakarta.json.JsonObject; -import jakarta.json.JsonObjectBuilder; import org.eclipse.microprofile.graphql.tck.apps.basic.api.ScalarTestApi; import org.eclipse.microprofile.graphql.tck.apps.superhero.api.HeroFinder; @@ -27,6 +24,7 @@ import io.smallrye.graphql.schema.model.Schema; import io.smallrye.graphql.spi.LookupService; import tools.jackson.databind.node.ArrayNode; +import tools.jackson.databind.node.JsonNodeFactory; import tools.jackson.databind.node.ObjectNode; /** @@ -394,10 +392,8 @@ private ArrayNode executeAndGetError(String graphQL) { return (ArrayNode) result.getExecutionResultAsJsonObject().get(ERRORS); } - private JsonObject toJsonObject(String graphQL) { - JsonObjectBuilder builder = JSON_PROVIDER.createObjectBuilder(); - builder.add("query", graphQL); - return builder.build(); + private ObjectNode toJsonObject(String graphQL) { + return JsonNodeFactory.instance.objectNode().put("query", graphQL); } private static final String DATA = "data"; diff --git a/server/implementation-servlet/pom.xml b/server/implementation-servlet/pom.xml index 0ca67f243..2c560fc75 100644 --- a/server/implementation-servlet/pom.xml +++ b/server/implementation-servlet/pom.xml @@ -57,11 +57,6 @@ jakarta.annotation-api provided - - jakarta.json - jakarta.json-api - provided - tools.jackson.core jackson-databind diff --git a/server/implementation-servlet/src/main/java/io/smallrye/graphql/servlet/ExecutionServlet.java b/server/implementation-servlet/src/main/java/io/smallrye/graphql/servlet/ExecutionServlet.java index 2a87a3bad..0b9c8c84c 100644 --- a/server/implementation-servlet/src/main/java/io/smallrye/graphql/servlet/ExecutionServlet.java +++ b/server/implementation-servlet/src/main/java/io/smallrye/graphql/servlet/ExecutionServlet.java @@ -1,26 +1,20 @@ package io.smallrye.graphql.servlet; -import static io.smallrye.graphql.JsonProviderHolder.JSON_PROVIDER; - import java.io.BufferedReader; import java.io.IOException; -import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; import jakarta.inject.Inject; -import jakarta.json.JsonObject; -import jakarta.json.JsonObjectBuilder; -import jakarta.json.JsonReader; -import jakarta.json.JsonReaderFactory; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; @@ -28,6 +22,11 @@ import io.smallrye.graphql.execution.ExecutionService; import io.smallrye.graphql.spi.config.Config; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.node.JsonNodeFactory; +import tools.jackson.databind.node.ObjectNode; /** * Executing the GraphQL request @@ -38,7 +37,8 @@ public class ExecutionServlet extends HttpServlet { private static final long serialVersionUID = -2859915918802356120L; - private static final JsonReaderFactory jsonReaderFactory = JSON_PROVIDER.createReaderFactory(null); + private static final ObjectMapper OBJECT_MAPPER = JsonMapper.builder().build(); + private static final JsonNodeFactory NODE_FACTORY = JsonNodeFactory.instance; @Inject ExecutionService executionService; @@ -56,7 +56,7 @@ public ExecutionServlet(ExecutionService executionService) { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { try { if (config.isAllowGet()) { - JsonObject jsonObject = getJsonObjectFromQueryParameters(request); + ObjectNode jsonObject = getObjectNodeFromQueryParameters(request); executionService.executeSync(jsonObject, getMetaData(request), new HttpServletResponseWriter(response)); } else { response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "GET Queries is not enabled"); @@ -71,16 +71,14 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { try { Map metaData = getMetaData(request); - JsonObject jsonObjectFromBody = getJsonObjectFromBody(request); + ObjectNode objectNodeFromBody = getObjectNodeFromBody(request); if (request.getQueryString() != null && !request.getQueryString().isEmpty() && config.isAllowPostWithQueryParameters()) { - JsonObject jsonObjectFromQueryParameters = getJsonObjectFromQueryParameters(request); - JsonObject mergedJsonObject = JSON_PROVIDER.createMergePatch(jsonObjectFromQueryParameters) - .apply(jsonObjectFromBody) - .asJsonObject(); - executionService.executeSync(mergedJsonObject, metaData, new HttpServletResponseWriter(response)); + ObjectNode objectNodeFromQueryParameters = getObjectNodeFromQueryParameters(request); + ObjectNode mergedNode = mergeNodes(objectNodeFromBody, objectNodeFromQueryParameters); + executionService.executeSync(mergedNode, metaData, new HttpServletResponseWriter(response)); } else { - executionService.executeSync(jsonObjectFromBody, metaData, new HttpServletResponseWriter(response)); + executionService.executeSync(objectNodeFromBody, metaData, new HttpServletResponseWriter(response)); } } catch (IOException ex) { SmallRyeGraphQLServletLogging.log.ioException(ex); @@ -88,62 +86,72 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) } } - private JsonObject getJsonObjectFromQueryParameters(HttpServletRequest request) throws UnsupportedEncodingException { - JsonObjectBuilder input = JSON_PROVIDER.createObjectBuilder(); + private ObjectNode getObjectNodeFromQueryParameters(HttpServletRequest request) throws UnsupportedEncodingException { + ObjectNode input = NODE_FACTORY.objectNode(); // Query String query = request.getParameter(QUERY); if (query != null && !query.isEmpty()) { - input.add(QUERY, URLDecoder.decode(query, StandardCharsets.UTF_8)); + input.put(QUERY, URLDecoder.decode(query, StandardCharsets.UTF_8)); } // OperationName String operationName = request.getParameter(OPERATION_NAME); if (operationName != null && !operationName.isEmpty()) { - input.add(OPERATION_NAME, URLDecoder.decode(query, StandardCharsets.UTF_8)); + input.put(OPERATION_NAME, URLDecoder.decode(operationName, StandardCharsets.UTF_8)); } // Variables String variables = request.getParameter(VARIABLES); if (variables != null && !variables.isEmpty()) { - JsonObject jsonObject = toJsonObject(URLDecoder.decode(variables, StandardCharsets.UTF_8)); - input.add(VARIABLES, jsonObject); + ObjectNode jsonObject = toObjectNode(URLDecoder.decode(variables, StandardCharsets.UTF_8)); + input.set(VARIABLES, jsonObject); } // Extensions String extensions = request.getParameter(EXTENSIONS); if (extensions != null && !extensions.isEmpty()) { - JsonObject jsonObject = toJsonObject(URLDecoder.decode(extensions, StandardCharsets.UTF_8)); - input.add(EXTENSIONS, jsonObject); + ObjectNode jsonObject = toObjectNode(URLDecoder.decode(extensions, StandardCharsets.UTF_8)); + input.set(EXTENSIONS, jsonObject); } - return input.build(); + return input; } - private JsonObject getJsonObjectFromBody(HttpServletRequest request) throws IOException { + private ObjectNode getObjectNodeFromBody(HttpServletRequest request) throws IOException { String contentType = request.getContentType(); try (BufferedReader bufferedReader = request.getReader()) { - // If the content type is application/graphql, the query is in the body if (contentType != null && contentType.startsWith(APPLICATION_GRAPHQL)) { String query = bufferedReader.lines().collect(Collectors.joining("\n")); - JsonObjectBuilder input = JSON_PROVIDER.createObjectBuilder(); - input.add(QUERY, query); - return input.build(); - // Else we expect a Json in the content + return NODE_FACTORY.objectNode().put(QUERY, query); } else { - try (JsonReader jsonReader = jsonReaderFactory.createReader(bufferedReader)) { - return jsonReader.readObject(); - } + return (ObjectNode) OBJECT_MAPPER.readTree(bufferedReader); } } } - private static JsonObject toJsonObject(String jsonString) { + private static ObjectNode toObjectNode(String jsonString) { if (jsonString == null || jsonString.isEmpty()) { return null; } - try (JsonReader jsonReader = JSON_PROVIDER.createReader(new StringReader(jsonString))) { - return jsonReader.readObject(); + return (ObjectNode) OBJECT_MAPPER.readTree(jsonString); + } + + /** + * RFC 7386 JSON Merge Patch: fields from patch override fields in target; + * null-valued fields in patch remove the field from target. + */ + private static ObjectNode mergeNodes(ObjectNode target, ObjectNode patch) { + ObjectNode result = target.deepCopy(); + Iterator> fields = patch.properties().iterator(); + while (fields.hasNext()) { + Map.Entry entry = fields.next(); + if (entry.getValue().isNull()) { + result.remove(entry.getKey()); + } else { + result.set(entry.getKey(), entry.getValue()); + } } + return result; } protected Map getMetaData(HttpServletRequest request) { diff --git a/server/implementation/pom.xml b/server/implementation/pom.xml index 9d5147492..65ea3f003 100644 --- a/server/implementation/pom.xml +++ b/server/implementation/pom.xml @@ -116,10 +116,6 @@ test ${version.mockito} - - jakarta.json - jakarta.json-api - org.eclipse yasson diff --git a/server/implementation/src/main/java/io/smallrye/graphql/JsonProviderHolder.java b/server/implementation/src/main/java/io/smallrye/graphql/JsonProviderHolder.java deleted file mode 100644 index 614aed938..000000000 --- a/server/implementation/src/main/java/io/smallrye/graphql/JsonProviderHolder.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.smallrye.graphql; - -import jakarta.json.spi.JsonProvider; - -// A central place to get the JsonProvider to avoid calling `JsonProvider.provider()` many times -// due to associated performance costs. -public class JsonProviderHolder { - - public static final JsonProvider JSON_PROVIDER = JsonProvider.provider(); - -} diff --git a/server/implementation/src/main/java/io/smallrye/graphql/bootstrap/Bootstrap.java b/server/implementation/src/main/java/io/smallrye/graphql/bootstrap/Bootstrap.java index dd8a7423b..a6659b60e 100644 --- a/server/implementation/src/main/java/io/smallrye/graphql/bootstrap/Bootstrap.java +++ b/server/implementation/src/main/java/io/smallrye/graphql/bootstrap/Bootstrap.java @@ -347,8 +347,12 @@ private void createGraphQLDirectiveType(DirectiveType directiveType) { .type(inputType); if (argumentType.hasDefaultValue()) { - argumentBuilder = argumentBuilder.defaultValueProgrammatic( - sanitizeDefaultValue(argumentType)); + Object sanitized = sanitizeDefaultValue(argumentType); + argumentBuilder = argumentBuilder.defaultValueProgrammatic(sanitized); + if (sanitized != null) { + argumentBuilder = argumentBuilder.defaultValueLiteral( + DefaultValueAstHelper.toAstValue(sanitized)); + } } directiveBuilder = directiveBuilder.argument(argumentBuilder.build()); @@ -772,7 +776,12 @@ private GraphQLDirective createGraphQLDirectiveFrom(DirectiveInstance directiveI .valueProgrammatic(entry.getValue()); if (argumentType.hasDefaultValue()) { - argumentBuilder = argumentBuilder.defaultValueProgrammatic(sanitizeDefaultValue(argumentType)); + Object sanitized = sanitizeDefaultValue(argumentType); + argumentBuilder = argumentBuilder.defaultValueProgrammatic(sanitized); + if (sanitized != null) { + argumentBuilder = argumentBuilder.defaultValueLiteral( + DefaultValueAstHelper.toAstValue(sanitized)); + } } directiveBuilder.argument(argumentBuilder.build()); @@ -796,7 +805,12 @@ private GraphQLDirective createGraphQLDirectiveFrom(DirectiveInstance directiveI .type(inputType); if (argumentType.hasDefaultValue()) { - argumentBuilder = argumentBuilder.defaultValueProgrammatic(sanitizeDefaultValue(argumentType)); + Object sanitized = sanitizeDefaultValue(argumentType); + argumentBuilder = argumentBuilder.defaultValueProgrammatic(sanitized); + if (sanitized != null) { + argumentBuilder = argumentBuilder.defaultValueLiteral( + DefaultValueAstHelper.toAstValue(sanitized)); + } } directiveBuilder.argument(argumentBuilder.build()); @@ -984,7 +998,13 @@ private GraphQLInputObjectField createGraphQLInputObjectFieldFromField(Field fie // Default value (on method) if (field.hasDefaultValue()) { - inputFieldBuilder = inputFieldBuilder.defaultValueProgrammatic(sanitizeDefaultValue(field)); + Object sanitized = sanitizeDefaultValue(field); + inputFieldBuilder = inputFieldBuilder.defaultValueProgrammatic(sanitized); + // Also set literal value for schema printing + if (sanitized != null) { + inputFieldBuilder = inputFieldBuilder.defaultValueLiteral( + DefaultValueAstHelper.toAstValue(sanitized)); + } } return inputFieldBuilder.build(); @@ -1135,7 +1155,13 @@ private GraphQLArgument createGraphQLArgument(Argument argument) { .description(argument.getDescription()); if (argument.hasDefaultValue()) { - argumentBuilder = argumentBuilder.defaultValueProgrammatic(sanitizeDefaultValue(argument)); + Object sanitized = sanitizeDefaultValue(argument); + argumentBuilder = argumentBuilder.defaultValueProgrammatic(sanitized); + // Also set literal value for schema printing + if (sanitized != null) { + argumentBuilder = argumentBuilder.defaultValueLiteral( + DefaultValueAstHelper.toAstValue(sanitized)); + } } GraphQLInputType graphQLInputType = createGraphQLInputType(argument); @@ -1171,7 +1197,8 @@ private Object sanitizeDefaultValue(Field field) { ReferenceType referenceType = reference.getType(); if (referenceType.equals(ReferenceType.INPUT) || referenceType.equals(ReferenceType.TYPE)) { - deserType = Map.class; + // Use LinkedHashMap to preserve field order from JSON + deserType = java.util.LinkedHashMap.class; } else { deserType = classloadingService.loadClass(field.getReference().getClassName()); } diff --git a/server/implementation/src/main/java/io/smallrye/graphql/bootstrap/DataFetcherFactory.java b/server/implementation/src/main/java/io/smallrye/graphql/bootstrap/DataFetcherFactory.java index 95dcdffcf..b0be58a8c 100644 --- a/server/implementation/src/main/java/io/smallrye/graphql/bootstrap/DataFetcherFactory.java +++ b/server/implementation/src/main/java/io/smallrye/graphql/bootstrap/DataFetcherFactory.java @@ -92,6 +92,8 @@ private V get(Operation operation, Type type) { return (V) getMultiDataFetcher(operation, type); } else if (isPublisher(operation)) { return (V) getPublisherDataFetcher(operation, type); + } else if (isFlowPublisher(operation)) { + return (V) getPublisherDataFetcher(operation, type); } else if (isWrapped(operation)) { return (V) getOtherWrappedDataFetcher(operation, type); } @@ -214,6 +216,14 @@ private boolean isPublisher(Field field) { return false; } + private boolean isFlowPublisher(Field field) { + if (field.hasWrapper()) { + String wrapperClassName = field.getWrapper().getWrapperClassName(); + return wrapperClassName.equals("java.util.concurrent.Flow$Publisher"); + } + return false; + } + private boolean isMutinyMulti(Field field) { if (field.hasWrapper()) { String wrapperClassName = field.getWrapper().getWrapperClassName(); diff --git a/server/implementation/src/main/java/io/smallrye/graphql/bootstrap/DefaultValueAstHelper.java b/server/implementation/src/main/java/io/smallrye/graphql/bootstrap/DefaultValueAstHelper.java new file mode 100644 index 000000000..38714f83c --- /dev/null +++ b/server/implementation/src/main/java/io/smallrye/graphql/bootstrap/DefaultValueAstHelper.java @@ -0,0 +1,105 @@ +package io.smallrye.graphql.bootstrap; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Collection; +import java.util.Map; + +import org.jboss.logging.Logger; + +import graphql.language.ArrayValue; +import graphql.language.BooleanValue; +import graphql.language.EnumValue; +import graphql.language.FloatValue; +import graphql.language.IntValue; +import graphql.language.NullValue; +import graphql.language.ObjectField; +import graphql.language.ObjectValue; +import graphql.language.StringValue; +import graphql.language.Value; + +/** + * Converts Java objects to GraphQL AST {@link Value} nodes for schema printing. + *

+ * graphql-java's {@code defaultValueProgrammatic()} handles runtime behavior but does not + * render default values in printed SDL or introspection; {@code defaultValueLiteral()} with + * an AST Value is needed for that. This helper bridges the two representations. + */ +public class DefaultValueAstHelper { + + private static final Logger LOG = Logger.getLogger(DefaultValueAstHelper.class); + + private DefaultValueAstHelper() { + } + + public static Value toAstValue(Object value) { + if (value == null) { + return NullValue.newNullValue().build(); + } + + if (value instanceof Map) { + return toObjectValue((Map) value); + } else if (value instanceof Collection) { + return toCollectionValue((Collection) value); + } else if (value.getClass().isArray()) { + return toArrayValue(value); + } else if (value instanceof String) { + return StringValue.newStringValue((String) value).build(); + } else if (value instanceof Boolean) { + return BooleanValue.newBooleanValue((Boolean) value).build(); + } else if (value instanceof Number) { + return toNumericValue((Number) value); + } else if (value instanceof Enum) { + return EnumValue.newEnumValue(((Enum) value).name()).build(); + } + + LOG.warnf("Unrecognized default value type for schema printing: %s", value.getClass().getName()); + return StringValue.newStringValue(value.toString()).build(); + } + + private static ObjectValue toObjectValue(Map map) { + ObjectValue.Builder builder = ObjectValue.newObjectValue(); + for (Map.Entry entry : map.entrySet()) { + builder.objectField(ObjectField.newObjectField() + .name(entry.getKey().toString()) + .value(toAstValue(entry.getValue())) + .build()); + } + return builder.build(); + } + + private static ArrayValue toCollectionValue(Collection collection) { + ArrayValue.Builder builder = ArrayValue.newArrayValue(); + for (Object item : collection) { + builder.value(toAstValue(item)); + } + return builder.build(); + } + + private static ArrayValue toArrayValue(Object array) { + ArrayValue.Builder builder = ArrayValue.newArrayValue(); + int length = java.lang.reflect.Array.getLength(array); + for (int i = 0; i < length; i++) { + builder.value(toAstValue(java.lang.reflect.Array.get(array, i))); + } + return builder.build(); + } + + private static Value toNumericValue(Number number) { + if (number instanceof BigInteger) { + return IntValue.newIntValue((BigInteger) number).build(); + } + if (number instanceof BigDecimal) { + BigDecimal bd = (BigDecimal) number; + if (bd.scale() <= 0) { + return IntValue.newIntValue(bd.toBigInteger()).build(); + } + return FloatValue.newFloatValue(bd).build(); + } + if (number instanceof Integer || number instanceof Long + || number instanceof Short || number instanceof Byte) { + return IntValue.newIntValue(BigInteger.valueOf(number.longValue())).build(); + } + return FloatValue.newFloatValue(BigDecimal.valueOf(number.doubleValue())).build(); + } +} diff --git a/server/implementation/src/main/java/io/smallrye/graphql/execution/ExecutionResponse.java b/server/implementation/src/main/java/io/smallrye/graphql/execution/ExecutionResponse.java index f65d36f5a..8af2af7ce 100644 --- a/server/implementation/src/main/java/io/smallrye/graphql/execution/ExecutionResponse.java +++ b/server/implementation/src/main/java/io/smallrye/graphql/execution/ExecutionResponse.java @@ -1,6 +1,5 @@ package io.smallrye.graphql.execution; -import java.io.StringReader; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Collection; @@ -11,10 +10,6 @@ import java.util.Stack; import java.util.stream.Collectors; -import jakarta.json.JsonObject; -import jakarta.json.JsonReader; -import jakarta.json.spi.JsonProvider; - import com.fasterxml.jackson.annotation.JsonInclude; import graphql.ExecutionResult; @@ -83,20 +78,6 @@ public ObjectNode getExecutionResultAsJsonObject() { return returnObject; } - /** - * Bridge method: converts the Jackson ObjectNode to a JSON-P JsonObject. - * This is needed for callers that still use JSON-P types (websocket handlers, JsonObjectResponseWriter). - * Will be removed when those callers are migrated to Jackson. - */ - public JsonObject getExecutionResultAsJsonPObject() { - String jsonString = getExecutionResultAsString(); - JsonProvider jsonProvider = JsonProvider.provider(); - try (StringReader sr = new StringReader(jsonString); - JsonReader reader = jsonProvider.createReader(sr)) { - return reader.readObject(); - } - } - public String getExecutionResultAsString() { try { return OBJECT_MAPPER.writeValueAsString(getExecutionResultAsJsonObject()); @@ -223,8 +204,6 @@ private JsonNode toJsonNode(Object pojo) { return NODE_FACTORY.numberNode((BigInteger) pojo); } else if (pojo instanceof Enum) { return NODE_FACTORY.textNode(((Enum) pojo).name()); - } else if (pojo instanceof jakarta.json.JsonValue) { - return jsonPValueToJsonNode((jakarta.json.JsonValue) pojo); } else { try { return OBJECT_MAPPER.valueToTree(pojo); @@ -246,44 +225,6 @@ private void popFromThePathBuffer() { } } - private static JsonNode jsonPValueToJsonNode(jakarta.json.JsonValue jsonPValue) { - switch (jsonPValue.getValueType()) { - case OBJECT: - ObjectNode objectNode = NODE_FACTORY.objectNode(); - jakarta.json.JsonObject jsonPObject = jsonPValue.asJsonObject(); - for (String key : jsonPObject.keySet()) { - objectNode.set(key, jsonPValueToJsonNode(jsonPObject.get(key))); - } - return objectNode; - case ARRAY: - ArrayNode arrayNode = NODE_FACTORY.arrayNode(); - for (jakarta.json.JsonValue item : jsonPValue.asJsonArray()) { - arrayNode.add(jsonPValueToJsonNode(item)); - } - return arrayNode; - case STRING: - return NODE_FACTORY.textNode(((jakarta.json.JsonString) jsonPValue).getString()); - case NUMBER: - jakarta.json.JsonNumber jsonPNumber = (jakarta.json.JsonNumber) jsonPValue; - if (jsonPNumber.isIntegral()) { - try { - return NODE_FACTORY.numberNode(jsonPNumber.longValueExact()); - } catch (ArithmeticException e) { - return NODE_FACTORY.numberNode(jsonPNumber.bigIntegerValue()); - } - } - return NODE_FACTORY.numberNode(jsonPNumber.bigDecimalValue()); - case TRUE: - return NODE_FACTORY.booleanNode(true); - case FALSE: - return NODE_FACTORY.booleanNode(false); - case NULL: - return NODE_FACTORY.nullNode(); - default: - return NODE_FACTORY.nullNode(); - } - } - private static JsonNode excludeNullFields(JsonNode jsonNode) { if (jsonNode instanceof ObjectNode) { ObjectNode objectNode = (ObjectNode) jsonNode; diff --git a/server/implementation/src/main/java/io/smallrye/graphql/execution/ExecutionService.java b/server/implementation/src/main/java/io/smallrye/graphql/execution/ExecutionService.java index 5096cc92b..4cabf0869 100644 --- a/server/implementation/src/main/java/io/smallrye/graphql/execution/ExecutionService.java +++ b/server/implementation/src/main/java/io/smallrye/graphql/execution/ExecutionService.java @@ -14,8 +14,6 @@ import java.util.concurrent.SubmissionPublisher; import java.util.concurrent.atomic.AtomicLong; -import jakarta.json.JsonObject; - import org.dataloader.BatchLoaderWithContext; import org.dataloader.CacheKey; import org.dataloader.DataLoader; @@ -56,6 +54,7 @@ import io.smallrye.graphql.spi.config.Config; import io.smallrye.graphql.spi.config.LogPayloadOption; import io.smallrye.mutiny.Uni; +import tools.jackson.databind.node.ObjectNode; /** * Executing the GraphQL request @@ -118,7 +117,7 @@ public ExecutionService(GraphQLSchema graphQLSchema, Schema schema, Optional(), writer); } - public void executeSync(JsonObject jsonInput, Map context, ExecutionResponseWriter writer) { + public void executeSync(ObjectNode jsonInput, Map context, ExecutionResponseWriter writer) { execute(jsonInput, context, writer, false); } - public void executeAsync(JsonObject jsonInput, ExecutionResponseWriter writer) { + public void executeAsync(ObjectNode jsonInput, ExecutionResponseWriter writer) { executeAsync(jsonInput, new HashMap<>(), writer); } - public void executeAsync(JsonObject jsonInput, Map context, ExecutionResponseWriter writer) { + public void executeAsync(ObjectNode jsonInput, Map context, ExecutionResponseWriter writer) { execute(jsonInput, context, writer, true); } - public void execute(JsonObject jsonInput, ExecutionResponseWriter writer, boolean async) { + public void execute(ObjectNode jsonInput, ExecutionResponseWriter writer, boolean async) { execute(jsonInput, new HashMap<>(), writer, async); } - public void execute(JsonObject jsonInput, Map context, ExecutionResponseWriter writer, boolean async) { + public void execute(ObjectNode jsonInput, Map context, ExecutionResponseWriter writer, boolean async) { SmallRyeContext smallRyeContext = SmallRyeContextManager.fromInitialRequest(jsonInput); // ExecutionId diff --git a/server/implementation/src/main/java/io/smallrye/graphql/execution/JsonObjectResponseWriter.java b/server/implementation/src/main/java/io/smallrye/graphql/execution/JsonObjectResponseWriter.java index 66af6f8d4..21824d5a6 100644 --- a/server/implementation/src/main/java/io/smallrye/graphql/execution/JsonObjectResponseWriter.java +++ b/server/implementation/src/main/java/io/smallrye/graphql/execution/JsonObjectResponseWriter.java @@ -1,20 +1,14 @@ package io.smallrye.graphql.execution; -import static io.smallrye.graphql.JsonProviderHolder.JSON_PROVIDER; - -import java.io.IOException; -import java.io.StringWriter; -import java.util.HashMap; -import java.util.Map; - -import jakarta.json.JsonObject; -import jakarta.json.JsonObjectBuilder; -import jakarta.json.JsonWriter; -import jakarta.json.JsonWriterFactory; -import jakarta.json.stream.JsonGenerator; - import org.jboss.logging.Logger; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.SerializationFeature; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.node.JsonNodeFactory; +import tools.jackson.databind.node.ObjectNode; + /** * A default implementation for Execution Response Writer * @@ -23,15 +17,20 @@ public class JsonObjectResponseWriter implements ExecutionResponseWriter { protected static final Logger LOG = Logger.getLogger(JsonObjectResponseWriter.class.getName()); + private static final ObjectMapper PRETTY_MAPPER = JsonMapper.builder() + .enable(SerializationFeature.INDENT_OUTPUT) + .build(); + private static final JsonNodeFactory NODE_FACTORY = JsonNodeFactory.instance; + private ExecutionResponse executionResponse = null; private Throwable throwable = null; - private final JsonObject input; + private final ObjectNode input; JsonObjectResponseWriter(String graphQL) { - this.input = toJsonObject(graphQL); + this.input = NODE_FACTORY.objectNode().put("query", graphQL); } - JsonObjectResponseWriter(JsonObject input) { + JsonObjectResponseWriter(ObjectNode input) { this.input = input; } @@ -46,55 +45,37 @@ public void fail(Throwable t) { } public void logInput() { - String prettyInput = getPrettyJson(input); - LOG.info(prettyInput); + LOG.info(getPrettyJson(input)); } public void logOutput() { if (executionResponse != null) { - String prettyData = getPrettyJson(executionResponse.getExecutionResultAsJsonPObject()); - LOG.info(prettyData); + LOG.info(getPrettyJson(executionResponse.getExecutionResultAsJsonObject())); } else if (throwable != null) { LOG.error("ERROR", throwable); } } - public JsonObject getInput() { + public ObjectNode getInput() { return this.input; } - public JsonObject getOutput() { + public ObjectNode getOutput() { if (this.executionResponse != null) { - return this.executionResponse.getExecutionResultAsJsonPObject(); + return this.executionResponse.getExecutionResultAsJsonObject(); } - return JsonObject.EMPTY_JSON_OBJECT; + return NODE_FACTORY.objectNode(); } public ExecutionResponse getExecutionResponse() { return this.executionResponse; } - private String getPrettyJson(JsonObject jsonObject) { - - JsonWriterFactory writerFactory = JSON_PROVIDER.createWriterFactory(JSON_PROPERTIES); - - try (StringWriter sw = new StringWriter(); - JsonWriter jsonWriter = writerFactory.createWriter(sw)) { - jsonWriter.writeObject(jsonObject); - return sw.toString(); - } catch (IOException ex) { - throw new RuntimeException(ex); + private String getPrettyJson(ObjectNode node) { + try { + return PRETTY_MAPPER.writeValueAsString(node); + } catch (JacksonException e) { + throw new RuntimeException(e); } } - - private JsonObject toJsonObject(String graphQL) { - JsonObjectBuilder builder = JSON_PROVIDER.createObjectBuilder(); - builder.add("query", graphQL); - return builder.build(); - } - - private static final Map JSON_PROPERTIES = new HashMap<>(1); - static { - JSON_PROPERTIES.put(JsonGenerator.PRETTY_PRINTING, true); - } } diff --git a/server/implementation/src/main/java/io/smallrye/graphql/execution/context/SmallRyeContext.java b/server/implementation/src/main/java/io/smallrye/graphql/execution/context/SmallRyeContext.java index 422fdcfc0..b0f3b24ec 100644 --- a/server/implementation/src/main/java/io/smallrye/graphql/execution/context/SmallRyeContext.java +++ b/server/implementation/src/main/java/io/smallrye/graphql/execution/context/SmallRyeContext.java @@ -7,9 +7,6 @@ import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; -import jakarta.json.JsonArray; -import jakarta.json.JsonObject; - import graphql.ExecutionInput; import graphql.ExecutionResult; import graphql.language.Document; @@ -17,6 +14,8 @@ import io.smallrye.graphql.api.Context; import io.smallrye.graphql.execution.QueryCache; import io.smallrye.graphql.schema.model.Field; +import tools.jackson.databind.node.ArrayNode; +import tools.jackson.databind.node.ObjectNode; /** * Implements the Context from MicroProfile API. @@ -37,15 +36,15 @@ public class SmallRyeContext implements Context { private final String createdBy; private String fetchId; - private JsonObject request; + private ObjectNode request; private String executionId; private Field field; private String fieldName; private Map arguments; private Object source; private String path; - private JsonArray selectedFields; - private JsonArray selectedAndSourceFields; + private ArrayNode selectedFields; + private ArrayNode selectedAndSourceFields; private String operationType; private List requestedOperationTypes; private String parentTypeName; @@ -135,11 +134,11 @@ public SmallRyeContext(String createdBy) { } @Override - public JsonObject getRequest() { + public ObjectNode getRequest() { return this.request; } - public void setRequest(JsonObject request) { + public void setRequest(ObjectNode request) { this.request = request; } @@ -197,20 +196,20 @@ public void setPath(String path) { } @Override - public JsonArray getSelectedFields() { + public ArrayNode getSelectedFields() { return this.selectedFields; } - public void setSelectedFields(JsonArray selectedFields) { + public void setSelectedFields(ArrayNode selectedFields) { this.selectedFields = selectedFields; } @Override - public JsonArray getSelectedAndSourceFields() { + public ArrayNode getSelectedAndSourceFields() { return selectedAndSourceFields; } - public void setSelectedAndSourceFields(JsonArray selectedAndSourceFields) { + public void setSelectedAndSourceFields(ArrayNode selectedAndSourceFields) { this.selectedAndSourceFields = selectedAndSourceFields; } diff --git a/server/implementation/src/main/java/io/smallrye/graphql/execution/context/SmallRyeContextManager.java b/server/implementation/src/main/java/io/smallrye/graphql/execution/context/SmallRyeContextManager.java index 33552bffc..68fa84777 100644 --- a/server/implementation/src/main/java/io/smallrye/graphql/execution/context/SmallRyeContextManager.java +++ b/server/implementation/src/main/java/io/smallrye/graphql/execution/context/SmallRyeContextManager.java @@ -1,22 +1,14 @@ package io.smallrye.graphql.execution.context; -import static io.smallrye.graphql.JsonProviderHolder.JSON_PROVIDER; - import java.util.ArrayList; import java.util.HashMap; +import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; -import jakarta.json.JsonArray; -import jakarta.json.JsonArrayBuilder; -import jakarta.json.JsonBuilderFactory; -import jakarta.json.JsonObject; -import jakarta.json.JsonObjectBuilder; -import jakarta.json.JsonValue; - import graphql.ExecutionInput; import graphql.GraphQLContext; import graphql.language.Document; @@ -36,6 +28,10 @@ import io.smallrye.graphql.schema.model.Field; import io.smallrye.graphql.schema.model.Operation; import io.smallrye.graphql.schema.model.Type; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.node.ArrayNode; +import tools.jackson.databind.node.JsonNodeFactory; +import tools.jackson.databind.node.ObjectNode; /** * Adds methods to make the life cycle of the context easy to implement @@ -43,7 +39,7 @@ * @author Phillip Kruger (phillip.kruger@redhat.com) */ public class SmallRyeContextManager { - private static final JsonBuilderFactory jsonbuilder = JSON_PROVIDER.createBuilderFactory(null); + private static final JsonNodeFactory nodeFactory = JsonNodeFactory.instance; private static final InheritableThreadLocal current = new InheritableThreadLocal<>(); public static final String CONTEXT = "context"; @@ -72,7 +68,7 @@ public static void clearCurrentSmallRyeContext() { * @param request the original request * @return the initial context */ - public static SmallRyeContext fromInitialRequest(JsonObject request) { + public static SmallRyeContext fromInitialRequest(ObjectNode request) { SmallRyeContext smallRyeContext = getCurrentSmallRyeContext(); if (smallRyeContext == null) { @@ -165,42 +161,42 @@ private static String getOperationTypeFromDefinition(OperationDefinition definit return definition.getOperation().toString(); } - private static JsonArray buildSelectedFields(Type type, DataFetchingEnvironment dfe, Field field, + private static ArrayNode buildSelectedFields(Type type, DataFetchingEnvironment dfe, Field field, boolean includeSourceFields) { DataFetchingFieldSelectionSet selectionSet = dfe.getSelectionSet(); Set fields = new LinkedHashSet<>(selectionSet.getFields()); - return toJsonArrayBuilder(type, fields, field, includeSourceFields).build(); + return toArrayNode(type, fields, field, includeSourceFields); } - private static JsonArrayBuilder toJsonArrayBuilder(Type type, Set fields, Field field, + private static ArrayNode toArrayNode(Type type, Set fields, Field field, boolean includeSourceFields) { - JsonArrayBuilder builder = jsonbuilder.createArrayBuilder(); + ArrayNode arrayNode = nodeFactory.arrayNode(); for (SelectedField selectedField : fields) { if (!isFlattenScalar(selectedField)) { if (includeSourceFields || !isSourceField(type, selectedField)) { if (isScalar(selectedField)) { - builder = builder.add(selectedField.getName()); + arrayNode.add(selectedField.getName()); } else { - builder = builder.add(toJsonObjectBuilder(type, selectedField, field, includeSourceFields)); + arrayNode.add(toObjectNode(type, selectedField, field, includeSourceFields)); } } } } - return builder; + return arrayNode; } private static boolean isFlattenScalar(SelectedField field) { return field.getQualifiedName().contains("/"); } - private static JsonObjectBuilder toJsonObjectBuilder(Type type, SelectedField selectedField, Field field, + private static ObjectNode toObjectNode(Type type, SelectedField selectedField, Field field, boolean includeSourceFields) { - JsonObjectBuilder builder = jsonbuilder.createObjectBuilder(); + ObjectNode objectNode = nodeFactory.objectNode(); Set fields = new LinkedHashSet<>(selectedField.getSelectionSet().getFields()); - builder = builder.add(selectedField.getName(), - toJsonArrayBuilder(type, fields, field, includeSourceFields)); - return builder; + objectNode.set(selectedField.getName(), + toArrayNode(type, fields, field, includeSourceFields)); + return objectNode; } private static boolean isSourceField(Type type, SelectedField selectedField) { @@ -269,12 +265,12 @@ private static List getRequestedOperationTypes(DocumentSupplier document return allRequestedTypes; } - private static String getOperationName(JsonObject request) { - if (request.containsKey(Context.OPERATION_NAME) + private static String getOperationName(ObjectNode request) { + if (request.has(Context.OPERATION_NAME) && request.get(Context.OPERATION_NAME) != null - && !request.get(Context.OPERATION_NAME).getValueType().equals(JsonValue.ValueType.NULL)) { + && !request.get(Context.OPERATION_NAME).isNull()) { - return request.getString(Context.OPERATION_NAME); + return request.get(Context.OPERATION_NAME).asText(); } return null; } @@ -291,51 +287,50 @@ private static String getOperationName(DataFetchingEnvironment dataFetchingEnvir private static final String EXTENSIONS = "extensions"; - private static Map getExtensions(JsonObject request) { - if (request.containsKey(EXTENSIONS) + private static Map getExtensions(ObjectNode request) { + if (request.has(EXTENSIONS) && request.get(EXTENSIONS) != null - && !request.get(EXTENSIONS).getValueType().equals(JsonValue.ValueType.NULL)) { - JsonObject extensionsJson = request.getJsonObject(EXTENSIONS); + && !request.get(EXTENSIONS).isNull()) { + ObjectNode extensionsJson = (ObjectNode) request.get(EXTENSIONS); Map result = new HashMap<>(); - for (Map.Entry entry : extensionsJson.entrySet()) { - result.put(entry.getKey(), convertJsonValue(entry.getValue())); + Iterator> fields = extensionsJson.properties().iterator(); + while (fields.hasNext()) { + Map.Entry entry = fields.next(); + result.put(entry.getKey(), convertJsonNode(entry.getValue())); } return result; } return null; } - private static Object convertJsonValue(JsonValue value) { - switch (value.getValueType()) { - case STRING: - return ((jakarta.json.JsonString) value).getString(); - case NUMBER: - jakarta.json.JsonNumber number = (jakarta.json.JsonNumber) value; - if (number.isIntegral()) { - return number.longValueExact(); - } - return number.doubleValue(); - case TRUE: - return Boolean.TRUE; - case FALSE: - return Boolean.FALSE; - case NULL: - return null; - case OBJECT: - Map map = new HashMap<>(); - for (Map.Entry entry : value.asJsonObject().entrySet()) { - map.put(entry.getKey(), convertJsonValue(entry.getValue())); - } - return map; - case ARRAY: - List list = new java.util.ArrayList<>(); - for (JsonValue item : value.asJsonArray()) { - list.add(convertJsonValue(item)); - } - return list; - default: - return value.toString(); + private static Object convertJsonNode(JsonNode node) { + if (node == null || node.isNull()) { + return null; + } else if (node.isTextual()) { + return node.asText(); + } else if (node.isNumber()) { + if (node.isIntegralNumber()) { + return node.longValue(); + } + return node.doubleValue(); + } else if (node.isBoolean()) { + return node.booleanValue(); + } else if (node.isObject()) { + Map map = new HashMap<>(); + Iterator> fields = node.properties().iterator(); + while (fields.hasNext()) { + Map.Entry entry = fields.next(); + map.put(entry.getKey(), convertJsonNode(entry.getValue())); + } + return map; + } else if (node.isArray()) { + List list = new ArrayList<>(); + for (JsonNode item : node) { + list.add(convertJsonNode(item)); + } + return list; } + return node.toString(); } } diff --git a/server/implementation/src/main/java/io/smallrye/graphql/execution/datafetcher/PublisherDataFetcher.java b/server/implementation/src/main/java/io/smallrye/graphql/execution/datafetcher/PublisherDataFetcher.java index ead33a949..2c1a5deb4 100644 --- a/server/implementation/src/main/java/io/smallrye/graphql/execution/datafetcher/PublisherDataFetcher.java +++ b/server/implementation/src/main/java/io/smallrye/graphql/execution/datafetcher/PublisherDataFetcher.java @@ -1,6 +1,7 @@ package io.smallrye.graphql.execution.datafetcher; import java.util.List; +import java.util.concurrent.Flow; import org.reactivestreams.Publisher; @@ -26,13 +27,28 @@ public PublisherDataFetcher(Operation operation, Type type) { @Override protected Multi handleUserMethodCall(DataFetchingEnvironment dfe, final Object[] transformedArguments) throws Exception { - Publisher publisher = operationInvoker.invoke(transformedArguments); - return (Multi) Multi.createFrom().publisher(AdaptersToFlow.publisher(publisher)); + return toMulti(operationInvoker.invoke(transformedArguments)); } @Override protected Multi> handleUserBatchLoad(DataFetchingEnvironment dfe, final Object[] arguments) throws Exception { - Publisher> publisher = operationInvoker.invoke(arguments); - return (Multi>) Multi.createFrom().publisher(AdaptersToFlow.publisher(publisher)); + return toMulti(operationInvoker.invoke(arguments)); + } + + @SuppressWarnings("unchecked") + private Multi toMulti(Object result) { + if (result == null) { + throw new IllegalStateException( + "Subscription method " + operation.getName() + " returned null"); + } + if (result instanceof Flow.Publisher) { + return Multi.createFrom().publisher((Flow.Publisher) result); + } + if (result instanceof Publisher) { + return (Multi) Multi.createFrom().publisher(AdaptersToFlow.publisher((Publisher) result)); + } + throw new IllegalStateException( + "Subscription method " + operation.getName() + " returned " + result.getClass().getName() + + " which is neither Flow.Publisher nor org.reactivestreams.Publisher"); } } diff --git a/server/implementation/src/main/java/io/smallrye/graphql/execution/error/ErrorCodeExtensionProvider.java b/server/implementation/src/main/java/io/smallrye/graphql/execution/error/ErrorCodeExtensionProvider.java index a2365da3d..b8ddaea55 100644 --- a/server/implementation/src/main/java/io/smallrye/graphql/execution/error/ErrorCodeExtensionProvider.java +++ b/server/implementation/src/main/java/io/smallrye/graphql/execution/error/ErrorCodeExtensionProvider.java @@ -2,25 +2,22 @@ import static java.util.Locale.ROOT; -import jakarta.json.JsonValue; -import jakarta.json.spi.JsonProvider; - import io.smallrye.graphql.api.ErrorExtensionProvider; import io.smallrye.graphql.schema.model.ErrorInfo; import io.smallrye.graphql.spi.config.Config; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.node.JsonNodeFactory; public class ErrorCodeExtensionProvider implements ErrorExtensionProvider { - private static final JsonProvider JSON_PROVIDER = JsonProvider.provider(); - @Override public String getKey() { return Config.ERROR_EXTENSION_CODE; } @Override - public JsonValue mapValueFrom(Throwable exception) { - return JSON_PROVIDER.createValue(errorCode(exception)); + public JsonNode mapValueFrom(Throwable exception) { + return JsonNodeFactory.instance.textNode(errorCode(exception)); } private String errorCode(Throwable exception) { diff --git a/server/implementation/src/main/java/io/smallrye/graphql/execution/error/ExceptionNameErrorExtensionProvider.java b/server/implementation/src/main/java/io/smallrye/graphql/execution/error/ExceptionNameErrorExtensionProvider.java index edb477cd3..6434bfd58 100644 --- a/server/implementation/src/main/java/io/smallrye/graphql/execution/error/ExceptionNameErrorExtensionProvider.java +++ b/server/implementation/src/main/java/io/smallrye/graphql/execution/error/ExceptionNameErrorExtensionProvider.java @@ -1,22 +1,19 @@ package io.smallrye.graphql.execution.error; -import jakarta.json.JsonString; -import jakarta.json.spi.JsonProvider; - import io.smallrye.graphql.api.ErrorExtensionProvider; import io.smallrye.graphql.spi.config.Config; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.node.JsonNodeFactory; public class ExceptionNameErrorExtensionProvider implements ErrorExtensionProvider { - private static final JsonProvider JSON_PROVIDER = JsonProvider.provider(); - @Override public String getKey() { return Config.ERROR_EXTENSION_EXCEPTION; } @Override - public JsonString mapValueFrom(Throwable exception) { - return JSON_PROVIDER.createValue(exception.getClass().getName()); + public JsonNode mapValueFrom(Throwable exception) { + return JsonNodeFactory.instance.textNode(exception.getClass().getName()); } } diff --git a/server/implementation/src/main/java/io/smallrye/graphql/execution/error/ExecutionErrorsService.java b/server/implementation/src/main/java/io/smallrye/graphql/execution/error/ExecutionErrorsService.java index 53e0e0d1d..1913ddc3d 100644 --- a/server/implementation/src/main/java/io/smallrye/graphql/execution/error/ExecutionErrorsService.java +++ b/server/implementation/src/main/java/io/smallrye/graphql/execution/error/ExecutionErrorsService.java @@ -4,8 +4,6 @@ import java.util.Map; import java.util.Optional; -import jakarta.json.JsonValue; - import graphql.ExceptionWhileDataFetching; import graphql.GraphQLError; import graphql.validation.ValidationError; @@ -87,30 +85,11 @@ private Optional getDataFetchingExtensions(ExceptionWhileDataFetchin private void addErrorExtensions(ObjectNode objectNode, Throwable exception) { errorExtensionProviders.get() .forEach(provider -> { - JsonValue jsonValue = provider.mapValueFrom(exception); - // Convert jakarta.json.JsonValue to Jackson JsonNode - JsonNode jacksonNode = convertJsonValueToJsonNode(jsonValue); + JsonNode jacksonNode = provider.mapValueFrom(exception); addKeyValue(objectNode, provider.getKey(), jacksonNode); }); } - /** - * Bridge method: converts a jakarta.json.JsonValue to a Jackson JsonNode. - * This is needed because ErrorExtensionProvider (in server/api) returns JSON-P types. - */ - private JsonNode convertJsonValueToJsonNode(JsonValue jsonValue) { - if (jsonValue == null) { - return NODE_FACTORY.nullNode(); - } - try { - // Use the JSON-P toString() which produces valid JSON, then parse with Jackson - return OBJECT_MAPPER.readTree(jsonValue.toString()); - } catch (Exception e) { - // fallback: treat as string - return NODE_FACTORY.textNode(jsonValue.toString()); - } - } - private void populateCustomExtensions(ObjectNode objectNode, Map extensions) { if (extensions != null) { for (Map.Entry entry : extensions.entrySet()) { @@ -120,8 +99,6 @@ private void populateCustomExtensions(ObjectNode objectNode, Map Object value = entry.getValue(); if (value instanceof JsonNode) { addKeyValue(objectNode, entry.getKey(), (JsonNode) value); - } else if (value instanceof JsonValue) { - addKeyValue(objectNode, entry.getKey(), convertJsonValueToJsonNode((JsonValue) value)); } else if (value instanceof Map) { addKeyValue(objectNode, entry.getKey(), OBJECT_MAPPER.valueToTree(value)); } else { diff --git a/server/implementation/src/main/java/io/smallrye/graphql/json/JacksonCreator.java b/server/implementation/src/main/java/io/smallrye/graphql/json/JacksonCreator.java index 7c3115800..73cacd15f 100644 --- a/server/implementation/src/main/java/io/smallrye/graphql/json/JacksonCreator.java +++ b/server/implementation/src/main/java/io/smallrye/graphql/json/JacksonCreator.java @@ -194,33 +194,6 @@ public CustomFloatScalar deserialize(JsonParser p, DeserializationContext ctxt) } }); - // jakarta.json.JsonObject deserializer — needed for JSON scalar type support - module.addDeserializer(jakarta.json.JsonObject.class, new ValueDeserializer() { - @Override - public jakarta.json.JsonObject deserialize(JsonParser p, DeserializationContext ctxt) { - JsonNode node = p.readValueAsTree(); - return (jakarta.json.JsonObject) jacksonNodeToJsonPValue(node); - } - }); - - // jakarta.json.JsonArray deserializer - module.addDeserializer(jakarta.json.JsonArray.class, new ValueDeserializer() { - @Override - public jakarta.json.JsonArray deserialize(JsonParser p, DeserializationContext ctxt) { - JsonNode node = p.readValueAsTree(); - return (jakarta.json.JsonArray) jacksonNodeToJsonPValue(node); - } - }); - - // jakarta.json.JsonValue deserializer - module.addDeserializer(jakarta.json.JsonValue.class, new ValueDeserializer() { - @Override - public jakarta.json.JsonValue deserialize(JsonParser p, DeserializationContext ctxt) { - JsonNode node = p.readValueAsTree(); - return jacksonNodeToJsonPValue(node); - } - }); - return module; } @@ -270,32 +243,4 @@ private static boolean isComplexKeyType(Class type) { && !name.startsWith("tools.jackson."); } - private static jakarta.json.JsonValue jacksonNodeToJsonPValue(JsonNode node) { - if (node == null || node.isNull()) { - return jakarta.json.JsonValue.NULL; - } - if (node.isObject()) { - jakarta.json.JsonObjectBuilder builder = jakarta.json.Json.createObjectBuilder(); - node.properties().forEach(entry -> builder.add(entry.getKey(), jacksonNodeToJsonPValue(entry.getValue()))); - return builder.build(); - } - if (node.isArray()) { - jakarta.json.JsonArrayBuilder builder = jakarta.json.Json.createArrayBuilder(); - node.forEach(item -> builder.add(jacksonNodeToJsonPValue(item))); - return builder.build(); - } - if (node.isTextual()) { - return jakarta.json.Json.createValue(node.asText()); - } - if (node.isBoolean()) { - return node.asBoolean() ? jakarta.json.JsonValue.TRUE : jakarta.json.JsonValue.FALSE; - } - if (node.isIntegralNumber()) { - return jakarta.json.Json.createValue(node.longValue()); - } - if (node.isFloatingPointNumber()) { - return jakarta.json.Json.createValue(node.decimalValue()); - } - return jakarta.json.JsonValue.NULL; - } } diff --git a/server/implementation/src/main/java/io/smallrye/graphql/websocket/AbstractGraphQLWebsocketHandler.java b/server/implementation/src/main/java/io/smallrye/graphql/websocket/AbstractGraphQLWebsocketHandler.java index 5aef37965..963985c5b 100644 --- a/server/implementation/src/main/java/io/smallrye/graphql/websocket/AbstractGraphQLWebsocketHandler.java +++ b/server/implementation/src/main/java/io/smallrye/graphql/websocket/AbstractGraphQLWebsocketHandler.java @@ -1,21 +1,12 @@ package io.smallrye.graphql.websocket; -import static io.smallrye.graphql.JsonProviderHolder.JSON_PROVIDER; - import java.io.IOException; -import java.io.StringReader; import java.time.Duration; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; -import jakarta.json.JsonBuilderFactory; -import jakarta.json.JsonObject; -import jakarta.json.JsonReader; -import jakarta.json.JsonReaderFactory; -import jakarta.json.stream.JsonParsingException; - import org.jboss.logging.Logger; import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; @@ -31,12 +22,17 @@ import io.smallrye.graphql.spi.LookupService; import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.subscription.Cancellable; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.node.JsonNodeFactory; +import tools.jackson.databind.node.ObjectNode; public abstract class AbstractGraphQLWebsocketHandler implements GraphQLWebsocketHandler { // TODO: Replace with prepared log messages protected static final Logger LOG = Logger.getLogger(GraphQLWebsocketHandler.class.getName()); - private static final JsonReaderFactory jsonReaderFactory = JSON_PROVIDER.createReaderFactory(null); - private static final JsonBuilderFactory jsonBuilderFactory = JSON_PROVIDER.createBuilderFactory(null); + protected static final ObjectMapper OBJECT_MAPPER = JsonMapper.builder().build(); + protected static final JsonNodeFactory NODE_FACTORY = JsonNodeFactory.instance; protected final ExecutionService executionService = LookupService.get().getInstance(ExecutionService.class).get(); protected final GraphQLWebSocketSession session; @@ -53,7 +49,7 @@ public AbstractGraphQLWebsocketHandler(GraphQLWebSocketSession session, String d this.dataMessageTypeName = dataMessageTypeName; this.context = context; this.connectionInitialized = new AtomicBoolean(false); - this.connectionAckMessage = createConnectionAckMessage().toString(); + this.connectionAckMessage = writeValueAsString(createConnectionAckMessage()); this.activeOperations = new ConcurrentHashMap<>(); this.keepAliveSender = Multi.createFrom().ticks() .startingAfter(Duration.ofSeconds(10)) @@ -66,7 +62,7 @@ public void onMessage(String text) { if (LOG.isTraceEnabled()) { LOG.trace("<<< " + text); } - onMessage(getMessageAsJsonObject(text)); + onMessage(getMessageAsObjectNode(text)); } @Override @@ -101,10 +97,10 @@ protected void sendConnectionAckMessage() throws IOException { } } - protected void onOperationRequest(JsonObject message) { - String operationId = message.getString("id"); + protected void onOperationRequest(ObjectNode message) { + String operationId = message.get("id").asText(); if (validSubscription(operationId)) { - JsonObject payload = message.getJsonObject("payload"); + ObjectNode payload = (ObjectNode) message.get("payload"); executionService.executeAsync(payload, context, new ExecutionResponseWriter() { @Override @@ -133,22 +129,14 @@ public void write(ExecutionResponse executionResponse) { if (executionResult != null) { try { if (!executionResult.isDataPresent()) { - // this means a validation error sendErrorMessage(operationId, executionResponse); } else { Object data = executionResponse.getExecutionResult().getData(); if (data instanceof Map) { - // this means the operation is a query or mutation - // only send the response if the operation hasn't been cancelled sendSingleMessage(operationId, executionResponse); } else if (data instanceof Publisher) { - // this means the operation is a subscription sendStreamingMessage(operationId, executionResponse); } else if (data == null) { - // if isDataPresent() == true && but data == null, - // then this is probably a subscription, but the subscription - // method threw an exception instead of returning - // a failed Multi sendErrorMessage(operationId, executionResponse); } else { logUnknownResult(executionResult); @@ -163,16 +151,15 @@ public void write(ExecutionResponse executionResponse) { } } - private JsonObject createConnectionAckMessage() { - return JSON_PROVIDER.createObjectBuilder() - .add("type", "connection_ack") - .build(); + private ObjectNode createConnectionAckMessage() { + return NODE_FACTORY.objectNode() + .put("type", "connection_ack"); } - private JsonObject getMessageAsJsonObject(String text) { + private ObjectNode getMessageAsObjectNode(String text) { try { return parseIncomingMessage(text); - } catch (JsonParsingException ex) { + } catch (JacksonException ex) { session.close((short) 4400, ex.getMessage()); return null; } catch (NullPointerException | IllegalArgumentException ex) { @@ -182,25 +169,22 @@ private JsonObject getMessageAsJsonObject(String text) { } // TODO: we need more validation on the incoming messages (correct fields and types etc) - private JsonObject parseIncomingMessage(String message) { - try (JsonReader jsonReader = jsonReaderFactory.createReader(new StringReader(message))) { - return jsonReader.readObject(); - } + private ObjectNode parseIncomingMessage(String message) { + return (ObjectNode) OBJECT_MAPPER.readTree(message); } - private JsonObject createCompleteMessage(String operationId) { - return jsonBuilderFactory.createObjectBuilder() - .add("type", "complete") - .add("id", operationId) - .build(); + private ObjectNode createCompleteMessage(String operationId) { + return NODE_FACTORY.objectNode() + .put("type", "complete") + .put("id", operationId); } - private JsonObject createDataMessage(String operationId, JsonObject payload) { - return jsonBuilderFactory.createObjectBuilder() - .add("type", this.dataMessageTypeName) - .add("id", operationId) - .add("payload", payload) - .build(); + private ObjectNode createDataMessage(String operationId, ObjectNode payload) { + ObjectNode msg = NODE_FACTORY.objectNode() + .put("type", this.dataMessageTypeName) + .put("id", operationId); + msg.set("payload", payload); + return msg; } private void logUnknownResult(ExecutionResult executionResult) { @@ -211,10 +195,9 @@ private void logUnknownResult(ExecutionResult executionResult) { private void sendSingleMessage(String operationId, ExecutionResponse executionResponse) throws IOException { if (activeOperations.remove(operationId) != null) { session.sendMessage( - createDataMessage(operationId, - executionResponse.getExecutionResultAsJsonPObject()) - .toString()); - session.sendMessage(createCompleteMessage(operationId).toString()); + writeValueAsString(createDataMessage(operationId, + executionResponse.getExecutionResultAsJsonObject()))); + session.sendMessage(writeValueAsString(createCompleteMessage(operationId))); } } @@ -223,8 +206,6 @@ private void sendStreamingMessage(String operationId, ExecutionResponse executio Publisher stream = executionResponse.getExecutionResult() .getData(); if (stream != null) { - // this is actually a subscription, so replace the `activeOperation` entry - // with the actual subscriber activeOperations.put(operationId, subscriber); stream.subscribe(subscriber); } @@ -239,8 +220,8 @@ private void sendKeepAlive() { } } - protected void sendCancelMessage(JsonObject message) { - String opId = message.getString("id"); + protected void sendCancelMessage(ObjectNode message) { + String opId = message.get("id").asText(); boolean cancelled = cancelOperation(opId); if (cancelled) { if (LOG.isDebugEnabled()) { @@ -255,8 +236,6 @@ protected void sendCancelMessage(JsonObject message) { } } - // cancel the operation with this id, returns true if it actually cancels an operation, - // false if no such operation is active private boolean cancelOperation(String opId) { Subscriber subscriber = activeOperations.remove(opId); if (subscriber != null) { @@ -281,7 +260,15 @@ private boolean validSubscription(String operationId) { return true; } - protected abstract void onMessage(JsonObject message); + protected static String writeValueAsString(ObjectNode node) { + try { + return OBJECT_MAPPER.writeValueAsString(node); + } catch (JacksonException e) { + throw new RuntimeException(e); + } + } + + protected abstract void onMessage(ObjectNode message); protected abstract void sendErrorMessage(String operationId, ExecutionResponse executionResponse) throws IOException; @@ -293,9 +280,6 @@ public Map getContext() { return context; } - /** - * The middleman that subscribes to an execution result and forwards its events to the websocket channel. - */ private class SubscriptionSubscriber implements Subscriber { private final AtomicReference subscription = new AtomicReference<>(); @@ -319,9 +303,8 @@ public void onNext(ExecutionResult executionResult) { ExecutionResponse executionResponse = new ExecutionResponse(executionResult); try { session.sendMessage( - createDataMessage(operationId, - executionResponse.getExecutionResultAsJsonPObject()) - .toString()); + writeValueAsString(createDataMessage(operationId, + executionResponse.getExecutionResultAsJsonObject()))); } catch (IOException e) { LOG.warn(e); } @@ -331,7 +314,6 @@ public void onNext(ExecutionResult executionResult) { @Override public void onError(Throwable t) { - // TODO: I'm not sure if/when this can happen. Even if the operation's root fails, it goes into `onNext`. t.printStackTrace(); } @@ -341,7 +323,7 @@ public void onComplete() { LOG.trace("Subscription with id " + operationId + " completed"); } try { - session.sendMessage(createCompleteMessage(operationId).toString()); + session.sendMessage(writeValueAsString(createCompleteMessage(operationId))); } catch (IOException e) { LOG.warn(e); } @@ -356,7 +338,6 @@ public void cancel() { } } - // dummy value to put into the `activeOperations` map for single-result operations private static final Subscriber SINGLE_RESULT_MARKER = new Subscriber() { @Override public void onSubscribe(Subscription s) { diff --git a/server/implementation/src/main/java/io/smallrye/graphql/websocket/graphqltransportws/GraphQLTransportWSSubprotocolHandler.java b/server/implementation/src/main/java/io/smallrye/graphql/websocket/graphqltransportws/GraphQLTransportWSSubprotocolHandler.java index 3d236f86f..e5aaa82e0 100644 --- a/server/implementation/src/main/java/io/smallrye/graphql/websocket/graphqltransportws/GraphQLTransportWSSubprotocolHandler.java +++ b/server/implementation/src/main/java/io/smallrye/graphql/websocket/graphqltransportws/GraphQLTransportWSSubprotocolHandler.java @@ -1,34 +1,28 @@ package io.smallrye.graphql.websocket.graphqltransportws; -import static io.smallrye.graphql.JsonProviderHolder.JSON_PROVIDER; - import java.io.IOException; import java.util.Collections; import java.util.Map; -import jakarta.json.JsonArray; -import jakarta.json.JsonBuilderFactory; -import jakarta.json.JsonObject; - import io.smallrye.graphql.execution.ExecutionResponse; import io.smallrye.graphql.execution.ExecutionService; import io.smallrye.graphql.websocket.AbstractGraphQLWebsocketHandler; import io.smallrye.graphql.websocket.GraphQLWebSocketSession; +import tools.jackson.databind.node.ArrayNode; +import tools.jackson.databind.node.ObjectNode; /** * Websocket subprotocol handler that implements the `graphql-transport-ws` subprotocol. */ public class GraphQLTransportWSSubprotocolHandler extends AbstractGraphQLWebsocketHandler { - private static final JsonBuilderFactory jsonBuilderFactory = JSON_PROVIDER.createBuilderFactory(null); - private final String pingMessage; private final String pongMessage; public GraphQLTransportWSSubprotocolHandler(GraphQLWebSocketSession session, Map context) { super(session, "next", context); - this.pongMessage = createPongMessage().toString(); - this.pingMessage = createPingMessage().toString(); + this.pongMessage = writeValueAsString(NODE_FACTORY.objectNode().put("type", "pong")); + this.pingMessage = writeValueAsString(NODE_FACTORY.objectNode().put("type", "ping")); } @Deprecated @@ -37,7 +31,7 @@ public GraphQLTransportWSSubprotocolHandler(GraphQLWebSocketSession session, Exe } @Override - protected void onMessage(JsonObject message) { + protected void onMessage(ObjectNode message) { if (message != null) { MessageType messageType = getMessageType(message); try { @@ -45,7 +39,8 @@ protected void onMessage(JsonObject message) { case CONNECTION_INIT: if (message.get("payload") != null) { getContext().put("init-payload", - Collections.unmodifiableMap((Map) message.get("payload"))); + Collections.unmodifiableMap((Map) OBJECT_MAPPER + .treeToValue(message.get("payload"), Map.class))); } sendConnectionAckMessage(); break; @@ -71,8 +66,8 @@ protected void onMessage(JsonObject message) { } } - private MessageType getMessageType(JsonObject message) { - return MessageType.fromString(message.getString("type")); + private MessageType getMessageType(ObjectNode message) { + return MessageType.fromString(message.get("type").asText()); } @Override @@ -82,16 +77,13 @@ protected void closeDueToConnectionNotInitialized() { @Override protected void sendErrorMessage(String operationId, ExecutionResponse executionResponse) throws IOException { - session.sendMessage(createErrorMessage(operationId, - executionResponse.getExecutionResultAsJsonPObject().getJsonArray("errors")).toString()); - } - - private JsonObject createErrorMessage(String operationId, JsonArray errors) { - return jsonBuilderFactory.createObjectBuilder() - .add("id", operationId) - .add("type", "error") - .add("payload", errors) - .build(); + ObjectNode result = executionResponse.getExecutionResultAsJsonObject(); + ArrayNode errors = (ArrayNode) result.get("errors"); + ObjectNode errorMessage = NODE_FACTORY.objectNode() + .put("id", operationId) + .put("type", "error"); + errorMessage.set("payload", errors); + session.sendMessage(writeValueAsString(errorMessage)); } private void sendPongMessage() throws IOException { @@ -103,16 +95,4 @@ protected String getPingMessage() { return pingMessage; } - private JsonObject createPongMessage() { - return jsonBuilderFactory.createObjectBuilder() - .add("type", "pong") - .build(); - } - - private JsonObject createPingMessage() { - return jsonBuilderFactory.createObjectBuilder() - .add("type", "ping") - .build(); - } - } diff --git a/server/implementation/src/main/java/io/smallrye/graphql/websocket/graphqlws/GraphQLWSSubprotocolHandler.java b/server/implementation/src/main/java/io/smallrye/graphql/websocket/graphqlws/GraphQLWSSubprotocolHandler.java index 30f462ec2..c996b3d0e 100644 --- a/server/implementation/src/main/java/io/smallrye/graphql/websocket/graphqlws/GraphQLWSSubprotocolHandler.java +++ b/server/implementation/src/main/java/io/smallrye/graphql/websocket/graphqlws/GraphQLWSSubprotocolHandler.java @@ -1,31 +1,27 @@ package io.smallrye.graphql.websocket.graphqlws; -import static io.smallrye.graphql.JsonProviderHolder.JSON_PROVIDER; - import java.io.IOException; import java.util.Collections; import java.util.Map; -import jakarta.json.JsonBuilderFactory; -import jakarta.json.JsonObject; - import io.smallrye.graphql.execution.ExecutionResponse; import io.smallrye.graphql.execution.ExecutionService; import io.smallrye.graphql.websocket.AbstractGraphQLWebsocketHandler; import io.smallrye.graphql.websocket.GraphQLWebSocketSession; +import tools.jackson.databind.node.ArrayNode; +import tools.jackson.databind.node.ObjectNode; /** * Websocket subprotocol handler that implements the `graphql-ws` subprotocol. */ public class GraphQLWSSubprotocolHandler extends AbstractGraphQLWebsocketHandler { - private static final JsonBuilderFactory jsonBuilderFactory = JSON_PROVIDER.createBuilderFactory(null); - private final String pingMessage; public GraphQLWSSubprotocolHandler(GraphQLWebSocketSession session, Map context) { super(session, MessageType.GQL_DATA.asString(), context); - pingMessage = createPingMessage().toString(); + pingMessage = writeValueAsString( + NODE_FACTORY.objectNode().put("type", MessageType.GQL_CONNECTION_KEEP_ALIVE.asString())); } @Deprecated @@ -34,7 +30,7 @@ public GraphQLWSSubprotocolHandler(GraphQLWebSocketSession session, ExecutionSer } @Override - protected void onMessage(JsonObject message) { + protected void onMessage(ObjectNode message) { if (message != null) { MessageType messageType = getMessageType(message); try { @@ -42,7 +38,8 @@ protected void onMessage(JsonObject message) { case GQL_CONNECTION_INIT: if (message.get("payload") != null) { getContext().put("init-payload", - Collections.unmodifiableMap((Map) message.get("payload"))); + Collections.unmodifiableMap((Map) OBJECT_MAPPER + .treeToValue(message.get("payload"), Map.class))); } sendConnectionAckMessage(); break; @@ -59,8 +56,8 @@ protected void onMessage(JsonObject message) { } } - private MessageType getMessageType(JsonObject message) { - return MessageType.fromString(message.getString("type")); + private MessageType getMessageType(ObjectNode message) { + return MessageType.fromString(message.get("type").asText()); } @Override @@ -75,25 +72,15 @@ protected String getPingMessage() { @Override protected void sendErrorMessage(String operationId, ExecutionResponse executionResponse) throws IOException { - session.sendMessage(createErrorMessage(operationId, - // TODO: the message should have a single error, but executionresult contains an array of errors? what do? - executionResponse.getExecutionResultAsJsonPObject().getJsonArray("errors").get(0) - .asJsonObject()) - .toString()); - } - - private JsonObject createErrorMessage(String operationId, JsonObject error) { - return jsonBuilderFactory.createObjectBuilder() - .add("id", operationId) - .add("type", MessageType.GQL_ERROR.asString()) - .add("payload", error) - .build(); - } - - private JsonObject createPingMessage() { - return jsonBuilderFactory.createObjectBuilder() - .add("type", MessageType.GQL_CONNECTION_KEEP_ALIVE.asString()) - .build(); + ObjectNode result = executionResponse.getExecutionResultAsJsonObject(); + ArrayNode errors = (ArrayNode) result.get("errors"); + // TODO: the message should have a single error, but executionresult contains an array of errors? what do? + ObjectNode error = (ObjectNode) errors.get(0); + ObjectNode errorMessage = NODE_FACTORY.objectNode() + .put("id", operationId) + .put("type", MessageType.GQL_ERROR.asString()); + errorMessage.set("payload", error); + session.sendMessage(writeValueAsString(errorMessage)); } } diff --git a/server/implementation/src/test/java/io/smallrye/graphql/bootstrap/DefaultValueAstHelperTest.java b/server/implementation/src/test/java/io/smallrye/graphql/bootstrap/DefaultValueAstHelperTest.java new file mode 100644 index 000000000..a58c3fd49 --- /dev/null +++ b/server/implementation/src/test/java/io/smallrye/graphql/bootstrap/DefaultValueAstHelperTest.java @@ -0,0 +1,160 @@ +package io.smallrye.graphql.bootstrap; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import graphql.language.ArrayValue; +import graphql.language.BooleanValue; +import graphql.language.EnumValue; +import graphql.language.FloatValue; +import graphql.language.IntValue; +import graphql.language.NullValue; +import graphql.language.ObjectValue; +import graphql.language.StringValue; +import graphql.language.Value; + +class DefaultValueAstHelperTest { + + @Test + void nullProducesNullValue() { + assertInstanceOf(NullValue.class, DefaultValueAstHelper.toAstValue(null)); + } + + @Test + void stringProducesStringValue() { + Value result = DefaultValueAstHelper.toAstValue("hello"); + assertInstanceOf(StringValue.class, result); + assertEquals("hello", ((StringValue) result).getValue()); + } + + @Test + void booleanProducesBooleanValue() { + Value t = DefaultValueAstHelper.toAstValue(true); + assertInstanceOf(BooleanValue.class, t); + assertEquals(true, ((BooleanValue) t).isValue()); + + Value f = DefaultValueAstHelper.toAstValue(false); + assertEquals(false, ((BooleanValue) f).isValue()); + } + + @Test + void integerProducesIntValue() { + Value result = DefaultValueAstHelper.toAstValue(42); + assertInstanceOf(IntValue.class, result); + assertEquals(BigInteger.valueOf(42), ((IntValue) result).getValue()); + } + + @Test + void longProducesIntValue() { + Value result = DefaultValueAstHelper.toAstValue(100L); + assertInstanceOf(IntValue.class, result); + assertEquals(BigInteger.valueOf(100), ((IntValue) result).getValue()); + } + + @Test + void bigIntegerProducesIntValue() { + BigInteger large = new BigInteger("99999999999999999999"); + Value result = DefaultValueAstHelper.toAstValue(large); + assertInstanceOf(IntValue.class, result); + assertEquals(large, ((IntValue) result).getValue()); + } + + @Test + void bigDecimalWholeNumberProducesIntValue() { + BigDecimal bd = new BigDecimal("1000"); + Value result = DefaultValueAstHelper.toAstValue(bd); + assertInstanceOf(IntValue.class, result); + assertEquals(BigInteger.valueOf(1000), ((IntValue) result).getValue()); + } + + @Test + void bigDecimalFractionalProducesFloatValue() { + BigDecimal bd = new BigDecimal("3.14"); + Value result = DefaultValueAstHelper.toAstValue(bd); + assertInstanceOf(FloatValue.class, result); + assertEquals(bd, ((FloatValue) result).getValue()); + } + + @Test + void bigDecimalHighPrecisionPreserved() { + BigDecimal bd = new BigDecimal("1.00000000000000001"); + Value result = DefaultValueAstHelper.toAstValue(bd); + assertInstanceOf(FloatValue.class, result); + assertEquals(bd, ((FloatValue) result).getValue()); + } + + @Test + void doubleProducesFloatValue() { + Value result = DefaultValueAstHelper.toAstValue(2.5); + assertInstanceOf(FloatValue.class, result); + } + + @Test + void enumProducesEnumValue() { + Value result = DefaultValueAstHelper.toAstValue(Thread.State.RUNNABLE); + assertInstanceOf(EnumValue.class, result); + assertEquals("RUNNABLE", ((EnumValue) result).getName()); + } + + @Test + void listProducesArrayValue() { + Value result = DefaultValueAstHelper.toAstValue(List.of("a", "b")); + assertInstanceOf(ArrayValue.class, result); + ArrayValue arr = (ArrayValue) result; + assertEquals(2, arr.getValues().size()); + assertEquals("a", ((StringValue) arr.getValues().get(0)).getValue()); + assertEquals("b", ((StringValue) arr.getValues().get(1)).getValue()); + } + + @Test + void setProducesArrayValue() { + LinkedHashSet set = new LinkedHashSet<>(Arrays.asList("x", "y")); + Value result = DefaultValueAstHelper.toAstValue(set); + assertInstanceOf(ArrayValue.class, result); + assertEquals(2, ((ArrayValue) result).getValues().size()); + } + + @Test + void javaArrayProducesArrayValue() { + Value result = DefaultValueAstHelper.toAstValue(new int[] { 1, 2, 3 }); + assertInstanceOf(ArrayValue.class, result); + ArrayValue arr = (ArrayValue) result; + assertEquals(3, arr.getValues().size()); + assertEquals(BigInteger.valueOf(1), ((IntValue) arr.getValues().get(0)).getValue()); + } + + @Test + void mapProducesObjectValue() { + Map map = new LinkedHashMap<>(); + map.put("id", new BigDecimal("1000")); + map.put("name", "Cape"); + Value result = DefaultValueAstHelper.toAstValue(map); + assertInstanceOf(ObjectValue.class, result); + ObjectValue obj = (ObjectValue) result; + assertEquals(2, obj.getObjectFields().size()); + assertEquals("id", obj.getObjectFields().get(0).getName()); + assertInstanceOf(IntValue.class, obj.getObjectFields().get(0).getValue()); + assertEquals("name", obj.getObjectFields().get(1).getName()); + assertInstanceOf(StringValue.class, obj.getObjectFields().get(1).getValue()); + } + + @Test + void nestedMapInList() { + Map inner = new LinkedHashMap<>(); + inner.put("key", "val"); + Value result = DefaultValueAstHelper.toAstValue(List.of(inner)); + assertInstanceOf(ArrayValue.class, result); + ArrayValue arr = (ArrayValue) result; + assertInstanceOf(ObjectValue.class, arr.getValues().get(0)); + } +} diff --git a/server/implementation/src/test/java/io/smallrye/graphql/execution/CompletableFutureTest.java b/server/implementation/src/test/java/io/smallrye/graphql/execution/CompletableFutureTest.java index 38110b3a3..d77f9fe0f 100644 --- a/server/implementation/src/test/java/io/smallrye/graphql/execution/CompletableFutureTest.java +++ b/server/implementation/src/test/java/io/smallrye/graphql/execution/CompletableFutureTest.java @@ -3,12 +3,12 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; -import jakarta.json.JsonObject; -import jakarta.json.JsonValue; - import org.jboss.jandex.IndexView; import org.junit.jupiter.api.Test; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.node.ObjectNode; + /** * Test a basic async endpoint * @@ -22,16 +22,16 @@ protected IndexView getIndex() { @Test public void testBasicQuery() { - JsonObject data = executeAndGetData(TEST_QUERY); + ObjectNode data = executeAndGetData(TEST_QUERY); - JsonValue jsonValue = data.get("book"); + JsonNode jsonValue = data.get("book"); assertNotNull(jsonValue); - JsonObject book = jsonValue.asJsonObject(); + ObjectNode book = (ObjectNode) jsonValue; assertNotNull(book); - assertFalse(book.isNull("title"), "title should not be null"); + assertFalse(book.has("title") && book.get("title").isNull(), "title should not be null"); } private static final String TEST_QUERY = "{\n" + diff --git a/server/implementation/src/test/java/io/smallrye/graphql/execution/ContextTest.java b/server/implementation/src/test/java/io/smallrye/graphql/execution/ContextTest.java index b50409cbd..2a56e076f 100644 --- a/server/implementation/src/test/java/io/smallrye/graphql/execution/ContextTest.java +++ b/server/implementation/src/test/java/io/smallrye/graphql/execution/ContextTest.java @@ -4,12 +4,11 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; -import jakarta.json.JsonObject; - import org.junit.jupiter.api.Test; import graphql.language.Document; import io.smallrye.graphql.execution.event.TestEventingService; +import tools.jackson.databind.node.ObjectNode; /** * Test the context @@ -20,25 +19,26 @@ public class ContextTest extends ExecutionTestBase { @Test public void testContext() { - JsonObject data = executeAndGetData(TEST_QUERY); + ObjectNode data = executeAndGetData(TEST_QUERY); - JsonObject testObject = data.getJsonObject("testContext"); + ObjectNode testObject = (ObjectNode) data.get("testContext"); assertNotNull(testObject); - assertFalse(testObject.isNull("executionId"), "executionId should not be null"); + assertFalse(testObject.has("executionId") && testObject.get("executionId").isNull(), + "executionId should not be null"); - assertFalse(testObject.isNull("path"), "path should not be null"); - assertEquals("/testContext", testObject.getString("path")); + assertFalse(testObject.has("path") && testObject.get("path").isNull(), "path should not be null"); + assertEquals("/testContext", testObject.get("path").asText()); - assertFalse(testObject.isNull("query"), "query should not be null"); + assertFalse(testObject.has("query") && testObject.get("query").isNull(), "query should not be null"); } @Test public void testUnwrapDocument() { TestEventingService.reset(); - JsonObject data = executeAndGetData(TEST_QUERY); + ObjectNode data = executeAndGetData(TEST_QUERY); Document document = TestEventingService.beforeExecuteContext.unwrap(Document.class); assertNotNull(document, "unwrapped document was null"); diff --git a/server/implementation/src/test/java/io/smallrye/graphql/execution/ExecutionGenericsTest.java b/server/implementation/src/test/java/io/smallrye/graphql/execution/ExecutionGenericsTest.java index 528e615f2..038b6c3d3 100644 --- a/server/implementation/src/test/java/io/smallrye/graphql/execution/ExecutionGenericsTest.java +++ b/server/implementation/src/test/java/io/smallrye/graphql/execution/ExecutionGenericsTest.java @@ -4,10 +4,10 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; -import jakarta.json.JsonObject; - import org.junit.jupiter.api.Test; +import tools.jackson.databind.node.ObjectNode; + /** * Test against generics * @@ -17,33 +17,33 @@ public class ExecutionGenericsTest extends ExecutionTestBase { @Test public void testQueryGeneric1() { - JsonObject data = executeAndGetData(TEST_QUERY_GENERIC_1); + ObjectNode data = executeAndGetData(TEST_QUERY_GENERIC_1); - JsonObject testObject = data.getJsonObject("generic1"); + ObjectNode testObject = (ObjectNode) data.get("generic1"); assertNotNull(testObject); - assertFalse(testObject.isNull("name"), "name should not be null"); - assertEquals("my name", testObject.getString("name")); + assertFalse(testObject.has("name") && testObject.get("name").isNull(), "name should not be null"); + assertEquals("my name", testObject.get("name").asText()); - assertFalse(testObject.isNull("param1"), "param1 should not be null"); - assertEquals("my param 1", testObject.getString("param1")); + assertFalse(testObject.has("param1") && testObject.get("param1").isNull(), "param1 should not be null"); + assertEquals("my param 1", testObject.get("param1").asText()); } @Test public void testQueryGeneric2() { - JsonObject data = executeAndGetData(TEST_QUERY_GENERIC_2); + ObjectNode data = executeAndGetData(TEST_QUERY_GENERIC_2); - JsonObject testObject = data.getJsonObject("generic2"); + ObjectNode testObject = (ObjectNode) data.get("generic2"); assertNotNull(testObject); - assertFalse(testObject.isNull("name"), "name should not be null"); - assertEquals("my name", testObject.getString("name")); + assertFalse(testObject.has("name") && testObject.get("name").isNull(), "name should not be null"); + assertEquals("my name", testObject.get("name").asText()); - assertFalse(testObject.isNull("param1"), "param1 should not be null"); - assertEquals(22, testObject.getInt("param1")); + assertFalse(testObject.has("param1") && testObject.get("param1").isNull(), "param1 should not be null"); + assertEquals(22, testObject.get("param1").asInt()); } diff --git a/server/implementation/src/test/java/io/smallrye/graphql/execution/ExecutionTest.java b/server/implementation/src/test/java/io/smallrye/graphql/execution/ExecutionTest.java index f8406f7ab..aef173379 100644 --- a/server/implementation/src/test/java/io/smallrye/graphql/execution/ExecutionTest.java +++ b/server/implementation/src/test/java/io/smallrye/graphql/execution/ExecutionTest.java @@ -2,12 +2,12 @@ import static org.junit.jupiter.api.Assertions.*; -import jakarta.json.*; - import org.assertj.core.api.AutoCloseableSoftAssertions; import org.junit.jupiter.api.Test; import io.smallrye.graphql.test.TestSourceConfiguration; +import tools.jackson.databind.node.ArrayNode; +import tools.jackson.databind.node.ObjectNode; /** * Test a basic query @@ -18,57 +18,59 @@ public class ExecutionTest extends ExecutionTestBase { @Test public void testBasicQuery() { - JsonObject data = executeAndGetData(TEST_QUERY); + ObjectNode data = executeAndGetData(TEST_QUERY); - JsonObject testObject = data.getJsonObject("testObject"); + ObjectNode testObject = (ObjectNode) data.get("testObject"); assertNotNull(testObject); - assertFalse(testObject.isNull("name"), "name should not be null"); - assertEquals("Phillip", testObject.getString("name")); + assertFalse(testObject.has("name") && testObject.get("name").isNull(), "name should not be null"); + assertEquals("Phillip", testObject.get("name").asText()); - assertFalse(testObject.isNull("id"), "id should not be null"); + assertFalse(testObject.has("id") && testObject.get("id").isNull(), "id should not be null"); // Testing source - assertFalse(testObject.isNull("timestamp"), "timestamp should not be null"); - assertFalse(testObject.get("timestamp").asJsonObject().isNull("value"), "timestamp value should not be null"); + assertFalse(testObject.has("timestamp") && testObject.get("timestamp").isNull(), "timestamp should not be null"); + assertFalse(((ObjectNode) testObject.get("timestamp")).has("value") + && ((ObjectNode) testObject.get("timestamp")).get("value").isNull(), "timestamp value should not be null"); } @Test public void testBasicListQuery() { - JsonObject data = executeAndGetData(TEST_LIST_QUERY); + ObjectNode data = executeAndGetData(TEST_LIST_QUERY); - JsonArray testObjects = data.getJsonArray("testObjects"); + ArrayNode testObjects = (ArrayNode) data.get("testObjects"); assertNotNull(testObjects); assertEquals(2, testObjects.size()); - JsonObject testObject = testObjects.getJsonObject(0); + ObjectNode testObject = (ObjectNode) testObjects.get(0); assertNotNull(testObject); - assertFalse(testObject.isNull("name"), "name should not be null"); - assertEquals("Phillip", testObject.getString("name")); + assertFalse(testObject.has("name") && testObject.get("name").isNull(), "name should not be null"); + assertEquals("Phillip", testObject.get("name").asText()); - assertFalse(testObject.isNull("id"), "id should not be null"); + assertFalse(testObject.has("id") && testObject.get("id").isNull(), "id should not be null"); // Testing batch - assertFalse(testObject.isNull("timestamp"), "timestamp should not be null"); - assertFalse(testObject.get("timestamp").asJsonObject().isNull("value"), "timestamp value should not be null"); + assertFalse(testObject.has("timestamp") && testObject.get("timestamp").isNull(), "timestamp should not be null"); + assertFalse(((ObjectNode) testObject.get("timestamp")).has("value") + && ((ObjectNode) testObject.get("timestamp")).get("value").isNull(), "timestamp value should not be null"); } @Test public void testBatchSourceConfigurationQuery() { - JsonObject data = executeAndGetData(TEST_BATCH_SOURCE_CONFIGURATION_QUERY); + ObjectNode data = executeAndGetData(TEST_BATCH_SOURCE_CONFIGURATION_QUERY); - Boolean active1 = data.getJsonArray("objectsWithConfig1").get(0).asJsonObject().getJsonObject("configuredSources") - .getJsonObject("configuration").getBoolean("active"); - Boolean active2 = data.getJsonArray("objectsWithConfig2").get(0).asJsonObject().getJsonObject("configuredSources") - .getJsonObject("configuration").getBoolean("active"); + Boolean active1 = ((ObjectNode) ((ObjectNode) ((ObjectNode) ((ArrayNode) data.get("objectsWithConfig1")).get(0)) + .get("configuredSources")).get("configuration")).get("active").asBoolean(); + Boolean active2 = ((ObjectNode) ((ObjectNode) ((ObjectNode) ((ArrayNode) data.get("objectsWithConfig2")).get(0)) + .get("configuredSources")).get("configuration")).get("active").asBoolean(); - var state1 = TestSourceConfiguration.TestSourceState.valueOf(data.getJsonArray("objectsWithConfig1").get(0) - .asJsonObject().getJsonObject("configuredSources").getJsonObject("configuration").getString("state")); - var state2 = TestSourceConfiguration.TestSourceState.valueOf(data.getJsonArray("objectsWithConfig2").get(0) - .asJsonObject().getJsonObject("configuredSources").getJsonObject("configuration").getString("state")); + var state1 = TestSourceConfiguration.TestSourceState.valueOf(((ObjectNode) ((ObjectNode) ((ObjectNode) ((ArrayNode) data + .get("objectsWithConfig1")).get(0)).get("configuredSources")).get("configuration")).get("state").asText()); + var state2 = TestSourceConfiguration.TestSourceState.valueOf(((ObjectNode) ((ObjectNode) ((ObjectNode) ((ArrayNode) data + .get("objectsWithConfig2")).get(0)).get("configuredSources")).get("configuration")).get("state").asText()); try (AutoCloseableSoftAssertions softly = new AutoCloseableSoftAssertions()) { softly.assertThat(active1).isNotEqualTo(active2); diff --git a/server/implementation/src/test/java/io/smallrye/graphql/execution/ExecutionTestBase.java b/server/implementation/src/test/java/io/smallrye/graphql/execution/ExecutionTestBase.java index 76bf2dbf9..f35abf605 100644 --- a/server/implementation/src/test/java/io/smallrye/graphql/execution/ExecutionTestBase.java +++ b/server/implementation/src/test/java/io/smallrye/graphql/execution/ExecutionTestBase.java @@ -2,10 +2,6 @@ import java.util.Map; -import jakarta.json.JsonArray; -import jakarta.json.JsonObject; -import jakarta.json.JsonValue; - import org.jboss.jandex.IndexView; import org.jboss.logging.Logger; import org.junit.jupiter.api.BeforeEach; @@ -14,6 +10,10 @@ import io.smallrye.graphql.bootstrap.Bootstrap; import io.smallrye.graphql.schema.SchemaBuilder; import io.smallrye.graphql.schema.model.Schema; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.node.ArrayNode; +import tools.jackson.databind.node.JsonNodeFactory; +import tools.jackson.databind.node.ObjectNode; /** * Base class for execution tests @@ -43,46 +43,46 @@ protected IndexView getIndex() { return Indexer.getAllTestIndex(); } - protected JsonObject executeAndGetData(String graphQL) { + protected ObjectNode executeAndGetData(String graphQL) { return executeAndGetData(graphQL, null); } - protected JsonObject executeAndGetData(String graphQL, Map context) { - JsonObject result = executeAndGetResult(graphQL, context); - JsonValue value = result.get(DATA); + protected ObjectNode executeAndGetData(String graphQL, Map context) { + ObjectNode result = executeAndGetResult(graphQL, context); + JsonNode value = result.get(DATA); if (value != null) { - return result.getJsonObject(DATA); + return (ObjectNode) result.get(DATA); } - return JsonObject.EMPTY_JSON_OBJECT; + return JsonNodeFactory.instance.objectNode(); } - protected JsonArray executeAndGetErrors(String graphQL) { + protected ArrayNode executeAndGetErrors(String graphQL) { return executeAndGetErrors(graphQL, null); } - protected JsonArray executeAndGetErrors(String graphQL, Map context) { - JsonObject result = executeAndGetResult(graphQL, context); - JsonValue value = result.get(ERRORS); + protected ArrayNode executeAndGetErrors(String graphQL, Map context) { + ObjectNode result = executeAndGetResult(graphQL, context); + JsonNode value = result.get(ERRORS); if (value != null) { - return result.getJsonArray(ERRORS); + return (ArrayNode) result.get(ERRORS); } return null; } - protected JsonObject executeAndGetExtensions(String graphQL) { + protected ObjectNode executeAndGetExtensions(String graphQL) { return executeAndGetExtensions(graphQL, null); } - protected JsonObject executeAndGetExtensions(String graphQL, Map context) { - JsonObject result = executeAndGetResult(graphQL, context); - JsonValue value = result.get(EXTENSIONS); + protected ObjectNode executeAndGetExtensions(String graphQL, Map context) { + ObjectNode result = executeAndGetResult(graphQL, context); + JsonNode value = result.get(EXTENSIONS); if (value != null) { - return result.getJsonObject(EXTENSIONS); + return (ObjectNode) result.get(EXTENSIONS); } return null; } - protected JsonObject executeAndGetResult(String graphQL, Map context) { + protected ObjectNode executeAndGetResult(String graphQL, Map context) { JsonObjectResponseWriter jsonObjectResponseWriter = new JsonObjectResponseWriter(graphQL); jsonObjectResponseWriter.logInput(); if (context == null) { diff --git a/server/implementation/src/test/java/io/smallrye/graphql/execution/ExecutionUnionsTest.java b/server/implementation/src/test/java/io/smallrye/graphql/execution/ExecutionUnionsTest.java index f36a24a30..f16448d3c 100644 --- a/server/implementation/src/test/java/io/smallrye/graphql/execution/ExecutionUnionsTest.java +++ b/server/implementation/src/test/java/io/smallrye/graphql/execution/ExecutionUnionsTest.java @@ -2,10 +2,10 @@ import static org.junit.jupiter.api.Assertions.*; -import jakarta.json.JsonObject; - import org.junit.jupiter.api.Test; +import tools.jackson.databind.node.ObjectNode; + /** * Test against unions * @@ -15,46 +15,46 @@ public class ExecutionUnionsTest extends ExecutionTestBase { @Test public void testBasicUnion() { - JsonObject data = executeAndGetData(TEST_BASIC_UNION); - JsonObject testObject = data.getJsonObject("basicUnion"); + ObjectNode data = executeAndGetData(TEST_BASIC_UNION); + ObjectNode testObject = (ObjectNode) data.get("basicUnion"); assertNotNull(testObject); - assertFalse(testObject.isNull("name"), "name should not be null"); - assertEquals("my name", testObject.getString("name")); + assertFalse(testObject.has("name") && testObject.get("name").isNull(), "name should not be null"); + assertEquals("my name", testObject.get("name").asText()); } @Test public void testUnionOfInterfacesReturningDirectImplementer() { - JsonObject data = executeAndGetData(TEST_NESTED_INTERFACE_DIRECT_IMPL); - JsonObject testObject = data.getJsonObject("unionOfInterfacesDirectImplementor"); + ObjectNode data = executeAndGetData(TEST_NESTED_INTERFACE_DIRECT_IMPL); + ObjectNode testObject = (ObjectNode) data.get("unionOfInterfacesDirectImplementor"); assertNotNull(testObject); - assertFalse(testObject.isNull("message"), "message should not be null"); - assertEquals("im in many unions", testObject.getString("message")); + assertFalse(testObject.has("message") && testObject.get("message").isNull(), "message should not be null"); + assertEquals("im in many unions", testObject.get("message").asText()); } @Test public void testUnionOfInterfacesReturningNestedInterfaceImpl1() { - JsonObject data = executeAndGetData(TEST_NESTED_INTERFACE_1); - JsonObject testObject = data.getJsonObject("unionOfInterfacesNestedInterface1"); + ObjectNode data = executeAndGetData(TEST_NESTED_INTERFACE_1); + ObjectNode testObject = (ObjectNode) data.get("unionOfInterfacesNestedInterface1"); assertNotNull(testObject); - assertFalse(testObject.isNull("name"), "name should not be null"); - assertEquals("my name", testObject.getString("name")); + assertFalse(testObject.has("name") && testObject.get("name").isNull(), "name should not be null"); + assertEquals("my name", testObject.get("name").asText()); } @Test public void testUnionOfInterfacesReturningNestedInterfaceImpl2() { - JsonObject data = executeAndGetData(TEST_NESTED_INTERFACE_2); - JsonObject testObject = data.getJsonObject("unionOfInterfacesNestedInterface2"); + ObjectNode data = executeAndGetData(TEST_NESTED_INTERFACE_2); + ObjectNode testObject = (ObjectNode) data.get("unionOfInterfacesNestedInterface2"); assertNotNull(testObject); - assertFalse(testObject.isNull("color"), "color should not be null"); - assertEquals("purple", testObject.getString("color")); + assertFalse(testObject.has("color") && testObject.get("color").isNull(), "color should not be null"); + assertEquals("purple", testObject.get("color").asText()); } private static final String TEST_BASIC_UNION = "{\n" + diff --git a/server/implementation/src/test/java/io/smallrye/graphql/execution/ExperimentalNamespaceTest.java b/server/implementation/src/test/java/io/smallrye/graphql/execution/ExperimentalNamespaceTest.java index 4c75555db..64cedb8e6 100644 --- a/server/implementation/src/test/java/io/smallrye/graphql/execution/ExperimentalNamespaceTest.java +++ b/server/implementation/src/test/java/io/smallrye/graphql/execution/ExperimentalNamespaceTest.java @@ -7,8 +7,6 @@ import java.io.InputStream; import java.util.stream.Stream; -import jakarta.json.JsonObject; - import org.jboss.jandex.IndexView; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -19,6 +17,7 @@ import io.smallrye.graphql.schema.model.Schema; import io.smallrye.graphql.test.namespace.ExperimentalNamespaceApi; import io.smallrye.graphql.test.namespace.ExperimentalNamespaceWithErrorApi; +import tools.jackson.databind.node.ObjectNode; /** * Test for Federated namespaces @@ -53,7 +52,7 @@ private static GraphQLSchema createGraphQLSchema(IndexView index) { return graphQLSchema; } - private static JsonObject executeAndGetResult(String graphQL) { + private static ObjectNode executeAndGetResult(String graphQL) { JsonObjectResponseWriter jsonObjectResponseWriter = new JsonObjectResponseWriter(graphQL); jsonObjectResponseWriter.logInput(); executionService.executeSync(jsonObjectResponseWriter.getInput(), jsonObjectResponseWriter); @@ -69,13 +68,13 @@ public void experimentalNamespaceTest() { Schema schema = SchemaBuilder.build(index); executionService = new ExecutionService(graphQLSchema, schema); - JsonObject jsonObject = executeAndGetResult(NAMESPACED_QUERY); + ObjectNode jsonObject = executeAndGetResult(NAMESPACED_QUERY); assertNotNull(jsonObject); - String result = jsonObject.getJsonObject("data") - .getJsonObject("admin") - .getJsonObject("users") - .getString("find"); + String result = ((ObjectNode) ((ObjectNode) ((ObjectNode) jsonObject.get("data")) + .get("admin")) + .get("users")) + .get("find").asText(); assertEquals(result, "AdminUsersFind"); } diff --git a/server/implementation/src/test/java/io/smallrye/graphql/execution/FederatedNamespaceTest.java b/server/implementation/src/test/java/io/smallrye/graphql/execution/FederatedNamespaceTest.java index 7d6d8e1ec..50ff95ed4 100644 --- a/server/implementation/src/test/java/io/smallrye/graphql/execution/FederatedNamespaceTest.java +++ b/server/implementation/src/test/java/io/smallrye/graphql/execution/FederatedNamespaceTest.java @@ -8,10 +8,6 @@ import java.util.function.BiFunction; import java.util.stream.Stream; -import jakarta.json.JsonObject; -import jakarta.json.JsonString; -import jakarta.json.JsonValue; - import org.jboss.jandex.IndexView; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -33,6 +29,9 @@ import io.smallrye.graphql.test.namespace.SourceNamespaceTestApi; import io.smallrye.graphql.test.namespace.UnamedModel; import io.smallrye.graphql.test.namespace.UnnamedTestApi; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.node.ArrayNode; +import tools.jackson.databind.node.ObjectNode; /** * Test for Federated namespaces @@ -92,7 +91,7 @@ private static GraphQLSchema createGraphQLSchema(IndexView index) { return graphQLSchema; } - private static JsonObject executeAndGetResult(String graphQL) { + private static ObjectNode executeAndGetResult(String graphQL) { JsonObjectResponseWriter jsonObjectResponseWriter = new JsonObjectResponseWriter(graphQL); jsonObjectResponseWriter.logInput(); executionService.executeSync(jsonObjectResponseWriter.getInput(), jsonObjectResponseWriter); @@ -101,14 +100,14 @@ private static JsonObject executeAndGetResult(String graphQL) { } private void test(String type, String id) { - JsonObject jsonObject = executeAndGetResult(TEST_QUERY.apply(type, id)); + ObjectNode jsonObject = executeAndGetResult(TEST_QUERY.apply(type, id)); assertNotNull(jsonObject); - JsonValue jsonValue = jsonObject.getJsonObject("data") - .getJsonArray("_entities") - .getJsonObject(0) + JsonNode jsonValue = ((ObjectNode) ((ArrayNode) ((ObjectNode) jsonObject.get("data")) + .get("_entities")) + .get(0)) .get("value"); - String value = ((JsonString) jsonValue).getString(); + String value = jsonValue.asText(); assertEquals(value, id); } @@ -131,23 +130,23 @@ public void findEntityWithSourceNamespace() { public void findEntityWithWithGroupedKeyAndNamespace() { String id = "grouped_key"; - JsonObject jsonObject = executeAndGetResult(GROUPED_KEY_QUERY.apply( + ObjectNode jsonObject = executeAndGetResult(GROUPED_KEY_QUERY.apply( NamedNamespaceWIthGroupingKeyModel.class.getSimpleName(), id)); assertNotNull(jsonObject); - JsonValue jsonValue = jsonObject.getJsonObject("data") - .getJsonArray("_entities") - .getJsonObject(0) + JsonNode jsonValue = ((ObjectNode) ((ArrayNode) ((ObjectNode) jsonObject.get("data")) + .get("_entities")) + .get(0)) .get("value"); - String value = ((JsonString) jsonValue).getString(); + String value = jsonValue.asText(); assertEquals(value, id); - jsonValue = jsonObject.getJsonObject("data") - .getJsonArray("_entities") - .getJsonObject(0) + jsonValue = ((ObjectNode) ((ArrayNode) ((ObjectNode) jsonObject.get("data")) + .get("_entities")) + .get(0)) .get("anotherId"); - String anotherId = ((JsonString) jsonValue).getString(); + String anotherId = jsonValue.asText(); assertEquals(anotherId, "otherKey_" + id); } diff --git a/server/implementation/src/test/java/io/smallrye/graphql/execution/FederatedTracingTest.java b/server/implementation/src/test/java/io/smallrye/graphql/execution/FederatedTracingTest.java index ccd2aaf1a..3c8813edb 100644 --- a/server/implementation/src/test/java/io/smallrye/graphql/execution/FederatedTracingTest.java +++ b/server/implementation/src/test/java/io/smallrye/graphql/execution/FederatedTracingTest.java @@ -11,13 +11,12 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import jakarta.json.JsonObject; -import jakarta.json.JsonString; - import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import io.smallrye.graphql.spi.config.Config; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.node.ObjectNode; /** * Test for Federated tracing @@ -39,7 +38,7 @@ public void testFederatedTracingEnabledWithFederationDisabled() { Map metaData = createMetaData(ftKey); - JsonObject extensions = executeAndGetExtensions(TEST_QUERY, metaData); + ObjectNode extensions = executeAndGetExtensions(TEST_QUERY, metaData); assertNull(extensions); } @@ -50,13 +49,13 @@ public void testFederatedTracingEnabledWithFederationEnabled() { Map metaData = createMetaData(ftKey); - JsonObject extensions = executeAndGetExtensions(TEST_QUERY, metaData); + ObjectNode extensions = executeAndGetExtensions(TEST_QUERY, metaData); assertNotNull(extensions); - assertTrue(extensions.containsKey(ftKey)); + assertTrue(extensions.has(ftKey)); - JsonString ftValue = extensions.getJsonString(ftKey); + JsonNode ftValue = extensions.get(ftKey); assertNotNull(ftValue); - assertFalse(ftValue.getString().isEmpty()); + assertFalse(ftValue.asText().isEmpty()); } @Test @@ -66,7 +65,7 @@ public void testFederatedTracingDisabledWithFederationEnabled() { Map metaData = createMetaData(ftKey); - JsonObject extensions = executeAndGetExtensions(TEST_QUERY, metaData); + ObjectNode extensions = executeAndGetExtensions(TEST_QUERY, metaData); assertNull(extensions); } diff --git a/server/implementation/src/test/java/io/smallrye/graphql/execution/GroupingTest.java b/server/implementation/src/test/java/io/smallrye/graphql/execution/GroupingTest.java index c1d843338..48f95161e 100644 --- a/server/implementation/src/test/java/io/smallrye/graphql/execution/GroupingTest.java +++ b/server/implementation/src/test/java/io/smallrye/graphql/execution/GroupingTest.java @@ -3,11 +3,11 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; -import jakarta.json.JsonObject; - import org.jboss.jandex.IndexView; import org.junit.jupiter.api.Test; +import tools.jackson.databind.node.ObjectNode; + /** * Test a basic grouping * @@ -21,13 +21,13 @@ protected IndexView getIndex() { @Test public void testBasicQuery() { - JsonObject data = executeAndGetData(TEST_QUERY); + ObjectNode data = executeAndGetData(TEST_QUERY); - JsonObject books = data.getJsonObject("books"); + ObjectNode books = (ObjectNode) data.get("books"); assertNotNull(books); - assertFalse(books.isNull("book"), "book should not be null"); + assertFalse(books.has("book") && books.get("book").isNull(), "book should not be null"); } private static final String TEST_QUERY = "{\n" + diff --git a/server/implementation/src/test/java/io/smallrye/graphql/execution/JsonbCreatorTest.java b/server/implementation/src/test/java/io/smallrye/graphql/execution/JsonbCreatorTest.java index 151cca4af..ddb8871e8 100644 --- a/server/implementation/src/test/java/io/smallrye/graphql/execution/JsonbCreatorTest.java +++ b/server/implementation/src/test/java/io/smallrye/graphql/execution/JsonbCreatorTest.java @@ -2,13 +2,11 @@ import static org.junit.jupiter.api.Assertions.*; -import jakarta.json.JsonObject; -import jakarta.json.JsonValue; - import org.jboss.jandex.IndexView; import org.junit.jupiter.api.Test; import io.smallrye.graphql.test.jsonbCreator.CreatorApi; +import tools.jackson.databind.node.ObjectNode; public class JsonbCreatorTest extends ExecutionTestBase { @@ -18,97 +16,97 @@ protected IndexView getIndex() { @Test public void testWithValue() { - JsonObject data = executeAndGetData("{\n" + ObjectNode data = executeAndGetData("{\n" + " withJsonbCreator(input: {field: \"A\"}) {\n" + " field\n" + " }\n" + "}"); - JsonObject testObject = data.getJsonObject("withJsonbCreator"); + ObjectNode testObject = (ObjectNode) data.get("withJsonbCreator"); assertNotNull(testObject); - String field = testObject.getJsonString("field").getString(); + String field = testObject.get("field").asText(); assertEquals("A", field); } @Test public void testWithStaticFactory() { - JsonObject data = executeAndGetData("{\n" + ObjectNode data = executeAndGetData("{\n" + " withStaticFactory(input: {field: \"A\"}) {\n" + " field\n" + " }\n" + "}"); - JsonObject testObject = data.getJsonObject("withStaticFactory"); + ObjectNode testObject = (ObjectNode) data.get("withStaticFactory"); assertNotNull(testObject); - String field = testObject.getJsonString("field").getString(); + String field = testObject.get("field").asText(); assertEquals("A", field); } @Test public void testWithMissingValue() { - JsonObject data = executeAndGetData("{\n" + ObjectNode data = executeAndGetData("{\n" + " withJsonbCreator(input: {}) {\n" + " field\n" + " }\n" + "}"); - JsonObject testObject = data.getJsonObject("withJsonbCreator"); + ObjectNode testObject = (ObjectNode) data.get("withJsonbCreator"); assertNotNull(testObject); - assertEquals(JsonValue.ValueType.NULL, testObject.get("field").getValueType()); + assertTrue(testObject.get("field").isNull()); } @Test public void testWithTransformation() { - JsonObject data = executeAndGetData("{\n" + ObjectNode data = executeAndGetData("{\n" + " creatorWithTransformation(input: {field: \"1\"}) {\n" + " field\n" + " }\n" + "}"); - JsonObject testObject = data.getJsonObject("creatorWithTransformation"); + ObjectNode testObject = (ObjectNode) data.get("creatorWithTransformation"); assertNotNull(testObject); - String field = testObject.getJsonString("field").getString(); + String field = testObject.get("field").asText(); assertEquals("1", field); } @Test public void testWithDefault() { - JsonObject data = executeAndGetData("{\n" + ObjectNode data = executeAndGetData("{\n" + " creatorWithFieldDefault(input: {}) {\n" + " field\n" + " }\n" + "}"); - JsonObject testObject = data.getJsonObject("creatorWithFieldDefault"); + ObjectNode testObject = (ObjectNode) data.get("creatorWithFieldDefault"); assertNotNull(testObject); - String field = testObject.getJsonString("field").getString(); + String field = testObject.get("field").asText(); assertEquals("Some value", field); } @Test public void testWithParameterDefault() { - JsonObject data = executeAndGetData("{\n" + ObjectNode data = executeAndGetData("{\n" + " creatorWithParameterDefault(input: {}) {\n" + " field\n" + " }\n" + "}"); - JsonObject testObject = data.getJsonObject("creatorWithParameterDefault"); + ObjectNode testObject = (ObjectNode) data.get("creatorWithParameterDefault"); assertNotNull(testObject); - String field = testObject.getJsonString("field").getString(); + String field = testObject.get("field").asText(); assertEquals("Some value", field); } @Test public void testWithMultipleParameters() { - JsonObject data = executeAndGetData("{\n" + ObjectNode data = executeAndGetData("{\n" + " creatorWithMultipleParameters(input: {string: \"Foobar\", localDate:\"2021-01-01\"}) {\n" + " string\n" + " integer\n" @@ -116,16 +114,15 @@ public void testWithMultipleParameters() { + " }\n" + "}"); - JsonObject testObject = data.getJsonObject("creatorWithMultipleParameters"); + ObjectNode testObject = (ObjectNode) data.get("creatorWithMultipleParameters"); assertNotNull(testObject); - String string = testObject.getJsonString("string").getString(); + String string = testObject.get("string").asText(); assertEquals("Foobar", string); - JsonValue integer = testObject.get("integer"); - assertEquals(JsonValue.ValueType.NULL, integer.getValueType()); + assertTrue(testObject.has("integer") && testObject.get("integer").isNull()); - String localDate = testObject.getJsonString("localDate").getString(); + String localDate = testObject.get("localDate").asText(); assertEquals("2021-01-01", localDate); } } diff --git a/server/implementation/src/test/java/io/smallrye/graphql/execution/MappingTest.java b/server/implementation/src/test/java/io/smallrye/graphql/execution/MappingTest.java index 532c273ba..63ba9ae3d 100644 --- a/server/implementation/src/test/java/io/smallrye/graphql/execution/MappingTest.java +++ b/server/implementation/src/test/java/io/smallrye/graphql/execution/MappingTest.java @@ -9,44 +9,42 @@ import java.util.List; import java.util.Map; -import jakarta.json.Json; -import jakarta.json.JsonObject; - import org.junit.jupiter.api.Test; import io.smallrye.graphql.api.Context; +import tools.jackson.databind.node.JsonNodeFactory; +import tools.jackson.databind.node.ObjectNode; public class MappingTest { + private static final JsonNodeFactory NF = JsonNodeFactory.instance; + @Test public void toMap_string() { Map expected = Collections.singletonMap("firstName", "John"); - JsonObject jo = Json.createObjectBuilder() - .add("firstName", "John") - .build(); - assertEquals(expected, Context.VariablesParser.toMap(toInput(jo).getJsonObject("variables")).get()); + ObjectNode jo = NF.objectNode().put("firstName", "John"); + assertEquals(expected, Context.VariablesParser.toMap(toInput(jo).get("variables")).get()); } @Test public void toMap_boolean() { Map expected = Collections.singletonMap("certified", true); - JsonObject jo = Json.createObjectBuilder().add("certified", true).build(); - assertEquals(expected, Context.VariablesParser.toMap(toInput(jo).getJsonObject("variables")).get()); + ObjectNode jo = NF.objectNode().put("certified", true); + assertEquals(expected, Context.VariablesParser.toMap(toInput(jo).get("variables")).get()); expected = Collections.singletonMap("refurbished", false); - jo = Json.createObjectBuilder().add("refurbished", false).build(); - assertEquals(expected, Context.VariablesParser.toMap(toInput(jo).getJsonObject("variables")).get()); + jo = NF.objectNode().put("refurbished", false); + assertEquals(expected, Context.VariablesParser.toMap(toInput(jo).get("variables")).get()); } @Test public void toMap_numbers() { - JsonObject jo = Json.createObjectBuilder() - .add("weight", 17.003) - .add("block_count", 1025) - .add("bigNum", 1234567890987654321L) - .add("float", 0.00000023f) - .build(); - Map returned = Context.VariablesParser.toMap(toInput(jo).getJsonObject("variables")).get(); + ObjectNode jo = NF.objectNode() + .put("weight", 17.003) + .put("block_count", 1025) + .put("bigNum", 1234567890987654321L) + .put("float", 0.00000023f); + Map returned = Context.VariablesParser.toMap(toInput(jo).get("variables")).get(); assertEquals(4, returned.size()); assertEquals(17.003, ((BigDecimal) returned.get("weight")).doubleValue(), 0.001); assertEquals(1025, ((BigDecimal) returned.get("block_count")).intValue()); @@ -69,55 +67,35 @@ public void toMap_objects() { childMap2.put("state", "AL"); childMap2.put("zip", "99992"); - JsonObject jo = Json.createObjectBuilder() - .add("customer", Json.createObjectBuilder() - .add("name", "Joe Busy") - .add("address", Json.createObjectBuilder() - .add("street_num", "1003") - .add("street_name", "Elm Boulevard") - .add("city", "Nowhere") - .add("state", "AL") - .add("zip", "99992") - .build()) - .add("acct", 12345) - .build()) - .build(); - assertEquals(expected, Context.VariablesParser.toMap(toInput(jo).getJsonObject("variables")).get()); + ObjectNode address = NF.objectNode() + .put("street_num", "1003") + .put("street_name", "Elm Boulevard") + .put("city", "Nowhere") + .put("state", "AL") + .put("zip", "99992"); + ObjectNode customer = NF.objectNode() + .put("name", "Joe Busy"); + customer.set("address", address); + customer.put("acct", 12345); + ObjectNode jo = NF.objectNode(); + jo.set("customer", customer); + + assertEquals(expected, Context.VariablesParser.toMap(toInput(jo).get("variables")).get()); } @SuppressWarnings("unchecked") @Test public void toMap_array() { - JsonObject jo = Json.createObjectBuilder() - .add("names", Json.createArrayBuilder() - .add("bob") - .add("tom") - .add("dick") - .build()) - .add("games", Json.createArrayBuilder() - .add("basketball") - .add("hockey") - .add("rugby") - .add("baseball") - .build()) - .add("numbers", Json.createArrayBuilder() - .add(3.14) - .add(17) - .add(20.003f) - .add(98765432123456789L) - .build()) - .add("mixed", Json.createArrayBuilder() - .add(65535) - .add("fred") - .add(true) - .add(Json.createObjectBuilder() - .add("name", "Widget") - .add("length", 29) - .build()) - .build()) - .add("empty", Json.createArrayBuilder().build()) - .build(); - Map returned = Context.VariablesParser.toMap(toInput(jo).getJsonObject("variables")).get(); + ObjectNode jo = NF.objectNode(); + jo.set("names", NF.arrayNode().add("bob").add("tom").add("dick")); + jo.set("games", NF.arrayNode().add("basketball").add("hockey").add("rugby").add("baseball")); + jo.set("numbers", NF.arrayNode().add(3.14).add(17).add(20.003f).add(98765432123456789L)); + + ObjectNode widget = NF.objectNode().put("name", "Widget").put("length", 29); + jo.set("mixed", NF.arrayNode().add(65535).add("fred").add(true).add(widget)); + jo.set("empty", NF.arrayNode()); + + Map returned = Context.VariablesParser.toMap(toInput(jo).get("variables")).get(); assertEquals(5, returned.size()); assertEquals(Arrays.asList("bob", "tom", "dick"), returned.get("names")); assertEquals(Arrays.asList("basketball", "hockey", "rugby", "baseball"), returned.get("games")); @@ -142,7 +120,9 @@ public void toMap_array() { assertEquals(Collections.emptyList(), returned.get("empty")); } - private JsonObject toInput(JsonObject jo) { - return Json.createObjectBuilder().add("variables", jo).build(); + private ObjectNode toInput(ObjectNode jo) { + ObjectNode input = NF.objectNode(); + input.set("variables", jo); + return input; } } diff --git a/server/implementation/src/test/java/io/smallrye/graphql/execution/MutinyTest.java b/server/implementation/src/test/java/io/smallrye/graphql/execution/MutinyTest.java index 0e92f107f..b74fa7b2b 100644 --- a/server/implementation/src/test/java/io/smallrye/graphql/execution/MutinyTest.java +++ b/server/implementation/src/test/java/io/smallrye/graphql/execution/MutinyTest.java @@ -4,14 +4,13 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; -import jakarta.json.JsonArray; -import jakarta.json.JsonObject; -import jakarta.json.JsonValue; - import org.jboss.jandex.IndexView; import org.junit.jupiter.api.Test; import io.smallrye.graphql.test.mutiny.CustomException; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.node.ArrayNode; +import tools.jackson.databind.node.ObjectNode; public class MutinyTest extends ExecutionTestBase { @@ -21,29 +20,30 @@ protected IndexView getIndex() { @Test public void testBasicQuery() { - JsonObject data = executeAndGetData(TEST_QUERY); + ObjectNode data = executeAndGetData(TEST_QUERY); - JsonValue jsonValue = data.get("book"); + JsonNode jsonValue = data.get("book"); assertNotNull(jsonValue); - JsonObject book = jsonValue.asJsonObject(); + ObjectNode book = (ObjectNode) jsonValue; assertNotNull(book); - assertFalse(book.isNull("title"), "title should not be null"); + assertFalse(book.has("title") && book.get("title").isNull(), "title should not be null"); } @Test public void testFailureQuery() { - JsonArray errors = executeAndGetErrors(FAILURE_TEST_QUERY); + ArrayNode errors = executeAndGetErrors(FAILURE_TEST_QUERY); assertNotNull(errors); assertEquals(errors.size(), 1); - var extensions = errors.get(0).asJsonObject().getJsonObject("extensions"); - assertEquals("custom-error", extensions.getString("code"), "error code"); - assertEquals(CustomException.class.getName(), extensions.getString("exception"), "exception"); - assertEquals("DataFetchingException", extensions.getString("classification"), "classification"); - assertEquals(CustomException.class.getSimpleName().length(), extensions.getInt("test-extension"), "test extension"); + ObjectNode extensions = (ObjectNode) ((ObjectNode) errors.get(0)).get("extensions"); + assertEquals("custom-error", extensions.get("code").asText(), "error code"); + assertEquals(CustomException.class.getName(), extensions.get("exception").asText(), "exception"); + assertEquals("DataFetchingException", extensions.get("classification").asText(), "classification"); + assertEquals(CustomException.class.getSimpleName().length(), extensions.get("test-extension").asInt(), + "test extension"); } private static final String TEST_QUERY = "{\n" + diff --git a/server/implementation/src/test/java/io/smallrye/graphql/execution/ResolverTest.java b/server/implementation/src/test/java/io/smallrye/graphql/execution/ResolverTest.java index 278f0585f..04e4f61a5 100644 --- a/server/implementation/src/test/java/io/smallrye/graphql/execution/ResolverTest.java +++ b/server/implementation/src/test/java/io/smallrye/graphql/execution/ResolverTest.java @@ -8,10 +8,6 @@ import java.io.InputStream; import java.util.stream.Stream; -import jakarta.json.JsonObject; -import jakarta.json.JsonString; -import jakarta.json.JsonValue; - import org.jboss.jandex.IndexView; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -29,6 +25,9 @@ import io.smallrye.graphql.spi.config.Config; import io.smallrye.graphql.test.resolver.ExtendedApi; import io.smallrye.graphql.test.resolver.ExtendedType; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.node.ArrayNode; +import tools.jackson.databind.node.ObjectNode; /** * Test for Federated namespaces @@ -84,7 +83,7 @@ private static GraphQLSchema createGraphQLSchema(IndexView index) { return graphQLSchema; } - private static JsonObject executeAndGetResult(String graphQL) { + private static ObjectNode executeAndGetResult(String graphQL) { JsonObjectResponseWriter jsonObjectResponseWriter = new JsonObjectResponseWriter(graphQL); jsonObjectResponseWriter.logInput(); executionService.executeSync(jsonObjectResponseWriter.getInput(), jsonObjectResponseWriter); @@ -94,38 +93,38 @@ private static JsonObject executeAndGetResult(String graphQL) { @Test public void findByIdTest() { - JsonObject jsonObject = executeAndGetResult(TEST_ID_QUERY); + ObjectNode jsonObject = executeAndGetResult(TEST_ID_QUERY); assertNotNull(jsonObject); - JsonValue jsonValue = jsonObject.getJsonObject("data") - .getJsonArray("_entities") - .getJsonObject(0) + JsonNode jsonValue = ((ArrayNode) ((ObjectNode) jsonObject.get("data")) + .get("_entities")) + .get(0) .get("id"); - assertEquals(((JsonString) jsonValue).getString(), "id"); + assertEquals(jsonValue.asText(), "id"); - jsonValue = jsonObject.getJsonObject("data") - .getJsonArray("_entities") - .getJsonObject(0) + jsonValue = ((ArrayNode) ((ObjectNode) jsonObject.get("data")) + .get("_entities")) + .get(0) .get("value"); assertNull(jsonValue); } @Test public void extendsTest() { - JsonObject jsonObject = executeAndGetResult(TEST_ID_NAME_KEY_QUERY); + ObjectNode jsonObject = executeAndGetResult(TEST_ID_NAME_KEY_QUERY); assertNotNull(jsonObject); - JsonValue jsonValue = jsonObject.getJsonObject("data") - .getJsonArray("_entities") - .getJsonObject(0) + JsonNode jsonValue = ((ArrayNode) ((ObjectNode) jsonObject.get("data")) + .get("_entities")) + .get(0) .get("id"); - assertEquals(((JsonString) jsonValue).getString(), "id"); + assertEquals(jsonValue.asText(), "id"); - jsonValue = jsonObject.getJsonObject("data") - .getJsonArray("_entities") - .getJsonObject(0) + jsonValue = ((ArrayNode) ((ObjectNode) jsonObject.get("data")) + .get("_entities")) + .get(0) .get("value"); - assertEquals(((JsonString) jsonValue).getString(), "idnamekey"); + assertEquals(jsonValue.asText(), "idnamekey"); } private static final String TEST_ID_QUERY = "query {\n" + diff --git a/server/implementation/src/test/java/io/smallrye/graphql/execution/TestErrorExtensionProvider.java b/server/implementation/src/test/java/io/smallrye/graphql/execution/TestErrorExtensionProvider.java index d33305d8c..49944b3f9 100644 --- a/server/implementation/src/test/java/io/smallrye/graphql/execution/TestErrorExtensionProvider.java +++ b/server/implementation/src/test/java/io/smallrye/graphql/execution/TestErrorExtensionProvider.java @@ -1,9 +1,8 @@ package io.smallrye.graphql.execution; -import jakarta.json.Json; -import jakarta.json.JsonNumber; - import io.smallrye.graphql.api.ErrorExtensionProvider; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.node.JsonNodeFactory; public class TestErrorExtensionProvider implements ErrorExtensionProvider { @Override @@ -12,7 +11,7 @@ public String getKey() { } @Override - public JsonNumber mapValueFrom(Throwable exception) { - return Json.createValue(exception.getClass().getSimpleName().length()); + public JsonNode mapValueFrom(Throwable exception) { + return JsonNodeFactory.instance.numberNode(exception.getClass().getSimpleName().length()); } } diff --git a/server/implementation/src/test/java/io/smallrye/graphql/schema/DirectivesTestApi.java b/server/implementation/src/test/java/io/smallrye/graphql/schema/DirectivesTestApi.java index 2fc1c47fd..22ed0cddf 100644 --- a/server/implementation/src/test/java/io/smallrye/graphql/schema/DirectivesTestApi.java +++ b/server/implementation/src/test/java/io/smallrye/graphql/schema/DirectivesTestApi.java @@ -5,8 +5,8 @@ import org.eclipse.microprofile.graphql.GraphQLApi; import org.eclipse.microprofile.graphql.Mutation; import org.eclipse.microprofile.graphql.Query; +import org.eclipse.microprofile.graphql.Subscription; -import io.smallrye.graphql.api.Subscription; import io.smallrye.mutiny.Multi; @GraphQLApi diff --git a/server/implementation/src/test/java/io/smallrye/graphql/schema/SchemaTestBase.java b/server/implementation/src/test/java/io/smallrye/graphql/schema/SchemaTestBase.java index 7e995e108..10c895456 100644 --- a/server/implementation/src/test/java/io/smallrye/graphql/schema/SchemaTestBase.java +++ b/server/implementation/src/test/java/io/smallrye/graphql/schema/SchemaTestBase.java @@ -11,6 +11,7 @@ import org.jboss.jandex.Indexer; import org.junit.jupiter.api.AfterEach; +import graphql.language.BooleanValue; import graphql.language.StringValue; import graphql.schema.GraphQLDirective; import graphql.schema.GraphQLSchema; @@ -36,7 +37,8 @@ protected void assertKeyDirective(GraphQLDirective graphQLDirective, String fiel ((StringValue) graphQLDirective.getArguments().get(0).toAppliedArgument().getArgumentValue().getValue()) .getValue()); assertEquals(resolvableValue, graphQLDirective.getArguments().get(1).toAppliedArgument().getArgumentValue().getValue()); - assertEquals(true, graphQLDirective.getArguments().get(1).getArgumentDefaultValue().getValue()); + assertEquals(true, + ((BooleanValue) graphQLDirective.getArguments().get(1).getArgumentDefaultValue().getValue()).isValue()); } protected GraphQLSchema createGraphQLSchema(Class... api) { diff --git a/server/implementation/src/test/java/io/smallrye/graphql/schema/rolesallowedschemas/RolesSchema1.java b/server/implementation/src/test/java/io/smallrye/graphql/schema/rolesallowedschemas/RolesSchema1.java index 906c2e40f..606919a3b 100644 --- a/server/implementation/src/test/java/io/smallrye/graphql/schema/rolesallowedschemas/RolesSchema1.java +++ b/server/implementation/src/test/java/io/smallrye/graphql/schema/rolesallowedschemas/RolesSchema1.java @@ -5,8 +5,8 @@ import org.eclipse.microprofile.graphql.GraphQLApi; import org.eclipse.microprofile.graphql.Mutation; import org.eclipse.microprofile.graphql.Query; +import org.eclipse.microprofile.graphql.Subscription; -import io.smallrye.graphql.api.Subscription; import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.operators.multi.processors.BroadcastProcessor; diff --git a/server/implementation/src/test/java/io/smallrye/graphql/schema/rolesallowedschemas/RolesSchema2.java b/server/implementation/src/test/java/io/smallrye/graphql/schema/rolesallowedschemas/RolesSchema2.java index f1d3eba38..bf43b8951 100644 --- a/server/implementation/src/test/java/io/smallrye/graphql/schema/rolesallowedschemas/RolesSchema2.java +++ b/server/implementation/src/test/java/io/smallrye/graphql/schema/rolesallowedschemas/RolesSchema2.java @@ -6,8 +6,8 @@ import org.eclipse.microprofile.graphql.Mutation; import org.eclipse.microprofile.graphql.Query; import org.eclipse.microprofile.graphql.Source; +import org.eclipse.microprofile.graphql.Subscription; -import io.smallrye.graphql.api.Subscription; import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.operators.multi.processors.BroadcastProcessor; diff --git a/server/implementation/src/test/java/io/smallrye/graphql/schema/rolesallowedschemas/RolesSchema3.java b/server/implementation/src/test/java/io/smallrye/graphql/schema/rolesallowedschemas/RolesSchema3.java index 278075b1c..b118a2ee0 100644 --- a/server/implementation/src/test/java/io/smallrye/graphql/schema/rolesallowedschemas/RolesSchema3.java +++ b/server/implementation/src/test/java/io/smallrye/graphql/schema/rolesallowedschemas/RolesSchema3.java @@ -4,8 +4,8 @@ import org.eclipse.microprofile.graphql.Mutation; import org.eclipse.microprofile.graphql.Query; import org.eclipse.microprofile.graphql.Source; +import org.eclipse.microprofile.graphql.Subscription; -import io.smallrye.graphql.api.Subscription; import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.operators.multi.processors.BroadcastProcessor; diff --git a/server/integration-tests/src/test/java/io/smallrye/graphql/tests/GraphQLAssured.java b/server/integration-tests/src/test/java/io/smallrye/graphql/tests/GraphQLAssured.java index f051e0104..7b9a8b032 100644 --- a/server/integration-tests/src/test/java/io/smallrye/graphql/tests/GraphQLAssured.java +++ b/server/integration-tests/src/test/java/io/smallrye/graphql/tests/GraphQLAssured.java @@ -1,18 +1,17 @@ package io.smallrye.graphql.tests; -import java.io.StringReader; import java.net.URL; import java.util.Map; -import jakarta.json.Json; -import jakarta.json.JsonObject; -import jakarta.json.JsonObjectBuilder; -import jakarta.json.JsonReader; - import io.restassured.RestAssured; import io.restassured.config.EncoderConfig; import io.restassured.http.ContentType; import io.restassured.specification.RequestSpecification; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.node.JsonNodeFactory; +import tools.jackson.databind.node.ObjectNode; /** * Using RestAssured for GraphQL @@ -23,6 +22,8 @@ public class GraphQLAssured { private static final String MEDIATYPE_JSON = "application/json"; private static final String MEDIATYPE_GRAPHQL = "application/graphql"; + private static final ObjectMapper OBJECT_MAPPER = JsonMapper.builder().build(); + private static final JsonNodeFactory NODE_FACTORY = JsonNodeFactory.instance; protected URL testingURL; @@ -88,27 +89,28 @@ public String postAsGraphQL(String request, Map queryparams) { } private String getPayload(String query, String variables) { - JsonObject jsonObject = createRequestBody(query, variables); - return jsonObject.toString(); + ObjectNode jsonObject = createRequestBody(query, variables); + try { + return OBJECT_MAPPER.writeValueAsString(jsonObject); + } catch (JacksonException e) { + throw new RuntimeException(e); + } } - private JsonObject createRequestBody(String graphQL, String variables) { - // Create the request - - JsonObject vjo = Json.createObjectBuilder().build(); + private ObjectNode createRequestBody(String graphQL, String variables) { + ObjectNode vjo = NODE_FACTORY.objectNode(); if (variables != null && !variables.isEmpty()) { - try (JsonReader jsonReader = Json.createReader(new StringReader(variables))) { - vjo = jsonReader.readObject(); - } + vjo = (ObjectNode) OBJECT_MAPPER.readTree(variables); } - JsonObjectBuilder job = Json.createObjectBuilder(); + ObjectNode job = NODE_FACTORY.objectNode(); if (graphQL != null && !graphQL.isEmpty()) { - job.add(QUERY, graphQL); + job.put(QUERY, graphQL); } - return job.add(VARIABLES, vjo).build(); + job.set(VARIABLES, vjo); + return job; } private static final String QUERY = "query"; diff --git a/server/integration-tests/src/test/java/io/smallrye/graphql/tests/client/dynamic/subscription/DynamicClientSubscriptionApi.java b/server/integration-tests/src/test/java/io/smallrye/graphql/tests/client/dynamic/subscription/DynamicClientSubscriptionApi.java index 91b0ea900..8eb0d1f0e 100644 --- a/server/integration-tests/src/test/java/io/smallrye/graphql/tests/client/dynamic/subscription/DynamicClientSubscriptionApi.java +++ b/server/integration-tests/src/test/java/io/smallrye/graphql/tests/client/dynamic/subscription/DynamicClientSubscriptionApi.java @@ -2,8 +2,8 @@ import org.eclipse.microprofile.graphql.GraphQLApi; import org.eclipse.microprofile.graphql.Query; +import org.eclipse.microprofile.graphql.Subscription; -import io.smallrye.graphql.api.Subscription; import io.smallrye.mutiny.Multi; @GraphQLApi diff --git a/server/integration-tests/src/test/java/io/smallrye/graphql/tests/client/typesafe/subscription/SubscriptionApi.java b/server/integration-tests/src/test/java/io/smallrye/graphql/tests/client/typesafe/subscription/SubscriptionApi.java index 17a4393b8..2f67f68ce 100644 --- a/server/integration-tests/src/test/java/io/smallrye/graphql/tests/client/typesafe/subscription/SubscriptionApi.java +++ b/server/integration-tests/src/test/java/io/smallrye/graphql/tests/client/typesafe/subscription/SubscriptionApi.java @@ -3,8 +3,8 @@ import org.eclipse.microprofile.graphql.GraphQLApi; import org.eclipse.microprofile.graphql.Query; import org.eclipse.microprofile.graphql.Source; +import org.eclipse.microprofile.graphql.Subscription; -import io.smallrye.graphql.api.Subscription; import io.smallrye.mutiny.Multi; @GraphQLApi diff --git a/server/integration-tests/src/test/java/io/smallrye/graphql/tests/client/typesafe/subscription/SubscriptionClientApi.java b/server/integration-tests/src/test/java/io/smallrye/graphql/tests/client/typesafe/subscription/SubscriptionClientApi.java index 7677aa5ca..01d4f17f6 100644 --- a/server/integration-tests/src/test/java/io/smallrye/graphql/tests/client/typesafe/subscription/SubscriptionClientApi.java +++ b/server/integration-tests/src/test/java/io/smallrye/graphql/tests/client/typesafe/subscription/SubscriptionClientApi.java @@ -2,7 +2,8 @@ import java.io.Closeable; -import io.smallrye.graphql.api.Subscription; +import org.eclipse.microprofile.graphql.Subscription; + import io.smallrye.graphql.client.typesafe.api.GraphQLClientApi; import io.smallrye.mutiny.Multi; diff --git a/server/integration-tests/src/test/java/io/smallrye/graphql/tests/metrics/MicrometerMetricsTestCase.java b/server/integration-tests/src/test/java/io/smallrye/graphql/tests/metrics/MicrometerMetricsTestCase.java index c29f9fe28..84888f1f2 100644 --- a/server/integration-tests/src/test/java/io/smallrye/graphql/tests/metrics/MicrometerMetricsTestCase.java +++ b/server/integration-tests/src/test/java/io/smallrye/graphql/tests/metrics/MicrometerMetricsTestCase.java @@ -10,6 +10,7 @@ import org.eclipse.microprofile.graphql.GraphQLApi; import org.eclipse.microprofile.graphql.Query; +import org.eclipse.microprofile.graphql.Subscription; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.junit5.ArquillianExtension; import org.jboss.arquillian.test.api.ArquillianResource; @@ -25,7 +26,6 @@ import io.micrometer.core.instrument.Metrics; import io.micrometer.core.instrument.Tags; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; -import io.smallrye.graphql.api.Subscription; import io.smallrye.graphql.client.Response; import io.smallrye.graphql.client.dynamic.api.DynamicGraphQLClient; import io.smallrye.graphql.client.vertx.dynamic.VertxDynamicGraphQLClientBuilder; diff --git a/server/integration-tests/src/test/java/io/smallrye/graphql/tests/subscription/SubscriptionFieldBatchingTest.java b/server/integration-tests/src/test/java/io/smallrye/graphql/tests/subscription/SubscriptionFieldBatchingTest.java index f583b0dd3..2ca0f134a 100644 --- a/server/integration-tests/src/test/java/io/smallrye/graphql/tests/subscription/SubscriptionFieldBatchingTest.java +++ b/server/integration-tests/src/test/java/io/smallrye/graphql/tests/subscription/SubscriptionFieldBatchingTest.java @@ -8,6 +8,7 @@ import org.eclipse.microprofile.graphql.GraphQLApi; import org.eclipse.microprofile.graphql.Source; +import org.eclipse.microprofile.graphql.Subscription; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.junit5.ArquillianExtension; import org.jboss.arquillian.test.api.ArquillianResource; diff --git a/server/integration-tests/src/test/java/io/smallrye/graphql/tests/subscription/SubscriptionHasToBeCancelledOnWebSocketCloseTestCase.java b/server/integration-tests/src/test/java/io/smallrye/graphql/tests/subscription/SubscriptionHasToBeCancelledOnWebSocketCloseTestCase.java index 47f6e155c..7a0a62307 100644 --- a/server/integration-tests/src/test/java/io/smallrye/graphql/tests/subscription/SubscriptionHasToBeCancelledOnWebSocketCloseTestCase.java +++ b/server/integration-tests/src/test/java/io/smallrye/graphql/tests/subscription/SubscriptionHasToBeCancelledOnWebSocketCloseTestCase.java @@ -10,6 +10,7 @@ import org.eclipse.microprofile.graphql.GraphQLApi; import org.eclipse.microprofile.graphql.Query; +import org.eclipse.microprofile.graphql.Subscription; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.junit5.ArquillianExtension; import org.jboss.arquillian.test.api.ArquillianResource; @@ -18,7 +19,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import io.smallrye.graphql.api.Subscription; import io.smallrye.graphql.client.Response; import io.smallrye.graphql.client.dynamic.api.DynamicGraphQLClient; import io.smallrye.graphql.client.vertx.dynamic.VertxDynamicGraphQLClient; diff --git a/server/tck/pom.xml b/server/tck/pom.xml index 26b19c5d4..f53a6e5e6 100644 --- a/server/tck/pom.xml +++ b/server/tck/pom.xml @@ -109,6 +109,21 @@ compile 7.12.0 + + + + io.vertx + vertx-core + ${version.vertx} + test + + + + + org.eclipse.jetty.ee9.websocket + jetty-ee9-websocket-jakarta-server + test + @@ -118,20 +133,15 @@ maven-surefire-plugin - org.eclipse.microprofile.graphql:microprofile-graphql-server-tck - org.eclipse.microprofile.graphql:microprofile-graphql-tck + org.eclipse.microprofile.graphql:microprofile-graphql-tck testng.xml - - - true ${project.basedir}/src/test/resources/logging.properties - true diff --git a/server/tck/src/test/java/io/smallrye/graphql/SmallRyeGraphQLArchiveProcessor.java b/server/tck/src/test/java/io/smallrye/graphql/SmallRyeGraphQLArchiveProcessor.java index 4e7d14d73..54c17c1dc 100644 --- a/server/tck/src/test/java/io/smallrye/graphql/SmallRyeGraphQLArchiveProcessor.java +++ b/server/tck/src/test/java/io/smallrye/graphql/SmallRyeGraphQLArchiveProcessor.java @@ -60,11 +60,11 @@ public void process(Archive applicationArchive, TestClass testClass) { WebArchive war = (WebArchive) applicationArchive; - // Exclude the TCK beans in the deployed app. The TCK jar also has a beans.xml which causes duplicated beans + // Exclude the TCK test infrastructure but keep the API classes as CDI beans war.addAsWebInfResource(new StringAsset( "\n" + " \n" + - " \n" + + " \n" + " \n" + ""), "beans.xml"); diff --git a/server/tck/src/test/java/io/smallrye/graphql/TestInterceptor.java b/server/tck/src/test/java/io/smallrye/graphql/TestInterceptor.java index e6771bb43..eeb4a73cc 100644 --- a/server/tck/src/test/java/io/smallrye/graphql/TestInterceptor.java +++ b/server/tck/src/test/java/io/smallrye/graphql/TestInterceptor.java @@ -1,7 +1,6 @@ package io.smallrye.graphql; import java.io.IOException; -import java.io.StringReader; import java.nio.file.DirectoryStream; import java.nio.file.FileVisitResult; import java.nio.file.FileVisitor; @@ -20,16 +19,15 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import jakarta.json.Json; -import jakarta.json.JsonObject; -import jakarta.json.JsonReader; - import org.eclipse.microprofile.graphql.tck.dynamic.ExecutionDynamicTest; import org.eclipse.microprofile.graphql.tck.dynamic.SchemaDynamicValidityTest; import org.jboss.logging.Logger; import org.testng.ITestResult; import org.testng.TestListenerAdapter; +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.ObjectMapper; + /** * This allows us to override the input and output of the spec's TCK. * Sometimes this is needed when the spec should be updated but can not yet @@ -183,7 +181,7 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) } case "variables.json": { String content = getFileContent(file); - testData.setVariables(toJsonObject(content)); + testData.setVariables(toVariablesMap(content)); break; } case "test.properties": { @@ -291,12 +289,18 @@ private static boolean isHeader(String line) { return line.trim().startsWith(COMMENT); } - private JsonObject toJsonObject(String jsonString) { + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + private static final TypeReference> MAP_TYPE = new TypeReference<>() { + }; + + private Map toVariablesMap(String jsonString) { if (jsonString == null || jsonString.isEmpty()) { return null; } - try (JsonReader jsonReader = Json.createReader(new StringReader(jsonString))) { - return jsonReader.readObject(); + try { + return OBJECT_MAPPER.readValue(jsonString, MAP_TYPE); + } catch (Exception e) { + throw new RuntimeException(e); } } diff --git a/server/tck/src/test/java/io/smallrye/graphql/test/apps/context/api/ContextApi.java b/server/tck/src/test/java/io/smallrye/graphql/test/apps/context/api/ContextApi.java index f93baf30f..9abefa1e5 100644 --- a/server/tck/src/test/java/io/smallrye/graphql/test/apps/context/api/ContextApi.java +++ b/server/tck/src/test/java/io/smallrye/graphql/test/apps/context/api/ContextApi.java @@ -1,14 +1,9 @@ package io.smallrye.graphql.test.apps.context.api; -import java.io.IOException; -import java.io.StringWriter; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import jakarta.inject.Inject; -import jakarta.json.Json; -import jakarta.json.JsonArray; -import jakarta.json.JsonWriter; import org.eclipse.microprofile.context.ThreadContext; import org.eclipse.microprofile.graphql.DefaultValue; @@ -17,6 +12,8 @@ import org.eclipse.microprofile.graphql.Source; import io.smallrye.graphql.api.Context; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.node.ArrayNode; @GraphQLApi public class ContextApi { @@ -89,14 +86,11 @@ public CompletionStage asyncOperationType(@Source Pojo pojo) { threadContext.currentContextExecutor())); } - private String toString(JsonArray jsonArray) { - try (StringWriter sw = new StringWriter(); - JsonWriter writer = Json.createWriter(sw)) { - writer.writeArray(jsonArray); - writer.close(); - return sw.toString(); - } catch (IOException ex) { - throw new RuntimeException(ex); + private String toString(ArrayNode arrayNode) { + try { + return new ObjectMapper().writeValueAsString(arrayNode); + } catch (tools.jackson.core.JacksonException e) { + throw new RuntimeException(e); } } } diff --git a/server/tck/src/test/java/io/smallrye/graphql/test/apps/jsonp/api/JsonPApi.java b/server/tck/src/test/java/io/smallrye/graphql/test/apps/jsonp/api/JsonPApi.java index 929256c01..38f1e13c9 100644 --- a/server/tck/src/test/java/io/smallrye/graphql/test/apps/jsonp/api/JsonPApi.java +++ b/server/tck/src/test/java/io/smallrye/graphql/test/apps/jsonp/api/JsonPApi.java @@ -1,30 +1,28 @@ package io.smallrye.graphql.test.apps.jsonp.api; -import jakarta.json.Json; -import jakarta.json.JsonArray; -import jakarta.json.JsonArrayBuilder; -import jakarta.json.JsonObject; -import jakarta.json.JsonObjectBuilder; - import org.eclipse.microprofile.graphql.GraphQLApi; import org.eclipse.microprofile.graphql.Query; +import tools.jackson.databind.node.ArrayNode; +import tools.jackson.databind.node.JsonNodeFactory; +import tools.jackson.databind.node.ObjectNode; + @GraphQLApi public class JsonPApi { @Query("jsonpObject") - public JsonObject getRawToken() { - JsonObjectBuilder builder = Json.createObjectBuilder(); - builder.add("foo", "bar"); - return builder.build(); + public ObjectNode getRawToken() { + ObjectNode node = JsonNodeFactory.instance.objectNode(); + node.put("foo", "bar"); + return node; } @Query("jsonpArray") - public JsonArray getRawTokens() { - JsonArrayBuilder builder = Json.createArrayBuilder(); - builder.add("foo"); - builder.add("bar"); - return builder.build(); + public ArrayNode getRawTokens() { + ArrayNode array = JsonNodeFactory.instance.arrayNode(); + array.add("foo"); + array.add("bar"); + return array; } @Query("jsonpField") diff --git a/server/tck/src/test/java/io/smallrye/graphql/test/apps/jsonp/api/Token.java b/server/tck/src/test/java/io/smallrye/graphql/test/apps/jsonp/api/Token.java index c46fb8339..d60a6aad2 100644 --- a/server/tck/src/test/java/io/smallrye/graphql/test/apps/jsonp/api/Token.java +++ b/server/tck/src/test/java/io/smallrye/graphql/test/apps/jsonp/api/Token.java @@ -1,8 +1,8 @@ package io.smallrye.graphql.test.apps.jsonp.api; -import jakarta.json.JsonObject; +import tools.jackson.databind.node.ObjectNode; public class Token { public String name; - public JsonObject value; + public ObjectNode value; } diff --git a/server/tck/src/test/java/io/smallrye/graphql/test/apps/subscription/api/StocksApi.java b/server/tck/src/test/java/io/smallrye/graphql/test/apps/subscription/api/StocksApi.java index 590de41c2..c60969fec 100644 --- a/server/tck/src/test/java/io/smallrye/graphql/test/apps/subscription/api/StocksApi.java +++ b/server/tck/src/test/java/io/smallrye/graphql/test/apps/subscription/api/StocksApi.java @@ -1,13 +1,14 @@ package io.smallrye.graphql.test.apps.subscription.api; import java.util.List; +import java.util.concurrent.Flow; import org.eclipse.microprofile.graphql.Description; import org.eclipse.microprofile.graphql.GraphQLApi; import org.eclipse.microprofile.graphql.Query; +import org.eclipse.microprofile.graphql.Subscription; import org.reactivestreams.Publisher; -import io.smallrye.graphql.api.Subscription; import io.smallrye.mutiny.Multi; import mutiny.zero.flow.adapters.AdaptersToFlow; @@ -45,6 +46,12 @@ public Multi arraySubscription() { return Multi.createFrom().empty(); } + @Subscription + @Description("Get stock quote changes using Flow.Publisher (MicroProfile spec requirement)") + public Flow.Publisher getStockQuoteFlow(String stockCode) { + return AdaptersToFlow.publisher(STOCK_TICKER_PUBLISHER.getPublisher()); + } + // TODO: Support other Publisher types ? } diff --git a/server/tck/src/test/resources/overrides/basicScalarTests.csv b/server/tck/src/test/resources/overrides/basicScalarTests.csv deleted file mode 100644 index 5f2526d80..000000000 --- a/server/tck/src/test/resources/overrides/basicScalarTests.csv +++ /dev/null @@ -1,3 +0,0 @@ -# Basic Scalar Types -23| type ScalarHolder | charArray: [String!] | Expecting a String Array Scalar (for Java Char[]) Type in type ScalarHolder -47| type Query | testCharArray: [String!] | Expecting a non null Stirng Array (for Java Char[]) Scalar Type in type Query \ No newline at end of file diff --git a/server/tck/src/test/resources/overrides/createNewNullNamedHero/output2.json b/server/tck/src/test/resources/overrides/createNewNullNamedHero/output2.json deleted file mode 100644 index f272245ba..000000000 --- a/server/tck/src/test/resources/overrides/createNewNullNamedHero/output2.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "errors": [ - { - "message": "Validation error (WrongType@[createNewHero]) : argument 'hero.name' with value 'NullValue{}' must not be null", - "locations": [ - { - "line": 2, - "column": 19 - } - ] - } - ] -} \ No newline at end of file diff --git a/server/tck/src/test/resources/overrides/createNewUnnamedHero/output2.json b/server/tck/src/test/resources/overrides/createNewUnnamedHero/output2.json deleted file mode 100644 index 60575b6ad..000000000 --- a/server/tck/src/test/resources/overrides/createNewUnnamedHero/output2.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "errors": [ - { - "message": "Validation error (WrongType@[createNewHero]) : argument 'hero' with value 'ObjectValue{objectFields=[ObjectField{name='realName', value=StringValue{value='John Smith'}}]}' is missing required fields '[name]'", - "locations": [ - { - "line": 2, - "column": 19 - } - ] - } - ] -} \ No newline at end of file diff --git a/server/tck/src/test/resources/overrides/invalidDataTypeValue/output3.json b/server/tck/src/test/resources/overrides/invalidDataTypeValue/output3.json deleted file mode 100644 index 2cc5307e6..000000000 --- a/server/tck/src/test/resources/overrides/invalidDataTypeValue/output3.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "errors": [ - { - "message": "Validation error (WrongType@[updateItemPowerLevel]) : argument 'powerLevel' with value 'StringValue{value='Unlimited'}' is not a valid 'Int' - SRGQL000022: Can not parse a number from [StringValue{value='Unlimited'}]", - "locations": [ - { - "line": 2, - "column": 37 - } - ] - } - ] -} \ No newline at end of file diff --git a/server/tck/src/test/resources/overrides/invalidEnumValue/output2.json b/server/tck/src/test/resources/overrides/invalidEnumValue/output2.json deleted file mode 100644 index c4a6ff809..000000000 --- a/server/tck/src/test/resources/overrides/invalidEnumValue/output2.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "errors": [ - { - "message": "Validation error (WrongType@[createNewHero]) : argument 'hero.tshirtSize' with value 'EnumValue{name='XLTall'}' is not a valid 'ShirtSize' - Literal value not in allowable values for enum 'ShirtSize' - 'EnumValue{name='XLTall'}'", - "locations": [ - { - "line": 3, - "column": 19 - } - ] - } - ] -} \ No newline at end of file diff --git a/server/tck/src/test/resources/overrides/invalidLocalDateFormattedValue/output3.json b/server/tck/src/test/resources/overrides/invalidLocalDateFormattedValue/output3.json deleted file mode 100644 index 4fcff2375..000000000 --- a/server/tck/src/test/resources/overrides/invalidLocalDateFormattedValue/output3.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "errors": [ - { - "message": "argument 'date' with value 'StringValue{value='Today'}' is not a valid 'Date'", - "locations": [ - { - "line": 2, - "column": 49 - } - ] - } - ], - "data": { - "checkInWithCorrectDateFormat": null - } -} diff --git a/server/tck/src/test/resources/overrides/invalidLocalDateTimeFormattedValue/output3.json b/server/tck/src/test/resources/overrides/invalidLocalDateTimeFormattedValue/output3.json deleted file mode 100644 index 34be441fa..000000000 --- a/server/tck/src/test/resources/overrides/invalidLocalDateTimeFormattedValue/output3.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "errors": [ - { - "message": "argument 'dateTime' with value 'StringValue{value='Today'}' is not a valid 'DateTime'", - "locations": [ - { - "line": 2, - "column": 48 - } - ] - } - ], - "data": { - "battleWithCorrectDateFormat": null - } -} \ No newline at end of file diff --git a/server/tck/src/test/resources/overrides/invalidLocalDateTimeValue/output3.json b/server/tck/src/test/resources/overrides/invalidLocalDateTimeValue/output3.json deleted file mode 100644 index af2c0dc4e..000000000 --- a/server/tck/src/test/resources/overrides/invalidLocalDateTimeValue/output3.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "errors": [ - { - "message": "argument 'dateTime' with value 'StringValue{value='Today'}' is not a valid 'DateTime'", - "locations": [ - { - "line": 2, - "column": 27 - } - ] - } - ], - "data": { - "battle": null - } -} \ No newline at end of file diff --git a/server/tck/src/test/resources/overrides/invalidLocalDateValue/output3.json b/server/tck/src/test/resources/overrides/invalidLocalDateValue/output3.json deleted file mode 100644 index 49fac88d8..000000000 --- a/server/tck/src/test/resources/overrides/invalidLocalDateValue/output3.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "errors": [ - { - "message": "argument 'date' with value 'StringValue{value='Today'}' is not a valid 'Date'", - "locations": [ - { - "line": 2, - "column": 28 - } - ] - } - ], - "data": { - "checkIn": null - } -} diff --git a/server/tck/src/test/resources/overrides/invalidLocalTimeFormattedValue/output3.json b/server/tck/src/test/resources/overrides/invalidLocalTimeFormattedValue/output3.json deleted file mode 100644 index dbd808830..000000000 --- a/server/tck/src/test/resources/overrides/invalidLocalTimeFormattedValue/output3.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "errors": [ - { - "message": "argument 'time' with value 'StringValue{value='Today'}' is not a valid 'Time'", - "locations": [ - { - "line": 2, - "column": 57 - } - ] - } - ], - "data": { - "startPatrollingWithCorrectDateFormat": null - } -} diff --git a/server/tck/src/test/resources/overrides/invalidLocalTimeValue/output3.json b/server/tck/src/test/resources/overrides/invalidLocalTimeValue/output3.json deleted file mode 100644 index 78d6bfd37..000000000 --- a/server/tck/src/test/resources/overrides/invalidLocalTimeValue/output3.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "errors": [ - { - "message": "argument 'time' with value 'StringValue{value='Today'}' is not a valid 'Time'", - "locations": [ - { - "line": 2, - "column": 36 - } - ] - } - ], - "data": { - "startPatrolling": null - } -} \ No newline at end of file diff --git a/server/tck/src/test/resources/overrides/schemaTests.csv b/server/tck/src/test/resources/overrides/schemaTests.csv deleted file mode 100644 index 3447d7235..000000000 --- a/server/tck/src/test/resources/overrides/schemaTests.csv +++ /dev/null @@ -1,2 +0,0 @@ -# testJsonDefault -60|type Mutation | id : "1000" | Expecting an id in the default value for item for provisionHero \ No newline at end of file diff --git a/server/tck/src/test/resources/overrides/unknownField/output2.json b/server/tck/src/test/resources/overrides/unknownField/output2.json deleted file mode 100644 index c8eedf495..000000000 --- a/server/tck/src/test/resources/overrides/unknownField/output2.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "errors": [ - { - "message": "Validation error (FieldUndefined@[allHeroes/weaknesses]) : Field 'weaknesses' in type 'SuperHero' is undefined", - "locations": [ - { - "line": 4, - "column": 5 - } - ] - } - ] -} \ No newline at end of file diff --git a/server/tck/src/test/resources/overrides/unknownMutation/output2.json b/server/tck/src/test/resources/overrides/unknownMutation/output2.json deleted file mode 100644 index c1e1c0c82..000000000 --- a/server/tck/src/test/resources/overrides/unknownMutation/output2.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "errors": [ - { - "message": "Validation error (FieldUndefined@[createNewHeroCat]) : Field 'createNewHeroCat' in type 'Mutation' is undefined", - "locations": [ - { - "line": 2, - "column": 5 - } - ] - } - ] -} \ No newline at end of file diff --git a/server/tck/src/test/resources/overrides/unknownQuery/output2.json b/server/tck/src/test/resources/overrides/unknownQuery/output2.json deleted file mode 100644 index 55941f7c0..000000000 --- a/server/tck/src/test/resources/overrides/unknownQuery/output2.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "errors": [ - { - "message": "Validation error (FieldUndefined@[allHeroesWhoLikeIceCream]) : Field 'allHeroesWhoLikeIceCream' in type 'Query' is undefined", - "locations": [ - { - "line": 2, - "column": 3 - } - ] - } - ] -} diff --git a/server/tck/src/test/resources/tests/subscriptionTests.csv b/server/tck/src/test/resources/tests/subscriptionTests.csv index c04fe71cb..d2934187d 100644 --- a/server/tck/src/test/resources/tests/subscriptionTests.csv +++ b/server/tck/src/test/resources/tests/subscriptionTests.csv @@ -1,4 +1,5 @@ # testSubscriptions 1| type Subscription | arraySubscription: [String] | Java array in subscription should map to [String] in graphql -2| type Subscription | listSubscription: [String] | Java array in subscription should map to [String] in graphql -3| type Subscription | stockQuoteMulti(stockCode: String): Stock| Subscription must be able to take arguments \ No newline at end of file +2| type Subscription | listSubscription: [String] | Java List in subscription should map to [String] in graphql +3| type Subscription | stockQuoteMulti(stockCode: String): Stock| Subscription must be able to take arguments +4| type Subscription | stockQuoteFlow(stockCode: String): Stock | Flow.Publisher subscription (MicroProfile spec requirement)