alexa / alexa-skills-kit-sdk-for-java

The Alexa Skills Kit SDK for Java helps you get a skill up and running quickly, letting you focus on skill logic instead of boilerplate code.
http://developer.amazon.com/ask
Apache License 2.0
815 stars 748 forks source link

Deserialization of non-published Requests fails #326

Open kkocel opened 2 years ago

kkocel commented 2 years ago

I'm submitting a...

[ ] Regression (a behavior that used to work and stopped working in a new release)
[X] Bug report  
[ ] Performance issue
[ ] Feature request
[ ] Documentation issue or request
[ ] Other... Please describe:

Expected Behavior

When dealing with a Request that is not yet present in the official SDK there should be a fallback to generic Request containing only common fields.

Current Behavior

Currently, SDK fails with deserialization error.

Steps to Reproduce (for bugs)

Call an API endpoint with a request that is not present in the SDK.

Possible Solution

Provide defaultImpl in @JsonTypeInfo annotation for Request class:

before:

package com.amazon.ask.model;

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type", visible = true )
@JsonSubTypes({
  @JsonSubTypes.Type(value = com.amazon.ask.model.events.skillevents.SkillEnabledRequest.class, name = "AlexaSkillEvent.SkillEnabled"),
  // ...
  @JsonSubTypes.Type(value = com.amazon.ask.model.interfaces.alexa.presentation.apla.RuntimeErrorEvent.class, name = "Alexa.Presentation.APLA.RuntimeError"),
})
public abstract class Request {

after:

package com.amazon.ask.model;

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type", visible = true, defaultImpl = GenericRequest::class)
@JsonSubTypes({
  @JsonSubTypes.Type(value = com.amazon.ask.model.events.skillevents.SkillEnabledRequest.class, name = "AlexaSkillEvent.SkillEnabled"),
  // ...
  @JsonSubTypes.Type(value = com.amazon.ask.model.interfaces.alexa.presentation.apla.RuntimeErrorEvent.class, name = "Alexa.Presentation.APLA.RuntimeError"),
})
public abstract class Request {
// ..
}

@JsonDeserialize(
        builder = GenericRequest.Builder.class
)
public class GenericRequest extends Request {

    public static GenericRequest.Builder builder() {
        return new GenericRequest.Builder();
    }

    private GenericRequest(GenericRequest.Builder builder) {
        if (builder.requestId != null) {
            this.requestId = builder.requestId;
        }

        if (builder.timestamp != null) {
            this.timestamp = builder.timestamp;
        }

        if (builder.type != null) {
            this.type = builder.type;
        }

        if (builder.locale != null) {
            this.locale = builder.locale;
        }
    }

    public static class Builder {
        private String type;
        private String requestId;
        private OffsetDateTime timestamp;
        private String locale;

        private Builder() {
        }

        @JsonProperty("requestId")
        public GenericRequest.Builder withRequestId(String requestId) {
            this.requestId = requestId;
            return this;
        }

        @JsonProperty("timestamp")
        public GenericRequest.Builder withTimestamp(OffsetDateTime timestamp) {
            this.timestamp = timestamp;
            return this;
        }

        @JsonProperty("type")
        public GenericRequest.Builder withType(String type) {
            this.type = type;
            return this;
        }

        @JsonProperty("locale")
        public GenericRequest.Builder withLocale(String locale) {
            this.locale = locale;
            return this;
        }

        public GenericRequest build() {
            return new GenericRequest(this);
        }
    }
}

Context

I was working on a private beta and had requests that were not present in the SDK.