asyncapi/jasyncapi

Custom deserializer shouldn't instantiate their own ObjectMapper

guillaumelamirand opened this issue · 9 comments

Describe the bug

While trying to deserialize AsyncAPI with a custom object mapper which disables FAIL_ON_UNKNOWN_PROPERTIES, stack exceptions are logged anyway and if any extensions are present under Component, Schema, or Message the result AsyncAPI object is half empty.

I will try to work on a PR.

How to Reproduce

Steps to reproduce the issue. Attach all resources that can help us understand the issue:

  • Using the following unit test with the attached AsyncAPI will fail
public class ImportAsyncTest {

    @Test
    void should_parse_asyncapi() throws IOException {
        ClassLoader classloader = Thread.currentThread().getContextClassLoader();
        try (InputStream myStream = classloader.getResourceAsStream("solace.yaml")) {
            String asyncapi = IOUtils.toString(myStream, Charset.defaultCharset()).trim();
            ObjectMapper yamlMapper = new ObjectMapper(
                YAMLFactory
                    .builder()
                    .disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER)
                    .enable(StreamReadFeature.STRICT_DUPLICATE_DETECTION)
                    .build()
            )
                .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
                .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
                .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
                .setSerializationInclusion(JsonInclude.Include.NON_NULL)
                .findAndRegisterModules();
            var asyncApi = yamlMapper.readValue(asyncapi, AsyncAPI.class);

            assertThat(asyncApi.getServers()).hasSize(2);
            assertThat(asyncApi.getChannels()).hasSize(2);
            assertThat(asyncApi.getComponents()).isNotNull();
            assertThat(asyncApi.getComponents().getMessages()).hasSize(2);
            assertThat(asyncApi.getComponents().getSchemas()).hasSize(2);
        }
    }
components:
  schemas:
    UserSignedUpSchema:
      x-ep-schema-version: "0.1.0"
      x-ep-schema-state-id: "2"
      x-ep-schema-version-id: "5rlnuswe64g"
      $schema: "http://json-schema.org/draft-06/schema#"
      x-ep-schema-id: "hcq88rogdaj"
      x-ep-schema-state-name: "RELEASED"
      x-ep-schema-name: "UserSignedUpSchema"
      definitions:
        UserSignedUp:
          additionalProperties: false
          type: "object"
          title: "UserSignedUp"
          properties:
            displayName:
              type: "string"
            email:
              type: "string"
          required:
            - "displayName"
            - "email"
      x-ep-schema-version-displayname: ""
    UserSignedInSchema:
      x-ep-schema-version: "0.1.0"
      x-ep-schema-state-id: "2"
      x-ep-schema-version-id: "fxnate3zvf6"
      $schema: "http://json-schema.org/draft-06/schema#"
      x-ep-schema-id: "iigkr9ji3kg"
      x-ep-schema-state-name: "RELEASED"
      x-ep-schema-name: "UserSignedInSchema"
      definitions:
        UserSignedIn:
          additionalProperties: false
          type: "object"
          title: "UserSignedn"
          properties:
            displayName:
              type: "string"
            email:
              type: "string"
          required:
            - "displayName"
            - "email"
      x-ep-schema-version-displayname: ""
asyncapi: "2.5.0"
info:
  x-ep-event-api-product-version-id: "4ndau725iz0"
  x-ep-event-api-id: "jcc06dwq1bh"
  x-ep-event-api-product-version: "0.1.0"
  x-ep-state-name: "RELEASED"
  title: "User Event Api"
  x-ep-application-domain-id: "4auuz4amd31"
  version: "0.1.0"
  x-ep-event-api-product-id: "tcpugx95gc0"
  x-ep-event-api-version-id: "t8hkbn8lit9"
  x-ep-application-domain-name: "gravitee_demo"
  x-ep-event-api-version: "0.1.0"
  x-ep-event-api-product-name: "User Event Api Product"
  x-ep-state-id: "2"

Expected behavior

All ignore properties (x-*) are ignored.

Welcome to AsyncAPI. Thanks a lot for reporting your first issue. Please check out our contributors guide and the instructions about a basic recommended setup useful for opening a pull request.
Keep in mind there are also other channels you can use to interact with AsyncAPI community. For more details check out this issue.

@guillaumelamirand hi!

Can you tell me more about #131 (comment)?

You crafted schema by hands or by objectMapper?

upd:
When I wrote response to you I didn't check changes from @dennis-brinley. He did all right, by using @JsonAnyGetter, @JsonAnySetter

On deserialization:

{
  "description": "Id of the user.",
  "schema": {
    "type": "string"
  },
  "location": "$message.payload#/user/id",
  "x-ep-event-api-product-version-id": "4ndau725iz0",
  "x-ep-event-api-id": "jcc06dwq1bh",
  "x-ep-event-api-product-version": "0.1.0",
  "x-ep-state-name": "RELEASED",
  "x-ep-application-domain-id": "4auuz4amd31",
  "x-ep-event-api-product-id": "tcpugx95gc0",
  "x-ep-event-api-version-id": "t8hkbn8lit9",
  "x-ep-application-domain-name": "gravitee_demo",
  "x-ep-event-api-version": "0.1.0",
  "x-ep-event-api-product-name": "User Event Api Product",
  "x-ep-state-id": "2"
}

On serialization:

Parameter(
  description = "Id of the user.", 
  ...
  location = "$message.payload#/user/id", 
  extensionFields = {
    x - ep - event - api - version = 0.1 .0,
    x - ep - event - api - product - version - id = 4n dau725iz0,
    x - ep - event - api - id = jcc06dwq1bh,
    x - ep - event - api - product - name = User Event Api Product,
    x - ep - event - api - product - version = 0.1 .0,
    x - ep - state - name = RELEASED,
    x - ep - application - domain - id = 4 auuz4amd31,
    x - ep - event - api - product - id = tcpugx95gc0,
    x - ep - event - api - version - id = t8hkbn8lit9,
    x - ep - state - id = 2,
    x - ep - application - domain - name = gravitee_demo
})

Here is only one type in value - String instead of Object

My comment regarding extensionFields is not related to this PR, but it is due to a change made with aec9541a4a469b1b62dd58f3728203f41e755957 the extra extensionFields get written back when you serialized the model.

I tested by reading AsyncAPI from file to model with x-xxx attributes which creates extra extensionFields maps, then I write it back and the result is the one I attached above.

Great, looks like everything works as expected. Will close this issue tomorrow

upd:
extensionFields are for internal use only and must not be present in specification

Great, looks like everything works as expected. Will close this issue tomorrow

upd: extensionFields are for internal use only and must not be present in specification

Yes that is exactly the meaning of my comment. The field got generated when I serialized the model to the file and it shouldn’t.

I'm a little bit confused, is here unexpected behavior or not😅

If you think that yes, tell how to reproduce.

I checked once more, on reading of spec with 'x-*' fields, they have been moved to 'extensionFuelds', on writing they have been put to object body directly

I will check it again, let you know.

Check on new develop. I merged changes - #133