Skip to content

Commit

Permalink
fix #4289: adding an AnyType base mapping that can be any value
Browse files Browse the repository at this point in the history
  • Loading branch information
shawkins authored and manusa committed Oct 5, 2022
1 parent 60c2800 commit 15f8335
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 155 deletions.
Expand Up @@ -23,7 +23,6 @@
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import io.fabric8.kubernetes.api.model.KubernetesResource;
import io.fabric8.kubernetes.api.model.runtime.RawExtension;
import io.fabric8.kubernetes.client.KubernetesClientException;
import io.fabric8.kubernetes.model.jackson.UnmatchedFieldTypeModule;
import org.yaml.snakeyaml.DumperOptions;
Expand Down Expand Up @@ -418,35 +417,6 @@ public static <T> T clone(T resource) {
}
}

/**
* Convert any object to a KubernetesResource, if it is not already.
*
* @param resource
* @return a {@link KubernetesResource}, which may be a {@link RawExtension}
*/
public static KubernetesResource asKubernetesResource(Object resource) {
if (resource instanceof KubernetesResource || resource == null) {
return (KubernetesResource) resource;
}
return new RawExtension(asMap(resource));
}

/**
* Convert any object to a Map.
*
* @param resource
*/
public static Map<String, Object> asMap(Object resource) {
Map<String, Object> value = null;
if (resource instanceof Map && ((Map) resource).keySet().stream().allMatch(String.class::isInstance)) {
value = (Map<String, Object>) resource;
} else {
value = JSON_MAPPER.convertValue(resource, new TypeReference<Map<String, Object>>() {
});
}
return value;
}

