mirror of https://github.com/halo-dev/halo
Create unstructured Extension which can store any Extensions (#2111)
* Extract ExtensionOperator and MetadataOperator * Move groupVersionKind methods up to ExtensionOperator interface * Add Unstructured Extension for generic Extension * Refine mapping of GVK and Scheme * Add two compatible methodspull/2119/head
parent
0f4ae08fd8
commit
b5d7f194ef
|
@ -1,6 +1,5 @@
|
|||
package run.halo.app.extension;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
|
@ -11,33 +10,10 @@ import lombok.Data;
|
|||
@Data
|
||||
public abstract class AbstractExtension implements Extension {
|
||||
|
||||
@Schema(required = true)
|
||||
private String apiVersion;
|
||||
|
||||
@Schema(required = true)
|
||||
private String kind;
|
||||
|
||||
@Schema(required = true)
|
||||
private Metadata metadata;
|
||||
private MetadataOperator metadata;
|
||||
|
||||
@Override
|
||||
public void groupVersionKind(GroupVersionKind gvk) {
|
||||
this.apiVersion = gvk.groupVersion().toString();
|
||||
this.kind = gvk.kind();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GroupVersionKind groupVersionKind() {
|
||||
return GroupVersionKind.fromAPIVersionAndKind(this.apiVersion, this.kind);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void metadata(Metadata metadata) {
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Metadata metadata() {
|
||||
return this.metadata;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ public class DefaultExtensionClient implements ExtensionClient {
|
|||
|
||||
@Override
|
||||
public <E extends Extension> void create(E extension) {
|
||||
extension.metadata().setCreationTimestamp(Instant.now());
|
||||
extension.getMetadata().setCreationTimestamp(Instant.now());
|
||||
var extensionStore = converter.convertTo(extension);
|
||||
storeClient.create(extensionStore.getName(), extensionStore.getData());
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ public class DefaultExtensionClient implements ExtensionClient {
|
|||
@Override
|
||||
public <E extends Extension> void update(E extension) {
|
||||
var extensionStore = converter.convertTo(extension);
|
||||
Assert.notNull(extension.metadata().getVersion(),
|
||||
Assert.notNull(extension.getMetadata().getVersion(),
|
||||
"Extension version must not be null when updating");
|
||||
storeClient.update(extensionStore.getName(), extensionStore.getVersion(),
|
||||
extensionStore.getData());
|
||||
|
|
|
@ -4,34 +4,6 @@ package run.halo.app.extension;
|
|||
* Extension is an interface which represents an Extension. It contains setters and getters of
|
||||
* GroupVersionKind and Metadata.
|
||||
*/
|
||||
public interface Extension {
|
||||
|
||||
/**
|
||||
* Sets GroupVersionKind of the Extension.
|
||||
*
|
||||
* @param gvk is GroupVersionKind data.
|
||||
*/
|
||||
void groupVersionKind(GroupVersionKind gvk);
|
||||
|
||||
/**
|
||||
* Gets GroupVersionKind of the Extension.
|
||||
*
|
||||
* @return GroupVersionKind of the Extension.
|
||||
*/
|
||||
GroupVersionKind groupVersionKind();
|
||||
|
||||
/**
|
||||
* Sets metadata of the Extension.
|
||||
*
|
||||
* @param metadata metadata of the Extension.
|
||||
*/
|
||||
void metadata(Metadata metadata);
|
||||
|
||||
/**
|
||||
* Gets metadata of the Extension.
|
||||
*
|
||||
* @return metadata of the Extension.
|
||||
*/
|
||||
Metadata metadata();
|
||||
public interface Extension extends ExtensionOperator {
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
package run.halo.app.extension;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
/**
|
||||
* ExtensionOperator contains some getters and setters for required fields of Extension.
|
||||
*
|
||||
* @author johnniang
|
||||
*/
|
||||
public interface ExtensionOperator {
|
||||
|
||||
@Schema(required = true)
|
||||
@JsonProperty("apiVersion")
|
||||
String getApiVersion();
|
||||
|
||||
@Schema(required = true)
|
||||
@JsonProperty("kind")
|
||||
String getKind();
|
||||
|
||||
@Schema(required = true, implementation = Metadata.class)
|
||||
@JsonProperty("metadata")
|
||||
MetadataOperator getMetadata();
|
||||
|
||||
void setApiVersion(String apiVersion);
|
||||
|
||||
void setKind(String kind);
|
||||
|
||||
void setMetadata(MetadataOperator metadata);
|
||||
|
||||
/**
|
||||
* This method is only for backward compatibility. Same as {@link #getMetadata()}.
|
||||
*
|
||||
* @return Extension metadata.
|
||||
* @see #getMetadata()
|
||||
*/
|
||||
@JsonIgnore
|
||||
@Deprecated(forRemoval = true)
|
||||
default MetadataOperator metadata() {
|
||||
return getMetadata();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is only for backward compatibility. Same as
|
||||
* {@link #setMetadata(MetadataOperator)}.
|
||||
*
|
||||
* @param metadata is Extension metadata.
|
||||
* @see #setMetadata(MetadataOperator)
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
default void metadata(MetadataOperator metadata) {
|
||||
setMetadata(metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets GroupVersionKind of the Extension.
|
||||
*
|
||||
* @param gvk is GroupVersionKind data.
|
||||
*/
|
||||
default void groupVersionKind(GroupVersionKind gvk) {
|
||||
setApiVersion(gvk.groupVersion().toString());
|
||||
setKind(gvk.kind());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets GroupVersionKind of the Extension.
|
||||
*
|
||||
* @return GroupVersionKind of the Extension.
|
||||
*/
|
||||
@JsonIgnore
|
||||
default GroupVersionKind groupVersionKind() {
|
||||
return GroupVersionKind.fromAPIVersionAndKind(getApiVersion(), getKind());
|
||||
}
|
||||
|
||||
}
|
|
@ -33,8 +33,9 @@ public class JSONExtensionConverter implements ExtensionConverter {
|
|||
|
||||
@Override
|
||||
public <E extends Extension> ExtensionStore convertTo(E extension) {
|
||||
var scheme = Schemes.INSTANCE.get(extension.getClass());
|
||||
var storeName = ExtensionUtil.buildStoreName(scheme, extension.metadata().getName());
|
||||
var gvk = extension.groupVersionKind();
|
||||
var scheme = Schemes.INSTANCE.get(gvk);
|
||||
var storeName = ExtensionUtil.buildStoreName(scheme, extension.getMetadata().getName());
|
||||
try {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("JSON schema({}): {}", scheme.type(),
|
||||
|
@ -56,7 +57,7 @@ public class JSONExtensionConverter implements ExtensionConverter {
|
|||
|
||||
// keep converting
|
||||
var data = objectMapper.writeValueAsBytes(extensionNode);
|
||||
var version = extension.metadata().getVersion();
|
||||
var version = extension.getMetadata().getVersion();
|
||||
return new ExtensionStore(storeName, data, version);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new ExtensionConvertException("Failed write Extension as bytes", e);
|
||||
|
@ -67,7 +68,7 @@ public class JSONExtensionConverter implements ExtensionConverter {
|
|||
public <E extends Extension> E convertFrom(Class<E> type, ExtensionStore extensionStore) {
|
||||
try {
|
||||
var extension = objectMapper.readValue(extensionStore.getData(), type);
|
||||
extension.metadata().setVersion(extensionStore.getVersion());
|
||||
extension.getMetadata().setVersion(extensionStore.getVersion());
|
||||
return extension;
|
||||
} catch (IOException e) {
|
||||
throw new ExtensionConvertException("Failed to read Extension " + type + " from bytes",
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package run.halo.app.extension;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
import lombok.Data;
|
||||
|
@ -11,42 +10,36 @@ import lombok.Data;
|
|||
* @author johnniang
|
||||
*/
|
||||
@Data
|
||||
public class Metadata {
|
||||
public class Metadata implements MetadataOperator {
|
||||
|
||||
/**
|
||||
* Metadata name. The name is unique globally.
|
||||
*/
|
||||
@Schema(required = true)
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* Labels are like key-value format.
|
||||
*/
|
||||
@Schema(nullable = true)
|
||||
private Map<String, String> labels;
|
||||
|
||||
/**
|
||||
* Annotations are like key-value format.
|
||||
*/
|
||||
@Schema(nullable = true)
|
||||
private Map<String, String> annotations;
|
||||
|
||||
/**
|
||||
* Current version of the Extension. It will be bumped up every update.
|
||||
*/
|
||||
@Schema(nullable = true)
|
||||
private Long version;
|
||||
|
||||
/**
|
||||
* Creation timestamp of the Extension.
|
||||
*/
|
||||
@Schema(nullable = true)
|
||||
private Instant creationTimestamp;
|
||||
|
||||
/**
|
||||
* Deletion timestamp of the Extension.
|
||||
*/
|
||||
@Schema(nullable = true)
|
||||
private Instant deletionTimestamp;
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
package run.halo.app.extension;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* MetadataOperator contains some getters and setters for required fields of metadata.
|
||||
*
|
||||
* @author johnniang
|
||||
*/
|
||||
@JsonDeserialize(as = Metadata.class)
|
||||
@Schema(implementation = Metadata.class)
|
||||
public interface MetadataOperator {
|
||||
|
||||
@Schema(required = true)
|
||||
@JsonProperty("name")
|
||||
String getName();
|
||||
|
||||
@Schema(nullable = true)
|
||||
@JsonProperty("labels")
|
||||
Map<String, String> getLabels();
|
||||
|
||||
@Schema(nullable = true)
|
||||
@JsonProperty("annotations")
|
||||
Map<String, String> getAnnotations();
|
||||
|
||||
@Schema(nullable = true)
|
||||
@JsonProperty("version")
|
||||
Long getVersion();
|
||||
|
||||
@Schema(nullable = true)
|
||||
@JsonProperty("creationTimestamp")
|
||||
Instant getCreationTimestamp();
|
||||
|
||||
@Schema(nullable = true)
|
||||
@JsonProperty("deletionTimestamp")
|
||||
Instant getDeletionTimestamp();
|
||||
|
||||
void setName(String name);
|
||||
|
||||
void setLabels(Map<String, String> labels);
|
||||
|
||||
void setAnnotations(Map<String, String> annotations);
|
||||
|
||||
void setVersion(Long version);
|
||||
|
||||
void setCreationTimestamp(Instant creationTimestamp);
|
||||
|
||||
void setDeletionTimestamp(Instant deletionTimestamp);
|
||||
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package run.halo.app.extension;
|
||||
|
||||
import com.github.victools.jsonschema.generator.Option;
|
||||
import com.github.victools.jsonschema.generator.OptionPreset;
|
||||
import com.github.victools.jsonschema.generator.SchemaGenerator;
|
||||
import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
|
||||
|
@ -39,23 +40,22 @@ public enum Schemes {
|
|||
/**
|
||||
* The map mapping GroupVersionKind and type of Extension.
|
||||
*/
|
||||
private final Map<GroupVersionKind, Class<? extends Extension>> gvkToType;
|
||||
private final Map<GroupVersionKind, Scheme> gvkToScheme;
|
||||
|
||||
Schemes() {
|
||||
schemes = new HashSet<>();
|
||||
typeToScheme = new HashMap<>();
|
||||
gvkToType = new HashMap<>();
|
||||
gvkToScheme = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear registered schemes.
|
||||
* <p>
|
||||
* This method is only for test.
|
||||
*/
|
||||
void clear() {
|
||||
schemes.clear();
|
||||
typeToScheme.clear();
|
||||
gvkToType.clear();
|
||||
gvkToScheme.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -74,10 +74,17 @@ public enum Schemes {
|
|||
type.getName()));
|
||||
}
|
||||
|
||||
// TODO Move the generation logic outside.
|
||||
// generate JSON schema
|
||||
var module = new Swagger2Module();
|
||||
var config =
|
||||
new SchemaGeneratorConfigBuilder(SchemaVersion.DRAFT_2019_09, OptionPreset.PLAIN_JSON)
|
||||
.with(
|
||||
// See https://victools.github.io/jsonschema-generator/#generator-options
|
||||
// fore more.
|
||||
Option.INLINE_ALL_SCHEMAS,
|
||||
Option.MAP_VALUES_AS_ADDITIONAL_PROPERTIES
|
||||
)
|
||||
.with(module)
|
||||
.build();
|
||||
var generator = new SchemaGenerator(config);
|
||||
|
@ -102,7 +109,7 @@ public enum Schemes {
|
|||
return;
|
||||
}
|
||||
typeToScheme.put(scheme.type(), scheme);
|
||||
gvkToType.put(scheme.groupVersionKind(), scheme.type());
|
||||
gvkToScheme.put(scheme.groupVersionKind(), scheme);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -116,6 +123,10 @@ public enum Schemes {
|
|||
}
|
||||
|
||||
|
||||
public Optional<Scheme> fetch(GroupVersionKind gvk) {
|
||||
return Optional.ofNullable(gvkToScheme.get(gvk));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a scheme using Extension type.
|
||||
*
|
||||
|
@ -128,4 +139,9 @@ public enum Schemes {
|
|||
"Scheme was not found for Extension " + type.getSimpleName()));
|
||||
}
|
||||
|
||||
public Scheme get(GroupVersionKind gvk) {
|
||||
return fetch(gvk).orElseThrow(() -> new SchemeNotFoundException(
|
||||
"Scheme was not found for GVK " + gvk));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
package run.halo.app.extension;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
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.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Unstructured is a generic Extension, which wraps ObjectNode to maintain the Extension data, like
|
||||
* apiVersion, kind, metadata and others.
|
||||
*
|
||||
* @author johnniang
|
||||
*/
|
||||
@JsonSerialize(using = Unstructured.UnstructuredSerializer.class)
|
||||
@JsonDeserialize(using = Unstructured.UnstructuredDeserializer.class)
|
||||
public class Unstructured implements Extension {
|
||||
|
||||
public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||
|
||||
static {
|
||||
OBJECT_MAPPER.registerModule(new JavaTimeModule());
|
||||
}
|
||||
|
||||
private final ObjectNode extension;
|
||||
|
||||
public Unstructured() {
|
||||
this(OBJECT_MAPPER.createObjectNode());
|
||||
}
|
||||
|
||||
public Unstructured(ObjectNode extension) {
|
||||
this.extension = extension;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getApiVersion() {
|
||||
return extension.get("apiVersion").asText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKind() {
|
||||
return extension.get("kind").asText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetadataOperator getMetadata() {
|
||||
var metaMap = extension.get("metadata");
|
||||
return OBJECT_MAPPER.convertValue(metaMap, Metadata.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApiVersion(String apiVersion) {
|
||||
extension.put("apiVersion", apiVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKind(String kind) {
|
||||
extension.put("kind", kind);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMetadata(MetadataOperator metadata) {
|
||||
JsonNode metaNode = OBJECT_MAPPER.valueToTree(metadata);
|
||||
extension.set("metadata", metaNode);
|
||||
}
|
||||
|
||||
ObjectNode getExtension() {
|
||||
return extension;
|
||||
}
|
||||
|
||||
// TODO Add other convenient methods here to set and get nested fields in the future.
|
||||
|
||||
public static class UnstructuredSerializer extends JsonSerializer<Unstructured> {
|
||||
|
||||
@Override
|
||||
public void serialize(Unstructured value, JsonGenerator gen, SerializerProvider serializers)
|
||||
throws IOException {
|
||||
gen.writeTree(value.extension);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class UnstructuredDeserializer extends JsonDeserializer<Unstructured> {
|
||||
|
||||
@Override
|
||||
public Unstructured deserialize(JsonParser p, DeserializationContext ctxt)
|
||||
throws IOException {
|
||||
return new Unstructured(p.getCodec().readTree(p));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -45,7 +45,7 @@ class AbstractExtensionTest {
|
|||
|
||||
Metadata metadata = new Metadata();
|
||||
metadata.setName("fake");
|
||||
extension.metadata(metadata);
|
||||
extension.setMetadata(metadata);
|
||||
|
||||
assertEquals(metadata, extension.getMetadata());
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import static org.mockito.Mockito.times;
|
|||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
|
@ -58,11 +59,35 @@ class DefaultExtensionClientTest {
|
|||
}
|
||||
|
||||
ExtensionStore createExtensionStore(String name) {
|
||||
return createExtensionStore(name, null);
|
||||
}
|
||||
|
||||
ExtensionStore createExtensionStore(String name, Long version) {
|
||||
var extensionStore = new ExtensionStore();
|
||||
extensionStore.setName(name);
|
||||
extensionStore.setVersion(version);
|
||||
return extensionStore;
|
||||
}
|
||||
|
||||
Unstructured createUnstructured() throws JsonProcessingException {
|
||||
String extensionJson = """
|
||||
{
|
||||
"apiVersion": "fake.halo.run/v1alpha1",
|
||||
"kind": "Fake",
|
||||
"metadata": {
|
||||
"labels": {
|
||||
"category": "fake",
|
||||
"default": "true"
|
||||
},
|
||||
"name": "fake",
|
||||
"creationTimestamp": "2011-12-03T10:15:30Z",
|
||||
"version": 12345
|
||||
}
|
||||
}
|
||||
""";
|
||||
return Unstructured.OBJECT_MAPPER.readValue(extensionJson, Unstructured.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldThrowSchemeNotFoundExceptionWhenSchemeNotRegistered() {
|
||||
class UnRegisteredExtension extends AbstractExtension {
|
||||
|
@ -205,8 +230,24 @@ class DefaultExtensionClientTest {
|
|||
|
||||
client.create(fake);
|
||||
|
||||
verify(converter, times(1)).convertTo(any());
|
||||
verify(storeClient, times(1)).create(any(), any());
|
||||
verify(converter, times(1)).convertTo(eq(fake));
|
||||
verify(storeClient, times(1)).create(eq("/registry/fake.halo.run/fakes/fake"), any());
|
||||
assertNotNull(fake.getMetadata().getCreationTimestamp());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateUsingUnstructuredSuccessfully() throws JsonProcessingException {
|
||||
var fake = createUnstructured();
|
||||
|
||||
when(converter.convertTo(any())).thenReturn(
|
||||
createExtensionStore("/registry/fake.halo.run/fakes/fake"));
|
||||
when(storeClient.create(any(), any())).thenReturn(
|
||||
createExtensionStore("/registry/fake.halo.run/fakes/fake"));
|
||||
|
||||
client.create(fake);
|
||||
|
||||
verify(converter, times(1)).convertTo(eq(fake));
|
||||
verify(storeClient, times(1)).create(eq("/registry/fake.halo.run/fakes/fake"), any());
|
||||
assertNotNull(fake.getMetadata().getCreationTimestamp());
|
||||
}
|
||||
|
||||
|
@ -214,14 +255,30 @@ class DefaultExtensionClientTest {
|
|||
void shouldUpdateSuccessfully() {
|
||||
var fake = createFakeExtension("fake", 2L);
|
||||
when(converter.convertTo(any())).thenReturn(
|
||||
createExtensionStore("/registry/fake.halo.run/fakes/fake"));
|
||||
createExtensionStore("/registry/fake.halo.run/fakes/fake", 2L));
|
||||
when(storeClient.update(any(), any(), any())).thenReturn(
|
||||
createExtensionStore("/registry/fake.halo.run/fakes/fake"));
|
||||
createExtensionStore("/registry/fake.halo.run/fakes/fake", 2L));
|
||||
|
||||
client.update(fake);
|
||||
|
||||
verify(converter, times(1)).convertTo(any());
|
||||
verify(storeClient, times(1)).update(any(), any(), any());
|
||||
verify(converter, times(1)).convertTo(eq(fake));
|
||||
verify(storeClient, times(1))
|
||||
.update(eq("/registry/fake.halo.run/fakes/fake"), eq(2L), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUpdateUnstructuredSuccessfully() throws JsonProcessingException {
|
||||
var fake = createUnstructured();
|
||||
when(converter.convertTo(any())).thenReturn(
|
||||
createExtensionStore("/registry/fake.halo.run/fakes/fake", 12345L));
|
||||
when(storeClient.update(any(), any(), any())).thenReturn(
|
||||
createExtensionStore("/registry/fake.halo.run/fakes/fake", 12345L));
|
||||
|
||||
client.update(fake);
|
||||
|
||||
verify(converter, times(1)).convertTo(eq(fake));
|
||||
verify(storeClient, times(1))
|
||||
.update(eq("/registry/fake.halo.run/fakes/fake"), eq(12345L), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -87,7 +87,7 @@ class JSONExtensionConverterTest {
|
|||
Metadata metadata = new Metadata();
|
||||
metadata.setName(name);
|
||||
metadata.setVersion(version);
|
||||
fake.metadata(metadata);
|
||||
fake.setMetadata(metadata);
|
||||
|
||||
return fake;
|
||||
}
|
||||
|
|
|
@ -3,11 +3,13 @@ package run.halo.app.extension;
|
|||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static run.halo.app.extension.GroupVersionKind.fromAPIVersionAndKind;
|
||||
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import run.halo.app.extension.exception.ExtensionException;
|
||||
import run.halo.app.extension.exception.SchemeNotFoundException;
|
||||
|
||||
class SchemesTest {
|
||||
|
||||
|
@ -35,6 +37,13 @@ class SchemesTest {
|
|||
void shouldFetchNothingWhenUnregistered() {
|
||||
var scheme = Schemes.INSTANCE.fetch(FakeExtension.class);
|
||||
assertEquals(Optional.empty(), scheme);
|
||||
assertThrows(SchemeNotFoundException.class,
|
||||
() -> Schemes.INSTANCE.get(FakeExtension.class));
|
||||
|
||||
var gvk = fromAPIVersionAndKind("fake.halo.run/v1alpha1", "Fake");
|
||||
scheme = Schemes.INSTANCE.fetch(gvk);
|
||||
assertEquals(Optional.empty(), scheme);
|
||||
assertThrows(SchemeNotFoundException.class, () -> Schemes.INSTANCE.get(gvk));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -43,5 +52,8 @@ class SchemesTest {
|
|||
|
||||
var scheme = Schemes.INSTANCE.fetch(FakeExtension.class);
|
||||
assertTrue(scheme.isPresent());
|
||||
|
||||
scheme = Schemes.INSTANCE.fetch(fromAPIVersionAndKind("fake.halo.run/v1alpha1", "Fake"));
|
||||
assertTrue(scheme.isPresent());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package run.halo.app.extension;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class UnstructuredTest {
|
||||
|
||||
ObjectMapper objectMapper = Unstructured.OBJECT_MAPPER;
|
||||
|
||||
String extensionJson = """
|
||||
{
|
||||
"apiVersion": "fake.halo.run/v1alpha1",
|
||||
"kind": "Fake",
|
||||
"metadata": {
|
||||
"labels": {
|
||||
"category": "fake",
|
||||
"default": "true"
|
||||
},
|
||||
"name": "fake-extension",
|
||||
"creationTimestamp": "2011-12-03T10:15:30Z",
|
||||
"version": 12345
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
@BeforeAll
|
||||
static void setUpGlobally() {
|
||||
Schemes.INSTANCE.register(FakeExtension.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSerializeCorrectly() throws JsonProcessingException {
|
||||
var extensionNode = (ObjectNode) objectMapper.readTree(extensionJson);
|
||||
var extension = new Unstructured(extensionNode);
|
||||
|
||||
var gotNode = objectMapper.valueToTree(extension);
|
||||
assertEquals(extensionNode, gotNode);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldDeserializeCorrectly() throws JsonProcessingException {
|
||||
var extension = objectMapper.readValue(extensionJson, Unstructured.class);
|
||||
var wantJsonNode = objectMapper.readTree(extensionJson);
|
||||
assertEquals(wantJsonNode, extension.getExtension());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGetExtensionCorrectly() throws JsonProcessingException {
|
||||
var extension = objectMapper.readValue(extensionJson, Unstructured.class);
|
||||
|
||||
assertEquals("fake.halo.run/v1alpha1", extension.getApiVersion());
|
||||
assertEquals("Fake", extension.getKind());
|
||||
assertEquals(createMetadata(), extension.getMetadata());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSetExtensionCorrectly() {
|
||||
var extension = new Unstructured();
|
||||
extension.setApiVersion("fake.halo.run/v1alpha1");
|
||||
extension.setKind("Fake");
|
||||
extension.setMetadata(createMetadata());
|
||||
|
||||
assertEquals("fake.halo.run/v1alpha1", extension.getApiVersion());
|
||||
assertEquals("Fake", extension.getKind());
|
||||
assertEquals(createMetadata(), extension.getMetadata());
|
||||
}
|
||||
|
||||
private Metadata createMetadata() {
|
||||
var metadata = new Metadata();
|
||||
metadata.setName("fake-extension");
|
||||
metadata.setLabels(Map.of("category", "fake", "default", "true"));
|
||||
metadata.setCreationTimestamp(Instant.parse("2011-12-03T10:15:30Z"));
|
||||
metadata.setVersion(12345L);
|
||||
return metadata;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue