diff --git a/events/api/src/main/java/org/projectnessie/events/api/Content.java b/events/api/src/main/java/org/projectnessie/events/api/Content.java index 147705f420f..893bc342e56 100644 --- a/events/api/src/main/java/org/projectnessie/events/api/Content.java +++ b/events/api/src/main/java/org/projectnessie/events/api/Content.java @@ -15,28 +15,37 @@ */ package org.projectnessie.events.api; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; import java.util.Collections; import java.util.Map; import org.immutables.value.Value; -import org.projectnessie.events.api.json.ContentTypeIdResolver; -/** - * An object stored in Nessie, such as a table or a view. - * - * @see Namespace - * @see IcebergTable - * @see IcebergView - * @see DeltaLakeTable - * @see UDF - * @see GenericContent - */ -@JsonTypeIdResolver(ContentTypeIdResolver.class) -@JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM, visible = true, property = "type") +/** An object stored in Nessie, such as a table or a view. */ +@Value.Immutable +@JsonSerialize(as = ImmutableContent.class) +@JsonDeserialize(as = ImmutableContent.class) public interface Content { - ContentType getType(); + /** + * Returns the type for this object. + * + *

Values returned here match the JSON type name used for serializing the original content + * object. + * + *

The currently-known built-in content types are: + * + *

+ * + * Other content types may be added in the future, or registered by end users. + */ + String getType(); /** * Unique id for this object. diff --git a/events/api/src/main/java/org/projectnessie/events/api/ContentType.java b/events/api/src/main/java/org/projectnessie/events/api/ContentType.java deleted file mode 100644 index 9e7f0a38a16..00000000000 --- a/events/api/src/main/java/org/projectnessie/events/api/ContentType.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2023 Dremio - * - * 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 org.projectnessie.events.api; - -/** - * An enum of all possible content types. - * - *

Please note that this enum may evolve in the future, and more enum values may be added. It is - * important that SPI implementers be prepared to handle unknown enum values. - */ -public enum ContentType { - /** - * The content is a namespace. - * - * @see Namespace - */ - NAMESPACE(Namespace.class), - - /** - * The content is an Iceberg table. - * - * @see IcebergTable - */ - ICEBERG_TABLE(IcebergTable.class), - - /** - * The content is an Iceberg view. - * - * @see IcebergView - */ - ICEBERG_VIEW(IcebergView.class), - - /** - * The content is a Delta Lake table. - * - * @see DeltaLakeTable - */ - DELTA_LAKE_TABLE(DeltaLakeTable.class), - - /** - * The content is a UDF. - * - * @see UDF - */ - UDF(UDF.class), - - /** - * The content is of an unknown type. This type is a catch-all type for all other runtime types - * that are not one of the built-in types above. - * - * @see GenericContent - */ - GENERIC(GenericContent.class), - ; - - private final Class subtype; - - ContentType(Class subtype) { - this.subtype = subtype; - } - - /** Returns the subtype of {@link Content} that this enum value represents. */ - public Class getSubtype() { - return subtype; - } -} diff --git a/events/api/src/main/java/org/projectnessie/events/api/DeltaLakeTable.java b/events/api/src/main/java/org/projectnessie/events/api/DeltaLakeTable.java deleted file mode 100644 index e1c03256867..00000000000 --- a/events/api/src/main/java/org/projectnessie/events/api/DeltaLakeTable.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2023 Dremio - * - * 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 org.projectnessie.events.api; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import java.util.List; -import org.immutables.value.Value; - -/** A {@link Content} object that represents a Delta Lake table. */ -@Value.Immutable -@JsonSerialize(as = ImmutableDeltaLakeTable.class) -@JsonDeserialize(as = ImmutableDeltaLakeTable.class) -public interface DeltaLakeTable extends Content { - - @Override - @Value.Default - default ContentType getType() { - return ContentType.DELTA_LAKE_TABLE; - } - - List getMetadataLocationHistory(); - - List getCheckpointLocationHistory(); - - String getLastCheckpoint(); -} diff --git a/events/api/src/main/java/org/projectnessie/events/api/GenericContent.java b/events/api/src/main/java/org/projectnessie/events/api/GenericContent.java deleted file mode 100644 index ee2daeddd94..00000000000 --- a/events/api/src/main/java/org/projectnessie/events/api/GenericContent.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2023 Dremio - * - * 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 org.projectnessie.events.api; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import org.immutables.value.Value; -import org.projectnessie.events.api.json.GenericContentDeserializer; -import org.projectnessie.events.api.json.GenericContentSerializer; - -/** - * A generic {@link Content} whose runtime type is not known. - * - *

This special content subtype is only used when deserializing unknown content types. Since - * contents are pluggable in Nessie, this situation can happen when a custom content other than the - * built-in ones is registered server-side, but the client deserializing the content does not have - * the concrete class available on its classpath. - * - *

All properties of the original content object will be deserialized in the {@link - * #getProperties()} map. - */ -@Value.Immutable -@JsonSerialize(using = GenericContentSerializer.class) -@JsonDeserialize(using = GenericContentDeserializer.class) -public interface GenericContent extends Content { - - @Override - @Value.Default - default ContentType getType() { - return ContentType.GENERIC; - } - - /** The actual runtime type name of this content object. */ - String getGenericType(); -} diff --git a/events/api/src/main/java/org/projectnessie/events/api/IcebergTable.java b/events/api/src/main/java/org/projectnessie/events/api/IcebergTable.java deleted file mode 100644 index 6c892abe776..00000000000 --- a/events/api/src/main/java/org/projectnessie/events/api/IcebergTable.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2023 Dremio - * - * 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 org.projectnessie.events.api; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import org.immutables.value.Value; - -/** A {@link Content} object that represents an Iceberg table. */ -@Value.Immutable -@JsonSerialize(as = ImmutableIcebergTable.class) -@JsonDeserialize(as = ImmutableIcebergTable.class) -public interface IcebergTable extends Content { - - @Override - @Value.Default - default ContentType getType() { - return ContentType.ICEBERG_TABLE; - } - - /** - * Location where Iceberg stored its {@code TableMetadata} file. The location depends on the - * (implementation of) Iceberg's {@code FileIO} configured for the particular Iceberg table. - */ - String getMetadataLocation(); - - /** Corresponds to Iceberg's {@code currentSnapshotId}. */ - long getSnapshotId(); - - /** Corresponds to Iceberg's {@code currentSchemaId}. */ - int getSchemaId(); - - /** Corresponds to Iceberg's {@code defaultSpecId}. */ - int getSpecId(); - - /** Corresponds to Iceberg's {@code defaultSortOrderId}. */ - int getSortOrderId(); -} diff --git a/events/api/src/main/java/org/projectnessie/events/api/IcebergView.java b/events/api/src/main/java/org/projectnessie/events/api/IcebergView.java deleted file mode 100644 index c8e30a48081..00000000000 --- a/events/api/src/main/java/org/projectnessie/events/api/IcebergView.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2023 Dremio - * - * 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 org.projectnessie.events.api; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import java.util.Optional; -import org.immutables.value.Value; - -/** A {@link Content} object that represents an Iceberg view. */ -@Value.Immutable -@JsonSerialize(as = ImmutableIcebergView.class) -@JsonDeserialize(as = ImmutableIcebergView.class) -public interface IcebergView extends Content { - - @Override - @Value.Default - default ContentType getType() { - return ContentType.ICEBERG_VIEW; - } - - /** - * Location where Iceberg stored its {@code TableMetadata} file. The location depends on the - * (implementation of) Iceberg's {@code FileIO} configured for the particular Iceberg table. - */ - String getMetadataLocation(); - - /** Corresponds to Iceberg's {@code currentVersionId}. */ - long getVersionId(); - - int getSchemaId(); - - String getSqlText(); - - Optional getDialect(); -} diff --git a/events/api/src/main/java/org/projectnessie/events/api/Namespace.java b/events/api/src/main/java/org/projectnessie/events/api/Namespace.java deleted file mode 100644 index 42408687e9a..00000000000 --- a/events/api/src/main/java/org/projectnessie/events/api/Namespace.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2023 Dremio - * - * 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 org.projectnessie.events.api; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import java.util.List; -import java.util.stream.Collectors; -import org.immutables.value.Value; - -/** A {@link Content} object that represents a namespace. */ -@Value.Immutable -@JsonSerialize(as = ImmutableNamespace.class) -@JsonDeserialize(as = ImmutableNamespace.class) -public interface Namespace extends Content { - - @Override - @Value.Default - default ContentType getType() { - return ContentType.NAMESPACE; - } - - List getElements(); - - /** - * The simple name of the namespace. - * - *

The simple name is the last element of the namespace. For example, the simple name of - * namespace {@code ["a", "b", "c"]} is {@code "c"}. - */ - @Value.Lazy - @JsonIgnore - default String getSimpleName() { - return getElements().get(getElements().size() - 1); - } - - /** - * The full name of the namespace. - * - *

The full name is composed of all the elements of the namespace, separated by dots. For - * example, the full name of the namespace {@code ["a", "b", "c"]} is {@code "a.b.c"}. - * - *

When an element contains a dot or the NUL character (unicode {@code U+0000}), it is replaced - * by the unicode character {@code U+001D}. - */ - @Value.Lazy - @JsonIgnore - default String getName() { - // see org.projectnessie.model.Util - return getElements().stream() - .map(element -> element.replace('.', '\u001D').replace('\u0000', '\u001D')) - .collect(Collectors.joining(".")); - } -} diff --git a/events/api/src/main/java/org/projectnessie/events/api/UDF.java b/events/api/src/main/java/org/projectnessie/events/api/UDF.java deleted file mode 100644 index 926e90ee35c..00000000000 --- a/events/api/src/main/java/org/projectnessie/events/api/UDF.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2023 Dremio - * - * 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 org.projectnessie.events.api; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import java.util.Optional; -import org.immutables.value.Value; - -/** A {@link Content} object that represents a UDF. */ -@Value.Immutable -@JsonSerialize(as = ImmutableUDF.class) -@JsonDeserialize(as = ImmutableUDF.class) -public interface UDF extends Content { - - @Override - @Value.Default - default ContentType getType() { - return ContentType.UDF; - } - - String getSqlText(); - - Optional getDialect(); -} diff --git a/events/api/src/main/java/org/projectnessie/events/api/json/ContentTypeIdResolver.java b/events/api/src/main/java/org/projectnessie/events/api/json/ContentTypeIdResolver.java deleted file mode 100644 index 944e2c54d4d..00000000000 --- a/events/api/src/main/java/org/projectnessie/events/api/json/ContentTypeIdResolver.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2023 Dremio - * - * 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 org.projectnessie.events.api.json; - -import java.util.Locale; -import org.projectnessie.events.api.Content; -import org.projectnessie.events.api.ContentType; -import org.projectnessie.events.api.GenericContent; - -public final class ContentTypeIdResolver extends AbstractEnumBasedTypeIdResolver { - - @Override - public String idFromValue(Object value) { - return ((Content) value).getType().name(); - } - - @Override - protected Class subtypeFromId(String id) { - return ContentType.valueOf(id.toUpperCase(Locale.ROOT)).getSubtype(); - } - - @Override - protected Class genericSubtype() { - return GenericContent.class; - } -} diff --git a/events/api/src/main/java/org/projectnessie/events/api/json/GenericContentDeserializer.java b/events/api/src/main/java/org/projectnessie/events/api/json/GenericContentDeserializer.java deleted file mode 100644 index c789689258a..00000000000 --- a/events/api/src/main/java/org/projectnessie/events/api/json/GenericContentDeserializer.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2023 Dremio - * - * 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 org.projectnessie.events.api.json; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.deser.std.StdDeserializer; -import java.io.IOException; -import java.util.Map; -import java.util.Objects; -import org.projectnessie.events.api.GenericContent; -import org.projectnessie.events.api.ImmutableGenericContent; - -public final class GenericContentDeserializer extends StdDeserializer { - - private static final TypeReference> MAP_TYPE = - new TypeReference>() {}; - - public GenericContentDeserializer() { - super(GenericContent.class); - } - - @Override - public GenericContent deserialize(JsonParser p, DeserializationContext ctx) throws IOException { - try { - Map properties = p.readValueAs(MAP_TYPE); - Object id = Objects.requireNonNull(properties.remove("id")); - Object genericType = Objects.requireNonNull(properties.remove("type")); - return ImmutableGenericContent.builder() - .id(id.toString()) - .genericType(genericType.toString()) - .properties(properties) - .build(); - } catch (Exception e) { - throw JsonMappingException.from(ctx, "Failed to deserialize GenericContent", e); - } - } -} diff --git a/events/api/src/main/java/org/projectnessie/events/api/json/GenericContentSerializer.java b/events/api/src/main/java/org/projectnessie/events/api/json/GenericContentSerializer.java deleted file mode 100644 index 7af4bf3f907..00000000000 --- a/events/api/src/main/java/org/projectnessie/events/api/json/GenericContentSerializer.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2023 Dremio - * - * 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 org.projectnessie.events.api.json; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.jsontype.TypeSerializer; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; -import java.io.IOException; -import java.util.Map; -import org.projectnessie.events.api.GenericContent; - -public final class GenericContentSerializer extends StdSerializer { - - public GenericContentSerializer() { - super(GenericContent.class); - } - - @Override - public void serializeWithType( - GenericContent value, - JsonGenerator gen, - SerializerProvider serializers, - TypeSerializer typeSer) - throws IOException { - serialize(value, gen, serializers); - } - - @Override - public void serialize(GenericContent value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - gen.writeStartObject(); - gen.writeStringField("id", value.getId()); - gen.writeStringField("type", value.getGenericType()); - for (Map.Entry entry : value.getProperties().entrySet()) { - gen.writeFieldName(entry.getKey()); - gen.writeObject(entry.getValue()); - } - gen.writeEndObject(); - } -} diff --git a/events/api/src/test/java/org/projectnessie/events/api/TestContentType.java b/events/api/src/test/java/org/projectnessie/events/api/TestContentType.java deleted file mode 100644 index ca6f6168648..00000000000 --- a/events/api/src/test/java/org/projectnessie/events/api/TestContentType.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2023 Dremio - * - * 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 org.projectnessie.events.api; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; - -class TestContentType { - - @Test - void namespace() { - Namespace content = ImmutableNamespace.builder().id("id").addElements("name").build(); - assertThat(content.getType()).isEqualTo(ContentType.NAMESPACE); - } - - @Test - void icebergTable() { - IcebergTable content = - ImmutableIcebergTable.builder() - .id("id") - .metadataLocation("metadataLocation") - .snapshotId(1L) - .schemaId(2) - .specId(3) - .sortOrderId(4) - .build(); - assertThat(content.getType()).isEqualTo(ContentType.ICEBERG_TABLE); - } - - @Test - void deltaLakeTable() { - DeltaLakeTable content = - ImmutableDeltaLakeTable.builder().id("id").lastCheckpoint("lastCheckpoint").build(); - assertThat(content.getType()).isEqualTo(ContentType.DELTA_LAKE_TABLE); - } - - @Test - void icebergView() { - IcebergView content = - ImmutableIcebergView.builder() - .id("id") - .metadataLocation("metadataLocation") - .versionId(1L) - .schemaId(2) - .sqlText("sqlText") - .dialect("dialect") - .build(); - assertThat(content.getType()).isEqualTo(ContentType.ICEBERG_VIEW); - } - - @Test - void udf() { - UDF content = ImmutableUDF.builder().id("id").sqlText("sqlText").dialect("dialect").build(); - assertThat(content.getType()).isEqualTo(ContentType.UDF); - } - - @Test - void generic() { - GenericContent content = - ImmutableGenericContent.builder().id("id").genericType("genericType").build(); - assertThat(content.getType()).isEqualTo(ContentType.GENERIC); - } -} diff --git a/events/api/src/test/java/org/projectnessie/events/api/TestJsonSerde.java b/events/api/src/test/java/org/projectnessie/events/api/TestJsonSerde.java index 441476a1177..e0cabe5a445 100644 --- a/events/api/src/test/java/org/projectnessie/events/api/TestJsonSerde.java +++ b/events/api/src/test/java/org/projectnessie/events/api/TestJsonSerde.java @@ -236,13 +236,14 @@ void contentStored() throws Exception { .putProperty( "complex", ImmutableMap.of("string", "foo", "number", 123, "boolean", true)) .content( - ImmutableIcebergTable.builder() - .metadataLocation("metadataLocation") + ImmutableContent.builder() .id("id") - .snapshotId(1L) - .schemaId(2) - .specId(3) - .sortOrderId(4) + .type("MY_CONTENT") + .putProperty("string", "foo") + .putProperty("number", 123) + .putProperty("boolean", true) + .putProperty( + "complex", ImmutableMap.of("string", "foo", "number", 123, "boolean", true)) .build()) .build(); assertThat(deserialize(serialize(event), Event.class)).isEqualTo(event); @@ -273,83 +274,6 @@ void contentRemoved() throws Exception { assertThat(deserialize(serialize(event), Event.class)).isEqualTo(event); } - @Test - void icebergTable() throws Exception { - IcebergTable content = - ImmutableIcebergTable.builder() - .id("id") - .metadataLocation("metadataLocation") - .snapshotId(1L) - .schemaId(2) - .specId(3) - .sortOrderId(4) - .putProperty("string", "foo") - .putProperty("number", 123) - .putProperty("boolean", true) - .putProperty( - "complex", ImmutableMap.of("string", "foo", "number", 123, "boolean", true)) - .build(); - assertThat(deserialize(serialize(content), Content.class)).isEqualTo(content); - } - - @Test - void deltaLakeTable() throws Exception { - DeltaLakeTable content = - ImmutableDeltaLakeTable.builder() - .id("id") - .addCheckpointLocationHistory("checkpoint") - .addMetadataLocationHistory("metadata") - .lastCheckpoint("lastCheckpoint") - .putProperty("string", "foo") - .putProperty("number", 123) - .putProperty("boolean", true) - .putProperty( - "complex", ImmutableMap.of("string", "foo", "number", 123, "boolean", true)) - .build(); - assertThat(deserialize(serialize(content), Content.class)).isEqualTo(content); - } - - @Test - void icebergView() throws Exception { - IcebergView content = - ImmutableIcebergView.builder() - .id("id") - .metadataLocation("metadataLocation") - .versionId(1L) - .schemaId(2) - .sqlText("sqlText") - .dialect("dialect") - .putProperty("string", "foo") - .putProperty("number", 123) - .putProperty("boolean", true) - .putProperty( - "complex", ImmutableMap.of("string", "foo", "number", 123, "boolean", true)) - .build(); - assertThat(deserialize(serialize(content), Content.class)).isEqualTo(content); - } - - @Test - void udf() throws Exception { - UDF content = ImmutableUDF.builder().id("id").sqlText("sqlText").dialect("dialect").build(); - assertThat(deserialize(serialize(content), Content.class)).isEqualTo(content); - } - - @Test - void namespace() throws Exception { - Namespace content = - ImmutableNamespace.builder() - .id("id") - .addElement("level1") - .addElement("level2") - .putProperty("string", "foo") - .putProperty("number", 123) - .putProperty("boolean", true) - .putProperty( - "complex", ImmutableMap.of("string", "foo", "number", 123, "boolean", true)) - .build(); - assertThat(deserialize(serialize(content), Content.class)).isEqualTo(content); - } - @Test void reference() throws Exception { Reference ref = @@ -411,49 +335,6 @@ void genericEventSerialization() throws Exception { + "}"); } - @Test - void genericContent() throws Exception { - GenericContent content = - ImmutableGenericContent.builder() - .id("id") - .genericType("genericType") - .putProperty("string", "foo") - .putProperty("number", 123) - .putProperty("boolean", true) - .putProperty( - "complex", ImmutableMap.of("string", "foo", "number", 123, "boolean", true)) - .build(); - assertThat(deserialize(serialize(content), Content.class)).isEqualTo(content); - } - - @Test - void genericContentSerialization() throws Exception { - GenericContent content = - ImmutableGenericContent.builder() - .id("id") - .genericType("weird") - .putProperty("string", "foo") - .putProperty("number", 123) - .putProperty("boolean", true) - .putProperty( - "complex", ImmutableMap.of("string", "foo", "number", 123, "boolean", true)) - .build(); - assertThat(serialize(content)) - .isEqualTo( - "{" - + "\"id\":\"id\"," - + "\"type\":\"weird\"," - + "\"string\":\"foo\"," - + "\"number\":123," - + "\"boolean\":true," - + "\"complex\":{" - + "\"string\":\"foo\"," - + "\"number\":123," - + "\"boolean\":true" - + "}" - + "}"); - } - @Test void unknownEventDeserialization() throws Exception { assertThat( @@ -487,35 +368,6 @@ void unknownEventDeserialization() throws Exception { .build()); } - @Test - void unknownContentDeserialization() throws Exception { - assertThat( - deserialize( - "{" - + "\"id\":\"id\"," - + "\"type\":\"weird\"," - + "\"string\":\"foo\"," - + "\"number\":123," - + "\"boolean\":true," - + "\"complex\":{" - + "\"string\":\"foo\"," - + "\"number\":123," - + "\"boolean\":true" - + "}" - + "}", - Content.class)) - .isEqualTo( - ImmutableGenericContent.builder() - .id("id") - .genericType("weird") - .putProperty("string", "foo") - .putProperty("number", 123) - .putProperty("boolean", true) - .putProperty( - "complex", ImmutableMap.of("string", "foo", "number", 123, "boolean", true)) - .build()); - } - @Test void deserializeKnownEventTypeToGenericSubtype() throws Exception { assertThat( @@ -557,45 +409,6 @@ void deserializeKnownEventTypeToGenericSubtype() throws Exception { .build()); } - @Test - void deserializeKnownContentTypeToGenericSubtype() throws Exception { - assertThat( - deserialize( - "{" - + "\"id\":\"id\"," - + "\"type\":\"ICEBERG_TABLE\"," - + "\"metadataLocation\":\"location\"," - + "\"snapshotId\":1," - + "\"schemaId\":2," - + "\"specId\":3," - + "\"sortOrderId\":4," - + "\"string\":\"foo\"," - + "\"number\":123," - + "\"boolean\":true," - + "\"complex\":{" - + "\"string\":\"foo\"," - + "\"number\":123," - + "\"boolean\":true" - + "}" - + "}", - GenericContent.class)) - .isEqualTo( - ImmutableGenericContent.builder() - .id("id") - .genericType("ICEBERG_TABLE") - .putProperty("metadataLocation", "location") - .putProperty("snapshotId", 1) - .putProperty("schemaId", 2) - .putProperty("specId", 3) - .putProperty("sortOrderId", 4) - .putProperty("string", "foo") - .putProperty("number", 123) - .putProperty("boolean", true) - .putProperty( - "complex", ImmutableMap.of("string", "foo", "number", 123, "boolean", true)) - .build()); - } - @Test void deserializeEventToIncompatibleSubtype() { assertThatThrownBy( @@ -617,26 +430,6 @@ void deserializeEventToIncompatibleSubtype() { "Type id REFERENCE_CREATED is not convertible to interface org.projectnessie.events.api.ReferenceDeletedEvent"); } - @Test - void deserializeContentToIncompatibleSubtype() { - assertThatThrownBy( - () -> - deserialize( - "{" - + "\"id\":\"id\"," - + "\"type\":\"ICEBERG_TABLE\"," - + "\"metadataLocation\":\"location\"," - + "\"snapshotId\":1," - + "\"schemaId\":2," - + "\"specId\":3," - + "\"sortOrderId\":4" - + "}", - IcebergView.class)) - .isInstanceOf(JsonMappingException.class) - .hasMessageContaining( - "Type id ICEBERG_TABLE is not convertible to interface org.projectnessie.events.api.IcebergView"); - } - private Object deserialize(String json, Class clazz) throws JsonProcessingException { return MAPPER.readValue(json, clazz); } diff --git a/events/api/src/test/java/org/projectnessie/events/api/TestNamespace.java b/events/api/src/test/java/org/projectnessie/events/api/TestNamespace.java deleted file mode 100644 index cc8d93f8cc3..00000000000 --- a/events/api/src/test/java/org/projectnessie/events/api/TestNamespace.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2023 Dremio - * - * 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 org.projectnessie.events.api; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; - -class TestNamespace { - - @Test - void getName() { - assertThat(ns("name").getName()).isEqualTo("name"); - assertThat(ns("name1", "name2").getName()).isEqualTo("name1.name2"); - assertThat(ns("name1", "name2", "name3").getName()).isEqualTo("name1.name2.name3"); - assertThat(ns("na.me1", "na\u0000me2").getName()).isEqualTo("na\u001dme1.na\u001dme2"); - } - - @Test - void getSimpleName() { - assertThat(ns("name").getSimpleName()).isEqualTo("name"); - assertThat(ns("name1", "name2").getSimpleName()).isEqualTo("name2"); - assertThat(ns("name1", "name2", "name3").getSimpleName()).isEqualTo("name3"); - assertThat(ns("na.me1", "na\u0000me2").getSimpleName()).isEqualTo("na\u0000me2"); - } - - private static Namespace ns(String... elements) { - return ImmutableNamespace.builder().id("id").addElements(elements).build(); - } -} diff --git a/events/service/src/main/java/org/projectnessie/events/service/util/ContentMapping.java b/events/service/src/main/java/org/projectnessie/events/service/util/ContentMapping.java index 0258b75200e..13bceb7666e 100644 --- a/events/service/src/main/java/org/projectnessie/events/service/util/ContentMapping.java +++ b/events/service/src/main/java/org/projectnessie/events/service/util/ContentMapping.java @@ -15,28 +15,18 @@ */ package org.projectnessie.events.service.util; +import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Map; -import java.util.Objects; -import java.util.Optional; import org.projectnessie.events.api.Content; import org.projectnessie.events.api.ContentKey; -import org.projectnessie.events.api.ImmutableDeltaLakeTable; -import org.projectnessie.events.api.ImmutableGenericContent; -import org.projectnessie.events.api.ImmutableIcebergTable; -import org.projectnessie.events.api.ImmutableIcebergView; -import org.projectnessie.events.api.ImmutableNamespace; -import org.projectnessie.events.api.ImmutableUDF; -import org.projectnessie.model.DeltaLakeTable; -import org.projectnessie.model.IcebergTable; -import org.projectnessie.model.IcebergView; -import org.projectnessie.model.UDF; -import org.projectnessie.model.types.GenericContent; +import org.projectnessie.events.api.ImmutableContent; public final class ContentMapping { - private static final ObjectMapper MAPPER = new ObjectMapper(); + private static final ObjectMapper MAPPER = + new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL); private static final TypeReference> MAP_TYPE = new TypeReference>() {}; @@ -44,65 +34,12 @@ public final class ContentMapping { private ContentMapping() {} public static Content map(org.projectnessie.model.Content content) { - org.projectnessie.model.Content.Type type = content.getType(); - if (type == org.projectnessie.model.Content.Type.NAMESPACE) { - return ImmutableNamespace.builder() - .id(Objects.requireNonNull(content.getId())) - .elements(((org.projectnessie.model.Namespace) content).getElements()) - .build(); - } else if (type == org.projectnessie.model.Content.Type.ICEBERG_TABLE) { - IcebergTable table = (IcebergTable) content; - return ImmutableIcebergTable.builder() - .id(Objects.requireNonNull(content.getId())) - .metadataLocation(table.getMetadataLocation()) - .snapshotId(table.getSnapshotId()) - .schemaId(table.getSchemaId()) - .specId(table.getSpecId()) - .sortOrderId(table.getSortOrderId()) - .build(); - } else if (type == org.projectnessie.model.Content.Type.ICEBERG_VIEW) { - IcebergView view = (IcebergView) content; - return ImmutableIcebergView.builder() - .id(Objects.requireNonNull(content.getId())) - .metadataLocation(view.getMetadataLocation()) - .versionId(view.getVersionId()) - .schemaId(view.getSchemaId()) - .sqlText(view.getSqlText()) - .dialect(Optional.ofNullable(view.getDialect())) - .build(); - } else if (type == org.projectnessie.model.Content.Type.DELTA_LAKE_TABLE) { - DeltaLakeTable table = (DeltaLakeTable) content; - return ImmutableDeltaLakeTable.builder() - .id(Objects.requireNonNull(content.getId())) - .metadataLocationHistory(table.getMetadataLocationHistory()) - .lastCheckpoint(table.getLastCheckpoint()) - .checkpointLocationHistory(table.getCheckpointLocationHistory()) - .build(); - } else if (type == org.projectnessie.model.Content.Type.UDF) { - UDF udf = (UDF) content; - return ImmutableUDF.builder() - .id(Objects.requireNonNull(content.getId())) - .sqlText(udf.getSqlText()) - .dialect(Optional.ofNullable(udf.getDialect())) - .build(); - } else if (content instanceof GenericContent) { - GenericContent genericContent = (GenericContent) content; - ImmutableGenericContent.Builder builder = - ImmutableGenericContent.builder() - .id(Objects.requireNonNull(content.getId())) - .genericType(genericContent.getType().name()); - if (genericContent.getAttributes() != null) { - builder.putAllProperties(genericContent.getAttributes()); - } - return builder.build(); - } else { - Map map = MAPPER.convertValue(content, MAP_TYPE); - return ImmutableGenericContent.builder() - .id((String) map.remove("id")) - .genericType((String) map.remove("type")) - .putAllProperties(map) - .build(); - } + Map map = MAPPER.convertValue(content, MAP_TYPE); + return ImmutableContent.builder() + .id((String) map.remove("id")) + .type((String) map.remove("type")) + .putAllProperties(map) + .build(); } public static ContentKey map(org.projectnessie.model.ContentKey key) { diff --git a/events/service/src/test/java/org/projectnessie/events/service/TestEventFactory.java b/events/service/src/test/java/org/projectnessie/events/service/TestEventFactory.java index cfbdd042dac..1a07bbcf65f 100644 --- a/events/service/src/test/java/org/projectnessie/events/service/TestEventFactory.java +++ b/events/service/src/test/java/org/projectnessie/events/service/TestEventFactory.java @@ -25,14 +25,14 @@ import java.util.UUID; import java.util.function.Supplier; import org.junit.jupiter.api.Test; +import org.projectnessie.events.api.Content; import org.projectnessie.events.api.ContentKey; import org.projectnessie.events.api.Event; -import org.projectnessie.events.api.IcebergTable; import org.projectnessie.events.api.ImmutableCommitEvent; import org.projectnessie.events.api.ImmutableCommitMeta; +import org.projectnessie.events.api.ImmutableContent; import org.projectnessie.events.api.ImmutableContentRemovedEvent; import org.projectnessie.events.api.ImmutableContentStoredEvent; -import org.projectnessie.events.api.ImmutableIcebergTable; import org.projectnessie.events.api.ImmutableMergeEvent; import org.projectnessie.events.api.ImmutableReference; import org.projectnessie.events.api.ImmutableReferenceCreatedEvent; @@ -259,14 +259,15 @@ void newReferenceDeletedEvent() { @Test void newContentStoredEvent() { EventFactory ef = new EventFactory(config); - IcebergTable table = - ImmutableIcebergTable.builder() + Content table = + ImmutableContent.builder() .id("table1") - .metadataLocation("location") - .schemaId(1) - .specId(2) - .sortOrderId(3) - .snapshotId(4) + .type("ICEBERG_TABLE") + .putProperty("metadataLocation", "location") + .putProperty("schemaId", 1) + .putProperty("specId", 2) + .putProperty("sortOrderId", 3) + .putProperty("snapshotId", 4) .build(); Event actual = ef.newContentStoredEvent( diff --git a/events/service/src/test/java/org/projectnessie/events/service/util/TestContentMapping.java b/events/service/src/test/java/org/projectnessie/events/service/util/TestContentMapping.java index 8902fe80d04..c8df9bc3c37 100644 --- a/events/service/src/test/java/org/projectnessie/events/service/util/TestContentMapping.java +++ b/events/service/src/test/java/org/projectnessie/events/service/util/TestContentMapping.java @@ -19,6 +19,7 @@ import com.google.common.collect.ImmutableMap; import java.time.Instant; +import java.util.Arrays; import java.util.Collections; import java.util.stream.Stream; import javax.annotation.Nullable; @@ -28,13 +29,8 @@ import org.junit.jupiter.params.provider.MethodSource; import org.projectnessie.events.api.Content; import org.projectnessie.events.api.ContentKey; +import org.projectnessie.events.api.ImmutableContent; import org.projectnessie.events.api.ImmutableContentKey; -import org.projectnessie.events.api.ImmutableDeltaLakeTable; -import org.projectnessie.events.api.ImmutableGenericContent; -import org.projectnessie.events.api.ImmutableIcebergTable; -import org.projectnessie.events.api.ImmutableIcebergView; -import org.projectnessie.events.api.ImmutableNamespace; -import org.projectnessie.events.api.ImmutableUDF; import org.projectnessie.model.CommitMeta; class TestContentMapping { @@ -63,7 +59,11 @@ public static Stream mapContent() { .id("id") .addElements("foo", "bar") .build(), - ImmutableNamespace.builder().id("id").addElements("foo", "bar").build()), + ImmutableContent.builder() + .id("id") + .type("NAMESPACE") + .putProperty("elements", Arrays.asList("foo", "bar")) + .build()), Arguments.of( org.projectnessie.model.ImmutableIcebergTable.builder() .id("id") @@ -73,13 +73,14 @@ public static Stream mapContent() { .specId(3) .sortOrderId(4) .build(), - ImmutableIcebergTable.builder() + ImmutableContent.builder() .id("id") - .metadataLocation("metadataLocation") - .snapshotId(1L) - .schemaId(2) - .specId(3) - .sortOrderId(4) + .type("ICEBERG_TABLE") + .putProperty("metadataLocation", "metadataLocation") + .putProperty("snapshotId", 1L) + .putProperty("schemaId", 2) + .putProperty("specId", 3) + .putProperty("sortOrderId", 4) .build()), Arguments.of( org.projectnessie.model.ImmutableIcebergView.builder() @@ -90,13 +91,14 @@ public static Stream mapContent() { .sqlText("sqlText") .dialect("dialect") .build(), - ImmutableIcebergView.builder() + ImmutableContent.builder() .id("id") - .metadataLocation("metadataLocation") - .versionId(1L) - .schemaId(2) - .sqlText("sqlText") - .dialect("dialect") + .type("ICEBERG_VIEW") + .putProperty("metadataLocation", "metadataLocation") + .putProperty("versionId", 1L) + .putProperty("schemaId", 2) + .putProperty("sqlText", "sqlText") + .putProperty("dialect", "dialect") .build()), Arguments.of( org.projectnessie.model.ImmutableIcebergView.builder() @@ -107,12 +109,13 @@ public static Stream mapContent() { .sqlText("sqlText") // no dialect .build(), - ImmutableIcebergView.builder() + ImmutableContent.builder() .id("id") - .metadataLocation("metadataLocation") - .versionId(1L) - .schemaId(2) - .sqlText("sqlText") + .type("ICEBERG_VIEW") + .putProperty("metadataLocation", "metadataLocation") + .putProperty("versionId", 1L) + .putProperty("schemaId", 2) + .putProperty("sqlText", "sqlText") .build()), Arguments.of( org.projectnessie.model.ImmutableDeltaLakeTable.builder() @@ -121,11 +124,12 @@ public static Stream mapContent() { .addMetadataLocationHistory("metadata") .lastCheckpoint("lastCheckpoint") .build(), - ImmutableDeltaLakeTable.builder() + ImmutableContent.builder() .id("id") - .addCheckpointLocationHistory("checkpoint") - .addMetadataLocationHistory("metadata") - .lastCheckpoint("lastCheckpoint") + .type("DELTA_LAKE_TABLE") + .putProperty("checkpointLocationHistory", Collections.singletonList("checkpoint")) + .putProperty("metadataLocationHistory", Collections.singletonList("metadata")) + .putProperty("lastCheckpoint", "lastCheckpoint") .build()), Arguments.of( org.projectnessie.model.ImmutableUDF.builder() @@ -133,14 +137,23 @@ public static Stream mapContent() { .sqlText("sqlText") .dialect("dialect") .build(), - ImmutableUDF.builder().id("id").sqlText("sqlText").dialect("dialect").build()), + ImmutableContent.builder() + .id("id") + .type("UDF") + .putProperty("sqlText", "sqlText") + .putProperty("dialect", "dialect") + .build()), Arguments.of( org.projectnessie.model.ImmutableUDF.builder() .id("id") .sqlText("sqlText") // no dialect .build(), - ImmutableUDF.builder().id("id").sqlText("sqlText").build()), + ImmutableContent.builder() + .id("id") + .type("UDF") + .putProperty("sqlText", "sqlText") + .build()), Arguments.of( org.projectnessie.model.types.ImmutableGenericContent.builder() .id("id") @@ -160,18 +173,18 @@ public Class type() { .putAttributes("number", 123) .putAttributes("boolean", true) .build(), - ImmutableGenericContent.builder() + ImmutableContent.builder() .id("id") - .genericType("GENERIC") + .type("GENERIC") .putProperty("text", "foo") .putProperty("number", 123) .putProperty("boolean", true) .build()), Arguments.of( new MyCustomContent(), - ImmutableGenericContent.builder() + ImmutableContent.builder() .id("id") - .genericType("MY_CONTENT") + .type("MY_CONTENT") .putProperty("text", "foo") .putProperty("number", 123) .putProperty("boolean", true)