private static class CustomYamlTagResolverWithLimit extends Resolver {
@Override
public void addImplicitResolver(Tag tag, Pattern regexp, String first, int limit) {
Expand Down
Expand Up @@ -24,6 +24,7 @@
import com.fasterxml.jackson.databind.node.BooleanNode;
import com.fasterxml.jackson.databind.node.IntNode;
import com.fasterxml.jackson.databind.node.TextNode;
import io.fabric8.kubernetes.api.model.AnyType;
import io.fabric8.kubernetes.api.model.Config;
import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.GenericKubernetesResource;
Expand Down Expand Up @@ -57,6 +58,7 @@
import java.nio.file.Files;
import java.time.ZonedDateTime;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -245,7 +247,7 @@ void unmarshalWithInvalidYamlShouldThrowException() {
void unmarshalRawResource() {
InputStream is = SerializationTest.class.getResourceAsStream("/serialization/invalid-resource.yml");
RawExtension raw = Serialization.unmarshal(is);
raw.getAdditionalProperties().get("not-a").equals("resource");
((Map) raw.getValue()).get("not-a").equals("resource");
}

@Test
Expand Down Expand Up @@ -444,4 +446,30 @@ void unmarshal_configMapWithBoolAndDateData_shouldBeDeserializedAsString() {
.containsEntry("date", "2022-07-22");
}

@Test
void testAnyTypeSerialization() {
// Given
AnyType value = new AnyType("x");

// When
String yaml = Serialization.asYaml(value);

// Then
assertThat(yaml).isEqualTo("--- \"x\"\n");
}

@Test
void testAnyTypeDeserialization() {
// Given
String yaml = "x: 1";
HashMap<String, Object> map = new HashMap<>();
map.put("x", 1);

// When
AnyType type = Serialization.unmarshal(yaml, AnyType.class);

// Then
assertThat(type.getValue()).isEqualTo(map);
}

}
@@ -0,0 +1,79 @@
/**
* 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.api.model;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.sundr.builder.annotations.Buildable;
import lombok.EqualsAndHashCode;
import lombok.Setter;
import lombok.ToString;

import java.io.IOException;
import java.io.Serializable;

/**
* A holder for a value of any type - a wrapped primitive, an array, or object value.
* <p>
* It does not matter what the object type is, default serialization logic will be used.
*/
@JsonSerialize(using = AnyType.Serializer.class)
@JsonInclude(JsonInclude.Include.NON_NULL)
@ToString
@Setter
@EqualsAndHashCode
@Buildable(editableEnabled = false, validationEnabled = false, generateBuilderPackage = true, lazyCollectionInitEnabled = false, builderPackage = "io.fabric8.kubernetes.api.builder")
public class AnyType implements Serializable {

protected Object value;

public AnyType() {
}

@JsonCreator
//Builders are generated for the first non-empty constructor found.
@Buildable(editableEnabled = false, generateBuilderPackage = true, builderPackage = "io.fabric8.kubernetes.api.builder")
public AnyType(Object value) {
this.value = value;
}

/**
* Get Raw enclosed object.
* <p>
* If this instance has been deserialized, the Object will be
* composed of the default object mapping - a wrapped primitive value,
* a Map&lt;String, Object&gt;, or a List of those values
*
* @return Object value
*/
public Object getValue() {
return value;
}

public static class Serializer extends JsonSerializer<AnyType> {

@Override
public void serialize(AnyType value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
jgen.writeObject(value.value);
}
}

}
Expand Up @@ -15,60 +15,27 @@
*/
package io.fabric8.kubernetes.api.model;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.annotation.JsonCreator;
import io.sundr.builder.annotations.Buildable;
import lombok.EqualsAndHashCode;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;

import java.io.IOException;
import java.io.Serializable;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonDeserialize(using = IntOrString.Deserializer.class)
@JsonSerialize(using = IntOrString.Serializer.class)
@ToString
@EqualsAndHashCode
@Setter
@Accessors(prefix = {
"_",
""
})
public class IntOrString implements Serializable {

private Object value;
public class IntOrString extends AnyType {

public IntOrString() {
}

@JsonCreator
//Builders are generated for the first non-empty constructor found.
@Buildable(editableEnabled = false, generateBuilderPackage = true, builderPackage = "io.fabric8.kubernetes.api.builder")
public IntOrString(Object value) {
if (value == null || value instanceof Integer || value instanceof String) {
this.value = value;
} else {
throw new IllegalArgumentException("Either integer or string value needs to be provided");
}
setValue(value);
}

/**
* Get Raw enclosed object.
*
* @return Object value
*/
public Object getValue() {
return value;
@Override
public void setValue(Object value) {
if (value != null && !(value instanceof Integer) && !(value instanceof String)) {
throw new IllegalArgumentException("Either integer or string value needs to be provided");
}
super.setValue(value);
}

/**
Expand All @@ -95,43 +62,4 @@ public String getStrVal() {
return null;
}

public static class Serializer extends JsonSerializer<IntOrString> {

@Override
public void serialize(IntOrString value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
if (value != null) {
Integer intValue = value.getIntVal();
if (intValue != null) {
jgen.writeNumber(intValue);
} else {
String stringValue = value.getStrVal();
if (stringValue != null) {
jgen.writeString(stringValue);
} else {
jgen.writeNull();
}
}
} else {
jgen.writeNull();
}
}
}

public static class Deserializer extends JsonDeserializer<IntOrString> {

@Override
public IntOrString deserialize(JsonParser jsonParser, DeserializationContext ctxt)
throws IOException {
ObjectCodec oc = jsonParser.getCodec();
JsonNode node = oc.readTree(jsonParser);
IntOrString intOrString;
if (node.isInt()) {
intOrString = new IntOrString(node.asInt());
} else {
intOrString = new IntOrString(node.asText());
}
return intOrString;
}

}
}
Expand Up @@ -16,58 +16,22 @@

package io.fabric8.kubernetes.api.model.runtime;

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import io.fabric8.kubernetes.api.model.AnyType;
import io.fabric8.kubernetes.api.model.KubernetesResource;
import io.sundr.builder.annotations.Buildable;
import lombok.EqualsAndHashCode;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;

import java.util.LinkedHashMap;
import java.util.Map;

@JsonDeserialize(using = com.fasterxml.jackson.databind.JsonDeserializer.None.class)
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
"apiVersion",
"kind",
"metadata"
})
@ToString
@EqualsAndHashCode
@Setter
@Accessors(prefix = {
"_",
""
})
@Buildable(editableEnabled = false, validationEnabled = false, generateBuilderPackage = true, lazyCollectionInitEnabled = false, builderPackage = "io.fabric8.kubernetes.api.builder")
public class RawExtension implements KubernetesResource /* , Map<String, Object> */
{
public class RawExtension extends AnyType implements KubernetesResource {

public RawExtension() {
}

public RawExtension(Map<String, Object> additionalProperties) {
this.additionalProperties.putAll(additionalProperties);
}

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

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

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

}
Expand Up @@ -16,6 +16,7 @@

package io.fabric8.openshift.api.model.runtime;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
Expand Down Expand Up @@ -64,4 +65,12 @@
@Deprecated
public class RawExtension extends io.fabric8.kubernetes.api.model.runtime.RawExtension {

public RawExtension() {
}

@JsonCreator
public RawExtension(Object value) {
super(value);
}

}

0 comments on commit 15f8335

Please sign in to comment.