Skip to content

Commit

Permalink
fix fabric8io#5009: ensuring underlying serialization is used
Browse files Browse the repository at this point in the history
  • Loading branch information
shawkins authored and manusa committed Apr 4, 2023
1 parent d15341c commit 84dbf24
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 12 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* Fix #4963: Openshift Client return 403 when use websocket
* Fix #4985: triggering the immediate cleanup of the okhttp idle task
* fix #5002: Jetty response completion accounts for header processing
* Fix #5009: addressing issue with serialization of wrapped polymophic types

#### Improvements
* Fix #4434: Update CronJobIT to use `batch/v1` CronJob instead
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/**
* Copyright (C) 2015 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.fabric8.kubernetes.client.utils;

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeName;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import io.fabric8.kubernetes.api.model.KubernetesResource;
import io.fabric8.kubernetes.api.model.Namespaced;
import io.fabric8.kubernetes.client.CustomResource;
import io.fabric8.kubernetes.model.annotation.Group;
import io.fabric8.kubernetes.model.annotation.Version;
import lombok.Data;
import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;

class SerializationWrappedPolymorphicTest {

@Group("example.com")
@Version("v1alpha1")
static class TestCR extends CustomResource<TestCR.TestCRSpec, Void> implements Namespaced {

@JsonDeserialize(using = JsonDeserializer.None.class)
static class TestCRSpec implements KubernetesResource {

@JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();

private List<Child> children = new ArrayList<>();

public List<Child> getChildren() {
return children;
}

public void setChildren(List<Child> children) {
this.children = children;
}

@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}

@JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}

@Override
protected TestCRSpec initSpec() {
return new TestCRSpec();
}
}

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
@JsonSubTypes({ //
@JsonSubTypes.Type(FooChild.class), //
@JsonSubTypes.Type(BarChild.class), //
})
@JsonDeserialize(using = JsonDeserializer.None.class)
interface Child extends KubernetesResource {
}

@JsonTypeName("foo")
@Data
static class FooChild implements Child {
private String name;
}

@JsonTypeName("bar")
@Data
static class BarChild implements Child {
private String file;
}

@Test
void testPolyRoundTrip() {
TestCR original = new TestCR();
FooChild foo = new FooChild();
foo.setName("alice");
BarChild bar = new BarChild();
bar.setFile("bob");
original.getSpec().setChildren(Arrays.asList(foo, bar));
String json = Serialization.asJson(original);
assertThat(json).isEqualTo(
"{\"apiVersion\":\"example.com/v1alpha1\",\"kind\":\"TestCR\",\"metadata\":{},\"spec\":{\"children\":[{\"foo\":{\"name\":\"alice\"}},{\"bar\":{\"file\":\"bob\"}}]}}");
TestCR deserialized = Serialization.unmarshal(json, TestCR.class);
assertEquals(deserialized.getSpec().getChildren(), original.getSpec().getChildren());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -42,31 +42,27 @@ public class BeanPropertyWriterDelegate extends BeanPropertyWriter {

private static final Logger logger = LoggerFactory.getLogger(BeanPropertyWriterDelegate.class);

private final BeanPropertyWriter delegate;
private final AnnotatedMember anyGetter;
private final transient Supplier<Boolean> logDuplicateWarning;

BeanPropertyWriterDelegate(BeanPropertyWriter delegate, AnnotatedMember anyGetter, Supplier<Boolean> logDuplicateWarning) {
super(delegate);
this.delegate = delegate;
this.anyGetter = anyGetter;
this.logDuplicateWarning = logDuplicateWarning;
}

@Override
public void serializeAsField(Object bean, JsonGenerator gen, SerializerProvider prov) throws Exception {
Object valueInAnyGetter = null;
if (anyGetter != null) {
Object anyGetterValue = anyGetter.getValue(bean);
if (anyGetterValue != null) {
valueInAnyGetter = ((Map<?, ?>) anyGetterValue).get(delegate.getName());
}
Object anyGetterValue = anyGetter.getValue(bean);
if (anyGetterValue != null) {
valueInAnyGetter = ((Map<?, ?>) anyGetterValue).get(getName());
}
if (valueInAnyGetter == null) {
delegate.serializeAsField(bean, gen, prov);
super.serializeAsField(bean, gen, prov);
} else if (Boolean.TRUE.equals(logDuplicateWarning.get())) {
logger.warn("Value in field '{}' ignored in favor of value in additionalProperties ({}) for {}",
delegate.getName(), valueInAnyGetter, bean.getClass().getName());
getName(), valueInAnyGetter, bean.getClass().getName());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,6 @@ public Object setAndReturn(Object instance, Object value) throws IOException {
}

private boolean shouldUseAnySetter() {
if (anySetter == null) {
return false;
}
return useAnySetter.getAsBoolean();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ public UnmatchedFieldTypeModule(boolean logWarnings, boolean restrictToTemplates
@Override
public BeanDeserializerBuilder updateBuilder(DeserializationConfig config, BeanDescription beanDesc,
BeanDeserializerBuilder builder) {
if (builder.getAnySetter() == null) {
return builder;
}
builder.getProperties().forEachRemaining(p -> builder.addOrReplaceProperty(
new SettableBeanPropertyDelegate(p, builder.getAnySetter(), UnmatchedFieldTypeModule.this::useAnySetter) {
}, true));
Expand All @@ -64,6 +67,9 @@ public BeanDeserializerBuilder updateBuilder(DeserializationConfig config, BeanD
@Override
public BeanSerializerBuilder updateBuilder(SerializationConfig config, BeanDescription beanDesc,
BeanSerializerBuilder builder) {
if (builder.getBeanDescription().findAnyGetter() == null) {
return builder;
}
builder.setProperties(builder.getProperties().stream()
.map(p -> new BeanPropertyWriterDelegate(p, builder.getBeanDescription().findAnyGetter(),
UnmatchedFieldTypeModule.this::isLogWarnings))
Expand Down

0 comments on commit 84dbf24

Please sign in to comment.