micronaut-projects / micronaut-serialization

Build Time Serialization APIs for Micronaut
Apache License 2.0
26 stars 19 forks source link

An http client from a dependency requires parsing DTOs composed of internal and other dependencies DTOs #844

Open marcosflobo opened 4 months ago

marcosflobo commented 4 months ago

Expected Behavior

Solution/workaround

Micronaut doesn't seem to support @SerdeImport on classes which are already @Introspected (maybe the introspection companion objects are not compatible ?). To circumvent that we redefined com.xxxxx.datamodel.Output and com.xxxxx.datamodel.Input locally as @Serdeable objects. Which was not bad since we were already cherry-picking dependencies from the com.xxxxx.* package to build our own dtos

Actual Behaviour

package com.xxxxx.tools.client.act.dto;

import com.xxxx.datamodel.Purpose;
import com.xxxx.datamodel.Targeting;
import io.micronaut.serde.annotation.Serdeable;
import java.util.Set;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@NoArgsConstructor
@Serdeable
@Getter
@Setter
// Used for tests
@EqualsAndHashCode
@ToString
public final class RemoteActionDto {
  private String id;
  private String uuid;
  private String name;
  private String description;

  private Set<Purpose> purpose;
  private Targeting targeting;
  private ScriptInfoDto scriptInfo;
...

Purpose and Targeting classes both come from a dependency of the library defining the HTTP client.

package com.xxxxxx.tools.client.act.dto;

import com.xxxx.datamodel.Input;
import com.xxxx.datamodel.Output;
import java.util.Collections;
import java.util.List;
import lombok.Builder;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;

@Jacksonized
@Builder
@Value
public class ScriptInfoDto {
  String runAs;
  int timeoutSeconds;
  boolean hasScriptWindows;
  boolean hasScriptMacOs;
  @Builder.Default List<Input> inputs = Collections.emptyList();
  @Builder.Default List<Output> outputs = Collections.emptyList();
}

Same as for RemoteActionDto, Input, Output come from external dependencies of the library

In the microservice, the deserialization of RemoteActionDto fails with the following NullPoiinterException

Client 'act-api-client': Error decoding HTTP response body: Error decoding JSON stream for type [B]: Error decoding property [ScriptInfoDto scriptInfo] of type [class com.xxxxxx.tools.client.act.dto.RemoteActionDto]: Cannot invoke "io.micronaut.serde.Deserializer.deserializeNullable(io.micronaut.serde.Decoder, io.micronaut.serde.Deserializer$DecoderContext, io.micronaut.core.type.Argument)" because "this.deserializer" is null
io.micronaut.http.client.exceptions.HttpClientResponseException: Client 'act-api-client': Error decoding HTTP response body: Error decoding JSON stream for type [B]: Error decoding property [ScriptInfoDto scriptInfo] of type [class com.xxxxxxl.tools.client.act.dto.RemoteActionDto]: Cannot invoke "io.micronaut.serde.Deserializer.deserializeNullable(io.micronaut.serde.Decoder, io.micronaut.serde.Deserializer$DecoderContext, io.micronaut.core.type.Argument)" because "this.deserializer" is null
    at io.micronaut.http.client.netty.DefaultHttpClient$FullHttpResponseHandler.makeNormalBodyParseError(DefaultHttpClient.java:2293)
    at io.micronaut.http.client.netty.DefaultHttpClient$FullHttpResponseHandler.forwardResponseToPromise(DefaultHttpClient.java:2223)
    at io.micronaut.http.client.netty.DefaultHttpClient$FullHttpResponseHandler.channelReadInstrumented(DefaultHttpClient.java:2179)
    at io.micronaut.http.client.netty.DefaultHttpClient$FullHttpResponseHandler.channelReadInstrumented(DefaultHttpClient.java:2147)
...
Caused by: java.lang.NullPointerException: Cannot invoke "io.micronaut.serde.Deserializer.deserializeNullable(io.micronaut.serde.Decoder, io.micronaut.serde.Deserializer$DecoderContext, io.micronaut.core.type.Argument)" because "derProperty.deserializer" is null
    at io.micronaut.serde.support.deserializers.SpecificObjectDeserializer.deserializeValue(SpecificObjectDeserializer.java:300)
    at io.micronaut.serde.support.deserializers.SpecificObjectDeserializer$ConstructorValuesDeserializer.tryConsume(SpecificObjectDeserializer.java:691)
    at io.micronaut.serde.support.deserializers.SpecificObjectDeserializer$ArgsConstructorBeanDeserializer.tryConsume(SpecificObjectDeserializer.java:831)
    at io.micronaut.serde.support.deserializers.SpecificObjectDeserializer.deserialize(SpecificObjectDeserializer.java:105)
    at io.micronaut.serde.support.deserializers.SpecificObjectDeserializer.deserialize(SpecificObjectDeserializer.java:73)
    at io.micronaut.serde.support.deserializers.SpecificObjectDeserializer.deserializeNullable(SpecificObjectDeserializer.java:239)

Steps To Reproduce

No response

Environment Information

Example Application

No response

Version

4.3.7

marcosflobo commented 4 months ago

cc @alvarosanchez @sdelamo

dstepanov commented 4 months ago

@marcosflobo Please provide a project or a PR with a test case that reproduces it

dstepanov commented 4 months ago

And please test without Lombok and provide samples without it