question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

JsonSubTypes not properly handled while generating CRD from Java annotated class

See original GitHub issue

Describe the bug

Disclaimer: I’ve tried to check whether that’s a limitation on the CRD spec, but I don’t know for sure whether it’s the case or not, so I decided to create a bug anyway.

I’m trying to generate the CRD from a Java class, but I need to use a generic type and while generating the CRD, it comes out as object instead of having the actual properties. Both for a List<T> and for property of the generic type itself

Output:

# Generated by Fabric8 CRDGenerator, manual edits might get overwritten!
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: examples.dev.renann
spec:
  group: dev.renann
  names:
    kind: example
    plural: examples
    singular: example
  scope: Namespaced
  versions:
  - name: v1alpha1
    schema:
      openAPIV3Schema:
        properties:
          spec:
            properties:
              myList:
                items:
                  type: object
                type: array
              myField:
                type: object
              status:
                properties:
                  error:
                    type: boolean
                  message:
                    type: string
                type: object
            type: object
          status:
            properties:
              error:
                type: boolean
              message:
                type: string
            type: object
        type: object
    served: true
    storage: true
    subresources:
      status: {}

Fabric8 Kubernetes Client version

6.0.0

Steps to reproduce

Create a class like the below and try to generate the CRD definition.

package dev.renann.kubernetes.operator;

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonTypeResolver;
import io.fabric8.kubernetes.api.model.Namespaced;
import io.fabric8.kubernetes.client.CustomResource;
import io.fabric8.kubernetes.model.annotation.*;

import java.util.List;

@Group("dev.renann")
@Version("v1alpha1")
@Kind("example")
public class ExampleCrd extends CustomResource<ExampleCrd.MySpecArgs, ExampleCrd.ExampleStatus> implements Namespaced {
    public ExampleCrd() {
    }

    public static class ExampleStatus {
        boolean error;
        String message;

        public ExampleStatus(boolean error, String message) {
            this.error = error;
            this.message = message;
        }
    }

    public static class MySpecArgs {
        private final List<MyInterface> myList;
        private final MyInterface myField;
        private final ExampleStatus status;
        public MySpecArgs(List<MyInterface> myList, MyInterface myField, ExampleStatus status) {
            this.myList = myList;
            this.myField = myField;
            this.status = status;
        }

        public List<MyInterface> getMyList() {
            return myList;
        }

        public ExampleStatus getStatus() {
            return status;
        }

        @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
        @JsonSubTypes({
                @JsonSubTypes.Type(
                        value = MyImpl.class,
                        name = "the-impl")
        })
        private interface MyInterface {
            String getType();
        }

        public static class MyImpl implements MyInterface {
            private final String name;

            public MyImpl(String name) {
                this.name = name;
            }

            public String getName() {
                return name;
            }

            @Override
            public String getType() {
                return "the-type";
            }
        }
    }
}

Expected behavior

As I said, I’m not 100% sure whether this is supported by the CRD spec, but I would expect that the generic types properties are present on the spec and that a certain field support multiple types.

Runtime

Kubernetes (vanilla)

Kubernetes API Server version

1.23

Environment

macOS

Fabric8 Kubernetes Client Logs

No response

Additional context

No response

Issue Analytics

  • State:closed
  • Created a year ago
  • Comments:12 (7 by maintainers)

github_iconTop GitHub Comments

1reaction
andreaTPcommented, Aug 16, 2022

For example, I have a field where you can input a URL, which is a string. But not any string is a valid URL, so even though the type is correct, the value might still be wrong.

I agree that the “built-in” validation is not going to cover a lot of cases, but I just wanna mention that simple validation like URL is supported using the built-in pattern.

If I understood correctly, let’s say you have 3 implementations of the interface, then you’d have

Let me say that my thoughts are not settled here, but I’m looking for something like:

my-array:
  oneOf:
    - array-type1
    ...
    - array-type2
    ...
    - array-type3
    ...

I get what you mean. I don’t have capacity to propose something now unfortunately, but I’ll come back to this in the future. I also have just got started with using the library and implementing operators, so I bet I have lots of things to learn before I can make a good proposal.

Enjoy the journey!!! 🎉

But thanks for being open for it!

no problem at all, thanks for the discussion, for the moment I would prefer to close this issue as it seems to be:

  • a niche enough use-case
  • in need of a more “structured” solution/proposal

Feel free to re-open it for any compelling reason, or, open a new issue when you come to it 🙂

0reactions
renannpradocommented, Aug 14, 2022

Adding x-kubernetes-preserve-unknown-fields is a workaround as it allows any unknown field to be included, preventing any kind of reasonable validation.

The validation that is built-in is not enough from my POV. For example, I have a field where you can input a URL, which is a string. But not any string is a valid URL, so even though the type is correct, the value might still be wrong.

Moreover,

if the presence or absence of fields clearly discriminates all the possible implementations an implementation is possible (possibly with a few limitations around required fields?)

Are you suggesting to squash all fields under the same object? I mean, that could work, but at this point I would favor your last suggestion.

if the type is serialized along in an explicit field it’s again doable with a few minor restrictions

If I understood correctly, let’s say you have 3 implementations of the interface, then you’d have

array-type1:
  - obj-type1...
array-type2:
  - obj-type2...
array-type3:
  - obj-type3...

I’m not sure if I like that, but I’ll think about it.

The biggest unknown, for me, is how those options will play with the default serialization used all around in this codebase (IIRC you should instruct the Jackson mapper to have the desired behavior). All in all, I’m happy to listen to proposals and review POCs or WIPs in this direction as I do not have, at the moment, a compelling use case to reason about/spend time on.

I get what you mean. I don’t have capacity to propose something now unfortunately, but I’ll come back to this in the future. I also have just got started with using the library and implementing operators, so I bet I have lots of things to learn before I can make a good proposal.

But thanks for being open for it!

Read more comments on GitHub >

github_iconTop Results From Across the Web

Quarkus is not handling annontation @JsonSubTypes properly
For @JsonSubTypes I have an app that defines the annotation as @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.
Read more >
InvalidSchemaException using JsonSubTypes, not picking up ...
What is happening is that when doing the validation it is only looking at what CatTopic is Implementing which only contains of of...
Read more >
Make Swagger UI usable even with class inheritance ... - Wei He
It is of course no go in Java. My workaround was to modify the generated api-docs manually, but I had to give up...
Read more >
com.fasterxml.jackson.annotation.JsonSubTypes Java Exaples
This page shows Java code examples of com.fasterxml.jackson.annotation.JsonSubTypes.
Read more >
JsonTypeInfo (Jackson-annotations 2.6.0 API) - FasterXML
ImplClass") as JSON property "class" @JsonTypeInfo(use=Id.CLASS, ... type identifier is completely handled by Jackson, and is not passed to deserializers.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found