From 64bc64ed56f24c043f59c528d9ab29a07cd42a60 Mon Sep 17 00:00:00 2001 From: Yves Piel Date: Mon, 5 Jan 2026 18:30:30 +0100 Subject: [PATCH 1/8] fix(QTDI-2215): Add schema/Entry pojo. --- .../component/server/front/model/Entry.java | 1112 +++++++++++++++++ .../component/server/front/model/Schema.java | 780 ++++++++++++ 2 files changed, 1892 insertions(+) create mode 100644 component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Entry.java create mode 100644 component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Schema.java diff --git a/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Entry.java b/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Entry.java new file mode 100644 index 0000000000000..e5391a14cccd5 --- /dev/null +++ b/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Entry.java @@ -0,0 +1,1112 @@ +package org.talend.sdk.component.server.front.model; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.talend.sdk.component.api.record.Schema; + +public final class Entry implements Schema.Entry { + private final String name; + private final String rawName; + private final String originalFieldName; + private final Schema.Type type; + private final boolean isNullable; + private final boolean isMetadata; + private final boolean isErrorCapable; + private final boolean isValid; + private final Schema elementSchema; + private final String comment; + private final Map props; + private final Object internalDefaultValue; + private final Map properties; + + private Entry( + String name, + String rawName, + String originalFieldName, + Schema.Type type, + boolean isNullable, + boolean isMetadata, + boolean isErrorCapable, + boolean isValid, + Schema elementSchema, + String comment, + Map props, + Object internalDefaultValue, + Map properties) { + this.name = name; + this.rawName = rawName; + this.originalFieldName = originalFieldName; + this.type = type; + this.isNullable = isNullable; + this.isMetadata = isMetadata; + this.isErrorCapable = isErrorCapable; + this.isValid = isValid; + this.elementSchema = elementSchema; + this.comment = comment; + this.props = props; + this.internalDefaultValue = internalDefaultValue; + this.properties = properties; + } + + @Override + public T getDefaultValue(){ + return (T) this.getInternalDefaultValue(); + } + + @Override + public String getProp(String key) { + return this.getProperties().get(key); + } + + /** + * @return The value of the {@code name} attribute + */ + @Override + public String getName() { + return name; + } + + /** + * @return The value of the {@code rawName} attribute + */ + @Override + public String getRawName() { + return rawName; + } + + /** + * @return The value of the {@code originalFieldName} attribute + */ + @Override + public String getOriginalFieldName() { + return originalFieldName; + } + + /** + * @return The value of the {@code type} attribute + */ + @Override + public Schema.Type getType() { + return type; + } + + /** + * @return The value of the {@code isNullable} attribute + */ + @Override + public boolean isNullable() { + return isNullable; + } + + /** + * @return The value of the {@code isMetadata} attribute + */ + @Override + public boolean isMetadata() { + return isMetadata; + } + + /** + * @return The value of the {@code isErrorCapable} attribute + */ + @Override + public boolean isErrorCapable() { + return isErrorCapable; + } + + /** + * @return The value of the {@code isValid} attribute + */ + @Override + public boolean isValid() { + return isValid; + } + + /** + * @return The value of the {@code elementSchema} attribute + */ + @Override + public Schema getElementSchema() { + return elementSchema; + } + + /** + * @return The value of the {@code comment} attribute + */ + @Override + public String getComment() { + return comment; + } + + /** + * @return The value of the {@code props} attribute + */ + @Override + public Map getProps() { + return props; + } + + /** + * @return The value of the {@code internalDefaultValue} attribute + */ + public Object getInternalDefaultValue() { + return internalDefaultValue; + } + + /** + * @return The value of the {@code properties} attribute + */ + public Map getProperties() { + return properties; + } + + /** + * Copy the current immutable object by setting a value for the {@link Entry#getName() name} attribute. + * An equals check used to prevent copying of the same value by returning {@code this}. + * + * @param value A new value for name + * @return A modified copy of the {@code this} object + */ + public final Entry withName(String value) { + String newValue = Objects.requireNonNull(value, "name"); + if (this.name.equals(newValue)) return this; + return new Entry( + newValue, + this.rawName, + this.originalFieldName, + this.type, + this.isNullable, + this.isMetadata, + this.isErrorCapable, + this.isValid, + this.elementSchema, + this.comment, + this.props, + this.internalDefaultValue, + this.properties); + } + + /** + * Copy the current immutable object by setting a value for the {@link Entry#getRawName() rawName} attribute. + * An equals check used to prevent copying of the same value by returning {@code this}. + * + * @param value A new value for rawName + * @return A modified copy of the {@code this} object + */ + public final Entry withRawName(String value) { + String newValue = Objects.requireNonNull(value, "rawName"); + if (this.rawName.equals(newValue)) return this; + return new Entry( + this.name, + newValue, + this.originalFieldName, + this.type, + this.isNullable, + this.isMetadata, + this.isErrorCapable, + this.isValid, + this.elementSchema, + this.comment, + this.props, + this.internalDefaultValue, + this.properties); + } + + /** + * Copy the current immutable object by setting a value for the {@link Entry#getOriginalFieldName() originalFieldName} attribute. + * An equals check used to prevent copying of the same value by returning {@code this}. + * + * @param value A new value for originalFieldName + * @return A modified copy of the {@code this} object + */ + public final Entry withOriginalFieldName(String value) { + String newValue = Objects.requireNonNull(value, "originalFieldName"); + if (this.originalFieldName.equals(newValue)) return this; + return new Entry( + this.name, + this.rawName, + newValue, + this.type, + this.isNullable, + this.isMetadata, + this.isErrorCapable, + this.isValid, + this.elementSchema, + this.comment, + this.props, + this.internalDefaultValue, + this.properties); + } + + /** + * Copy the current immutable object by setting a value for the {@link Entry#getType() type} attribute. + * A value equality check is used to prevent copying of the same value by returning {@code this}. + * + * @param value A new value for type + * @return A modified copy of the {@code this} object + */ + public final Entry withType(Schema.Type value) { + Schema.Type newValue = Objects.requireNonNull(value, "type"); + if (this.type == newValue) return this; + return new Entry( + this.name, + this.rawName, + this.originalFieldName, + newValue, + this.isNullable, + this.isMetadata, + this.isErrorCapable, + this.isValid, + this.elementSchema, + this.comment, + this.props, + this.internalDefaultValue, + this.properties); + } + + /** + * Copy the current immutable object by setting a value for the {@link Entry#isNullable() isNullable} attribute. + * A value equality check is used to prevent copying of the same value by returning {@code this}. + * + * @param value A new value for isNullable + * @return A modified copy of the {@code this} object + */ + public final Entry withIsNullable(boolean value) { + if (this.isNullable == value) return this; + return new Entry( + this.name, + this.rawName, + this.originalFieldName, + this.type, + value, + this.isMetadata, + this.isErrorCapable, + this.isValid, + this.elementSchema, + this.comment, + this.props, + this.internalDefaultValue, + this.properties); + } + + /** + * Copy the current immutable object by setting a value for the {@link Entry#isMetadata() isMetadata} attribute. + * A value equality check is used to prevent copying of the same value by returning {@code this}. + * + * @param value A new value for isMetadata + * @return A modified copy of the {@code this} object + */ + public final Entry withIsMetadata(boolean value) { + if (this.isMetadata == value) return this; + return new Entry( + this.name, + this.rawName, + this.originalFieldName, + this.type, + this.isNullable, + value, + this.isErrorCapable, + this.isValid, + this.elementSchema, + this.comment, + this.props, + this.internalDefaultValue, + this.properties); + } + + /** + * Copy the current immutable object by setting a value for the {@link Entry#isErrorCapable() isErrorCapable} attribute. + * A value equality check is used to prevent copying of the same value by returning {@code this}. + * + * @param value A new value for isErrorCapable + * @return A modified copy of the {@code this} object + */ + public final Entry withIsErrorCapable(boolean value) { + if (this.isErrorCapable == value) return this; + return new Entry( + this.name, + this.rawName, + this.originalFieldName, + this.type, + this.isNullable, + this.isMetadata, + value, + this.isValid, + this.elementSchema, + this.comment, + this.props, + this.internalDefaultValue, + this.properties); + } + + /** + * Copy the current immutable object by setting a value for the {@link Entry#isValid() isValid} attribute. + * A value equality check is used to prevent copying of the same value by returning {@code this}. + * + * @param value A new value for isValid + * @return A modified copy of the {@code this} object + */ + public final Entry withIsValid(boolean value) { + if (this.isValid == value) return this; + return new Entry( + this.name, + this.rawName, + this.originalFieldName, + this.type, + this.isNullable, + this.isMetadata, + this.isErrorCapable, + value, + this.elementSchema, + this.comment, + this.props, + this.internalDefaultValue, + this.properties); + } + + /** + * Copy the current immutable object by setting a value for the {@link Entry#getElementSchema() elementSchema} attribute. + * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}. + * + * @param value A new value for elementSchema + * @return A modified copy of the {@code this} object + */ + public final Entry withElementSchema(Schema value) { + if (this.elementSchema == value) return this; + Schema newValue = Objects.requireNonNull(value, "elementSchema"); + return new Entry( + this.name, + this.rawName, + this.originalFieldName, + this.type, + this.isNullable, + this.isMetadata, + this.isErrorCapable, + this.isValid, + newValue, + this.comment, + this.props, + this.internalDefaultValue, + this.properties); + } + + /** + * Copy the current immutable object by setting a value for the {@link Entry#getComment() comment} attribute. + * An equals check used to prevent copying of the same value by returning {@code this}. + * + * @param value A new value for comment + * @return A modified copy of the {@code this} object + */ + public final Entry withComment(String value) { + String newValue = Objects.requireNonNull(value, "comment"); + if (this.comment.equals(newValue)) return this; + return new Entry( + this.name, + this.rawName, + this.originalFieldName, + this.type, + this.isNullable, + this.isMetadata, + this.isErrorCapable, + this.isValid, + this.elementSchema, + newValue, + this.props, + this.internalDefaultValue, + this.properties); + } + + /** + * Copy the current immutable object by replacing the {@link Entry#getProps() props} map with the specified map. + * Nulls are not permitted as keys or values. + * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}. + * + * @param entries The entries to be added to the props map + * @return A modified copy of {@code this} object + */ + public final Entry withProps(Map entries) { + if (this.props == entries) return this; + Map newValue = createUnmodifiableMap(true, false, entries); + return new Entry( + this.name, + this.rawName, + this.originalFieldName, + this.type, + this.isNullable, + this.isMetadata, + this.isErrorCapable, + this.isValid, + this.elementSchema, + this.comment, + newValue, + this.internalDefaultValue, + this.properties); + } + + /** + * Copy the current immutable object by setting a value for the {@link Entry#getInternalDefaultValue() internalDefaultValue} attribute. + * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}. + * + * @param value A new value for internalDefaultValue + * @return A modified copy of the {@code this} object + */ + public final Entry withInternalDefaultValue(Object value) { + if (this.internalDefaultValue == value) return this; + Object newValue = Objects.requireNonNull(value, "internalDefaultValue"); + return new Entry( + this.name, + this.rawName, + this.originalFieldName, + this.type, + this.isNullable, + this.isMetadata, + this.isErrorCapable, + this.isValid, + this.elementSchema, + this.comment, + this.props, + newValue, + this.properties); + } + + /** + * Copy the current immutable object by replacing the {@link Entry#getProperties() properties} map with the specified map. + * Nulls are not permitted as keys or values. + * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}. + * + * @param entries The entries to be added to the properties map + * @return A modified copy of {@code this} object + */ + public final Entry withProperties(Map entries) { + if (this.properties == entries) return this; + Map newValue = createUnmodifiableMap(true, false, entries); + return new Entry( + this.name, + this.rawName, + this.originalFieldName, + this.type, + this.isNullable, + this.isMetadata, + this.isErrorCapable, + this.isValid, + this.elementSchema, + this.comment, + this.props, + this.internalDefaultValue, + newValue); + } + + /** + * This instance is equal to all instances of {@code EntryDetail} that have equal attribute values. + * + * @return {@code true} if {@code this} is equal to {@code another} instance + */ + @Override + public boolean equals(Object another) { + if (this == another) return true; + return another instanceof Entry + && equalTo(0, (Entry) another); + } + + private boolean equalTo(int synthetic, Entry another) { + return name.equals(another.name) + && rawName.equals(another.rawName) + && originalFieldName.equals(another.originalFieldName) + && type.equals(another.type) + && isNullable == another.isNullable + && isMetadata == another.isMetadata + && isErrorCapable == another.isErrorCapable + && isValid == another.isValid + && elementSchema.equals(another.elementSchema) + && comment.equals(another.comment) + && props.equals(another.props) + && internalDefaultValue.equals(another.internalDefaultValue) + && properties.equals(another.properties); + } + + /** + * Computes a hash code from attributes: {@code name}, {@code rawName}, {@code originalFieldName}, {@code type}, {@code isNullable}, {@code isMetadata}, {@code isErrorCapable}, {@code isValid}, {@code elementSchema}, {@code comment}, {@code props}, {@code internalDefaultValue}, {@code properties}. + * + * @return hashCode value + */ + @Override + public int hashCode() { + int h = 5381; + h += (h << 5) + name.hashCode(); + h += (h << 5) + rawName.hashCode(); + h += (h << 5) + originalFieldName.hashCode(); + h += (h << 5) + type.hashCode(); + h += (h << 5) + Boolean.hashCode(isNullable); + h += (h << 5) + Boolean.hashCode(isMetadata); + h += (h << 5) + Boolean.hashCode(isErrorCapable); + h += (h << 5) + Boolean.hashCode(isValid); + h += (h << 5) + elementSchema.hashCode(); + h += (h << 5) + comment.hashCode(); + h += (h << 5) + props.hashCode(); + h += (h << 5) + internalDefaultValue.hashCode(); + h += (h << 5) + properties.hashCode(); + return h; + } + + /** + * Prints the immutable value {@code Entry} with attribute values. + * + * @return A string representation of the value + */ + @Override + public String toString() { + return "Entry{" + + "name=" + name + + ", rawName=" + rawName + + ", originalFieldName=" + originalFieldName + + ", type=" + type + + ", isNullable=" + isNullable + + ", isMetadata=" + isMetadata + + ", isErrorCapable=" + isErrorCapable + + ", isValid=" + isValid + + ", elementSchema=" + elementSchema + + ", comment=" + comment + + ", props=" + props + + ", internalDefaultValue=" + internalDefaultValue + + ", properties=" + properties + + "}"; + } + + /** + * Creates an immutable copy of a {@link Entry} value. + * Uses accessors to get values to initialize the new immutable instance. + * If an instance is already immutable, it is returned as is. + * + * @param instance The instance to copy + * @return A copied immutable Entry instance + */ + public static Entry copyOf(Entry instance) { + if (instance instanceof Entry) { + return (Entry) instance; + } + return Entry.builder() + .from(instance) + .build(); + } + + /** + * Creates a builder for {@link Entry EntryDetail}. + *
+     * EntryDetail.builder()
+     *    .name(String) // required {@link Entry#getName() name}
+     *    .rawName(String) // required {@link Entry#getRawName() rawName}
+     *    .originalFieldName(String) // required {@link Entry#getOriginalFieldName() originalFieldName}
+     *    .type(org.talend.sdk.component.api.record.Schema.Type) // required {@link Entry#getType() type}
+     *    .isNullable(boolean) // required {@link Entry#isNullable() isNullable}
+     *    .isMetadata(boolean) // required {@link Entry#isMetadata() isMetadata}
+     *    .isErrorCapable(boolean) // required {@link Entry#isErrorCapable() isErrorCapable}
+     *    .isValid(boolean) // required {@link Entry#isValid() isValid}
+     *    .elementSchema(org.talend.sdk.component.api.record.Schema) // required {@link Entry#getElementSchema() elementSchema}
+     *    .comment(String) // required {@link Entry#getComment() comment}
+     *    .putProps|putAllProps(String => String) // {@link Entry#getProps() props} mappings
+     *    .internalDefaultValue(Object) // required {@link Entry#getInternalDefaultValue() internalDefaultValue}
+     *    .putProperties|putAllProperties(String => String) // {@link Entry#getProperties() properties} mappings
+     *    .build();
+     * 
+ * + * @return A new EntryDetail builder + */ + public static Entry.Builder builder() { + return new Entry.Builder(); + } + + /** + * Builds instances of type {@link Entry EntryDetail}. + * Initialize attributes and then invoke the {@link #build()} method to create an + * immutable instance. + *

{@code Builder} is not thread-safe and generally should not be stored in a field or collection, + * but instead used immediately to create instances. + */ + public static final class Builder { + private static final long INIT_BIT_NAME = 0x1L; + private static final long INIT_BIT_RAW_NAME = 0x2L; + private static final long INIT_BIT_ORIGINAL_FIELD_NAME = 0x4L; + private static final long INIT_BIT_TYPE = 0x8L; + private static final long INIT_BIT_IS_NULLABLE = 0x10L; + private static final long INIT_BIT_IS_METADATA = 0x20L; + private static final long INIT_BIT_IS_ERROR_CAPABLE = 0x40L; + private static final long INIT_BIT_IS_VALID = 0x80L; + private static final long INIT_BIT_ELEMENT_SCHEMA = 0x100L; + private static final long INIT_BIT_COMMENT = 0x200L; + private static final long INIT_BIT_INTERNAL_DEFAULT_VALUE = 0x400L; + private long initBits = 0x7ffL; + + private String name; + private String rawName; + private String originalFieldName; + private Schema.Type type; + private boolean isNullable; + private boolean isMetadata; + private boolean isErrorCapable; + private boolean isValid; + private Schema elementSchema; + private String comment; + private Map props = new LinkedHashMap(); + private Object internalDefaultValue; + private Map properties = new LinkedHashMap(); + + private Builder() { + } + + /** + * Fill a builder with attribute values from the provided {@code org.talend.sdk.component.server.front.model.Entry} instance. + * + * @param instance The instance from which to copy values + * @return {@code this} builder for use in a chained invocation + */ + public final Builder from(Entry instance) { + Objects.requireNonNull(instance, "instance"); + from((short) 0, (Object) instance); + return this; + } + + /** + * Fill a builder with attribute values from the provided {@code org.talend.sdk.component.api.record.Schema.Entry} instance. + * + * @param instance The instance from which to copy values + * @return {@code this} builder for use in a chained invocation + */ + public final Builder from(Schema.Entry instance) { + Objects.requireNonNull(instance, "instance"); + from((short) 0, (Object) instance); + return this; + } + + private void from(short _unused, Object object) { + long bits = 0; + if (object instanceof Entry) { + Entry instance = (Entry) object; + if ((bits & 0x1L) == 0) { + this.elementSchema(instance.getElementSchema()); + bits |= 0x1L; + } + if ((bits & 0x2L) == 0) { + this.isValid(instance.isValid()); + bits |= 0x2L; + } + this.internalDefaultValue(instance.getInternalDefaultValue()); + if ((bits & 0x4L) == 0) { + this.type(instance.getType()); + bits |= 0x4L; + } + if ((bits & 0x8L) == 0) { + this.rawName(instance.getRawName()); + bits |= 0x8L; + } + if ((bits & 0x10L) == 0) { + putAllProps(instance.getProps()); + bits |= 0x10L; + } + if ((bits & 0x20L) == 0) { + this.originalFieldName(instance.getOriginalFieldName()); + bits |= 0x20L; + } + if ((bits & 0x40L) == 0) { + this.isNullable(instance.isNullable()); + bits |= 0x40L; + } + if ((bits & 0x80L) == 0) { + this.name(instance.getName()); + bits |= 0x80L; + } + if ((bits & 0x100L) == 0) { + this.comment(instance.getComment()); + bits |= 0x100L; + } + if ((bits & 0x200L) == 0) { + this.isMetadata(instance.isMetadata()); + bits |= 0x200L; + } + if ((bits & 0x400L) == 0) { + this.isErrorCapable(instance.isErrorCapable()); + bits |= 0x400L; + } + putAllProperties(instance.getProperties()); + } + if (object instanceof Schema.Entry) { + Schema.Entry instance = (Schema.Entry) object; + if ((bits & 0x1L) == 0) { + this.elementSchema(instance.getElementSchema()); + bits |= 0x1L; + } + if ((bits & 0x20L) == 0) { + this.originalFieldName(instance.getOriginalFieldName()); + bits |= 0x20L; + } + if ((bits & 0x2L) == 0) { + this.isValid(instance.isValid()); + bits |= 0x2L; + } + if ((bits & 0x40L) == 0) { + this.isNullable(instance.isNullable()); + bits |= 0x40L; + } + if ((bits & 0x80L) == 0) { + this.name(instance.getName()); + bits |= 0x80L; + } + if ((bits & 0x100L) == 0) { + this.comment(instance.getComment()); + bits |= 0x100L; + } + if ((bits & 0x4L) == 0) { + this.type(instance.getType()); + bits |= 0x4L; + } + if ((bits & 0x200L) == 0) { + this.isMetadata(instance.isMetadata()); + bits |= 0x200L; + } + if ((bits & 0x400L) == 0) { + this.isErrorCapable(instance.isErrorCapable()); + bits |= 0x400L; + } + if ((bits & 0x8L) == 0) { + this.rawName(instance.getRawName()); + bits |= 0x8L; + } + if ((bits & 0x10L) == 0) { + putAllProps(instance.getProps()); + bits |= 0x10L; + } + } + } + + /** + * Initializes the value for the {@link Entry#getName() name} attribute. + * + * @param name The value for name + * @return {@code this} builder for use in a chained invocation + */ + public final Builder name(String name) { + this.name = Objects.requireNonNull(name, "name"); + initBits &= ~INIT_BIT_NAME; + return this; + } + + /** + * Initializes the value for the {@link Entry#getRawName() rawName} attribute. + * + * @param rawName The value for rawName + * @return {@code this} builder for use in a chained invocation + */ + public final Builder rawName(String rawName) { + this.rawName = Objects.requireNonNull(rawName, "rawName"); + initBits &= ~INIT_BIT_RAW_NAME; + return this; + } + + /** + * Initializes the value for the {@link Entry#getOriginalFieldName() originalFieldName} attribute. + * + * @param originalFieldName The value for originalFieldName + * @return {@code this} builder for use in a chained invocation + */ + public final Builder originalFieldName(String originalFieldName) { + this.originalFieldName = Objects.requireNonNull(originalFieldName, "originalFieldName"); + initBits &= ~INIT_BIT_ORIGINAL_FIELD_NAME; + return this; + } + + /** + * Initializes the value for the {@link Entry#getType() type} attribute. + * + * @param type The value for type + * @return {@code this} builder for use in a chained invocation + */ + public final Builder type(Schema.Type type) { + this.type = Objects.requireNonNull(type, "type"); + initBits &= ~INIT_BIT_TYPE; + return this; + } + + /** + * Initializes the value for the {@link Entry#isNullable() isNullable} attribute. + * + * @param isNullable The value for isNullable + * @return {@code this} builder for use in a chained invocation + */ + public final Builder isNullable(boolean isNullable) { + this.isNullable = isNullable; + initBits &= ~INIT_BIT_IS_NULLABLE; + return this; + } + + /** + * Initializes the value for the {@link Entry#isMetadata() isMetadata} attribute. + * + * @param isMetadata The value for isMetadata + * @return {@code this} builder for use in a chained invocation + */ + public final Builder isMetadata(boolean isMetadata) { + this.isMetadata = isMetadata; + initBits &= ~INIT_BIT_IS_METADATA; + return this; + } + + /** + * Initializes the value for the {@link Entry#isErrorCapable() isErrorCapable} attribute. + * + * @param isErrorCapable The value for isErrorCapable + * @return {@code this} builder for use in a chained invocation + */ + public final Builder isErrorCapable(boolean isErrorCapable) { + this.isErrorCapable = isErrorCapable; + initBits &= ~INIT_BIT_IS_ERROR_CAPABLE; + return this; + } + + /** + * Initializes the value for the {@link Entry#isValid() isValid} attribute. + * + * @param isValid The value for isValid + * @return {@code this} builder for use in a chained invocation + */ + public final Builder isValid(boolean isValid) { + this.isValid = isValid; + initBits &= ~INIT_BIT_IS_VALID; + return this; + } + + /** + * Initializes the value for the {@link Entry#getElementSchema() elementSchema} attribute. + * + * @param elementSchema The value for elementSchema + * @return {@code this} builder for use in a chained invocation + */ + public final Builder elementSchema(Schema elementSchema) { + this.elementSchema = Objects.requireNonNull(elementSchema, "elementSchema"); + initBits &= ~INIT_BIT_ELEMENT_SCHEMA; + return this; + } + + /** + * Initializes the value for the {@link Entry#getComment() comment} attribute. + * + * @param comment The value for comment + * @return {@code this} builder for use in a chained invocation + */ + public final Builder comment(String comment) { + this.comment = Objects.requireNonNull(comment, "comment"); + initBits &= ~INIT_BIT_COMMENT; + return this; + } + + /** + * Put one entry to the {@link Entry#getProps() props} map. + * + * @param key The key in the props map + * @param value The associated value in the props map + * @return {@code this} builder for use in a chained invocation + */ + public final Builder putProps(String key, String value) { + this.props.put( + Objects.requireNonNull(key, "props key"), + Objects.requireNonNull(value, value == null ? "props value for key: " + key : null)); + return this; + } + + /** + * Put one entry to the {@link Entry#getProps() props} map. Nulls are not permitted + * + * @param entry The key and value entry + * @return {@code this} builder for use in a chained invocation + */ + public final Builder putProps(Map.Entry entry) { + String k = entry.getKey(); + String v = entry.getValue(); + this.props.put( + Objects.requireNonNull(k, "props key"), + Objects.requireNonNull(v, v == null ? "props value for key: " + k : null)); + return this; + } + + /** + * Sets or replaces all mappings from the specified map as entries for the {@link Entry#getProps() props} map. Nulls are not permitted + * + * @param entries The entries that will be added to the props map + * @return {@code this} builder for use in a chained invocation + */ + public final Builder props(Map entries) { + this.props.clear(); + return putAllProps(entries); + } + + /** + * Put all mappings from the specified map as entries to {@link Entry#getProps() props} map. Nulls are not permitted + * + * @param entries The entries that will be added to the props map + * @return {@code this} builder for use in a chained invocation + */ + public final Builder putAllProps(Map entries) { + for (Map.Entry e : entries.entrySet()) { + String k = e.getKey(); + String v = e.getValue(); + this.props.put( + Objects.requireNonNull(k, "props key"), + Objects.requireNonNull(v, v == null ? "props value for key: " + k : null)); + } + return this; + } + + /** + * Initializes the value for the {@link Entry#getInternalDefaultValue() internalDefaultValue} attribute. + * + * @param internalDefaultValue The value for internalDefaultValue + * @return {@code this} builder for use in a chained invocation + */ + public final Builder internalDefaultValue(Object internalDefaultValue) { + this.internalDefaultValue = Objects.requireNonNull(internalDefaultValue, "internalDefaultValue"); + initBits &= ~INIT_BIT_INTERNAL_DEFAULT_VALUE; + return this; + } + + /** + * Put one entry to the {@link Entry#getProperties() properties} map. + * + * @param key The key in the properties map + * @param value The associated value in the properties map + * @return {@code this} builder for use in a chained invocation + */ + public final Builder putProperties(String key, String value) { + this.properties.put( + Objects.requireNonNull(key, "properties key"), + Objects.requireNonNull(value, value == null ? "properties value for key: " + key : null)); + return this; + } + + /** + * Put one entry to the {@link Entry#getProperties() properties} map. Nulls are not permitted + * + * @param entry The key and value entry + * @return {@code this} builder for use in a chained invocation + */ + public final Builder putProperties(Map.Entry entry) { + String k = entry.getKey(); + String v = entry.getValue(); + this.properties.put( + Objects.requireNonNull(k, "properties key"), + Objects.requireNonNull(v, v == null ? "properties value for key: " + k : null)); + return this; + } + + /** + * Sets or replaces all mappings from the specified map as entries for the {@link Entry#getProperties() properties} map. Nulls are not permitted + * + * @param entries The entries that will be added to the properties map + * @return {@code this} builder for use in a chained invocation + */ + public final Builder properties(Map entries) { + this.properties.clear(); + return putAllProperties(entries); + } + + /** + * Put all mappings from the specified map as entries to {@link Entry#getProperties() properties} map. Nulls are not permitted + * + * @param entries The entries that will be added to the properties map + * @return {@code this} builder for use in a chained invocation + */ + public final Builder putAllProperties(Map entries) { + for (Map.Entry e : entries.entrySet()) { + String k = e.getKey(); + String v = e.getValue(); + this.properties.put( + Objects.requireNonNull(k, "properties key"), + Objects.requireNonNull(v, v == null ? "properties value for key: " + k : null)); + } + return this; + } + + /** + * Builds a new {@link Entry EntryDetail}. + * + * @return An immutable instance of Entry + * @throws IllegalStateException if any required attributes are missing + */ + public Entry build() { + if (initBits != 0) { + throw new IllegalStateException(formatRequiredAttributesMessage()); + } + return new Entry( + name, + rawName, + originalFieldName, + type, + isNullable, + isMetadata, + isErrorCapable, + isValid, + elementSchema, + comment, + createUnmodifiableMap(false, false, props), + internalDefaultValue, + createUnmodifiableMap(false, false, properties)); + } + + private String formatRequiredAttributesMessage() { + List attributes = new ArrayList<>(); + if ((initBits & INIT_BIT_NAME) != 0) attributes.add("name"); + if ((initBits & INIT_BIT_RAW_NAME) != 0) attributes.add("rawName"); + if ((initBits & INIT_BIT_ORIGINAL_FIELD_NAME) != 0) attributes.add("originalFieldName"); + if ((initBits & INIT_BIT_TYPE) != 0) attributes.add("type"); + if ((initBits & INIT_BIT_IS_NULLABLE) != 0) attributes.add("isNullable"); + if ((initBits & INIT_BIT_IS_METADATA) != 0) attributes.add("isMetadata"); + if ((initBits & INIT_BIT_IS_ERROR_CAPABLE) != 0) attributes.add("isErrorCapable"); + if ((initBits & INIT_BIT_IS_VALID) != 0) attributes.add("isValid"); + if ((initBits & INIT_BIT_ELEMENT_SCHEMA) != 0) attributes.add("elementSchema"); + if ((initBits & INIT_BIT_COMMENT) != 0) attributes.add("comment"); + if ((initBits & INIT_BIT_INTERNAL_DEFAULT_VALUE) != 0) attributes.add("internalDefaultValue"); + return "Cannot build Entry, some of required attributes are not set " + attributes; + } + } + + private static Map createUnmodifiableMap(boolean checkNulls, boolean skipNulls, Map map) { + switch (map.size()) { + case 0: + return Collections.emptyMap(); + case 1: { + Map.Entry e = map.entrySet().iterator().next(); + K k = e.getKey(); + V v = e.getValue(); + if (checkNulls) { + Objects.requireNonNull(k, "key"); + Objects.requireNonNull(v, v == null ? "value for key: " + k : null); + } + if (skipNulls && (k == null || v == null)) { + return Collections.emptyMap(); + } + return Collections.singletonMap(k, v); + } + default: { + Map linkedMap = new LinkedHashMap<>(map.size() * 4 / 3 + 1); + if (skipNulls || checkNulls) { + for (Map.Entry e : map.entrySet()) { + K k = e.getKey(); + V v = e.getValue(); + if (skipNulls) { + if (k == null || v == null) continue; + } else if (checkNulls) { + Objects.requireNonNull(k, "key"); + Objects.requireNonNull(v, v == null ? "value for key: " + k : null); + } + linkedMap.put(k, v); + } + } else { + linkedMap.putAll(map); + } + return Collections.unmodifiableMap(linkedMap); + } + } + } +} \ No newline at end of file diff --git a/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Schema.java b/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Schema.java new file mode 100644 index 0000000000000..0c64631393144 --- /dev/null +++ b/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Schema.java @@ -0,0 +1,780 @@ +package org.talend.sdk.component.server.front.model; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Stream; + +public final class Schema implements org.talend.sdk.component.api.record.Schema { + private final Type type; + private final org.talend.sdk.component.api.record.Schema elementSchema; + private final List entries; + private final List metadata; + private final Stream allEntries; + private final Map props; + private final Map properties; + + private Schema( + Type type, + org.talend.sdk.component.api.record.Schema elementSchema, + List entries, + List metadata, + Stream allEntries, + Map props, + Map properties) { + this.type = type; + this.elementSchema = elementSchema; + this.entries = entries; + this.metadata = metadata; + this.allEntries = allEntries; + this.props = props; + this.properties = properties; + } + + @Override + public String getProp(String key){ + return this.getProperties().get(key); + } + + /** + * @return The value of the {@code type} attribute + */ + @Override + public Type getType() { + return type; + } + + /** + * @return The value of the {@code elementSchema} attribute + */ + @Override + public org.talend.sdk.component.api.record.Schema getElementSchema() { + return elementSchema; + } + + /** + * @return The value of the {@code entries} attribute + */ + @Override + public List getEntries() { + return entries; + } + + /** + * @return The value of the {@code metadata} attribute + */ + @Override + public List getMetadata() { + return metadata; + } + + /** + * @return The value of the {@code allEntries} attribute + */ + @Override + public Stream getAllEntries() { + return allEntries; + } + + /** + * @return The value of the {@code props} attribute + */ + @Override + public Map getProps() { + return props; + } + + public Map getProperties() { + return properties; + } + + /** + * Copy the current immutable object by setting a value for the {@link Schema#getType() type} attribute. + * A value equality check is used to prevent copying of the same value by returning {@code this}. + * @param value A new value for type + * @return A modified copy of the {@code this} object + */ + public final Schema withType(Type value) { + Type newValue = Objects.requireNonNull(value, "type"); + if (this.type == newValue) return this; + return new Schema( + newValue, + this.elementSchema, + this.entries, + this.metadata, + this.allEntries, + this.props, + this.properties); + } + + /** + * Copy the current immutable object by setting a value for the {@link Schema#getElementSchema() elementSchema} attribute. + * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}. + * @param value A new value for elementSchema + * @return A modified copy of the {@code this} object + */ + public final Schema withElementSchema(org.talend.sdk.component.api.record.Schema value) { + if (this.elementSchema == value) return this; + org.talend.sdk.component.api.record.Schema newValue = Objects.requireNonNull(value, "elementSchema"); + return new Schema(this.type, newValue, this.entries, this.metadata, this.allEntries, this.props, this.properties); + } + + /** + * Copy the current immutable object with elements that replace the content of {@link Schema#getEntries() entries}. + * @param elements The elements to set + * @return A modified copy of {@code this} object + */ + public final Schema withEntries(Entry... elements) { + List newValue = createUnmodifiableList(false, createSafeList(Arrays.asList(elements), true, false)); + return new Schema( + this.type, + this.elementSchema, + newValue, + this.metadata, + this.allEntries, + this.props, + this.properties); + } + + /** + * Copy the current immutable object with elements that replace the content of {@link Schema#getEntries() entries}. + * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}. + * @param elements An iterable of entries elements to set + * @return A modified copy of {@code this} object + */ + public final Schema withEntries(Iterable elements) { + if (this.entries == elements) return this; + List newValue = createUnmodifiableList(false, createSafeList(elements, true, false)); + return new Schema( + this.type, + this.elementSchema, + newValue, + this.metadata, + this.allEntries, + this.props, + this.properties); + } + + /** + * Copy the current immutable object with elements that replace the content of {@link Schema#getMetadata() metadata}. + * @param elements The elements to set + * @return A modified copy of {@code this} object + */ + public final Schema withMetadata(Entry... elements) { + List newValue = createUnmodifiableList(false, createSafeList(Arrays.asList(elements), true, false)); + return new Schema( + this.type, + this.elementSchema, + this.entries, + newValue, + this.allEntries, + this.props, + this.properties); + } + + /** + * Copy the current immutable object with elements that replace the content of {@link Schema#getMetadata() metadata}. + * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}. + * @param elements An iterable of metadata elements to set + * @return A modified copy of {@code this} object + */ + public final Schema withMetadata(Iterable elements) { + if (this.metadata == elements) return this; + List newValue = createUnmodifiableList(false, createSafeList(elements, true, false)); + return new Schema( + this.type, + this.elementSchema, + this.entries, + newValue, + this.allEntries, + this.props, + this.properties); + } + + /** + * Copy the current immutable object by setting a value for the {@link Schema#getAllEntries() allEntries} attribute. + * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}. + * @param value A new value for allEntries + * @return A modified copy of the {@code this} object + */ + public final Schema withAllEntries(Stream value) { + if (this.allEntries == value) return this; + Stream newValue = Objects.requireNonNull(value, "allEntries"); + return new Schema( + this.type, + this.elementSchema, + this.entries, + this.metadata, + newValue, + this.props, + this.properties); + } + + /** + * Copy the current immutable object by replacing the {@link Schema#getProps() props} map with the specified map. + * Nulls are not permitted as keys or values. + * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}. + * @param entries The entries to be added to the props map + * @return A modified copy of {@code this} object + */ + public final Schema withProps(Map entries) { + if (this.props == entries) return this; + Map newValue = createUnmodifiableMap(true, false, entries); + return new Schema( + this.type, + this.elementSchema, + this.entries, + this.metadata, + this.allEntries, + newValue, + this.properties); + } + + /** + * Copy the current immutable object by replacing the {@link Schema#getProperties() properties} map with the specified map. + * Nulls are not permitted as keys or values. + * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}. + * @param entries The entries to be added to the properties map + * @return A modified copy of {@code this} object + */ + public final Schema withProperties(Map entries) { + if (this.properties == entries) return this; + Map newValue = createUnmodifiableMap(true, false, entries); + return new Schema( + this.type, + this.elementSchema, + this.entries, + this.metadata, + this.allEntries, + this.props, + newValue); + } + + /** + * This instance is equal to all instances of {@code SchemaDetail} that have equal attribute values. + * @return {@code true} if {@code this} is equal to {@code another} instance + */ + @Override + public boolean equals(Object another) { + if (this == another) return true; + return another instanceof Schema + && equalTo(0, (Schema) another); + } + + private boolean equalTo(int synthetic, Schema another) { + return type.equals(another.type) + && elementSchema.equals(another.elementSchema) + && entries.equals(another.entries) + && metadata.equals(another.metadata) + && allEntries.equals(another.allEntries) + && props.equals(another.props) + && properties.equals(another.properties); + } + + /** + * Computes a hash code from attributes: {@code type}, {@code elementSchema}, {@code entries}, {@code metadata}, {@code allEntries}, {@code props}, {@code properties}. + * @return hashCode value + */ + @Override + public int hashCode() { + int h = 5381; + h += (h << 5) + type.hashCode(); + h += (h << 5) + elementSchema.hashCode(); + h += (h << 5) + entries.hashCode(); + h += (h << 5) + metadata.hashCode(); + h += (h << 5) + allEntries.hashCode(); + h += (h << 5) + props.hashCode(); + h += (h << 5) + properties.hashCode(); + return h; + } + + /** + * Prints the immutable value {@code Schema} with attribute values. + * @return A string representation of the value + */ + @Override + public String toString() { + return "Schema{" + + "type=" + type + + ", elementSchema=" + elementSchema + + ", entries=" + entries + + ", metadata=" + metadata + + ", allEntries=" + allEntries + + ", props=" + props + + ", properties=" + properties + + "}"; + } + + /** + * Creates an immutable copy of a {@link Schema} value. + * Uses accessors to get values to initialize the new immutable instance. + * If an instance is already immutable, it is returned as is. + * @param instance The instance to copy + * @return A copied immutable Schema instance + */ + public static Schema copyOf(Schema instance) { + if (instance instanceof Schema) { + return (Schema) instance; + } + return Schema.builder() + .from(instance) + .build(); + } + + /** + * Creates a builder for {@link Schema SchemaDetail}. + *

+   * SchemaDetail.builder()
+   *    .type(org.talend.sdk.component.api.record.Schema.Type) // required {@link Schema#getType() type}
+   *    .elementSchema(org.talend.sdk.component.api.record.Schema) // required {@link Schema#getElementSchema() elementSchema}
+   *    .addEntries|addAllEntries(org.talend.sdk.component.api.record.Schema.Entry) // {@link Schema#getEntries() entries} elements
+   *    .addMetadata|addAllMetadata(org.talend.sdk.component.api.record.Schema.Entry) // {@link Schema#getMetadata() metadata} elements
+   *    .allEntries(stream.Stream&lt;org.talend.sdk.component.api.record.Schema.Entry&gt;) // required {@link Schema#getAllEntries() allEntries}
+   *    .putProps|putAllProps(String => String) // {@link Schema#getProps() props} mappings
+   *    .putProperties|putAllProperties(String => String) // {@link Schema#getProperties() properties} mappings
+   *    .build();
+   * 
+ * @return A new SchemaDetail builder + */ + public static Schema.Builder builder() { + return new Schema.Builder(); + } + + /** + * Builds instances of type {@link Schema SchemaDetail}. + * Initialize attributes and then invoke the {@link #build()} method to create an + * immutable instance. + *

{@code Builder} is not thread-safe and generally should not be stored in a field or collection, + * but instead used immediately to create instances. + */ + public static final class Builder { + private static final long INIT_BIT_TYPE = 0x1L; + private static final long INIT_BIT_ELEMENT_SCHEMA = 0x2L; + private static final long INIT_BIT_ALL_ENTRIES = 0x4L; + private long initBits = 0x7L; + + private Type type; + private org.talend.sdk.component.api.record.Schema elementSchema; + private List entries = new ArrayList(); + private List metadata = new ArrayList(); + private Stream allEntries; + private Map props = new LinkedHashMap(); + private Map properties = new LinkedHashMap(); + + private Builder() { + } + + /** + * Fill a builder with attribute values from the provided {@code org.talend.sdk.component.api.record.Schema} instance. + * @param instance The instance from which to copy values + * @return {@code this} builder for use in a chained invocation + */ + public final Builder from(org.talend.sdk.component.api.record.Schema instance) { + Objects.requireNonNull(instance, "instance"); + from((short) 0, (Object) instance); + return this; + } + + /** + * Fill a builder with attribute values from the provided {@code org.talend.sdk.component.server.front.model.Schema} instance. + * @param instance The instance from which to copy values + * @return {@code this} builder for use in a chained invocation + */ + public final Builder from(Schema instance) { + Objects.requireNonNull(instance, "instance"); + from((short) 0, (Object) instance); + return this; + } + + private void from(short _unused, Object object) { + long bits = 0; + if (object instanceof org.talend.sdk.component.api.record.Schema) { + org.talend.sdk.component.api.record.Schema instance = (org.talend.sdk.component.api.record.Schema) object; + if ((bits & 0x1L) == 0) { + this.elementSchema(instance.getElementSchema()); + bits |= 0x1L; + } + if ((bits & 0x2L) == 0) { + addAllMetadata(instance.getMetadata()); + bits |= 0x2L; + } + if ((bits & 0x20L) == 0) { + addAllEntries(instance.getEntries()); + bits |= 0x20L; + } + if ((bits & 0x4L) == 0) { + this.allEntries(instance.getAllEntries()); + bits |= 0x4L; + } + if ((bits & 0x8L) == 0) { + this.type(instance.getType()); + bits |= 0x8L; + } + if ((bits & 0x10L) == 0) { + putAllProps(instance.getProps()); + bits |= 0x10L; + } + } + if (object instanceof Schema) { + Schema instance = (Schema) object; + if ((bits & 0x1L) == 0) { + this.elementSchema(instance.getElementSchema()); + bits |= 0x1L; + } + if ((bits & 0x2L) == 0) { + addAllMetadata(instance.getMetadata()); + bits |= 0x2L; + } + if ((bits & 0x20L) == 0) { + addAllEntries(instance.getEntries()); + bits |= 0x20L; + } + if ((bits & 0x4L) == 0) { + this.allEntries(instance.getAllEntries()); + bits |= 0x4L; + } + if ((bits & 0x8L) == 0) { + this.type(instance.getType()); + bits |= 0x8L; + } + putAllProperties(instance.getProperties()); + if ((bits & 0x10L) == 0) { + putAllProps(instance.getProps()); + bits |= 0x10L; + } + } + } + + /** + * Initializes the value for the {@link Schema#getType() type} attribute. + * @param type The value for type + * @return {@code this} builder for use in a chained invocation + */ + public final Builder type(Type type) { + this.type = Objects.requireNonNull(type, "type"); + initBits &= ~INIT_BIT_TYPE; + return this; + } + + /** + * Initializes the value for the {@link Schema#getElementSchema() elementSchema} attribute. + * @param elementSchema The value for elementSchema + * @return {@code this} builder for use in a chained invocation + */ + public final Builder elementSchema(org.talend.sdk.component.api.record.Schema elementSchema) { + this.elementSchema = Objects.requireNonNull(elementSchema, "elementSchema"); + initBits &= ~INIT_BIT_ELEMENT_SCHEMA; + return this; + } + + /** + * Adds one element to {@link Schema#getEntries() entries} list. + * @param element A entries element + * @return {@code this} builder for use in a chained invocation + */ + public final Builder addEntries(Entry element) { + this.entries.add(Objects.requireNonNull(element, "entries element")); + return this; + } + + /** + * Adds elements to {@link Schema#getEntries() entries} list. + * @param elements An array of entries elements + * @return {@code this} builder for use in a chained invocation + */ + public final Builder addEntries(Entry... elements) { + for (Entry element : elements) { + this.entries.add(Objects.requireNonNull(element, "entries element")); + } + return this; + } + + + /** + * Sets or replaces all elements for {@link Schema#getEntries() entries} list. + * @param elements An iterable of entries elements + * @return {@code this} builder for use in a chained invocation + */ + public final Builder entries(Iterable elements) { + this.entries.clear(); + return addAllEntries(elements); + } + + /** + * Adds elements to {@link Schema#getEntries() entries} list. + * @param elements An iterable of entries elements + * @return {@code this} builder for use in a chained invocation + */ + public final Builder addAllEntries(Iterable elements) { + for (Entry element : elements) { + this.entries.add(Objects.requireNonNull(element, "entries element")); + } + return this; + } + + /** + * Adds one element to {@link Schema#getMetadata() metadata} list. + * @param element A metadata element + * @return {@code this} builder for use in a chained invocation + */ + public final Builder addMetadata(Entry element) { + this.metadata.add(Objects.requireNonNull(element, "metadata element")); + return this; + } + + /** + * Adds elements to {@link Schema#getMetadata() metadata} list. + * @param elements An array of metadata elements + * @return {@code this} builder for use in a chained invocation + */ + public final Builder addMetadata(Entry... elements) { + for (Entry element : elements) { + this.metadata.add(Objects.requireNonNull(element, "metadata element")); + } + return this; + } + + + /** + * Sets or replaces all elements for {@link Schema#getMetadata() metadata} list. + * @param elements An iterable of metadata elements + * @return {@code this} builder for use in a chained invocation + */ + public final Builder metadata(Iterable elements) { + this.metadata.clear(); + return addAllMetadata(elements); + } + + /** + * Adds elements to {@link Schema#getMetadata() metadata} list. + * @param elements An iterable of metadata elements + * @return {@code this} builder for use in a chained invocation + */ + public final Builder addAllMetadata(Iterable elements) { + for (Entry element : elements) { + this.metadata.add(Objects.requireNonNull(element, "metadata element")); + } + return this; + } + + /** + * Initializes the value for the {@link Schema#getAllEntries() allEntries} attribute. + * @param allEntries The value for allEntries + * @return {@code this} builder for use in a chained invocation + */ + public final Builder allEntries(Stream allEntries) { + this.allEntries = Objects.requireNonNull(allEntries, "allEntries"); + initBits &= ~INIT_BIT_ALL_ENTRIES; + return this; + } + + /** + * Put one entry to the {@link Schema#getProps() props} map. + * @param key The key in the props map + * @param value The associated value in the props map + * @return {@code this} builder for use in a chained invocation + */ + public final Builder putProps(String key, String value) { + this.props.put( + Objects.requireNonNull(key, "props key"), + Objects.requireNonNull(value, value == null ? "props value for key: " + key : null)); + return this; + } + + /** + * Put one entry to the {@link Schema#getProps() props} map. Nulls are not permitted + * @param entry The key and value entry + * @return {@code this} builder for use in a chained invocation + */ + public final Builder putProps(Map.Entry entry) { + String k = entry.getKey(); + String v = entry.getValue(); + this.props.put( + Objects.requireNonNull(k, "props key"), + Objects.requireNonNull(v, v == null ? "props value for key: " + k : null)); + return this; + } + + /** + * Sets or replaces all mappings from the specified map as entries for the {@link Schema#getProps() props} map. Nulls are not permitted + * @param entries The entries that will be added to the props map + * @return {@code this} builder for use in a chained invocation + */ + public final Builder props(Map entries) { + this.props.clear(); + return putAllProps(entries); + } + + /** + * Put all mappings from the specified map as entries to {@link Schema#getProps() props} map. Nulls are not permitted + * @param entries The entries that will be added to the props map + * @return {@code this} builder for use in a chained invocation + */ + public final Builder putAllProps(Map entries) { + for (Map.Entry e : entries.entrySet()) { + String k = e.getKey(); + String v = e.getValue(); + this.props.put( + Objects.requireNonNull(k, "props key"), + Objects.requireNonNull(v, v == null ? "props value for key: " + k : null)); + } + return this; + } + + /** + * Put one entry to the {@link Schema#getProperties() properties} map. + * @param key The key in the properties map + * @param value The associated value in the properties map + * @return {@code this} builder for use in a chained invocation + */ + public final Builder putProperties(String key, String value) { + this.properties.put( + Objects.requireNonNull(key, "properties key"), + Objects.requireNonNull(value, value == null ? "properties value for key: " + key : null)); + return this; + } + + /** + * Put one entry to the {@link Schema#getProperties() properties} map. Nulls are not permitted + * @param entry The key and value entry + * @return {@code this} builder for use in a chained invocation + */ + public final Builder putProperties(Map.Entry entry) { + String k = entry.getKey(); + String v = entry.getValue(); + this.properties.put( + Objects.requireNonNull(k, "properties key"), + Objects.requireNonNull(v, v == null ? "properties value for key: " + k : null)); + return this; + } + + /** + * Sets or replaces all mappings from the specified map as entries for the {@link Schema#getProperties() properties} map. Nulls are not permitted + * @param entries The entries that will be added to the properties map + * @return {@code this} builder for use in a chained invocation + */ + public final Builder properties(Map entries) { + this.properties.clear(); + return putAllProperties(entries); + } + + /** + * Put all mappings from the specified map as entries to {@link Schema#getProperties() properties} map. Nulls are not permitted + * @param entries The entries that will be added to the properties map + * @return {@code this} builder for use in a chained invocation + */ + public final Builder putAllProperties(Map entries) { + for (Map.Entry e : entries.entrySet()) { + String k = e.getKey(); + String v = e.getValue(); + this.properties.put( + Objects.requireNonNull(k, "properties key"), + Objects.requireNonNull(v, v == null ? "properties value for key: " + k : null)); + } + return this; + } + + /** + * Builds a new {@link Schema SchemaDetail}. + * @return An immutable instance of Schema + * @throws IllegalStateException if any required attributes are missing + */ + public Schema build() { + if (initBits != 0) { + throw new IllegalStateException(formatRequiredAttributesMessage()); + } + return new Schema( + type, + elementSchema, + createUnmodifiableList(true, entries), + createUnmodifiableList(true, metadata), + allEntries, + createUnmodifiableMap(false, false, props), + createUnmodifiableMap(false, false, properties)); + } + + private String formatRequiredAttributesMessage() { + List attributes = new ArrayList<>(); + if ((initBits & INIT_BIT_TYPE) != 0) attributes.add("type"); + if ((initBits & INIT_BIT_ELEMENT_SCHEMA) != 0) attributes.add("elementSchema"); + if ((initBits & INIT_BIT_ALL_ENTRIES) != 0) attributes.add("allEntries"); + return "Cannot build Schema, some of required attributes are not set " + attributes; + } + } + + private static List createSafeList(Iterable iterable, boolean checkNulls, boolean skipNulls) { + ArrayList list; + if (iterable instanceof Collection) { + int size = ((Collection) iterable).size(); + if (size == 0) return Collections.emptyList(); + list = new ArrayList<>(size); + } else { + list = new ArrayList<>(); + } + for (T element : iterable) { + if (skipNulls && element == null) continue; + if (checkNulls) Objects.requireNonNull(element, "element"); + list.add(element); + } + return list; + } + + private static List createUnmodifiableList(boolean clone, List list) { + switch(list.size()) { + case 0: return Collections.emptyList(); + case 1: return Collections.singletonList(list.get(0)); + default: + if (clone) { + return Collections.unmodifiableList(new ArrayList<>(list)); + } else { + if (list instanceof ArrayList) { + ((ArrayList) list).trimToSize(); + } + return Collections.unmodifiableList(list); + } + } + } + + private static Map createUnmodifiableMap(boolean checkNulls, boolean skipNulls, Map map) { + switch (map.size()) { + case 0: return Collections.emptyMap(); + case 1: { + Map.Entry e = map.entrySet().iterator().next(); + K k = e.getKey(); + V v = e.getValue(); + if (checkNulls) { + Objects.requireNonNull(k, "key"); + Objects.requireNonNull(v, v == null ? "value for key: " + k : null); + } + if (skipNulls && (k == null || v == null)) { + return Collections.emptyMap(); + } + return Collections.singletonMap(k, v); + } + default: { + Map linkedMap = new LinkedHashMap<>(map.size() * 4 / 3 + 1); + if (skipNulls || checkNulls) { + for (Map.Entry e : map.entrySet()) { + K k = e.getKey(); + V v = e.getValue(); + if (skipNulls) { + if (k == null || v == null) continue; + } else if (checkNulls) { + Objects.requireNonNull(k, "key"); + Objects.requireNonNull(v, v == null ? "value for key: " + k : null); + } + linkedMap.put(k, v); + } + } else { + linkedMap.putAll(map); + } + return Collections.unmodifiableMap(linkedMap); + } + } + } +} \ No newline at end of file From 81786f1ccebb67b14974aa0f4ee051b23bbdaffe Mon Sep 17 00:00:00 2001 From: yyin-talend Date: Fri, 9 Jan 2026 09:57:34 +0800 Subject: [PATCH 2/8] clean and add junit --- .../component-server-model/pom.xml | 96 ++ .../component/server/front/model/Entry.java | 895 ++++-------------- .../component-server/pom.xml | 6 + .../sdk/component/server/EntryTest.java | 188 ++++ .../component/server/front/SchemaTest.java | 683 +++++++++++++ 5 files changed, 1177 insertions(+), 691 deletions(-) create mode 100644 component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/EntryTest.java create mode 100644 component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/SchemaTest.java diff --git a/component-server-parent/component-server-model/pom.xml b/component-server-parent/component-server-model/pom.xml index 030254eeac938..1492e4f2d023a 100644 --- a/component-server-parent/component-server-model/pom.xml +++ b/component-server-parent/component-server-model/pom.xml @@ -45,6 +45,102 @@ + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + **/HelpMojo*,**/maven/legacy/model/** + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + verify-style + + check + + process-classes + + + org.apache.felix maven-bundle-plugin diff --git a/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Entry.java b/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Entry.java index e5391a14cccd5..6f6c27e543779 100644 --- a/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Entry.java +++ b/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Entry.java @@ -1,591 +1,324 @@ +/** + * Copyright (C) 2006-2025 Talend Inc. - www.talend.com + * + * 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.talend.sdk.component.server.front.model; -import java.util.ArrayList; +import java.beans.ConstructorProperties; import java.util.Collections; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; import java.util.Objects; -import org.talend.sdk.component.api.record.Schema; +import lombok.Getter; -public final class Entry implements Schema.Entry { +public final class Entry { + @Getter private final String name; + + @Getter private final String rawName; - private final String originalFieldName; + + @Getter private final Schema.Type type; - private final boolean isNullable; - private final boolean isMetadata; - private final boolean isErrorCapable; - private final boolean isValid; + + @Getter + private final boolean nullable; + + @Getter + private final boolean metadata; + + @Getter + private final boolean errorCapable; + + @Getter + private final boolean valid; + + @Getter private final Schema elementSchema; + + @Getter private final String comment; - private final Map props; + + @Getter + private final Map props = new LinkedHashMap<>(0);; + + @Getter private final Object internalDefaultValue; - private final Map properties; + @ConstructorProperties({"name", "rawName", "type", "nullable", "metadata", "errorCapable", + "valid", "elementSchema", "comment", "props", "internalDefaultValue"}) private Entry( - String name, - String rawName, - String originalFieldName, - Schema.Type type, - boolean isNullable, - boolean isMetadata, - boolean isErrorCapable, - boolean isValid, - Schema elementSchema, - String comment, - Map props, - Object internalDefaultValue, - Map properties) { + final String name, + final String rawName, + final Schema.Type type, + final boolean nullable, + final boolean metadata, + final boolean errorCapable, + final boolean valid, + final Schema elementSchema, + final String comment, + final Map props, + final Object internalDefaultValue) { this.name = name; this.rawName = rawName; - this.originalFieldName = originalFieldName; this.type = type; - this.isNullable = isNullable; - this.isMetadata = isMetadata; - this.isErrorCapable = isErrorCapable; - this.isValid = isValid; + this.nullable = nullable; + this.metadata = metadata; + this.errorCapable = errorCapable; + this.valid = valid; this.elementSchema = elementSchema; this.comment = comment; - this.props = props; + this.props.putAll(props); this.internalDefaultValue = internalDefaultValue; - this.properties = properties; } - @Override public T getDefaultValue(){ return (T) this.getInternalDefaultValue(); } - @Override - public String getProp(String key) { - return this.getProperties().get(key); - } - - /** - * @return The value of the {@code name} attribute - */ - @Override - public String getName() { - return name; - } - - /** - * @return The value of the {@code rawName} attribute - */ - @Override - public String getRawName() { - return rawName; - } - - /** - * @return The value of the {@code originalFieldName} attribute - */ - @Override public String getOriginalFieldName() { - return originalFieldName; - } - - /** - * @return The value of the {@code type} attribute - */ - @Override - public Schema.Type getType() { - return type; - } - - /** - * @return The value of the {@code isNullable} attribute - */ - @Override - public boolean isNullable() { - return isNullable; - } - - /** - * @return The value of the {@code isMetadata} attribute - */ - @Override - public boolean isMetadata() { - return isMetadata; - } - - /** - * @return The value of the {@code isErrorCapable} attribute - */ - @Override - public boolean isErrorCapable() { - return isErrorCapable; - } - - /** - * @return The value of the {@code isValid} attribute - */ - @Override - public boolean isValid() { - return isValid; - } - - /** - * @return The value of the {@code elementSchema} attribute - */ - @Override - public Schema getElementSchema() { - return elementSchema; - } - - /** - * @return The value of the {@code comment} attribute - */ - @Override - public String getComment() { - return comment; - } - - /** - * @return The value of the {@code props} attribute - */ - @Override - public Map getProps() { - return props; - } - - /** - * @return The value of the {@code internalDefaultValue} attribute - */ - public Object getInternalDefaultValue() { - return internalDefaultValue; + return rawName != null ? rawName : name; } - /** - * @return The value of the {@code properties} attribute - */ - public Map getProperties() { - return properties; + public String getProp(final String key) { + return this.props.get(key); } - /** - * Copy the current immutable object by setting a value for the {@link Entry#getName() name} attribute. - * An equals check used to prevent copying of the same value by returning {@code this}. - * - * @param value A new value for name - * @return A modified copy of the {@code this} object - */ - public final Entry withName(String value) { + public final Entry withName(final String value) { String newValue = Objects.requireNonNull(value, "name"); - if (this.name.equals(newValue)) return this; + if (this.name.equals(newValue)) { + return this; + } return new Entry( newValue, this.rawName, - this.originalFieldName, this.type, - this.isNullable, - this.isMetadata, - this.isErrorCapable, - this.isValid, + this.nullable, + this.metadata, + this.errorCapable, + this.valid, this.elementSchema, this.comment, this.props, - this.internalDefaultValue, - this.properties); + this.internalDefaultValue); } - /** - * Copy the current immutable object by setting a value for the {@link Entry#getRawName() rawName} attribute. - * An equals check used to prevent copying of the same value by returning {@code this}. - * - * @param value A new value for rawName - * @return A modified copy of the {@code this} object - */ - public final Entry withRawName(String value) { + public final Entry withRawName(final String value) { String newValue = Objects.requireNonNull(value, "rawName"); - if (this.rawName.equals(newValue)) return this; - return new Entry( - this.name, - newValue, - this.originalFieldName, - this.type, - this.isNullable, - this.isMetadata, - this.isErrorCapable, - this.isValid, - this.elementSchema, - this.comment, - this.props, - this.internalDefaultValue, - this.properties); - } - - /** - * Copy the current immutable object by setting a value for the {@link Entry#getOriginalFieldName() originalFieldName} attribute. - * An equals check used to prevent copying of the same value by returning {@code this}. - * - * @param value A new value for originalFieldName - * @return A modified copy of the {@code this} object - */ - public final Entry withOriginalFieldName(String value) { - String newValue = Objects.requireNonNull(value, "originalFieldName"); - if (this.originalFieldName.equals(newValue)) return this; + if (this.rawName.equals(newValue)) { + return this; + } return new Entry( this.name, - this.rawName, newValue, this.type, - this.isNullable, - this.isMetadata, - this.isErrorCapable, - this.isValid, + this.nullable, + this.metadata, + this.errorCapable, + this.valid, this.elementSchema, this.comment, this.props, - this.internalDefaultValue, - this.properties); + this.internalDefaultValue); } - /** - * Copy the current immutable object by setting a value for the {@link Entry#getType() type} attribute. - * A value equality check is used to prevent copying of the same value by returning {@code this}. - * - * @param value A new value for type - * @return A modified copy of the {@code this} object - */ - public final Entry withType(Schema.Type value) { + public final Entry withType(final Schema.Type value) { Schema.Type newValue = Objects.requireNonNull(value, "type"); - if (this.type == newValue) return this; + if (this.type == newValue) { + return this; + } return new Entry( this.name, this.rawName, - this.originalFieldName, newValue, - this.isNullable, - this.isMetadata, - this.isErrorCapable, - this.isValid, + this.nullable, + this.metadata, + this.errorCapable, + this.valid, this.elementSchema, this.comment, this.props, - this.internalDefaultValue, - this.properties); + this.internalDefaultValue); } - /** - * Copy the current immutable object by setting a value for the {@link Entry#isNullable() isNullable} attribute. - * A value equality check is used to prevent copying of the same value by returning {@code this}. - * - * @param value A new value for isNullable - * @return A modified copy of the {@code this} object - */ - public final Entry withIsNullable(boolean value) { - if (this.isNullable == value) return this; + public final Entry withIsNullable(final boolean value) { + if (this.nullable == value) { + return this; + } return new Entry( this.name, this.rawName, - this.originalFieldName, this.type, value, - this.isMetadata, - this.isErrorCapable, - this.isValid, + this.metadata, + this.errorCapable, + this.valid, this.elementSchema, this.comment, this.props, - this.internalDefaultValue, - this.properties); + this.internalDefaultValue); } - /** - * Copy the current immutable object by setting a value for the {@link Entry#isMetadata() isMetadata} attribute. - * A value equality check is used to prevent copying of the same value by returning {@code this}. - * - * @param value A new value for isMetadata - * @return A modified copy of the {@code this} object - */ - public final Entry withIsMetadata(boolean value) { - if (this.isMetadata == value) return this; + public final Entry withIsMetadata(final boolean value) { + if (this.metadata == value) { + return this; + } return new Entry( this.name, this.rawName, - this.originalFieldName, this.type, - this.isNullable, + this.nullable, value, - this.isErrorCapable, - this.isValid, + this.errorCapable, + this.valid, this.elementSchema, this.comment, this.props, - this.internalDefaultValue, - this.properties); + this.internalDefaultValue); } - /** - * Copy the current immutable object by setting a value for the {@link Entry#isErrorCapable() isErrorCapable} attribute. - * A value equality check is used to prevent copying of the same value by returning {@code this}. - * - * @param value A new value for isErrorCapable - * @return A modified copy of the {@code this} object - */ - public final Entry withIsErrorCapable(boolean value) { - if (this.isErrorCapable == value) return this; + public final Entry withIsErrorCapable(final boolean value) { + if (this.errorCapable == value) { + return this; + } return new Entry( this.name, this.rawName, - this.originalFieldName, this.type, - this.isNullable, - this.isMetadata, + this.nullable, + this.metadata, value, - this.isValid, + this.valid, this.elementSchema, this.comment, this.props, - this.internalDefaultValue, - this.properties); + this.internalDefaultValue); } - /** - * Copy the current immutable object by setting a value for the {@link Entry#isValid() isValid} attribute. - * A value equality check is used to prevent copying of the same value by returning {@code this}. - * - * @param value A new value for isValid - * @return A modified copy of the {@code this} object - */ - public final Entry withIsValid(boolean value) { - if (this.isValid == value) return this; + public final Entry withIsValid(final boolean value) { + if (this.valid == value) { + return this; + } return new Entry( this.name, this.rawName, - this.originalFieldName, this.type, - this.isNullable, - this.isMetadata, - this.isErrorCapable, + this.nullable, + this.metadata, + this.errorCapable, value, this.elementSchema, this.comment, this.props, - this.internalDefaultValue, - this.properties); + this.internalDefaultValue); } - /** - * Copy the current immutable object by setting a value for the {@link Entry#getElementSchema() elementSchema} attribute. - * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}. - * - * @param value A new value for elementSchema - * @return A modified copy of the {@code this} object - */ - public final Entry withElementSchema(Schema value) { - if (this.elementSchema == value) return this; + public final Entry withElementSchema(final Schema value) { + if (this.elementSchema == value) { + return this; + } Schema newValue = Objects.requireNonNull(value, "elementSchema"); return new Entry( this.name, this.rawName, - this.originalFieldName, this.type, - this.isNullable, - this.isMetadata, - this.isErrorCapable, - this.isValid, + this.nullable, + this.metadata, + this.errorCapable, + this.valid, newValue, this.comment, this.props, - this.internalDefaultValue, - this.properties); + this.internalDefaultValue); } - /** - * Copy the current immutable object by setting a value for the {@link Entry#getComment() comment} attribute. - * An equals check used to prevent copying of the same value by returning {@code this}. - * - * @param value A new value for comment - * @return A modified copy of the {@code this} object - */ - public final Entry withComment(String value) { + public final Entry withComment(final String value) { String newValue = Objects.requireNonNull(value, "comment"); - if (this.comment.equals(newValue)) return this; + if (this.comment.equals(newValue)) { + return this; + } return new Entry( this.name, this.rawName, - this.originalFieldName, this.type, - this.isNullable, - this.isMetadata, - this.isErrorCapable, - this.isValid, + this.nullable, + this.metadata, + this.errorCapable, + this.valid, this.elementSchema, newValue, this.props, - this.internalDefaultValue, - this.properties); + this.internalDefaultValue); } - /** - * Copy the current immutable object by replacing the {@link Entry#getProps() props} map with the specified map. - * Nulls are not permitted as keys or values. - * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}. - * - * @param entries The entries to be added to the props map - * @return A modified copy of {@code this} object - */ - public final Entry withProps(Map entries) { - if (this.props == entries) return this; + public final Entry withProps(final Map entries) { + if (this.props == entries) { + return this; + } Map newValue = createUnmodifiableMap(true, false, entries); return new Entry( this.name, this.rawName, - this.originalFieldName, this.type, - this.isNullable, - this.isMetadata, - this.isErrorCapable, - this.isValid, + this.nullable, + this.metadata, + this.errorCapable, + this.valid, this.elementSchema, this.comment, newValue, - this.internalDefaultValue, - this.properties); + this.internalDefaultValue); } - /** - * Copy the current immutable object by setting a value for the {@link Entry#getInternalDefaultValue() internalDefaultValue} attribute. - * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}. - * - * @param value A new value for internalDefaultValue - * @return A modified copy of the {@code this} object - */ - public final Entry withInternalDefaultValue(Object value) { - if (this.internalDefaultValue == value) return this; + public final Entry withInternalDefaultValue(final Object value) { + if (this.internalDefaultValue == value) { + return this; + } Object newValue = Objects.requireNonNull(value, "internalDefaultValue"); return new Entry( this.name, this.rawName, - this.originalFieldName, this.type, - this.isNullable, - this.isMetadata, - this.isErrorCapable, - this.isValid, + this.nullable, + this.metadata, + this.errorCapable, + this.valid, this.elementSchema, this.comment, this.props, - newValue, - this.properties); - } - - /** - * Copy the current immutable object by replacing the {@link Entry#getProperties() properties} map with the specified map. - * Nulls are not permitted as keys or values. - * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}. - * - * @param entries The entries to be added to the properties map - * @return A modified copy of {@code this} object - */ - public final Entry withProperties(Map entries) { - if (this.properties == entries) return this; - Map newValue = createUnmodifiableMap(true, false, entries); - return new Entry( - this.name, - this.rawName, - this.originalFieldName, - this.type, - this.isNullable, - this.isMetadata, - this.isErrorCapable, - this.isValid, - this.elementSchema, - this.comment, - this.props, - this.internalDefaultValue, newValue); } - /** - * This instance is equal to all instances of {@code EntryDetail} that have equal attribute values. - * - * @return {@code true} if {@code this} is equal to {@code another} instance - */ - @Override - public boolean equals(Object another) { - if (this == another) return true; - return another instanceof Entry - && equalTo(0, (Entry) another); - } - - private boolean equalTo(int synthetic, Entry another) { - return name.equals(another.name) - && rawName.equals(another.rawName) - && originalFieldName.equals(another.originalFieldName) - && type.equals(another.type) - && isNullable == another.isNullable - && isMetadata == another.isMetadata - && isErrorCapable == another.isErrorCapable - && isValid == another.isValid - && elementSchema.equals(another.elementSchema) - && comment.equals(another.comment) - && props.equals(another.props) - && internalDefaultValue.equals(another.internalDefaultValue) - && properties.equals(another.properties); - } - - /** - * Computes a hash code from attributes: {@code name}, {@code rawName}, {@code originalFieldName}, {@code type}, {@code isNullable}, {@code isMetadata}, {@code isErrorCapable}, {@code isValid}, {@code elementSchema}, {@code comment}, {@code props}, {@code internalDefaultValue}, {@code properties}. - * - * @return hashCode value - */ - @Override - public int hashCode() { - int h = 5381; - h += (h << 5) + name.hashCode(); - h += (h << 5) + rawName.hashCode(); - h += (h << 5) + originalFieldName.hashCode(); - h += (h << 5) + type.hashCode(); - h += (h << 5) + Boolean.hashCode(isNullable); - h += (h << 5) + Boolean.hashCode(isMetadata); - h += (h << 5) + Boolean.hashCode(isErrorCapable); - h += (h << 5) + Boolean.hashCode(isValid); - h += (h << 5) + elementSchema.hashCode(); - h += (h << 5) + comment.hashCode(); - h += (h << 5) + props.hashCode(); - h += (h << 5) + internalDefaultValue.hashCode(); - h += (h << 5) + properties.hashCode(); - return h; - } - - /** - * Prints the immutable value {@code Entry} with attribute values. - * - * @return A string representation of the value - */ @Override public String toString() { return "Entry{" + "name=" + name + ", rawName=" + rawName - + ", originalFieldName=" + originalFieldName + ", type=" + type - + ", isNullable=" + isNullable - + ", isMetadata=" + isMetadata - + ", isErrorCapable=" + isErrorCapable - + ", isValid=" + isValid + + ", nullable=" + nullable + + ", metadata=" + metadata + + ", errorCapable=" + errorCapable + + ", valid=" + valid + ", elementSchema=" + elementSchema + ", comment=" + comment + ", props=" + props + ", internalDefaultValue=" + internalDefaultValue - + ", properties=" + properties + "}"; } - /** - * Creates an immutable copy of a {@link Entry} value. - * Uses accessors to get values to initialize the new immutable instance. - * If an instance is already immutable, it is returned as is. - * - * @param instance The instance to copy - * @return A copied immutable Entry instance - */ - public static Entry copyOf(Entry instance) { + public static Entry copyOf(final Entry instance) { if (instance instanceof Entry) { return (Entry) instance; } @@ -594,39 +327,10 @@ public static Entry copyOf(Entry instance) { .build(); } - /** - * Creates a builder for {@link Entry EntryDetail}. - *

-     * EntryDetail.builder()
-     *    .name(String) // required {@link Entry#getName() name}
-     *    .rawName(String) // required {@link Entry#getRawName() rawName}
-     *    .originalFieldName(String) // required {@link Entry#getOriginalFieldName() originalFieldName}
-     *    .type(org.talend.sdk.component.api.record.Schema.Type) // required {@link Entry#getType() type}
-     *    .isNullable(boolean) // required {@link Entry#isNullable() isNullable}
-     *    .isMetadata(boolean) // required {@link Entry#isMetadata() isMetadata}
-     *    .isErrorCapable(boolean) // required {@link Entry#isErrorCapable() isErrorCapable}
-     *    .isValid(boolean) // required {@link Entry#isValid() isValid}
-     *    .elementSchema(org.talend.sdk.component.api.record.Schema) // required {@link Entry#getElementSchema() elementSchema}
-     *    .comment(String) // required {@link Entry#getComment() comment}
-     *    .putProps|putAllProps(String => String) // {@link Entry#getProps() props} mappings
-     *    .internalDefaultValue(Object) // required {@link Entry#getInternalDefaultValue() internalDefaultValue}
-     *    .putProperties|putAllProperties(String => String) // {@link Entry#getProperties() properties} mappings
-     *    .build();
-     * 
- * - * @return A new EntryDetail builder - */ - public static Entry.Builder builder() { - return new Entry.Builder(); + public static Builder builder() { + return new Builder(); } - /** - * Builds instances of type {@link Entry EntryDetail}. - * Initialize attributes and then invoke the {@link #build()} method to create an - * immutable instance. - *

{@code Builder} is not thread-safe and generally should not be stored in a field or collection, - * but instead used immediately to create instances. - */ public static final class Builder { private static final long INIT_BIT_NAME = 0x1L; private static final long INIT_BIT_RAW_NAME = 0x2L; @@ -645,44 +349,25 @@ public static final class Builder { private String rawName; private String originalFieldName; private Schema.Type type; - private boolean isNullable; - private boolean isMetadata; - private boolean isErrorCapable; - private boolean isValid; + private boolean nullable; + private boolean metadata; + private boolean errorCapable; + private boolean valid; private Schema elementSchema; private String comment; private Map props = new LinkedHashMap(); private Object internalDefaultValue; - private Map properties = new LinkedHashMap(); private Builder() { } - /** - * Fill a builder with attribute values from the provided {@code org.talend.sdk.component.server.front.model.Entry} instance. - * - * @param instance The instance from which to copy values - * @return {@code this} builder for use in a chained invocation - */ - public final Builder from(Entry instance) { + public final Builder from(final Entry instance) { Objects.requireNonNull(instance, "instance"); - from((short) 0, (Object) instance); + from((Object) instance); return this; } - /** - * Fill a builder with attribute values from the provided {@code org.talend.sdk.component.api.record.Schema.Entry} instance. - * - * @param instance The instance from which to copy values - * @return {@code this} builder for use in a chained invocation - */ - public final Builder from(Schema.Entry instance) { - Objects.requireNonNull(instance, "instance"); - from((short) 0, (Object) instance); - return this; - } - - private void from(short _unused, Object object) { + private void from(final Object object) { long bits = 0; if (object instanceof Entry) { Entry instance = (Entry) object; @@ -731,10 +416,9 @@ private void from(short _unused, Object object) { this.isErrorCapable(instance.isErrorCapable()); bits |= 0x400L; } - putAllProperties(instance.getProperties()); } - if (object instanceof Schema.Entry) { - Schema.Entry instance = (Schema.Entry) object; + if (object instanceof Entry) { + Entry instance = (Entry) object; if ((bits & 0x1L) == 0) { this.elementSchema(instance.getElementSchema()); bits |= 0x1L; @@ -782,147 +466,74 @@ private void from(short _unused, Object object) { } } - /** - * Initializes the value for the {@link Entry#getName() name} attribute. - * - * @param name The value for name - * @return {@code this} builder for use in a chained invocation - */ - public final Builder name(String name) { + public final Builder name(final String name) { this.name = Objects.requireNonNull(name, "name"); initBits &= ~INIT_BIT_NAME; return this; } - /** - * Initializes the value for the {@link Entry#getRawName() rawName} attribute. - * - * @param rawName The value for rawName - * @return {@code this} builder for use in a chained invocation - */ - public final Builder rawName(String rawName) { + public final Builder rawName(final String rawName) { this.rawName = Objects.requireNonNull(rawName, "rawName"); initBits &= ~INIT_BIT_RAW_NAME; return this; } - /** - * Initializes the value for the {@link Entry#getOriginalFieldName() originalFieldName} attribute. - * - * @param originalFieldName The value for originalFieldName - * @return {@code this} builder for use in a chained invocation - */ - public final Builder originalFieldName(String originalFieldName) { + public final Builder originalFieldName(final String originalFieldName) { this.originalFieldName = Objects.requireNonNull(originalFieldName, "originalFieldName"); initBits &= ~INIT_BIT_ORIGINAL_FIELD_NAME; return this; } - /** - * Initializes the value for the {@link Entry#getType() type} attribute. - * - * @param type The value for type - * @return {@code this} builder for use in a chained invocation - */ - public final Builder type(Schema.Type type) { + public final Builder type(final Schema.Type type) { this.type = Objects.requireNonNull(type, "type"); initBits &= ~INIT_BIT_TYPE; return this; } - /** - * Initializes the value for the {@link Entry#isNullable() isNullable} attribute. - * - * @param isNullable The value for isNullable - * @return {@code this} builder for use in a chained invocation - */ - public final Builder isNullable(boolean isNullable) { - this.isNullable = isNullable; + public final Builder isNullable(final boolean isNullable) { + this.nullable = isNullable; initBits &= ~INIT_BIT_IS_NULLABLE; return this; } - /** - * Initializes the value for the {@link Entry#isMetadata() isMetadata} attribute. - * - * @param isMetadata The value for isMetadata - * @return {@code this} builder for use in a chained invocation - */ - public final Builder isMetadata(boolean isMetadata) { - this.isMetadata = isMetadata; + public final Builder isMetadata(final boolean isMetadata) { + this.metadata = isMetadata; initBits &= ~INIT_BIT_IS_METADATA; return this; } - /** - * Initializes the value for the {@link Entry#isErrorCapable() isErrorCapable} attribute. - * - * @param isErrorCapable The value for isErrorCapable - * @return {@code this} builder for use in a chained invocation - */ - public final Builder isErrorCapable(boolean isErrorCapable) { - this.isErrorCapable = isErrorCapable; + public final Builder isErrorCapable(final boolean isErrorCapable) { + this.errorCapable = isErrorCapable; initBits &= ~INIT_BIT_IS_ERROR_CAPABLE; return this; } - /** - * Initializes the value for the {@link Entry#isValid() isValid} attribute. - * - * @param isValid The value for isValid - * @return {@code this} builder for use in a chained invocation - */ - public final Builder isValid(boolean isValid) { - this.isValid = isValid; + public final Builder isValid(final boolean isValid) { + this.valid = isValid; initBits &= ~INIT_BIT_IS_VALID; return this; } - /** - * Initializes the value for the {@link Entry#getElementSchema() elementSchema} attribute. - * - * @param elementSchema The value for elementSchema - * @return {@code this} builder for use in a chained invocation - */ - public final Builder elementSchema(Schema elementSchema) { + public final Builder elementSchema(final Schema elementSchema) { this.elementSchema = Objects.requireNonNull(elementSchema, "elementSchema"); initBits &= ~INIT_BIT_ELEMENT_SCHEMA; return this; } - /** - * Initializes the value for the {@link Entry#getComment() comment} attribute. - * - * @param comment The value for comment - * @return {@code this} builder for use in a chained invocation - */ - public final Builder comment(String comment) { + public final Builder comment(final String comment) { this.comment = Objects.requireNonNull(comment, "comment"); initBits &= ~INIT_BIT_COMMENT; return this; } - /** - * Put one entry to the {@link Entry#getProps() props} map. - * - * @param key The key in the props map - * @param value The associated value in the props map - * @return {@code this} builder for use in a chained invocation - */ - public final Builder putProps(String key, String value) { + public final Builder putProps(final String key, final String value) { this.props.put( Objects.requireNonNull(key, "props key"), Objects.requireNonNull(value, value == null ? "props value for key: " + key : null)); return this; } - /** - * Put one entry to the {@link Entry#getProps() props} map. Nulls are not permitted - * - * @param entry The key and value entry - * @return {@code this} builder for use in a chained invocation - */ - public final Builder putProps(Map.Entry entry) { + public final Builder putProps(final Map.Entry entry) { String k = entry.getKey(); String v = entry.getValue(); this.props.put( @@ -931,24 +542,12 @@ public final Builder putProps(Map.Entry entry) { return this; } - /** - * Sets or replaces all mappings from the specified map as entries for the {@link Entry#getProps() props} map. Nulls are not permitted - * - * @param entries The entries that will be added to the props map - * @return {@code this} builder for use in a chained invocation - */ - public final Builder props(Map entries) { + public final Builder props(final Map entries) { this.props.clear(); return putAllProps(entries); } - /** - * Put all mappings from the specified map as entries to {@link Entry#getProps() props} map. Nulls are not permitted - * - * @param entries The entries that will be added to the props map - * @return {@code this} builder for use in a chained invocation - */ - public final Builder putAllProps(Map entries) { + public final Builder putAllProps(final Map entries) { for (Map.Entry e : entries.entrySet()) { String k = e.getKey(); String v = e.getValue(); @@ -959,119 +558,31 @@ public final Builder putAllProps(Map entries) { return this; } - /** - * Initializes the value for the {@link Entry#getInternalDefaultValue() internalDefaultValue} attribute. - * - * @param internalDefaultValue The value for internalDefaultValue - * @return {@code this} builder for use in a chained invocation - */ - public final Builder internalDefaultValue(Object internalDefaultValue) { + public final Builder internalDefaultValue(final Object internalDefaultValue) { this.internalDefaultValue = Objects.requireNonNull(internalDefaultValue, "internalDefaultValue"); initBits &= ~INIT_BIT_INTERNAL_DEFAULT_VALUE; return this; } - /** - * Put one entry to the {@link Entry#getProperties() properties} map. - * - * @param key The key in the properties map - * @param value The associated value in the properties map - * @return {@code this} builder for use in a chained invocation - */ - public final Builder putProperties(String key, String value) { - this.properties.put( - Objects.requireNonNull(key, "properties key"), - Objects.requireNonNull(value, value == null ? "properties value for key: " + key : null)); - return this; - } - - /** - * Put one entry to the {@link Entry#getProperties() properties} map. Nulls are not permitted - * - * @param entry The key and value entry - * @return {@code this} builder for use in a chained invocation - */ - public final Builder putProperties(Map.Entry entry) { - String k = entry.getKey(); - String v = entry.getValue(); - this.properties.put( - Objects.requireNonNull(k, "properties key"), - Objects.requireNonNull(v, v == null ? "properties value for key: " + k : null)); - return this; - } - - /** - * Sets or replaces all mappings from the specified map as entries for the {@link Entry#getProperties() properties} map. Nulls are not permitted - * - * @param entries The entries that will be added to the properties map - * @return {@code this} builder for use in a chained invocation - */ - public final Builder properties(Map entries) { - this.properties.clear(); - return putAllProperties(entries); - } - - /** - * Put all mappings from the specified map as entries to {@link Entry#getProperties() properties} map. Nulls are not permitted - * - * @param entries The entries that will be added to the properties map - * @return {@code this} builder for use in a chained invocation - */ - public final Builder putAllProperties(Map entries) { - for (Map.Entry e : entries.entrySet()) { - String k = e.getKey(); - String v = e.getValue(); - this.properties.put( - Objects.requireNonNull(k, "properties key"), - Objects.requireNonNull(v, v == null ? "properties value for key: " + k : null)); - } - return this; - } - - /** - * Builds a new {@link Entry EntryDetail}. - * - * @return An immutable instance of Entry - * @throws IllegalStateException if any required attributes are missing - */ public Entry build() { - if (initBits != 0) { - throw new IllegalStateException(formatRequiredAttributesMessage()); - } return new Entry( name, rawName, - originalFieldName, type, - isNullable, - isMetadata, - isErrorCapable, - isValid, + nullable, + metadata, + errorCapable, + valid, elementSchema, comment, createUnmodifiableMap(false, false, props), - internalDefaultValue, - createUnmodifiableMap(false, false, properties)); + internalDefaultValue); } - private String formatRequiredAttributesMessage() { - List attributes = new ArrayList<>(); - if ((initBits & INIT_BIT_NAME) != 0) attributes.add("name"); - if ((initBits & INIT_BIT_RAW_NAME) != 0) attributes.add("rawName"); - if ((initBits & INIT_BIT_ORIGINAL_FIELD_NAME) != 0) attributes.add("originalFieldName"); - if ((initBits & INIT_BIT_TYPE) != 0) attributes.add("type"); - if ((initBits & INIT_BIT_IS_NULLABLE) != 0) attributes.add("isNullable"); - if ((initBits & INIT_BIT_IS_METADATA) != 0) attributes.add("isMetadata"); - if ((initBits & INIT_BIT_IS_ERROR_CAPABLE) != 0) attributes.add("isErrorCapable"); - if ((initBits & INIT_BIT_IS_VALID) != 0) attributes.add("isValid"); - if ((initBits & INIT_BIT_ELEMENT_SCHEMA) != 0) attributes.add("elementSchema"); - if ((initBits & INIT_BIT_COMMENT) != 0) attributes.add("comment"); - if ((initBits & INIT_BIT_INTERNAL_DEFAULT_VALUE) != 0) attributes.add("internalDefaultValue"); - return "Cannot build Entry, some of required attributes are not set " + attributes; - } } - private static Map createUnmodifiableMap(boolean checkNulls, boolean skipNulls, Map map) { + private static Map createUnmodifiableMap(final boolean checkNulls, final boolean skipNulls, + final Map map) { switch (map.size()) { case 0: return Collections.emptyMap(); @@ -1095,7 +606,9 @@ private static Map createUnmodifiableMap(boolean checkNulls, boolea K k = e.getKey(); V v = e.getValue(); if (skipNulls) { - if (k == null || v == null) continue; + if (k == null || v == null) { + continue; + } } else if (checkNulls) { Objects.requireNonNull(k, "key"); Objects.requireNonNull(v, v == null ? "value for key: " + k : null); diff --git a/component-server-parent/component-server/pom.xml b/component-server-parent/component-server/pom.xml index e66b7a97df095..60707d9bfbb0c 100644 --- a/component-server-parent/component-server/pom.xml +++ b/component-server-parent/component-server/pom.xml @@ -197,6 +197,12 @@ ${project.version} test + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + test + diff --git a/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/EntryTest.java b/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/EntryTest.java new file mode 100644 index 0000000000000..2fbf6da679dac --- /dev/null +++ b/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/EntryTest.java @@ -0,0 +1,188 @@ +/** + * Copyright (C) 2006-2025 Talend Inc. - www.talend.com + * + * 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.talend.sdk.component.server; + +import com.fasterxml.jackson.databind.ObjectMapper; +import static org.junit.jupiter.api.Assertions.*; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.Test; +import org.talend.sdk.component.server.front.model.Entry; +import org.talend.sdk.component.server.front.model.Schema; + +/** + * Unit tests for {@link Entry}. + */ +class EntryTest { + + private Entry createValidEntry() { + return Entry.builder() + .name("name") + .rawName("raw") + .originalFieldName("original") + .type(Schema.Type.STRING) + .isNullable(true) + .isMetadata(false) + .isErrorCapable(true) + .isValid(true) + .comment("comment") + .putProps("p1", "v1") + .internalDefaultValue("default") + .build(); + } + + // ---------------------------------------------------------------------- + // Builder + // ---------------------------------------------------------------------- + + @Test + void builderCreatesValidEntry() { + Entry entry = createValidEntry(); + + assertEquals("name", entry.getName()); + assertEquals("raw", entry.getRawName()); + assertEquals("raw", entry.getOriginalFieldName()); + assertEquals(Schema.Type.STRING, entry.getType()); + assertTrue(entry.isNullable()); + assertFalse(entry.isMetadata()); + assertTrue(entry.isErrorCapable()); + assertTrue(entry.isValid()); + assertEquals("comment", entry.getComment()); + assertEquals("default", entry.getInternalDefaultValue()); + assertEquals("v1", entry.getProps().get("p1")); + } + + // ---------------------------------------------------------------------- + // withXxx methods + // ---------------------------------------------------------------------- + + @Test + void withNameSameValueReturnsSameInstance() { + Entry entry = createValidEntry(); + assertSame(entry, entry.withName("name")); + } + + @Test + void withNameDifferentValueReturnsNewInstance() { + Entry entry = createValidEntry(); + Entry modified = entry.withName("other"); + + assertNotSame(entry, modified); + assertEquals("other", modified.getName()); + assertEquals(entry.getRawName(), modified.getRawName()); + } + + @Test + void withIsNullableChangesValue() { + Entry entry = createValidEntry(); + Entry modified = entry.withIsNullable(false); + + assertFalse(modified.isNullable()); + assertTrue(entry.isNullable()); + } + + @Test + void withPropsReplacesMap() { + Entry entry = createValidEntry(); + + Map newProps = new HashMap<>(); + newProps.put("a", "b"); + + Entry modified = entry.withProps(newProps); + + assertEquals(Map.of("a", "b"), modified.getProps()); + assertEquals(Map.of("p1", "v1"), entry.getProps()); + } + + // ---------------------------------------------------------------------- + // Accessors + // ---------------------------------------------------------------------- + + @Test + void getDefaultValueIsTyped() { + Entry entry = createValidEntry(); + + String value = entry.getDefaultValue(); + assertEquals("default", value); + } + + @Test + void getPropReturnsProperty() { + Entry entry = createValidEntry(); + assertEquals("v1", entry.getProp("p1")); + assertNull(entry.getProp("k1")); + } + + // ---------------------------------------------------------------------- + // copyOf + // ---------------------------------------------------------------------- + + @Test + void copyOfReturnsSameInstanceForEntry() { + Entry entry = createValidEntry(); + assertSame(entry, Entry.copyOf(entry)); + } + + // ---------------------------------------------------------------------- + // JSON deserialization + // ---------------------------------------------------------------------- + + @Test + void deserializeEntryFromJson() throws Exception { + String json = """ + { + "name": "field", + "rawName": "field_raw", + "type": "STRING", + "nullable": true, + "metadata": false, + "errorCapable": true, + "valid": true, + "elementSchema": { + "type": "STRING" + }, + "comment": "test comment", + "props": { + "p1": "v1" + }, + "internalDefaultValue": "defaultValue" + } + """; + + ObjectMapper mapper = new ObjectMapper(); + Entry entry = mapper.readValue(json, Entry.class); + + assertEquals("field", entry.getName()); + assertEquals("field_raw", entry.getRawName()); + assertEquals("field_raw", entry.getOriginalFieldName()); + assertEquals(Schema.Type.STRING, entry.getType()); + + assertTrue(entry.isNullable()); + assertFalse(entry.isMetadata()); + assertTrue(entry.isErrorCapable()); + assertTrue(entry.isValid()); + + assertNotNull(entry.getElementSchema()); + assertEquals(Schema.Type.STRING, entry.getElementSchema().getType()); + + assertEquals("test comment", entry.getComment()); + assertEquals("v1", entry.getProps().get("p1")); + assertEquals("defaultValue", entry.getInternalDefaultValue()); + } +} + diff --git a/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/SchemaTest.java b/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/SchemaTest.java new file mode 100644 index 0000000000000..fa3f6ae2fbde4 --- /dev/null +++ b/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/SchemaTest.java @@ -0,0 +1,683 @@ +/** + * Copyright (C) 2006-2025 Talend Inc. - www.talend.com + * + * 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.talend.sdk.component.server.front; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import static org.talend.sdk.component.api.record.Schema.Type.LONG; +import static org.talend.sdk.component.api.record.Schema.Type.STRING; + +import java.io.StringReader; +import java.util.List; +import javax.json.bind.Jsonb; +import javax.json.bind.JsonbBuilder; + +import org.talend.sdk.component.api.record.Schema; +import org.talend.sdk.component.runtime.record.SchemaImpl; +import org.talend.sdk.component.server.front.model.Entry; +import org.talend.sdk.component.server.front.model.JsonEntryModel; +import org.talend.sdk.component.server.front.model.JsonSchemaModel; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class SchemaTest { + + private final Jsonb jsonb = JsonbBuilder.create(); + + + private org.talend.sdk.component.server.front.model.Schema emptySchema() { + // minimal self-referential schema to satisfy required fields + org.talend.sdk.component.server.front.model.Schema placeholder = + org.talend.sdk.component.server.front.model.Schema.builder() + .type(org.talend.sdk.component.server.front.model.Schema.Type.RECORD) + .build(); + + return org.talend.sdk.component.server.front.model.Schema.builder() + .type(org.talend.sdk.component.server.front.model.Schema.Type.RECORD) + .elementSchema(placeholder) + .build(); + } + + private Entry entry(String name) { + return Entry.builder() + .name(name) + .rawName(name) + .originalFieldName(name) + .type(org.talend.sdk.component.server.front.model.Schema.Type.STRING) + .isNullable(false) + .isMetadata(false) + .isErrorCapable(false) + .isValid(true) + .elementSchema(emptySchema()) + .comment("c") + .internalDefaultValue("") + .build(); + } + + // ---------------------------------------------------------------------- + // withEntries + // ---------------------------------------------------------------------- + + @Test + void withEntriesVarargsReplacesEntries() { + org.talend.sdk.component.server.front.model.Schema schema = emptySchema(); + + Entry e1 = entry("a"); + Entry e2 = entry("b"); + + org.talend.sdk.component.server.front.model.Schema updated = schema.withEntries(e1, e2); + + assertEquals(List.of(e1, e2), updated.getEntries()); + assertNotSame(schema, updated); + } + + @Test + void withEntriesIterableReplacesEntries() { + org.talend.sdk.component.server.front.model.Schema schema = emptySchema(); + Entry e1 = entry("x"); + + List list = List.of(e1); + org.talend.sdk.component.server.front.model.Schema updated = schema.withEntries(list); + + assertEquals(list, updated.getEntries()); + } + + @Test + void withEntriesReturnsSameInstanceWhenSameReference() { + Entry e1 = entry("a"); + + org.talend.sdk.component.server.front.model.Schema schema = + org.talend.sdk.component.server.front.model.Schema.builder() + .type(org.talend.sdk.component.server.front.model.Schema.Type.RECORD) + .elementSchema(emptySchema()) + .addEntries(e1) + .build(); + + // same list reference + org.talend.sdk.component.server.front.model.Schema same = schema.withEntries(schema.getEntries()); + + assertSame(schema, same); + } + + @Test + void withEntriesListIsUnmodifiable() { + org.talend.sdk.component.server.front.model.Schema schema = emptySchema(); + Entry e1 = entry("a"); + + org.talend.sdk.component.server.front.model.Schema updated = schema.withEntries(e1); + + assertThrows(UnsupportedOperationException.class, + () -> updated.getEntries().add(entry("b"))); + } + + // ---------------------------------------------------------------------- + // withMetadata + // ---------------------------------------------------------------------- + + @Test + void withMetadataVarargsReplacesMetadata() { + org.talend.sdk.component.server.front.model.Schema schema = emptySchema(); + + Entry m1 = entry("m1"); + Entry m2 = entry("m2"); + + org.talend.sdk.component.server.front.model.Schema updated = schema.withMetadata(m1, m2); + + assertEquals(List.of(m1, m2), updated.getMetadata()); + assertNotSame(schema, updated); + } + + @Test + void withMetadataIterableReplacesMetadata() { + org.talend.sdk.component.server.front.model.Schema schema = emptySchema(); + Entry m1 = entry("meta"); + + List list = List.of(m1); + org.talend.sdk.component.server.front.model.Schema updated = schema.withMetadata(list); + + assertEquals(list, updated.getMetadata()); + } + + @Test + void withMetadataReturnsSameInstanceWhenSameReference() { + Entry m1 = entry("meta"); + + org.talend.sdk.component.server.front.model.Schema schema = + org.talend.sdk.component.server.front.model.Schema.builder() + .type(org.talend.sdk.component.server.front.model.Schema.Type.RECORD) + .elementSchema(emptySchema()) + .addMetadata(m1) + .build(); + + org.talend.sdk.component.server.front.model.Schema same = schema.withMetadata(schema.getMetadata()); + + assertSame(schema, same); + } + + @Test + void withMetadataListIsUnmodifiable() { + org.talend.sdk.component.server.front.model.Schema schema = emptySchema(); + Entry m1 = entry("meta"); + + org.talend.sdk.component.server.front.model.Schema updated = schema.withMetadata(m1); + + assertThrows(UnsupportedOperationException.class, + () -> updated.getMetadata().add(entry("x"))); + } + + // ---------------------------------------------------------------------- + // Regression: ensure other fields are untouched + // ---------------------------------------------------------------------- + + @Test + void withEntriesDoesNotModifyMetadata() { + Entry meta = entry("meta"); + + org.talend.sdk.component.server.front.model.Schema schema = + org.talend.sdk.component.server.front.model.Schema.builder() + .type(org.talend.sdk.component.server.front.model.Schema.Type.RECORD) + .elementSchema(emptySchema()) + .addMetadata(meta) + .build(); + + org.talend.sdk.component.server.front.model.Schema updated = schema.withEntries(entry("field")); + + assertEquals(List.of(meta), updated.getMetadata()); + } + + @Test + void withMetadataDoesNotModifyEntries() { + Entry field = entry("field"); + + org.talend.sdk.component.server.front.model.Schema schema = + org.talend.sdk.component.server.front.model.Schema.builder() + .type(org.talend.sdk.component.server.front.model.Schema.Type.RECORD) + .elementSchema(emptySchema()) + .addEntries(field) + .build(); + + org.talend.sdk.component.server.front.model.Schema updated = schema.withMetadata(entry("meta")); + + assertEquals(List.of(field), updated.getEntries()); + } + + @Test + void shouldSerializeSchemaImplAndDeserializeToJsonSchemaModel() { + // given + final Schema schema = new SchemaImpl.BuilderImpl() // + .withType(Schema.Type.RECORD) + .withEntry(new SchemaImpl.EntryImpl.BuilderImpl() + .withName("id") + .withType(STRING) + .build()) + .withEntry(new SchemaImpl.EntryImpl.BuilderImpl() + .withName("field2") + .withType(LONG) + .withNullable(false) + .withComment("field2 comment") + .build()) + .withProp("namespace", "test") + .build(); + + // when: serialize SchemaImpl + String json = jsonb.toJson(schema); + + // then: sanity check JSON + assertTrue(json.contains("\"type\":\"RECORD\"")); + assertTrue(json.contains("\"entries\"")); + + // when: deserialize into Schema + org.talend.sdk.component.server.front.model.Schema model = jsonb.fromJson(new StringReader(json), + org.talend.sdk.component.server.front.model.Schema.class); + + // then + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.RECORD, model.getType()); + assertEquals("test", model.getProps().get("namespace")); + + assertEquals(2, model.getEntries().size()); + assertEquals("id", model.getEntries().get(0).getName()); + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.STRING, model.getEntries().get(0).getType()); + assertEquals("field2", model.getEntries().get(1).getName()); + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.LONG, model.getEntries().get(1).getType()); + assertEquals("field2 comment", model.getEntries().get(1).getComment()); + assertEquals(false, model.getEntries().get(1).isNullable()); + + + } + + @Test + void deserializeRecordSchemaWithEntriesAndMetadata() { + String json = + """ + { + "type": "RECORD", + "props": { + "p1": "v1" + }, + "entries": [ + { + "name": "id", + "rawName": "id", + "originalFieldName": "id", + "type": "INT", + "nullable": false, + "metadata": false, + "errorCapable": false, + "valid": true, + "defaultValue": 0, + "comment": "identifier", + "props": { + "logicalType": "int" + } + } + ], + "metadata": [ + { + "name": "source", + "rawName": "source", + "originalFieldName": "source", + "type": "STRING", + "nullable": true, + "metadata": true, + "errorCapable": false, + "valid": true, + "comment": "meta field", + "props": { + "m": "v" + } + } + ] + } + """; + + org.talend.sdk.component.server.front.model.Schema schema = jsonb.fromJson(new StringReader(json), + org.talend.sdk.component.server.front.model.Schema.class); + + assertNotNull(schema); + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.RECORD, schema.getType()); + + // ---- props + assertEquals("v1", schema.getProp("p1")); + assertEquals("v1", schema.getProps().get("p1")); + + // ---- entries + List entries = schema.getEntries(); + assertEquals(1, entries.size()); + + Entry id = entries.get(0); + assertEquals("id", id.getName()); + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.INT, id.getType()); + assertFalse(id.isNullable()); + assertEquals("identifier", id.getComment()); + assertEquals("int", id.getProp("logicalType")); + + // ---- metadata + List metadata = schema.getMetadata(); + assertEquals(1, metadata.size()); + + Entry source = metadata.get(0); + assertTrue(source.isMetadata()); + assertEquals("source", source.getName()); + assertEquals("v", source.getProp("m")); + + // ---- entry lookup + assertSame(id, schema.getEntries().get(0)); + assertSame(source, schema.getMetadata().get(0)); + } + + @Test + void deserializeArraySchemaWithElementSchema() { + String json = + """ + { + "type": "ARRAY", + "elementSchema": { + "type": "STRING" + } + } + """; + + org.talend.sdk.component.server.front.model.Schema schema = jsonb.fromJson(json, + org.talend.sdk.component.server.front.model.Schema.class); + + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.ARRAY, schema.getType()); + assertNotNull(schema.getElementSchema()); + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.STRING, schema.getElementSchema().getType()); + } + + @Test + void jsonPropIsParsedAsJsonWhenPossible() { + String json = + """ + { + "type": "RECORD", + "props": { + "config": "{\\"a\\":1}" + } + } + """; + + org.talend.sdk.component.server.front.model.Schema schema = jsonb.fromJson(json, + org.talend.sdk.component.server.front.model.Schema.class); + + assertNotNull(schema.getProp("config")); + assertEquals("{\"a\":1}", schema.getProp("config")); + } + + @Test + void unknownPropertyIsNull() { + String json = + """ + { + "type": "RECORD" + } + """; + + org.talend.sdk.component.server.front.model.Schema schema = jsonb.fromJson(json, + org.talend.sdk.component.server.front.model.Schema.class); + + assertNull(schema.getProps()); + } + + @Test + void testAllDataTypes() { + // given + final Schema schema = new SchemaImpl.BuilderImpl() // + .withType(Schema.Type.RECORD) + .withEntry(new SchemaImpl.EntryImpl.BuilderImpl() + .withName("id") + .withType(Schema.Type.INT) + .withNullable(false) + .withErrorCapable(true) + .build()) + .withEntry(new SchemaImpl.EntryImpl.BuilderImpl() + .withName("field_date") + .withType(Schema.Type.DATETIME) + .withNullable(false) + .withErrorCapable(false) + .withComment("field date") + .build()) + .withEntry(new SchemaImpl.EntryImpl.BuilderImpl() + .withName("field_boolean") + .withType(Schema.Type.BOOLEAN) + .withNullable(true) + .withComment("field boolean") + .build()) + .withEntry(new SchemaImpl.EntryImpl.BuilderImpl() + .withName("field_bytes") + .withType(Schema.Type.BYTES) + .withComment("field bytes") + .withNullable(true) + .build()) + .withEntry(new SchemaImpl.EntryImpl.BuilderImpl() + .withName("field_decimal") + .withType(Schema.Type.DECIMAL) + .withComment("field decimal") + .withNullable(true) + .build()) + .withEntry(new SchemaImpl.EntryImpl.BuilderImpl() + .withName("field_double") + .withType(Schema.Type.DOUBLE) + .withComment("field double") + .withNullable(true) + .build()) + .withEntry(new SchemaImpl.EntryImpl.BuilderImpl() + .withName("field_float") + .withType(Schema.Type.FLOAT) + .withComment("field float") + .withNullable(true) + .build()) + .withProp("namespace", "test") + .build(); + + // when: serialize SchemaImpl + String json = jsonb.toJson(schema); + + // then: sanity check JSON + assertTrue(json.contains("\"type\":\"RECORD\"")); + assertTrue(json.contains("\"entries\"")); + + // when: deserialize into Schema + org.talend.sdk.component.server.front.model.Schema model = jsonb.fromJson(new StringReader(json), + org.talend.sdk.component.server.front.model.Schema.class); + + // then + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.RECORD, model.getType()); + assertEquals("test", model.getProps().get("namespace")); + + assertEquals(7, model.getEntries().size()); + assertEquals("id", model.getEntries().get(0).getName()); + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.INT, model.getEntries().get(0).getType()); + assertFalse(model.getEntries().get(0).isNullable()); + assertTrue(model.getEntries().get(0).isErrorCapable()); + assertEquals("field_date", model.getEntries().get(1).getName()); + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.DATETIME, model.getEntries().get(1).getType()); + assertEquals("field date", model.getEntries().get(1).getComment()); + assertFalse(model.getEntries().get(1).isNullable()); + assertFalse(model.getEntries().get(1).isErrorCapable()); + + assertEquals("field_boolean", model.getEntries().get(2).getName()); + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.BOOLEAN, model.getEntries().get(2).getType()); + assertEquals("field boolean", model.getEntries().get(2).getComment()); + assertTrue(model.getEntries().get(2).isNullable()); + assertFalse(model.getEntries().get(2).isErrorCapable()); + assertEquals("field_bytes", model.getEntries().get(3).getName()); + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.BYTES, model.getEntries().get(3).getType()); + assertEquals("field bytes", model.getEntries().get(3).getComment()); + assertTrue(model.getEntries().get(3).isNullable()); + + assertEquals("field_decimal", model.getEntries().get(4).getName()); + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.DECIMAL, model.getEntries().get(4).getType()); + assertEquals("field decimal", model.getEntries().get(4).getComment()); + assertTrue(model.getEntries().get(4).isNullable()); + assertEquals("field_double", model.getEntries().get(5).getName()); + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.DOUBLE, model.getEntries().get(5).getType()); + assertEquals("field double", model.getEntries().get(5).getComment()); + assertTrue(model.getEntries().get(5).isNullable()); + + assertEquals("field_float", model.getEntries().get(6).getName()); + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.FLOAT, model.getEntries().get(6).getType()); + assertEquals("field float", model.getEntries().get(6).getComment()); + assertTrue(model.getEntries().get(6).isNullable()); + + assertEquals("id,field_date,field_boolean,field_bytes,field_decimal,field_double,field_float", + model.getProps().get("talend.fields.order")); + } + + @Test + void shouldParseArrayEntryElementSchema() { + Schema.Entry nameEntry = new SchemaImpl.EntryImpl.BuilderImpl() + .withName("name") + .withNullable(true) + .withType(Schema.Type.STRING) + .build(); + Schema.Entry ageEntry = new SchemaImpl.EntryImpl.BuilderImpl() + .withName("age") + .withNullable(true) + .withType(Schema.Type.INT) + .build(); + Schema customerSchema = new SchemaImpl.BuilderImpl() // + .withType(Schema.Type.RECORD) + .withEntry(nameEntry) + .withEntry(ageEntry) + .build(); + + final Schema schema = new SchemaImpl.BuilderImpl() // + .withType(Schema.Type.ARRAY) // + .withElementSchema(customerSchema) + .withProp("namespace", "test") + .build(); + + String json = jsonb.toJson(schema); + // when: deserialize into Schema + org.talend.sdk.component.server.front.model.Schema model = jsonb.fromJson(new StringReader(json), + org.talend.sdk.component.server.front.model.Schema.class); + + org.talend.sdk.component.server.front.model.Schema innerSchema = model.getElementSchema(); + + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.ARRAY, model.getType()); + assertNotNull(innerSchema); + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.RECORD, innerSchema.getType()); + //check entry name + final List entryNames = innerSchema.getEntries().stream().map(Entry::getName) + .toList(); + Assertions.assertEquals(2, entryNames.size()); + Assertions.assertTrue(entryNames.contains("name")); + Assertions.assertTrue(entryNames.contains("age")); + + //check entry type + final List entryTypes = + innerSchema.getEntries().stream().map(Entry::getType).toList(); + Assertions.assertTrue(entryTypes.contains(org.talend.sdk.component.server.front.model.Schema.Type.INT)); + Assertions.assertTrue(entryTypes.contains(org.talend.sdk.component.server.front.model.Schema.Type.STRING)); + } + + + @Test + void shouldMarkMetadataEntries() { + Schema.Entry meta1 = new SchemaImpl.EntryImpl.BuilderImpl() // + .withName("meta1") // + .withType(Schema.Type.INT) // + .withMetadata(true) // + .build(); + + Schema.Entry meta2 = new SchemaImpl.EntryImpl.BuilderImpl() // + .withName("meta2") // + .withType(Schema.Type.STRING) // + .withMetadata(true) // + .withNullable(true) // + .build(); + + Schema.Entry data1 = new SchemaImpl.EntryImpl.BuilderImpl() // + .withName("data1") // + .withType(Schema.Type.INT) // + .build(); + Schema.Entry data2 = new SchemaImpl.EntryImpl.BuilderImpl() // + .withName("data2") // + .withType(Schema.Type.STRING) // + .withNullable(true) // + .build(); + + Schema schema = new SchemaImpl.BuilderImpl() // + .withType(Schema.Type.RECORD) // + .withEntry(data1) // + .withEntry(meta1) // + .withEntry(data2) // + .withEntry(meta2) // + .build(); + + String json = jsonb.toJson(schema); + // when: deserialize into Schema + org.talend.sdk.component.server.front.model.Schema model = jsonb.fromJson(new StringReader(json), + org.talend.sdk.component.server.front.model.Schema.class); + + Entry metaEntry = model.getMetadata().get(0); + + //check entry name + final List entryNames = model.getEntries().stream().map(Entry::getName) + .toList(); + Assertions.assertEquals(2, entryNames.size()); + Assertions.assertTrue(entryNames.contains(data1.getName())); + Assertions.assertTrue(entryNames.contains(data2.getName())); + Assertions.assertEquals(4, schema.getAllEntries().count()); + + //check entry type + final List entryTypes = model.getEntries().stream().map(Entry::getType) + .map(org.talend.sdk.component.server.front.model.Schema.Type::name) + .toList(); + Assertions.assertEquals(2, entryTypes.size()); + Assertions.assertTrue(entryTypes.contains(data1.getType().name())); + Assertions.assertTrue(entryTypes.contains(data2.getType().name())); + + //check meta name + final List metaEntryNames = model.getMetadata().stream().map(Entry::getName) + .toList(); + Assertions.assertEquals(2, metaEntryNames.size()); + Assertions.assertTrue(metaEntryNames.contains(meta1.getName())); + Assertions.assertTrue(metaEntryNames.contains(meta2.getName())); + + //check meta type + final List metaEntryTypes = model.getMetadata().stream().map(Entry::getType) + .map(org.talend.sdk.component.server.front.model.Schema.Type::name) + .toList(); + Assertions.assertEquals(2, metaEntryTypes.size()); + Assertions.assertTrue(metaEntryTypes.contains(meta1.getType().name())); + Assertions.assertTrue(metaEntryTypes.contains(meta2.getType().name())); + + assertTrue(metaEntry.isMetadata()); + assertEquals("meta1", metaEntry.getName()); + } + + @Test + void shouldParseEntryProps() { + Schema schema = new SchemaImpl.BuilderImpl() // + .withType(Schema.Type.RECORD) + //.type(Schema.Type.RECORD) + .withEntry(new SchemaImpl.EntryImpl.BuilderImpl() + .withName("field") + .withType(Schema.Type.STRING) + .withProp("format", "email") + .build()) + .build(); + + String json = jsonb.toJson(schema); + JsonSchemaModel model = new JsonSchemaModel(json); + + JsonEntryModel entry = model.getEntries().get(0); + + assertEquals("email", entry.getProps().get("format")); + } + + @Test + void shouldFailForInvalidEntryType() { + String invalidJson = """ + { + "type": "RECORD", + "entries": [ + { "name": "x", "type": "INVALID" } + ] + } + """; + + assertThrows(IllegalArgumentException.class, + () -> new JsonSchemaModel(invalidJson)); + } + + + @Test + void shouldFailForInvalidSchemaType() { + String invalidJson = """ + { + "type": "NOT_VALID", + "entries": [] + } + """; + + assertThrows(IllegalArgumentException.class, + () -> new JsonSchemaModel(invalidJson)); + } + +} + + From b6ca07cb3404e31a60204f32447a027a2309569e Mon Sep 17 00:00:00 2001 From: yyin-talend Date: Fri, 9 Jan 2026 14:33:38 +0800 Subject: [PATCH 3/8] clean and add junit --- .../component/server/front/model/Schema.java | 605 +++++------------- 1 file changed, 158 insertions(+), 447 deletions(-) diff --git a/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Schema.java b/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Schema.java index 0c64631393144..d83db6c12ec84 100644 --- a/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Schema.java +++ b/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Schema.java @@ -1,302 +1,177 @@ +/** + * Copyright (C) 2006-2025 Talend Inc. - www.talend.com + * + * 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.talend.sdk.component.server.front.model; +import java.beans.ConstructorProperties; +import java.math.BigDecimal; +import java.time.temporal.Temporal; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Date; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.stream.Stream; -public final class Schema implements org.talend.sdk.component.api.record.Schema { +import lombok.Getter; + +public final class Schema { + + @Getter private final Type type; - private final org.talend.sdk.component.api.record.Schema elementSchema; + + @Getter + private final Schema elementSchema; + + @Getter private final List entries; + + @Getter private final List metadata; - private final Stream allEntries; + + @Getter private final Map props; - private final Map properties; + @ConstructorProperties({"type", "elementSchema", "entries", "metadata", "props"}) private Schema( - Type type, - org.talend.sdk.component.api.record.Schema elementSchema, - List entries, - List metadata, - Stream allEntries, - Map props, - Map properties) { + final Type type, + final Schema elementSchema, + final List entries, + final List metadata, + final Map props) { this.type = type; this.elementSchema = elementSchema; this.entries = entries; this.metadata = metadata; - this.allEntries = allEntries; this.props = props; - this.properties = properties; - } - - @Override - public String getProp(String key){ - return this.getProperties().get(key); - } - - /** - * @return The value of the {@code type} attribute - */ - @Override - public Type getType() { - return type; - } - - /** - * @return The value of the {@code elementSchema} attribute - */ - @Override - public org.talend.sdk.component.api.record.Schema getElementSchema() { - return elementSchema; } - /** - * @return The value of the {@code entries} attribute - */ - @Override - public List getEntries() { - return entries; + public String getProp(final String key){ + return this.props.get(key); } - /** - * @return The value of the {@code metadata} attribute - */ - @Override - public List getMetadata() { - return metadata; - } - - /** - * @return The value of the {@code allEntries} attribute - */ - @Override - public Stream getAllEntries() { - return allEntries; - } - - /** - * @return The value of the {@code props} attribute - */ - @Override - public Map getProps() { - return props; - } - - public Map getProperties() { - return properties; - } - - /** - * Copy the current immutable object by setting a value for the {@link Schema#getType() type} attribute. - * A value equality check is used to prevent copying of the same value by returning {@code this}. - * @param value A new value for type - * @return A modified copy of the {@code this} object - */ - public final Schema withType(Type value) { + public final Schema withType(final Type value) { Type newValue = Objects.requireNonNull(value, "type"); - if (this.type == newValue) return this; + if (this.type == newValue) { + return this; + } return new Schema( newValue, this.elementSchema, this.entries, this.metadata, - this.allEntries, - this.props, - this.properties); + this.props); } - /** - * Copy the current immutable object by setting a value for the {@link Schema#getElementSchema() elementSchema} attribute. - * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}. - * @param value A new value for elementSchema - * @return A modified copy of the {@code this} object - */ - public final Schema withElementSchema(org.talend.sdk.component.api.record.Schema value) { - if (this.elementSchema == value) return this; - org.talend.sdk.component.api.record.Schema newValue = Objects.requireNonNull(value, "elementSchema"); - return new Schema(this.type, newValue, this.entries, this.metadata, this.allEntries, this.props, this.properties); + public final Schema withElementSchema(final Schema value) { + if (this.elementSchema == value) { + return this; + } + Schema newValue = Objects.requireNonNull(value, "elementSchema"); + return new Schema(this.type, newValue, this.entries, this.metadata, this.props); } - /** - * Copy the current immutable object with elements that replace the content of {@link Schema#getEntries() entries}. - * @param elements The elements to set - * @return A modified copy of {@code this} object - */ - public final Schema withEntries(Entry... elements) { + public final Schema withEntries(final Entry... elements) { List newValue = createUnmodifiableList(false, createSafeList(Arrays.asList(elements), true, false)); return new Schema( this.type, this.elementSchema, newValue, this.metadata, - this.allEntries, - this.props, - this.properties); + this.props); } - /** - * Copy the current immutable object with elements that replace the content of {@link Schema#getEntries() entries}. - * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}. - * @param elements An iterable of entries elements to set - * @return A modified copy of {@code this} object - */ - public final Schema withEntries(Iterable elements) { - if (this.entries == elements) return this; + public final Schema withEntries(final Iterable elements) { + if (this.entries == elements) { + return this; + } List newValue = createUnmodifiableList(false, createSafeList(elements, true, false)); return new Schema( this.type, this.elementSchema, newValue, this.metadata, - this.allEntries, - this.props, - this.properties); + this.props); } - /** - * Copy the current immutable object with elements that replace the content of {@link Schema#getMetadata() metadata}. - * @param elements The elements to set - * @return A modified copy of {@code this} object - */ - public final Schema withMetadata(Entry... elements) { + public final Schema withMetadata(final Entry... elements) { List newValue = createUnmodifiableList(false, createSafeList(Arrays.asList(elements), true, false)); return new Schema( this.type, this.elementSchema, this.entries, newValue, - this.allEntries, - this.props, - this.properties); + this.props); } - /** - * Copy the current immutable object with elements that replace the content of {@link Schema#getMetadata() metadata}. - * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}. - * @param elements An iterable of metadata elements to set - * @return A modified copy of {@code this} object - */ - public final Schema withMetadata(Iterable elements) { - if (this.metadata == elements) return this; + public final Schema withMetadata(final Iterable elements) { + if (this.metadata == elements) { + return this; + } List newValue = createUnmodifiableList(false, createSafeList(elements, true, false)); return new Schema( this.type, this.elementSchema, this.entries, newValue, - this.allEntries, - this.props, - this.properties); - } - - /** - * Copy the current immutable object by setting a value for the {@link Schema#getAllEntries() allEntries} attribute. - * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}. - * @param value A new value for allEntries - * @return A modified copy of the {@code this} object - */ - public final Schema withAllEntries(Stream value) { - if (this.allEntries == value) return this; - Stream newValue = Objects.requireNonNull(value, "allEntries"); - return new Schema( - this.type, - this.elementSchema, - this.entries, - this.metadata, - newValue, - this.props, - this.properties); - } - - /** - * Copy the current immutable object by replacing the {@link Schema#getProps() props} map with the specified map. - * Nulls are not permitted as keys or values. - * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}. - * @param entries The entries to be added to the props map - * @return A modified copy of {@code this} object - */ - public final Schema withProps(Map entries) { - if (this.props == entries) return this; - Map newValue = createUnmodifiableMap(true, false, entries); - return new Schema( - this.type, - this.elementSchema, - this.entries, - this.metadata, - this.allEntries, - newValue, - this.properties); + this.props); } - /** - * Copy the current immutable object by replacing the {@link Schema#getProperties() properties} map with the specified map. - * Nulls are not permitted as keys or values. - * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}. - * @param entries The entries to be added to the properties map - * @return A modified copy of {@code this} object - */ - public final Schema withProperties(Map entries) { - if (this.properties == entries) return this; + public final Schema withProps(final Map entries) { + if (this.props == entries) { + return this; + } Map newValue = createUnmodifiableMap(true, false, entries); return new Schema( this.type, this.elementSchema, this.entries, this.metadata, - this.allEntries, - this.props, newValue); } - /** - * This instance is equal to all instances of {@code SchemaDetail} that have equal attribute values. - * @return {@code true} if {@code this} is equal to {@code another} instance - */ @Override - public boolean equals(Object another) { - if (this == another) return true; + public boolean equals(final Object another) { + if (this == another) { + return true; + } return another instanceof Schema && equalTo(0, (Schema) another); } - private boolean equalTo(int synthetic, Schema another) { + private boolean equalTo(final int synthetic, final Schema another) { return type.equals(another.type) && elementSchema.equals(another.elementSchema) && entries.equals(another.entries) && metadata.equals(another.metadata) - && allEntries.equals(another.allEntries) - && props.equals(another.props) - && properties.equals(another.properties); + && props.equals(another.props); } - /** - * Computes a hash code from attributes: {@code type}, {@code elementSchema}, {@code entries}, {@code metadata}, {@code allEntries}, {@code props}, {@code properties}. - * @return hashCode value - */ - @Override public int hashCode() { int h = 5381; h += (h << 5) + type.hashCode(); h += (h << 5) + elementSchema.hashCode(); h += (h << 5) + entries.hashCode(); h += (h << 5) + metadata.hashCode(); - h += (h << 5) + allEntries.hashCode(); h += (h << 5) + props.hashCode(); - h += (h << 5) + properties.hashCode(); return h; } - /** - * Prints the immutable value {@code Schema} with attribute values. - * @return A string representation of the value - */ @Override public String toString() { return "Schema{" @@ -304,21 +179,12 @@ public String toString() { + ", elementSchema=" + elementSchema + ", entries=" + entries + ", metadata=" + metadata - + ", allEntries=" + allEntries + ", props=" + props - + ", properties=" + properties + "}"; } - /** - * Creates an immutable copy of a {@link Schema} value. - * Uses accessors to get values to initialize the new immutable instance. - * If an instance is already immutable, it is returned as is. - * @param instance The instance to copy - * @return A copied immutable Schema instance - */ - public static Schema copyOf(Schema instance) { - if (instance instanceof Schema) { + public static Schema copyOf(final Schema instance) { + if (instance != null && instance instanceof Schema) { return (Schema) instance; } return Schema.builder() @@ -326,32 +192,10 @@ public static Schema copyOf(Schema instance) { .build(); } - /** - * Creates a builder for {@link Schema SchemaDetail}. - *

-   * SchemaDetail.builder()
-   *    .type(org.talend.sdk.component.api.record.Schema.Type) // required {@link Schema#getType() type}
-   *    .elementSchema(org.talend.sdk.component.api.record.Schema) // required {@link Schema#getElementSchema() elementSchema}
-   *    .addEntries|addAllEntries(org.talend.sdk.component.api.record.Schema.Entry) // {@link Schema#getEntries() entries} elements
-   *    .addMetadata|addAllMetadata(org.talend.sdk.component.api.record.Schema.Entry) // {@link Schema#getMetadata() metadata} elements
-   *    .allEntries(stream.Stream&lt;org.talend.sdk.component.api.record.Schema.Entry&gt;) // required {@link Schema#getAllEntries() allEntries}
-   *    .putProps|putAllProps(String => String) // {@link Schema#getProps() props} mappings
-   *    .putProperties|putAllProperties(String => String) // {@link Schema#getProperties() properties} mappings
-   *    .build();
-   * 
- * @return A new SchemaDetail builder - */ public static Schema.Builder builder() { return new Schema.Builder(); } - /** - * Builds instances of type {@link Schema SchemaDetail}. - * Initialize attributes and then invoke the {@link #build()} method to create an - * immutable instance. - *

{@code Builder} is not thread-safe and generally should not be stored in a field or collection, - * but instead used immediately to create instances. - */ public static final class Builder { private static final long INIT_BIT_TYPE = 0x1L; private static final long INIT_BIT_ELEMENT_SCHEMA = 0x2L; @@ -359,42 +203,24 @@ public static final class Builder { private long initBits = 0x7L; private Type type; - private org.talend.sdk.component.api.record.Schema elementSchema; + private Schema elementSchema; private List entries = new ArrayList(); private List metadata = new ArrayList(); - private Stream allEntries; private Map props = new LinkedHashMap(); - private Map properties = new LinkedHashMap(); private Builder() { } - /** - * Fill a builder with attribute values from the provided {@code org.talend.sdk.component.api.record.Schema} instance. - * @param instance The instance from which to copy values - * @return {@code this} builder for use in a chained invocation - */ - public final Builder from(org.talend.sdk.component.api.record.Schema instance) { + public final Builder from(final Schema instance) { Objects.requireNonNull(instance, "instance"); - from((short) 0, (Object) instance); + from((Object) instance); return this; } - /** - * Fill a builder with attribute values from the provided {@code org.talend.sdk.component.server.front.model.Schema} instance. - * @param instance The instance from which to copy values - * @return {@code this} builder for use in a chained invocation - */ - public final Builder from(Schema instance) { - Objects.requireNonNull(instance, "instance"); - from((short) 0, (Object) instance); - return this; - } - - private void from(short _unused, Object object) { + private void from(final Object object) { long bits = 0; - if (object instanceof org.talend.sdk.component.api.record.Schema) { - org.talend.sdk.component.api.record.Schema instance = (org.talend.sdk.component.api.record.Schema) object; + if (object instanceof Schema) { + Schema instance = (Schema) object; if ((bits & 0x1L) == 0) { this.elementSchema(instance.getElementSchema()); bits |= 0x1L; @@ -407,10 +233,6 @@ private void from(short _unused, Object object) { addAllEntries(instance.getEntries()); bits |= 0x20L; } - if ((bits & 0x4L) == 0) { - this.allEntries(instance.getAllEntries()); - bits |= 0x4L; - } if ((bits & 0x8L) == 0) { this.type(instance.getType()); bits |= 0x8L; @@ -434,164 +256,81 @@ private void from(short _unused, Object object) { addAllEntries(instance.getEntries()); bits |= 0x20L; } - if ((bits & 0x4L) == 0) { - this.allEntries(instance.getAllEntries()); - bits |= 0x4L; - } if ((bits & 0x8L) == 0) { this.type(instance.getType()); bits |= 0x8L; } - putAllProperties(instance.getProperties()); - if ((bits & 0x10L) == 0) { - putAllProps(instance.getProps()); - bits |= 0x10L; - } } } - /** - * Initializes the value for the {@link Schema#getType() type} attribute. - * @param type The value for type - * @return {@code this} builder for use in a chained invocation - */ - public final Builder type(Type type) { + public final Builder type(final Type type) { this.type = Objects.requireNonNull(type, "type"); initBits &= ~INIT_BIT_TYPE; return this; } - /** - * Initializes the value for the {@link Schema#getElementSchema() elementSchema} attribute. - * @param elementSchema The value for elementSchema - * @return {@code this} builder for use in a chained invocation - */ - public final Builder elementSchema(org.talend.sdk.component.api.record.Schema elementSchema) { + public final Builder elementSchema(final Schema elementSchema) { this.elementSchema = Objects.requireNonNull(elementSchema, "elementSchema"); initBits &= ~INIT_BIT_ELEMENT_SCHEMA; return this; } - /** - * Adds one element to {@link Schema#getEntries() entries} list. - * @param element A entries element - * @return {@code this} builder for use in a chained invocation - */ - public final Builder addEntries(Entry element) { + public final Builder addEntries(final Entry element) { this.entries.add(Objects.requireNonNull(element, "entries element")); return this; } - /** - * Adds elements to {@link Schema#getEntries() entries} list. - * @param elements An array of entries elements - * @return {@code this} builder for use in a chained invocation - */ - public final Builder addEntries(Entry... elements) { + public final Builder addEntries(final Entry... elements) { for (Entry element : elements) { this.entries.add(Objects.requireNonNull(element, "entries element")); } return this; } - - /** - * Sets or replaces all elements for {@link Schema#getEntries() entries} list. - * @param elements An iterable of entries elements - * @return {@code this} builder for use in a chained invocation - */ - public final Builder entries(Iterable elements) { + public final Builder entries(final Iterable elements) { this.entries.clear(); return addAllEntries(elements); } - /** - * Adds elements to {@link Schema#getEntries() entries} list. - * @param elements An iterable of entries elements - * @return {@code this} builder for use in a chained invocation - */ - public final Builder addAllEntries(Iterable elements) { + public final Builder addAllEntries(final Iterable elements) { for (Entry element : elements) { this.entries.add(Objects.requireNonNull(element, "entries element")); } return this; } - /** - * Adds one element to {@link Schema#getMetadata() metadata} list. - * @param element A metadata element - * @return {@code this} builder for use in a chained invocation - */ - public final Builder addMetadata(Entry element) { + public final Builder addMetadata(final Entry element) { this.metadata.add(Objects.requireNonNull(element, "metadata element")); return this; } - /** - * Adds elements to {@link Schema#getMetadata() metadata} list. - * @param elements An array of metadata elements - * @return {@code this} builder for use in a chained invocation - */ - public final Builder addMetadata(Entry... elements) { + public final Builder addMetadata(final Entry... elements) { for (Entry element : elements) { this.metadata.add(Objects.requireNonNull(element, "metadata element")); } return this; } - - /** - * Sets or replaces all elements for {@link Schema#getMetadata() metadata} list. - * @param elements An iterable of metadata elements - * @return {@code this} builder for use in a chained invocation - */ - public final Builder metadata(Iterable elements) { + public final Builder metadata(final Iterable elements) { this.metadata.clear(); return addAllMetadata(elements); } - /** - * Adds elements to {@link Schema#getMetadata() metadata} list. - * @param elements An iterable of metadata elements - * @return {@code this} builder for use in a chained invocation - */ - public final Builder addAllMetadata(Iterable elements) { + public final Builder addAllMetadata(final Iterable elements) { for (Entry element : elements) { this.metadata.add(Objects.requireNonNull(element, "metadata element")); } return this; } - /** - * Initializes the value for the {@link Schema#getAllEntries() allEntries} attribute. - * @param allEntries The value for allEntries - * @return {@code this} builder for use in a chained invocation - */ - public final Builder allEntries(Stream allEntries) { - this.allEntries = Objects.requireNonNull(allEntries, "allEntries"); - initBits &= ~INIT_BIT_ALL_ENTRIES; - return this; - } - - /** - * Put one entry to the {@link Schema#getProps() props} map. - * @param key The key in the props map - * @param value The associated value in the props map - * @return {@code this} builder for use in a chained invocation - */ - public final Builder putProps(String key, String value) { + public final Builder putProps(final String key, final String value) { this.props.put( Objects.requireNonNull(key, "props key"), Objects.requireNonNull(value, value == null ? "props value for key: " + key : null)); return this; } - /** - * Put one entry to the {@link Schema#getProps() props} map. Nulls are not permitted - * @param entry The key and value entry - * @return {@code this} builder for use in a chained invocation - */ - public final Builder putProps(Map.Entry entry) { + public final Builder putProps(final Map.Entry entry) { String k = entry.getKey(); String v = entry.getValue(); this.props.put( @@ -600,22 +339,12 @@ public final Builder putProps(Map.Entry entry) { return this; } - /** - * Sets or replaces all mappings from the specified map as entries for the {@link Schema#getProps() props} map. Nulls are not permitted - * @param entries The entries that will be added to the props map - * @return {@code this} builder for use in a chained invocation - */ - public final Builder props(Map entries) { + public final Builder props(final Map entries) { this.props.clear(); return putAllProps(entries); } - /** - * Put all mappings from the specified map as entries to {@link Schema#getProps() props} map. Nulls are not permitted - * @param entries The entries that will be added to the props map - * @return {@code this} builder for use in a chained invocation - */ - public final Builder putAllProps(Map entries) { + public final Builder putAllProps(final Map entries) { for (Map.Entry e : entries.entrySet()) { String k = e.getKey(); String v = e.getValue(); @@ -626,105 +355,41 @@ public final Builder putAllProps(Map entries) { return this; } - /** - * Put one entry to the {@link Schema#getProperties() properties} map. - * @param key The key in the properties map - * @param value The associated value in the properties map - * @return {@code this} builder for use in a chained invocation - */ - public final Builder putProperties(String key, String value) { - this.properties.put( - Objects.requireNonNull(key, "properties key"), - Objects.requireNonNull(value, value == null ? "properties value for key: " + key : null)); - return this; - } - - /** - * Put one entry to the {@link Schema#getProperties() properties} map. Nulls are not permitted - * @param entry The key and value entry - * @return {@code this} builder for use in a chained invocation - */ - public final Builder putProperties(Map.Entry entry) { - String k = entry.getKey(); - String v = entry.getValue(); - this.properties.put( - Objects.requireNonNull(k, "properties key"), - Objects.requireNonNull(v, v == null ? "properties value for key: " + k : null)); - return this; - } - - /** - * Sets or replaces all mappings from the specified map as entries for the {@link Schema#getProperties() properties} map. Nulls are not permitted - * @param entries The entries that will be added to the properties map - * @return {@code this} builder for use in a chained invocation - */ - public final Builder properties(Map entries) { - this.properties.clear(); - return putAllProperties(entries); - } - - /** - * Put all mappings from the specified map as entries to {@link Schema#getProperties() properties} map. Nulls are not permitted - * @param entries The entries that will be added to the properties map - * @return {@code this} builder for use in a chained invocation - */ - public final Builder putAllProperties(Map entries) { - for (Map.Entry e : entries.entrySet()) { - String k = e.getKey(); - String v = e.getValue(); - this.properties.put( - Objects.requireNonNull(k, "properties key"), - Objects.requireNonNull(v, v == null ? "properties value for key: " + k : null)); - } - return this; - } - - /** - * Builds a new {@link Schema SchemaDetail}. - * @return An immutable instance of Schema - * @throws IllegalStateException if any required attributes are missing - */ public Schema build() { - if (initBits != 0) { - throw new IllegalStateException(formatRequiredAttributesMessage()); - } return new Schema( type, elementSchema, createUnmodifiableList(true, entries), createUnmodifiableList(true, metadata), - allEntries, - createUnmodifiableMap(false, false, props), - createUnmodifiableMap(false, false, properties)); + createUnmodifiableMap(false, false, props)); } - private String formatRequiredAttributesMessage() { - List attributes = new ArrayList<>(); - if ((initBits & INIT_BIT_TYPE) != 0) attributes.add("type"); - if ((initBits & INIT_BIT_ELEMENT_SCHEMA) != 0) attributes.add("elementSchema"); - if ((initBits & INIT_BIT_ALL_ENTRIES) != 0) attributes.add("allEntries"); - return "Cannot build Schema, some of required attributes are not set " + attributes; - } } - private static List createSafeList(Iterable iterable, boolean checkNulls, boolean skipNulls) { + private static List createSafeList(final Iterable iterable, final boolean checkNulls, final boolean skipNulls) { ArrayList list; if (iterable instanceof Collection) { int size = ((Collection) iterable).size(); - if (size == 0) return Collections.emptyList(); + if (size == 0) { + return Collections.emptyList(); + } list = new ArrayList<>(size); } else { list = new ArrayList<>(); } for (T element : iterable) { - if (skipNulls && element == null) continue; - if (checkNulls) Objects.requireNonNull(element, "element"); + if (skipNulls && element == null) { + continue; + } + if (checkNulls) { + Objects.requireNonNull(element, "element"); + } list.add(element); } return list; } - private static List createUnmodifiableList(boolean clone, List list) { + private static List createUnmodifiableList(final boolean clone, final List list) { switch(list.size()) { case 0: return Collections.emptyList(); case 1: return Collections.singletonList(list.get(0)); @@ -740,7 +405,8 @@ private static List createUnmodifiableList(boolean clone, List list) { } } - private static Map createUnmodifiableMap(boolean checkNulls, boolean skipNulls, Map map) { + private static Map createUnmodifiableMap(final boolean checkNulls, final boolean skipNulls, + final Map map) { switch (map.size()) { case 0: return Collections.emptyMap(); case 1: { @@ -763,7 +429,9 @@ private static Map createUnmodifiableMap(boolean checkNulls, boolea K k = e.getKey(); V v = e.getValue(); if (skipNulls) { - if (k == null || v == null) continue; + if (k == null || v == null) { + continue; + } } else if (checkNulls) { Objects.requireNonNull(k, "key"); Objects.requireNonNull(v, v == null ? "value for key: " + k : null); @@ -777,4 +445,47 @@ private static Map createUnmodifiableMap(boolean checkNulls, boolea } } } + + public enum Type { + + RECORD(new Class[] { Record.class }), + ARRAY(new Class[] { Collection.class }), + STRING(new Class[] { String.class, Object.class }), + BYTES(new Class[] { byte[].class, Byte[].class }), + INT(new Class[] { Integer.class }), + LONG(new Class[] { Long.class }), + FLOAT(new Class[] { Float.class }), + DOUBLE(new Class[] { Double.class }), + BOOLEAN(new Class[] { Boolean.class }), + DATETIME(new Class[] { Long.class, Date.class, Temporal.class }), + DECIMAL(new Class[] { BigDecimal.class }); + + /** + * All compatibles Java classes + */ + private final Class[] classes; + + Type(final Class[] classes) { + this.classes = classes; + } + + /** + * Check if input can be affected to an entry of this type. + * + * @param input : object. + * + * @return true if input is null or ok. + */ + public boolean isCompatible(final Object input) { + if (input == null) { + return true; + } + for (final Class clazz : classes) { + if (clazz.isInstance(input)) { + return true; + } + } + return false; + } + } } \ No newline at end of file From af87fd2c7c2945087d028b06433ccd382de73631 Mon Sep 17 00:00:00 2001 From: yyin-talend Date: Mon, 12 Jan 2026 14:33:36 +0800 Subject: [PATCH 4/8] clean and fix sonars --- .../component/server/front/model/Entry.java | 549 +----------------- .../component/server/front/model/Schema.java | 396 +------------ .../sdk/component/server/EntryTest.java | 78 +-- .../component/server/front/SchemaTest.java | 272 +++------ 4 files changed, 90 insertions(+), 1205 deletions(-) diff --git a/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Entry.java b/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Entry.java index 6f6c27e543779..6939a68c5f4d3 100644 --- a/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Entry.java +++ b/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Entry.java @@ -16,50 +16,39 @@ package org.talend.sdk.component.server.front.model; import java.beans.ConstructorProperties; -import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; -import java.util.Objects; -import lombok.Getter; +import lombok.Data; +@Data public final class Entry { - @Getter + private final String name; - @Getter private final String rawName; - @Getter private final Schema.Type type; - @Getter private final boolean nullable; - @Getter private final boolean metadata; - @Getter private final boolean errorCapable; - @Getter private final boolean valid; - @Getter private final Schema elementSchema; - @Getter private final String comment; - @Getter - private final Map props = new LinkedHashMap<>(0);; + private final Map props = new LinkedHashMap<>(0); - @Getter private final Object internalDefaultValue; @ConstructorProperties({"name", "rawName", "type", "nullable", "metadata", "errorCapable", - "valid", "elementSchema", "comment", "props", "internalDefaultValue"}) - private Entry( + "valid", "elementSchema", "comment", "props", "internalDefaultValue"}) + public Entry( final String name, final String rawName, final Schema.Type type, @@ -96,530 +85,4 @@ public String getProp(final String key) { return this.props.get(key); } - public final Entry withName(final String value) { - String newValue = Objects.requireNonNull(value, "name"); - if (this.name.equals(newValue)) { - return this; - } - return new Entry( - newValue, - this.rawName, - this.type, - this.nullable, - this.metadata, - this.errorCapable, - this.valid, - this.elementSchema, - this.comment, - this.props, - this.internalDefaultValue); - } - - public final Entry withRawName(final String value) { - String newValue = Objects.requireNonNull(value, "rawName"); - if (this.rawName.equals(newValue)) { - return this; - } - return new Entry( - this.name, - newValue, - this.type, - this.nullable, - this.metadata, - this.errorCapable, - this.valid, - this.elementSchema, - this.comment, - this.props, - this.internalDefaultValue); - } - - public final Entry withType(final Schema.Type value) { - Schema.Type newValue = Objects.requireNonNull(value, "type"); - if (this.type == newValue) { - return this; - } - return new Entry( - this.name, - this.rawName, - newValue, - this.nullable, - this.metadata, - this.errorCapable, - this.valid, - this.elementSchema, - this.comment, - this.props, - this.internalDefaultValue); - } - - public final Entry withIsNullable(final boolean value) { - if (this.nullable == value) { - return this; - } - return new Entry( - this.name, - this.rawName, - this.type, - value, - this.metadata, - this.errorCapable, - this.valid, - this.elementSchema, - this.comment, - this.props, - this.internalDefaultValue); - } - - public final Entry withIsMetadata(final boolean value) { - if (this.metadata == value) { - return this; - } - return new Entry( - this.name, - this.rawName, - this.type, - this.nullable, - value, - this.errorCapable, - this.valid, - this.elementSchema, - this.comment, - this.props, - this.internalDefaultValue); - } - - public final Entry withIsErrorCapable(final boolean value) { - if (this.errorCapable == value) { - return this; - } - return new Entry( - this.name, - this.rawName, - this.type, - this.nullable, - this.metadata, - value, - this.valid, - this.elementSchema, - this.comment, - this.props, - this.internalDefaultValue); - } - - public final Entry withIsValid(final boolean value) { - if (this.valid == value) { - return this; - } - return new Entry( - this.name, - this.rawName, - this.type, - this.nullable, - this.metadata, - this.errorCapable, - value, - this.elementSchema, - this.comment, - this.props, - this.internalDefaultValue); - } - - public final Entry withElementSchema(final Schema value) { - if (this.elementSchema == value) { - return this; - } - Schema newValue = Objects.requireNonNull(value, "elementSchema"); - return new Entry( - this.name, - this.rawName, - this.type, - this.nullable, - this.metadata, - this.errorCapable, - this.valid, - newValue, - this.comment, - this.props, - this.internalDefaultValue); - } - - public final Entry withComment(final String value) { - String newValue = Objects.requireNonNull(value, "comment"); - if (this.comment.equals(newValue)) { - return this; - } - return new Entry( - this.name, - this.rawName, - this.type, - this.nullable, - this.metadata, - this.errorCapable, - this.valid, - this.elementSchema, - newValue, - this.props, - this.internalDefaultValue); - } - - public final Entry withProps(final Map entries) { - if (this.props == entries) { - return this; - } - Map newValue = createUnmodifiableMap(true, false, entries); - return new Entry( - this.name, - this.rawName, - this.type, - this.nullable, - this.metadata, - this.errorCapable, - this.valid, - this.elementSchema, - this.comment, - newValue, - this.internalDefaultValue); - } - - public final Entry withInternalDefaultValue(final Object value) { - if (this.internalDefaultValue == value) { - return this; - } - Object newValue = Objects.requireNonNull(value, "internalDefaultValue"); - return new Entry( - this.name, - this.rawName, - this.type, - this.nullable, - this.metadata, - this.errorCapable, - this.valid, - this.elementSchema, - this.comment, - this.props, - newValue); - } - - @Override - public String toString() { - return "Entry{" - + "name=" + name - + ", rawName=" + rawName - + ", type=" + type - + ", nullable=" + nullable - + ", metadata=" + metadata - + ", errorCapable=" + errorCapable - + ", valid=" + valid - + ", elementSchema=" + elementSchema - + ", comment=" + comment - + ", props=" + props - + ", internalDefaultValue=" + internalDefaultValue - + "}"; - } - - public static Entry copyOf(final Entry instance) { - if (instance instanceof Entry) { - return (Entry) instance; - } - return Entry.builder() - .from(instance) - .build(); - } - - public static Builder builder() { - return new Builder(); - } - - public static final class Builder { - private static final long INIT_BIT_NAME = 0x1L; - private static final long INIT_BIT_RAW_NAME = 0x2L; - private static final long INIT_BIT_ORIGINAL_FIELD_NAME = 0x4L; - private static final long INIT_BIT_TYPE = 0x8L; - private static final long INIT_BIT_IS_NULLABLE = 0x10L; - private static final long INIT_BIT_IS_METADATA = 0x20L; - private static final long INIT_BIT_IS_ERROR_CAPABLE = 0x40L; - private static final long INIT_BIT_IS_VALID = 0x80L; - private static final long INIT_BIT_ELEMENT_SCHEMA = 0x100L; - private static final long INIT_BIT_COMMENT = 0x200L; - private static final long INIT_BIT_INTERNAL_DEFAULT_VALUE = 0x400L; - private long initBits = 0x7ffL; - - private String name; - private String rawName; - private String originalFieldName; - private Schema.Type type; - private boolean nullable; - private boolean metadata; - private boolean errorCapable; - private boolean valid; - private Schema elementSchema; - private String comment; - private Map props = new LinkedHashMap(); - private Object internalDefaultValue; - - private Builder() { - } - - public final Builder from(final Entry instance) { - Objects.requireNonNull(instance, "instance"); - from((Object) instance); - return this; - } - - private void from(final Object object) { - long bits = 0; - if (object instanceof Entry) { - Entry instance = (Entry) object; - if ((bits & 0x1L) == 0) { - this.elementSchema(instance.getElementSchema()); - bits |= 0x1L; - } - if ((bits & 0x2L) == 0) { - this.isValid(instance.isValid()); - bits |= 0x2L; - } - this.internalDefaultValue(instance.getInternalDefaultValue()); - if ((bits & 0x4L) == 0) { - this.type(instance.getType()); - bits |= 0x4L; - } - if ((bits & 0x8L) == 0) { - this.rawName(instance.getRawName()); - bits |= 0x8L; - } - if ((bits & 0x10L) == 0) { - putAllProps(instance.getProps()); - bits |= 0x10L; - } - if ((bits & 0x20L) == 0) { - this.originalFieldName(instance.getOriginalFieldName()); - bits |= 0x20L; - } - if ((bits & 0x40L) == 0) { - this.isNullable(instance.isNullable()); - bits |= 0x40L; - } - if ((bits & 0x80L) == 0) { - this.name(instance.getName()); - bits |= 0x80L; - } - if ((bits & 0x100L) == 0) { - this.comment(instance.getComment()); - bits |= 0x100L; - } - if ((bits & 0x200L) == 0) { - this.isMetadata(instance.isMetadata()); - bits |= 0x200L; - } - if ((bits & 0x400L) == 0) { - this.isErrorCapable(instance.isErrorCapable()); - bits |= 0x400L; - } - } - if (object instanceof Entry) { - Entry instance = (Entry) object; - if ((bits & 0x1L) == 0) { - this.elementSchema(instance.getElementSchema()); - bits |= 0x1L; - } - if ((bits & 0x20L) == 0) { - this.originalFieldName(instance.getOriginalFieldName()); - bits |= 0x20L; - } - if ((bits & 0x2L) == 0) { - this.isValid(instance.isValid()); - bits |= 0x2L; - } - if ((bits & 0x40L) == 0) { - this.isNullable(instance.isNullable()); - bits |= 0x40L; - } - if ((bits & 0x80L) == 0) { - this.name(instance.getName()); - bits |= 0x80L; - } - if ((bits & 0x100L) == 0) { - this.comment(instance.getComment()); - bits |= 0x100L; - } - if ((bits & 0x4L) == 0) { - this.type(instance.getType()); - bits |= 0x4L; - } - if ((bits & 0x200L) == 0) { - this.isMetadata(instance.isMetadata()); - bits |= 0x200L; - } - if ((bits & 0x400L) == 0) { - this.isErrorCapable(instance.isErrorCapable()); - bits |= 0x400L; - } - if ((bits & 0x8L) == 0) { - this.rawName(instance.getRawName()); - bits |= 0x8L; - } - if ((bits & 0x10L) == 0) { - putAllProps(instance.getProps()); - bits |= 0x10L; - } - } - } - - public final Builder name(final String name) { - this.name = Objects.requireNonNull(name, "name"); - initBits &= ~INIT_BIT_NAME; - return this; - } - - public final Builder rawName(final String rawName) { - this.rawName = Objects.requireNonNull(rawName, "rawName"); - initBits &= ~INIT_BIT_RAW_NAME; - return this; - } - - public final Builder originalFieldName(final String originalFieldName) { - this.originalFieldName = Objects.requireNonNull(originalFieldName, "originalFieldName"); - initBits &= ~INIT_BIT_ORIGINAL_FIELD_NAME; - return this; - } - - public final Builder type(final Schema.Type type) { - this.type = Objects.requireNonNull(type, "type"); - initBits &= ~INIT_BIT_TYPE; - return this; - } - - public final Builder isNullable(final boolean isNullable) { - this.nullable = isNullable; - initBits &= ~INIT_BIT_IS_NULLABLE; - return this; - } - - public final Builder isMetadata(final boolean isMetadata) { - this.metadata = isMetadata; - initBits &= ~INIT_BIT_IS_METADATA; - return this; - } - - public final Builder isErrorCapable(final boolean isErrorCapable) { - this.errorCapable = isErrorCapable; - initBits &= ~INIT_BIT_IS_ERROR_CAPABLE; - return this; - } - - public final Builder isValid(final boolean isValid) { - this.valid = isValid; - initBits &= ~INIT_BIT_IS_VALID; - return this; - } - - public final Builder elementSchema(final Schema elementSchema) { - this.elementSchema = Objects.requireNonNull(elementSchema, "elementSchema"); - initBits &= ~INIT_BIT_ELEMENT_SCHEMA; - return this; - } - - public final Builder comment(final String comment) { - this.comment = Objects.requireNonNull(comment, "comment"); - initBits &= ~INIT_BIT_COMMENT; - return this; - } - - public final Builder putProps(final String key, final String value) { - this.props.put( - Objects.requireNonNull(key, "props key"), - Objects.requireNonNull(value, value == null ? "props value for key: " + key : null)); - return this; - } - - public final Builder putProps(final Map.Entry entry) { - String k = entry.getKey(); - String v = entry.getValue(); - this.props.put( - Objects.requireNonNull(k, "props key"), - Objects.requireNonNull(v, v == null ? "props value for key: " + k : null)); - return this; - } - - public final Builder props(final Map entries) { - this.props.clear(); - return putAllProps(entries); - } - - public final Builder putAllProps(final Map entries) { - for (Map.Entry e : entries.entrySet()) { - String k = e.getKey(); - String v = e.getValue(); - this.props.put( - Objects.requireNonNull(k, "props key"), - Objects.requireNonNull(v, v == null ? "props value for key: " + k : null)); - } - return this; - } - - public final Builder internalDefaultValue(final Object internalDefaultValue) { - this.internalDefaultValue = Objects.requireNonNull(internalDefaultValue, "internalDefaultValue"); - initBits &= ~INIT_BIT_INTERNAL_DEFAULT_VALUE; - return this; - } - - public Entry build() { - return new Entry( - name, - rawName, - type, - nullable, - metadata, - errorCapable, - valid, - elementSchema, - comment, - createUnmodifiableMap(false, false, props), - internalDefaultValue); - } - - } - - private static Map createUnmodifiableMap(final boolean checkNulls, final boolean skipNulls, - final Map map) { - switch (map.size()) { - case 0: - return Collections.emptyMap(); - case 1: { - Map.Entry e = map.entrySet().iterator().next(); - K k = e.getKey(); - V v = e.getValue(); - if (checkNulls) { - Objects.requireNonNull(k, "key"); - Objects.requireNonNull(v, v == null ? "value for key: " + k : null); - } - if (skipNulls && (k == null || v == null)) { - return Collections.emptyMap(); - } - return Collections.singletonMap(k, v); - } - default: { - Map linkedMap = new LinkedHashMap<>(map.size() * 4 / 3 + 1); - if (skipNulls || checkNulls) { - for (Map.Entry e : map.entrySet()) { - K k = e.getKey(); - V v = e.getValue(); - if (skipNulls) { - if (k == null || v == null) { - continue; - } - } else if (checkNulls) { - Objects.requireNonNull(k, "key"); - Objects.requireNonNull(v, v == null ? "value for key: " + k : null); - } - linkedMap.put(k, v); - } - } else { - linkedMap.putAll(map); - } - return Collections.unmodifiableMap(linkedMap); - } - } - } } \ No newline at end of file diff --git a/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Schema.java b/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Schema.java index d83db6c12ec84..c26365203420d 100644 --- a/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Schema.java +++ b/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Schema.java @@ -18,37 +18,28 @@ import java.beans.ConstructorProperties; import java.math.BigDecimal; import java.time.temporal.Temporal; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.Date; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Objects; -import lombok.Getter; +import lombok.Data; +@Data public final class Schema { - @Getter private final Type type; - @Getter private final Schema elementSchema; - @Getter private final List entries; - @Getter private final List metadata; - @Getter private final Map props; @ConstructorProperties({"type", "elementSchema", "entries", "metadata", "props"}) - private Schema( + public Schema( final Type type, final Schema elementSchema, final List entries, @@ -65,387 +56,6 @@ public String getProp(final String key){ return this.props.get(key); } - public final Schema withType(final Type value) { - Type newValue = Objects.requireNonNull(value, "type"); - if (this.type == newValue) { - return this; - } - return new Schema( - newValue, - this.elementSchema, - this.entries, - this.metadata, - this.props); - } - - public final Schema withElementSchema(final Schema value) { - if (this.elementSchema == value) { - return this; - } - Schema newValue = Objects.requireNonNull(value, "elementSchema"); - return new Schema(this.type, newValue, this.entries, this.metadata, this.props); - } - - public final Schema withEntries(final Entry... elements) { - List newValue = createUnmodifiableList(false, createSafeList(Arrays.asList(elements), true, false)); - return new Schema( - this.type, - this.elementSchema, - newValue, - this.metadata, - this.props); - } - - public final Schema withEntries(final Iterable elements) { - if (this.entries == elements) { - return this; - } - List newValue = createUnmodifiableList(false, createSafeList(elements, true, false)); - return new Schema( - this.type, - this.elementSchema, - newValue, - this.metadata, - this.props); - } - - public final Schema withMetadata(final Entry... elements) { - List newValue = createUnmodifiableList(false, createSafeList(Arrays.asList(elements), true, false)); - return new Schema( - this.type, - this.elementSchema, - this.entries, - newValue, - this.props); - } - - public final Schema withMetadata(final Iterable elements) { - if (this.metadata == elements) { - return this; - } - List newValue = createUnmodifiableList(false, createSafeList(elements, true, false)); - return new Schema( - this.type, - this.elementSchema, - this.entries, - newValue, - this.props); - } - - public final Schema withProps(final Map entries) { - if (this.props == entries) { - return this; - } - Map newValue = createUnmodifiableMap(true, false, entries); - return new Schema( - this.type, - this.elementSchema, - this.entries, - this.metadata, - newValue); - } - - @Override - public boolean equals(final Object another) { - if (this == another) { - return true; - } - return another instanceof Schema - && equalTo(0, (Schema) another); - } - - private boolean equalTo(final int synthetic, final Schema another) { - return type.equals(another.type) - && elementSchema.equals(another.elementSchema) - && entries.equals(another.entries) - && metadata.equals(another.metadata) - && props.equals(another.props); - } - - public int hashCode() { - int h = 5381; - h += (h << 5) + type.hashCode(); - h += (h << 5) + elementSchema.hashCode(); - h += (h << 5) + entries.hashCode(); - h += (h << 5) + metadata.hashCode(); - h += (h << 5) + props.hashCode(); - return h; - } - - @Override - public String toString() { - return "Schema{" - + "type=" + type - + ", elementSchema=" + elementSchema - + ", entries=" + entries - + ", metadata=" + metadata - + ", props=" + props - + "}"; - } - - public static Schema copyOf(final Schema instance) { - if (instance != null && instance instanceof Schema) { - return (Schema) instance; - } - return Schema.builder() - .from(instance) - .build(); - } - - public static Schema.Builder builder() { - return new Schema.Builder(); - } - - public static final class Builder { - private static final long INIT_BIT_TYPE = 0x1L; - private static final long INIT_BIT_ELEMENT_SCHEMA = 0x2L; - private static final long INIT_BIT_ALL_ENTRIES = 0x4L; - private long initBits = 0x7L; - - private Type type; - private Schema elementSchema; - private List entries = new ArrayList(); - private List metadata = new ArrayList(); - private Map props = new LinkedHashMap(); - - private Builder() { - } - - public final Builder from(final Schema instance) { - Objects.requireNonNull(instance, "instance"); - from((Object) instance); - return this; - } - - private void from(final Object object) { - long bits = 0; - if (object instanceof Schema) { - Schema instance = (Schema) object; - if ((bits & 0x1L) == 0) { - this.elementSchema(instance.getElementSchema()); - bits |= 0x1L; - } - if ((bits & 0x2L) == 0) { - addAllMetadata(instance.getMetadata()); - bits |= 0x2L; - } - if ((bits & 0x20L) == 0) { - addAllEntries(instance.getEntries()); - bits |= 0x20L; - } - if ((bits & 0x8L) == 0) { - this.type(instance.getType()); - bits |= 0x8L; - } - if ((bits & 0x10L) == 0) { - putAllProps(instance.getProps()); - bits |= 0x10L; - } - } - if (object instanceof Schema) { - Schema instance = (Schema) object; - if ((bits & 0x1L) == 0) { - this.elementSchema(instance.getElementSchema()); - bits |= 0x1L; - } - if ((bits & 0x2L) == 0) { - addAllMetadata(instance.getMetadata()); - bits |= 0x2L; - } - if ((bits & 0x20L) == 0) { - addAllEntries(instance.getEntries()); - bits |= 0x20L; - } - if ((bits & 0x8L) == 0) { - this.type(instance.getType()); - bits |= 0x8L; - } - } - } - - public final Builder type(final Type type) { - this.type = Objects.requireNonNull(type, "type"); - initBits &= ~INIT_BIT_TYPE; - return this; - } - - public final Builder elementSchema(final Schema elementSchema) { - this.elementSchema = Objects.requireNonNull(elementSchema, "elementSchema"); - initBits &= ~INIT_BIT_ELEMENT_SCHEMA; - return this; - } - - public final Builder addEntries(final Entry element) { - this.entries.add(Objects.requireNonNull(element, "entries element")); - return this; - } - - public final Builder addEntries(final Entry... elements) { - for (Entry element : elements) { - this.entries.add(Objects.requireNonNull(element, "entries element")); - } - return this; - } - - public final Builder entries(final Iterable elements) { - this.entries.clear(); - return addAllEntries(elements); - } - - public final Builder addAllEntries(final Iterable elements) { - for (Entry element : elements) { - this.entries.add(Objects.requireNonNull(element, "entries element")); - } - return this; - } - - public final Builder addMetadata(final Entry element) { - this.metadata.add(Objects.requireNonNull(element, "metadata element")); - return this; - } - - public final Builder addMetadata(final Entry... elements) { - for (Entry element : elements) { - this.metadata.add(Objects.requireNonNull(element, "metadata element")); - } - return this; - } - - public final Builder metadata(final Iterable elements) { - this.metadata.clear(); - return addAllMetadata(elements); - } - - public final Builder addAllMetadata(final Iterable elements) { - for (Entry element : elements) { - this.metadata.add(Objects.requireNonNull(element, "metadata element")); - } - return this; - } - - public final Builder putProps(final String key, final String value) { - this.props.put( - Objects.requireNonNull(key, "props key"), - Objects.requireNonNull(value, value == null ? "props value for key: " + key : null)); - return this; - } - - public final Builder putProps(final Map.Entry entry) { - String k = entry.getKey(); - String v = entry.getValue(); - this.props.put( - Objects.requireNonNull(k, "props key"), - Objects.requireNonNull(v, v == null ? "props value for key: " + k : null)); - return this; - } - - public final Builder props(final Map entries) { - this.props.clear(); - return putAllProps(entries); - } - - public final Builder putAllProps(final Map entries) { - for (Map.Entry e : entries.entrySet()) { - String k = e.getKey(); - String v = e.getValue(); - this.props.put( - Objects.requireNonNull(k, "props key"), - Objects.requireNonNull(v, v == null ? "props value for key: " + k : null)); - } - return this; - } - - public Schema build() { - return new Schema( - type, - elementSchema, - createUnmodifiableList(true, entries), - createUnmodifiableList(true, metadata), - createUnmodifiableMap(false, false, props)); - } - - } - - private static List createSafeList(final Iterable iterable, final boolean checkNulls, final boolean skipNulls) { - ArrayList list; - if (iterable instanceof Collection) { - int size = ((Collection) iterable).size(); - if (size == 0) { - return Collections.emptyList(); - } - list = new ArrayList<>(size); - } else { - list = new ArrayList<>(); - } - for (T element : iterable) { - if (skipNulls && element == null) { - continue; - } - if (checkNulls) { - Objects.requireNonNull(element, "element"); - } - list.add(element); - } - return list; - } - - private static List createUnmodifiableList(final boolean clone, final List list) { - switch(list.size()) { - case 0: return Collections.emptyList(); - case 1: return Collections.singletonList(list.get(0)); - default: - if (clone) { - return Collections.unmodifiableList(new ArrayList<>(list)); - } else { - if (list instanceof ArrayList) { - ((ArrayList) list).trimToSize(); - } - return Collections.unmodifiableList(list); - } - } - } - - private static Map createUnmodifiableMap(final boolean checkNulls, final boolean skipNulls, - final Map map) { - switch (map.size()) { - case 0: return Collections.emptyMap(); - case 1: { - Map.Entry e = map.entrySet().iterator().next(); - K k = e.getKey(); - V v = e.getValue(); - if (checkNulls) { - Objects.requireNonNull(k, "key"); - Objects.requireNonNull(v, v == null ? "value for key: " + k : null); - } - if (skipNulls && (k == null || v == null)) { - return Collections.emptyMap(); - } - return Collections.singletonMap(k, v); - } - default: { - Map linkedMap = new LinkedHashMap<>(map.size() * 4 / 3 + 1); - if (skipNulls || checkNulls) { - for (Map.Entry e : map.entrySet()) { - K k = e.getKey(); - V v = e.getValue(); - if (skipNulls) { - if (k == null || v == null) { - continue; - } - } else if (checkNulls) { - Objects.requireNonNull(k, "key"); - Objects.requireNonNull(v, v == null ? "value for key: " + k : null); - } - linkedMap.put(k, v); - } - } else { - linkedMap.putAll(map); - } - return Collections.unmodifiableMap(linkedMap); - } - } - } - public enum Type { RECORD(new Class[] { Record.class }), diff --git a/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/EntryTest.java b/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/EntryTest.java index 2fbf6da679dac..51f30824d39f8 100644 --- a/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/EntryTest.java +++ b/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/EntryTest.java @@ -15,10 +15,15 @@ */ package org.talend.sdk.component.server; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + import com.fasterxml.jackson.databind.ObjectMapper; -import static org.junit.jupiter.api.Assertions.*; +import java.util.LinkedHashMap; -import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Test; @@ -31,19 +36,10 @@ class EntryTest { private Entry createValidEntry() { - return Entry.builder() - .name("name") - .rawName("raw") - .originalFieldName("original") - .type(Schema.Type.STRING) - .isNullable(true) - .isMetadata(false) - .isErrorCapable(true) - .isValid(true) - .comment("comment") - .putProps("p1", "v1") - .internalDefaultValue("default") - .build(); + Map props = new LinkedHashMap<>(0); + props.put("p1", "v1"); + return new Entry("name", "raw", Schema.Type.STRING, true, false, true, + true, null,"comment", props, "default"); } // ---------------------------------------------------------------------- @@ -67,48 +63,6 @@ void builderCreatesValidEntry() { assertEquals("v1", entry.getProps().get("p1")); } - // ---------------------------------------------------------------------- - // withXxx methods - // ---------------------------------------------------------------------- - - @Test - void withNameSameValueReturnsSameInstance() { - Entry entry = createValidEntry(); - assertSame(entry, entry.withName("name")); - } - - @Test - void withNameDifferentValueReturnsNewInstance() { - Entry entry = createValidEntry(); - Entry modified = entry.withName("other"); - - assertNotSame(entry, modified); - assertEquals("other", modified.getName()); - assertEquals(entry.getRawName(), modified.getRawName()); - } - - @Test - void withIsNullableChangesValue() { - Entry entry = createValidEntry(); - Entry modified = entry.withIsNullable(false); - - assertFalse(modified.isNullable()); - assertTrue(entry.isNullable()); - } - - @Test - void withPropsReplacesMap() { - Entry entry = createValidEntry(); - - Map newProps = new HashMap<>(); - newProps.put("a", "b"); - - Entry modified = entry.withProps(newProps); - - assertEquals(Map.of("a", "b"), modified.getProps()); - assertEquals(Map.of("p1", "v1"), entry.getProps()); - } - // ---------------------------------------------------------------------- // Accessors // ---------------------------------------------------------------------- @@ -128,16 +82,6 @@ void getPropReturnsProperty() { assertNull(entry.getProp("k1")); } - // ---------------------------------------------------------------------- - // copyOf - // ---------------------------------------------------------------------- - - @Test - void copyOfReturnsSameInstanceForEntry() { - Entry entry = createValidEntry(); - assertSame(entry, Entry.copyOf(entry)); - } - // ---------------------------------------------------------------------- // JSON deserialization // ---------------------------------------------------------------------- diff --git a/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/SchemaTest.java b/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/SchemaTest.java index fa3f6ae2fbde4..301ea76f655d7 100644 --- a/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/SchemaTest.java +++ b/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/SchemaTest.java @@ -18,7 +18,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -47,179 +46,19 @@ class SchemaTest { private org.talend.sdk.component.server.front.model.Schema emptySchema() { + // minimal self-referential schema to satisfy required fields org.talend.sdk.component.server.front.model.Schema placeholder = - org.talend.sdk.component.server.front.model.Schema.builder() - .type(org.talend.sdk.component.server.front.model.Schema.Type.RECORD) - .build(); + new org.talend.sdk.component.server.front.model.Schema(org.talend.sdk.component.server.front.model.Schema.Type.RECORD, + null, null, null, null); - return org.talend.sdk.component.server.front.model.Schema.builder() - .type(org.talend.sdk.component.server.front.model.Schema.Type.RECORD) - .elementSchema(placeholder) - .build(); + return new org.talend.sdk.component.server.front.model.Schema(org.talend.sdk.component.server.front.model.Schema.Type.RECORD, + placeholder, null, null, null); } private Entry entry(String name) { - return Entry.builder() - .name(name) - .rawName(name) - .originalFieldName(name) - .type(org.talend.sdk.component.server.front.model.Schema.Type.STRING) - .isNullable(false) - .isMetadata(false) - .isErrorCapable(false) - .isValid(true) - .elementSchema(emptySchema()) - .comment("c") - .internalDefaultValue("") - .build(); - } - - // ---------------------------------------------------------------------- - // withEntries - // ---------------------------------------------------------------------- - - @Test - void withEntriesVarargsReplacesEntries() { - org.talend.sdk.component.server.front.model.Schema schema = emptySchema(); - - Entry e1 = entry("a"); - Entry e2 = entry("b"); - - org.talend.sdk.component.server.front.model.Schema updated = schema.withEntries(e1, e2); - - assertEquals(List.of(e1, e2), updated.getEntries()); - assertNotSame(schema, updated); - } - - @Test - void withEntriesIterableReplacesEntries() { - org.talend.sdk.component.server.front.model.Schema schema = emptySchema(); - Entry e1 = entry("x"); - - List list = List.of(e1); - org.talend.sdk.component.server.front.model.Schema updated = schema.withEntries(list); - - assertEquals(list, updated.getEntries()); - } - - @Test - void withEntriesReturnsSameInstanceWhenSameReference() { - Entry e1 = entry("a"); - - org.talend.sdk.component.server.front.model.Schema schema = - org.talend.sdk.component.server.front.model.Schema.builder() - .type(org.talend.sdk.component.server.front.model.Schema.Type.RECORD) - .elementSchema(emptySchema()) - .addEntries(e1) - .build(); - - // same list reference - org.talend.sdk.component.server.front.model.Schema same = schema.withEntries(schema.getEntries()); - - assertSame(schema, same); - } - - @Test - void withEntriesListIsUnmodifiable() { - org.talend.sdk.component.server.front.model.Schema schema = emptySchema(); - Entry e1 = entry("a"); - - org.talend.sdk.component.server.front.model.Schema updated = schema.withEntries(e1); - - assertThrows(UnsupportedOperationException.class, - () -> updated.getEntries().add(entry("b"))); - } - - // ---------------------------------------------------------------------- - // withMetadata - // ---------------------------------------------------------------------- - - @Test - void withMetadataVarargsReplacesMetadata() { - org.talend.sdk.component.server.front.model.Schema schema = emptySchema(); - - Entry m1 = entry("m1"); - Entry m2 = entry("m2"); - - org.talend.sdk.component.server.front.model.Schema updated = schema.withMetadata(m1, m2); - - assertEquals(List.of(m1, m2), updated.getMetadata()); - assertNotSame(schema, updated); - } - - @Test - void withMetadataIterableReplacesMetadata() { - org.talend.sdk.component.server.front.model.Schema schema = emptySchema(); - Entry m1 = entry("meta"); - - List list = List.of(m1); - org.talend.sdk.component.server.front.model.Schema updated = schema.withMetadata(list); - - assertEquals(list, updated.getMetadata()); - } - - @Test - void withMetadataReturnsSameInstanceWhenSameReference() { - Entry m1 = entry("meta"); - - org.talend.sdk.component.server.front.model.Schema schema = - org.talend.sdk.component.server.front.model.Schema.builder() - .type(org.talend.sdk.component.server.front.model.Schema.Type.RECORD) - .elementSchema(emptySchema()) - .addMetadata(m1) - .build(); - - org.talend.sdk.component.server.front.model.Schema same = schema.withMetadata(schema.getMetadata()); - - assertSame(schema, same); - } - - @Test - void withMetadataListIsUnmodifiable() { - org.talend.sdk.component.server.front.model.Schema schema = emptySchema(); - Entry m1 = entry("meta"); - - org.talend.sdk.component.server.front.model.Schema updated = schema.withMetadata(m1); - - assertThrows(UnsupportedOperationException.class, - () -> updated.getMetadata().add(entry("x"))); - } - - // ---------------------------------------------------------------------- - // Regression: ensure other fields are untouched - // ---------------------------------------------------------------------- - - @Test - void withEntriesDoesNotModifyMetadata() { - Entry meta = entry("meta"); - - org.talend.sdk.component.server.front.model.Schema schema = - org.talend.sdk.component.server.front.model.Schema.builder() - .type(org.talend.sdk.component.server.front.model.Schema.Type.RECORD) - .elementSchema(emptySchema()) - .addMetadata(meta) - .build(); - - org.talend.sdk.component.server.front.model.Schema updated = schema.withEntries(entry("field")); - - assertEquals(List.of(meta), updated.getMetadata()); - } - - @Test - void withMetadataDoesNotModifyEntries() { - Entry field = entry("field"); - - org.talend.sdk.component.server.front.model.Schema schema = - org.talend.sdk.component.server.front.model.Schema.builder() - .type(org.talend.sdk.component.server.front.model.Schema.Type.RECORD) - .elementSchema(emptySchema()) - .addEntries(field) - .build(); - - org.talend.sdk.component.server.front.model.Schema updated = schema.withMetadata(entry("meta")); - - assertEquals(List.of(field), updated.getEntries()); + return new Entry(name, name, org.talend.sdk.component.server.front.model.Schema.Type.STRING,false, + false, false, true, emptySchema(), "c", null, ""); } @Test @@ -401,7 +240,7 @@ void unknownPropertyIsNull() { } @Test - void testAllDataTypes() { + void testAllDataTypes1() { // given final Schema schema = new SchemaImpl.BuilderImpl() // .withType(Schema.Type.RECORD) @@ -430,6 +269,54 @@ void testAllDataTypes() { .withComment("field bytes") .withNullable(true) .build()) + .withProp("namespace", "test") + .build(); + + // when: serialize SchemaImpl + String json = jsonb.toJson(schema); + + // then: sanity check JSON + assertTrue(json.contains("\"type\":\"RECORD\"")); + assertTrue(json.contains("\"entries\"")); + + // when: deserialize into Schema + org.talend.sdk.component.server.front.model.Schema model = jsonb.fromJson(new StringReader(json), + org.talend.sdk.component.server.front.model.Schema.class); + + // then + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.RECORD, model.getType()); + assertEquals("test", model.getProps().get("namespace")); + + assertEquals(4, model.getEntries().size()); + assertEquals("id", model.getEntries().get(0).getName()); + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.INT, model.getEntries().get(0).getType()); + assertFalse(model.getEntries().get(0).isNullable()); + assertTrue(model.getEntries().get(0).isErrorCapable()); + assertEquals("field_date", model.getEntries().get(1).getName()); + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.DATETIME, model.getEntries().get(1).getType()); + assertEquals("field date", model.getEntries().get(1).getComment()); + assertFalse(model.getEntries().get(1).isNullable()); + assertFalse(model.getEntries().get(1).isErrorCapable()); + + assertEquals("field_boolean", model.getEntries().get(2).getName()); + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.BOOLEAN, model.getEntries().get(2).getType()); + assertEquals("field boolean", model.getEntries().get(2).getComment()); + assertTrue(model.getEntries().get(2).isNullable()); + assertFalse(model.getEntries().get(2).isErrorCapable()); + assertEquals("field_bytes", model.getEntries().get(3).getName()); + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.BYTES, model.getEntries().get(3).getType()); + assertEquals("field bytes", model.getEntries().get(3).getComment()); + assertTrue(model.getEntries().get(3).isNullable()); + + assertEquals("id,field_date,field_boolean,field_bytes", + model.getProps().get("talend.fields.order")); + } + + @Test + void testAllDataTypes2() { + // given + final Schema schema = new SchemaImpl.BuilderImpl() // + .withType(Schema.Type.RECORD) .withEntry(new SchemaImpl.EntryImpl.BuilderImpl() .withName("field_decimal") .withType(Schema.Type.DECIMAL) @@ -466,42 +353,23 @@ void testAllDataTypes() { assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.RECORD, model.getType()); assertEquals("test", model.getProps().get("namespace")); - assertEquals(7, model.getEntries().size()); - assertEquals("id", model.getEntries().get(0).getName()); - assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.INT, model.getEntries().get(0).getType()); - assertFalse(model.getEntries().get(0).isNullable()); - assertTrue(model.getEntries().get(0).isErrorCapable()); - assertEquals("field_date", model.getEntries().get(1).getName()); - assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.DATETIME, model.getEntries().get(1).getType()); - assertEquals("field date", model.getEntries().get(1).getComment()); - assertFalse(model.getEntries().get(1).isNullable()); - assertFalse(model.getEntries().get(1).isErrorCapable()); + assertEquals(3, model.getEntries().size()); - assertEquals("field_boolean", model.getEntries().get(2).getName()); - assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.BOOLEAN, model.getEntries().get(2).getType()); - assertEquals("field boolean", model.getEntries().get(2).getComment()); + assertEquals("field_decimal", model.getEntries().get(0).getName()); + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.DECIMAL, model.getEntries().get(0).getType()); + assertEquals("field decimal", model.getEntries().get(0).getComment()); + assertTrue(model.getEntries().get(0).isNullable()); + assertEquals("field_double", model.getEntries().get(1).getName()); + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.DOUBLE, model.getEntries().get(1).getType()); + assertEquals("field double", model.getEntries().get(1).getComment()); + assertTrue(model.getEntries().get(1).isNullable()); + + assertEquals("field_float", model.getEntries().get(2).getName()); + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.FLOAT, model.getEntries().get(2).getType()); + assertEquals("field float", model.getEntries().get(2).getComment()); assertTrue(model.getEntries().get(2).isNullable()); - assertFalse(model.getEntries().get(2).isErrorCapable()); - assertEquals("field_bytes", model.getEntries().get(3).getName()); - assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.BYTES, model.getEntries().get(3).getType()); - assertEquals("field bytes", model.getEntries().get(3).getComment()); - assertTrue(model.getEntries().get(3).isNullable()); - assertEquals("field_decimal", model.getEntries().get(4).getName()); - assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.DECIMAL, model.getEntries().get(4).getType()); - assertEquals("field decimal", model.getEntries().get(4).getComment()); - assertTrue(model.getEntries().get(4).isNullable()); - assertEquals("field_double", model.getEntries().get(5).getName()); - assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.DOUBLE, model.getEntries().get(5).getType()); - assertEquals("field double", model.getEntries().get(5).getComment()); - assertTrue(model.getEntries().get(5).isNullable()); - - assertEquals("field_float", model.getEntries().get(6).getName()); - assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.FLOAT, model.getEntries().get(6).getType()); - assertEquals("field float", model.getEntries().get(6).getComment()); - assertTrue(model.getEntries().get(6).isNullable()); - - assertEquals("id,field_date,field_boolean,field_bytes,field_decimal,field_double,field_float", + assertEquals("field_decimal,field_double,field_float", model.getProps().get("talend.fields.order")); } From 85b9a01173f4ddcaaa433a79f1572e01fa097b97 Mon Sep 17 00:00:00 2001 From: yyin-talend Date: Tue, 13 Jan 2026 17:01:42 +0800 Subject: [PATCH 5/8] remove unused in test --- .../sdk/component/server/front/SchemaTest.java | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/SchemaTest.java b/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/SchemaTest.java index 301ea76f655d7..68b33a0e70ed2 100644 --- a/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/SchemaTest.java +++ b/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/SchemaTest.java @@ -44,23 +44,6 @@ class SchemaTest { private final Jsonb jsonb = JsonbBuilder.create(); - - private org.talend.sdk.component.server.front.model.Schema emptySchema() { - - // minimal self-referential schema to satisfy required fields - org.talend.sdk.component.server.front.model.Schema placeholder = - new org.talend.sdk.component.server.front.model.Schema(org.talend.sdk.component.server.front.model.Schema.Type.RECORD, - null, null, null, null); - - return new org.talend.sdk.component.server.front.model.Schema(org.talend.sdk.component.server.front.model.Schema.Type.RECORD, - placeholder, null, null, null); - } - - private Entry entry(String name) { - return new Entry(name, name, org.talend.sdk.component.server.front.model.Schema.Type.STRING,false, - false, false, true, emptySchema(), "c", null, ""); - } - @Test void shouldSerializeSchemaImplAndDeserializeToJsonSchemaModel() { // given From 19dfe21ec253d9187ba2ec307a0b382f2301cf5e Mon Sep 17 00:00:00 2001 From: Yves Piel Date: Tue, 13 Jan 2026 10:21:56 +0100 Subject: [PATCH 6/8] fix(QTDI-2215): Add some tests + remove checkstyle config. --- .../component-server-model/pom.xml | 98 +---- .../component/server/front/model/Entry.java | 19 +- .../server/front/model/JsonEntryModel.java | 127 ------ .../server/front/model/JsonSchemaModel.java | 155 -------- .../component/server/front/model/Schema.java | 114 +++--- .../component-server/pom.xml | 3 +- .../server/{ => front}/EntryTest.java | 95 +++-- .../server/front/JsonSchemaTest.java | 357 ----------------- .../component/server/front/SchemaTest.java | 374 ++++++++++++------ 9 files changed, 374 insertions(+), 968 deletions(-) delete mode 100644 component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/JsonEntryModel.java delete mode 100644 component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/JsonSchemaModel.java rename component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/{ => front}/EntryTest.java (58%) delete mode 100644 component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/JsonSchemaTest.java diff --git a/component-server-parent/component-server-model/pom.xml b/component-server-parent/component-server-model/pom.xml index 1492e4f2d023a..7842a0230affc 100644 --- a/component-server-parent/component-server-model/pom.xml +++ b/component-server-parent/component-server-model/pom.xml @@ -45,102 +45,6 @@ - - org.apache.maven.plugins - maven-checkstyle-plugin - - true - **/HelpMojo*,**/maven/legacy/model/** - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - verify-style - - check - - process-classes - - - org.apache.felix maven-bundle-plugin @@ -172,4 +76,4 @@ - + \ No newline at end of file diff --git a/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Entry.java b/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Entry.java index 6939a68c5f4d3..813f816d6f48b 100644 --- a/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Entry.java +++ b/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Entry.java @@ -44,10 +44,12 @@ public final class Entry { private final Map props = new LinkedHashMap<>(0); - private final Object internalDefaultValue; + private final Object defaultValue; - @ConstructorProperties({"name", "rawName", "type", "nullable", "metadata", "errorCapable", - "valid", "elementSchema", "comment", "props", "internalDefaultValue"}) + @ConstructorProperties({ "name", "rawName", "type", "nullable", "metadata", "errorCapable", + "valid", "elementSchema", "comment", "props", "defaultValue" }) + // Checkstyle off to let have 11 parameters to this constructor (normally 10 max) + // CHECKSTYLE:OFF public Entry( final String name, final String rawName, @@ -59,7 +61,8 @@ public Entry( final Schema elementSchema, final String comment, final Map props, - final Object internalDefaultValue) { + final Object defaultValue) { + // CHECKSTYLE:ON this.name = name; this.rawName = rawName; this.type = type; @@ -70,10 +73,14 @@ public Entry( this.elementSchema = elementSchema; this.comment = comment; this.props.putAll(props); - this.internalDefaultValue = internalDefaultValue; + this.defaultValue = defaultValue; } - public T getDefaultValue(){ + private Object getInternalDefaultValue() { + return defaultValue; + } + + public T getDefaultValue() { return (T) this.getInternalDefaultValue(); } diff --git a/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/JsonEntryModel.java b/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/JsonEntryModel.java deleted file mode 100644 index a5302e565df3e..0000000000000 --- a/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/JsonEntryModel.java +++ /dev/null @@ -1,127 +0,0 @@ -/** - * Copyright (C) 2006-2025 Talend Inc. - www.talend.com - * - * 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.talend.sdk.component.server.front.model; - -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; - -import javax.json.JsonObject; -import javax.json.JsonValue; - -import lombok.Getter; -import lombok.Setter; - -public class JsonEntryModel { - - private final JsonObject jsonEntry; - - @Getter - private final boolean isMetadata; - - /** - * Type of the entry, this determine which other fields are populated. - */ - @Setter - @Getter - private JsonSchemaModel.Type type; - - /** - * Is this entry a metadata entry. - */ - @Setter - @Getter - private boolean metadata; - - /** - * For type == record, the element type. - */ - @Getter - @Setter - private JsonSchemaModel elementSchema; - - /** - * metadata - */ - @Setter - @Getter - private Map props = new LinkedHashMap<>(0); - - JsonEntryModel(final JsonObject jsonEntry, final boolean isMetadata) { - this.jsonEntry = jsonEntry; - this.isMetadata = isMetadata; - this.type = JsonSchemaModel.Type.valueOf(jsonEntry.getString("type")); - this.props = parseProps(jsonEntry.getJsonObject("props")); - - // Parse element schema if present - this.elementSchema = jsonEntry.containsKey("elementSchema") - ? new JsonSchemaModel(jsonEntry.getJsonObject("elementSchema").toString()) - : null; - } - - private Map parseProps(final JsonObject propsObj) { - if (propsObj == null) { - return Collections.emptyMap(); - } - - Map result = new HashMap<>(); - propsObj.forEach((key, value) -> - result.put(key, value.getValueType() == JsonValue.ValueType.STRING - ? ((javax.json.JsonString) value).getString() - : value.toString())); - return result; - } - - public String getName() { - return jsonEntry.getString("name"); - } - - public String getRawName() { - return jsonEntry.getString("rawName", getName()); - } - - public String getOriginalFieldName() { - return getRawName() != null ? getRawName() : getName(); - } - - public boolean isNullable() { - return !jsonEntry.containsKey("nullable") || jsonEntry.getBoolean("nullable"); - } - - public boolean isErrorCapable() { - return jsonEntry.getBoolean("errorCapable", false); - } - - public boolean isValid() { - return jsonEntry.getBoolean("valid", true); - } - - public T getDefaultValue() { - return jsonEntry.containsKey("defaultValue") - ? (T) jsonEntry.get("defaultValue") - : null; - } - - public String getComment() { - return jsonEntry.getString("comment", null); - } - - public String getProp(final String property) { - return props.get(property); - } - -} diff --git a/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/JsonSchemaModel.java b/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/JsonSchemaModel.java deleted file mode 100644 index 426321f2ff046..0000000000000 --- a/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/JsonSchemaModel.java +++ /dev/null @@ -1,155 +0,0 @@ -/** - * Copyright (C) 2006-2025 Talend Inc. - www.talend.com - * - * 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.talend.sdk.component.server.front.model; - -import java.io.StringReader; -import java.math.BigDecimal; -import java.time.temporal.Temporal; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import javax.json.Json; -import javax.json.JsonArray; -import javax.json.JsonObject; -import javax.json.JsonReader; -import javax.json.JsonValue; - -import lombok.Getter; -import lombok.Setter; - - -public class JsonSchemaModel { - - @Getter - @Setter - private Type type; - - private final JsonObject jsonSchema; - - @Setter - @Getter - private JsonSchemaModel elementSchema; - - @Getter - @Setter - private List entries = new ArrayList<>(); - - @Getter - private List metadataEntries = new ArrayList<>(); - - @Getter - @Setter - private Map props = new HashMap<>(); - - @Getter - @Setter - private Map entryMap = new HashMap<>(); - - public JsonSchemaModel(final String jsonString) { - try (JsonReader reader = Json.createReader(new StringReader(jsonString))) { - this.jsonSchema = reader.readObject(); - } - - this.type = Type.valueOf(jsonSchema.getString("type")); - this.props = parseProps(jsonSchema.getJsonObject("props")); - - // Parse entries - this.entries = parseEntries(jsonSchema.getJsonArray("entries"), false); - this.metadataEntries = parseEntries(jsonSchema.getJsonArray("metadata"), true); - - // Build entry map - this.entryMap = new HashMap<>(); - getEntries().forEach(e -> entryMap.put(e.getName(), e)); - - // Parse element schema for ARRAY types - this.elementSchema = jsonSchema.containsKey("elementSchema") - ? new JsonSchemaModel(jsonSchema.getJsonObject("elementSchema").toString()) - : null; - } - - private List parseEntries(final JsonArray jsonArray, final boolean isMetadata) { - if (jsonArray == null) { - return Collections.emptyList(); - } - - return jsonArray.stream() - .map(JsonValue::asJsonObject) - .map(obj -> new JsonEntryModel(obj, isMetadata)) - .toList(); - } - - private Map parseProps(final JsonObject propsObj) { - if (propsObj == null) { - return Collections.emptyMap(); - } - - Map result = new HashMap<>(); - propsObj.forEach((key, value) -> - result.put(key, value.getValueType() == JsonValue.ValueType.STRING - ? ((javax.json.JsonString) value).getString() - : value.toString())); - return result; - } - - public enum Type { - - RECORD(new Class[] { Record.class }), - ARRAY(new Class[] { Collection.class }), - STRING(new Class[] { String.class, Object.class }), - BYTES(new Class[] { byte[].class, Byte[].class }), - INT(new Class[] { Integer.class }), - LONG(new Class[] { Long.class }), - FLOAT(new Class[] { Float.class }), - DOUBLE(new Class[] { Double.class }), - BOOLEAN(new Class[] { Boolean.class }), - DATETIME(new Class[] { Long.class, Date.class, Temporal.class }), - DECIMAL(new Class[] { BigDecimal.class }); - - /** - * All compatibles Java classes - */ - private final Class[] classes; - - Type(final Class[] classes) { - this.classes = classes; - } - - /** - * Check if input can be affected to an entry of this type. - * - * @param input : object. - * - * @return true if input is null or ok. - */ - public boolean isCompatible(final Object input) { - if (input == null) { - return true; - } - for (final Class clazz : classes) { - if (clazz.isInstance(input)) { - return true; - } - } - return false; - } - } -} diff --git a/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Schema.java b/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Schema.java index c26365203420d..50ef551ead82b 100644 --- a/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Schema.java +++ b/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Schema.java @@ -28,74 +28,74 @@ @Data public final class Schema { - private final Type type; + private final Type type; - private final Schema elementSchema; + private final Schema elementSchema; - private final List entries; + private final List entries; - private final List metadata; + private final List metadata; - private final Map props; + private final Map props; - @ConstructorProperties({"type", "elementSchema", "entries", "metadata", "props"}) - public Schema( - final Type type, - final Schema elementSchema, - final List entries, - final List metadata, - final Map props) { - this.type = type; - this.elementSchema = elementSchema; - this.entries = entries; - this.metadata = metadata; - this.props = props; - } + @ConstructorProperties({ "type", "elementSchema", "entries", "metadata", "props" }) + public Schema( + final Type type, + final Schema elementSchema, + final List entries, + final List metadata, + final Map props) { + this.type = type; + this.elementSchema = elementSchema; + this.entries = entries; + this.metadata = metadata; + this.props = props; + } - public String getProp(final String key){ - return this.props.get(key); - } + public String getProp(final String key) { + return this.props.get(key); + } - public enum Type { + public enum Type { - RECORD(new Class[] { Record.class }), - ARRAY(new Class[] { Collection.class }), - STRING(new Class[] { String.class, Object.class }), - BYTES(new Class[] { byte[].class, Byte[].class }), - INT(new Class[] { Integer.class }), - LONG(new Class[] { Long.class }), - FLOAT(new Class[] { Float.class }), - DOUBLE(new Class[] { Double.class }), - BOOLEAN(new Class[] { Boolean.class }), - DATETIME(new Class[] { Long.class, Date.class, Temporal.class }), - DECIMAL(new Class[] { BigDecimal.class }); + RECORD(new Class[] { Record.class }), + ARRAY(new Class[] { Collection.class }), + STRING(new Class[] { String.class, Object.class }), + BYTES(new Class[] { byte[].class, Byte[].class }), + INT(new Class[] { Integer.class }), + LONG(new Class[] { Long.class }), + FLOAT(new Class[] { Float.class }), + DOUBLE(new Class[] { Double.class }), + BOOLEAN(new Class[] { Boolean.class }), + DATETIME(new Class[] { Long.class, Date.class, Temporal.class }), + DECIMAL(new Class[] { BigDecimal.class }); - /** - * All compatibles Java classes - */ - private final Class[] classes; + /** + * All compatibles Java classes + */ + private final Class[] classes; - Type(final Class[] classes) { - this.classes = classes; - } + Type(final Class[] classes) { + this.classes = classes; + } - /** - * Check if input can be affected to an entry of this type. - * - * @param input : object. - * - * @return true if input is null or ok. - */ - public boolean isCompatible(final Object input) { - if (input == null) { - return true; - } - for (final Class clazz : classes) { - if (clazz.isInstance(input)) { - return true; + /** + * Check if input can be affected to an entry of this type. + * + * @param input : object. + * + * @return true if input is null or ok. + */ + public boolean isCompatible(final Object input) { + if (input == null) { + return true; + } + for (final Class clazz : classes) { + if (clazz.isInstance(input)) { + return true; + } + } + return false; } - } - return false; } - } } \ No newline at end of file diff --git a/component-server-parent/component-server/pom.xml b/component-server-parent/component-server/pom.xml index 60707d9bfbb0c..6cecd547fd78a 100644 --- a/component-server-parent/component-server/pom.xml +++ b/component-server-parent/component-server/pom.xml @@ -203,7 +203,6 @@ ${jackson.version} test - @@ -499,4 +498,4 @@ - + \ No newline at end of file diff --git a/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/EntryTest.java b/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/EntryTest.java similarity index 58% rename from component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/EntryTest.java rename to component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/EntryTest.java index 51f30824d39f8..1b7cfe60b85b8 100644 --- a/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/EntryTest.java +++ b/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/EntryTest.java @@ -13,20 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.talend.sdk.component.server; +package org.talend.sdk.component.server.front; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; -import com.fasterxml.jackson.databind.ObjectMapper; import java.util.LinkedHashMap; - import java.util.Map; +import javax.json.bind.Jsonb; +import javax.json.bind.JsonbBuilder; + +import com.fasterxml.jackson.databind.ObjectMapper; + import org.junit.jupiter.api.Test; +import org.talend.sdk.component.api.record.SchemaProperty; +import org.talend.sdk.component.api.record.SchemaProperty.LogicalType; +import org.talend.sdk.component.runtime.record.RecordBuilderFactoryImpl; import org.talend.sdk.component.server.front.model.Entry; import org.talend.sdk.component.server.front.model.Schema; @@ -35,11 +40,11 @@ */ class EntryTest { - private Entry createValidEntry() { - Map props = new LinkedHashMap<>(0); - props.put("p1", "v1"); + private Entry createValidEntry() { + Map props = new LinkedHashMap<>(0); + props.put("p1", "v1"); return new Entry("name", "raw", Schema.Type.STRING, true, false, true, - true, null,"comment", props, "default"); + true, null, "comment", props, "default"); } // ---------------------------------------------------------------------- @@ -59,7 +64,7 @@ void builderCreatesValidEntry() { assertTrue(entry.isErrorCapable()); assertTrue(entry.isValid()); assertEquals("comment", entry.getComment()); - assertEquals("default", entry.getInternalDefaultValue()); + assertEquals("default", entry.getDefaultValue()); assertEquals("v1", entry.getProps().get("p1")); } @@ -88,45 +93,39 @@ void getPropReturnsProperty() { @Test void deserializeEntryFromJson() throws Exception { - String json = """ - { - "name": "field", - "rawName": "field_raw", - "type": "STRING", - "nullable": true, - "metadata": false, - "errorCapable": true, - "valid": true, - "elementSchema": { - "type": "STRING" - }, - "comment": "test comment", - "props": { - "p1": "v1" - }, - "internalDefaultValue": "defaultValue" + RecordBuilderFactoryImpl factory = new RecordBuilderFactoryImpl("test"); + org.talend.sdk.component.api.record.Schema.Entry entryImpl = factory.newEntryBuilder() + .withName("éèfield") + .withLogicalType(LogicalType.UUID) + .withNullable(false) + .withMetadata(false) + .withErrorCapable(false) + .withComment("test comment") + .withProps(Map.of("p1", "v1")) + .withDefaultValue("defaultValue") + .build(); + + try (Jsonb jsonb = JsonbBuilder.create()) { + String json = jsonb.toJson(entryImpl); + + ObjectMapper mapper = new ObjectMapper(); + Entry entry = mapper.readValue(json, Entry.class); + + assertEquals("_field", entry.getName()); + assertEquals("éèfield", entry.getRawName()); + assertEquals("éèfield", entry.getOriginalFieldName()); + assertEquals(Schema.Type.STRING, entry.getType()); + assertEquals(LogicalType.UUID.key(), entry.getProp(SchemaProperty.LOGICAL_TYPE)); + + assertFalse(entry.isNullable()); + assertFalse(entry.isMetadata()); + assertFalse(entry.isErrorCapable()); + assertTrue(entry.isValid()); + + assertEquals("test comment", entry.getComment()); + assertEquals("v1", entry.getProps().get("p1")); + assertEquals("defaultValue", entry.getDefaultValue()); } - """; - - ObjectMapper mapper = new ObjectMapper(); - Entry entry = mapper.readValue(json, Entry.class); - - assertEquals("field", entry.getName()); - assertEquals("field_raw", entry.getRawName()); - assertEquals("field_raw", entry.getOriginalFieldName()); - assertEquals(Schema.Type.STRING, entry.getType()); - - assertTrue(entry.isNullable()); - assertFalse(entry.isMetadata()); - assertTrue(entry.isErrorCapable()); - assertTrue(entry.isValid()); - - assertNotNull(entry.getElementSchema()); - assertEquals(Schema.Type.STRING, entry.getElementSchema().getType()); - - assertEquals("test comment", entry.getComment()); - assertEquals("v1", entry.getProps().get("p1")); - assertEquals("defaultValue", entry.getInternalDefaultValue()); } -} +} \ No newline at end of file diff --git a/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/JsonSchemaTest.java b/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/JsonSchemaTest.java deleted file mode 100644 index 1bb2c4e0bbb38..0000000000000 --- a/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/JsonSchemaTest.java +++ /dev/null @@ -1,357 +0,0 @@ -/** - * Copyright (C) 2006-2025 Talend Inc. - www.talend.com - * - * 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.talend.sdk.component.server.front; - -import java.util.List; -import org.junit.jupiter.api.Assertions; -import static org.talend.sdk.component.api.record.Schema.Type.LONG; -import static org.talend.sdk.component.api.record.Schema.Type.STRING; -import static org.junit.jupiter.api.Assertions.*; - -import javax.json.bind.Jsonb; -import javax.json.bind.JsonbBuilder; -import org.junit.jupiter.api.Test; - -import org.talend.sdk.component.api.record.Schema; -import org.talend.sdk.component.runtime.record.SchemaImpl; -import org.talend.sdk.component.server.front.model.JsonEntryModel; -import org.talend.sdk.component.server.front.model.JsonSchemaModel; - -class JsonSchemaTest { - - private final Jsonb jsonb = JsonbBuilder.create(); - - @Test - void shouldSerializeSchemaImplAndDeserializeToJsonSchemaModel() { - // given - final Schema schema = new SchemaImpl.BuilderImpl() // - .withType(Schema.Type.RECORD) - .withEntry(new SchemaImpl.EntryImpl.BuilderImpl() - .withName("id") - .withType(STRING) - .build()) - .withEntry(new SchemaImpl.EntryImpl.BuilderImpl() - .withName("field2") - .withType(LONG) - .withNullable(false) - .withComment("field2 comment") - .build()) - .withProp("namespace", "test") - .build(); - - // when: serialize SchemaImpl - String json = jsonb.toJson(schema); - - // then: sanity check JSON - assertTrue(json.contains("\"type\":\"RECORD\"")); - assertTrue(json.contains("\"entries\"")); - - // when: deserialize into JsonSchemaModel - JsonSchemaModel model = new JsonSchemaModel(json); - - // then - assertEquals(JsonSchemaModel.Type.RECORD, model.getType()); - assertEquals("test", model.getProps().get("namespace")); - - assertEquals(2, model.getEntries().size()); - assertEquals("id", model.getEntries().get(0).getName()); - assertEquals(JsonSchemaModel.Type.STRING, model.getEntries().get(0).getType()); - assertEquals("field2", model.getEntries().get(1).getName()); - assertEquals(JsonSchemaModel.Type.LONG, model.getEntries().get(1).getType()); - assertEquals("field2 comment", model.getEntries().get(1).getComment()); - assertEquals(false, model.getEntries().get(1).isNullable()); - - // entryMap built correctly - assertTrue(model.getEntryMap().containsKey("id")); - } - - @Test - void testAllDataTypes() { - // given - final Schema schema = new SchemaImpl.BuilderImpl() // - .withType(Schema.Type.RECORD) - .withEntry(new SchemaImpl.EntryImpl.BuilderImpl() - .withName("id") - .withType(Schema.Type.INT) - .withNullable(false) - .withErrorCapable(true) - .build()) - .withEntry(new SchemaImpl.EntryImpl.BuilderImpl() - .withName("field_date") - .withType(Schema.Type.DATETIME) - .withNullable(false) - .withErrorCapable(false) - .withComment("field date") - .build()) - .withEntry(new SchemaImpl.EntryImpl.BuilderImpl() - .withName("field_boolean") - .withType(Schema.Type.BOOLEAN) - .withNullable(true) - .withComment("field boolean") - .build()) - .withEntry(new SchemaImpl.EntryImpl.BuilderImpl() - .withName("field_bytes") - .withType(Schema.Type.BYTES) - .withComment("field bytes") - .withNullable(true) - .build()) - .withEntry(new SchemaImpl.EntryImpl.BuilderImpl() - .withName("field_decimal") - .withType(Schema.Type.DECIMAL) - .withComment("field decimal") - .withNullable(true) - .build()) - .withEntry(new SchemaImpl.EntryImpl.BuilderImpl() - .withName("field_double") - .withType(Schema.Type.DOUBLE) - .withComment("field double") - .withNullable(true) - .build()) - .withEntry(new SchemaImpl.EntryImpl.BuilderImpl() - .withName("field_float") - .withType(Schema.Type.FLOAT) - .withComment("field float") - .withNullable(true) - .build()) - .withProp("namespace", "test") - .build(); - - // when: serialize SchemaImpl - String json = jsonb.toJson(schema); - - // then: sanity check JSON - assertTrue(json.contains("\"type\":\"RECORD\"")); - assertTrue(json.contains("\"entries\"")); - - // when: deserialize into JsonSchemaModel - JsonSchemaModel model = new JsonSchemaModel(json); - - // then - assertEquals(JsonSchemaModel.Type.RECORD, model.getType()); - assertEquals("test", model.getProps().get("namespace")); - - assertEquals(7, model.getEntries().size()); - assertEquals("id", model.getEntries().get(0).getName()); - assertEquals(JsonSchemaModel.Type.INT, model.getEntries().get(0).getType()); - assertFalse(model.getEntries().get(0).isNullable()); - assertTrue(model.getEntries().get(0).isErrorCapable()); - assertEquals("field_date", model.getEntries().get(1).getName()); - assertEquals(JsonSchemaModel.Type.DATETIME, model.getEntries().get(1).getType()); - assertEquals("field date", model.getEntries().get(1).getComment()); - assertFalse(model.getEntries().get(1).isNullable()); - assertFalse(model.getEntries().get(1).isErrorCapable()); - - assertEquals("field_boolean", model.getEntries().get(2).getName()); - assertEquals(JsonSchemaModel.Type.BOOLEAN, model.getEntries().get(2).getType()); - assertEquals("field boolean", model.getEntries().get(2).getComment()); - assertTrue(model.getEntries().get(2).isNullable()); - assertFalse(model.getEntries().get(2).isErrorCapable()); - assertEquals("field_bytes", model.getEntries().get(3).getName()); - assertEquals(JsonSchemaModel.Type.BYTES, model.getEntries().get(3).getType()); - assertEquals("field bytes", model.getEntries().get(3).getComment()); - assertTrue(model.getEntries().get(3).isNullable()); - - assertEquals("field_decimal", model.getEntries().get(4).getName()); - assertEquals(JsonSchemaModel.Type.DECIMAL, model.getEntries().get(4).getType()); - assertEquals("field decimal", model.getEntries().get(4).getComment()); - assertTrue(model.getEntries().get(4).isNullable()); - assertEquals("field_double", model.getEntries().get(5).getName()); - assertEquals(JsonSchemaModel.Type.DOUBLE, model.getEntries().get(5).getType()); - assertEquals("field double", model.getEntries().get(5).getComment()); - assertTrue(model.getEntries().get(5).isNullable()); - - assertEquals("field_float", model.getEntries().get(6).getName()); - assertEquals(JsonSchemaModel.Type.FLOAT, model.getEntries().get(6).getType()); - assertEquals("field float", model.getEntries().get(6).getComment()); - assertTrue(model.getEntries().get(6).isNullable()); - // entryMap built correctly - assertTrue(model.getEntryMap().containsKey("id")); - assertEquals("id,field_date,field_boolean,field_bytes,field_decimal,field_double,field_float", - model.getProps().get("talend.fields.order")); - } - - @Test - void shouldParseArrayEntryElementSchema() { - Schema.Entry nameEntry = new SchemaImpl.EntryImpl.BuilderImpl() - .withName("name") - .withNullable(true) - .withType(Schema.Type.STRING) - .build(); - Schema.Entry ageEntry = new SchemaImpl.EntryImpl.BuilderImpl() - .withName("age") - .withNullable(true) - .withType(Schema.Type.INT) - .build(); - Schema customerSchema = new SchemaImpl.BuilderImpl() // - .withType(Schema.Type.RECORD) - .withEntry(nameEntry) - .withEntry(ageEntry) - .build(); - - final Schema schema = new SchemaImpl.BuilderImpl() // - .withType(Schema.Type.ARRAY) // - .withElementSchema(customerSchema) - .withProp("namespace", "test") - .build(); - - String json = jsonb.toJson(schema); - JsonSchemaModel model = new JsonSchemaModel(json); - - JsonSchemaModel innerSchema = model.getElementSchema(); - - assertEquals(JsonSchemaModel.Type.ARRAY, model.getType()); - assertNotNull(innerSchema); - assertEquals(JsonSchemaModel.Type.RECORD, innerSchema.getType()); - //check entry name - final List entryNames = innerSchema.getEntries().stream().map(JsonEntryModel::getName) - .toList(); - Assertions.assertEquals(2, entryNames.size()); - Assertions.assertTrue(entryNames.contains("name")); - Assertions.assertTrue(entryNames.contains("age")); - - //check entry type - final List entryTypes = innerSchema.getEntries().stream().map(JsonEntryModel::getType) - .toList(); - Assertions.assertTrue(entryTypes.contains(JsonSchemaModel.Type.INT)); - Assertions.assertTrue(entryTypes.contains(JsonSchemaModel.Type.STRING)); - } - - - @Test - void shouldMarkMetadataEntries() { - Schema.Entry meta1 = new SchemaImpl.EntryImpl.BuilderImpl() // - .withName("meta1") // - .withType(Schema.Type.INT) // - .withMetadata(true) // - .build(); - - Schema.Entry meta2 = new SchemaImpl.EntryImpl.BuilderImpl() // - .withName("meta2") // - .withType(Schema.Type.STRING) // - .withMetadata(true) // - .withNullable(true) // - .build(); - - Schema.Entry data1 = new SchemaImpl.EntryImpl.BuilderImpl() // - .withName("data1") // - .withType(Schema.Type.INT) // - .build(); - Schema.Entry data2 = new SchemaImpl.EntryImpl.BuilderImpl() // - .withName("data2") // - .withType(Schema.Type.STRING) // - .withNullable(true) // - .build(); - - Schema schema = new SchemaImpl.BuilderImpl() // - .withType(Schema.Type.RECORD) // - .withEntry(data1) // - .withEntry(meta1) // - .withEntry(data2) // - .withEntry(meta2) // - .build(); - - String json = jsonb.toJson(schema); - JsonSchemaModel model = new JsonSchemaModel(json); - - JsonEntryModel metaEntry = model.getMetadataEntries().get(0); - - //check entry name - final List entryNames = model.getEntries().stream().map(JsonEntryModel::getName) - .toList(); - Assertions.assertEquals(2, entryNames.size()); - Assertions.assertTrue(entryNames.contains(data1.getName())); - Assertions.assertTrue(entryNames.contains(data2.getName())); - Assertions.assertEquals(4, schema.getAllEntries().count()); - - //check entry type - final List entryTypes = model.getEntries().stream().map(JsonEntryModel::getType) - .map(JsonSchemaModel.Type::name) - .toList(); - Assertions.assertEquals(2, entryTypes.size()); - Assertions.assertTrue(entryTypes.contains(data1.getType().name())); - Assertions.assertTrue(entryTypes.contains(data2.getType().name())); - - //check meta name - final List metaEntryNames = model.getMetadataEntries().stream().map(JsonEntryModel::getName) - .toList(); - Assertions.assertEquals(2, metaEntryNames.size()); - Assertions.assertTrue(metaEntryNames.contains(meta1.getName())); - Assertions.assertTrue(metaEntryNames.contains(meta2.getName())); - - //check meta type - final List metaEntryTypes = model.getMetadataEntries().stream().map(JsonEntryModel::getType) - .map(JsonSchemaModel.Type::name) - .toList(); - Assertions.assertEquals(2, metaEntryTypes.size()); - Assertions.assertTrue(metaEntryTypes.contains(meta1.getType().name())); - Assertions.assertTrue(metaEntryTypes.contains(meta2.getType().name())); - - assertTrue(metaEntry.isMetadata()); - assertEquals("meta1", metaEntry.getName()); - } - - @Test - void shouldParseEntryProps() { - Schema schema = new SchemaImpl.BuilderImpl() // - .withType(Schema.Type.RECORD) - //.type(Schema.Type.RECORD) - .withEntry(new SchemaImpl.EntryImpl.BuilderImpl() - .withName("field") - .withType(Schema.Type.STRING) - .withProp("format", "email") - .build()) - .build(); - - String json = jsonb.toJson(schema); - JsonSchemaModel model = new JsonSchemaModel(json); - - JsonEntryModel entry = model.getEntries().get(0); - - assertEquals("email", entry.getProps().get("format")); - } - - @Test - void shouldFailForInvalidEntryType() { - String invalidJson = """ - { - "type": "RECORD", - "entries": [ - { "name": "x", "type": "INVALID" } - ] - } - """; - - assertThrows(IllegalArgumentException.class, - () -> new JsonSchemaModel(invalidJson)); - } - - - @Test - void shouldFailForInvalidSchemaType() { - String invalidJson = """ - { - "type": "NOT_VALID", - "entries": [] - } - """; - - assertThrows(IllegalArgumentException.class, - () -> new JsonSchemaModel(invalidJson)); - } - -} - - diff --git a/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/SchemaTest.java b/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/SchemaTest.java index 301ea76f655d7..a533e7298c4f9 100644 --- a/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/SchemaTest.java +++ b/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/SchemaTest.java @@ -20,52 +20,224 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; - import static org.talend.sdk.component.api.record.Schema.Type.LONG; import static org.talend.sdk.component.api.record.Schema.Type.STRING; import java.io.StringReader; import java.util.List; + import javax.json.bind.Jsonb; import javax.json.bind.JsonbBuilder; -import org.talend.sdk.component.api.record.Schema; -import org.talend.sdk.component.runtime.record.SchemaImpl; -import org.talend.sdk.component.server.front.model.Entry; -import org.talend.sdk.component.server.front.model.JsonEntryModel; -import org.talend.sdk.component.server.front.model.JsonSchemaModel; +import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.talend.sdk.component.runtime.record.RecordBuilderFactoryImpl; +import org.talend.sdk.component.runtime.record.SchemaImpl; +import org.talend.sdk.component.server.front.model.Entry; +import org.talend.sdk.component.server.front.model.Schema; class SchemaTest { private final Jsonb jsonb = JsonbBuilder.create(); - private org.talend.sdk.component.server.front.model.Schema emptySchema() { // minimal self-referential schema to satisfy required fields org.talend.sdk.component.server.front.model.Schema placeholder = - new org.talend.sdk.component.server.front.model.Schema(org.talend.sdk.component.server.front.model.Schema.Type.RECORD, + new org.talend.sdk.component.server.front.model.Schema( + org.talend.sdk.component.server.front.model.Schema.Type.RECORD, null, null, null, null); - return new org.talend.sdk.component.server.front.model.Schema(org.talend.sdk.component.server.front.model.Schema.Type.RECORD, + return new org.talend.sdk.component.server.front.model.Schema( + org.talend.sdk.component.server.front.model.Schema.Type.RECORD, placeholder, null, null, null); } private Entry entry(String name) { - return new Entry(name, name, org.talend.sdk.component.server.front.model.Schema.Type.STRING,false, + return new Entry(name, name, org.talend.sdk.component.server.front.model.Schema.Type.STRING, false, false, false, true, emptySchema(), "c", null, ""); } + @Test + void deserializeSchemaFromJson() throws Exception { + RecordBuilderFactoryImpl factory = new RecordBuilderFactoryImpl("test"); + org.talend.sdk.component.api.record.Schema schemaImpl = + factory.newSchemaBuilder(org.talend.sdk.component.api.record.Schema.Type.RECORD) + .withEntry(factory.newEntryBuilder() + .withName("id") + .withType(org.talend.sdk.component.api.record.Schema.Type.LONG) + .withNullable(false) + .withComment("User ID") + .build()) + .withEntry(factory.newEntryBuilder() + .withName("username") + .withType(org.talend.sdk.component.api.record.Schema.Type.STRING) + .withNullable(false) + .withComment("Username") + .build()) + .withEntry(factory.newEntryBuilder() + .withName("email") + .withType(org.talend.sdk.component.api.record.Schema.Type.STRING) + .withNullable(false) + .withComment("Email address") + .build()) + .withEntry(factory.newEntryBuilder() + .withName("passwordHash") + .withType(org.talend.sdk.component.api.record.Schema.Type.BYTES) + .withNullable(false) + .withComment("Password hash") + .build()) + .withEntry(factory.newEntryBuilder() + .withName("createdAt") + .withType(org.talend.sdk.component.api.record.Schema.Type.DATETIME) + .withNullable(false) + .withComment("Account creation date") + .build()) + .withEntry(factory.newEntryBuilder() + .withName("lastLogin") + .withType(org.talend.sdk.component.api.record.Schema.Type.DATETIME) + .withNullable(true) + .withComment("Last login date") + .build()) + .withEntry(factory.newEntryBuilder() + .withName("isActive") + .withType(org.talend.sdk.component.api.record.Schema.Type.BOOLEAN) + .withNullable(false) + .withDefaultValue(true) + .withComment("Is active user") + .build()) + .withEntry(factory.newEntryBuilder() + .withName("roles") + .withType(org.talend.sdk.component.api.record.Schema.Type.ARRAY) + .withElementSchema( + factory.newSchemaBuilder(org.talend.sdk.component.api.record.Schema.Type.STRING) + .build()) + .withNullable(true) + .withComment("User roles") + .build()) + .withEntry(factory.newEntryBuilder() + .withName("profile") + .withType(org.talend.sdk.component.api.record.Schema.Type.RECORD) + .withElementSchema( + factory.newSchemaBuilder(org.talend.sdk.component.api.record.Schema.Type.RECORD) + .withEntry(factory.newEntryBuilder() + .withName("firstName") + .withType( + org.talend.sdk.component.api.record.Schema.Type.STRING) + .withNullable(true) + .build()) + .withEntry(factory.newEntryBuilder() + .withName("lastName") + .withType( + org.talend.sdk.component.api.record.Schema.Type.STRING) + .withNullable(true) + .build()) + .withEntry(factory.newEntryBuilder() + .withName("birthDate") + .withType( + org.talend.sdk.component.api.record.Schema.Type.DATETIME) + .withNullable(true) + .build()) + .build()) + .withNullable(true) + .withComment("User profile") + .build()) + .build(); + + String json = jsonb.toJson(schemaImpl); + + ObjectMapper mapper = new ObjectMapper(); + Schema schema = mapper.readValue(json, Schema.class); + + assertNotNull(schema); + assertEquals(Schema.Type.RECORD, schema.getType()); + assertNotNull(schema.getEntries()); + assertEquals(9, schema.getEntries().size()); + + Entry idEntry = schema.getEntries().get(0); + assertEquals("id", idEntry.getName()); + assertEquals(Schema.Type.LONG, idEntry.getType()); + assertFalse(idEntry.isNullable()); + assertEquals("User ID", idEntry.getComment()); + + Entry usernameEntry = schema.getEntries().get(1); + assertEquals("username", usernameEntry.getName()); + assertEquals(Schema.Type.STRING, usernameEntry.getType()); + assertFalse(usernameEntry.isNullable()); + assertEquals("Username", usernameEntry.getComment()); + + Entry emailEntry = schema.getEntries().get(2); + assertEquals("email", emailEntry.getName()); + assertEquals(Schema.Type.STRING, emailEntry.getType()); + assertFalse(emailEntry.isNullable()); + assertEquals("Email address", emailEntry.getComment()); + + Entry passwordHashEntry = schema.getEntries().get(3); + assertEquals("passwordHash", passwordHashEntry.getName()); + assertEquals(Schema.Type.BYTES, passwordHashEntry.getType()); + assertFalse(passwordHashEntry.isNullable()); + assertEquals("Password hash", passwordHashEntry.getComment()); + + Entry createdAtEntry = schema.getEntries().get(4); + assertEquals("createdAt", createdAtEntry.getName()); + assertEquals(Schema.Type.DATETIME, createdAtEntry.getType()); + assertFalse(createdAtEntry.isNullable()); + assertEquals("Account creation date", createdAtEntry.getComment()); + + Entry lastLoginEntry = schema.getEntries().get(5); + assertEquals("lastLogin", lastLoginEntry.getName()); + assertEquals(Schema.Type.DATETIME, lastLoginEntry.getType()); + assertTrue(lastLoginEntry.isNullable()); + assertEquals("Last login date", lastLoginEntry.getComment()); + + Entry isActiveEntry = schema.getEntries().get(6); + assertEquals("isActive", isActiveEntry.getName()); + assertEquals(Schema.Type.BOOLEAN, isActiveEntry.getType()); + assertFalse(isActiveEntry.isNullable()); + assertEquals("Is active user", isActiveEntry.getComment()); + assertEquals(true, isActiveEntry.getDefaultValue()); + + Entry rolesEntry = schema.getEntries().get(7); + assertEquals("roles", rolesEntry.getName()); + assertEquals(Schema.Type.ARRAY, rolesEntry.getType()); + assertTrue(rolesEntry.isNullable()); + assertEquals("User roles", rolesEntry.getComment()); + assertNotNull(rolesEntry.getElementSchema()); + assertEquals(Schema.Type.STRING, rolesEntry.getElementSchema().getType()); + + Entry profileEntry = schema.getEntries().get(8); + assertEquals("profile", profileEntry.getName()); + assertEquals(Schema.Type.RECORD, profileEntry.getType()); + assertTrue(profileEntry.isNullable()); + assertEquals("User profile", profileEntry.getComment()); + assertNotNull(profileEntry.getElementSchema()); + assertEquals(Schema.Type.RECORD, profileEntry.getElementSchema().getType()); + assertEquals(3, profileEntry.getElementSchema().getEntries().size()); + + Entry firstNameEntry = profileEntry.getElementSchema().getEntries().get(0); + assertEquals("firstName", firstNameEntry.getName()); + assertEquals(Schema.Type.STRING, firstNameEntry.getType()); + assertTrue(firstNameEntry.isNullable()); + + Entry lastNameEntry = profileEntry.getElementSchema().getEntries().get(1); + assertEquals("lastName", lastNameEntry.getName()); + assertEquals(Schema.Type.STRING, lastNameEntry.getType()); + assertTrue(lastNameEntry.isNullable()); + + Entry birthDateEntry = profileEntry.getElementSchema().getEntries().get(2); + assertEquals("birthDate", birthDateEntry.getName()); + assertEquals(Schema.Type.DATETIME, birthDateEntry.getType()); + assertTrue(birthDateEntry.isNullable()); + } + @Test void shouldSerializeSchemaImplAndDeserializeToJsonSchemaModel() { // given - final Schema schema = new SchemaImpl.BuilderImpl() // - .withType(Schema.Type.RECORD) + final org.talend.sdk.component.api.record.Schema schema = new SchemaImpl.BuilderImpl() // + .withType(org.talend.sdk.component.api.record.Schema.Type.RECORD) .withEntry(new SchemaImpl.EntryImpl.BuilderImpl() .withName("id") .withType(STRING) @@ -88,7 +260,7 @@ void shouldSerializeSchemaImplAndDeserializeToJsonSchemaModel() { // when: deserialize into Schema org.talend.sdk.component.server.front.model.Schema model = jsonb.fromJson(new StringReader(json), - org.talend.sdk.component.server.front.model.Schema.class); + org.talend.sdk.component.server.front.model.Schema.class); // then assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.RECORD, model.getType()); @@ -96,12 +268,12 @@ void shouldSerializeSchemaImplAndDeserializeToJsonSchemaModel() { assertEquals(2, model.getEntries().size()); assertEquals("id", model.getEntries().get(0).getName()); - assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.STRING, model.getEntries().get(0).getType()); + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.STRING, + model.getEntries().get(0).getType()); assertEquals("field2", model.getEntries().get(1).getName()); assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.LONG, model.getEntries().get(1).getType()); assertEquals("field2 comment", model.getEntries().get(1).getComment()); - assertEquals(false, model.getEntries().get(1).isNullable()); - + assertFalse(model.getEntries().get(1).isNullable()); } @@ -202,7 +374,8 @@ void deserializeArraySchemaWithElementSchema() { assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.ARRAY, schema.getType()); assertNotNull(schema.getElementSchema()); - assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.STRING, schema.getElementSchema().getType()); + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.STRING, + schema.getElementSchema().getType()); } @Test @@ -242,30 +415,30 @@ void unknownPropertyIsNull() { @Test void testAllDataTypes1() { // given - final Schema schema = new SchemaImpl.BuilderImpl() // - .withType(Schema.Type.RECORD) + final org.talend.sdk.component.api.record.Schema schema = new SchemaImpl.BuilderImpl() // + .withType(org.talend.sdk.component.api.record.Schema.Type.RECORD) .withEntry(new SchemaImpl.EntryImpl.BuilderImpl() .withName("id") - .withType(Schema.Type.INT) + .withType(org.talend.sdk.component.api.record.Schema.Type.INT) .withNullable(false) .withErrorCapable(true) .build()) .withEntry(new SchemaImpl.EntryImpl.BuilderImpl() .withName("field_date") - .withType(Schema.Type.DATETIME) + .withType(org.talend.sdk.component.api.record.Schema.Type.DATETIME) .withNullable(false) .withErrorCapable(false) .withComment("field date") .build()) .withEntry(new SchemaImpl.EntryImpl.BuilderImpl() .withName("field_boolean") - .withType(Schema.Type.BOOLEAN) + .withType(org.talend.sdk.component.api.record.Schema.Type.BOOLEAN) .withNullable(true) .withComment("field boolean") .build()) .withEntry(new SchemaImpl.EntryImpl.BuilderImpl() .withName("field_bytes") - .withType(Schema.Type.BYTES) + .withType(org.talend.sdk.component.api.record.Schema.Type.BYTES) .withComment("field bytes") .withNullable(true) .build()) @@ -293,18 +466,21 @@ void testAllDataTypes1() { assertFalse(model.getEntries().get(0).isNullable()); assertTrue(model.getEntries().get(0).isErrorCapable()); assertEquals("field_date", model.getEntries().get(1).getName()); - assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.DATETIME, model.getEntries().get(1).getType()); + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.DATETIME, + model.getEntries().get(1).getType()); assertEquals("field date", model.getEntries().get(1).getComment()); assertFalse(model.getEntries().get(1).isNullable()); assertFalse(model.getEntries().get(1).isErrorCapable()); assertEquals("field_boolean", model.getEntries().get(2).getName()); - assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.BOOLEAN, model.getEntries().get(2).getType()); + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.BOOLEAN, + model.getEntries().get(2).getType()); assertEquals("field boolean", model.getEntries().get(2).getComment()); assertTrue(model.getEntries().get(2).isNullable()); assertFalse(model.getEntries().get(2).isErrorCapable()); assertEquals("field_bytes", model.getEntries().get(3).getName()); - assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.BYTES, model.getEntries().get(3).getType()); + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.BYTES, + model.getEntries().get(3).getType()); assertEquals("field bytes", model.getEntries().get(3).getComment()); assertTrue(model.getEntries().get(3).isNullable()); @@ -315,23 +491,23 @@ void testAllDataTypes1() { @Test void testAllDataTypes2() { // given - final Schema schema = new SchemaImpl.BuilderImpl() // - .withType(Schema.Type.RECORD) + final org.talend.sdk.component.api.record.Schema schema = new SchemaImpl.BuilderImpl() // + .withType(org.talend.sdk.component.api.record.Schema.Type.RECORD) .withEntry(new SchemaImpl.EntryImpl.BuilderImpl() .withName("field_decimal") - .withType(Schema.Type.DECIMAL) + .withType(org.talend.sdk.component.api.record.Schema.Type.DECIMAL) .withComment("field decimal") .withNullable(true) .build()) .withEntry(new SchemaImpl.EntryImpl.BuilderImpl() .withName("field_double") - .withType(Schema.Type.DOUBLE) + .withType(org.talend.sdk.component.api.record.Schema.Type.DOUBLE) .withComment("field double") .withNullable(true) .build()) .withEntry(new SchemaImpl.EntryImpl.BuilderImpl() .withName("field_float") - .withType(Schema.Type.FLOAT) + .withType(org.talend.sdk.component.api.record.Schema.Type.FLOAT) .withComment("field float") .withNullable(true) .build()) @@ -356,16 +532,19 @@ void testAllDataTypes2() { assertEquals(3, model.getEntries().size()); assertEquals("field_decimal", model.getEntries().get(0).getName()); - assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.DECIMAL, model.getEntries().get(0).getType()); + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.DECIMAL, + model.getEntries().get(0).getType()); assertEquals("field decimal", model.getEntries().get(0).getComment()); assertTrue(model.getEntries().get(0).isNullable()); assertEquals("field_double", model.getEntries().get(1).getName()); - assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.DOUBLE, model.getEntries().get(1).getType()); + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.DOUBLE, + model.getEntries().get(1).getType()); assertEquals("field double", model.getEntries().get(1).getComment()); assertTrue(model.getEntries().get(1).isNullable()); assertEquals("field_float", model.getEntries().get(2).getName()); - assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.FLOAT, model.getEntries().get(2).getType()); + assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.FLOAT, + model.getEntries().get(2).getType()); assertEquals("field float", model.getEntries().get(2).getComment()); assertTrue(model.getEntries().get(2).isNullable()); @@ -375,24 +554,24 @@ void testAllDataTypes2() { @Test void shouldParseArrayEntryElementSchema() { - Schema.Entry nameEntry = new SchemaImpl.EntryImpl.BuilderImpl() + org.talend.sdk.component.api.record.Schema.Entry nameEntry = new SchemaImpl.EntryImpl.BuilderImpl() .withName("name") .withNullable(true) - .withType(Schema.Type.STRING) + .withType(org.talend.sdk.component.api.record.Schema.Type.STRING) .build(); - Schema.Entry ageEntry = new SchemaImpl.EntryImpl.BuilderImpl() + org.talend.sdk.component.api.record.Schema.Entry ageEntry = new SchemaImpl.EntryImpl.BuilderImpl() .withName("age") .withNullable(true) - .withType(Schema.Type.INT) + .withType(org.talend.sdk.component.api.record.Schema.Type.INT) .build(); - Schema customerSchema = new SchemaImpl.BuilderImpl() // - .withType(Schema.Type.RECORD) + org.talend.sdk.component.api.record.Schema customerSchema = new SchemaImpl.BuilderImpl() // + .withType(org.talend.sdk.component.api.record.Schema.Type.RECORD) .withEntry(nameEntry) .withEntry(ageEntry) .build(); - final Schema schema = new SchemaImpl.BuilderImpl() // - .withType(Schema.Type.ARRAY) // + final org.talend.sdk.component.api.record.Schema schema = new SchemaImpl.BuilderImpl() // + .withType(org.talend.sdk.component.api.record.Schema.Type.ARRAY) // .withElementSchema(customerSchema) .withProp("namespace", "test") .build(); @@ -407,48 +586,49 @@ void shouldParseArrayEntryElementSchema() { assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.ARRAY, model.getType()); assertNotNull(innerSchema); assertEquals(org.talend.sdk.component.server.front.model.Schema.Type.RECORD, innerSchema.getType()); - //check entry name - final List entryNames = innerSchema.getEntries().stream().map(Entry::getName) + // check entry name + final List entryNames = innerSchema.getEntries() + .stream() + .map(Entry::getName) .toList(); Assertions.assertEquals(2, entryNames.size()); Assertions.assertTrue(entryNames.contains("name")); Assertions.assertTrue(entryNames.contains("age")); - //check entry type + // check entry type final List entryTypes = innerSchema.getEntries().stream().map(Entry::getType).toList(); Assertions.assertTrue(entryTypes.contains(org.talend.sdk.component.server.front.model.Schema.Type.INT)); Assertions.assertTrue(entryTypes.contains(org.talend.sdk.component.server.front.model.Schema.Type.STRING)); } - @Test void shouldMarkMetadataEntries() { - Schema.Entry meta1 = new SchemaImpl.EntryImpl.BuilderImpl() // + org.talend.sdk.component.api.record.Schema.Entry meta1 = new SchemaImpl.EntryImpl.BuilderImpl() // .withName("meta1") // - .withType(Schema.Type.INT) // + .withType(org.talend.sdk.component.api.record.Schema.Type.INT) // .withMetadata(true) // .build(); - Schema.Entry meta2 = new SchemaImpl.EntryImpl.BuilderImpl() // + org.talend.sdk.component.api.record.Schema.Entry meta2 = new SchemaImpl.EntryImpl.BuilderImpl() // .withName("meta2") // - .withType(Schema.Type.STRING) // + .withType(org.talend.sdk.component.api.record.Schema.Type.STRING) // .withMetadata(true) // .withNullable(true) // .build(); - Schema.Entry data1 = new SchemaImpl.EntryImpl.BuilderImpl() // + org.talend.sdk.component.api.record.Schema.Entry data1 = new SchemaImpl.EntryImpl.BuilderImpl() // .withName("data1") // - .withType(Schema.Type.INT) // + .withType(org.talend.sdk.component.api.record.Schema.Type.INT) // .build(); - Schema.Entry data2 = new SchemaImpl.EntryImpl.BuilderImpl() // + org.talend.sdk.component.api.record.Schema.Entry data2 = new SchemaImpl.EntryImpl.BuilderImpl() // .withName("data2") // - .withType(Schema.Type.STRING) // + .withType(org.talend.sdk.component.api.record.Schema.Type.STRING) // .withNullable(true) // .build(); - Schema schema = new SchemaImpl.BuilderImpl() // - .withType(Schema.Type.RECORD) // + org.talend.sdk.component.api.record.Schema schema = new SchemaImpl.BuilderImpl() // + .withType(org.talend.sdk.component.api.record.Schema.Type.RECORD) // .withEntry(data1) // .withEntry(meta1) // .withEntry(data2) // @@ -460,33 +640,41 @@ void shouldMarkMetadataEntries() { org.talend.sdk.component.server.front.model.Schema model = jsonb.fromJson(new StringReader(json), org.talend.sdk.component.server.front.model.Schema.class); - Entry metaEntry = model.getMetadata().get(0); + org.talend.sdk.component.server.front.model.Entry metaEntry = model.getMetadata().get(0); - //check entry name - final List entryNames = model.getEntries().stream().map(Entry::getName) + // check entry name + final List entryNames = model.getEntries() + .stream() + .map(Entry::getName) .toList(); Assertions.assertEquals(2, entryNames.size()); Assertions.assertTrue(entryNames.contains(data1.getName())); Assertions.assertTrue(entryNames.contains(data2.getName())); Assertions.assertEquals(4, schema.getAllEntries().count()); - //check entry type - final List entryTypes = model.getEntries().stream().map(Entry::getType) + // check entry type + final List entryTypes = model.getEntries() + .stream() + .map(Entry::getType) .map(org.talend.sdk.component.server.front.model.Schema.Type::name) .toList(); Assertions.assertEquals(2, entryTypes.size()); Assertions.assertTrue(entryTypes.contains(data1.getType().name())); Assertions.assertTrue(entryTypes.contains(data2.getType().name())); - //check meta name - final List metaEntryNames = model.getMetadata().stream().map(Entry::getName) + // check meta name + final List metaEntryNames = model.getMetadata() + .stream() + .map(Entry::getName) .toList(); Assertions.assertEquals(2, metaEntryNames.size()); Assertions.assertTrue(metaEntryNames.contains(meta1.getName())); Assertions.assertTrue(metaEntryNames.contains(meta2.getName())); - //check meta type - final List metaEntryTypes = model.getMetadata().stream().map(Entry::getType) + // check meta type + final List metaEntryTypes = model.getMetadata() + .stream() + .map(Entry::getType) .map(org.talend.sdk.component.server.front.model.Schema.Type::name) .toList(); Assertions.assertEquals(2, metaEntryTypes.size()); @@ -496,56 +684,4 @@ void shouldMarkMetadataEntries() { assertTrue(metaEntry.isMetadata()); assertEquals("meta1", metaEntry.getName()); } - - @Test - void shouldParseEntryProps() { - Schema schema = new SchemaImpl.BuilderImpl() // - .withType(Schema.Type.RECORD) - //.type(Schema.Type.RECORD) - .withEntry(new SchemaImpl.EntryImpl.BuilderImpl() - .withName("field") - .withType(Schema.Type.STRING) - .withProp("format", "email") - .build()) - .build(); - - String json = jsonb.toJson(schema); - JsonSchemaModel model = new JsonSchemaModel(json); - - JsonEntryModel entry = model.getEntries().get(0); - - assertEquals("email", entry.getProps().get("format")); - } - - @Test - void shouldFailForInvalidEntryType() { - String invalidJson = """ - { - "type": "RECORD", - "entries": [ - { "name": "x", "type": "INVALID" } - ] - } - """; - - assertThrows(IllegalArgumentException.class, - () -> new JsonSchemaModel(invalidJson)); - } - - - @Test - void shouldFailForInvalidSchemaType() { - String invalidJson = """ - { - "type": "NOT_VALID", - "entries": [] - } - """; - - assertThrows(IllegalArgumentException.class, - () -> new JsonSchemaModel(invalidJson)); - } - -} - - +} \ No newline at end of file From 70f4684cf48b3719eb7df057bc753d5a63aa9f22 Mon Sep 17 00:00:00 2001 From: Yves Piel Date: Tue, 13 Jan 2026 15:30:35 +0100 Subject: [PATCH 7/8] fix(QTDI-2215): Improve unit test. --- .../component/server/front/model/Entry.java | 10 +- .../component/server/front/SchemaTest.java | 622 +++++++++++++----- 2 files changed, 458 insertions(+), 174 deletions(-) diff --git a/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Entry.java b/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Entry.java index 813f816d6f48b..06110b648f113 100644 --- a/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Entry.java +++ b/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Entry.java @@ -80,8 +80,16 @@ private Object getInternalDefaultValue() { return defaultValue; } + @SuppressWarnings("unchecked") public T getDefaultValue() { - return (T) this.getInternalDefaultValue(); + return switch (this.getType()) { + case INT -> (T) ((Integer) ((Number) this.getInternalDefaultValue()).intValue()); + case LONG -> (T) ((Long) ((Number) this.getInternalDefaultValue()).longValue()); + case FLOAT -> (T) ((Float) ((Number) this.getInternalDefaultValue()).floatValue()); + case DOUBLE -> (T) ((Double) ((Number) this.getInternalDefaultValue()).doubleValue()); + default -> (T) this.getInternalDefaultValue(); + }; + } public String getOriginalFieldName() { diff --git a/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/SchemaTest.java b/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/SchemaTest.java index d2f9cf148e005..65eeea0feeab9 100644 --- a/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/SchemaTest.java +++ b/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/SchemaTest.java @@ -34,6 +34,8 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.talend.sdk.component.api.record.SchemaProperty; +import org.talend.sdk.component.api.record.SchemaProperty.LogicalType; import org.talend.sdk.component.runtime.record.RecordBuilderFactoryImpl; import org.talend.sdk.component.runtime.record.SchemaImpl; import org.talend.sdk.component.server.front.model.Entry; @@ -43,178 +45,6 @@ class SchemaTest { private final Jsonb jsonb = JsonbBuilder.create(); - @Test - void deserializeSchemaFromJson() throws Exception { - RecordBuilderFactoryImpl factory = new RecordBuilderFactoryImpl("test"); - org.talend.sdk.component.api.record.Schema schemaImpl = - factory.newSchemaBuilder(org.talend.sdk.component.api.record.Schema.Type.RECORD) - .withEntry(factory.newEntryBuilder() - .withName("id") - .withType(org.talend.sdk.component.api.record.Schema.Type.LONG) - .withNullable(false) - .withComment("User ID") - .build()) - .withEntry(factory.newEntryBuilder() - .withName("username") - .withType(org.talend.sdk.component.api.record.Schema.Type.STRING) - .withNullable(false) - .withComment("Username") - .build()) - .withEntry(factory.newEntryBuilder() - .withName("email") - .withType(org.talend.sdk.component.api.record.Schema.Type.STRING) - .withNullable(false) - .withComment("Email address") - .build()) - .withEntry(factory.newEntryBuilder() - .withName("passwordHash") - .withType(org.talend.sdk.component.api.record.Schema.Type.BYTES) - .withNullable(false) - .withComment("Password hash") - .build()) - .withEntry(factory.newEntryBuilder() - .withName("createdAt") - .withType(org.talend.sdk.component.api.record.Schema.Type.DATETIME) - .withNullable(false) - .withComment("Account creation date") - .build()) - .withEntry(factory.newEntryBuilder() - .withName("lastLogin") - .withType(org.talend.sdk.component.api.record.Schema.Type.DATETIME) - .withNullable(true) - .withComment("Last login date") - .build()) - .withEntry(factory.newEntryBuilder() - .withName("isActive") - .withType(org.talend.sdk.component.api.record.Schema.Type.BOOLEAN) - .withNullable(false) - .withDefaultValue(true) - .withComment("Is active user") - .build()) - .withEntry(factory.newEntryBuilder() - .withName("roles") - .withType(org.talend.sdk.component.api.record.Schema.Type.ARRAY) - .withElementSchema( - factory.newSchemaBuilder(org.talend.sdk.component.api.record.Schema.Type.STRING) - .build()) - .withNullable(true) - .withComment("User roles") - .build()) - .withEntry(factory.newEntryBuilder() - .withName("profile") - .withType(org.talend.sdk.component.api.record.Schema.Type.RECORD) - .withElementSchema( - factory.newSchemaBuilder(org.talend.sdk.component.api.record.Schema.Type.RECORD) - .withEntry(factory.newEntryBuilder() - .withName("firstName") - .withType( - org.talend.sdk.component.api.record.Schema.Type.STRING) - .withNullable(true) - .build()) - .withEntry(factory.newEntryBuilder() - .withName("lastName") - .withType( - org.talend.sdk.component.api.record.Schema.Type.STRING) - .withNullable(true) - .build()) - .withEntry(factory.newEntryBuilder() - .withName("birthDate") - .withType( - org.talend.sdk.component.api.record.Schema.Type.DATETIME) - .withNullable(true) - .build()) - .build()) - .withNullable(true) - .withComment("User profile") - .build()) - .build(); - - String json = jsonb.toJson(schemaImpl); - - ObjectMapper mapper = new ObjectMapper(); - Schema schema = mapper.readValue(json, Schema.class); - - assertNotNull(schema); - assertEquals(Schema.Type.RECORD, schema.getType()); - assertNotNull(schema.getEntries()); - assertEquals(9, schema.getEntries().size()); - - Entry idEntry = schema.getEntries().get(0); - assertEquals("id", idEntry.getName()); - assertEquals(Schema.Type.LONG, idEntry.getType()); - assertFalse(idEntry.isNullable()); - assertEquals("User ID", idEntry.getComment()); - - Entry usernameEntry = schema.getEntries().get(1); - assertEquals("username", usernameEntry.getName()); - assertEquals(Schema.Type.STRING, usernameEntry.getType()); - assertFalse(usernameEntry.isNullable()); - assertEquals("Username", usernameEntry.getComment()); - - Entry emailEntry = schema.getEntries().get(2); - assertEquals("email", emailEntry.getName()); - assertEquals(Schema.Type.STRING, emailEntry.getType()); - assertFalse(emailEntry.isNullable()); - assertEquals("Email address", emailEntry.getComment()); - - Entry passwordHashEntry = schema.getEntries().get(3); - assertEquals("passwordHash", passwordHashEntry.getName()); - assertEquals(Schema.Type.BYTES, passwordHashEntry.getType()); - assertFalse(passwordHashEntry.isNullable()); - assertEquals("Password hash", passwordHashEntry.getComment()); - - Entry createdAtEntry = schema.getEntries().get(4); - assertEquals("createdAt", createdAtEntry.getName()); - assertEquals(Schema.Type.DATETIME, createdAtEntry.getType()); - assertFalse(createdAtEntry.isNullable()); - assertEquals("Account creation date", createdAtEntry.getComment()); - - Entry lastLoginEntry = schema.getEntries().get(5); - assertEquals("lastLogin", lastLoginEntry.getName()); - assertEquals(Schema.Type.DATETIME, lastLoginEntry.getType()); - assertTrue(lastLoginEntry.isNullable()); - assertEquals("Last login date", lastLoginEntry.getComment()); - - Entry isActiveEntry = schema.getEntries().get(6); - assertEquals("isActive", isActiveEntry.getName()); - assertEquals(Schema.Type.BOOLEAN, isActiveEntry.getType()); - assertFalse(isActiveEntry.isNullable()); - assertEquals("Is active user", isActiveEntry.getComment()); - assertEquals(true, isActiveEntry.getDefaultValue()); - - Entry rolesEntry = schema.getEntries().get(7); - assertEquals("roles", rolesEntry.getName()); - assertEquals(Schema.Type.ARRAY, rolesEntry.getType()); - assertTrue(rolesEntry.isNullable()); - assertEquals("User roles", rolesEntry.getComment()); - assertNotNull(rolesEntry.getElementSchema()); - assertEquals(Schema.Type.STRING, rolesEntry.getElementSchema().getType()); - - Entry profileEntry = schema.getEntries().get(8); - assertEquals("profile", profileEntry.getName()); - assertEquals(Schema.Type.RECORD, profileEntry.getType()); - assertTrue(profileEntry.isNullable()); - assertEquals("User profile", profileEntry.getComment()); - assertNotNull(profileEntry.getElementSchema()); - assertEquals(Schema.Type.RECORD, profileEntry.getElementSchema().getType()); - assertEquals(3, profileEntry.getElementSchema().getEntries().size()); - - Entry firstNameEntry = profileEntry.getElementSchema().getEntries().get(0); - assertEquals("firstName", firstNameEntry.getName()); - assertEquals(Schema.Type.STRING, firstNameEntry.getType()); - assertTrue(firstNameEntry.isNullable()); - - Entry lastNameEntry = profileEntry.getElementSchema().getEntries().get(1); - assertEquals("lastName", lastNameEntry.getName()); - assertEquals(Schema.Type.STRING, lastNameEntry.getType()); - assertTrue(lastNameEntry.isNullable()); - - Entry birthDateEntry = profileEntry.getElementSchema().getEntries().get(2); - assertEquals("birthDate", birthDateEntry.getName()); - assertEquals(Schema.Type.DATETIME, birthDateEntry.getType()); - assertTrue(birthDateEntry.isNullable()); - } - @Test void shouldSerializeSchemaImplAndDeserializeToJsonSchemaModel() { // given @@ -584,7 +414,6 @@ void shouldParseArrayEntryElementSchema() { Assertions.assertTrue(entryTypes.contains(org.talend.sdk.component.server.front.model.Schema.Type.STRING)); } - @Test void shouldMarkMetadataEntries() { org.talend.sdk.component.api.record.Schema.Entry meta1 = new SchemaImpl.EntryImpl.BuilderImpl() // @@ -667,4 +496,451 @@ void shouldMarkMetadataEntries() { assertTrue(metaEntry.isMetadata()); assertEquals("meta1", metaEntry.getName()); } + + @Test + void deserializeCompleteSchemaWithAllTypesAndParameters() throws Exception { + RecordBuilderFactoryImpl factory = new RecordBuilderFactoryImpl("test"); + + // Build a comprehensive schema with all supported types and all field parameters + org.talend.sdk.component.api.record.Schema schemaImpl = + factory.newSchemaBuilder(org.talend.sdk.component.api.record.Schema.Type.RECORD) + .withProp("namespace", "com.talend.test") + .withProp("doc", "Comprehensive schema test") + .withProp("customProp1", "value1") + .withProp("customProp2", "value2") + // STRING type with various parameters + .withEntry(factory.newEntryBuilder() + .withName("stringField") + .withRawName("string_field") + .withType(org.talend.sdk.component.api.record.Schema.Type.STRING) + .withNullable(false) + .withDefaultValue("default string") + .withComment("String field with default value") + .withProp(SchemaProperty.SIZE, "255") + .withProp(SchemaProperty.PATTERN, "[a-zA-Z0-9]+") + .withProp(SchemaProperty.IS_KEY, "true") + .build()) + // INT type + .withEntry(factory.newEntryBuilder() + .withName("intField") + .withRawName("int_field") + .withType(org.talend.sdk.component.api.record.Schema.Type.INT) + .withNullable(true) + .withDefaultValue(42) + .withComment("Integer field with default") + .withProp(SchemaProperty.ORIGIN_TYPE, "integer") + .build()) + // LONG type with error handling + .withEntry(factory.newEntryBuilder() + .withName("longField") + .withRawName("long_field") + .withType(org.talend.sdk.component.api.record.Schema.Type.LONG) + .withNullable(false) + .withErrorCapable(true) + .withComment("Long field with error capability") + .withProp(SchemaProperty.IS_UNIQUE, "true") + .build()) + // FLOAT type + .withEntry(factory.newEntryBuilder() + .withName("floatField") + .withRawName("float_field") + .withType(org.talend.sdk.component.api.record.Schema.Type.FLOAT) + .withNullable(true) + .withDefaultValue(3.14f) + .withComment("Float field") + .withProp(SchemaProperty.SCALE, "2") + .build()) + // DOUBLE type + .withEntry(factory.newEntryBuilder() + .withName("doubleField") + .withRawName("double_field") + .withType(org.talend.sdk.component.api.record.Schema.Type.DOUBLE) + .withNullable(false) + .withDefaultValue(2.718281828) + .withComment("Double field with high precision") + .withProp(SchemaProperty.SCALE, "9") + .build()) + // BOOLEAN type + .withEntry(factory.newEntryBuilder() + .withName("booleanField") + .withRawName("boolean_field") + .withType(org.talend.sdk.component.api.record.Schema.Type.BOOLEAN) + .withNullable(true) + .withDefaultValue(true) + .withComment("Boolean field") + .build()) + // BYTES type + .withEntry(factory.newEntryBuilder() + .withName("bytesField") + .withRawName("bytes_field") + .withType(org.talend.sdk.component.api.record.Schema.Type.BYTES) + .withNullable(true) + .withComment("Bytes field for binary data") + .withProp(SchemaProperty.SIZE, "1024") + .build()) + // DECIMAL type + .withEntry(factory.newEntryBuilder() + .withName("decimalField") + .withRawName("decimal_field") + .withType(org.talend.sdk.component.api.record.Schema.Type.DECIMAL) + .withNullable(false) + .withComment("Decimal field for precise calculations") + .withProp(SchemaProperty.SIZE, "10") + .withProp(SchemaProperty.SCALE, "2") + .build()) + // DATETIME type with DATE logical type + .withEntry(factory.newEntryBuilder() + .withName("dateField") + .withRawName("date_field") + .withType(org.talend.sdk.component.api.record.Schema.Type.DATETIME) + .withLogicalType(LogicalType.DATE) + .withNullable(true) + .withComment("Date field with DATE logical type") + .withProp(SchemaProperty.PATTERN, "yyyy-MM-dd") + .build()) + // DATETIME type with TIME logical type + .withEntry(factory.newEntryBuilder() + .withName("timeField") + .withRawName("time_field") + .withType(org.talend.sdk.component.api.record.Schema.Type.DATETIME) + .withLogicalType(LogicalType.TIME) + .withNullable(true) + .withComment("Time field with TIME logical type") + .withProp(SchemaProperty.PATTERN, "HH:mm:ss") + .build()) + // DATETIME type with TIMESTAMP logical type + .withEntry(factory.newEntryBuilder() + .withName("timestampField") + .withRawName("timestamp_field") + .withType(org.talend.sdk.component.api.record.Schema.Type.DATETIME) + .withLogicalType(LogicalType.TIMESTAMP) + .withNullable(false) + .withComment("Timestamp field") + .withProp(SchemaProperty.PATTERN, "yyyy-MM-dd'T'HH:mm:ss.SSSZ") + .build()) + // STRING type with UUID logical type + .withEntry(factory.newEntryBuilder() + .withName("uuidField") + .withRawName("uuid_field") + .withType(org.talend.sdk.component.api.record.Schema.Type.STRING) + .withLogicalType(LogicalType.UUID) + .withNullable(true) + .withComment("UUID field") + .withProp(SchemaProperty.IS_FOREIGN_KEY, "true") + .build()) + // ARRAY type with primitive element schema + .withEntry(factory.newEntryBuilder() + .withName("stringArrayField") + .withRawName("string_array_field") + .withType(org.talend.sdk.component.api.record.Schema.Type.ARRAY) + .withElementSchema( + factory.newSchemaBuilder(org.talend.sdk.component.api.record.Schema.Type.STRING) + .build()) + .withNullable(true) + .withComment("Array of strings") + .build()) + // ARRAY type with complex element schema + .withEntry(factory.newEntryBuilder() + .withName("intArrayField") + .withRawName("int_array_field") + .withType(org.talend.sdk.component.api.record.Schema.Type.ARRAY) + .withElementSchema( + factory.newSchemaBuilder(org.talend.sdk.component.api.record.Schema.Type.INT) + .build()) + .withNullable(false) + .withComment("Array of integers") + .build()) + // RECORD type (nested record) + .withEntry(factory.newEntryBuilder() + .withName("nestedRecord") + .withRawName("nested_record") + .withType(org.talend.sdk.component.api.record.Schema.Type.RECORD) + .withElementSchema( + factory.newSchemaBuilder(org.talend.sdk.component.api.record.Schema.Type.RECORD) + .withProp("nestedNamespace", "com.talend.nested") + .withEntry(factory.newEntryBuilder() + .withName("nestedString") + .withRawName("nested_string") + .withType( + org.talend.sdk.component.api.record.Schema.Type.STRING) + .withNullable(true) + .withComment("Nested string field") + .build()) + .withEntry(factory.newEntryBuilder() + .withName("nestedInt") + .withRawName("nested_int") + .withType(org.talend.sdk.component.api.record.Schema.Type.INT) + .withNullable(false) + .withDefaultValue(100) + .withComment("Nested int field") + .build()) + .withEntry(factory.newEntryBuilder() + .withName("nestedDate") + .withRawName("nested_date") + .withType( + org.talend.sdk.component.api.record.Schema.Type.DATETIME) + .withLogicalType(LogicalType.DATE) + .withNullable(true) + .withComment("Nested date field") + .build()) + .build()) + .withNullable(true) + .withComment("Nested record structure") + .build()) + // ARRAY of RECORD type + .withEntry(factory.newEntryBuilder() + .withName("arrayOfRecordsField") + .withRawName("array_of_records_field") + .withType(org.talend.sdk.component.api.record.Schema.Type.ARRAY) + .withElementSchema( + factory.newSchemaBuilder(org.talend.sdk.component.api.record.Schema.Type.RECORD) + .withEntry(factory.newEntryBuilder() + .withName("recordInArrayString") + .withType( + org.talend.sdk.component.api.record.Schema.Type.STRING) + .withNullable(true) + .build()) + .withEntry(factory.newEntryBuilder() + .withName("recordInArrayLong") + .withType(org.talend.sdk.component.api.record.Schema.Type.LONG) + .withNullable(false) + .build()) + .build()) + .withNullable(true) + .withComment("Array of nested records") + .build()) + // Metadata entry + .withEntry(factory.newEntryBuilder() + .withName("metadataSource") + .withRawName("metadata_source") + .withType(org.talend.sdk.component.api.record.Schema.Type.STRING) + .withMetadata(true) + .withNullable(true) + .withComment("Metadata field indicating source") + .withProp("metaProp1", "metaValue1") + .build()) + .build(); + + // Serialize the schema to JSON + String json = jsonb.toJson(schemaImpl); + + // Deserialize using ObjectMapper (Jackson) + ObjectMapper mapper = new ObjectMapper(); + Schema schema = mapper.readValue(json, Schema.class); + + // ===== Validate top-level schema properties ===== + assertNotNull(schema); + assertEquals(Schema.Type.RECORD, schema.getType()); + assertNotNull(schema.getEntries()); + assertNotNull(schema.getProps()); + assertEquals("com.talend.test", schema.getProp("namespace")); + assertEquals("Comprehensive schema test", schema.getProp("doc")); + assertEquals("value1", schema.getProp("customProp1")); + assertEquals("value2", schema.getProp("customProp2")); + + // We have 17 data entries + 1 metadata entry = 18 total + // But entries list only contains data entries + assertEquals(schemaImpl.getEntries().size(), schema.getEntries().size()); + assertEquals(schemaImpl.getMetadata().size(), schema.getMetadata().size()); + + // ===== Validate STRING field ===== + Entry stringField = schema.getEntries().get(0); + assertEquals("stringField", stringField.getName()); + assertEquals("string_field", stringField.getRawName()); + assertEquals(Schema.Type.STRING, stringField.getType()); + assertFalse(stringField.isNullable()); + assertEquals("default string", stringField.getDefaultValue()); + assertEquals("String field with default value", stringField.getComment()); + assertEquals("255", stringField.getProp(SchemaProperty.SIZE)); + assertEquals("[a-zA-Z0-9]+", stringField.getProp(SchemaProperty.PATTERN)); + assertEquals("true", stringField.getProp(SchemaProperty.IS_KEY)); + assertFalse(stringField.isMetadata()); + + // ===== Validate INT field ===== + Entry intField = schema.getEntries().get(1); + assertEquals("intField", intField.getName()); + assertEquals("int_field", intField.getRawName()); + assertEquals(Schema.Type.INT, intField.getType()); + assertTrue(intField.isNullable()); + assertEquals(42, intField. getDefaultValue()); + assertEquals("Integer field with default", intField.getComment()); + assertEquals("integer", intField.getProp(SchemaProperty.ORIGIN_TYPE)); + + // ===== Validate LONG field with error capability ===== + Entry longField = schema.getEntries().get(2); + assertEquals("longField", longField.getName()); + assertEquals("long_field", longField.getRawName()); + assertEquals(Schema.Type.LONG, longField.getType()); + assertFalse(longField.isNullable()); + assertTrue(longField.isErrorCapable()); + assertEquals("Long field with error capability", longField.getComment()); + assertEquals("true", longField.getProp(SchemaProperty.IS_UNIQUE)); + + // ===== Validate FLOAT field ===== + Entry floatField = schema.getEntries().get(3); + assertEquals("floatField", floatField.getName()); + assertEquals("float_field", floatField.getRawName()); + assertEquals(Schema.Type.FLOAT, floatField.getType()); + assertTrue(floatField.isNullable()); + assertEquals(3.14f, floatField. getDefaultValue()); + assertEquals("Float field", floatField.getComment()); + assertEquals("2", floatField.getProp(SchemaProperty.SCALE)); + + // ===== Validate DOUBLE field ===== + Entry doubleField = schema.getEntries().get(4); + assertEquals("doubleField", doubleField.getName()); + assertEquals("double_field", doubleField.getRawName()); + assertEquals(Schema.Type.DOUBLE, doubleField.getType()); + assertFalse(doubleField.isNullable()); + assertEquals(2.718281828, doubleField.getDefaultValue()); + assertEquals("Double field with high precision", doubleField.getComment()); + assertEquals("9", doubleField.getProp(SchemaProperty.SCALE)); + + // ===== Validate BOOLEAN field ===== + Entry booleanField = schema.getEntries().get(5); + assertEquals("booleanField", booleanField.getName()); + assertEquals("boolean_field", booleanField.getRawName()); + assertEquals(Schema.Type.BOOLEAN, booleanField.getType()); + assertTrue(booleanField.isNullable()); + assertEquals(true, booleanField.getDefaultValue()); + assertEquals("Boolean field", booleanField.getComment()); + + // ===== Validate BYTES field ===== + Entry bytesField = schema.getEntries().get(6); + assertEquals("bytesField", bytesField.getName()); + assertEquals("bytes_field", bytesField.getRawName()); + assertEquals(Schema.Type.BYTES, bytesField.getType()); + assertTrue(bytesField.isNullable()); + assertEquals("Bytes field for binary data", bytesField.getComment()); + assertEquals("1024", bytesField.getProp(SchemaProperty.SIZE)); + + // ===== Validate DECIMAL field ===== + Entry decimalField = schema.getEntries().get(7); + assertEquals("decimalField", decimalField.getName()); + assertEquals("decimal_field", decimalField.getRawName()); + assertEquals(Schema.Type.DECIMAL, decimalField.getType()); + assertFalse(decimalField.isNullable()); + assertEquals("Decimal field for precise calculations", decimalField.getComment()); + assertEquals("10", decimalField.getProp(SchemaProperty.SIZE)); + assertEquals("2", decimalField.getProp(SchemaProperty.SCALE)); + + // ===== Validate DATE field (DATETIME with DATE logical type) ===== + Entry dateField = schema.getEntries().get(8); + assertEquals("dateField", dateField.getName()); + assertEquals("date_field", dateField.getRawName()); + assertEquals(Schema.Type.DATETIME, dateField.getType()); + assertEquals(LogicalType.DATE.key(), dateField.getProp(SchemaProperty.LOGICAL_TYPE)); + assertTrue(dateField.isNullable()); + assertEquals("Date field with DATE logical type", dateField.getComment()); + assertEquals("yyyy-MM-dd", dateField.getProp(SchemaProperty.PATTERN)); + + // ===== Validate TIME field (DATETIME with TIME logical type) ===== + Entry timeField = schema.getEntries().get(9); + assertEquals("timeField", timeField.getName()); + assertEquals("time_field", timeField.getRawName()); + assertEquals(Schema.Type.DATETIME, timeField.getType()); + assertEquals(LogicalType.TIME.key(), timeField.getProp(SchemaProperty.LOGICAL_TYPE)); + assertTrue(timeField.isNullable()); + assertEquals("Time field with TIME logical type", timeField.getComment()); + assertEquals("HH:mm:ss", timeField.getProp(SchemaProperty.PATTERN)); + + // ===== Validate TIMESTAMP field (DATETIME with TIMESTAMP logical type) ===== + Entry timestampField = schema.getEntries().get(10); + assertEquals("timestampField", timestampField.getName()); + assertEquals("timestamp_field", timestampField.getRawName()); + assertEquals(Schema.Type.DATETIME, timestampField.getType()); + assertEquals(LogicalType.TIMESTAMP.key(), timestampField.getProp(SchemaProperty.LOGICAL_TYPE)); + assertFalse(timestampField.isNullable()); + assertEquals("Timestamp field", timestampField.getComment()); + assertEquals("yyyy-MM-dd'T'HH:mm:ss.SSSZ", timestampField.getProp(SchemaProperty.PATTERN)); + + // ===== Validate UUID field (STRING with UUID logical type) ===== + Entry uuidField = schema.getEntries().get(11); + assertEquals("uuidField", uuidField.getName()); + assertEquals("uuid_field", uuidField.getRawName()); + assertEquals(Schema.Type.STRING, uuidField.getType()); + assertEquals(LogicalType.UUID.key(), uuidField.getProp(SchemaProperty.LOGICAL_TYPE)); + assertTrue(uuidField.isNullable()); + assertEquals("UUID field", uuidField.getComment()); + assertEquals("true", uuidField.getProp(SchemaProperty.IS_FOREIGN_KEY)); + + // ===== Validate ARRAY field with STRING elements ===== + Entry stringArrayField = schema.getEntries().get(12); + assertEquals("stringArrayField", stringArrayField.getName()); + assertEquals("string_array_field", stringArrayField.getRawName()); + assertEquals(Schema.Type.ARRAY, stringArrayField.getType()); + assertTrue(stringArrayField.isNullable()); + assertEquals("Array of strings", stringArrayField.getComment()); + assertNotNull(stringArrayField.getElementSchema()); + assertEquals(Schema.Type.STRING, stringArrayField.getElementSchema().getType()); + + // ===== Validate ARRAY field with INT elements ===== + Entry intArrayField = schema.getEntries().get(13); + assertEquals("intArrayField", intArrayField.getName()); + assertEquals("int_array_field", intArrayField.getRawName()); + assertEquals(Schema.Type.ARRAY, intArrayField.getType()); + assertFalse(intArrayField.isNullable()); + assertEquals("Array of integers", intArrayField.getComment()); + assertNotNull(intArrayField.getElementSchema()); + assertEquals(Schema.Type.INT, intArrayField.getElementSchema().getType()); + + // ===== Validate nested RECORD field ===== + Entry nestedRecord = schema.getEntries().get(14); + assertEquals("nestedRecord", nestedRecord.getName()); + assertEquals("nested_record", nestedRecord.getRawName()); + assertEquals(Schema.Type.RECORD, nestedRecord.getType()); + assertTrue(nestedRecord.isNullable()); + assertEquals("Nested record structure", nestedRecord.getComment()); + assertNotNull(nestedRecord.getElementSchema()); + assertEquals(Schema.Type.RECORD, nestedRecord.getElementSchema().getType()); + + // Validate nested record properties + Schema nestedSchema = nestedRecord.getElementSchema(); + assertEquals("com.talend.nested", nestedSchema.getProp("nestedNamespace")); + assertEquals(3, nestedSchema.getEntries().size()); + + // Validate nested string field + Entry nestedString = nestedSchema.getEntries().get(0); + assertEquals("nestedString", nestedString.getName()); + assertEquals("nested_string", nestedString.getRawName()); + assertEquals(Schema.Type.STRING, nestedString.getType()); + assertTrue(nestedString.isNullable()); + assertEquals("Nested string field", nestedString.getComment()); + + // Validate nested int field + Entry nestedInt = nestedSchema.getEntries().get(1); + assertEquals("nestedInt", nestedInt.getName()); + assertEquals("nested_int", nestedInt.getRawName()); + assertEquals(Schema.Type.INT, nestedInt.getType()); + assertFalse(nestedInt.isNullable()); + assertEquals(100, nestedInt. getDefaultValue()); + assertEquals("Nested int field", nestedInt.getComment()); + + // Validate nested date field + Entry nestedDate = nestedSchema.getEntries().get(2); + assertEquals("nestedDate", nestedDate.getName()); + assertEquals("nested_date", nestedDate.getRawName()); + assertEquals(Schema.Type.DATETIME, nestedDate.getType()); + assertEquals(LogicalType.DATE.key(), nestedDate.getProp(SchemaProperty.LOGICAL_TYPE)); + assertTrue(nestedDate.isNullable()); + assertEquals("Nested date field", nestedDate.getComment()); + + // ===== Validate ARRAY type with nested RECORD ===== + Entry arrayOfRecordsField = schema.getEntries().get(15); + assertEquals("arrayOfRecordsField", arrayOfRecordsField.getName()); + assertEquals(Schema.Type.ARRAY, arrayOfRecordsField.getType()); + assertNotNull(arrayOfRecordsField.getElementSchema()); + assertEquals(Schema.Type.RECORD, arrayOfRecordsField.getElementSchema().getType()); + + // ===== Validate metadata entry ===== + assertEquals(1, schema.getMetadata().size()); + Entry metadataEntry = schema.getMetadata().get(0); + assertEquals("metadataSource", metadataEntry.getName()); + assertEquals("metadata_source", metadataEntry.getRawName()); + assertEquals(Schema.Type.STRING, metadataEntry.getType()); + assertTrue(metadataEntry.isMetadata()); + assertTrue(metadataEntry.isNullable()); + assertEquals("Metadata field indicating source", metadataEntry.getComment()); + assertEquals("metaValue1", metadataEntry.getProp("metaProp1")); + } } \ No newline at end of file From 76e0ec3d2b7c7edda8d4345b1c01b81609814964 Mon Sep 17 00:00:00 2001 From: yyin-talend Date: Thu, 15 Jan 2026 16:42:26 +0800 Subject: [PATCH 8/8] Add check for defaultvalue --- .../sdk/component/server/front/model/Entry.java | 4 ++++ .../sdk/component/server/front/EntryTest.java | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Entry.java b/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Entry.java index 06110b648f113..0839b41acead1 100644 --- a/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Entry.java +++ b/component-server-parent/component-server-model/src/main/java/org/talend/sdk/component/server/front/model/Entry.java @@ -82,6 +82,10 @@ private Object getInternalDefaultValue() { @SuppressWarnings("unchecked") public T getDefaultValue() { + if (defaultValue == null) { + return null; + } + return switch (this.getType()) { case INT -> (T) ((Integer) ((Number) this.getInternalDefaultValue()).intValue()); case LONG -> (T) ((Long) ((Number) this.getInternalDefaultValue()).longValue()); diff --git a/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/EntryTest.java b/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/EntryTest.java index 1b7cfe60b85b8..4cf8538a39862 100644 --- a/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/EntryTest.java +++ b/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/front/EntryTest.java @@ -47,6 +47,13 @@ private Entry createValidEntry() { true, null, "comment", props, "default"); } + private Entry createEmptyEntry() { + Map props = new LinkedHashMap<>(0); + props.put("p1", "v1"); + return new Entry("name", null, Schema.Type.STRING, true, false, true, + true, null, null, props, null); + } + // ---------------------------------------------------------------------- // Builder // ---------------------------------------------------------------------- @@ -80,6 +87,14 @@ void getDefaultValueIsTyped() { assertEquals("default", value); } + @Test + void getDefaultValueEmpty() { + Entry entry = createEmptyEntry(); + + String value = entry.getDefaultValue(); + assertNull(value); + } + @Test void getPropReturnsProperty() { Entry entry = createValidEntry();