-
Notifications
You must be signed in to change notification settings - Fork 96
Description
I am using the gitlab graphql api and I am running this mutation request:
mutation workItemUpdate($arg0: WorkItemUpdateInput!) {
workItemUpdate(input: $arg0) {
errors
workItem {
id
webUrl
}
}
}
The java client I am using is here:
https://github.com/unblu/gitlab-workitem-graphql-client/
In particular see the WorkItemUpdateInput
class definition which has a member hierarchyWidget
of type WorkItemWidgetHierarchyUpdateInput
The documentation is not really clear about the hierarchyWidget
:
https://docs.gitlab.com/ee/api/graphql/reference/#workitemwidgethierarchyupdateinput
Those are the key point (for the client):
- for
parentId
you have 3 values:- setting an id (String value) to set a new parent
- setting
null
to remove the parent association - not setting the attribute to not modify the parent association
- you can not set
parentId
andchildrenIds
in the same request- runtime error:
"One and only one of children, parent or remove_child is required", so in theory we should do multiple runs, especially in case of "turning a child into a parent or the other way around".
- runtime error:
So again this is a case where not setting the value and setting it explicitly to null
has a different meaning.
json-schema-org/json-schema-spec#584 (comment)
And in Java this is always tricky.
I stared an implementation with a NullableProperty<T>
helper class (to support the 3 states) annotated with @JsonbTypeSerializer
and @JsonbTypeDeserializer
Yasson test (jbang)
///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS org.eclipse:yasson:3.0.4
//JAVA 11
import java.lang.reflect.Type;
import java.util.Optional;
import jakarta.json.JsonValue;
import jakarta.json.bind.Jsonb;
import jakarta.json.bind.JsonbBuilder;
import jakarta.json.bind.annotation.JsonbTypeDeserializer;
import jakarta.json.bind.annotation.JsonbTypeSerializer;
import jakarta.json.bind.serializer.DeserializationContext;
import jakarta.json.bind.serializer.JsonbDeserializer;
import jakarta.json.bind.serializer.JsonbSerializer;
import jakarta.json.bind.serializer.SerializationContext;
import jakarta.json.stream.JsonGenerator;
import jakarta.json.stream.JsonParser;
public class YassonTest {
public static void main(String[] args) {
Jsonb jsonb = JsonbBuilder.create();
Example example = new Example();
// Case 1: Set name to "John"
example.setName(NullableProperty.of("John"));
System.out.println(jsonb.toJson(example)); // {"name":"John"}
// Case 2: Set name to NullableProperty.empty()
example.setName(NullableProperty.empty());
System.out.println(jsonb.toJson(example)); // {"name":null}
// Case 3: Set name to null
example.setName(null);
System.out.println(jsonb.toJson(example)); // {}
}
public static class Example {
@JsonbTypeSerializer(NullablePropertySerializer.class)
@JsonbTypeDeserializer(NullablePropertyDeserializer.class)
private NullableProperty<String> name;
public NullableProperty<String> getName() {
return name;
}
public void setName(NullableProperty<String> name) {
this.name = name;
}
}
public static class NullablePropertyDeserializer<T> implements JsonbDeserializer<NullableProperty<T>> {
@Override
public NullableProperty<T> deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) {
JsonValue value = parser.getValue();
if (value == JsonValue.NULL) {
return NullableProperty.empty();
} else {
T deserializedValue = ctx.deserialize(rtType, parser);
return NullableProperty.of(deserializedValue);
}
}
}
public static class NullablePropertySerializer<T> implements JsonbSerializer<NullableProperty<T>> {
@Override
public void serialize(NullableProperty<T> obj, JsonGenerator generator, SerializationContext ctx) {
if (obj.isPresent()) {
ctx.serialize(obj.get(), generator);
} else {
generator.write(JsonValue.NULL);
}
}
}
public static class NullableProperty<T> {
private final T value;
private NullableProperty(T value) {
this.value = value;
}
public static <T> NullableProperty<T> of(T value) {
if (value == null) {
throw new IllegalArgumentException("Use NullableProperty.empty() for null values");
}
return new NullableProperty<>(value);
}
public static <T> NullableProperty<T> empty() {
return new NullableProperty<>(null);
}
public Optional<T> toOptional() {
return Optional.ofNullable(value);
}
public T get() {
return value;
}
public boolean isPresent() {
return value != null;
}
}
}
It works great at Json-b level, but I have the feeling that the typesafe client is doing something else when it comes to the serialization of the Input object.