diff --git a/jnosql-mapping/jnosql-mapping-semistructured/.gitignore b/jnosql-mapping/jnosql-mapping-semistructured/.gitignore new file mode 100644 index 000000000..defb1dc0b --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/.gitignore @@ -0,0 +1,15 @@ +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +test-output/ +/doc +*.iml +*.log +.classpath +-project +/.resourceCache +/.project +/.idea +.settings/ diff --git a/jnosql-mapping/jnosql-mapping-semistructured/pom.xml b/jnosql-mapping/jnosql-mapping-semistructured/pom.xml new file mode 100644 index 000000000..68d4f8fa2 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/pom.xml @@ -0,0 +1,46 @@ + + + + 4.0.0 + + org.eclipse.jnosql.mapping + jnosql-mapping-parent + 1.1.1-SNAPSHOT + + + jnosql-mapping-column + jar + + + + org.eclipse.jnosql.communication + jnosql-communication-column + ${project.version} + + + ${project.groupId} + jnosql-mapping-core + ${project.version} + + + jakarta.nosql + nosql-column + ${jakarta.nosql.version} + + + + diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/AbstractColumnTemplate.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/AbstractColumnTemplate.java new file mode 100644 index 000000000..2f9329164 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/AbstractColumnTemplate.java @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + + +import jakarta.data.exceptions.NonUniqueResultException; +import jakarta.nosql.PreparedStatement; +import jakarta.nosql.QueryMapper; +import jakarta.nosql.column.ColumnTemplate; +import org.eclipse.jnosql.communication.column.ColumnDeleteQuery; +import org.eclipse.jnosql.communication.column.ColumnEntity; +import org.eclipse.jnosql.communication.column.ColumnManager; +import org.eclipse.jnosql.communication.column.ColumnObserverParser; +import org.eclipse.jnosql.communication.column.ColumnQuery; +import org.eclipse.jnosql.communication.column.ColumnQueryParser; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.IdNotFoundException; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; +import org.eclipse.jnosql.mapping.metadata.EntityMetadata; +import org.eclipse.jnosql.mapping.metadata.FieldMetadata; +import org.eclipse.jnosql.mapping.core.util.ConverterUtil; +import org.eclipse.jnosql.mapping.metadata.InheritanceMetadata; + +import java.time.Duration; +import java.util.Iterator; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import static java.util.Objects.requireNonNull; + +/** + * The template method to {@link ColumnTemplate} + */ +public abstract class AbstractColumnTemplate implements JNoSQLColumnTemplate { + + private static final ColumnQueryParser PARSER = new ColumnQueryParser(); + + protected abstract ColumnEntityConverter getConverter(); + + protected abstract ColumnManager getManager(); + + protected abstract ColumnEventPersistManager getEventManager(); + + protected abstract EntitiesMetadata getEntities(); + + protected abstract Converters getConverters(); + + private final UnaryOperator insert = e -> getManager().insert(e); + + private final UnaryOperator update = e -> getManager().update(e); + + private ColumnObserverParser observer; + + + private ColumnObserverParser getObserver() { + if (Objects.isNull(observer)) { + observer = new ColumnMapperObserver(getEntities()); + } + return observer; + } + + @Override + public T insert(T entity) { + requireNonNull(entity, "entity is required"); + return persist(entity, insert); + } + + + @Override + public T insert(T entity, Duration ttl) { + requireNonNull(entity, "entity is required"); + requireNonNull(ttl, "ttl is required"); + return persist(entity, e -> getManager().insert(e, ttl)); + } + + + @Override + public T update(T entity) { + requireNonNull(entity, "entity is required"); + return persist(entity, update); + } + + @Override + public Iterable update(Iterable entities) { + requireNonNull(entities, "entity is required"); + return StreamSupport.stream(entities.spliterator(), false) + .map(this::update).collect(Collectors.toList()); + } + + @Override + public Iterable insert(Iterable entities) { + requireNonNull(entities, "entities is required"); + return StreamSupport.stream(entities.spliterator(), false) + .map(this::insert).collect(Collectors.toList()); + } + + @Override + public Iterable insert(Iterable entities, Duration ttl) { + requireNonNull(entities, "entities is required"); + requireNonNull(ttl, "ttl is required"); + return StreamSupport.stream(entities.spliterator(), false) + .map(e -> insert(e, ttl)) + .collect(Collectors.toList()); + } + + @Override + public void delete(ColumnDeleteQuery query) { + requireNonNull(query, "query is required"); + getManager().delete(query); + } + + + @Override + public Stream select(ColumnQuery query) { + requireNonNull(query, "query is required"); + return executeQuery(query); + } + + @Override + public long count(ColumnQuery query) { + return getManager().count(query); + } + + @Override + public boolean exists(ColumnQuery query) { + return getManager().exists(query); + } + + @Override + public Optional singleResult(ColumnQuery query) { + requireNonNull(query, "query is required"); + final Stream select = select(query); + + final Iterator iterator = select.iterator(); + + if (!iterator.hasNext()) { + return Optional.empty(); + } + final T entity = iterator.next(); + + if (!iterator.hasNext()) { + return Optional.of(entity); + } + throw new NonUniqueResultException("No Unique result found to the query: " + query); + } + + @Override + public Optional find(Class type, K id) { + requireNonNull(type, "type is required"); + requireNonNull(id, "id is required"); + EntityMetadata entityMetadata = getEntities().get(type); + FieldMetadata idField = entityMetadata.id() + .orElseThrow(() -> IdNotFoundException.newInstance(type)); + + Object value = ConverterUtil.getValue(id, entityMetadata, idField.fieldName(), getConverters()); + ColumnQuery query = ColumnQuery.select().from(entityMetadata.name()) + .where(idField.name()).eq(value).build(); + + return singleResult(query); + } + + @Override + public void delete(Class type, K id) { + requireNonNull(type, "type is required"); + requireNonNull(id, "id is required"); + + EntityMetadata entityMetadata = getEntities().get(type); + FieldMetadata idField = entityMetadata.id() + .orElseThrow(() -> IdNotFoundException.newInstance(type)); + Object value = ConverterUtil.getValue(id, entityMetadata, idField.fieldName(), getConverters()); + + ColumnDeleteQuery query = ColumnDeleteQuery.delete().from(entityMetadata.name()) + .where(idField.name()).eq(value).build(); + getManager().delete(query); + } + + + @Override + public Stream query(String query) { + requireNonNull(query, "query is required"); + return PARSER.query(query, getManager(), getObserver()).map(c -> getConverter().toEntity(c)); + } + + @Override + public Optional singleResult(String query) { + Stream entities = query(query); + final Iterator iterator = entities.iterator(); + + if (!iterator.hasNext()) { + return Optional.empty(); + } + final T entity = iterator.next(); + if (!iterator.hasNext()) { + return Optional.of(entity); + } + throw new NonUniqueResultException("No unique result found to the query: " + query); + } + + @Override + public PreparedStatement prepare(String query) { + return new ColumnPreparedStatement(PARSER.prepare(query, getManager(), getObserver()), getConverter()); + } + + + @Override + public long count(String columnFamily) { + return getManager().count(columnFamily); + } + + + @Override + public long count(Class type) { + requireNonNull(type, "entity class is required"); + return getManager().count(findAllQuery(type)); + } + + private Stream executeQuery(ColumnQuery query) { + requireNonNull(query, "query is required"); + Stream entities = getManager().select(query); + Function function = e -> getConverter().toEntity(e); + return entities.map(function).peek(getEventManager()::firePostEntity); + } + + @Override + public QueryMapper.MapperFrom select(Class type) { + Objects.requireNonNull(type, "type is required"); + EntityMetadata metadata = getEntities().get(type); + return new ColumnMapperSelect(metadata, getConverters(), this); + } + + @Override + public QueryMapper.MapperDeleteFrom delete(Class type) { + Objects.requireNonNull(type, "type is required"); + EntityMetadata metadata = getEntities().get(type); + return new ColumnMapperDelete(metadata, getConverters(), this); + } + + @Override + public Stream findAll(Class type) { + Objects.requireNonNull(type, "type is required"); + return select(findAllQuery(type)); + } + + @Override + public void deleteAll(Class type) { + Objects.requireNonNull(type, "type is required"); + EntityMetadata metadata = getEntities().get(type); + if(metadata.inheritance().isPresent()){ + InheritanceMetadata inheritanceMetadata = metadata.inheritance().orElseThrow(); + if(!inheritanceMetadata.parent().equals(metadata.type())){ + getManager().delete(ColumnDeleteQuery.delete().from(metadata.name()) + .where(inheritanceMetadata.discriminatorColumn()) + .eq(inheritanceMetadata.discriminatorValue()).build()); + return; + } + } + getManager().delete(ColumnDeleteQuery.delete().from(metadata.name()).build()); + } + + protected T persist(T entity, UnaryOperator persistAction) { + return Stream.of(entity) + .map(toUnary(getEventManager()::firePreEntity)) + .map(getConverter()::toColumn) + .map(persistAction) + .map(t -> getConverter().toEntity(entity, t)) + .map(toUnary(getEventManager()::firePostEntity)) + .findFirst() + .orElseThrow(); + } + + private UnaryOperator toUnary(Consumer consumer) { + return t -> { + consumer.accept(t); + return t; + }; + } + + private ColumnQuery findAllQuery(Class type){ + EntityMetadata metadata = getEntities().get(type); + + if(metadata.inheritance().isPresent()){ + InheritanceMetadata inheritanceMetadata = metadata.inheritance().orElseThrow(); + if(!inheritanceMetadata.parent().equals(metadata.type())){ + return ColumnQuery.select().from(metadata.name()) + .where(inheritanceMetadata.discriminatorColumn()).eq(inheritanceMetadata.discriminatorValue()).build(); + } + } + return ColumnQuery.select().from(metadata.name()).build(); + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/AbstractMapperQuery.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/AbstractMapperQuery.java new file mode 100644 index 000000000..44bcda20b --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/AbstractMapperQuery.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + +import org.eclipse.jnosql.communication.column.Column; +import org.eclipse.jnosql.communication.column.ColumnCondition; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.metadata.EntityMetadata; +import org.eclipse.jnosql.mapping.core.util.ConverterUtil; + +import java.util.List; +import java.util.stream.StreamSupport; + +import static java.util.Arrays.asList; +import static java.util.Objects.nonNull; +import static java.util.Objects.requireNonNull; +import static java.util.stream.Collectors.toList; + +abstract class AbstractMapperQuery { + + protected final String columnFamily; + + protected boolean negate; + + protected ColumnCondition condition; + + protected boolean and; + + protected String name; + + protected transient final EntityMetadata mapping; + + protected transient final Converters converters; + + protected transient final JNoSQLColumnTemplate template; + + protected long start; + + protected long limit; + + + AbstractMapperQuery(EntityMetadata mapping, Converters converters, JNoSQLColumnTemplate template) { + this.mapping = mapping; + this.converters = converters; + this.columnFamily = mapping.name(); + this.template = template; + mapping.inheritance().ifPresent(i -> { + if(!i.parent().equals(mapping.type())){ + this.condition = ColumnCondition.eq(Column.of(i.discriminatorColumn(), i.discriminatorValue())); + this.and = true; + } + }); + } + + protected void appendCondition(ColumnCondition incomingCondition) { + ColumnCondition columnCondition = getColumnCondition(incomingCondition); + + if (nonNull(condition)) { + this.condition = and ? this.condition.and(columnCondition) : this.condition.or(columnCondition); + } else { + this.condition = columnCondition; + } + + this.negate = false; + this.name = null; + } + + protected void betweenImpl(T valueA, T valueB) { + requireNonNull(valueA, "valueA is required"); + requireNonNull(valueB, "valueB is required"); + ColumnCondition newCondition = ColumnCondition + .between(Column.of(mapping.columnField(name), asList(getValue(valueA), getValue(valueB)))); + appendCondition(newCondition); + } + + + protected void inImpl(Iterable values) { + + requireNonNull(values, "values is required"); + List convertedValues = StreamSupport.stream(values.spliterator(), false) + .map(this::getValue).collect(toList()); + ColumnCondition newCondition = ColumnCondition + .in(Column.of(mapping.columnField(name), convertedValues)); + appendCondition(newCondition); + } + + protected void eqImpl(T value) { + requireNonNull(value, "value is required"); + + ColumnCondition newCondition = ColumnCondition + .eq(Column.of(mapping.columnField(name), getValue(value))); + appendCondition(newCondition); + } + + protected void likeImpl(String value) { + requireNonNull(value, "value is required"); + ColumnCondition newCondition = ColumnCondition + .like(Column.of(mapping.columnField(name), getValue(value))); + appendCondition(newCondition); + } + + protected void gteImpl(T value) { + requireNonNull(value, "value is required"); + ColumnCondition newCondition = ColumnCondition + .gte(Column.of(mapping.columnField(name), getValue(value))); + appendCondition(newCondition); + } + + protected void gtImpl(T value) { + requireNonNull(value, "value is required"); + ColumnCondition newCondition = ColumnCondition + .gt(Column.of(mapping.columnField(name), getValue(value))); + appendCondition(newCondition); + } + + protected void ltImpl(T value) { + requireNonNull(value, "value is required"); + ColumnCondition newCondition = ColumnCondition + .lt(Column.of(mapping.columnField(name), getValue(value))); + appendCondition(newCondition); + } + + protected void lteImpl(T value) { + requireNonNull(value, "value is required"); + ColumnCondition newCondition = ColumnCondition + .lte(Column.of(mapping.columnField(name), getValue(value))); + appendCondition(newCondition); + } + + protected Object getValue(Object value) { + return ConverterUtil.getValue(value, mapping, name, converters); + } + + private ColumnCondition getColumnCondition(ColumnCondition newCondition) { + if (negate) { + return newCondition.negate(); + } else { + return newCondition; + } + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/ColumnEntityConverter.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/ColumnEntityConverter.java new file mode 100644 index 000000000..91ef35fa2 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/ColumnEntityConverter.java @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + +import org.eclipse.jnosql.communication.column.Column; +import org.eclipse.jnosql.communication.column.ColumnEntity; +import org.eclipse.jnosql.mapping.core.Converters; +import jakarta.data.exceptions.MappingException; +import org.eclipse.jnosql.mapping.metadata.ConstructorBuilder; +import org.eclipse.jnosql.mapping.metadata.ConstructorMetadata; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; +import org.eclipse.jnosql.mapping.metadata.EntityMetadata; +import org.eclipse.jnosql.mapping.metadata.FieldMetadata; +import org.eclipse.jnosql.mapping.metadata.InheritanceMetadata; +import org.eclipse.jnosql.mapping.metadata.MappingType; +import org.eclipse.jnosql.mapping.metadata.ParameterMetaData; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Predicate; + +import static java.util.Objects.requireNonNull; +import static org.eclipse.jnosql.mapping.metadata.MappingType.EMBEDDED; +import static org.eclipse.jnosql.mapping.metadata.MappingType.ENTITY; + + +/** + * This interface represents the converter between an entity and the {@link ColumnEntity} + */ +public abstract class ColumnEntityConverter { + + protected abstract EntitiesMetadata getEntities(); + + protected abstract Converters getConverters(); + + /** + * Converts the instance entity to {@link ColumnEntity} + * + * @param entity the instance + * @return a {@link ColumnEntity} instance + * @throws NullPointerException when entity is null + */ + public ColumnEntity toColumn(Object entity) { + requireNonNull(entity, "entity is required"); + EntityMetadata mapping = getEntities().get(entity.getClass()); + ColumnEntity communication = ColumnEntity.of(mapping.name()); + mapping.fields().stream() + .map(f -> to(f, entity)) + .map(f -> f.toColumn(this, getConverters())) + .flatMap(List::stream) + .forEach(communication::add); + + mapping.inheritance().ifPresent(i -> communication.add(i.discriminatorColumn(), + i.discriminatorValue())); + return communication; + } + + /** + * Converts a {@link ColumnEntity} to entity + * + * @param type the entity class + * @param entity the {@link ColumnEntity} to be converted + * @param the entity type + * @return the instance from {@link ColumnEntity} + * @throws NullPointerException when either type or entity are null + */ + public T toEntity(Class type, ColumnEntity entity) { + requireNonNull(entity, "entity is required"); + requireNonNull(type, "type is required"); + return toEntity(type, entity.columns()); + } + + /** + * Converts a {@link ColumnEntity} to entity + * Instead of creating a new object is uses the instance used in this parameters + * + * @param type the instance + * @param entity the {@link ColumnEntity} to be converted + * @param the entity type + * @return the same instance with values set from {@link ColumnEntity} + * @throws NullPointerException when either type or entity are null + */ + public T toEntity(T type, ColumnEntity entity) { + requireNonNull(entity, "entity is required"); + requireNonNull(type, "type is required"); + + if (type.getClass().isRecord()) { + return (T) toEntity(type.getClass(), entity.columns()); + } + EntityMetadata mapping = getEntities().get(type.getClass()); + return convertEntity(entity.columns(), mapping, type); + } + + /** + * Similar to {@link ColumnEntityConverter#toEntity(Class, ColumnEntity)}, but + * search the instance type from {@link ColumnEntity#name()} + * + * @param entity the {@link ColumnEntity} to be converted + * @param the entity type + * @return the instance from {@link ColumnEntity} + * @throws NullPointerException when entity is null + */ + public T toEntity(ColumnEntity entity) { + requireNonNull(entity, "entity is required"); + EntityMetadata mapping = getEntities().findByName(entity.name()); + if (mapping.isInheritance()) { + return mapInheritanceEntity(entity, mapping.type()); + } + ConstructorMetadata constructor = mapping.constructor(); + if (constructor.isDefault()) { + T instance = mapping.newInstance(); + return convertEntity(entity.columns(), mapping, instance); + } else { + return convertEntityByConstructor(entity.columns(), mapping); + } + } + + protected ColumnFieldValue to(FieldMetadata field, Object entity) { + Object value = field.read(entity); + return DefaultColumnFieldValue.of(value, field); + } + + protected Consumer feedObject(T entity, List columns, Map fieldsGroupByName) { + return (String k) -> { + Optional column = columns.stream().filter(c -> c.name().equals(k)).findFirst(); + FieldMetadata field = fieldsGroupByName.get(k); + FieldConverter fieldConverter = FieldConverter.get(field); + if (ENTITY.equals(field.mappingType())) { + column.ifPresent(c -> fieldConverter.convert(entity, c, field, this)); + } else { + fieldConverter.convert(entity, columns, column.orElse(null), field, this); + } + }; + } + + + protected T toEntity(Class type, List columns) { + EntityMetadata mapping = getEntities().get(type); + if (mapping.isInheritance()) { + return inheritanceToEntity(columns, mapping); + } + ConstructorMetadata constructor = mapping.constructor(); + if (constructor.isDefault()) { + T instance = mapping.newInstance(); + return convertEntity(columns, mapping, instance); + } else { + return convertEntityByConstructor(columns, mapping); + } + } + + private T convertEntityByConstructor(List columns, EntityMetadata mapping) { + ConstructorBuilder builder = ConstructorBuilder.of(mapping.constructor()); + for (ParameterMetaData parameter : builder.parameters()) { + Optional column = columns.stream() + .filter(c -> c.name().equals(parameter.name())) + .findFirst(); + column.ifPresentOrElse(c -> { + ParameterConverter converter = ParameterConverter.of(parameter, getEntities()); + converter.convert(this, c, parameter, builder); + }, builder::addEmptyParameter); + } + return builder.build(); + } + + private T convertEntity(List columns, EntityMetadata mapping, T instance) { + final Map fieldsGroupByName = mapping.fieldsGroupByName(); + final List names = columns.stream().map(Column::name).sorted().toList(); + final Predicate existField = k -> Collections.binarySearch(names, k) >= 0; + final Predicate isElementType = k -> { + MappingType type = fieldsGroupByName.get(k).mappingType(); + return EMBEDDED.equals(type) || ENTITY.equals(type); + }; + fieldsGroupByName.keySet().stream() + .filter(existField.or(isElementType)) + .forEach(feedObject(instance, columns, fieldsGroupByName)); + + return instance; + } + + private T mapInheritanceEntity(ColumnEntity entity, Class type) { + Map group = getEntities() + .findByParentGroupByDiscriminatorValue(type); + + if (group.isEmpty()) { + throw new MappingException("There is no discriminator inheritance to the document collection " + + entity.name()); + } + String column = group.values() + .stream() + .findFirst() + .map(InheritanceMetadata::discriminatorColumn) + .orElseThrow(); + + String discriminator = entity.find(column, String.class) + .orElseThrow( + () -> new MappingException("To inheritance there is the discriminator column missing" + + " on the Document Collection, the document name: " + column)); + + InheritanceMetadata inheritance = Optional.ofNullable(group.get(discriminator)) + .orElseThrow(() -> new MappingException("There is no inheritance map to the discriminator" + + " column value " + discriminator)); + + EntityMetadata mapping = getEntities().get(inheritance.entity()); + ConstructorMetadata constructor = mapping.constructor(); + if (constructor.isDefault()) { + T instance = mapping.newInstance(); + return convertEntity(entity.columns(), mapping, instance); + } else { + return convertEntityByConstructor(entity.columns(), mapping); + } + } + + private T inheritanceToEntity(List columns, EntityMetadata mapping) { + Map group = getEntities() + .findByParentGroupByDiscriminatorValue(mapping.type()); + + if (group.isEmpty()) { + throw new MappingException("There is no discriminator inheritance to the document collection " + + mapping.name()); + } + + String column = group.values() + .stream() + .findFirst() + .map(InheritanceMetadata::discriminatorColumn) + .orElseThrow(); + + String discriminator = columns.stream() + .filter(d -> d.name().equals(column)) + .findFirst() + .map(d -> d.get(String.class)) + .orElseThrow( + () -> new MappingException("To inheritance there is the discriminator column missing" + + " on the Document Collection, the document name: " + column)); + + InheritanceMetadata inheritance = Optional.ofNullable(group.get(discriminator)) + .orElseThrow(() -> new MappingException("There is no inheritance map to the discriminator" + + " column value " + discriminator)); + + EntityMetadata inheritanceMetadata = getEntities().get(inheritance.entity()); + T instance = inheritanceMetadata.newInstance(); + return convertEntity(columns, inheritanceMetadata, instance); + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/ColumnEventPersistManager.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/ColumnEventPersistManager.java new file mode 100644 index 000000000..61efdc85e --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/ColumnEventPersistManager.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; +import org.eclipse.jnosql.mapping.EntityPostPersist; +import org.eclipse.jnosql.mapping.EntityPrePersist; + +/** + * This interface represents the manager of events. When an entity be either saved or updated an event will be fired. + * This order going to be: + * 1) firePreColumn + * 2) firePostColumn + * + * @see AbstractColumnTemplate + */ +@ApplicationScoped +public class ColumnEventPersistManager { + + + @Inject + private Event entityPrePersistEvent; + + @Inject + private Event entityPostPersistEvent; + + /** + * Fire an event once the method is called + * + * @param entity the entity + * @param the entity type + */ + public void firePreEntity(T entity) { + entityPrePersistEvent.fire(EntityPrePersist.of(entity)); + } + + /** + * Fire an event after firePostEntity + * + * @param entity the entity + * @param the entity kind + */ + public void firePostEntity(T entity) { + entityPostPersistEvent.fire(EntityPostPersist.of(entity)); + } + +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/ColumnFieldValue.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/ColumnFieldValue.java new file mode 100644 index 000000000..7b4f10580 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/ColumnFieldValue.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + + +import org.eclipse.jnosql.communication.column.Column; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.metadata.FieldValue; + +import java.util.List; + +/** + * The specialist {@link FieldValue} to column + */ +public interface ColumnFieldValue extends FieldValue { + + + /** + * Converts an entity to a {@link List} of columns + * @param converter the converter + * @param converters the converters + * @param the type of the entity attribute + * @param the type of the database column + * @return a {@link List} of columns from the field + */ + List toColumn(ColumnEntityConverter converter, Converters converters); + +} \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/ColumnMapperDelete.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/ColumnMapperDelete.java new file mode 100644 index 000000000..20c1d4c79 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/ColumnMapperDelete.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + +import jakarta.nosql.QueryMapper.MapperDeleteFrom; +import jakarta.nosql.QueryMapper.MapperDeleteNameCondition; +import jakarta.nosql.QueryMapper.MapperDeleteNotCondition; +import jakarta.nosql.QueryMapper.MapperDeleteWhere; +import org.eclipse.jnosql.communication.column.ColumnDeleteQuery; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.metadata.EntityMetadata; + +import static java.util.Objects.requireNonNull; + +final class ColumnMapperDelete extends AbstractMapperQuery implements MapperDeleteFrom, + MapperDeleteWhere, MapperDeleteNameCondition, MapperDeleteNotCondition { + + + ColumnMapperDelete(EntityMetadata mapping, Converters converters, JNoSQLColumnTemplate template) { + super(mapping, converters, template); + } + + @Override + public MapperDeleteNameCondition where(String name) { + requireNonNull(name, "name is required"); + this.name = name; + return this; + } + + + @Override + public MapperDeleteNameCondition and(String name) { + requireNonNull(name, "name is required"); + this.name = name; + this.and = true; + return this; + } + + @Override + public MapperDeleteNameCondition or(String name) { + requireNonNull(name, "name is required"); + this.name = name; + this.and = false; + return this; + } + + + @Override + public MapperDeleteNotCondition not() { + this.negate = true; + return this; + } + + @Override + public MapperDeleteWhere eq(T value) { + eqImpl(value); + return this; + } + + @Override + public MapperDeleteWhere like(String value) { + likeImpl(value); + return this; + } + + @Override + public MapperDeleteWhere gt(T value) { + gtImpl(value); + return this; + } + + @Override + public MapperDeleteWhere gte(T value) { + gteImpl(value); + return this; + } + + @Override + public MapperDeleteWhere lt(T value) { + ltImpl(value); + return this; + } + + @Override + public MapperDeleteWhere lte(T value) { + lteImpl(value); + return this; + } + + @Override + public MapperDeleteWhere between(T valueA, T valueB) { + betweenImpl(valueA, valueB); + return this; + } + + @Override + public MapperDeleteWhere in(Iterable values) { + inImpl(values); + return this; + } + + + private ColumnDeleteQuery build() { + return new MappingColumnDeleteQuery(columnFamily, condition); + } + + @Override + public void execute() { + ColumnDeleteQuery query = build(); + this.template.delete(query); + } + +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/ColumnMapperObserver.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/ColumnMapperObserver.java new file mode 100644 index 000000000..94d2ca42c --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/ColumnMapperObserver.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + +import org.eclipse.jnosql.communication.column.ColumnObserverParser; +import org.eclipse.jnosql.mapping.metadata.ClassInformationNotFoundException; +import org.eclipse.jnosql.mapping.metadata.EntityMetadata; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; + +import java.util.Optional; + +final class ColumnMapperObserver implements ColumnObserverParser { + + + private final EntitiesMetadata mappings; + + ColumnMapperObserver(EntitiesMetadata mappings) { + this.mappings = mappings; + } + + + @Override + public String fireEntity(String entity) { + Optional mapping = getEntityMetadata(entity); + return mapping.map(EntityMetadata::name).orElse(entity); + } + + @Override + public String fireField(String entity, String field) { + Optional mapping = getEntityMetadata(entity); + return mapping.map(c -> c.columnField(field)).orElse(field); + } + + private Optional getEntityMetadata(String entity) { + try { + return Optional.of(this.mappings.findByName(entity)); + } catch (ClassInformationNotFoundException e) { + return this.mappings.findBySimpleName(entity) + .or(() -> this.mappings.findByClassName(entity)); + } + } + +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/ColumnMapperSelect.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/ColumnMapperSelect.java new file mode 100644 index 000000000..586924c00 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/ColumnMapperSelect.java @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + +import jakarta.data.Direction; +import jakarta.data.Sort; +import org.eclipse.jnosql.communication.column.ColumnQuery; +import org.eclipse.jnosql.mapping.core.Converters; +import jakarta.nosql.QueryMapper.MapperFrom; +import jakarta.nosql.QueryMapper.MapperLimit; +import jakarta.nosql.QueryMapper.MapperNameCondition; +import jakarta.nosql.QueryMapper.MapperNameOrder; +import jakarta.nosql.QueryMapper.MapperNotCondition; +import jakarta.nosql.QueryMapper.MapperOrder; +import jakarta.nosql.QueryMapper.MapperSkip; +import jakarta.nosql.QueryMapper.MapperWhere; +import org.eclipse.jnosql.mapping.metadata.EntityMetadata; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +import static java.util.Objects.requireNonNull; + +final class ColumnMapperSelect extends AbstractMapperQuery implements MapperFrom, MapperLimit, + MapperSkip, MapperOrder, MapperNameCondition, + MapperNotCondition, MapperNameOrder, MapperWhere { + + private final List> sorts = new ArrayList<>(); + + ColumnMapperSelect(EntityMetadata mapping, Converters converters, JNoSQLColumnTemplate template) { + super(mapping, converters, template); + } + + @Override + public MapperNameCondition and(String name) { + requireNonNull(name, "name is required"); + this.name = name; + this.and = true; + return this; + } + + @Override + public MapperNameCondition or(String name) { + requireNonNull(name, "name is required"); + this.name = name; + this.and = false; + return this; + } + + @Override + public MapperNameCondition where(String name) { + requireNonNull(name, "name is required"); + this.name = name; + return this; + } + + @Override + public MapperSkip skip(long start) { + this.start = start; + return this; + } + + @Override + public MapperLimit limit(long limit) { + this.limit = limit; + return this; + } + + @Override + public MapperOrder orderBy(String name) { + requireNonNull(name, "name is required"); + this.name = name; + return this; + } + + @Override + public MapperNotCondition not() { + this.negate = true; + return this; + } + + @Override + public MapperWhere eq(T value) { + eqImpl(value); + return this; + } + + + @Override + public MapperWhere like(String value) { + likeImpl(value); + return this; + } + + @Override + public MapperWhere gt(T value) { + gtImpl(value); + return this; + } + + @Override + public MapperWhere gte(T value) { + gteImpl(value); + return this; + } + + @Override + public MapperWhere lt(T value) { + ltImpl(value); + return this; + } + + + @Override + public MapperWhere lte(T value) { + lteImpl(value); + return this; + } + + @Override + public MapperWhere between(T valueA, T valueB) { + betweenImpl(valueA, valueB); + return this; + } + + @Override + public MapperWhere in(Iterable values) { + inImpl(values); + return this; + } + + @Override + public MapperNameOrder asc() { + this.sorts.add(Sort.of(mapping.columnField(name), Direction.ASC, false)); + return this; + } + + @Override + public MapperNameOrder desc() { + this.sorts.add(Sort.of(mapping.columnField(name), Direction.DESC, false)); + return this; + } + private ColumnQuery build() { + return new MappingColumnQuery(sorts, limit, start, condition, columnFamily); + } + + @Override + public List result() { + ColumnQuery query = build(); + return this.template.select(query) + .toList(); + } + + @Override + public Stream stream() { + ColumnQuery query = build(); + return this.template.select(query); + } + + @Override + public Optional singleResult() { + ColumnQuery query = build(); + return this.template.singleResult(query); + } + +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/ColumnPreparedStatement.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/ColumnPreparedStatement.java new file mode 100644 index 000000000..8ec50c879 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/ColumnPreparedStatement.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + +import jakarta.nosql.PreparedStatement; +import org.eclipse.jnosql.communication.column.ColumnEntity; + +import java.util.Optional; +import java.util.stream.Stream; + +final class ColumnPreparedStatement implements PreparedStatement { + + private final org.eclipse.jnosql.communication.column.ColumnPreparedStatement preparedStatement; + + private final ColumnEntityConverter converter; + + ColumnPreparedStatement(org.eclipse.jnosql.communication.column.ColumnPreparedStatement preparedStatement, + ColumnEntityConverter converter) { + this.preparedStatement = preparedStatement; + this.converter = converter; + } + + @Override + public PreparedStatement bind(String name, Object value) { + preparedStatement.bind(name, value); + return this; + } + + @Override + public Stream result() { + return preparedStatement.result().map(c -> converter.toEntity(c)); + } + + @Override + public Optional singleResult() { + Optional singleResult = preparedStatement.singleResult(); + return singleResult.map(converter::toEntity); + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/ColumnTemplateProducer.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/ColumnTemplateProducer.java new file mode 100644 index 000000000..0d2a7907a --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/ColumnTemplateProducer.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Vetoed; +import jakarta.inject.Inject; +import jakarta.nosql.column.ColumnTemplate; +import org.eclipse.jnosql.communication.column.ColumnManager; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; + +import java.util.Objects; +import java.util.function.Function; + +/** + * The producer of {@link ColumnTemplate} + */ +@ApplicationScoped +public class ColumnTemplateProducer implements Function { + + + @Inject + private ColumnEntityConverter converter; + + @Inject + private ColumnEventPersistManager eventManager; + + @Inject + private EntitiesMetadata entities; + + @Inject + private Converters converters; + + @Override + public JNoSQLColumnTemplate apply(ColumnManager manager) { + Objects.requireNonNull(manager, "manager is required"); + return new ProducerColumnTemplate(converter, manager, + eventManager, entities, converters); + } + + + @Vetoed + static class ProducerColumnTemplate extends AbstractColumnTemplate { + + private ColumnEntityConverter converter; + + private ColumnManager manager; + + private ColumnEventPersistManager eventManager; + + private EntitiesMetadata entities; + + private Converters converters; + + ProducerColumnTemplate(ColumnEntityConverter converter, + ColumnManager manager, + ColumnEventPersistManager eventManager, + EntitiesMetadata entities, + Converters converters) { + this.converter = converter; + this.manager = manager; + this.eventManager = eventManager; + this.entities = entities; + this.converters = converters; + } + + ProducerColumnTemplate() { + } + + @Override + protected ColumnEntityConverter getConverter() { + return converter; + } + + @Override + protected ColumnManager getManager() { + return manager; + } + + @Override + protected ColumnEventPersistManager getEventManager() { + return eventManager; + } + + @Override + protected EntitiesMetadata getEntities() { + return entities; + } + + @Override + protected Converters getConverters() { + return converters; + } + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/DefaultColumnEntityConverter.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/DefaultColumnEntityConverter.java new file mode 100644 index 000000000..a7adfddd5 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/DefaultColumnEntityConverter.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + + +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +/** + * The default implementation to {@link ColumnEntityConverter} + */ +@ApplicationScoped +class DefaultColumnEntityConverter extends ColumnEntityConverter { + + @Inject + private EntitiesMetadata entities; + + @Inject + private Converters converters; + + @Override + protected EntitiesMetadata getEntities() { + return entities; + } + + @Override + protected Converters getConverters() { + return converters; + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/DefaultColumnFieldValue.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/DefaultColumnFieldValue.java new file mode 100644 index 000000000..2a39732c2 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/DefaultColumnFieldValue.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + +import org.eclipse.jnosql.mapping.AttributeConverter; +import org.eclipse.jnosql.communication.column.Column; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.metadata.FieldMetadata; +import org.eclipse.jnosql.mapping.metadata.MappingType; +import org.eclipse.jnosql.mapping.metadata.FieldValue; +import org.eclipse.jnosql.mapping.metadata.DefaultFieldValue; +import org.eclipse.jnosql.mapping.metadata.GenericFieldMetadata; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static org.eclipse.jnosql.mapping.metadata.MappingType.COLLECTION; + +import static org.eclipse.jnosql.mapping.metadata.MappingType.EMBEDDED; +import static org.eclipse.jnosql.mapping.metadata.MappingType.ENTITY; +import static java.util.Collections.singletonList; + +final class DefaultColumnFieldValue implements ColumnFieldValue { + + private final FieldValue fieldValue; + + private DefaultColumnFieldValue(FieldValue fieldValue) { + this.fieldValue = fieldValue; + } + + @Override + public Object value() { + return fieldValue.value(); + } + + @Override + public FieldMetadata field() { + return fieldValue.field(); + } + + @Override + public boolean isNotEmpty() { + return fieldValue.isNotEmpty(); + } + + @SuppressWarnings("unchecked") + @Override + public List toColumn(ColumnEntityConverter converter, Converters converters) { + if (value() == null) { + return singletonList(Column.of(getName(), null)); + } else if (EMBEDDED.equals(getType())) { + return converter.toColumn(value()).columns(); + } else if (ENTITY.equals(getType())) { + return singletonList(Column.of(getName(), converter.toColumn(value()).columns())); + } else if (isEmbeddableCollection()) { + return singletonList(Column.of(getName(), getColumns(converter))); + } + Optional>> optionalConverter = field().converter(); + if (optionalConverter.isPresent()) { + AttributeConverter attributeConverter = converters.get(field()); + return singletonList(Column.of(getName(), attributeConverter.convertToDatabaseColumn((X) value()))); + } + return singletonList(Column.of(getName(), value())); + } + + private List> getColumns(ColumnEntityConverter converter) { + List> columns = new ArrayList<>(); + for (Object element : (Iterable) value()) { + columns.add(converter.toColumn(element).columns()); + } + return columns; + } + + private boolean isEmbeddableCollection() { + return COLLECTION.equals(getType()) && isEmbeddableElement(); + } + + private MappingType getType() { + return field().mappingType(); + } + + private String getName() { + return field().name(); + } + + private boolean isEmbeddableElement() { + return ((GenericFieldMetadata) field()).isEmbeddable(); + } + + @Override + public String toString() { + return "ColumnFieldValue{" + "fieldValue=" + fieldValue + + '}'; + } + + static ColumnFieldValue of(Object value, FieldMetadata field) { + return new DefaultColumnFieldValue(new DefaultFieldValue(value, field)); + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/DefaultColumnTemplate.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/DefaultColumnTemplate.java new file mode 100644 index 000000000..045b141ec --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/DefaultColumnTemplate.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Default; +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; +import org.eclipse.jnosql.communication.column.ColumnManager; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.Database; +import org.eclipse.jnosql.mapping.DatabaseType; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; + +/** + * The default implementation of {@link JNoSQLColumnTemplate} + */ +@Default +@Database(DatabaseType.COLUMN) +@ApplicationScoped +class DefaultColumnTemplate extends AbstractColumnTemplate { + + private ColumnEntityConverter converter; + + private Instance manager; + + private ColumnEventPersistManager eventManager; + + private EntitiesMetadata entities; + + private Converters converters; + + @Inject + DefaultColumnTemplate(ColumnEntityConverter converter, Instance manager, + ColumnEventPersistManager eventManager, + EntitiesMetadata entities, Converters converters) { + this.converter = converter; + this.manager = manager; + this.eventManager = eventManager; + this.entities = entities; + this.converters = converters; + } + + DefaultColumnTemplate(){ + } + + @Override + protected ColumnEntityConverter getConverter() { + return converter; + } + + @Override + protected ColumnManager getManager() { + return manager.get(); + } + + @Override + protected ColumnEventPersistManager getEventManager() { + return eventManager; + } + + @Override + protected EntitiesMetadata getEntities() { + return entities; + } + + @Override + protected Converters getConverters() { + return converters; + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/FieldConverter.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/FieldConverter.java new file mode 100644 index 000000000..7e43252d5 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/FieldConverter.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + +import org.eclipse.jnosql.communication.TypeReference; +import org.eclipse.jnosql.communication.Value; +import org.eclipse.jnosql.communication.column.Column; +import org.eclipse.jnosql.mapping.AttributeConverter; +import org.eclipse.jnosql.mapping.metadata.EntityMetadata; +import org.eclipse.jnosql.mapping.metadata.FieldMetadata; +import org.eclipse.jnosql.mapping.metadata.GenericFieldMetadata; +import org.eclipse.jnosql.mapping.metadata.MappingType; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; + +enum FieldConverter { + EMBEDDED { + @Override + public void convert(T instance, List columns, Column column, + FieldMetadata field, ColumnEntityConverter converter) { + Object subEntity = converter.toEntity(field.type(), columns); + EntityMetadata mapping = converter.getEntities().get(subEntity.getClass()); + boolean areAllFieldsNull = mapping.fields() + .stream() + .map(f -> f.read(subEntity)) + .allMatch(Objects::isNull); + if (!areAllFieldsNull) { + field.write(instance, subEntity); + } + } + }, ENTITY { + @Override + public void convert(T instance, List columns, Column subColumn, FieldMetadata field, + ColumnEntityConverter converter) { + + if (Objects.nonNull(subColumn)) { + converterSubDocument(instance, subColumn, field, converter); + } else { + field.write(instance, converter.toEntity(field.type(), columns)); + } + } + + private void converterSubDocument(T instance, Column subColumn, FieldMetadata field, + ColumnEntityConverter converter) { + Object value = subColumn.get(); + if (value instanceof Map map) { + List embeddedColumns = new ArrayList<>(); + + for (Map.Entry entry : (Set) map.entrySet()) { + embeddedColumns.add(Column.of(entry.getKey().toString(), entry.getValue())); + } + field.write(instance, converter.toEntity(field.type(), embeddedColumns)); + + } else { + field.write(instance, converter.toEntity(field.type(), + subColumn.get(new TypeReference>() { + }))); + } + } + }, COLLECTION { + @SuppressWarnings("unchecked") + @Override + public void convert(T instance, List columns, Column column, FieldMetadata field, + ColumnEntityConverter converter) { + + if (Objects.nonNull(column)) { + GenericFieldMetadata genericField = (GenericFieldMetadata) field; + Collection elements = genericField.collectionInstance(); + List> embeddable = (List>) column.get(); + if(Objects.isNull(embeddable)) { + return; + } + for (List columnList : embeddable) { + Object element = converter.toEntity(genericField.elementType(), columnList); + elements.add(element); + } + field.write(instance, elements); + } + } + }, DEFAULT{ + @SuppressWarnings("unchecked") + @Override + public void convert(T instance, List columns, Column column, + FieldMetadata field, ColumnEntityConverter converter) { + if (Objects.nonNull(column)) { + Value value = column.value(); + Optional>> optionalConverter = field.converter(); + if (optionalConverter.isPresent()) { + AttributeConverter attributeConverter = converter.getConverters().get(field); + Y attr = (Y)(value.isInstanceOf(List.class) ? column : value.get()); + Object attributeConverted = attributeConverter.convertToEntityAttribute(attr); + field.write(instance, field.value(Value.of(attributeConverted))); + } else { + field.write(instance, field.value(value)); + } + } + } + }; + + static FieldConverter get(FieldMetadata field) { + if (MappingType.EMBEDDED.equals(field.mappingType())) { + return EMBEDDED; + } else if (MappingType.ENTITY.equals(field.mappingType())) { + return ENTITY; + } else if (isCollectionEmbeddable(field)) { + return COLLECTION; + } else { + return DEFAULT; + } + } + + private static boolean isCollectionEmbeddable(FieldMetadata field) { + return MappingType.COLLECTION.equals(field.mappingType()) && ((GenericFieldMetadata) field).isEmbeddable(); + } + + abstract void convert(T instance, List columns, Column column, FieldMetadata field, + ColumnEntityConverter converter); + + void convert(T instance, Column column, FieldMetadata field, + ColumnEntityConverter converter) { + convert(instance, null, column, field, converter); + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/JNoSQLColumnTemplate.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/JNoSQLColumnTemplate.java new file mode 100644 index 000000000..99c8141ec --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/JNoSQLColumnTemplate.java @@ -0,0 +1,93 @@ +/* + * + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + * + */ +package org.eclipse.jnosql.mapping.column; + +import jakarta.nosql.column.ColumnTemplate; +import org.eclipse.jnosql.communication.column.ColumnDeleteQuery; +import org.eclipse.jnosql.communication.column.ColumnQuery; + +import java.util.Optional; +import java.util.stream.Stream; + + +/** + * A {@link ColumnTemplate} specialization that operates on both ColumnDeleteQuery and ColumnQuery + */ +public interface JNoSQLColumnTemplate extends ColumnTemplate { + + /** + * Deletes an entity + * + * @param query query to delete an entity + * @throws NullPointerException when query is null + */ + void delete(ColumnDeleteQuery query); + + /** + * Finds entities from query + * + * @param query - query to figure out entities + * @param the instance type + * @return entities found by query + * @throws NullPointerException when query is null + */ + Stream select(ColumnQuery query); + + /** + * Returns the number of items in the column family that match a specified query. + * @param query the query + * @return the number of documents from query + * @throws NullPointerException when query is null + */ + long count(ColumnQuery query); + + /** + * Returns whether an entity that match a specified query. + * @param query the query + * @return true if an entity with the given query exists, false otherwise. + * @throws NullPointerException when query it null + */ + boolean exists(ColumnQuery query); + + /** + * Returns a single entity from query + * + * @param query - query to figure out entities + * @param the instance type + * @return an entity on {@link Optional} or {@link Optional#empty()} when the result is not found. + * @throws NullPointerException when query is null + */ + Optional singleResult(ColumnQuery query); + + /** + * Returns all entities on the database + * @param type the entity type filter + * @return the {@link Stream} + * @param the entity type + * @throws NullPointerException when type is null + */ + Stream findAll(Class type); + + /** + * delete all entities from the database + * @param type the entity type filter + * @param the entity type + * @throws NullPointerException when type is null + */ + void deleteAll(Class type); + +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/MappingColumnDeleteQuery.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/MappingColumnDeleteQuery.java new file mode 100644 index 000000000..24a402776 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/MappingColumnDeleteQuery.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + +import org.eclipse.jnosql.communication.column.ColumnCondition; +import org.eclipse.jnosql.communication.column.ColumnDeleteQuery; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +record MappingColumnDeleteQuery(String columnFamily, ColumnCondition columnCondition) implements ColumnDeleteQuery { + + + @Override + public String name() { + return columnFamily; + } + + @Override + public Optional condition() { + return Optional.ofNullable(columnCondition); + } + + @Override + public List columns() { + return Collections.emptyList(); + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/MappingColumnQuery.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/MappingColumnQuery.java new file mode 100644 index 000000000..ce1fb131e --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/MappingColumnQuery.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + + +import jakarta.data.Sort; +import org.eclipse.jnosql.communication.column.ColumnCondition; +import org.eclipse.jnosql.communication.column.ColumnQuery; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import static java.util.Collections.emptyList; + +/** + * A mapping implementation of {@link ColumnQuery} + */ +public record MappingColumnQuery(List> sorts, long limit, long skip, ColumnCondition columnCondition, String columnFamily) + implements ColumnQuery { + + + @Override + public String name() { + return columnFamily; + } + + @Override + public Optional condition() { + return Optional.ofNullable(columnCondition); + } + + @Override + public List columns() { + return emptyList(); + } + + @Override + public List> sorts() { + return Collections.unmodifiableList(sorts); + } + +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/ParameterConverter.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/ParameterConverter.java new file mode 100644 index 000000000..3fe274901 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/ParameterConverter.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + +import org.eclipse.jnosql.communication.TypeReference; +import org.eclipse.jnosql.communication.column.Column; +import org.eclipse.jnosql.mapping.metadata.ConstructorBuilder; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; +import org.eclipse.jnosql.mapping.metadata.GenericParameterMetaData; +import org.eclipse.jnosql.mapping.metadata.ParameterMetaData; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +enum ParameterConverter { + DEFAULT { + @Override + void convert(ColumnEntityConverter converter, + Column column, + ParameterMetaData metaData, + ConstructorBuilder builder) { + + metaData.converter().ifPresentOrElse(c -> { + Object value = converter.getConverters().get(metaData).convertToEntityAttribute(column.get()); + builder.add(value); + }, () -> builder.add(column.get(metaData.type()))); + + } + }, ENTITY { + @Override + void convert(ColumnEntityConverter converter, Column column, ParameterMetaData metaData, + ConstructorBuilder builder) { + + Object value = column.get(); + if (value instanceof Map map) { + List columns = new ArrayList<>(); + + for (Map.Entry entry : map.entrySet()) { + columns.add(Column.of(entry.getKey().toString(), entry.getValue())); + } + + Object entity = converter.toEntity(metaData.type(), columns); + builder.add(entity); + + } else { + List columns = column.get(new TypeReference<>() {}); + Object entity = converter.toEntity(metaData.type(), columns); + builder.add(entity); + } + } + }, COLLECTION { + @Override + void convert(ColumnEntityConverter converter, Column column, ParameterMetaData metaData, + ConstructorBuilder builder) { + + GenericParameterMetaData genericParameter = (GenericParameterMetaData) metaData; + Collection elements = genericParameter.collectionInstance(); + List> embeddable = (List>) column.get(); + for (List columnList : embeddable) { + Object element = converter.toEntity(genericParameter.elementType(), columnList); + elements.add(element); + } + builder.add(elements); + + } + + }; + + abstract void convert(ColumnEntityConverter converter, + Column column, ParameterMetaData metaData, + ConstructorBuilder builder); + + static ParameterConverter of(ParameterMetaData parameter, EntitiesMetadata entities) { + return switch (parameter.mappingType()) { + case COLLECTION -> validateCollection(parameter, entities); + case ENTITY -> ENTITY; + default -> DEFAULT; + }; + } + + private static ParameterConverter validateCollection(ParameterMetaData parameter, EntitiesMetadata entities) { + GenericParameterMetaData genericParameter = (GenericParameterMetaData) parameter; + Class type = genericParameter.elementType(); + if (entities.findByClassName(type.getName()).isPresent()) { + return COLLECTION; + } + return DEFAULT; + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/configuration/ColumnManagerSupplier.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/configuration/ColumnManagerSupplier.java new file mode 100644 index 000000000..82d573e45 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/configuration/ColumnManagerSupplier.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.configuration; + +import jakarta.data.exceptions.MappingException; +import org.eclipse.jnosql.communication.Settings; +import org.eclipse.jnosql.communication.column.ColumnConfiguration; +import org.eclipse.jnosql.communication.column.ColumnManager; +import org.eclipse.jnosql.communication.column.ColumnManagerFactory; +import org.eclipse.jnosql.mapping.core.config.MicroProfileSettings; +import org.eclipse.jnosql.mapping.reflection.Reflections; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Disposes; +import jakarta.enterprise.inject.Produces; +import jakarta.enterprise.inject.spi.CDI; +import java.util.Optional; +import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static org.eclipse.jnosql.mapping.core.config.MappingConfigurations.COLUMN_DATABASE; +import static org.eclipse.jnosql.mapping.core.config.MappingConfigurations.COLUMN_PROVIDER; + +@ApplicationScoped +class ColumnManagerSupplier implements Supplier { + + private static final Logger LOGGER = Logger.getLogger(ColumnManagerSupplier.class.getName()); + + @Override + @Produces + @ApplicationScoped + public ColumnManager get() { + Settings settings = MicroProfileSettings.INSTANCE; + + ColumnConfiguration configuration = settings.get(COLUMN_PROVIDER, Class.class) + .filter(ColumnConfiguration.class::isAssignableFrom) + .map(c -> { + final Reflections reflections = CDI.current().select(Reflections.class).get(); + return (ColumnConfiguration) reflections.newInstance(c); + }).orElseGet(ColumnConfiguration::getConfiguration); + + ColumnManagerFactory managerFactory = configuration.apply(settings); + + Optional database = settings.get(COLUMN_DATABASE, String.class); + String db = database.orElseThrow(() -> new MappingException("Please, inform the database filling up the property " + + COLUMN_DATABASE.get())); + ColumnManager manager = managerFactory.apply(db); + + LOGGER.log(Level.FINEST, "Starting a ColumnManager instance using Eclipse MicroProfile Config," + + " database name: " + db); + return manager; + } + + public void close(@Disposes ColumnManager manager) { + LOGGER.log(Level.FINEST, "Closing ColumnManager resource, database name: " + manager.name()); + manager.close(); + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/package-info.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/package-info.java new file mode 100644 index 000000000..cbaedcb8a --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/package-info.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ + +/** + * This package contains all objects to use a Column Family API. This API has focus in domain, in other words, + * ubiquitous language. A column family is a NoSQL object that contains columns of related data. It is a tuple (pair) + * that consists of a key-value pair, where the key is mapped to a value that is a set of columns. + * In analogy with relational databases, a column family is as a "table", each key-value pair being a "row". + * Each column is a tuple (triplet) consisting of a column name, a value, and a timestamp. In a relational + * database table, this data would be grouped together within a table with other non-related data. + * Ref: https://en.wikipedia.org/wiki/Column_family + * + */ +package org.eclipse.jnosql.mapping.column; \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/AbstractColumnRepository.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/AbstractColumnRepository.java new file mode 100644 index 000000000..4ee7693e1 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/AbstractColumnRepository.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.query; + +import jakarta.data.page.Page; +import jakarta.data.page.PageRequest; +; +import org.eclipse.jnosql.communication.column.ColumnQuery; +import org.eclipse.jnosql.mapping.core.NoSQLPage; +import org.eclipse.jnosql.mapping.column.JNoSQLColumnTemplate; +import org.eclipse.jnosql.mapping.column.MappingColumnQuery; +import org.eclipse.jnosql.mapping.core.query.AbstractRepository; +import org.eclipse.jnosql.mapping.metadata.EntityMetadata; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; + +/** + * The {@link org.eclipse.jnosql.mapping.NoSQLRepository} template method + */ +public abstract class AbstractColumnRepository extends AbstractRepository { + + protected abstract JNoSQLColumnTemplate template(); + + @Override + public long countBy() { + return template().count(type()); + } + + + @Override + public Page findAll(PageRequest pageRequest) { + Objects.requireNonNull(pageRequest, "pageRequest is required"); + EntityMetadata metadata = entityMetadata(); + ColumnQuery query = new MappingColumnQuery(pageRequest.sorts(), + pageRequest.size(), NoSQLPage.skip(pageRequest) + , null ,metadata.name()); + + List entities = template().select(query).toList(); + return NoSQLPage.of(entities, pageRequest); + } + + @Override + public Stream findAll() { + return template().findAll(type()); + } + + @Override + public void deleteAll() { + template().deleteAll(type()); + } + +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/AbstractColumnRepositoryProxy.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/AbstractColumnRepositoryProxy.java new file mode 100644 index 000000000..bc32d59fc --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/AbstractColumnRepositoryProxy.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.query; + +import org.eclipse.jnosql.communication.column.ColumnDeleteQuery; +import org.eclipse.jnosql.communication.column.ColumnQuery; +import org.eclipse.jnosql.mapping.core.repository.DynamicQueryMethodReturn; +import org.eclipse.jnosql.mapping.core.repository.RepositoryReflectionUtils; + +import java.lang.reflect.Method; +import java.util.Map; + +/** + * Template method to Repository proxy on column + * + * @param the entity type + * @param the K entity + */ +public abstract class AbstractColumnRepositoryProxy extends BaseColumnRepository { + + @Override + protected Object executeQuery(Object instance, Method method, Object[] params) { + Class type = entityMetadata().type(); + DynamicQueryMethodReturn methodReturn = DynamicQueryMethodReturn.builder() + .withArgs(params) + .withMethod(method) + .withTypeClass(type) + .withPrepareConverter(q -> template().prepare(q)) + .withQueryConverter(q -> template().query(q)).build(); + return methodReturn.execute(); + } + + @Override + protected Object executeDeleteByAll(Object instance, Method method, Object[] params) { + ColumnDeleteQuery deleteQuery = deleteQuery(method, params); + template().delete(deleteQuery); + return Void.class; + } + + @Override + protected Object executeFindAll(Object instance, Method method, Object[] params) { + Class type = entityMetadata().type(); + var query = ColumnQuery.select().from(entityMetadata().name()).build(); + return executeFindByQuery(method, params, type, updateQueryDynamically(params, query)); + } + + @Override + protected Object executeExistByQuery(Object instance, Method method, Object[] params) { + return executeExistsByQuery(query(method, params)); + } + + @Override + protected Object executeCountByQuery(Object instance, Method method, Object[] params) { + return executeCountByQuery(query(method, params)); + } + + @Override + protected Object executeFindByQuery(Object instance, Method method, Object[] params) { + Class type = entityMetadata().type(); + return executeFindByQuery(method, params, type, query(method, params)); + } + + @Override + protected Object executeParameterBased(Object instance, Method method, Object[] params) { + Class type = entityMetadata().type(); + Map parameters = RepositoryReflectionUtils.INSTANCE.getBy(method, params); + var query = ColumnParameterBasedQuery.INSTANCE.toQuery(parameters, entityMetadata()); + return executeFindByQuery(method, params, type, updateQueryDynamically(params, query)); + } + +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/BaseColumnRepository.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/BaseColumnRepository.java new file mode 100644 index 000000000..a32087074 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/BaseColumnRepository.java @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.query; + + +import jakarta.data.Limit; +import jakarta.data.Sort; +import jakarta.data.page.Page; +import jakarta.data.page.PageRequest; +import org.eclipse.jnosql.communication.Params; +import org.eclipse.jnosql.communication.column.Column; +import org.eclipse.jnosql.communication.column.ColumnCondition; +import org.eclipse.jnosql.communication.column.ColumnDeleteQuery; +import org.eclipse.jnosql.communication.column.ColumnDeleteQueryParams; +import org.eclipse.jnosql.communication.column.ColumnObserverParser; +import org.eclipse.jnosql.communication.column.ColumnQuery; +import org.eclipse.jnosql.communication.column.ColumnQueryParams; +import org.eclipse.jnosql.communication.column.DeleteQueryParser; +import org.eclipse.jnosql.communication.column.SelectQueryParser; +import org.eclipse.jnosql.communication.query.DeleteQuery; +import org.eclipse.jnosql.communication.query.SelectQuery; +import org.eclipse.jnosql.communication.query.method.DeleteMethodProvider; +import org.eclipse.jnosql.communication.query.method.SelectMethodProvider; +import org.eclipse.jnosql.mapping.column.MappingColumnQuery; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.NoSQLPage; +import org.eclipse.jnosql.mapping.column.JNoSQLColumnTemplate; +import org.eclipse.jnosql.mapping.core.query.AbstractRepositoryProxy; +import org.eclipse.jnosql.mapping.core.repository.SpecialParameters; +import org.eclipse.jnosql.mapping.metadata.EntityMetadata; +import org.eclipse.jnosql.mapping.core.repository.DynamicReturn; +import org.eclipse.jnosql.mapping.core.util.ParamsBinder; +import org.eclipse.jnosql.mapping.metadata.InheritanceMetadata; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Stream; + +/** + * A base abstract class for implementing column-oriented repositories in a Java NoSQL database. + * This class provides common functionality for executing CRUD operations using column queries. + * + * @param The type of entities managed by the repository. + * + */ +public abstract class BaseColumnRepository extends AbstractRepositoryProxy { + + private static final SelectQueryParser SELECT_PARSER = new SelectQueryParser(); + + private static final DeleteQueryParser DELETE_PARSER = new DeleteQueryParser(); + private static final Object[] EMPTY_PARAM = new Object[0]; + + /** + * Retrieves the Converters instance responsible for converting data types. + * + * @return The Converters instance. + */ + protected abstract Converters converters(); + + /** + * Retrieves the metadata information about the entity managed by the repository. + * + * @return The EntityMetadata instance. + */ + protected abstract EntityMetadata entityMetadata(); + + /** + * Retrieves the JNoSQLColumnTemplate instance for executing column queries. + * + * @return The JNoSQLColumnTemplate instance. + */ + protected abstract JNoSQLColumnTemplate template(); + + private ColumnObserverParser parser; + + private ParamsBinder paramsBinder; + + + protected ColumnQuery query(Method method, Object[] args) { + SelectMethodProvider provider = SelectMethodProvider.INSTANCE; + SelectQuery selectQuery = provider.apply(method, entityMetadata().name()); + ColumnQueryParams queryParams = SELECT_PARSER.apply(selectQuery, parser()); + ColumnQuery query = queryParams.query(); + Params params = queryParams.params(); + paramsBinder().bind(params, args(args), method); + return updateQueryDynamically(args(args), query); + } + + private static Object[] args(Object[] args) { + return args == null ? EMPTY_PARAM : args; + } + + protected ColumnDeleteQuery deleteQuery(Method method, Object[] args) { + DeleteMethodProvider deleteMethodFactory = DeleteMethodProvider.INSTANCE; + DeleteQuery deleteQuery = deleteMethodFactory.apply(method, entityMetadata().name()); + ColumnDeleteQueryParams queryParams = DELETE_PARSER.apply(deleteQuery, parser()); + ColumnDeleteQuery query = queryParams.query(); + Params params = queryParams.params(); + paramsBinder().bind(params, args(args), method); + return query; + } + + /** + * Retrieves the ColumnObserverParser instance for parsing column observations. + * + * @return The ColumnObserverParser instance. + */ + + protected ColumnObserverParser parser() { + if (parser == null) { + this.parser = new RepositoryColumnObserverParser(entityMetadata()); + } + return parser; + } + + /** + * Retrieves the ParamsBinder instance for binding parameters to queries. + * + * @return The ParamsBinder instance. + */ + protected ParamsBinder paramsBinder() { + if (Objects.isNull(paramsBinder)) { + this.paramsBinder = new ParamsBinder(entityMetadata(), converters()); + } + return paramsBinder; + } + + protected Object executeFindByQuery(Method method, Object[] args, Class typeClass, ColumnQuery query) { + DynamicReturn dynamicReturn = DynamicReturn.builder() + .withClassSource(typeClass) + .withMethodSource(method) + .withResult(() -> template().select(query)) + .withSingleResult(() -> template().singleResult(query)) + .withPagination(DynamicReturn.findPageRequest(args)) + .withStreamPagination(streamPagination(query)) + .withSingleResultPagination(getSingleResult(query)) + .withPage(getPage(query)) + .build(); + return dynamicReturn.execute(); + } + + private ColumnQuery includeInheritance(ColumnQuery query){ + EntityMetadata metadata = this.entityMetadata(); + if(metadata.inheritance().isPresent()){ + InheritanceMetadata inheritanceMetadata = metadata.inheritance().orElseThrow(); + if(!inheritanceMetadata.parent().equals(metadata.type())){ + ColumnCondition condition = ColumnCondition.eq(Column.of(inheritanceMetadata.discriminatorColumn(), + inheritanceMetadata.discriminatorValue())); + if(query.condition().isPresent()){ + ColumnCondition columnCondition = query.condition().orElseThrow(); + condition = condition.and(columnCondition); + } + return new MappingColumnQuery(query.sorts(), query.limit(), query.skip(), + condition, query.name()); + } + } + return query; + } + + protected Long executeCountByQuery(ColumnQuery query) { + return template().count(query); + } + + protected boolean executeExistsByQuery(ColumnQuery query) { + return template().exists(query); + } + + + + protected Function> getPage(ColumnQuery query) { + return p -> { + Stream entities = template().select(query); + return NoSQLPage.of(entities.toList(), p); + }; + } + + protected Function> getSingleResult(ColumnQuery query) { + return p -> template().singleResult(query); + } + + protected Function> streamPagination(ColumnQuery query) { + return p -> template().select(query); + } + + + protected ColumnQuery updateQueryDynamically(Object[] args, ColumnQuery query) { + ColumnQuery documentQuery = includeInheritance(query); + SpecialParameters special = DynamicReturn.findSpecialParameters(args); + + if (special.isEmpty()) { + return documentQuery; + } + Optional limit = special.limit(); + + if (special.hasOnlySort()) { + List> sorts = new ArrayList<>(); + sorts.addAll(documentQuery.sorts()); + sorts.addAll(special.sorts()); + long skip = limit.map(l -> l.startAt() - 1).orElse(documentQuery.skip()); + long max = limit.map(Limit::maxResults).orElse((int) documentQuery.limit()); + return new MappingColumnQuery(sorts, max, + skip, + documentQuery.condition().orElse(null), + documentQuery.name()); + } + + if (limit.isPresent()) { + long skip = limit.map(l -> l.startAt() - 1).orElse(documentQuery.skip()); + long max = limit.map(Limit::maxResults).orElse((int) documentQuery.limit()); + return new MappingColumnQuery(documentQuery.sorts(), max, + skip, + documentQuery.condition().orElse(null), + documentQuery.name()); + } + + return special.pageRequest().map(p -> { + long size = p.size(); + long skip = NoSQLPage.skip(p); + List> sorts = documentQuery.sorts(); + if (!special.sorts().isEmpty()) { + sorts = new ArrayList<>(documentQuery.sorts()); + sorts.addAll(special.sorts()); + } + return new MappingColumnQuery(sorts, size, skip, + documentQuery.condition().orElse(null), documentQuery.name()); + }).orElse(documentQuery); + } + + +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/ColumnParameterBasedQuery.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/ColumnParameterBasedQuery.java new file mode 100644 index 000000000..3315b71fa --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/ColumnParameterBasedQuery.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.query; + +import jakarta.enterprise.inject.spi.CDI; +import org.eclipse.jnosql.communication.column.ColumnCondition; +import org.eclipse.jnosql.communication.column.ColumnQuery; +import org.eclipse.jnosql.mapping.column.MappingColumnQuery; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.metadata.EntityMetadata; +import org.eclipse.jnosql.mapping.metadata.FieldMetadata; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.IntFunction; + +import static org.eclipse.jnosql.mapping.core.util.ConverterUtil.getValue; + +/** + * The ColumnParameterBasedQuery class is responsible for generating Column queries based on a set of parameters. + * It leverages the provided parameters, PageRequest information, and entity metadata to construct a ColumnQuery object + * tailored for querying a specific entity's columns. + */ +public enum ColumnParameterBasedQuery { + + + INSTANCE; + private static final IntFunction TO_ARRAY = ColumnCondition[]::new; + + /** + * Constructs a ColumnQuery based on the provided parameters, PageRequest information, and entity metadata. + * + * @param params The map of parameters used for filtering columns. + * @param entityMetadata Metadata describing the structure of the entity. + * @return A ColumnQuery instance tailored for the specified entity. + */ + public ColumnQuery toQuery(Map params, EntityMetadata entityMetadata) { + var convert = CDI.current().select(Converters.class).get(); + List conditions = new ArrayList<>(); + for (Map.Entry entry : params.entrySet()) { + conditions.add(getCondition(convert, entityMetadata, entry)); + } + + var columnCondition = columnCondition(conditions); + var columnFamily = entityMetadata.name(); + return new MappingColumnQuery(Collections.emptyList(), 0L, 0L, columnCondition, columnFamily); + } + + private ColumnCondition columnCondition(List conditions) { + if (conditions.isEmpty()) { + return null; + } else if (conditions.size() == 1) { + return conditions.get(0); + } + return ColumnCondition.and(conditions.toArray(TO_ARRAY)); + } + + private ColumnCondition getCondition(Converters convert, EntityMetadata entityMetadata, Map.Entry entry) { + var name = entityMetadata.fieldMapping(entry.getKey()) + .map(FieldMetadata::name) + .orElse(entry.getKey()); + var value = getValue(entry.getValue(), entityMetadata, entry.getKey(), convert); + return ColumnCondition.eq(name, value); + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/ColumnRepositoryProducer.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/ColumnRepositoryProducer.java new file mode 100644 index 000000000..72427c38f --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/ColumnRepositoryProducer.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.query; + +import jakarta.data.repository.BasicRepository; +import jakarta.nosql.column.ColumnTemplate; +import org.eclipse.jnosql.communication.column.ColumnManager; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.column.ColumnTemplateProducer; +import org.eclipse.jnosql.mapping.column.JNoSQLColumnTemplate; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import java.lang.reflect.Proxy; +import java.util.Objects; + +/** + * The producer of Repository + */ +@ApplicationScoped +public class ColumnRepositoryProducer { + + @Inject + private EntitiesMetadata entities; + + @Inject + private Converters converters; + + @Inject + private ColumnTemplateProducer producer; + + /** + * Produces a Repository class from repository class and {@link ColumnManager} + * + * @param repositoryClass the repository class + * @param manager the manager + * @param the entity of repository + * @param the K of the entity + * @param the repository type + * @return a Repository interface + * @throws NullPointerException when there is null parameter + */ + public > R get(Class repositoryClass, ColumnManager manager) { + Objects.requireNonNull(repositoryClass, "repository class is required"); + Objects.requireNonNull(manager, "manager class is required"); + JNoSQLColumnTemplate template = producer.apply(manager); + return get(repositoryClass, template); + } + + /** + * Produces a Repository class from repository class and {@link ColumnTemplate} + * + * @param repositoryClass the repository class + * @param template the template + * @param the entity of repository + * @param the K of the entity + * @param the repository type + * @return a Repository interface + * @throws NullPointerException when there is null parameter + */ + public > R get(Class repositoryClass, JNoSQLColumnTemplate template) { + Objects.requireNonNull(repositoryClass, "repository class is required"); + Objects.requireNonNull(template, "template class is required"); + + ColumnRepositoryProxy handler = new ColumnRepositoryProxy<>(template, + entities, repositoryClass, converters); + return (R) Proxy.newProxyInstance(repositoryClass.getClassLoader(), + new Class[]{repositoryClass}, + handler); + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/ColumnRepositoryProxy.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/ColumnRepositoryProxy.java new file mode 100644 index 000000000..f3fc09448 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/ColumnRepositoryProxy.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.query; + + +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.column.JNoSQLColumnTemplate; +import org.eclipse.jnosql.mapping.core.query.AbstractRepository; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; +import org.eclipse.jnosql.mapping.metadata.EntityMetadata; + +import java.lang.reflect.ParameterizedType; +import java.util.Objects; + + +/** + * Proxy handler to generate {@link org.eclipse.jnosql.mapping.NoSQLRepository} for column-based repositories. + * + * @param The entity type managed by the repository. + * @param The key type used for column-based operations. + */ +public class ColumnRepositoryProxy extends AbstractColumnRepositoryProxy { + + private final JNoSQLColumnTemplate template; + + private final ColumnRepository repository; + + private final EntityMetadata entityMetadata; + + private final Converters converters; + + private final Class repositoryType; + + + ColumnRepositoryProxy(JNoSQLColumnTemplate template, EntitiesMetadata entities, Class repositoryType, + Converters converters) { + this.template = template; + Class typeClass = (Class) ((ParameterizedType) repositoryType.getGenericInterfaces()[0]) + .getActualTypeArguments()[0]; + this.entityMetadata = entities.get(typeClass); + this.repository = new ColumnRepository<>(template, entityMetadata); + this.converters = converters; + this.repositoryType = repositoryType; + } + + @Override + protected AbstractRepository repository() { + return repository; + } + + @Override + protected EntityMetadata entityMetadata() { + return entityMetadata; + } + + @Override + protected JNoSQLColumnTemplate template() { + return template; + } + + @Override + protected Converters converters() { + return converters; + } + + @Override + protected Class repositoryType() { + return repositoryType; + } + + + /** + * Repository implementation for column-based repositories. + * + * @param The entity type managed by the repository. + * @param The key type used for column-based operations. + */ + public static class ColumnRepository extends AbstractColumnRepository { + + private final JNoSQLColumnTemplate template; + + private final EntityMetadata entityMetadata; + + ColumnRepository(JNoSQLColumnTemplate template, EntityMetadata entityMetadata) { + this.template = template; + this.entityMetadata = entityMetadata; + } + + @Override + protected JNoSQLColumnTemplate template() { + return template; + } + + @Override + protected EntityMetadata entityMetadata() { + return entityMetadata; + } + + /** + * Creates a new instance of ColumnRepository. + * + * @param The entity type managed by the repository. + * @param The key type used for column-based operations. + * @param template The JNoSQLColumnTemplate used for column database operations. Must not be {@code null}. + * @param metadata The metadata of the entity. Must not be {@code null}. + * @return A new instance of ColumnRepository. + * @throws NullPointerException If either the template or metadata is {@code null}. + */ + public static ColumnRepository of(JNoSQLColumnTemplate template, EntityMetadata metadata) { + Objects.requireNonNull(template,"template is required"); + Objects.requireNonNull(metadata,"metadata is required"); + return new ColumnRepository<>(template, metadata); + } + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/ColumnTokenProcessor.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/ColumnTokenProcessor.java new file mode 100644 index 000000000..2975ce245 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/ColumnTokenProcessor.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.query; + +import org.eclipse.jnosql.communication.column.ColumnCondition; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.metadata.EntityMetadata; + +interface ColumnTokenProcessor { + + ColumnCondition process(String token, + int index, + Object[] args, + String methodName, + EntityMetadata mapping, + Converters converters); +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/DynamicQuery.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/DynamicQuery.java new file mode 100644 index 000000000..f0d3e2cdd --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/DynamicQuery.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.query; + +import jakarta.data.Limit; +import jakarta.data.Sort; +import org.eclipse.jnosql.communication.column.ColumnQuery; +import org.eclipse.jnosql.mapping.core.NoSQLPage; +import org.eclipse.jnosql.mapping.column.MappingColumnQuery; +import org.eclipse.jnosql.mapping.core.repository.DynamicReturn; +import org.eclipse.jnosql.mapping.core.repository.SpecialParameters; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Supplier; + +/** + * A query converter to update dynamic query to a {@link ColumnQuery} + */ +public class DynamicQuery implements Supplier { + + private final SpecialParameters special; + private final ColumnQuery query; + + private DynamicQuery(SpecialParameters special, ColumnQuery query) { + this.special = special; + this.query = query; + } + + @Override + public ColumnQuery get() { + if (special.isEmpty()) { + return query; + } + Optional limit = special.limit(); + if (special.hasOnlySort()) { + List> sorts = new ArrayList<>(); + sorts.addAll(query.sorts()); + sorts.addAll(special.sorts()); + long skip = limit.map(l -> l.startAt() - 1).orElse(query.skip()); + long max = limit.map(Limit::maxResults).orElse((int) query.limit()); + return new MappingColumnQuery(sorts, max, + skip, + query.condition().orElse(null), + query.name()); + } + + if (limit.isPresent()) { + long skip = limit.map(l -> l.startAt() - 1).orElse(query.skip()); + long max = limit.map(Limit::maxResults).orElse((int) query.limit()); + List> sorts = query.sorts(); + if (!special.sorts().isEmpty()) { + sorts = new ArrayList<>(query.sorts()); + sorts.addAll(special.sorts()); + } + return new MappingColumnQuery(sorts, max, + skip, + query.condition().orElse(null), + query.name()); + } + + return special.pageRequest().map(p -> { + long size = p.size(); + long skip = NoSQLPage.skip(p); + List> sorts = query.sorts(); + if (!special.sorts().isEmpty()) { + sorts = new ArrayList<>(query.sorts()); + sorts.addAll(special.sorts()); + } + return new MappingColumnQuery(sorts, size, skip, + query.condition().orElse(null), query.name()); + }).orElse(query); + } + + /** + * Creates a {@link DynamicQuery} instance + * @param args the method parameters + * @param query the column query + * @return the {@link DynamicQuery} instance + * @throws NullPointerException when either args or query are null + */ + public static DynamicQuery of(Object[] args, ColumnQuery query) { + Objects.requireNonNull(args, "args is required"); + Objects.requireNonNull(query, "query is required"); + return new DynamicQuery(DynamicReturn.findSpecialParameters(args), query); + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/RepositoryColumnBean.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/RepositoryColumnBean.java new file mode 100644 index 000000000..fad9c3152 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/RepositoryColumnBean.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.query; + +import jakarta.data.repository.DataRepository; +import jakarta.enterprise.context.spi.CreationalContext; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.DatabaseQualifier; +import org.eclipse.jnosql.mapping.DatabaseType; +import org.eclipse.jnosql.mapping.column.JNoSQLColumnTemplate; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; +import org.eclipse.jnosql.mapping.core.spi.AbstractBean; +import org.eclipse.jnosql.mapping.core.util.AnnotationLiteralUtil; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Proxy; +import java.lang.reflect.Type; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * JNoSQL discoveryBean to CDI extension to register Repository + */ +public class RepositoryColumnBean> extends AbstractBean { + + private final Class type; + + private final Set types; + + private final String provider; + + private final Set qualifiers; + + /** + * Constructor + * + * @param type the tye + * @param provider the provider name, that must be a + */ + @SuppressWarnings("unchecked") + public RepositoryColumnBean(Class type, String provider) { + this.type = (Class) type; + this.types = Collections.singleton(type); + this.provider = provider; + if (provider.isEmpty()) { + this.qualifiers = new HashSet<>(); + qualifiers.add(DatabaseQualifier.ofColumn()); + qualifiers.add(AnnotationLiteralUtil.DEFAULT_ANNOTATION); + qualifiers.add(AnnotationLiteralUtil.ANY_ANNOTATION); + } else { + this.qualifiers = Collections.singleton(DatabaseQualifier.ofColumn(provider)); + } + } + + @Override + public Class getBeanClass() { + return type; + } + + @Override + public T create(CreationalContext creationalContext) { + EntitiesMetadata entities = getInstance(EntitiesMetadata.class); + JNoSQLColumnTemplate template = provider.isEmpty() ? getInstance(JNoSQLColumnTemplate.class) : + getInstance(JNoSQLColumnTemplate.class, DatabaseQualifier.ofColumn(provider)); + Converters converters = getInstance(Converters.class); + + ColumnRepositoryProxy handler = new ColumnRepositoryProxy(template, + entities, type, converters); + return (T) Proxy.newProxyInstance(type.getClassLoader(), + new Class[]{type}, + handler); + } + + @Override + public Set getTypes() { + return types; + } + + @Override + public Set getQualifiers() { + return qualifiers; + } + + @Override + public String getId() { + return type.getName() + '@' + DatabaseType.COLUMN + "-" + provider; + } + +} \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/RepositoryColumnObserverParser.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/RepositoryColumnObserverParser.java new file mode 100644 index 000000000..10913e673 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/RepositoryColumnObserverParser.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.query; + +import org.eclipse.jnosql.communication.column.ColumnObserverParser; +import org.eclipse.jnosql.mapping.metadata.EntityMetadata; +import org.eclipse.jnosql.mapping.core.repository.RepositoryObserverParser; + +import java.util.Objects; + +/** + * The {@link ColumnObserverParser} to {@link RepositoryObserverParser} + */ +public final class RepositoryColumnObserverParser implements ColumnObserverParser { + + private final RepositoryObserverParser parser; + + RepositoryColumnObserverParser(EntityMetadata entityMetadata) { + this.parser = RepositoryObserverParser.of(entityMetadata); + } + + @Override + public String fireEntity(String entity) { + return parser.name(); + } + + @Override + public String fireField(String entity, String field) { + return parser.field(field); + } + + /** + * @return RepositoryColumnObserverParser + * throws NullPointerException if entityMetadata is null + */ + public static ColumnObserverParser of(EntityMetadata entityMetadata) { + Objects.requireNonNull(entityMetadata, "entityMetadata is required"); + return new RepositoryColumnObserverParser(entityMetadata); + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/package-info.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/package-info.java new file mode 100644 index 000000000..baf5c4779 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/query/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ + +/** + * This package has the implementation to dynamic query to Repository + * on Column database. + */ +package org.eclipse.jnosql.mapping.column.query; \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/spi/ColumnExtension.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/spi/ColumnExtension.java new file mode 100644 index 000000000..e994b162c --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/spi/ColumnExtension.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.spi; + + +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.inject.spi.AfterBeanDiscovery; +import jakarta.enterprise.inject.spi.Extension; +import jakarta.enterprise.inject.spi.ProcessProducer; +import org.eclipse.jnosql.communication.column.ColumnManager; +import org.eclipse.jnosql.mapping.DatabaseMetadata; +import org.eclipse.jnosql.mapping.Databases; +import org.eclipse.jnosql.mapping.column.query.RepositoryColumnBean; +import org.eclipse.jnosql.mapping.metadata.ClassScanner; + +import java.util.HashSet; +import java.util.Set; +import java.util.logging.Logger; + +import static org.eclipse.jnosql.mapping.DatabaseType.COLUMN; + + +/** + * Extension to start up the ColumnTemplate and Repository + * from the {@link org.eclipse.jnosql.mapping.Database} qualifier + */ +public class ColumnExtension implements Extension { + + private static final Logger LOGGER = Logger.getLogger(ColumnExtension.class.getName()); + + private final Set databases = new HashSet<>(); + + + void observes(@Observes final ProcessProducer pp) { + Databases.addDatabase(pp, COLUMN, databases); + } + + void onAfterBeanDiscovery(@Observes final AfterBeanDiscovery afterBeanDiscovery) { + + ClassScanner scanner = ClassScanner.load(); + Set> crudTypes = scanner.repositoriesStandard(); + LOGGER.info(String.format("Processing Column extension: %d databases crud %d found", + databases.size(), crudTypes.size())); + LOGGER.info("Processing repositories as a Column implementation: " + crudTypes); + + databases.forEach(type -> { + final TemplateBean bean = new TemplateBean(type.getProvider()); + afterBeanDiscovery.addBean(bean); + }); + + crudTypes.forEach(type -> { + if (!databases.contains(DatabaseMetadata.DEFAULT_COLUMN)) { + afterBeanDiscovery.addBean(new RepositoryColumnBean<>(type, "")); + } + databases.forEach(database -> afterBeanDiscovery + .addBean(new RepositoryColumnBean<>(type, database.getProvider()))); + }); + + } + +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/spi/TemplateBean.java b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/spi/TemplateBean.java new file mode 100644 index 000000000..03d2664d9 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/java/org/eclipse/jnosql/mapping/column/spi/TemplateBean.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.spi; + + +import jakarta.enterprise.context.spi.CreationalContext; +import jakarta.nosql.column.ColumnTemplate; +import org.eclipse.jnosql.communication.column.ColumnManager; +import org.eclipse.jnosql.mapping.DatabaseQualifier; +import org.eclipse.jnosql.mapping.DatabaseType; +import org.eclipse.jnosql.mapping.column.ColumnTemplateProducer; +import org.eclipse.jnosql.mapping.column.JNoSQLColumnTemplate; +import org.eclipse.jnosql.mapping.core.spi.AbstractBean; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.Collections; +import java.util.Set; + +class TemplateBean extends AbstractBean { + + private static final Set TYPES = Set.of(ColumnTemplate.class, JNoSQLColumnTemplate.class); + private final String provider; + + private final Set qualifiers; + + /** + * Constructor + * + * @param provider the provider name, that must be a + */ + public TemplateBean(String provider) { + this.provider = provider; + this.qualifiers = Collections.singleton(DatabaseQualifier.ofColumn(provider)); + } + + @Override + public Class getBeanClass() { + return ColumnTemplate.class; + } + + + @Override + public JNoSQLColumnTemplate create(CreationalContext context) { + + ColumnTemplateProducer producer = getInstance(ColumnTemplateProducer.class); + ColumnManager manager = getColumnManager(); + return producer.apply(manager); + } + + private ColumnManager getColumnManager() { + return getInstance(ColumnManager.class, DatabaseQualifier.ofColumn(provider)); + } + + @Override + public Set getTypes() { + return TYPES; + } + + @Override + public Set getQualifiers() { + return qualifiers; + } + + @Override + public String getId() { + return ColumnTemplate.class.getName() + DatabaseType.COLUMN + "-" + provider; + } + +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/resources/META-INF/beans.xml b/jnosql-mapping/jnosql-mapping-semistructured/src/main/resources/META-INF/beans.xml new file mode 100644 index 000000000..0340ecdbc --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/resources/META-INF/beans.xml @@ -0,0 +1,21 @@ + + + + \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension b/jnosql-mapping/jnosql-mapping-semistructured/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension new file mode 100644 index 000000000..a2144ec8a --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension @@ -0,0 +1,15 @@ +# +# Copyright (c) 2022 Contributors to the Eclipse Foundation +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# and Apache License v2.0 which accompanies this distribution. +# The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html +# and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. +# +# You may elect to redistribute this code under either of these licenses. +# +# Contributors: +# +# Otavio Santana +# +org.eclipse.jnosql.mapping.column.spi.ColumnExtension \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/main/resources/META-INF/services/jakarta.nosql.mapping.column.ColumnQueryPagination$ColumnQueryPaginationProvider b/jnosql-mapping/jnosql-mapping-semistructured/src/main/resources/META-INF/services/jakarta.nosql.mapping.column.ColumnQueryPagination$ColumnQueryPaginationProvider new file mode 100644 index 000000000..c55293d45 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/main/resources/META-INF/services/jakarta.nosql.mapping.column.ColumnQueryPagination$ColumnQueryPaginationProvider @@ -0,0 +1 @@ +org.eclipse.jnosql.mapping.column.DefaultColumnQueryPaginationProvider \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/ColumnEntityConverterConstructorTest.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/ColumnEntityConverterConstructorTest.java new file mode 100644 index 000000000..e3a5c9d06 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/ColumnEntityConverterConstructorTest.java @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + +import jakarta.inject.Inject; +import org.assertj.core.api.SoftAssertions; +import org.eclipse.jnosql.communication.TypeReference; +import org.eclipse.jnosql.communication.column.Column; +import org.eclipse.jnosql.communication.column.ColumnEntity; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.column.entities.Animal; +import org.eclipse.jnosql.mapping.column.entities.Book; +import org.eclipse.jnosql.mapping.column.entities.BookRelease; +import org.eclipse.jnosql.mapping.column.entities.Money; +import org.eclipse.jnosql.mapping.column.entities.constructor.BookUser; +import org.eclipse.jnosql.mapping.column.entities.constructor.Computer; +import org.eclipse.jnosql.mapping.column.entities.constructor.PetOwner; +import org.eclipse.jnosql.mapping.column.entities.constructor.SuperHero; +import org.eclipse.jnosql.mapping.column.spi.ColumnExtension; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; +import org.junit.jupiter.api.Test; + +import java.time.Year; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, ColumnEntityConverter.class}) +@AddPackages(MockProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, ColumnExtension.class}) +class ColumnEntityConverterConstructorTest { + + @Inject + private ColumnEntityConverter converter; + + @Test + void shouldConverterEntityComputer() { + ColumnEntity communication = ColumnEntity.of("Computer"); + communication.add("_id", 10L); + communication.add("name", "Dell"); + communication.add("age", 2020); + communication.add("model", "Dell 2020"); + communication.add("price", "USD 20"); + Computer computer = this.converter.toEntity(communication); + assertNotNull(computer); + assertEquals(10L, computer.getId()); + assertEquals("Dell", computer.getName()); + assertEquals(2020, computer.getAge()); + assertEquals("Dell 2020", computer.getModel()); + assertEquals(Money.parse("USD 20"), computer.getPrice()); + } + + @Test + void shouldConvertComputerToCommunication() { + Computer computer = new Computer(10L, "Dell", 2020, "Dell 2020", + Money.parse("USD 20")); + ColumnEntity communication = this.converter.toColumn(computer); + assertNotNull(communication); + + assertEquals(computer.getId(), communication.find("_id", Long.class).get()); + assertEquals(computer.getName(), communication.find("name", String.class).get()); + assertEquals(computer.getAge(), communication.find("age", int.class).get()); + assertEquals(computer.getModel(), communication.find("model", String.class).get()); + assertEquals(computer.getPrice().toString(), communication.find("price", String.class).get()); + } + + @Test + void shouldConvertPetOwner() { + ColumnEntity communication = ColumnEntity.of("PetOwner"); + communication.add("_id", 10L); + communication.add("name", "Otavio"); + communication.add("animal", Arrays.asList(Column.of("_id", 23) + , Column.of("name", "Ada"))); + + PetOwner petOwner = this.converter.toEntity(communication); + assertNotNull(petOwner); + assertEquals(10L, petOwner.getId()); + assertEquals("Otavio", petOwner.getName()); + Animal animal = petOwner.getAnimal(); + assertEquals(23L, animal.getId()); + assertEquals("Ada", animal.getName()); + } + + @Test + void shouldConvertPetOwnerCommunication() { + Animal ada = new Animal("Ada"); + PetOwner petOwner = new PetOwner(10L, "Poliana", ada); + ColumnEntity communication = this.converter.toColumn(petOwner); + assertNotNull(communication); + assertEquals(10L, communication.find("_id", Long.class).get()); + assertEquals("Poliana", communication.find("name", String.class).get()); + List columns = communication.find("animal", new TypeReference>() {}) + .get(); + assertThat(columns).contains(Column.of("name", "Ada")); + } + + @Test + void shouldConvertBookUser() { + ColumnEntity communication = ColumnEntity.of("BookUser"); + communication.add("_id", "otaviojava"); + communication.add("native_name", "Otavio Santana"); + List> columns = new ArrayList<>(); + columns.add(Arrays.asList(Column.of("_id", 10), Column.of("name", "Effective Java"))); + columns.add(Arrays.asList(Column.of("_id", 12), Column.of("name", "Clean Code"))); + communication.add("books", columns); + + BookUser bookUser = this.converter.toEntity(communication); + assertNotNull(bookUser); + assertEquals("Otavio Santana", bookUser.getName()); + assertEquals("otaviojava", bookUser.getNickname()); + assertEquals(2, bookUser.getBooks().size()); + List names = bookUser.getBooks().stream().map(Book::getName).toList(); + assertThat(names).contains("Effective Java", "Clean Code"); + + } + + @Test + void shouldConverterFieldsOnEntityComputer() { + ColumnEntity communication = ColumnEntity.of("Computer"); + communication.add("_id", "10"); + communication.add("name", "Dell"); + communication.add("age", "2020"); + communication.add("model", "Dell 2020"); + communication.add("price", "USD 20"); + Computer computer = this.converter.toEntity(communication); + assertNotNull(computer); + assertEquals(10L, computer.getId()); + assertEquals("Dell", computer.getName()); + assertEquals(2020, computer.getAge()); + assertEquals("Dell 2020", computer.getModel()); + assertEquals(Money.parse("USD 20"), computer.getPrice()); + } + + @Test + void shouldConverterEntityBookRelease() { + ColumnEntity communication = ColumnEntity.of("BookRelease"); + communication.add("isbn", "9780132345286"); + communication.add("title", "Effective Java"); + communication.add("author", "Joshua Bloch"); + communication.add("year", Year.of(2001)); + BookRelease book = this.converter.toEntity(communication); + assertNotNull(book); + assertEquals("9780132345286", book.getIsbn()); + assertEquals("Effective Java", book.getTitle()); + assertEquals("Joshua Bloch", book.getAuthor()); + assertEquals(Year.of(2001), book.getYear()); + } + + @Test + void shouldConverterEntityBookReleaseOnStringYear() { + ColumnEntity communication = ColumnEntity.of("BookRelease"); + communication.add("isbn", "9780132345286"); + communication.add("title", "Effective Java"); + communication.add("author", "Joshua Bloch"); + communication.add("year", "2001"); + BookRelease book = this.converter.toEntity(communication); + assertNotNull(book); + assertEquals("9780132345286", book.getIsbn()); + assertEquals("Effective Java", book.getTitle()); + assertEquals("Joshua Bloch", book.getAuthor()); + assertEquals(Year.of(2001), book.getYear()); + } + + @Test + void shouldConvertHero() { + ColumnEntity communication = ColumnEntity.of("SuperHero"); + communication.add("_id", "10L"); + communication.add("name", "Otavio"); + communication.add("powers", List.of("speed", "strength")); + + SuperHero hero = this.converter.toEntity(communication); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(hero.id()).isEqualTo("10L"); + softly.assertThat(hero.name()).isEqualTo("Otavio"); + softly.assertThat(hero.powers()).contains("speed", "strength"); + }); + } + +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/ColumnEntityConverterInheritanceTest.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/ColumnEntityConverterInheritanceTest.java new file mode 100644 index 000000000..e4f3dbe01 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/ColumnEntityConverterInheritanceTest.java @@ -0,0 +1,432 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + +import jakarta.data.exceptions.MappingException; +import jakarta.inject.Inject; +import org.eclipse.jnosql.communication.TypeReference; +import org.eclipse.jnosql.communication.column.Column; +import org.eclipse.jnosql.communication.column.ColumnEntity; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.column.entities.inheritance.EmailNotification; +import org.eclipse.jnosql.mapping.column.entities.inheritance.LargeProject; +import org.eclipse.jnosql.mapping.column.entities.inheritance.Notification; +import org.eclipse.jnosql.mapping.column.entities.inheritance.NotificationReader; +import org.eclipse.jnosql.mapping.column.entities.inheritance.Project; +import org.eclipse.jnosql.mapping.column.entities.inheritance.ProjectManager; +import org.eclipse.jnosql.mapping.column.entities.inheritance.SmallProject; +import org.eclipse.jnosql.mapping.column.entities.inheritance.SmsNotification; +import org.eclipse.jnosql.mapping.column.entities.inheritance.SocialMediaNotification; +import org.eclipse.jnosql.mapping.column.spi.ColumnExtension; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, ColumnEntityConverter.class}) +@AddPackages(MockProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, ColumnExtension.class}) +class ColumnEntityConverterInheritanceTest { + + @Inject + private ColumnEntityConverter converter; + + @Test + void shouldConvertProjectToSmallProject() { + ColumnEntity entity = ColumnEntity.of("Project"); + entity.add("_id", "Small Project"); + entity.add("investor", "Otavio Santana"); + entity.add("size", "Small"); + Project project = this.converter.toEntity(entity); + assertEquals("Small Project", project.getName()); + assertEquals(SmallProject.class, project.getClass()); + SmallProject smallProject = SmallProject.class.cast(project); + assertEquals("Otavio Santana", smallProject.getInvestor()); + } + + @Test + void shouldConvertProjectToLargeProject() { + ColumnEntity entity = ColumnEntity.of("Project"); + entity.add("_id", "Large Project"); + entity.add("budget", BigDecimal.TEN); + entity.add("size", "Large"); + Project project = this.converter.toEntity(entity); + assertEquals("Large Project", project.getName()); + assertEquals(LargeProject.class, project.getClass()); + LargeProject smallProject = LargeProject.class.cast(project); + assertEquals(BigDecimal.TEN, smallProject.getBudget()); + } + + @Test + void shouldConvertLargeProjectToCommunicationEntity() { + LargeProject project = new LargeProject(); + project.setName("Large Project"); + project.setBudget(BigDecimal.TEN); + ColumnEntity entity = this.converter.toColumn(project); + assertNotNull(entity); + assertEquals("Project", entity.name()); + assertEquals(project.getName(), entity.find("_id", String.class).get()); + assertEquals(project.getBudget(), entity.find("budget", BigDecimal.class).get()); + assertEquals("Large", entity.find("size", String.class).get()); + } + + @Test + void shouldConvertSmallProjectToCommunicationEntity() { + SmallProject project = new SmallProject(); + project.setName("Small Project"); + project.setInvestor("Otavio Santana"); + ColumnEntity entity = this.converter.toColumn(project); + assertNotNull(entity); + assertEquals("Project", entity.name()); + assertEquals(project.getName(), entity.find("_id", String.class).get()); + assertEquals(project.getInvestor(), entity.find("investor", String.class).get()); + assertEquals("Small", entity.find("size", String.class).get()); + } + + @Test + void shouldConvertProject() { + ColumnEntity entity = ColumnEntity.of("Project"); + entity.add("_id", "Project"); + entity.add("size", "Project"); + Project project = this.converter.toEntity(entity); + assertEquals("Project", project.getName()); + } + + @Test + void shouldConvertProjectToCommunicationEntity() { + Project project = new Project(); + project.setName("Large Project"); + ColumnEntity entity = this.converter.toColumn(project); + assertNotNull(entity); + assertEquals("Project", entity.name()); + assertEquals(project.getName(), entity.find("_id", String.class).get()); + assertEquals("Project", entity.find("size", String.class).get()); + } + + @Test + void shouldConvertColumnEntityToSocialMedia(){ + LocalDate date = LocalDate.now(); + ColumnEntity entity = ColumnEntity.of("Notification"); + entity.add("_id", 100L); + entity.add("name", "Social Media"); + entity.add("nickname", "otaviojava"); + entity.add("createdOn",date); + entity.add("dtype", SocialMediaNotification.class.getSimpleName()); + SocialMediaNotification notification = this.converter.toEntity(entity); + assertEquals(100L, notification.getId()); + assertEquals("Social Media", notification.getName()); + assertEquals("otaviojava", notification.getNickname()); + assertEquals(date, notification.getCreatedOn()); + } + + @Test + void shouldConvertColumnEntityToSms(){ + LocalDate date = LocalDate.now(); + ColumnEntity entity = ColumnEntity.of("Notification"); + entity.add("_id", 100L); + entity.add("name", "SMS Notification"); + entity.add("phone", "+351987654123"); + entity.add("createdOn", date); + entity.add("dtype", "SMS"); + SmsNotification notification = this.converter.toEntity(entity); + Assertions.assertEquals(100L, notification.getId()); + Assertions.assertEquals("SMS Notification", notification.getName()); + Assertions.assertEquals("+351987654123", notification.getPhone()); + assertEquals(date, notification.getCreatedOn()); + } + + @Test + void shouldConvertColumnEntityToEmail(){ + LocalDate date = LocalDate.now(); + ColumnEntity entity = ColumnEntity.of("Notification"); + entity.add("_id", 100L); + entity.add("name", "Email Notification"); + entity.add("email", "otavio@otavio.test"); + entity.add("createdOn", date); + entity.add("dtype", "Email"); + EmailNotification notification = this.converter.toEntity(entity); + Assertions.assertEquals(100L, notification.getId()); + Assertions.assertEquals("Email Notification", notification.getName()); + Assertions.assertEquals("otavio@otavio.test", notification.getEmail()); + assertEquals(date, notification.getCreatedOn()); + } + + @Test + void shouldConvertSocialMediaToCommunicationEntity(){ + SocialMediaNotification notification = new SocialMediaNotification(); + notification.setId(100L); + notification.setName("Social Media"); + notification.setCreatedOn(LocalDate.now()); + notification.setNickname("otaviojava"); + ColumnEntity entity = this.converter.toColumn(notification); + assertNotNull(entity); + assertEquals("Notification", entity.name()); + assertEquals(notification.getId(), entity.find("_id", Long.class).get()); + assertEquals(notification.getName(), entity.find("name", String.class).get()); + assertEquals(notification.getNickname(), entity.find("nickname", String.class).get()); + assertEquals(notification.getCreatedOn(), entity.find("createdOn", LocalDate.class).get()); + } + + @Test + void shouldConvertSmsToCommunicationEntity(){ + SmsNotification notification = new SmsNotification(); + notification.setId(100L); + notification.setName("SMS"); + notification.setCreatedOn(LocalDate.now()); + notification.setPhone("+351123456987"); + ColumnEntity entity = this.converter.toColumn(notification); + assertNotNull(entity); + assertEquals("Notification", entity.name()); + assertEquals(notification.getId(), entity.find("_id", Long.class).get()); + assertEquals(notification.getName(), entity.find("name", String.class).get()); + assertEquals(notification.getPhone(), entity.find("phone", String.class).get()); + assertEquals(notification.getCreatedOn(), entity.find("createdOn", LocalDate.class).get()); + } + + @Test + void shouldConvertEmailToCommunicationEntity(){ + EmailNotification notification = new EmailNotification(); + notification.setId(100L); + notification.setName("Email Media"); + notification.setCreatedOn(LocalDate.now()); + notification.setEmail("otavio@otavio.test.com"); + ColumnEntity entity = this.converter.toColumn(notification); + assertNotNull(entity); + assertEquals("Notification", entity.name()); + assertEquals(notification.getId(), entity.find("_id", Long.class).get()); + assertEquals(notification.getName(), entity.find("name", String.class).get()); + assertEquals(notification.getEmail(), entity.find("email", String.class).get()); + assertEquals(notification.getCreatedOn(), entity.find("createdOn", LocalDate.class).get()); + } + + @Test + void shouldReturnErrorWhenConvertMissingColumn(){ + LocalDate date = LocalDate.now(); + ColumnEntity entity = ColumnEntity.of("Notification"); + entity.add("_id", 100L); + entity.add("name", "SMS Notification"); + entity.add("phone", "+351987654123"); + entity.add("createdOn", date); + Assertions.assertThrows(MappingException.class, ()-> this.converter.toEntity(entity)); + } + + @Test + void shouldReturnErrorWhenMismatchField() { + LocalDate date = LocalDate.now(); + ColumnEntity entity = ColumnEntity.of("Notification"); + entity.add("_id", 100L); + entity.add("name", "Email Notification"); + entity.add("email", "otavio@otavio.test"); + entity.add("createdOn", date); + entity.add("dtype", "Wrong"); + Assertions.assertThrows(MappingException.class, ()-> this.converter.toEntity(entity)); + } + + + + @Test + void shouldConvertCommunicationNotificationReaderEmail() { + ColumnEntity entity = ColumnEntity.of("NotificationReader"); + entity.add("_id", "poli"); + entity.add("name", "Poliana Santana"); + entity.add("notification", Arrays.asList( + Column.of("_id", 10L), + Column.of("name", "News"), + Column.of("email", "otavio@email.com"), + Column.of("_id", LocalDate.now()), + Column.of("dtype", "Email") + )); + + NotificationReader notificationReader = converter.toEntity(entity); + assertNotNull(notificationReader); + Assertions.assertEquals("poli", notificationReader.getNickname()); + Assertions.assertEquals("Poliana Santana", notificationReader.getName()); + Notification notification = notificationReader.getNotification(); + assertNotNull(notification); + Assertions.assertEquals(EmailNotification.class, notification.getClass()); + EmailNotification email = (EmailNotification) notification; + Assertions.assertEquals(10L, email.getId()); + Assertions.assertEquals("News", email.getName()); + Assertions.assertEquals("otavio@email.com", email.getEmail()); + } + + @Test + void shouldConvertCommunicationNotificationReaderSms() { + ColumnEntity entity = ColumnEntity.of("NotificationReader"); + entity.add("_id", "poli"); + entity.add("name", "Poliana Santana"); + entity.add("notification", Arrays.asList( + Column.of("_id", 10L), + Column.of("name", "News"), + Column.of("phone", "123456789"), + Column.of("_id", LocalDate.now()), + Column.of("dtype", "SMS") + )); + + NotificationReader notificationReader = converter.toEntity(entity); + assertNotNull(notificationReader); + Assertions.assertEquals("poli", notificationReader.getNickname()); + Assertions.assertEquals("Poliana Santana", notificationReader.getName()); + Notification notification = notificationReader.getNotification(); + assertNotNull(notification); + Assertions.assertEquals(SmsNotification.class, notification.getClass()); + SmsNotification sms = (SmsNotification) notification; + Assertions.assertEquals(10L, sms.getId()); + Assertions.assertEquals("News", sms.getName()); + Assertions.assertEquals("123456789", sms.getPhone()); + } + + @Test + void shouldConvertCommunicationNotificationReaderSocial() { + ColumnEntity entity = ColumnEntity.of("NotificationReader"); + entity.add("_id", "poli"); + entity.add("name", "Poliana Santana"); + entity.add("notification", Arrays.asList( + Column.of("_id", 10L), + Column.of("name", "News"), + Column.of("nickname", "123456789"), + Column.of("_id", LocalDate.now()), + Column.of("dtype", "SocialMediaNotification") + )); + + NotificationReader notificationReader = converter.toEntity(entity); + assertNotNull(notificationReader); + Assertions.assertEquals("poli", notificationReader.getNickname()); + Assertions.assertEquals("Poliana Santana", notificationReader.getName()); + Notification notification = notificationReader.getNotification(); + assertNotNull(notification); + Assertions.assertEquals(SocialMediaNotification.class, notification.getClass()); + SocialMediaNotification social = (SocialMediaNotification) notification; + Assertions.assertEquals(10L, social.getId()); + Assertions.assertEquals("News", social.getName()); + Assertions.assertEquals("123456789", social.getNickname()); + } + + @Test + void shouldConvertSocialCommunication() { + SocialMediaNotification notification = new SocialMediaNotification(); + notification.setId(10L); + notification.setName("Ada"); + notification.setNickname("ada.lovelace"); + NotificationReader reader = new NotificationReader("otavio", "Otavio", notification); + + ColumnEntity entity = this.converter.toColumn(reader); + assertNotNull(entity); + + assertEquals("NotificationReader", entity.name()); + assertEquals("otavio", entity.find("_id", String.class).get()); + assertEquals("Otavio", entity.find("name", String.class).get()); + List columns = entity.find("notification", new TypeReference>() { + }).get(); + + assertThat(columns).contains(Column.of("_id", 10L), + Column.of("name", "Ada"), + Column.of("dtype", "SocialMediaNotification"), + Column.of("nickname", "ada.lovelace")); + } + + @Test + void shouldConvertConvertProjectManagerCommunication() { + LargeProject large = new LargeProject(); + large.setBudget(BigDecimal.TEN); + large.setName("large"); + + SmallProject small = new SmallProject(); + small.setInvestor("new investor"); + small.setName("Start up"); + + List projects = new ArrayList<>(); + projects.add(large); + projects.add(small); + + ProjectManager manager = ProjectManager.of(10L, "manager", projects); + ColumnEntity entity = this.converter.toColumn(manager); + assertNotNull(entity); + + assertEquals("ProjectManager", entity.name()); + assertEquals(10L, entity.find("_id", Long.class).get()); + assertEquals("manager", entity.find("name", String.class).get()); + + List> columns = (List>) entity.find("projects").get().get(); + + List largeCommunication = columns.get(0); + List smallCommunication = columns.get(1); + assertThat(largeCommunication).contains( + Column.of("_id", "large"), + Column.of("size", "Large"), + Column.of("budget", BigDecimal.TEN) + ); + + assertThat(smallCommunication).contains( + Column.of("size", "Small"), + Column.of("investor", "new investor"), + Column.of("_id", "Start up") + ); + + } + + @Test + void shouldConvertConvertCommunicationProjectManager() { + ColumnEntity communication = ColumnEntity.of("ProjectManager"); + communication.add("_id", 10L); + communication.add("name", "manager"); + List> columns = new ArrayList<>(); + columns.add(Arrays.asList( + Column.of("_id","small-project"), + Column.of("size","Small"), + Column.of("investor","investor") + )); + columns.add(Arrays.asList( + Column.of("_id","large-project"), + Column.of("size","Large"), + Column.of("budget",BigDecimal.TEN) + )); + communication.add("projects", columns); + + ProjectManager manager = converter.toEntity(communication); + assertNotNull(manager); + + assertEquals(10L, manager.getId()); + assertEquals("manager", manager.getName()); + + List projects = manager.getProjects(); + assertEquals(2, projects.size()); + SmallProject small = (SmallProject) projects.get(0); + LargeProject large = (LargeProject) projects.get(1); + assertNotNull(small); + assertEquals("small-project", small.getName()); + assertEquals("investor", small.getInvestor()); + + assertNotNull(large); + assertEquals("large-project", large.getName()); + assertEquals(BigDecimal.TEN, large.getBudget()); + + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/ColumnEntityConverterTest.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/ColumnEntityConverterTest.java new file mode 100644 index 000000000..ebb753c98 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/ColumnEntityConverterTest.java @@ -0,0 +1,603 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + +import jakarta.inject.Inject; +import org.assertj.core.api.SoftAssertions; +import org.eclipse.jnosql.communication.TypeReference; +import org.eclipse.jnosql.communication.Value; +import org.eclipse.jnosql.communication.column.Column; +import org.eclipse.jnosql.communication.column.ColumnEntity; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.column.entities.Actor; +import org.eclipse.jnosql.mapping.column.entities.Address; +import org.eclipse.jnosql.mapping.column.entities.AppointmentBook; +import org.eclipse.jnosql.mapping.column.entities.Citizen; +import org.eclipse.jnosql.mapping.column.entities.Contact; +import org.eclipse.jnosql.mapping.column.entities.ContactType; +import org.eclipse.jnosql.mapping.column.entities.Director; +import org.eclipse.jnosql.mapping.column.entities.Download; +import org.eclipse.jnosql.mapping.column.entities.Job; +import org.eclipse.jnosql.mapping.column.entities.MainStepType; +import org.eclipse.jnosql.mapping.column.entities.Money; +import org.eclipse.jnosql.mapping.column.entities.Movie; +import org.eclipse.jnosql.mapping.column.entities.Person; +import org.eclipse.jnosql.mapping.column.entities.Transition; +import org.eclipse.jnosql.mapping.column.entities.Vendor; +import org.eclipse.jnosql.mapping.column.entities.Worker; +import org.eclipse.jnosql.mapping.column.entities.WorkflowStep; +import org.eclipse.jnosql.mapping.column.entities.ZipCode; +import org.eclipse.jnosql.mapping.column.spi.ColumnExtension; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; +import static java.util.Collections.singleton; +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.jnosql.mapping.column.entities.StepTransitionReason.REPEAT; +import static org.junit.jupiter.api.Assertions.*; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, ColumnEntityConverter.class}) +@AddPackages(MockProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, ColumnExtension.class}) +class ColumnEntityConverterTest { + + @Inject + private DefaultColumnEntityConverter converter; + + private Column[] columns; + + private final Actor actor = Actor.actorBuilder().withAge() + .withId() + .withName() + .withPhones(asList("234", "2342")) + .withMovieCharacter(Collections.singletonMap("JavaZone", "Jedi")) + .withMovieRating(Collections.singletonMap("JavaZone", 10)) + .build(); + + @BeforeEach + void init() { + + columns = new Column[]{Column.of("_id", 12L), + Column.of("age", 10), Column.of("name", "Otavio"), + Column.of("phones", asList("234", "2342")) + , Column.of("movieCharacter", Collections.singletonMap("JavaZone", "Jedi")) + , Column.of("movieRating", Collections.singletonMap("JavaZone", 10))}; + } + + @Test + void shouldConvertEntityFromColumnEntity() { + + Person person = Person.builder().withAge() + .withId(12) + .withName("Otavio") + .withPhones(asList("234", "2342")).build(); + + ColumnEntity entity = converter.toColumn(person); + assertEquals("Person", entity.name()); + assertEquals(4, entity.size()); + assertThat(entity.columns()).contains(Column.of("_id", 12L), + Column.of("age", 10), Column.of("name", "Otavio"), + Column.of("phones", Arrays.asList("234", "2342"))); + + } + + @Test + void shouldConvertColumnEntityFromEntity() { + + ColumnEntity entity = converter.toColumn(actor); + assertEquals("Actor", entity.name()); + assertEquals(6, entity.size()); + + assertThat(entity.columns()).contains(columns); + } + + @Test + void shouldConvertColumnEntityToEntity() { + ColumnEntity entity = ColumnEntity.of("Actor"); + Stream.of(columns).forEach(entity::add); + + Actor actor = converter.toEntity(Actor.class, entity); + assertNotNull(actor); + assertEquals(10, actor.getAge()); + assertEquals(12L, actor.getId()); + assertEquals(asList("234", "2342"), actor.getPhones()); + assertEquals(Collections.singletonMap("JavaZone", "Jedi"), actor.getMovieCharacter()); + assertEquals(Collections.singletonMap("JavaZone", 10), actor.getMovieRating()); + } + + @Test + void shouldConvertColumnEntityToEntity2() { + ColumnEntity entity = ColumnEntity.of("Actor"); + Stream.of(columns).forEach(entity::add); + + Actor actor = converter.toEntity(entity); + assertNotNull(actor); + assertEquals(10, actor.getAge()); + assertEquals(12L, actor.getId()); + assertEquals(asList("234", "2342"), actor.getPhones()); + assertEquals(Collections.singletonMap("JavaZone", "Jedi"), actor.getMovieCharacter()); + assertEquals(Collections.singletonMap("JavaZone", 10), actor.getMovieRating()); + } + + @Test + void shouldConvertColumnEntityToExistEntity() { + ColumnEntity entity = ColumnEntity.of("Actor"); + Stream.of(columns).forEach(entity::add); + Actor actor = Actor.actorBuilder().build(); + Actor result = converter.toEntity(actor, entity); + + assertSame(actor, result); + assertEquals(10, actor.getAge()); + assertEquals(12L, actor.getId()); + assertEquals(asList("234", "2342"), actor.getPhones()); + assertEquals(Collections.singletonMap("JavaZone", "Jedi"), actor.getMovieCharacter()); + assertEquals(Collections.singletonMap("JavaZone", 10), actor.getMovieRating()); + } + + @Test + void shouldReturnErrorWhenToEntityIsNull() { + ColumnEntity entity = ColumnEntity.of("Actor"); + Stream.of(columns).forEach(entity::add); + Actor actor = Actor.actorBuilder().build(); + + assertThrows(NullPointerException.class, () -> converter.toEntity(null, entity)); + + assertThrows(NullPointerException.class, () -> converter.toEntity(actor, null)); + } + + + @Test + void shouldConvertEntityToColumnEntity2() { + + Movie movie = new Movie("Matrix", 2012, Collections.singleton("Actor")); + Director director = Director.builderDirector().withAge(12) + .withId(12) + .withName("Otavio") + .withPhones(asList("234", "2342")).withMovie(movie).build(); + + ColumnEntity entity = converter.toColumn(director); + assertEquals(5, entity.size()); + + assertEquals(getValue(entity.find("name")), director.getName()); + assertEquals(getValue(entity.find("age")), director.getAge()); + assertEquals(getValue(entity.find("_id")), director.getId()); + assertEquals(getValue(entity.find("phones")), director.getPhones()); + + + Column subColumn = entity.find("movie").get(); + List columns = subColumn.get(new TypeReference<>() { + }); + + assertEquals(3, columns.size()); + assertEquals("movie", subColumn.name()); + assertEquals(movie.getTitle(), columns.stream().filter(c -> "title".equals(c.name())).findFirst().get().get()); + assertEquals(movie.getYear(), columns.stream().filter(c -> "year".equals(c.name())).findFirst().get().get()); + assertEquals(movie.getActors(), columns.stream().filter(c -> "actors".equals(c.name())).findFirst().get().get()); + + + } + + @Test + void shouldConvertToEmbeddedClassWhenHasSubColumn() { + Movie movie = new Movie("Matrix", 2012, Collections.singleton("Actor")); + Director director = Director.builderDirector().withAge(12) + .withId(12) + .withName("Otavio") + .withPhones(asList("234", "2342")).withMovie(movie).build(); + + ColumnEntity entity = converter.toColumn(director); + Director director1 = converter.toEntity(entity); + + assertEquals(movie, director1.getMovie()); + assertEquals(director.getName(), director1.getName()); + assertEquals(director.getAge(), director1.getAge()); + assertEquals(director.getId(), director1.getId()); + } + + @Test + void shouldConvertToEmbeddedClassWhenHasSubColumn2() { + Movie movie = new Movie("Matrix", 2012, singleton("Actor")); + Director director = Director.builderDirector().withAge(12) + .withId(12) + .withName("Otavio") + .withPhones(asList("234", "2342")).withMovie(movie).build(); + + ColumnEntity entity = converter.toColumn(director); + entity.remove("movie"); + entity.add(Column.of("movie", Arrays.asList(Column.of("title", "Matrix"), + Column.of("year", 2012), Column.of("actors", singleton("Actor"))))); + Director director1 = converter.toEntity(entity); + + assertEquals(movie, director1.getMovie()); + assertEquals(director.getName(), director1.getName()); + assertEquals(director.getAge(), director1.getAge()); + assertEquals(director.getId(), director1.getId()); + } + + @Test + void shouldConvertToEmbeddedClassWhenHasSubColumn3() { + Movie movie = new Movie("Matrix", 2012, singleton("Actor")); + Director director = Director.builderDirector().withAge(12) + .withId(12) + .withName("Otavio") + .withPhones(asList("234", "2342")).withMovie(movie).build(); + + ColumnEntity entity = converter.toColumn(director); + entity.remove("movie"); + Map map = new HashMap<>(); + map.put("title", "Matrix"); + map.put("year", 2012); + map.put("actors", singleton("Actor")); + + entity.add(Column.of("movie", map)); + Director director1 = converter.toEntity(entity); + + assertEquals(movie, director1.getMovie()); + assertEquals(director.getName(), director1.getName()); + assertEquals(director.getAge(), director1.getAge()); + assertEquals(director.getId(), director1.getId()); + } + + @Test + void shouldConvertToColumnWhenHaConverter() { + Worker worker = new Worker(); + Job job = new Job(); + job.setCity("Sao Paulo"); + job.setDescription("Java Developer"); + worker.setName("Bob"); + worker.setSalary(new Money("BRL", BigDecimal.TEN)); + worker.setJob(job); + ColumnEntity entity = converter.toColumn(worker); + assertEquals("Worker", entity.name()); + assertEquals("Bob", entity.find("name").get().get()); + assertEquals("Sao Paulo", entity.find("city").get().get()); + assertEquals("Java Developer", entity.find("description").get().get()); + assertEquals("BRL 10", entity.find("money").get().get()); + } + + @Test + void shouldConvertToEntityWhenHasConverter() { + Worker worker = new Worker(); + Job job = new Job(); + job.setCity("Sao Paulo"); + job.setDescription("Java Developer"); + worker.setName("Bob"); + worker.setSalary(new Money("BRL", BigDecimal.TEN)); + worker.setJob(job); + ColumnEntity entity = converter.toColumn(worker); + Worker worker1 = converter.toEntity(entity); + assertEquals(worker.getSalary(), worker1.getSalary()); + assertEquals(job.getCity(), worker1.getJob().getCity()); + assertEquals(job.getDescription(), worker1.getJob().getDescription()); + } + + @Test + void shouldConvertEmbeddableLazily() { + ColumnEntity entity = ColumnEntity.of("Worker"); + entity.add("name", "Otavio"); + entity.add("money", "BRL 10"); + + Worker worker = converter.toEntity(entity); + assertEquals("Otavio", worker.getName()); + assertEquals(new Money("BRL", BigDecimal.TEN), worker.getSalary()); + Assertions.assertNull(worker.getJob()); + + } + + + @Test + void shouldConvertToListEmbeddable() { + AppointmentBook appointmentBook = new AppointmentBook("ids"); + appointmentBook.add(Contact.builder().withType(ContactType.EMAIL) + .withName("Ada").withInformation("ada@lovelace.com").build()); + appointmentBook.add(Contact.builder().withType(ContactType.MOBILE) + .withName("Ada").withInformation("11 1231231 123").build()); + appointmentBook.add(Contact.builder().withType(ContactType.PHONE) + .withName("Ada").withInformation("12 123 1231 123123").build()); + + ColumnEntity entity = converter.toColumn(appointmentBook); + Column contacts = entity.find("contacts").get(); + assertEquals("ids", appointmentBook.getId()); + List> columns = (List>) contacts.get(); + + assertEquals(3L, columns.stream().flatMap(Collection::stream) + .filter(c -> c.name().equals("contact_name")) + .count()); + } + + @Test + void shouldConvertFromListEmbeddable() { + ColumnEntity entity = ColumnEntity.of("AppointmentBook"); + entity.add(Column.of("_id", "ids")); + List> columns = new ArrayList<>(); + + columns.add(asList(Column.of("contact_name", "Ada"), Column.of("type", ContactType.EMAIL), + Column.of("information", "ada@lovelace.com"))); + + columns.add(asList(Column.of("contact_name", "Ada"), Column.of("type", ContactType.MOBILE), + Column.of("information", "11 1231231 123"))); + + columns.add(asList(Column.of("contact_name", "Ada"), Column.of("type", ContactType.PHONE), + Column.of("information", "phone"))); + + entity.add(Column.of("contacts", columns)); + + AppointmentBook appointmentBook = converter.toEntity(entity); + + List contacts = appointmentBook.getContacts(); + assertEquals("ids", appointmentBook.getId()); + assertEquals("Ada", contacts.stream().map(Contact::getName).distinct().findFirst().get()); + + } + + + @Test + void shouldConvertSubEntity() { + ZipCode zipcode = new ZipCode(); + zipcode.setZip("12321"); + zipcode.setPlusFour("1234"); + + Address address = new Address(); + address.setCity("Salvador"); + address.setState("Bahia"); + address.setStreet("Rua Engenheiro Jose Anasoh"); + address.setZipCode(zipcode); + + ColumnEntity columnEntity = converter.toColumn(address); + List columns = columnEntity.columns(); + assertEquals("Address", columnEntity.name()); + assertEquals(4, columns.size()); + List zip = columnEntity.find("zipCode").map(d -> d.get(new TypeReference>() { + })).orElse(Collections.emptyList()); + + assertEquals("Rua Engenheiro Jose Anasoh", getValue(columnEntity.find("street"))); + assertEquals("Salvador", getValue(columnEntity.find("city"))); + assertEquals("Bahia", getValue(columnEntity.find("state"))); + assertEquals("12321", getValue(zip.stream().filter(d -> d.name().equals("zip")).findFirst())); + assertEquals("1234", getValue(zip.stream().filter(d -> d.name().equals("plusFour")).findFirst())); + } + + @Test + void shouldConvertColumnInSubEntity() { + + ColumnEntity entity = ColumnEntity.of("Address"); + + entity.add(Column.of("street", "Rua Engenheiro Jose Anasoh")); + entity.add(Column.of("city", "Salvador")); + entity.add(Column.of("state", "Bahia")); + entity.add(Column.of("zipCode", Arrays.asList( + Column.of("zip", "12321"), + Column.of("plusFour", "1234")))); + Address address = converter.toEntity(entity); + + assertEquals("Rua Engenheiro Jose Anasoh", address.getStreet()); + assertEquals("Salvador", address.getCity()); + assertEquals("Bahia", address.getState()); + assertEquals("12321", address.getZipCode().getZip()); + assertEquals("1234", address.getZipCode().getPlusFour()); + + } + + @Test + void shouldReturnNullWhenThereIsNotSubEntity() { + ColumnEntity entity = ColumnEntity.of("Address"); + + entity.add(Column.of("street", "Rua Engenheiro Jose Anasoh")); + entity.add(Column.of("city", "Salvador")); + entity.add(Column.of("state", "Bahia")); + entity.add(Column.of("zip", "12321")); + entity.add(Column.of("plusFour", "1234")); + + Address address = converter.toEntity(entity); + + assertEquals("Rua Engenheiro Jose Anasoh", address.getStreet()); + assertEquals("Salvador", address.getCity()); + assertEquals("Bahia", address.getState()); + assertNull(address.getZipCode()); + } + + @Test + void shouldConvertAndDoNotUseUnmodifiableCollection() { + ColumnEntity entity = ColumnEntity.of("vendors"); + entity.add("name", "name"); + entity.add("prefixes", Arrays.asList("value", "value2")); + + Vendor vendor = converter.toEntity(entity); + vendor.add("value3"); + + Assertions.assertEquals(3, vendor.getPrefixes().size()); + + } + + @Test + void shouldConvertEntityToDocumentWithArray() { + byte[] contents = {1, 2, 3, 4, 5, 6}; + + ColumnEntity entity = ColumnEntity.of("download"); + entity.add("_id", 1L); + entity.add("contents", contents); + + Download download = converter.toEntity(entity); + Assertions.assertEquals(1L, download.getId()); + Assertions.assertArrayEquals(contents, download.getContents()); + } + + @Test + void shouldConvertDocumentToEntityWithArray() { + byte[] contents = {1, 2, 3, 4, 5, 6}; + + Download download = new Download(); + download.setId(1L); + download.setContents(contents); + + ColumnEntity entity = converter.toColumn(download); + + Assertions.assertEquals(1L, entity.find("_id").get().get()); + final byte[] bytes = entity.find("contents").map(v -> v.get(byte[].class)).orElse(new byte[0]); + Assertions.assertArrayEquals(contents, bytes); + } + + @Test + void shouldCreateUserScope() { + ColumnEntity entity = ColumnEntity.of("UserScope"); + entity.add("_id", "userName"); + entity.add("scope", "scope"); + entity.add("properties", Collections.singletonList(Column.of("halo", "weld"))); + + UserScope user = converter.toEntity(entity); + Assertions.assertNotNull(user); + Assertions.assertEquals("userName",user.getUserName()); + Assertions.assertEquals("scope",user.getScope()); + Assertions.assertEquals(Collections.singletonMap("halo", "weld"),user.getProperties()); + + } + + @Test + void shouldCreateUserScope2() { + ColumnEntity entity = ColumnEntity.of("UserScope"); + entity.add("_id", "userName"); + entity.add("scope", "scope"); + entity.add("properties", Column.of("halo", "weld")); + + UserScope user = converter.toEntity(entity); + Assertions.assertNotNull(user); + Assertions.assertEquals("userName",user.getUserName()); + Assertions.assertEquals("scope",user.getScope()); + Assertions.assertEquals(Collections.singletonMap("halo", "weld"),user.getProperties()); + + } + + @Test + void shouldCreateLazilyEntity() { + ColumnEntity entity = ColumnEntity.of("Citizen"); + entity.add("id", "10"); + entity.add("name", "Salvador"); + + Citizen citizen = converter.toEntity(entity); + Assertions.assertNotNull(citizen); + Assertions.assertNull(citizen.getCity()); + } + + + @Test + void shouldReturnNullValuePresent() { + Person person = Person.builder().build(); + + ColumnEntity entity = converter.toColumn(person); + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(entity.find("name")).isPresent(); + soft.assertThat(entity.find("age")).isPresent(); + soft.assertThat(entity.find("phones")).isPresent(); + soft.assertThat(entity.find("ignore")).isNotPresent(); + + soft.assertThat(entity.find("name", String.class)).isNotPresent(); + soft.assertThat(entity.find("phones", String.class)).isNotPresent(); + }); + } + + @Test + void shouldConvertWorkflow(){ + var workflowStep = WorkflowStep.builder() + .id("id") + .key("key") + .workflowSchemaKey("workflowSchemaKey") + .stepName("stepName") + .mainStepType(MainStepType.MAIN) + .stepNo(1) + .componentConfigurationKey("componentConfigurationKey") + .relationTypeKey("relationTypeKey") + .availableTransitions(List.of(new Transition("TEST_WORKFLOW_STEP_KEY", REPEAT, + null, List.of("ADMIN")))) + .build(); + + var document = this.converter.toColumn(workflowStep); + WorkflowStep result = this.converter.toEntity(document); + SoftAssertions.assertSoftly(soft ->{ + soft.assertThat(result).isNotNull(); + soft.assertThat(result.id()).isEqualTo("id"); + soft.assertThat(result.key()).isEqualTo("key"); + soft.assertThat(result.workflowSchemaKey()).isEqualTo("workflowSchemaKey"); + soft.assertThat(result.stepName()).isEqualTo("stepName"); + soft.assertThat(result.mainStepType()).isEqualTo(MainStepType.MAIN); + soft.assertThat(result.stepNo()).isEqualTo(1L); + soft.assertThat(result.componentConfigurationKey()).isEqualTo("componentConfigurationKey"); + soft.assertThat(result.relationTypeKey()).isEqualTo("relationTypeKey"); + soft.assertThat(result.availableTransitions()).hasSize(1); + soft.assertThat(result.availableTransitions().get(0).targetWorkflowStepKey()).isEqualTo("TEST_WORKFLOW_STEP_KEY"); + soft.assertThat(result.availableTransitions().get(0).stepTransitionReason()).isEqualTo(REPEAT); + soft.assertThat(result.availableTransitions().get(0).mailTemplateKey()).isNull(); + soft.assertThat(result.availableTransitions().get(0).restrictedRoleGroups()).hasSize(1); + soft.assertThat(result.availableTransitions().get(0).restrictedRoleGroups().get(0)).isEqualTo("ADMIN"); + }); + + } + + @Test + void shouldUpdateEmbeddable2() { + var workflowStep = WorkflowStep.builder() + .id("id") + .key("key") + .workflowSchemaKey("workflowSchemaKey") + .stepName("stepName") + .mainStepType(MainStepType.MAIN) + .stepNo(null) + .componentConfigurationKey("componentConfigurationKey") + .relationTypeKey("relationTypeKey") + .availableTransitions(null) + .build(); + var document = this.converter.toColumn(workflowStep); + WorkflowStep result = this.converter.toEntity(document); + SoftAssertions.assertSoftly(soft ->{ + soft.assertThat(result).isNotNull(); + soft.assertThat(result.id()).isEqualTo("id"); + soft.assertThat(result.key()).isEqualTo("key"); + soft.assertThat(result.workflowSchemaKey()).isEqualTo("workflowSchemaKey"); + soft.assertThat(result.stepName()).isEqualTo("stepName"); + soft.assertThat(result.mainStepType()).isEqualTo(MainStepType.MAIN); + soft.assertThat(result.stepNo()).isNull(); + soft.assertThat(result.componentConfigurationKey()).isEqualTo("componentConfigurationKey"); + soft.assertThat(result.relationTypeKey()).isEqualTo("relationTypeKey"); + soft.assertThat(result.availableTransitions()).isNull(); + + }); + + } + + + private Object getValue(Optional column) { + return column.map(Column::value).map(Value::get).orElse(null); + } + +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/ColumnEntityImmutableTest.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/ColumnEntityImmutableTest.java new file mode 100644 index 000000000..bda4f1a91 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/ColumnEntityImmutableTest.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + +import jakarta.inject.Inject; +import org.eclipse.jnosql.communication.column.Column; +import org.eclipse.jnosql.communication.column.ColumnEntity; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.column.entities.Car; +import org.eclipse.jnosql.mapping.column.entities.Hero; +import org.eclipse.jnosql.mapping.column.spi.ColumnExtension; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.time.Year; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.junit.jupiter.api.Assertions.*; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, ColumnEntityConverter.class}) +@AddPackages(MockProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, ColumnExtension.class}) +class ColumnEntityImmutableTest { + + @Inject + private DefaultColumnEntityConverter converter; + + private Column[] columns; + + private Car car; + + @BeforeEach + void init() { + + this.car = new Car("123456789", "SF90", "Ferrari", Year.now()); + + columns = new Column[]{Column.of("_id", "123456789"), + Column.of("model", "SF90"), + Column.of("manufacturer", "Ferrari"), + Column.of("year", Year.now()) + }; + } + + @Test + void shouldConvertCommunicationEntity() { + + ColumnEntity entity = converter.toColumn(car); + assertEquals("Car", entity.name()); + assertEquals(4, entity.size()); + assertThat(entity.columns()).contains(Column.of("_id", "123456789"), + Column.of("model", "SF90"), + Column.of("manufacturer", "Ferrari")); + + } + + @Test + void shouldConvertCommunicationEntity2() { + + ColumnEntity entity = converter.toColumn(car); + assertEquals("Car", entity.name()); + assertEquals(4, entity.size()); + + assertThat(entity.columns()).contains(columns); + } + + @Test + void shouldConvertEntity() { + ColumnEntity entity = ColumnEntity.of("Car"); + Stream.of(columns).forEach(entity::add); + + Car ferrari = converter.toEntity(Car.class, entity); + assertNotNull(ferrari); + assertEquals("123456789", ferrari.plate()); + assertEquals("SF90", ferrari.model()); + assertEquals("Ferrari", ferrari.manufacturer()); + assertEquals(Year.now(), ferrari.year()); + + } + + @Test + void shouldConvertExistRecord() { + ColumnEntity entity = ColumnEntity.of("Car"); + Stream.of(columns).forEach(entity::add); + Car ferrari = new Car(null, null, null, null); + Car result = converter.toEntity(ferrari, entity); + + assertEquals("123456789", result.plate()); + assertEquals("SF90", result.model()); + assertEquals("Ferrari", result.manufacturer()); + assertEquals(Year.now(), result.year()); + assertNotSame(ferrari, car); + assertSoftly(soft -> { + soft.assertThat(ferrari.model()).isNull(); + soft.assertThat(ferrari.manufacturer()).isNull(); + soft.assertThat(ferrari.plate()).isNull(); + soft.assertThat(ferrari.year()).isNull(); + + soft.assertThat(result.model()).isEqualTo("SF90"); + soft.assertThat(result.manufacturer()).isEqualTo("Ferrari"); + soft.assertThat(result.plate()).isEqualTo("123456789"); + soft.assertThat(result.year()).isEqualTo(Year.now()); + }); + } + + @Test + void shouldConvertExist() { + ColumnEntity entity = ColumnEntity.of("Hero"); + entity.add("_id", "2342"); + entity.add("name", "Iron man"); + Hero hero = new Hero(null, null); + Hero result = converter.toEntity(hero, entity); + assertSame(hero, result); + assertSoftly(soft -> { + soft.assertThat(hero.id()).isEqualTo("2342"); + soft.assertThat(hero.name()).isEqualTo("Iron man"); + } + ); + } + + +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/ColumnEventPersistManagerTest.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/ColumnEventPersistManagerTest.java new file mode 100644 index 000000000..4e6c059e8 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/ColumnEventPersistManagerTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + +import jakarta.enterprise.event.Event; +import org.eclipse.jnosql.mapping.EntityPostPersist; +import org.eclipse.jnosql.mapping.EntityPrePersist; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +class ColumnEventPersistManagerTest { + + + @InjectMocks + private ColumnEventPersistManager subject; + + + @Mock + private Event entityPrePersistEvent; + + @Mock + private Event entityPostPersistEvent; + + + + + + + @Test + void shouldFirePreEntity() { + Jedi jedi = new Jedi(); + jedi.name = "Luke"; + subject.firePreEntity(jedi); + ArgumentCaptor captor = ArgumentCaptor.forClass(EntityPrePersist.class); + verify(entityPrePersistEvent).fire(captor.capture()); + EntityPrePersist value = captor.getValue(); + assertEquals(jedi, value.get()); + } + + @Test + void shouldFirePostEntity() { + Jedi jedi = new Jedi(); + jedi.name = "Luke"; + subject.firePostEntity(jedi); + ArgumentCaptor captor = ArgumentCaptor.forClass(EntityPostPersist.class); + verify(entityPostPersistEvent).fire(captor.capture()); + EntityPostPersist value = captor.getValue(); + assertEquals(jedi, value.get()); + } + + + static class Jedi { + private String name; + } +} \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/ColumnManagerMock.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/ColumnManagerMock.java new file mode 100644 index 000000000..e0af01aac --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/ColumnManagerMock.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + +import org.eclipse.jnosql.communication.Settings; +import org.eclipse.jnosql.communication.column.ColumnConfiguration; +import org.eclipse.jnosql.communication.column.ColumnManager; +import org.eclipse.jnosql.communication.column.ColumnManagerFactory; +import org.mockito.Mockito; + +public class ColumnManagerMock implements ColumnConfiguration { + + + + @Override + public MockFamilyManager apply(Settings settings) { + return new MockFamilyManager(settings); + } + + public record MockFamilyManager(Settings settings) implements ColumnManagerFactory { + + @Override + public ColumnManager apply(String database) { + return Mockito.mock(ColumnManager.class); + } + + @Override + public void close() { + + } + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/ColumnMapperObserverTest.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/ColumnMapperObserverTest.java new file mode 100644 index 000000000..a538b21ba --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/ColumnMapperObserverTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + +import jakarta.inject.Inject; +import org.eclipse.jnosql.communication.column.ColumnObserverParser; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.column.entities.Car; +import org.eclipse.jnosql.mapping.column.entities.Vendor; +import org.eclipse.jnosql.mapping.column.entities.Worker; +import org.eclipse.jnosql.mapping.column.spi.ColumnExtension; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + + +@EnableAutoWeld +@AddPackages(value = {Converters.class, ColumnEntityConverter.class}) +@AddPackages(MockProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, ColumnExtension.class}) +class ColumnMapperObserverTest { + + @Inject + private EntitiesMetadata mappings; + + private ColumnObserverParser parser; + + @BeforeEach + void setUp() { + this.parser = new ColumnMapperObserver(mappings); + } + + @Test + void shouldFireEntity(){ + var entity = parser.fireEntity("Vendor"); + Assertions.assertEquals("vendors", entity); + } + + @Test + void shouldFireFromClass(){ + var entity = parser.fireEntity(Car.class.getSimpleName()); + Assertions.assertEquals("Car", entity); + } + + @Test + void shouldFireFromClassName(){ + var entity = parser.fireEntity(Car.class.getSimpleName()); + Assertions.assertEquals("Car", entity); + } + + @Test + void shouldFireField(){ + var field = parser.fireField("Worker", "salary"); + Assertions.assertEquals("money", field); + } + + @Test + void shouldFireFieldFromClassName(){ + var field = parser.fireField(Worker.class.getName(), "salary"); + Assertions.assertEquals("money", field); + } + + @Test + void shouldFireFieldFromSimplesName(){ + var field = parser.fireField(Worker.class.getSimpleName(), "salary"); + Assertions.assertEquals("money", field); + } + + @Test + void shouldFireFieldFromEntity(){ + var field = parser.fireField(Vendor.class.getSimpleName(), "name"); + Assertions.assertEquals("_id", field); + } + +} \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/ColumnTemplateInheritanceTest.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/ColumnTemplateInheritanceTest.java new file mode 100644 index 000000000..198bc9287 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/ColumnTemplateInheritanceTest.java @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; +import org.eclipse.jnosql.communication.Condition; +import org.eclipse.jnosql.communication.TypeReference; +import org.eclipse.jnosql.communication.column.Column; +import org.eclipse.jnosql.communication.column.ColumnCondition; +import org.eclipse.jnosql.communication.column.ColumnDeleteQuery; +import org.eclipse.jnosql.communication.column.ColumnManager; +import org.eclipse.jnosql.communication.column.ColumnQuery; +import org.eclipse.jnosql.mapping.column.entities.inheritance.EmailNotification; +import org.eclipse.jnosql.mapping.column.entities.inheritance.Notification; +import org.eclipse.jnosql.mapping.column.spi.ColumnExtension; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; + +import java.util.List; + +import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.mockito.Mockito.when; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, ColumnEntityConverter.class}) +@AddPackages(MockProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, ColumnExtension.class}) +class ColumnTemplateInheritanceTest { + + @Inject + private ColumnEntityConverter converter; + + @Inject + private EntitiesMetadata entities; + + @Inject + private Converters converters; + + private ColumnManager managerMock; + + private DefaultColumnTemplate template; + + + @BeforeEach + void setUp() { + managerMock = Mockito.mock(ColumnManager.class); + var documentEventPersistManager = Mockito.mock(ColumnEventPersistManager.class); + + Instance instance = Mockito.mock(Instance.class); + when(instance.get()).thenReturn(managerMock); + this.template = new DefaultColumnTemplate(converter, instance, + documentEventPersistManager, entities, converters); + } + + @Test + void shouldSelectFilter(){ + var captor = ArgumentCaptor.forClass(ColumnQuery.class); + template.select(EmailNotification.class).stream().toList(); + Mockito.verify(this.managerMock).select(captor.capture()); + var query = captor.getValue(); + assertSoftly(soft ->{ + soft.assertThat(query.name()).isEqualTo("Notification"); + soft.assertThat(query.condition()).isPresent(); + ColumnCondition condition = query.condition().orElseThrow(); + soft.assertThat(condition.condition()).isEqualTo(Condition.EQUALS); + soft.assertThat(condition.column()).isEqualTo(Column.of("dtype", "Email")); + }); + } + + @Test + void shouldSelectNoFilter(){ + var captor = ArgumentCaptor.forClass(ColumnQuery.class); + template.select(Notification.class).stream().toList(); + Mockito.verify(this.managerMock).select(captor.capture()); + var query = captor.getValue(); + assertSoftly(soft ->{ + soft.assertThat(query.name()).isEqualTo("Notification"); + soft.assertThat(query.condition()).isEmpty(); + }); + } + + @Test + void shouldDeleteFilter(){ + var captor = ArgumentCaptor.forClass(ColumnDeleteQuery.class); + template.delete(EmailNotification.class).execute(); + Mockito.verify(this.managerMock).delete(captor.capture()); + var query = captor.getValue(); + assertSoftly(soft ->{ + soft.assertThat(query.name()).isEqualTo("Notification"); + soft.assertThat(query.condition()).isPresent(); + ColumnCondition condition = query.condition().orElseThrow(); + soft.assertThat(condition.condition()).isEqualTo(Condition.EQUALS); + soft.assertThat(condition.column()).isEqualTo(Column.of("dtype", "Email")); + }); + } + + @Test + void shouldDeleteNoFilter(){ + var captor = ArgumentCaptor.forClass(ColumnDeleteQuery.class); + template.delete(Notification.class).execute(); + Mockito.verify(this.managerMock).delete(captor.capture()); + var query = captor.getValue(); + assertSoftly(soft ->{ + soft.assertThat(query.name()).isEqualTo("Notification"); + soft.assertThat(query.condition()).isEmpty(); + }); + } + + @Test + void shouldSelectFilterCondition(){ + var captor = ArgumentCaptor.forClass(ColumnQuery.class); + template.select(EmailNotification.class).where("name") + .eq("notification").stream().toList(); + Mockito.verify(this.managerMock).select(captor.capture()); + var query = captor.getValue(); + assertSoftly(soft ->{ + soft.assertThat(query.name()).isEqualTo("Notification"); + soft.assertThat(query.condition()).isPresent(); + ColumnCondition condition = query.condition().orElseThrow(); + soft.assertThat(condition.condition()).isEqualTo(Condition.AND); + var documents = condition.column().get(new TypeReference>() {}); + soft.assertThat(documents).contains(ColumnCondition.eq(Column.of("dtype", "Email")), + ColumnCondition.eq(Column.of("name", "notification"))); + }); + } + + @Test + void shouldDeleteFilterCondition(){ + var captor = ArgumentCaptor.forClass(ColumnDeleteQuery.class); + template.delete(EmailNotification.class).where("name") + .eq("notification").execute(); + Mockito.verify(this.managerMock).delete(captor.capture()); + var query = captor.getValue(); + assertSoftly(soft ->{ + soft.assertThat(query.name()).isEqualTo("Notification"); + soft.assertThat(query.condition()).isPresent(); + ColumnCondition condition = query.condition().orElseThrow(); + soft.assertThat(condition.condition()).isEqualTo(Condition.AND); + var documents = condition.column().get(new TypeReference>() {}); + soft.assertThat(documents).contains(ColumnCondition.eq(Column.of("dtype", "Email")), + ColumnCondition.eq(Column.of("name", "notification"))); + }); + } + + @Test + void shouldCountAllFilter(){ + var captor = ArgumentCaptor.forClass(ColumnQuery.class); + template.count(EmailNotification.class); + Mockito.verify(this.managerMock).count(captor.capture()); + var query = captor.getValue(); + + assertSoftly(soft ->{ + soft.assertThat(query.name()).isEqualTo("Notification"); + soft.assertThat(query.condition()).isPresent(); + ColumnCondition condition = query.condition().orElseThrow(); + soft.assertThat(condition.condition()).isEqualTo(Condition.EQUALS); + soft.assertThat(condition.column()).isEqualTo(Column.of("dtype", "Email")); + }); + } + + @Test + void shouldFindAllFilter(){ + var captor = ArgumentCaptor.forClass(ColumnQuery.class); + template.findAll(EmailNotification.class); + Mockito.verify(this.managerMock).select(captor.capture()); + var query = captor.getValue(); + + assertSoftly(soft ->{ + soft.assertThat(query.name()).isEqualTo("Notification"); + soft.assertThat(query.condition()).isPresent(); + ColumnCondition condition = query.condition().orElseThrow(); + soft.assertThat(condition.condition()).isEqualTo(Condition.EQUALS); + soft.assertThat(condition.column()).isEqualTo(Column.of("dtype", "Email")); + }); + } + + @Test + void shouldDeleteAllFilter(){ + var captor = ArgumentCaptor.forClass(ColumnDeleteQuery.class); + template.deleteAll(EmailNotification.class); + Mockito.verify(this.managerMock).delete(captor.capture()); + var query = captor.getValue(); + + assertSoftly(soft ->{ + soft.assertThat(query.name()).isEqualTo("Notification"); + soft.assertThat(query.condition()).isPresent(); + ColumnCondition condition = query.condition().orElseThrow(); + soft.assertThat(condition.condition()).isEqualTo(Condition.EQUALS); + soft.assertThat(condition.column()).isEqualTo(Column.of("dtype", "Email")); + }); + } + + + @Test + void shouldCountAllNoFilter(){ + var captor = ArgumentCaptor.forClass(ColumnQuery.class); + template.count(Notification.class); + Mockito.verify(this.managerMock).count(captor.capture()); + var query = captor.getValue(); + + assertSoftly(soft ->{ + soft.assertThat(query.name()).isEqualTo("Notification"); + soft.assertThat(query.condition()).isEmpty(); + }); + } + + @Test + void shouldFindAllNoFilter(){ + var captor = ArgumentCaptor.forClass(ColumnQuery.class); + template.findAll(Notification.class); + Mockito.verify(this.managerMock).select(captor.capture()); + var query = captor.getValue(); + + assertSoftly(soft ->{ + soft.assertThat(query.name()).isEqualTo("Notification"); + soft.assertThat(query.condition()).isEmpty(); + }); + } + @Test + void shouldDeleteAllNoFilter(){ + var captor = ArgumentCaptor.forClass(ColumnDeleteQuery.class); + template.deleteAll(Notification.class); + Mockito.verify(this.managerMock).delete(captor.capture()); + var query = captor.getValue(); + + assertSoftly(soft ->{ + soft.assertThat(query.name()).isEqualTo("Notification"); + soft.assertThat(query.condition()).isEmpty(); + }); + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/ColumnTemplateTest.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/ColumnTemplateTest.java new file mode 100644 index 000000000..0cb569622 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/ColumnTemplateTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + +import jakarta.inject.Inject; +import jakarta.nosql.Template; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.Database; +import org.eclipse.jnosql.mapping.column.spi.ColumnExtension; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.eclipse.jnosql.mapping.DatabaseType.COLUMN; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, ColumnEntityConverter.class}) +@AddPackages(MockProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, ColumnExtension.class}) +class ColumnTemplateTest { + + @Inject + private Template template; + + @Inject + @Database(COLUMN) + private Template qualifier; + + @Test + void shouldInjectTemplate() { + Assertions.assertNotNull(template); + } + + @Test + void shouldInjectQualifier() { + Assertions.assertNotNull(qualifier); + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/DatabaseQualifierTest.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/DatabaseQualifierTest.java new file mode 100644 index 000000000..d8ed215c7 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/DatabaseQualifierTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + +import org.eclipse.jnosql.mapping.DatabaseQualifier; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.eclipse.jnosql.mapping.DatabaseType.*; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class DatabaseQualifierTest { + + @Test + void shouldReturnDefaultColumn() { + DatabaseQualifier qualifier = DatabaseQualifier.ofColumn(); + assertEquals("", qualifier.provider()); + assertEquals(COLUMN, qualifier.value()); + } + + @Test + void shouldReturnColumnProvider() { + String provider = "provider"; + DatabaseQualifier qualifier = DatabaseQualifier.ofColumn(provider); + assertEquals(provider, qualifier.provider()); + assertEquals(COLUMN, qualifier.value()); + } + + @Test + void shouldReturnErrorWhenColumnNull() { + Assertions.assertThrows(NullPointerException.class, () -> DatabaseQualifier.ofColumn(null)); + } + + @Test + void shouldReturnDefaultDocument() { + DatabaseQualifier qualifier = DatabaseQualifier.ofDocument(); + assertEquals("", qualifier.provider()); + assertEquals(DOCUMENT, qualifier.value()); + } + + @Test + void shouldReturnDocumentProvider() { + String provider = "provider"; + DatabaseQualifier qualifier = DatabaseQualifier.ofDocument(provider); + assertEquals(provider, qualifier.provider()); + assertEquals(DOCUMENT, qualifier.value()); + } + + @Test + void shouldReturnErrorWhenDocumentNull() { + Assertions.assertThrows(NullPointerException.class, () -> DatabaseQualifier.ofDocument(null)); + } + + @Test + void shouldReturnErrorWhenKeyValueNull() { + Assertions.assertThrows(NullPointerException.class, () -> DatabaseQualifier.ofKeyValue(null)); + } + + @Test + void shouldReturnKeyValueProvider() { + String provider = "provider"; + DatabaseQualifier qualifier = DatabaseQualifier.ofKeyValue(provider); + assertEquals(provider, qualifier.provider()); + assertEquals(KEY_VALUE, qualifier.value()); + } + + @Test + void shouldReturnDefaultKeyValue() { + DatabaseQualifier qualifier = DatabaseQualifier.ofKeyValue(); + assertEquals("", qualifier.provider()); + assertEquals(KEY_VALUE, qualifier.value()); + } + + + @Test + void shouldReturnErrorWhenGraphNull() { + Assertions.assertThrows(NullPointerException.class, () -> DatabaseQualifier.ofGraph(null)); + } + + @Test + void shouldReturnGraphProvider() { + String provider = "provider"; + DatabaseQualifier qualifier = DatabaseQualifier.ofGraph(provider); + assertEquals(provider, qualifier.provider()); + assertEquals(GRAPH, qualifier.value()); + } + + @Test + void shouldReturnDefaultGraph() { + DatabaseQualifier qualifier = DatabaseQualifier.ofGraph(); + assertEquals("", qualifier.provider()); + assertEquals(GRAPH, qualifier.value()); + } +} \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/DefaultColumnTemplateProducerTest.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/DefaultColumnTemplateProducerTest.java new file mode 100644 index 000000000..f3b11ff5e --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/DefaultColumnTemplateProducerTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + +import jakarta.inject.Inject; +import jakarta.nosql.column.ColumnTemplate; +import org.eclipse.jnosql.communication.column.ColumnManager; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.column.spi.ColumnExtension; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, ColumnEntityConverter.class}) +@AddPackages(MockProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, ColumnExtension.class}) +class DefaultColumnTemplateProducerTest { + + @Inject + private ColumnTemplateProducer producer; + + @Test + void shouldReturnErrorWhenColumnManagerNull() { + Assertions.assertThrows(NullPointerException.class, () -> producer.apply(null)); + } + + @Test + void shouldReturn() { + ColumnManager manager = Mockito.mock(ColumnManager.class); + ColumnTemplate columnTemplate = producer.apply(manager); + assertNotNull(columnTemplate); + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/DefaultColumnTemplateTest.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/DefaultColumnTemplateTest.java new file mode 100644 index 000000000..b09422463 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/DefaultColumnTemplateTest.java @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + +import jakarta.data.exceptions.NonUniqueResultException; +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; +import jakarta.nosql.PreparedStatement; +import org.assertj.core.api.SoftAssertions; +import org.eclipse.jnosql.communication.column.Column; +import org.eclipse.jnosql.communication.column.ColumnCondition; +import org.eclipse.jnosql.communication.column.ColumnDeleteQuery; +import org.eclipse.jnosql.communication.column.ColumnEntity; +import org.eclipse.jnosql.communication.column.ColumnManager; +import org.eclipse.jnosql.communication.column.ColumnQuery; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.IdNotFoundException; +import org.eclipse.jnosql.mapping.column.entities.Job; +import org.eclipse.jnosql.mapping.column.entities.Person; +import org.eclipse.jnosql.mapping.column.spi.ColumnExtension; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; + +import java.time.Duration; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.eclipse.jnosql.communication.column.ColumnDeleteQuery.delete; +import static org.eclipse.jnosql.communication.column.ColumnQuery.select; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, ColumnEntityConverter.class}) +@AddPackages(MockProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, ColumnExtension.class}) +class DefaultColumnTemplateTest { + + private final Person person = Person.builder(). + withAge(). + withPhones(Arrays.asList("234", "432")). + withName("Name") + .withId(19) + .withIgnore().build(); + + private final Column[] columns = new Column[]{ + Column.of("age", 10), + Column.of("phones", Arrays.asList("234", "432")), + Column.of("name", "Name"), + Column.of("id", 19L), + }; + + @Inject + private ColumnEntityConverter converter; + + @Inject + private EntitiesMetadata entities; + + @Inject + private Converters converters; + + private ColumnManager managerMock; + + private DefaultColumnTemplate template; + + private ArgumentCaptor captor; + + private ColumnEventPersistManager columnEventPersistManager; + + @SuppressWarnings("unchecked") + @BeforeEach + void setUp() { + managerMock = Mockito.mock(ColumnManager.class); + columnEventPersistManager = Mockito.mock(ColumnEventPersistManager.class); + captor = ArgumentCaptor.forClass(ColumnEntity.class); + Instance instance = Mockito.mock(Instance.class); + Mockito.when(instance.get()).thenReturn(managerMock); + this.template = new DefaultColumnTemplate(converter, instance, + columnEventPersistManager, entities, converters); + } + + @Test + void shouldInsert() { + ColumnEntity columnEntity = ColumnEntity.of("Person"); + columnEntity.addAll(Stream.of(columns).collect(Collectors.toList())); + + Mockito.when(managerMock + .insert(any(ColumnEntity.class))) + .thenReturn(columnEntity); + + template.insert(this.person); + verify(managerMock).insert(captor.capture()); + verify(columnEventPersistManager).firePostEntity(any(Person.class)); + verify(columnEventPersistManager).firePreEntity(any(Person.class)); + ColumnEntity value = captor.getValue(); + assertEquals("Person", value.name()); + assertEquals(4, value.columns().size()); + } + + + @Test + void shouldMergeOnInsert() { + ColumnEntity columnEntity = ColumnEntity.of("Person"); + columnEntity.addAll(Stream.of(columns).collect(Collectors.toList())); + + Mockito.when(managerMock + .insert(any(ColumnEntity.class))) + .thenReturn(columnEntity); + + Person person = Person.builder().build(); + Person result = template.insert(person); + verify(managerMock).insert(captor.capture()); + verify(columnEventPersistManager).firePostEntity(any(Person.class)); + verify(columnEventPersistManager).firePreEntity(any(Person.class)); + assertSame(person, result); + assertEquals(10, person.getAge()); + + } + + + + + @Test + void shouldInsertTTL() { + ColumnEntity columnEntity = ColumnEntity.of("Person"); + columnEntity.addAll(Stream.of(columns).collect(Collectors.toList())); + + Mockito.when(managerMock + .insert(any(ColumnEntity.class), + any(Duration.class))) + .thenReturn(columnEntity); + + template.insert(this.person, Duration.ofHours(2)); + verify(managerMock).insert(captor.capture(), Mockito.eq(Duration.ofHours(2))); + verify(columnEventPersistManager).firePostEntity(any(Person.class)); + verify(columnEventPersistManager).firePreEntity(any(Person.class)); + ColumnEntity value = captor.getValue(); + assertEquals("Person", value.name()); + assertEquals(4, value.columns().size()); + } + + @Test + void shouldUpdate() { + ColumnEntity columnEntity = ColumnEntity.of("Person"); + columnEntity.addAll(Stream.of(columns).collect(Collectors.toList())); + + Mockito.when(managerMock + .update(any(ColumnEntity.class))) + .thenReturn(columnEntity); + + template.update(this.person); + verify(managerMock).update(captor.capture()); + verify(columnEventPersistManager).firePostEntity(any(Person.class)); + verify(columnEventPersistManager).firePreEntity(any(Person.class)); + ColumnEntity value = captor.getValue(); + assertEquals("Person", value.name()); + assertEquals(4, value.columns().size()); + } + + @Test + void shouldMergeOnUpdate() { + ColumnEntity columnEntity = ColumnEntity.of("Person"); + columnEntity.addAll(Stream.of(columns).collect(Collectors.toList())); + + Mockito.when(managerMock + .update(any(ColumnEntity.class))) + .thenReturn(columnEntity); + + Person person = Person.builder().build(); + Person result = template.update(person); + verify(managerMock).update(captor.capture()); + verify(columnEventPersistManager).firePostEntity(any(Person.class)); + verify(columnEventPersistManager).firePreEntity(any(Person.class)); + assertSame(person, result); + assertEquals(10, person.getAge()); + + } + + @Test + void shouldInsertEntitiesTTL() { + ColumnEntity columnEntity = ColumnEntity.of("Person"); + columnEntity.addAll(Stream.of(columns).collect(Collectors.toList())); + Duration duration = Duration.ofHours(2); + + Mockito.when(managerMock + .insert(any(ColumnEntity.class), Mockito.eq(duration))) + .thenReturn(columnEntity); + + template.insert(Arrays.asList(person, person), duration); + verify(managerMock, times(2)).insert(any(ColumnEntity.class), any(Duration.class)); + } + + @Test + void shouldInsertEntities() { + ColumnEntity columnEntity = ColumnEntity.of("Person"); + columnEntity.addAll(Stream.of(columns).collect(Collectors.toList())); + + Mockito.when(managerMock + .insert(any(ColumnEntity.class))) + .thenReturn(columnEntity); + + template.insert(Arrays.asList(person, person)); + verify(managerMock, times(2)).insert(any(ColumnEntity.class)); + } + + @Test + void shouldUpdateEntities() { + ColumnEntity columnEntity = ColumnEntity.of("Person"); + columnEntity.addAll(Stream.of(columns).collect(Collectors.toList())); + + Mockito.when(managerMock + .update(any(ColumnEntity.class))) + .thenReturn(columnEntity); + + template.update(Arrays.asList(person, person)); + verify(managerMock, times(2)).update(any(ColumnEntity.class)); + } + + @Test + void shouldDelete() { + + ColumnDeleteQuery query = delete().from("delete").build(); + template.delete(query); + verify(managerMock).delete(query); + } + + @Test + void shouldSelect() { + ColumnQuery query = select().from("person").build(); + template.select(query); + verify(managerMock).select(query); + } + + @Test + void shouldCountBy() { + ColumnQuery query = select().from("person").build(); + template.count(query); + verify(managerMock).count(query); + } + + @Test + void shouldExist() { + ColumnQuery query = select().from("person").build(); + template.exists(query); + verify(managerMock).exists(query); + } + + @Test + void shouldReturnSingleResult() { + ColumnEntity columnEntity = ColumnEntity.of("Person"); + columnEntity.addAll(Stream.of(columns).collect(Collectors.toList())); + + Mockito.when(managerMock + .select(any(ColumnQuery.class))) + .thenReturn(Stream.of(columnEntity)); + + ColumnQuery query = select().from("person").build(); + + Optional result = template.singleResult(query); + assertTrue(result.isPresent()); + } + + @Test + void shouldReturnSingleResultIsEmpty() { + Mockito.when(managerMock + .select(any(ColumnQuery.class))) + .thenReturn(Stream.empty()); + + ColumnQuery query = select().from("person").build(); + + Optional result = template.singleResult(query); + assertFalse(result.isPresent()); + } + + @Test + void shouldReturnErrorWhenThereMoreThanASingleResult() { + Assertions.assertThrows(NonUniqueResultException.class, () -> { + ColumnEntity columnEntity = ColumnEntity.of("Person"); + columnEntity.addAll(Stream.of(columns).collect(Collectors.toList())); + + Mockito.when(managerMock + .select(any(ColumnQuery.class))) + .thenReturn(Stream.of(columnEntity, columnEntity)); + + ColumnQuery query = select().from("person").build(); + + template.singleResult(query); + }); + } + + + @Test + void shouldReturnErrorWhenFindIdHasIdNull() { + Assertions.assertThrows(NullPointerException.class, () -> template.find(Person.class, null)); + } + + @Test + void shouldReturnErrorWhenFindIdHasClassNull() { + Assertions.assertThrows(NullPointerException.class, () -> template.find(null, "10")); + } + + @Test + void shouldReturnErrorWhenThereIsNotIdInFind() { + Assertions.assertThrows(IdNotFoundException.class, () -> template.find(Job.class, "10")); + } + + @Test + void shouldReturnFind() { + template.find(Person.class, "10"); + ArgumentCaptor queryCaptor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(managerMock).select(queryCaptor.capture()); + ColumnQuery query = queryCaptor.getValue(); + ColumnCondition condition = query.condition().get(); + + assertEquals("Person", query.name()); + assertEquals(ColumnCondition.eq(Column.of("_id", 10L)), condition); + } + + @Test + void shouldDeleteEntity() { + template.delete(Person.class, "10"); + ArgumentCaptor queryCaptor = ArgumentCaptor.forClass(ColumnDeleteQuery.class); + verify(managerMock).delete(queryCaptor.capture()); + + ColumnDeleteQuery query = queryCaptor.getValue(); + + ColumnCondition condition = query.condition().get(); + + assertEquals("Person", query.name()); + assertEquals(ColumnCondition.eq(Column.of("_id", 10L)), condition); + } + + @Test + void shouldExecuteQuery() { + template.query("select * from Person"); + ArgumentCaptor queryCaptor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(managerMock).select(queryCaptor.capture()); + ColumnQuery query = queryCaptor.getValue(); + assertEquals("Person", query.name()); + } + + @Test + void shouldConvertEntity() { + template.query("select * from Movie"); + ArgumentCaptor queryCaptor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(managerMock).select(queryCaptor.capture()); + ColumnQuery query = queryCaptor.getValue(); + assertEquals("movie", query.name()); + } + + @Test + void shouldConvertEntityName() { + template.query("select * from download"); + ArgumentCaptor queryCaptor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(managerMock).select(queryCaptor.capture()); + ColumnQuery query = queryCaptor.getValue(); + assertEquals("download", query.name()); + } + @Test + void shouldConvertEntityNameClassName() { + template.query("select * from " + Person.class.getName()); + ArgumentCaptor queryCaptor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(managerMock).select(queryCaptor.capture()); + ColumnQuery query = queryCaptor.getValue(); + assertEquals("Person", query.name()); + } + + @Test + void shouldConvertConvertFromAnnotationEntity(){ + template.query("select * from Vendor" ); + ArgumentCaptor queryCaptor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(managerMock).select(queryCaptor.capture()); + ColumnQuery query = queryCaptor.getValue(); + assertEquals("vendors", query.name()); + } + + @Test + void shouldPreparedStatement() { + PreparedStatement preparedStatement = template.prepare("select * from Person where name = @name"); + preparedStatement.bind("name", "Ada"); + preparedStatement.result(); + ArgumentCaptor queryCaptor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(managerMock).select(queryCaptor.capture()); + ColumnQuery query = queryCaptor.getValue(); + assertEquals("Person", query.name()); + } + + @Test + void shouldCount() { + template.count("Person"); + verify(managerMock).count("Person"); + } + + @Test + void shouldCountFromEntityClass() { + template.count(Person.class); + var captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(managerMock).count(captor.capture()); + var query = captor.getValue(); + SoftAssertions.assertSoftly(soft ->{ + soft.assertThat(query.condition()).isEmpty(); + }); + } + + + @Test + void shouldFindAll(){ + template.findAll(Person.class); + verify(managerMock).select(select().from("Person").build()); + } + + @Test + void shouldDeleteAll(){ + template.deleteAll(Person.class); + verify(managerMock).delete(delete().from("Person").build()); + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/MapperDeleteTest.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/MapperDeleteTest.java new file mode 100644 index 000000000..d5df16897 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/MapperDeleteTest.java @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; +import org.eclipse.jnosql.communication.column.ColumnDeleteQuery; +import org.eclipse.jnosql.communication.column.ColumnManager; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.column.entities.Address; +import org.eclipse.jnosql.mapping.column.entities.Money; +import org.eclipse.jnosql.mapping.column.entities.Person; +import org.eclipse.jnosql.mapping.column.entities.Worker; +import org.eclipse.jnosql.mapping.column.spi.ColumnExtension; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; + +import java.math.BigDecimal; + +import static org.eclipse.jnosql.communication.column.ColumnDeleteQuery.delete; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, ColumnEntityConverter.class}) +@AddPackages(MockProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, ColumnExtension.class}) +class MapperDeleteTest { + + @Inject + private ColumnEntityConverter converter; + + @Inject + private EntitiesMetadata entities; + + @Inject + private Converters converters; + + private ColumnManager managerMock; + + private DefaultColumnTemplate template; + + + private ArgumentCaptor captor; + + @BeforeEach + void setUp() { + managerMock = Mockito.mock(ColumnManager.class); + ColumnEventPersistManager persistManager = Mockito.mock(ColumnEventPersistManager.class); + Instance instance = Mockito.mock(Instance.class); + this.captor = ArgumentCaptor.forClass(ColumnDeleteQuery.class); + when(instance.get()).thenReturn(managerMock); + this.template = new DefaultColumnTemplate(converter, instance, + persistManager, entities, converters); + } + + @Test + void shouldReturnDeleteFrom() { + template.delete(Person.class).execute(); + Mockito.verify(managerMock).delete(captor.capture()); + ColumnDeleteQuery query = captor.getValue(); + ColumnDeleteQuery queryExpected = delete().from("Person").build(); + assertEquals(queryExpected, query); + } + + + @Test + void shouldDeleteWhereEq() { + template.delete(Person.class).where("name").eq("Ada").execute(); + Mockito.verify(managerMock).delete(captor.capture()); + ColumnDeleteQuery query = captor.getValue(); + + ColumnDeleteQuery queryExpected = delete().from("Person").where("name") + .eq("Ada").build(); + assertEquals(queryExpected, query); + } + + @Test + void shouldDeleteWhereLike() { + template.delete(Person.class).where("name").like("Ada").execute(); + Mockito.verify(managerMock).delete(captor.capture()); + ColumnDeleteQuery query = captor.getValue(); + ColumnDeleteQuery queryExpected = delete().from("Person").where("name") + .like("Ada").build(); + assertEquals(queryExpected, query); + } + + @Test + void shouldDeleteWhereGt() { + template.delete(Person.class).where("id").gt(10).execute(); + Mockito.verify(managerMock).delete(captor.capture()); + ColumnDeleteQuery query = captor.getValue(); + ColumnDeleteQuery queryExpected = delete().from("Person").where("_id").gt(10L).build(); + assertEquals(queryExpected, query); + } + + @Test + void shouldDeleteWhereGte() { + template.delete(Person.class).where("id").gte(10).execute(); + Mockito.verify(managerMock).delete(captor.capture()); + ColumnDeleteQuery query = captor.getValue(); + ColumnDeleteQuery queryExpected = delete().from("Person").where("_id") + .gte(10L).build(); + assertEquals(queryExpected, query); + } + + @Test + void shouldDeleteWhereLt() { + template.delete(Person.class).where("id").lt(10).execute(); + Mockito.verify(managerMock).delete(captor.capture()); + ColumnDeleteQuery query = captor.getValue(); + ColumnDeleteQuery queryExpected = delete().from("Person").where("_id").lt(10L).build(); + assertEquals(queryExpected, query); + } + + @Test + void shouldDeleteWhereLte() { + template.delete(Person.class).where("id").lte(10).execute(); + Mockito.verify(managerMock).delete(captor.capture()); + ColumnDeleteQuery query = captor.getValue(); + ColumnDeleteQuery queryExpected = delete().from("Person").where("_id").lte(10L).build(); + assertEquals(queryExpected, query); + } + + @Test + void shouldDeleteWhereBetween() { + template.delete(Person.class).where("id") + .between(10, 20).execute(); + Mockito.verify(managerMock).delete(captor.capture()); + ColumnDeleteQuery query = captor.getValue(); + ColumnDeleteQuery queryExpected = delete().from("Person").where("_id") + .between(10L, 20L).build(); + assertEquals(queryExpected, query); + } + + @Test + void shouldDeleteWhereNot() { + template.delete(Person.class).where("name").not().like("Ada").execute(); + Mockito.verify(managerMock).delete(captor.capture()); + ColumnDeleteQuery query = captor.getValue(); + ColumnDeleteQuery queryExpected = delete().from("Person").where("name").not().like("Ada").build(); + assertEquals(queryExpected, query); + } + + + @Test + void shouldDeleteWhereAnd() { + template.delete(Person.class).where("age").between(10, 20) + .and("name").eq("Ada").execute(); + Mockito.verify(managerMock).delete(captor.capture()); + ColumnDeleteQuery query = captor.getValue(); + ColumnDeleteQuery queryExpected = delete().from("Person").where("age") + .between(10, 20) + .and("name").eq("Ada").build(); + + assertEquals(queryExpected, query); + } + + @Test + void shouldDeleteWhereOr() { + template.delete(Person.class).where("id").between(10, 20) + .or("name").eq("Ada").execute(); + Mockito.verify(managerMock).delete(captor.capture()); + ColumnDeleteQuery query = captor.getValue(); + ColumnDeleteQuery queryExpected = delete().from("Person").where("_id") + .between(10L, 20L) + .or("name").eq("Ada").build(); + + assertEquals(queryExpected, query); + } + + @Test + void shouldConvertField() { + template.delete(Person.class).where("id").eq("20") + .execute(); + Mockito.verify(managerMock).delete(captor.capture()); + ColumnDeleteQuery query = captor.getValue(); + ColumnDeleteQuery queryExpected = delete().from("Person").where("_id").eq(20L) + .build(); + + assertEquals(queryExpected, query); + } + + @Test + void shouldUseAttributeConverter() { + template.delete(Worker.class).where("salary") + .eq(new Money("USD", BigDecimal.TEN)).execute(); + Mockito.verify(managerMock).delete(captor.capture()); + ColumnDeleteQuery query = captor.getValue(); + ColumnDeleteQuery queryExpected = delete().from("Worker").where("money") + .eq("USD 10").build(); + assertEquals(queryExpected, query); + } + + @Test + void shouldQueryByEmbeddable() { + template.delete(Worker.class).where("job.city").eq("Salvador") + .execute(); + + Mockito.verify(managerMock).delete(captor.capture()); + ColumnDeleteQuery query = captor.getValue(); + ColumnDeleteQuery queryExpected = delete().from("Worker").where("city").eq("Salvador") + .build(); + + assertEquals(queryExpected, query); + } + + @Test + void shouldQueryBySubEntity() { + template.delete(Address.class).where("zipCode.zip").eq("01312321") + .execute(); + + Mockito.verify(managerMock).delete(captor.capture()); + ColumnDeleteQuery query = captor.getValue(); + + ColumnDeleteQuery queryExpected = delete().from("Address").where("zipCode.zip").eq("01312321") + .build(); + + assertEquals(queryExpected, query); + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/MapperSelectTest.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/MapperSelectTest.java new file mode 100644 index 000000000..d112b6dc6 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/MapperSelectTest.java @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; +import org.eclipse.jnosql.communication.column.ColumnEntity; +import org.eclipse.jnosql.communication.column.ColumnManager; +import org.eclipse.jnosql.communication.column.ColumnQuery; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.column.entities.Address; +import org.eclipse.jnosql.mapping.column.entities.Money; +import org.eclipse.jnosql.mapping.column.entities.Person; +import org.eclipse.jnosql.mapping.column.entities.Worker; +import org.eclipse.jnosql.mapping.column.spi.ColumnExtension; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.jnosql.communication.column.ColumnQuery.select; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, ColumnEntityConverter.class}) +@AddPackages(MockProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, ColumnExtension.class}) +class MapperSelectTest { + + @Inject + private ColumnEntityConverter converter; + + @Inject + private EntitiesMetadata entities; + + @Inject + private Converters converters; + + private ColumnManager managerMock; + + private DefaultColumnTemplate template; + + private ArgumentCaptor captor; + + @BeforeEach + public void setUp() { + managerMock = Mockito.mock(ColumnManager.class); + ColumnEventPersistManager persistManager = Mockito.mock(ColumnEventPersistManager.class); + Instance instance = Mockito.mock(Instance.class); + this.captor = ArgumentCaptor.forClass(ColumnQuery.class); + when(instance.get()).thenReturn(managerMock); + this.template = new DefaultColumnTemplate(converter, instance, + persistManager, entities, converters); + } + + + @Test + void shouldExecuteSelectFrom() { + template.select(Person.class).result(); + ColumnQuery queryExpected = select().from("Person").build(); + Mockito.verify(managerMock).select(captor.capture()); + ColumnQuery query = captor.getValue(); + assertEquals(queryExpected, query); + } + + @Test + void shouldSelectOrderAsc() { + template.select(Worker.class).orderBy("salary").asc().result(); + Mockito.verify(managerMock).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnQuery queryExpected = select().from("Worker").orderBy("money").asc().build(); + assertEquals(queryExpected, query); + } + + @Test + void shouldSelectOrderDesc() { + template.select(Worker.class).orderBy("salary").desc().result(); + ColumnQuery queryExpected = select().from("Worker").orderBy("money").desc().build(); + Mockito.verify(managerMock).select(captor.capture()); + ColumnQuery query = captor.getValue(); + assertEquals(queryExpected, query); + } + + @Test + void shouldSelectLimit() { + template.select(Worker.class).limit(10).result(); + ColumnQuery queryExpected = select().from("Worker").limit(10L).build(); + Mockito.verify(managerMock).select(captor.capture()); + ColumnQuery query = captor.getValue(); + assertEquals(queryExpected, query); + } + + @Test + void shouldSelectStart() { + template.select(Worker.class).skip(10).result(); + ColumnQuery queryExpected = select().from("Worker").skip(10L).build(); + Mockito.verify(managerMock).select(captor.capture()); + ColumnQuery query = captor.getValue(); + assertEquals(queryExpected, query); + } + + + @Test + void shouldSelectWhereEq() { + template.select(Person.class).where("name").eq("Ada").result(); + ColumnQuery queryExpected = select().from("Person").where("name") + .eq("Ada").build(); + Mockito.verify(managerMock).select(captor.capture()); + ColumnQuery query = captor.getValue(); + assertEquals(queryExpected, query); + } + + @Test + void shouldSelectWhereLike() { + template.select(Person.class).where("name").like("Ada").result(); + ColumnQuery queryExpected = select().from("Person").where("name") + .like("Ada").build(); + Mockito.verify(managerMock).select(captor.capture()); + ColumnQuery query = captor.getValue(); + assertEquals(queryExpected, query); + } + + @Test + void shouldSelectWhereGt() { + template.select(Person.class).where("id").gt(10).result(); + ColumnQuery queryExpected = select().from("Person").where("_id") + .gt(10L).build(); + Mockito.verify(managerMock).select(captor.capture()); + ColumnQuery query = captor.getValue(); + assertEquals(queryExpected, query); + } + + @Test + void shouldSelectWhereGte() { + template.select(Person.class).where("id").gte(10).result(); + ColumnQuery queryExpected = select().from("Person").where("_id") + .gte(10L).build(); + Mockito.verify(managerMock).select(captor.capture()); + ColumnQuery query = captor.getValue(); + assertEquals(queryExpected, query); + } + + + @Test + void shouldSelectWhereLt() { + template.select(Person.class).where("id").lt(10).result(); + ColumnQuery queryExpected = select().from("Person").where("_id") + .lt(10L).build(); + Mockito.verify(managerMock).select(captor.capture()); + ColumnQuery query = captor.getValue(); + assertEquals(queryExpected, query); + } + + @Test + void shouldSelectWhereLte() { + template.select(Person.class).where("id").lte(10).result(); + ColumnQuery queryExpected = select().from("Person").where("_id") + .lte(10L).build(); + Mockito.verify(managerMock).select(captor.capture()); + ColumnQuery query = captor.getValue(); + assertEquals(queryExpected, query); + } + + @Test + void shouldSelectWhereBetween() { + template.select(Person.class).where("id") + .between(10, 20).result(); + ColumnQuery queryExpected = select().from("Person").where("_id") + .between(10L, 20L).build(); + Mockito.verify(managerMock).select(captor.capture()); + ColumnQuery query = captor.getValue(); + assertEquals(queryExpected, query); + } + + @Test + void shouldSelectWhereNot() { + template.select(Person.class).where("name").not().like("Ada").result(); + ColumnQuery queryExpected = select().from("Person").where("name") + .not().like("Ada").build(); + Mockito.verify(managerMock).select(captor.capture()); + ColumnQuery query = captor.getValue(); + assertEquals(queryExpected, query); + } + + + @Test + void shouldSelectWhereAnd() { + template.select(Person.class).where("age").between(10, 20) + .and("name").eq("Ada").result(); + ColumnQuery queryExpected = select().from("Person").where("age") + .between(10, 20) + .and("name").eq("Ada").build(); + Mockito.verify(managerMock).select(captor.capture()); + ColumnQuery query = captor.getValue(); + + assertEquals(queryExpected, query); + } + + @Test + void shouldSelectWhereOr() { + template.select(Person.class).where("id").between(10, 20) + .or("name").eq("Ada").result(); + ColumnQuery queryExpected = select().from("Person").where("_id") + .between(10L, 20L) + .or("name").eq("Ada").build(); + + Mockito.verify(managerMock).select(captor.capture()); + ColumnQuery query = captor.getValue(); + assertEquals(queryExpected, query); + } + + @Test + void shouldConvertField() { + template.select(Person.class).where("id").eq("20") + .result(); + ColumnQuery queryExpected = select().from("Person").where("_id").eq(20L) + .build(); + + Mockito.verify(managerMock).select(captor.capture()); + ColumnQuery query = captor.getValue(); + + assertEquals(queryExpected, query); + } + + @Test + void shouldUseAttributeConverter() { + template.select(Worker.class).where("salary") + .eq(new Money("USD", BigDecimal.TEN)).result(); + ColumnQuery queryExpected = select().from("Worker").where("money") + .eq("USD 10").build(); + + Mockito.verify(managerMock).select(captor.capture()); + ColumnQuery query = captor.getValue(); + assertEquals(queryExpected, query); + } + + @Test + void shouldQueryByEmbeddable() { + template.select(Worker.class).where("job.city").eq("Salvador") + .result(); + ColumnQuery queryExpected = select().from("Worker").where("city") + .eq("Salvador") + .build(); + + Mockito.verify(managerMock).select(captor.capture()); + ColumnQuery query = captor.getValue(); + assertEquals(queryExpected, query); + } + + @Test + void shouldQueryBySubEntity() { + template.select(Address.class).where("zipCode.zip").eq("01312321") + .result(); + ColumnQuery queryExpected = select().from("Address").where("zipCode.zip") + .eq("01312321") + .build(); + + Mockito.verify(managerMock).select(captor.capture()); + ColumnQuery query = captor.getValue(); + assertEquals(queryExpected, query); + } + + + @Test + void shouldResult() { + ColumnQuery query = select().from("Person").build(); + ColumnEntity entity = ColumnEntity.of("Person"); + entity.add("_id", 1L); + entity.add("name", "Ada"); + entity.add("age", 20); + Mockito.when(managerMock.select(query)).thenReturn(Stream.of(entity)); + List result = template.select(Person.class).result(); + Assertions.assertNotNull(result); + assertThat(result).hasSize(1) + .map(Person::getName).contains("Ada"); + } + + + @Test + void shouldStream() { + + ColumnQuery query = select().from("Person").build(); + ColumnEntity entity = ColumnEntity.of("Person"); + entity.add("_id", 1L); + entity.add("name", "Ada"); + entity.add("age", 20); + Mockito.when(managerMock.select(query)).thenReturn(Stream.of(entity)); + Stream result = template.select(Person.class).stream(); + Assertions.assertNotNull(result); + } + + @Test + void shouldSingleResult() { + + ColumnQuery query = select().from("Person").build(); + ColumnEntity entity = ColumnEntity.of("Person"); + entity.add("_id", 1L); + entity.add("name", "Ada"); + entity.add("age", 20); + Mockito.when(managerMock.select(query)).thenReturn(Stream.of(entity)); + Optional result = template.select(Person.class).singleResult(); + Assertions.assertNotNull(result); + Assertions.assertTrue(result.isPresent()); + } + + @Test + void shouldReturnErrorSelectWhenOrderIsNull() { + Assertions.assertThrows(NullPointerException.class, () -> template.select(Worker.class).orderBy(null)); + } + +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/MockProducer.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/MockProducer.java new file mode 100644 index 000000000..535deaf61 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/MockProducer.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Alternative; +import jakarta.enterprise.inject.Produces; +import jakarta.interceptor.Interceptor; +import org.eclipse.jnosql.communication.column.Column; +import org.eclipse.jnosql.communication.column.ColumnEntity; +import org.eclipse.jnosql.communication.column.ColumnManager; +import org.eclipse.jnosql.mapping.Database; +import org.eclipse.jnosql.mapping.DatabaseType; +import org.mockito.Mockito; + +import java.util.function.Supplier; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@ApplicationScoped +@Alternative +@Priority(Interceptor.Priority.APPLICATION) +public class MockProducer implements Supplier { + + + @Produces + @Override + public ColumnManager get() { + ColumnEntity entity = ColumnEntity.of("Person"); + entity.add(Column.of("name", "Default")); + entity.add(Column.of("age", 10)); + ColumnManager manager = mock(ColumnManager.class); + when(manager.insert(Mockito.any(ColumnEntity.class))).thenReturn(entity); + return manager; + + } + + @Produces + @Database(value = DatabaseType.COLUMN, provider = "columnRepositoryMock") + public ColumnManager getColumnManagerMock() { + ColumnEntity entity = ColumnEntity.of("Person"); + entity.add(Column.of("name", "columnRepositoryMock")); + entity.add(Column.of("age", 10)); + ColumnManager manager = mock(ColumnManager.class); + when(manager.insert(Mockito.any(ColumnEntity.class))).thenReturn(entity); + return manager; + + } + +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/UserScope.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/UserScope.java new file mode 100644 index 000000000..f4162b9f4 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/UserScope.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column; + + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import jakarta.nosql.Id; + +import java.util.HashMap; +import java.util.Map; + +@Entity +public class UserScope { + + @Id + private String userName; + + @Column("scope") + private String scope; + + @Column("properties") + private Map properties = new HashMap<>(); + + public String getUserName() { + return userName; + } + + public String getScope() { + return scope; + } + + public Map getProperties() { + return properties; + } +} \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/configuration/ColumnConfigurationMock.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/configuration/ColumnConfigurationMock.java new file mode 100644 index 000000000..70e66dfdf --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/configuration/ColumnConfigurationMock.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.configuration; + +import org.eclipse.jnosql.communication.Settings; +import org.eclipse.jnosql.communication.column.ColumnConfiguration; +import org.eclipse.jnosql.communication.column.ColumnDeleteQuery; +import org.eclipse.jnosql.communication.column.ColumnEntity; +import org.eclipse.jnosql.communication.column.ColumnManager; +import org.eclipse.jnosql.communication.column.ColumnManagerFactory; +import org.eclipse.jnosql.communication.column.ColumnQuery; + +import java.time.Duration; +import java.util.stream.Stream; + +class ColumnConfigurationMock implements ColumnConfiguration { + + + @Override + public ColumnManagerFactoryMock apply(Settings settings) { + return new ColumnManagerFactoryMock(settings); + } + + + public record ColumnManagerFactoryMock(Settings settings) implements ColumnManagerFactory { + + @Override + public ColumnManagerMock apply(String database) { + return new ColumnManagerMock(database); + } + + @Override + public void close() { + + } + } + + public record ColumnManagerMock(String name) implements ColumnManager { + + @Override + public ColumnEntity insert(ColumnEntity entity) { + return null; + } + + @Override + public ColumnEntity update(ColumnEntity entity) { + return null; + } + + @Override + public Iterable update(Iterable entities) { + return null; + } + + @Override + public ColumnEntity insert(ColumnEntity entity, Duration ttl) { + return null; + } + + @Override + public Iterable insert(Iterable entities) { + return null; + } + + @Override + public Iterable insert(Iterable entities, Duration ttl) { + return null; + } + + @Override + public void delete(ColumnDeleteQuery query) { + + } + + @Override + public Stream select(ColumnQuery query) { + return null; + } + + @Override + public long count(String columnFamily) { + return 0; + } + + @Override + public void close() { + + } + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/configuration/ColumnConfigurationMock2.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/configuration/ColumnConfigurationMock2.java new file mode 100644 index 000000000..397e0ad1a --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/configuration/ColumnConfigurationMock2.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.configuration; + +import org.eclipse.jnosql.communication.Settings; +import org.eclipse.jnosql.communication.column.ColumnConfiguration; +import org.eclipse.jnosql.communication.column.ColumnDeleteQuery; +import org.eclipse.jnosql.communication.column.ColumnEntity; +import org.eclipse.jnosql.communication.column.ColumnManager; +import org.eclipse.jnosql.communication.column.ColumnManagerFactory; +import org.eclipse.jnosql.communication.column.ColumnQuery; + +import java.time.Duration; +import java.util.stream.Stream; + +public class ColumnConfigurationMock2 implements ColumnConfiguration { + + + @Override + public ColumnManagerFactoryMock apply(Settings settings) { + return new ColumnManagerFactoryMock(settings); + } + + + public record ColumnManagerFactoryMock(Settings settings) implements ColumnManagerFactory { + + @Override + public ColumnManagerMock apply(String database) { + return new ColumnManagerMock(database); + } + + @Override + public void close() { + + } + } + + public record ColumnManagerMock(String name) implements ColumnManager { + + @Override + public ColumnEntity insert(ColumnEntity entity) { + return null; + } + + @Override + public ColumnEntity update(ColumnEntity entity) { + return null; + } + + @Override + public Iterable update(Iterable entities) { + return null; + } + + @Override + public ColumnEntity insert(ColumnEntity entity, Duration ttl) { + return null; + } + + @Override + public Iterable insert(Iterable entities) { + return null; + } + + @Override + public Iterable insert(Iterable entities, Duration ttl) { + return null; + } + + @Override + public void delete(ColumnDeleteQuery query) { + + } + + @Override + public Stream select(ColumnQuery query) { + return null; + } + + @Override + public long count(String columnFamily) { + return 0; + } + + @Override + public void close() { + + } + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/configuration/ColumnManagerSupplierTest.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/configuration/ColumnManagerSupplierTest.java new file mode 100644 index 000000000..1bf7b8ebd --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/configuration/ColumnManagerSupplierTest.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.configuration; + +import jakarta.data.exceptions.MappingException; +import jakarta.inject.Inject; +import org.eclipse.jnosql.communication.column.ColumnManager; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.column.ColumnEntityConverter; +import org.eclipse.jnosql.mapping.column.MockProducer; +import org.eclipse.jnosql.mapping.column.spi.ColumnExtension; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.jnosql.mapping.core.config.MappingConfigurations.COLUMN_DATABASE; +import static org.eclipse.jnosql.mapping.core.config.MappingConfigurations.COLUMN_PROVIDER; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, ColumnEntityConverter.class}) +@AddPackages(MockProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, ColumnExtension.class}) +class ColumnManagerSupplierTest { + + @Inject + private ColumnManagerSupplier supplier; + + @BeforeEach + void beforeEach(){ + System.clearProperty(COLUMN_PROVIDER.get()); + System.clearProperty(COLUMN_DATABASE.get()); + } + + @Test + void shouldGetManager() { + System.setProperty(COLUMN_PROVIDER.get(), ColumnConfigurationMock.class.getName()); + System.setProperty(COLUMN_DATABASE.get(), "database"); + ColumnManager manager = supplier.get(); + Assertions.assertNotNull(manager); + assertThat(manager).isInstanceOf(ColumnConfigurationMock.ColumnManagerMock.class); + } + + + @Test + void shouldUseDefaultConfigurationWhenProviderIsWrong() { + System.setProperty(COLUMN_PROVIDER.get(), Integer.class.getName()); + System.setProperty(COLUMN_DATABASE.get(), "database"); + ColumnManager manager = supplier.get(); + Assertions.assertNotNull(manager); + assertThat(manager).isInstanceOf(ColumnConfigurationMock2.ColumnManagerMock.class); + } + + @Test + void shouldUseDefaultConfiguration() { + System.setProperty(COLUMN_DATABASE.get(), "database"); + ColumnManager manager = supplier.get(); + Assertions.assertNotNull(manager); + assertThat(manager).isInstanceOf(ColumnConfigurationMock2.ColumnManagerMock.class); + } + + @Test + void shouldReturnErrorWhenThereIsNotDatabase() { + Assertions.assertThrows(MappingException.class, () -> supplier.get()); + } + + @Test + void shouldClose(){ + ColumnManager manager = Mockito.mock(ColumnManager.class); + supplier.close(manager); + Mockito.verify(manager).close(); + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Actor.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Actor.java new file mode 100644 index 000000000..71e5792e2 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Actor.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + + + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; + +import java.util.List; +import java.util.Map; + + +@Entity +public class Actor extends Person { + + @Column + private Map movieCharacter; + + @Column + private Map movieRating; + + Actor(long id, String name, int age, List phones, String ignore, Map movieCharacter, Map movieRating) { + super(id, name, age, phones, ignore); + this.movieCharacter = movieCharacter; + this.movieRating = movieRating; + } + + Actor() { + } + + public Map getMovieCharacter() { + return movieCharacter; + } + + public Map getMovieRating() { + return movieRating; + } + + public static ActorBuilder actorBuilder() { + return new ActorBuilder(); + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/ActorBuilder.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/ActorBuilder.java new file mode 100644 index 000000000..226ed3e0e --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/ActorBuilder.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + +import java.util.List; +import java.util.Map; + +public class ActorBuilder { + private long id; + private String name; + private int age; + private List phones; + private String ignore; + private Map movieCharacter; + private Map movieRating; + + ActorBuilder() { + } + + public ActorBuilder withId() { + this.id = 12; + return this; + } + + public ActorBuilder withName() { + this.name = "Otavio"; + return this; + } + + public ActorBuilder withAge() { + this.age = 10; + return this; + } + + public ActorBuilder withPhones(List phones) { + this.phones = phones; + return this; + } + + public ActorBuilder withIgnore(String ignore) { + this.ignore = ignore; + return this; + } + + public ActorBuilder withMovieCharacter(Map movieCharacter) { + this.movieCharacter = movieCharacter; + return this; + } + + public ActorBuilder withMovieRating(Map movieRating) { + this.movieRating = movieRating; + return this; + } + + public Actor build() { + return new Actor(id, name, age, phones, ignore, movieCharacter, movieRating); + } +} \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Address.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Address.java new file mode 100644 index 000000000..b215cd47b --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Address.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; + +@Entity +public class Address { + + @Column + private String street; + @Column + private String city; + @Column + private String state; + @Column + private ZipCode zipCode; + + public String getStreet() { + return street; + } + + public void setStreet(String street) { + this.street = street; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public ZipCode getZipCode() { + return zipCode; + } + + public void setZipCode(ZipCode zipCode) { + this.zipCode = zipCode; + } + + @Override + public String toString() { + return "Address{" + + "street='" + street + '\'' + + ", city='" + city + '\'' + + ", state='" + state + '\'' + + ", zipCode=" + zipCode + + '}'; + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Animal.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Animal.java new file mode 100644 index 000000000..54fe6e542 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Animal.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import jakarta.nosql.Id; + +import java.util.Objects; + +@Entity +public class Animal { + + @Id + private Long id; + + @Column + private String name; + + Animal() { + } + + public Animal(String name) { + this.name = name; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Animal animal)) { + return false; + } + return Objects.equals(id, animal.id) && + Objects.equals(name, animal.name); + } + + @Override + public int hashCode() { + return Objects.hash(id, name); + } + + @Override + public String toString() { + return "Animal{" + "name='" + name + '\'' + + '}'; + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/AppointmentBook.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/AppointmentBook.java new file mode 100644 index 000000000..22667de6e --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/AppointmentBook.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import jakarta.nosql.Id; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +@Entity +public class AppointmentBook { + + + @Id + private String id; + + + @Column + private List contacts = new ArrayList<>(); + + + AppointmentBook() { + } + + public AppointmentBook(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + + public List getContacts() { + return contacts; + } + + public void add(Contact contact) { + this.contacts.add(contact); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AppointmentBook appointmentBook = (AppointmentBook) o; + return Objects.equals(id, appointmentBook.id); + } + + @Override + public int hashCode() { + return Objects.hashCode(id); + } + + @Override + public String toString() { + return "AppointmentBook{" + "id='" + id + '\'' + + ", contacts=" + contacts + + '}'; + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Book.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Book.java new file mode 100644 index 000000000..b572a13d8 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Book.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import jakarta.nosql.Id; + +import java.util.Objects; + +@Entity +public final class Book { + + @Id + private Long id; + + @Column + private String name; + + @Column + private Integer age; + + + Book() { + } + + Book(Long id, String name, Integer age) { + this.id = id; + this.name = name; + this.age = age; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public Integer getAge() { + return age; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Book)) { + return false; + } + Book book = (Book) o; + return Objects.equals(id, book.id); + } + + @Override + public int hashCode() { + return Objects.hashCode(id); + } + + @Override + public String toString() { + return "Book{" + + "id=" + id + + ", name='" + name + '\'' + + ", age=" + age + + '}'; + } + + public static BookBuilder builder() { + return new BookBuilder(); + } + + public static class BookBuilder { + private String name; + private Integer age; + private Long id; + + private BookBuilder() { + } + + public BookBuilder withName(String name) { + this.name = name; + return this; + } + + public BookBuilder withAge(Integer age) { + this.age = age; + return this; + } + + public BookBuilder withId(Long id) { + this.id = id; + return this; + } + + public Book build() { + return new Book(id, name, age); + } + } +} \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/BookRelease.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/BookRelease.java new file mode 100644 index 000000000..9ead6c377 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/BookRelease.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import jakarta.nosql.Id; + +import java.time.Year; +import java.util.Objects; + +@Entity +public class BookRelease { + + @Id("isbn") + private final String isbn; + @Column("title") + private final String title; + @Column("author") + private final String author; + @Column("year") + private final Year year; + + public BookRelease(@Id("isbn") String isbn, + @Column("title") String title, + @Column("author") String author, + @Column("year") Year year) { + this.isbn = isbn; + this.title = title; + this.author = author; + this.year = year; + } + + + public String getIsbn() { + return isbn; + } + + public String getTitle() { + return title; + } + + public String getAuthor() { + return author; + } + + public Year getYear() { + return year; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + BookRelease that = (BookRelease) o; + return Objects.equals(isbn, that.isbn); + } + + @Override + public int hashCode() { + return Objects.hashCode(isbn); + } + + @Override + public String toString() { + return "BookRelease{" + + "isbn='" + isbn + '\'' + + ", title='" + title + '\'' + + ", author='" + author + '\'' + + ", year=" + year + + '}'; + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Car.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Car.java new file mode 100644 index 000000000..97d3e5f61 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Car.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import jakarta.nosql.Id; + +import java.time.Year; + +@Entity +public record Car(@Id String plate, @Column("model") String model, + @Column("manufacturer") String manufacturer, + @Column("year") Year year) { +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Citizen.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Citizen.java new file mode 100644 index 000000000..d1c68319d --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Citizen.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import jakarta.nosql.Id; + +import java.util.Objects; + +@Entity +public class Citizen { + + @Id + private String id; + + @Column + private String name; + + @Column + private City city; + Citizen() { + } + + private Citizen(String id, String name, City city) { + this.id = id; + this.name = name; + this.city = city; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public City getCity() { + return city; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Citizen citizen = (Citizen) o; + return Objects.equals(id, citizen.id); + } + + @Override + public int hashCode() { + return Objects.hashCode(id); + } + + @Override + public String toString() { + return "Citizen{" + + "id='" + id + '\'' + + ", name='" + name + '\'' + + ", city=" + city + + '}'; + } + + public static Citizen of(String id, String name, City city){ + Objects.requireNonNull(id, "id is required"); + Objects.requireNonNull(name, "name is required"); + Objects.requireNonNull(city, "city is required"); + return new Citizen(id, name, city); + } + + public static Citizen of(String id, String name){ + Objects.requireNonNull(id, "id is required"); + Objects.requireNonNull(name, "name is required"); + return new Citizen(id, name, null); + } +} \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/City.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/City.java new file mode 100644 index 000000000..3b00850ff --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/City.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; + +import java.util.Objects; + +@Entity +public class City { + + @Column + private String id; + + @Column + private String name; + + @Deprecated + public City() { + } + + + private City(String id, String name) { + this.id = id; + this.name = name; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return "City{" + + "id='" + id + '\'' + + ", name='" + name + '\'' + + '}'; + } + + public static City of(String id, String name) { + Objects.requireNonNull(id, "id is required"); + Objects.requireNonNull(name, "name is required"); + return new City(id, name); + } +} \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Contact.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Contact.java new file mode 100644 index 000000000..d7eeb3ead --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Contact.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + +import jakarta.nosql.Column; +import org.eclipse.jnosql.mapping.Embeddable; + +import java.util.Objects; + +@Embeddable +public final class Contact { + + @Column + private ContactType type; + + @Column("contact_name") + private String name; + + @Column + private String information; + + Contact() { + } + + private Contact(ContactType type, String name, String information) { + this.type = type; + this.name = name; + this.information = information; + } + + public ContactType getType() { + return type; + } + + public String getName() { + return name; + } + + public String getInformation() { + return information; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Contact contact = (Contact) o; + return type == contact.type && + Objects.equals(name, contact.name) && + Objects.equals(information, contact.information); + } + + @Override + public int hashCode() { + return Objects.hash(type, name, information); + } + + @Override + public String toString() { + return "Contact{" + "type=" + type + + ", name='" + name + '\'' + + ", information='" + information + '\'' + + '}'; + } + + public static ContactBuilder builder() { + return new ContactBuilder(); + } + + public static class ContactBuilder { + + private ContactType type; + + private String name; + + private String information; + + private ContactBuilder() { + } + + public ContactBuilder withType(ContactType type) { + this.type = type; + return this; + } + + public ContactBuilder withName(String name) { + this.name = name; + return this; + } + + public ContactBuilder withInformation(String information) { + this.information = information; + return this; + } + + public Contact build() { + return new Contact(type, name, information); + } + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/ContactType.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/ContactType.java new file mode 100644 index 000000000..52065c1a3 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/ContactType.java @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + +public enum ContactType { + MOBILE, PHONE, EMAIL +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Director.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Director.java new file mode 100644 index 000000000..ea60f3faa --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Director.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; + +import java.util.List; +import java.util.Objects; + +@Entity +public final class Director extends Person { + + @Column + private Movie movie; + + + Director() { + } + + public Director(long id, String name, int age, List phones, String ignore, Movie movie) { + super(id, name, age, phones, ignore); + this.movie = movie; + } + + public Movie getMovie() { + return movie; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Director director = (Director) o; + return Objects.equals(movie, director.movie); + } + + @Override + public int hashCode() { + return Objects.hash(movie); + } + + public static DirectorBuilder builderDirector() { + return new DirectorBuilder(); + } + + public static class DirectorBuilder { + private long id; + private String name; + private int age; + private List phones; + private String ignore; + private Movie movie; + + private DirectorBuilder() { + } + + public DirectorBuilder withId(long id) { + this.id = id; + return this; + } + + public DirectorBuilder withName(String name) { + this.name = name; + return this; + } + + public DirectorBuilder withAge(int age) { + this.age = age; + return this; + } + + public DirectorBuilder withPhones(List phones) { + this.phones = phones; + return this; + } + + public DirectorBuilder withIgnore(String ignore) { + this.ignore = ignore; + return this; + } + + public DirectorBuilder withMovie(Movie movie) { + this.movie = movie; + return this; + } + + public Director build() { + return new Director(id, name, age, phones, ignore, movie); + } + } + +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Download.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Download.java new file mode 100644 index 000000000..562a1a0d3 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Download.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import jakarta.nosql.Id; + +@Entity("download") +public class Download { + + @Id + private Long id; + + @Column + private byte[] contents; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public byte[] getContents() { + if(contents != null) { + byte[] copiedArray = new byte[contents.length]; + System.arraycopy(contents, 0, copiedArray, 0, contents.length); + return copiedArray; + } + return new byte[0]; + } + + public void setContents(byte[] contents) { + if(contents != null) { + this.contents = new byte[contents.length]; + System.arraycopy(contents, 0, this.contents, 0, contents.length); + + } + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Hero.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Hero.java new file mode 100644 index 000000000..4ee44c0e9 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Hero.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import jakarta.nosql.Id; + +@Entity +public class Hero { + + @Id + private final String id; + + @Column + private final String name; + + public Hero(@Id String id, @Column("name") String name) { + this.id = id; + this.name = name; + } + + public String id() { + return id; + } + + public String name() { + return name; + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Job.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Job.java new file mode 100644 index 000000000..2b47b8029 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Job.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + + +import jakarta.nosql.Column; +import org.eclipse.jnosql.mapping.Embeddable; + +import java.util.Objects; + +@Embeddable +public class Job { + + @Column + private String description; + + @Column + private String city; + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Job job = (Job) o; + return Objects.equals(description, job.description); + } + + @Override + public int hashCode() { + return Objects.hashCode(description); + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/MainStepType.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/MainStepType.java new file mode 100644 index 000000000..494fc5024 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/MainStepType.java @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + +public enum MainStepType { + MAIN, ENTRY +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Money.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Money.java new file mode 100644 index 000000000..74acd608e --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Money.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + + +import java.math.BigDecimal; +import java.util.Objects; + +public record Money(String currency, BigDecimal value) { + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Money money = (Money) o; + return Objects.equals(currency, money.currency) && + Objects.equals(value.doubleValue(), money.value.doubleValue()); + } + + @Override + public int hashCode() { + return Objects.hash(currency, value.doubleValue()); + } + + @Override + public String toString() { + return currency + " " + value.toString(); + } + + public static Money parse(String dbData) { + String[] values = dbData.split(" "); + String currency = values[0]; + BigDecimal value = BigDecimal.valueOf(Double.valueOf(values[1])); + return new Money(currency, value); + } +} + diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/MoneyConverter.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/MoneyConverter.java new file mode 100644 index 000000000..02535e42c --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/MoneyConverter.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + + +import org.eclipse.jnosql.mapping.AttributeConverter; + +public class MoneyConverter implements AttributeConverter { + + + @Override + public String convertToDatabaseColumn(Money attribute) { + return attribute.toString(); + } + + @Override + public Money convertToEntityAttribute(String dbData) { + return Money.parse(dbData); + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Movie.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Movie.java new file mode 100644 index 000000000..19488bc97 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Movie.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; + +import java.util.Objects; +import java.util.Set; + +@Entity("movie") +public class Movie { + + @Column + private String title; + + @Column + private long year; + + @Column + private Set actors; + + Movie() { + } + + public Movie(String title, long year, Set actors) { + this.title = title; + this.year = year; + this.actors = actors; + } + + public String getTitle() { + return title; + } + + public long getYear() { + return year; + } + + public Set getActors() { + return actors; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Movie movie = (Movie) o; + return year == movie.year && + Objects.equals(title, movie.title) && + Objects.equals(actors, movie.actors); + } + + @Override + public int hashCode() { + return Objects.hash(title, year, actors); + } + + @Override + public String toString() { + return "Movie{" + + "title='" + title + '\'' + + ", year=" + year + + ", actors=" + actors + + '}'; + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Person.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Person.java new file mode 100644 index 000000000..1f3fc0b2c --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Person.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import jakarta.nosql.Id; +import org.eclipse.jnosql.mapping.MappedSuperclass; + +import java.util.List; +import java.util.Objects; + +@Entity +@MappedSuperclass +public class Person { + + @Id + private long id; + + @Column + private String name; + + @Column + private int age; + + @Column + private List phones; + + private String ignore; + + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public int getAge() { + return age; + } + + public List getPhones() { + return phones; + } + + public String getIgnore() { + return ignore; + } + + public boolean isAdult() { + return age > 21; + } + + Person() { + } + + Person(long id, String name, int age, List phones, String ignore) { + this.id = id; + this.name = name; + this.age = age; + this.phones = phones; + this.ignore = ignore; + } + + @Override + public String toString() { + return "Person{" + "id=" + id + + ", name='" + name + '\'' + + ", age=" + age + + ", phones=" + phones + + ", ignore='" + ignore + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Person person = (Person) o; + return id == person.id && + age == person.age && + Objects.equals(name, person.name) && + Objects.equals(phones, person.phones); + } + + @Override + public int hashCode() { + return Objects.hash(id, name, age, phones, ignore); + } + + public static PersonBuilder builder() { + return new PersonBuilder(); + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/PersonBuilder.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/PersonBuilder.java new file mode 100644 index 000000000..e0ac612ab --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/PersonBuilder.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + +import java.util.List; + +public class PersonBuilder { + private long id; + private String name; + private int age; + private List phones; + private String ignore; + + public PersonBuilder withId(long id) { + this.id = id; + return this; + } + + public PersonBuilder withName(String name) { + this.name = name; + return this; + } + + public PersonBuilder withAge() { + this.age = 10; + return this; + } + + public PersonBuilder withAge(int age) { + this.age = age; + return this; + } + + + public PersonBuilder withPhones(List phones) { + this.phones = phones; + return this; + } + + public PersonBuilder withIgnore() { + this.ignore = "Just Ignore"; + return this; + } + + public Person build() { + return new Person(id, name, age, phones, ignore); + } +} \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/PersonRepository.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/PersonRepository.java new file mode 100644 index 000000000..eaa2b40f1 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/PersonRepository.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + + +import jakarta.data.repository.Repository; +import org.eclipse.jnosql.mapping.NoSQLRepository; + +@Repository +public interface PersonRepository extends NoSQLRepository { +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/PersonStatisticRepository.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/PersonStatisticRepository.java new file mode 100644 index 000000000..f08a4c70d --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/PersonStatisticRepository.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + +public interface PersonStatisticRepository { + + PersonStatistic statistics(String city); + + record PersonStatistic(String city, double average, int sum, int max, int min, int count) { + + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/PersonStatisticRepositoryProducer.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/PersonStatisticRepositoryProducer.java new file mode 100644 index 000000000..3b4ef65db --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/PersonStatisticRepositoryProducer.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; + +@ApplicationScoped +class PersonStatisticRepositoryProducer { + + @Produces + public PersonStatisticRepository get() { + return (city) -> new PersonStatisticRepository.PersonStatistic(city, 26.0, + 26, 26, 26, 1); + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/StepTransitionReason.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/StepTransitionReason.java new file mode 100644 index 000000000..4c270bd3e --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/StepTransitionReason.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + + +public enum StepTransitionReason { + + REPEAT +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Transition.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Transition.java new file mode 100644 index 000000000..af993f2bd --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Transition.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + +import jakarta.nosql.Column; +import org.eclipse.jnosql.mapping.Embeddable; + +import java.util.List; + +@Embeddable +public class Transition { + + @Column + private String targetWorkflowStepKey; + @Column + private StepTransitionReason stepTransitionReason; + @Column + private String mailTemplateKey; + @Column + private List restrictedRoleGroups; + + public Transition(String targetWorkflowStepKey, + StepTransitionReason stepTransitionReason, + String mailTemplateKey, + List restrictedRoleGroups) { + this.targetWorkflowStepKey = targetWorkflowStepKey; + this.stepTransitionReason = stepTransitionReason; + this.mailTemplateKey = mailTemplateKey; + this.restrictedRoleGroups = restrictedRoleGroups; + } + + public Transition() { + } + + public String targetWorkflowStepKey() { + return targetWorkflowStepKey; + } + + public StepTransitionReason stepTransitionReason() { + return stepTransitionReason; + } + + public String mailTemplateKey() { + return mailTemplateKey; + } + + public List restrictedRoleGroups() { + return restrictedRoleGroups; + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Vendor.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Vendor.java new file mode 100644 index 000000000..bf7a88ec6 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Vendor.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import jakarta.nosql.Id; + +import java.util.Collections; +import java.util.Set; + +@Entity("vendors") +public class Vendor { + + @Id + private String name; + + @Column + private Set prefixes; + + Vendor() { + } + + public Vendor(String name) { + this.name = name; + prefixes = Collections.emptySet(); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Set getPrefixes() { + return prefixes; + } + + public void setPrefixes(Set prefixes) { + this.prefixes = prefixes; + } + + public void add(String prefix) { + this.prefixes.add(prefix); + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Worker.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Worker.java new file mode 100644 index 000000000..557e5f1e9 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/Worker.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import org.eclipse.jnosql.mapping.Convert; + +@Entity +public class Worker { + + @Column + private String name; + + @Column + private Job job; + + @Column("money") + @Convert(MoneyConverter.class) + private Money salary; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Job getJob() { + return job; + } + + public void setJob(Job job) { + this.job = job; + } + + public Money getSalary() { + return salary; + } + + public void setSalary(Money salary) { + this.salary = salary; + } + + +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/WorkflowStep.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/WorkflowStep.java new file mode 100644 index 000000000..47327a6af --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/WorkflowStep.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import jakarta.nosql.Id; + +import java.util.List; + +@Entity("workflow_step") +public class WorkflowStep { + + @Id + private String id; + + @Column("_key") + private String key; + + @Column + private String workflowSchemaKey; + + @Column + private String stepName; + + @Column + private MainStepType mainStepType; + + @Column + private Integer stepNo; + + @Column + private String componentConfigurationKey; + + @Column + private String relationTypeKey; + + @Column + private List availableTransitions; + + WorkflowStep(String id, String key, String workflowSchemaKey, + String stepName, MainStepType mainStepType, + Integer stepNo, String componentConfigurationKey, + String relationTypeKey, List availableTransitions) { + this.id = id; + this.key = key; + this.workflowSchemaKey = workflowSchemaKey; + this.stepName = stepName; + this.mainStepType = mainStepType; + this.stepNo = stepNo; + this.componentConfigurationKey = componentConfigurationKey; + this.relationTypeKey = relationTypeKey; + this.availableTransitions = availableTransitions; + } + + WorkflowStep() { + } + + public String id() { + return id; + } + + public String key() { + return key; + } + + public String workflowSchemaKey() { + return workflowSchemaKey; + } + + public String stepName() { + return stepName; + } + + public MainStepType mainStepType() { + return mainStepType; + } + + public Integer stepNo() { + return stepNo; + } + + public String componentConfigurationKey() { + return componentConfigurationKey; + } + + public String relationTypeKey() { + return relationTypeKey; + } + + public List availableTransitions() { + return availableTransitions; + } + + public static WorkflowStepBuilder builder() { + return new WorkflowStepBuilder(); + } + +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/WorkflowStepBuilder.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/WorkflowStepBuilder.java new file mode 100644 index 000000000..c13d89a5d --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/WorkflowStepBuilder.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + +import java.util.List; + +public class WorkflowStepBuilder { + private String id; + private String key; + private String workflowSchemaKey; + private String stepName; + private MainStepType mainStepType; + private Integer stepNo; + private String componentConfigurationKey; + private String relationTypeKey; + private List availableTransitions; + + public WorkflowStepBuilder id(String id) { + this.id = id; + return this; + } + + public WorkflowStepBuilder key(String key) { + this.key = key; + return this; + } + + public WorkflowStepBuilder workflowSchemaKey(String workflowSchemaKey) { + this.workflowSchemaKey = workflowSchemaKey; + return this; + } + + public WorkflowStepBuilder stepName(String stepName) { + this.stepName = stepName; + return this; + } + + public WorkflowStepBuilder mainStepType(MainStepType mainStepType) { + this.mainStepType = mainStepType; + return this; + } + + public WorkflowStepBuilder stepNo(Integer stepNo) { + this.stepNo = stepNo; + return this; + } + + public WorkflowStepBuilder componentConfigurationKey(String componentConfigurationKey) { + this.componentConfigurationKey = componentConfigurationKey; + return this; + } + + public WorkflowStepBuilder relationTypeKey(String relationTypeKey) { + this.relationTypeKey = relationTypeKey; + return this; + } + + public WorkflowStepBuilder availableTransitions(List availableTransitions) { + this.availableTransitions = availableTransitions; + return this; + } + + public WorkflowStep build() { + return new WorkflowStep(id, key, workflowSchemaKey, stepName, mainStepType, + stepNo, componentConfigurationKey, relationTypeKey, availableTransitions); + } +} \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/ZipCode.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/ZipCode.java new file mode 100644 index 000000000..cdae1dc6e --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/ZipCode.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities; + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; + +@Entity +public class ZipCode { + + @Column + private String zip; + + @Column + private String plusFour; + + + public String getZip() { + return zip; + } + + public void setZip(String zip) { + this.zip = zip; + } + + public String getPlusFour() { + return plusFour; + } + + public void setPlusFour(String plusFour) { + this.plusFour = plusFour; + } + + + @Override + public String toString() { + return "ZipCode{" + "zip='" + zip + '\'' + + ", plusFour='" + plusFour + '\'' + + '}'; + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/constructor/BookUser.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/constructor/BookUser.java new file mode 100644 index 000000000..947b19d41 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/constructor/BookUser.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities.constructor; + + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import jakarta.nosql.Id; +import org.eclipse.jnosql.mapping.column.entities.Book; + +import java.util.List; +import java.util.Objects; + +@Entity +public class BookUser { + + @Id + private final String nickname; + + @Column + private final String name; + + @Column + private final List books; + + BookUser(@Id String nickname, + @Column("native_name") String name, + @Column("books") List books) { + this.nickname = nickname; + this.name = name; + this.books = books; + } + + public String getNickname() { + return nickname; + } + + public String getName() { + return name; + } + + public List getBooks() { + return books; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + BookUser bookUser = (BookUser) o; + return Objects.equals(nickname, bookUser.nickname); + } + + @Override + public int hashCode() { + return Objects.hashCode(nickname); + } + + @Override + public String toString() { + return "BookUser{" + + "nickname='" + nickname + '\'' + + ", name='" + name + '\'' + + ", books=" + books + + '}'; + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/constructor/Computer.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/constructor/Computer.java new file mode 100644 index 000000000..ca4afb0f0 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/constructor/Computer.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities.constructor; + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import jakarta.nosql.Id; +import org.eclipse.jnosql.mapping.Convert; +import org.eclipse.jnosql.mapping.column.entities.Money; +import org.eclipse.jnosql.mapping.column.entities.MoneyConverter; + +@Entity +public class Computer { + + @Id + private final Long id; + + @Column + private final String name; + + @Column + private final int age; + + @Column + private final String model; + + @Column + @Convert(MoneyConverter.class) + private final Money price; + + public Computer(@Id Long id, @Column("name") String name, + @Column("age") int age, @Column("model") String model, + @Column("price") @Convert(MoneyConverter.class) Money price) { + this.id = id; + this.name = name; + this.age = age; + this.model = model; + this.price = price; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public int getAge() { + return age; + } + + public String getModel() { + return model; + } + + public Money getPrice() { + return price; + } + + @Override + public String toString() { + return "Computer{" + + "id=" + id + + ", name='" + name + '\'' + + ", age=" + age + + ", model='" + model + '\'' + + ", price=" + price + + '}'; + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/constructor/PetOwner.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/constructor/PetOwner.java new file mode 100644 index 000000000..4ff7a52ad --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/constructor/PetOwner.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities.constructor; + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import jakarta.nosql.Id; +import org.eclipse.jnosql.mapping.column.entities.Animal; + +import java.util.Objects; + +@Entity +public class PetOwner { + + @Id + private final Long id; + + @Column + private final String name; + + @Column + private final Animal animal; + + public PetOwner(@Id Long id, @Column("name") String name, @Column("animal") Animal animal) { + this.id = id; + this.name = name; + this.animal = animal; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public Animal getAnimal() { + return animal; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PetOwner petOwner = (PetOwner) o; + return Objects.equals(id, petOwner.id); + } + + @Override + public int hashCode() { + return Objects.hashCode(id); + } + + @Override + public String toString() { + return "PetOwner{" + + "id=" + id + + ", name='" + name + '\'' + + ", animal=" + animal + + '}'; + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/constructor/SuperHero.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/constructor/SuperHero.java new file mode 100644 index 000000000..300376a69 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/constructor/SuperHero.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities.constructor; + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import jakarta.nosql.Id; + +import java.util.List; + +@Entity +public record SuperHero(@Id String id, @Column String name, @Column List powers) { +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/inheritance/EmailNotification.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/inheritance/EmailNotification.java new file mode 100644 index 000000000..5ae445dea --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/inheritance/EmailNotification.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ + +package org.eclipse.jnosql.mapping.column.entities.inheritance; + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import org.eclipse.jnosql.mapping.DiscriminatorValue; + +@Entity +@DiscriminatorValue("Email") +public class EmailNotification extends Notification { + + @Column + private String email; + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + @Override + public String send() { + return "Sending message to email sms to the email: " + email; + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/inheritance/LargeProject.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/inheritance/LargeProject.java new file mode 100644 index 000000000..c8dd964a2 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/inheritance/LargeProject.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ + +package org.eclipse.jnosql.mapping.column.entities.inheritance; + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import org.eclipse.jnosql.mapping.DiscriminatorValue; + +import java.math.BigDecimal; + +@Entity +@DiscriminatorValue("Large") +public class LargeProject extends Project { + + @Column + private BigDecimal budget; + + public void setBudget(BigDecimal budget) { + this.budget = budget; + } + + public BigDecimal getBudget() { + return budget; + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/inheritance/Notification.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/inheritance/Notification.java new file mode 100644 index 000000000..df69dfa0a --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/inheritance/Notification.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ + +package org.eclipse.jnosql.mapping.column.entities.inheritance; + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import jakarta.nosql.Id; +import org.eclipse.jnosql.mapping.Inheritance; + +import java.time.LocalDate; +import java.util.Objects; + +@Entity +@Inheritance +public abstract class Notification { + + @Id + protected Long id; + + @Column + protected String name; + + @Column + protected LocalDate createdOn; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public LocalDate getCreatedOn() { + return createdOn; + } + + public void setCreatedOn(LocalDate createdOn) { + this.createdOn = createdOn; + } + + public abstract String send(); + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Notification that = (Notification) o; + return Objects.equals(id, that.id); + } + + @Override + public int hashCode() { + return Objects.hashCode(id); + } + + @Override + public String toString() { + return "Notification{" + + "id=" + id + + ", name='" + name + '\'' + + ", createdOn=" + createdOn + + '}'; + } +} \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/inheritance/NotificationReader.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/inheritance/NotificationReader.java new file mode 100644 index 000000000..2bdddc640 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/inheritance/NotificationReader.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities.inheritance; + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import jakarta.nosql.Id; + +import java.util.Objects; + +@Entity +public class NotificationReader { + + @Id + private String nickname; + + @Column + private String name; + + @Column + private Notification notification; + + @Deprecated + NotificationReader() { + } + + public NotificationReader(String nickname, String name, Notification notification) { + this.nickname = nickname; + this.name = name; + this.notification = notification; + } + + public String getNickname() { + return nickname; + } + + public String getName() { + return name; + } + + public Notification getNotification() { + return notification; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + NotificationReader that = (NotificationReader) o; + return Objects.equals(nickname, that.nickname); + } + + @Override + public int hashCode() { + return Objects.hashCode(nickname); + } + + @Override + public String toString() { + return "NotificationReader{" + + "nickname='" + nickname + '\'' + + ", name='" + name + '\'' + + ", notification=" + notification + + '}'; + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/inheritance/Project.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/inheritance/Project.java new file mode 100644 index 000000000..75e60910e --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/inheritance/Project.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ + +package org.eclipse.jnosql.mapping.column.entities.inheritance; + +import jakarta.nosql.Entity; +import jakarta.nosql.Id; +import org.eclipse.jnosql.mapping.DiscriminatorColumn; +import org.eclipse.jnosql.mapping.Inheritance; + +import java.util.Objects; + +@Entity +@Inheritance +@DiscriminatorColumn("size") +public class Project { + + @Id + protected String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Project project = (Project) o; + return Objects.equals(name, project.name); + } + + @Override + public int hashCode() { + return Objects.hashCode(name); + } + + @Override + public String toString() { + return "Project{" + + "name='" + name + '\'' + + '}'; + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/inheritance/ProjectManager.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/inheritance/ProjectManager.java new file mode 100644 index 000000000..e8ca10125 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/inheritance/ProjectManager.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.entities.inheritance; + + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import jakarta.nosql.Id; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +@Entity +public class ProjectManager { + + @Id + private Long id; + + @Column + private String name; + + @Column + private List projects; + + @Deprecated + ProjectManager() { + } + + private ProjectManager(Long id, String name, List projects) { + this.id = id; + this.name = name; + this.projects = projects; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public List getProjects() { + if(projects == null) { + return Collections.emptyList(); + } + return Collections.unmodifiableList(projects); + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ProjectManager that = (ProjectManager) o; + return Objects.equals(id, that.id); + } + + @Override + public int hashCode() { + return Objects.hashCode(id); + } + + @Override + public String toString() { + return "ProjectManager{" + + "id='" + id + '\'' + + ", name='" + name + '\'' + + ", projects=" + projects + + '}'; + } + + public static ProjectManager of(Long id, String name, List projects) { + Objects.requireNonNull(id, "id is required"); + Objects.requireNonNull(name, "name is required"); + Objects.requireNonNull(projects, "projects is required"); + return new ProjectManager(id, name, projects); + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/inheritance/SmallProject.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/inheritance/SmallProject.java new file mode 100644 index 000000000..f4728527a --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/inheritance/SmallProject.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ + +package org.eclipse.jnosql.mapping.column.entities.inheritance; + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import org.eclipse.jnosql.mapping.DiscriminatorValue; + +@Entity +@DiscriminatorValue("Small") +public class SmallProject extends Project { + + @Column + private String investor; + + public String getInvestor() { + return investor; + } + + public void setInvestor(String investor) { + this.investor = investor; + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/inheritance/SmsNotification.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/inheritance/SmsNotification.java new file mode 100644 index 000000000..d60cd20a5 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/inheritance/SmsNotification.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ + +package org.eclipse.jnosql.mapping.column.entities.inheritance; + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import org.eclipse.jnosql.mapping.DiscriminatorValue; + +@Entity +@DiscriminatorValue("SMS") +public class SmsNotification extends Notification { + + @Column + private String phone; + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + @Override + public String send() { + return "Sending message to via sms to the phone: " + phone; + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/inheritance/SocialMediaNotification.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/inheritance/SocialMediaNotification.java new file mode 100644 index 000000000..64ec3a0b8 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/entities/inheritance/SocialMediaNotification.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ + +package org.eclipse.jnosql.mapping.column.entities.inheritance; + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; + +@Entity +public class SocialMediaNotification extends Notification { + + @Column + private String nickname; + + public String getNickname() { + return nickname; + } + + public void setNickname(String nickname) { + this.nickname = nickname; + } + + @Override + public String send() { + return "Sending message to via social media to the nickname: " + nickname; + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/query/ColumnCrudInheritanceRepositoryProxyTest.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/query/ColumnCrudInheritanceRepositoryProxyTest.java new file mode 100644 index 000000000..deba08a53 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/query/ColumnCrudInheritanceRepositoryProxyTest.java @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.query; + + +import jakarta.data.repository.CrudRepository; +import jakarta.inject.Inject; +import org.eclipse.jnosql.communication.TypeReference; +import org.eclipse.jnosql.communication.column.Column; +import org.eclipse.jnosql.communication.column.ColumnCondition; +import org.eclipse.jnosql.communication.column.ColumnQuery; +import org.eclipse.jnosql.mapping.NoSQLRepository; +import org.eclipse.jnosql.mapping.column.ColumnEntityConverter; +import org.eclipse.jnosql.mapping.column.JNoSQLColumnTemplate; +import org.eclipse.jnosql.mapping.column.MockProducer; +import org.eclipse.jnosql.mapping.column.entities.inheritance.EmailNotification; +import org.eclipse.jnosql.mapping.column.spi.ColumnExtension; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; + +import java.lang.reflect.Proxy; +import java.util.List; + +import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.eclipse.jnosql.communication.Condition.AND; +import static org.mockito.Mockito.verify; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, ColumnEntityConverter.class}) +@AddPackages(MockProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, ColumnExtension.class}) +class ColumnCrudInheritanceRepositoryProxyTest { + + private JNoSQLColumnTemplate template; + + @Inject + private EntitiesMetadata entities; + + @Inject + private Converters converters; + + private EmailRepository emailRepository; + + + + @BeforeEach + public void setUp() { + this.template = Mockito.mock(JNoSQLColumnTemplate.class); + + ColumnRepositoryProxy personHandler = new ColumnRepositoryProxy<>(template, + entities, EmailRepository.class, converters); + + + emailRepository = (EmailRepository) Proxy.newProxyInstance(EmailRepository.class.getClassLoader(), + new Class[]{EmailRepository.class}, + personHandler); + } + + @Test + void shouldPutFilterAtFindAll() { + emailRepository.findAll(); + verify(template).findAll(EmailNotification.class); + } + + @Test + void shouldPutFilterAtCount() { + emailRepository.countBy(); + verify(template).count(EmailNotification.class); + } + + @Test + void shouldPutFilterAtFindByName() { + emailRepository.findByName("name"); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + assertSoftly(soft ->{ + soft.assertThat(query.name()).isEqualTo("Notification"); + soft.assertThat(query.condition()).isPresent(); + ColumnCondition condition = query.condition().orElseThrow(); + soft.assertThat(condition.condition()).isEqualTo(AND); + var conditions = condition.column().get(new TypeReference>() { + }); + soft.assertThat(conditions).hasSize(2).contains(ColumnCondition.eq(Column.of("dtype", "Email"))); + }); + } + + @Test + void shouldPutFilterAtCountByName() { + emailRepository.countByName("name"); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).count(captor.capture()); + ColumnQuery query = captor.getValue(); + assertSoftly(soft ->{ + soft.assertThat(query.name()).isEqualTo("Notification"); + soft.assertThat(query.condition()).isPresent(); + ColumnCondition condition = query.condition().orElseThrow(); + soft.assertThat(condition.condition()).isEqualTo(AND); + var conditions = condition.column().get(new TypeReference>() { + }); + soft.assertThat(conditions).hasSize(2).contains(ColumnCondition.eq(Column.of("dtype", "Email"))); + }); + } + + + @Test + void shouldPutFilterAtExistByName() { + emailRepository.existsByName("name"); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).exists(captor.capture()); + ColumnQuery query = captor.getValue(); + assertSoftly(soft ->{ + soft.assertThat(query.name()).isEqualTo("Notification"); + soft.assertThat(query.condition()).isPresent(); + ColumnCondition condition = query.condition().orElseThrow(); + soft.assertThat(condition.condition()).isEqualTo(AND); + var conditions = condition.column().get(new TypeReference>() { + }); + soft.assertThat(conditions).hasSize(2).contains(ColumnCondition.eq(Column.of("dtype", "Email"))); + }); + } + + @Test + void shouldPutFilterAtDeleteAll() { + emailRepository.deleteAll(); + verify(template).deleteAll(EmailNotification.class); + } + + + public interface EmailRepository extends NoSQLRepository { + + List findByName(String name); + + long countByName(String name); + + boolean existsByName(String name); + + + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/query/ColumnCrudRepositoryProxyTest.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/query/ColumnCrudRepositoryProxyTest.java new file mode 100644 index 000000000..d6edad09b --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/query/ColumnCrudRepositoryProxyTest.java @@ -0,0 +1,804 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.query; + +import jakarta.data.repository.CrudRepository; +import jakarta.data.repository.Param; +import jakarta.data.repository.Query; +import jakarta.data.Sort; +import jakarta.inject.Inject; +import jakarta.nosql.PreparedStatement; +import org.assertj.core.api.Assertions; +import org.eclipse.jnosql.communication.Condition; +import org.eclipse.jnosql.communication.TypeReference; +import org.eclipse.jnosql.communication.Value; +import org.eclipse.jnosql.communication.column.Column; +import org.eclipse.jnosql.communication.column.ColumnCondition; +import org.eclipse.jnosql.communication.column.ColumnDeleteQuery; +import org.eclipse.jnosql.communication.column.ColumnQuery; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.column.ColumnEntityConverter; +import org.eclipse.jnosql.mapping.column.JNoSQLColumnTemplate; +import org.eclipse.jnosql.mapping.column.MockProducer; +import org.eclipse.jnosql.mapping.column.entities.Address; +import org.eclipse.jnosql.mapping.column.entities.Person; +import org.eclipse.jnosql.mapping.column.entities.Vendor; +import org.eclipse.jnosql.mapping.column.spi.ColumnExtension; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; + +import java.lang.reflect.Proxy; +import java.math.BigDecimal; +import java.time.Duration; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.Queue; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.jnosql.communication.Condition.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, ColumnEntityConverter.class}) +@AddPackages(MockProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, ColumnExtension.class}) +class ColumnCrudRepositoryProxyTest { + + private JNoSQLColumnTemplate template; + + @Inject + private EntitiesMetadata entities; + + @Inject + private Converters converters; + + private PersonRepository personRepository; + + private VendorRepository vendorRepository; + + private AddressRepository addressRepository; + + + @BeforeEach + public void setUp() { + this.template = Mockito.mock(JNoSQLColumnTemplate.class); + + ColumnRepositoryProxy personHandler = new ColumnRepositoryProxy(template, + entities, PersonRepository.class, converters); + + ColumnRepositoryProxy vendorHandler = new ColumnRepositoryProxy(template, + entities, VendorRepository.class, converters); + + ColumnRepositoryProxy addressHandler = new ColumnRepositoryProxy(template, + entities, AddressRepository.class, converters); + + + when(template.insert(any(Person.class))).thenReturn(Person.builder().build()); + when(template.insert(any(Person.class), any(Duration.class))).thenReturn(Person.builder().build()); + when(template.update(any(Person.class))).thenReturn(Person.builder().build()); + + personRepository = (PersonRepository) Proxy.newProxyInstance(PersonRepository.class.getClassLoader(), + new Class[]{PersonRepository.class}, + personHandler); + vendorRepository = (VendorRepository) Proxy.newProxyInstance(VendorRepository.class.getClassLoader(), + new Class[]{VendorRepository.class}, vendorHandler); + + addressRepository = (AddressRepository) Proxy.newProxyInstance(AddressRepository.class.getClassLoader(), + new Class[]{AddressRepository.class}, addressHandler); + } + + + @Test + void shouldSaveUsingInsertWhenDataDoesNotExist() { + when(template.find(Person.class, 10L)).thenReturn(Optional.empty()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Person.class); + Person person = Person.builder().withName("Ada") + .withId(10L) + .withPhones(singletonList("123123")) + .build(); + assertNotNull(personRepository.save(person)); + verify(template).insert(captor.capture()); + Person value = captor.getValue(); + assertEquals(person, value); + } + + + @Test + void shouldSaveUsingUpdateWhenDataExists() { + + when(template.find(Person.class, 10L)).thenReturn(Optional.of(Person.builder().build())); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Person.class); + Person person = Person.builder().withName("Ada") + .withId(10L) + .withPhones(singletonList("123123")) + .build(); + assertNotNull(personRepository.save(person)); + verify(template).update(captor.capture()); + Person value = captor.getValue(); + assertEquals(person, value); + } + + + @Test + void shouldSaveIterable() { + when(personRepository.findById(10L)).thenReturn(Optional.empty()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Person.class); + Person person = Person.builder().withName("Ada") + .withId(10L) + .withPhones(singletonList("123123")) + .build(); + + personRepository.saveAll(singletonList(person)); + verify(template).insert(captor.capture()); + Person personCapture = captor.getValue(); + assertEquals(person, personCapture); + } + + + @Test + void shouldInsert() { + + ArgumentCaptor captor = ArgumentCaptor.forClass(Person.class); + Person person = Person.builder().withName("Ada") + .withId(10L) + .withPhones(singletonList("123123")) + .build(); + assertNotNull(personRepository.insert(person)); + verify(template).insert(captor.capture()); + Person value = captor.getValue(); + assertEquals(person, value); + } + + @Test + void shouldUpdate() { + + ArgumentCaptor captor = ArgumentCaptor.forClass(Person.class); + Person person = Person.builder().withName("Ada") + .withId(10L) + .withPhones(singletonList("123123")) + .build(); + personRepository.update(person); + verify(template).update(captor.capture()); + Person value = captor.getValue(); + assertEquals(person, value); + } + + + @Test + void shouldInsertIterable() { + + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + Person person = Person.builder().withName("Ada") + .withId(10L) + .withPhones(singletonList("123123")) + .build(); + assertNotNull(personRepository.insertAll(List.of(person))); + verify(template).insert(captor.capture()); + List value = captor.getValue(); + assertThat(value).contains(person); + } + + @Test + void shouldUpdateIterable() { + + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + Person person = Person.builder().withName("Ada") + .withId(10L) + .withPhones(singletonList("123123")) + .build(); + personRepository.updateAll(List.of(person)); + verify(template).update(captor.capture()); + List value = captor.getValue(); + assertThat(value).contains(person); + } + + + @Test + void shouldFindByNameInstance() { + + when(template.singleResult(any(ColumnQuery.class))).thenReturn(Optional + .of(Person.builder().build())); + + personRepository.findByName("name"); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).singleResult(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(Condition.EQUALS, condition.condition()); + assertEquals(Column.of("name", "name"), condition.column()); + + assertNotNull(personRepository.findByName("name")); + when(template.singleResult(any(ColumnQuery.class))).thenReturn(Optional + .empty()); + + assertNull(personRepository.findByName("name")); + + + } + + @Test + void shouldFindByNameANDAge() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + List persons = personRepository.findByNameAndAge("name", 20); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + assertThat(persons).contains(ada); + + } + + @Test + void shouldFindByAgeANDName() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + Set persons = personRepository.findByAgeAndName(20, "name"); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + assertThat(persons).contains(ada); + + } + + @Test + void shouldFindByNameANDAgeOrderByName() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + Stream persons = personRepository.findByNameAndAgeOrderByName("name", 20); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + assertThat(persons.collect(Collectors.toList())).contains(ada); + + } + + @Test + void shouldFindByNameANDAgeOrderByAge() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + Queue persons = personRepository.findByNameAndAgeOrderByAge("name", 20); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + assertThat(persons).contains(ada); + + } + + @Test + void shouldDeleteByName() { + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnDeleteQuery.class); + personRepository.deleteByName("Ada"); + verify(template).delete(captor.capture()); + ColumnDeleteQuery deleteQuery = captor.getValue(); + ColumnCondition condition = deleteQuery.condition().get(); + assertEquals("Person", deleteQuery.name()); + assertEquals(Condition.EQUALS, condition.condition()); + assertEquals(Column.of("name", "Ada"), condition.column()); + + } + + @Test + void shouldFindById() { + personRepository.findById(10L); + verify(template).find(Person.class, 10L); + } + + @Test + void shouldFindByIds() { + when(template.find(Mockito.eq(Person.class), Mockito.any(Long.class))) + .thenReturn(Optional.of(Person.builder().build())); + + personRepository.findByIdIn(singletonList(10L)).toList(); + verify(template).find(Person.class, 10L); + + personRepository.findByIdIn(asList(1L, 2L, 3L)).toList(); + verify(template, times(4)).find(Mockito.eq(Person.class), Mockito.any(Long.class)); + } + + @Test + void shouldDeleteById() { + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnDeleteQuery.class); + personRepository.deleteById(10L); + verify(template).delete(Person.class, 10L); + } + + @Test + void shouldDeleteByIds() { + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnDeleteQuery.class); + personRepository.deleteByIdIn(singletonList(10L)); + verify(template).delete(Person.class, 10L); + } + + + @Test + void shouldContainsById() { + when(template.find(Person.class, 10L)).thenReturn(Optional.of(Person.builder().build())); + + assertTrue(personRepository.existsById(10L)); + Mockito.verify(template).find(Person.class, 10L); + + when(template.find(Person.class, 10L)).thenReturn(Optional.empty()); + assertFalse(personRepository.existsById(10L)); + + } + + @Test + void shouldFindAll() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + personRepository.findAll().toList(); + ArgumentCaptor> captor = ArgumentCaptor.forClass(Class.class); + verify(template).findAll(captor.capture()); + assertEquals(captor.getValue(), Person.class); + + } + + @Test + void shouldReturnToString() { + assertNotNull(personRepository.toString()); + } + + @Test + void shouldReturnHasCode() { + assertEquals(personRepository.hashCode(), personRepository.hashCode()); + } + + @Test + void shouldFindByNameAndAgeGreaterEqualThan() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + personRepository.findByNameAndAgeGreaterThanEqual("Ada", 33); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(AND, condition.condition()); + List conditions = condition.column().get(new TypeReference<>() { + }); + ColumnCondition columnCondition = conditions.get(0); + ColumnCondition columnCondition2 = conditions.get(1); + + assertEquals(Condition.EQUALS, columnCondition.condition()); + assertEquals("Ada", columnCondition.column().get()); + assertEquals("name", columnCondition.column().name()); + + assertEquals(Condition.GREATER_EQUALS_THAN, columnCondition2.condition()); + assertEquals(33, columnCondition2.column().get()); + assertEquals("age", columnCondition2.column().name()); + } + + @Test + void shouldFindByGreaterThan() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + personRepository.findByAgeGreaterThan(33); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(GREATER_THAN, condition.condition()); + assertEquals(Column.of("age", 33), condition.column()); + + } + + @Test + void shouldFindByAgeLessThanEqual() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + personRepository.findByAgeLessThanEqual(33); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(LESSER_EQUALS_THAN, condition.condition()); + assertEquals(Column.of("age", 33), condition.column()); + + } + + @Test + void shouldFindByAgeLessEqual() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + personRepository.findByAgeLessThan(33); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(LESSER_THAN, condition.condition()); + assertEquals(Column.of("age", 33), condition.column()); + + } + + @Test + void shouldFindByAgeBetween() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + personRepository.findByAgeBetween(10, 15); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(BETWEEN, condition.condition()); + List values = condition.column().get(new TypeReference<>() { + }); + assertEquals(Arrays.asList(10, 15), values.stream().map(Value::get).collect(Collectors.toList())); + assertTrue(condition.column().name().contains("age")); + } + + + @Test + void shouldFindByNameLike() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + personRepository.findByNameLike("Ada"); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(LIKE, condition.condition()); + assertEquals(Column.of("name", "Ada"), condition.column()); + + } + + + @Test + void shouldFindByStringWhenFieldIsSet() { + Vendor vendor = new Vendor("vendor"); + vendor.setPrefixes(Collections.singleton("prefix")); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(vendor)); + + vendorRepository.findByPrefixes("prefix"); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).singleResult(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("vendors", query.name()); + assertEquals(EQUALS, condition.condition()); + assertEquals(Column.of("prefixes", "prefix"), condition.column()); + + } + + @Test + void shouldFindByIn() { + Vendor vendor = new Vendor("vendor"); + vendor.setPrefixes(Collections.singleton("prefix")); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(vendor)); + + vendorRepository.findByPrefixesIn(singletonList("prefix")); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).singleResult(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("vendors", query.name()); + assertEquals(IN, condition.condition()); + + } + + + @Test + void shouldConvertFieldToTheType() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + personRepository.findByAge("120"); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(EQUALS, condition.condition()); + assertEquals(Column.of("age", 120), condition.column()); + } + + + @Test + void shouldExecuteJNoSQLQuery() { + personRepository.findByQuery(); + verify(template).query("select * from Person"); + } + + @Test + void shouldExecuteJNoSQLPrepare() { + PreparedStatement statement = Mockito.mock(PreparedStatement.class); + when(template.prepare(Mockito.anyString())).thenReturn(statement); + personRepository.findByQuery("Ada"); + verify(statement).bind("id", "Ada"); + } + + @Test + void shouldFindBySalary_Currency() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + personRepository.findBySalary_Currency("USD"); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + final Column column = condition.column(); + assertEquals("Person", query.name()); + assertEquals("salary.currency", column.name()); + + } + + @Test + void shouldFindBySalary_CurrencyAndSalary_Value() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + personRepository.findBySalary_CurrencyAndSalary_Value("USD", BigDecimal.TEN); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + final Column column = condition.column(); + final List conditions = column.get(new TypeReference<>() { + }); + final List names = conditions.stream().map(ColumnCondition::column) + .map(Column::name).collect(Collectors.toList()); + assertEquals("Person", query.name()); + assertThat(names).contains("salary.currency", "salary.value"); + + } + + @Test + void shouldFindBySalary_CurrencyOrderByCurrency_Name() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + personRepository.findBySalary_CurrencyOrderByCurrency_Name("USD"); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + final Sort sort = query.sorts().get(0); + final Column document = condition.column(); + assertEquals("Person", query.name()); + assertEquals("salary.currency", document.name()); + assertEquals("currency.name", sort.property()); + + } + + @Test + void shouldFindByNameNotEquals() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + personRepository.findByNameNotEquals("Otavio"); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition negate = query.condition().get(); + assertEquals(Condition.NOT, negate.condition()); + ColumnCondition condition = negate.column().get(ColumnCondition.class); + assertEquals(EQUALS, condition.condition()); + assertEquals(Column.of("name", "Otavio"), condition.column()); + } + + @Test + void shouldFindByAgeNotGreaterThan() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + personRepository.findByAgeNotGreaterThan(10); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition negate = query.condition().get(); + assertEquals(Condition.NOT, negate.condition()); + ColumnCondition condition = negate.column().get(ColumnCondition.class); + assertEquals(GREATER_THAN, condition.condition()); + assertEquals(Column.of("age", 10), condition.column()); + } + + @Test + void shouldConvertMapAddressRepository() { + + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + addressRepository.findByZipCodeZip("123456"); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + Assertions.assertThat(query) + .isNotNull() + .matches(c -> c.name().equals("Address")) + .matches(c -> c.columns().isEmpty()) + .matches(c -> c.sorts().isEmpty()) + .extracting(ColumnQuery::condition) + .extracting(Optional::orElseThrow) + .matches(c -> c.condition().equals(EQUALS)) + .extracting(ColumnCondition::column) + .matches(d -> d.value().get().equals("123456")) + .matches(d -> d.name().equals("zipCode.zip")); + + } + + @Test + void shouldConvertMapAddressRepositoryOrder() { + + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + addressRepository.findByZipCodeZipOrderByZipCodeZip("123456"); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + Assertions.assertThat(query) + .isNotNull() + .matches(c -> c.name().equals("Address")) + .matches(c -> c.columns().isEmpty()) + .matches(c -> !c.sorts().isEmpty()) + .extracting(ColumnQuery::condition) + .extracting(Optional::orElseThrow) + .matches(c -> c.condition().equals(EQUALS)) + .extracting(ColumnCondition::column) + .matches(d -> d.value().get().equals("123456")) + .matches(d -> d.name().equals("zipCode.zip")); + + + Assertions.assertThat(query.sorts()).contains(Sort.asc("zipCode.zip")); + + } + + + interface PersonRepository extends CrudRepository { + + List findBySalary_Currency(String currency); + + List findBySalary_CurrencyAndSalary_Value(String currency, BigDecimal value); + + List findBySalary_CurrencyOrderByCurrency_Name(String currency); + + Person findByName(String name); + + List findByNameNotEquals(String name); + + List findByAgeNotGreaterThan(Integer age); + + void deleteByName(String name); + + List findByAge(String age); + + List findByNameAndAge(String name, Integer age); + + Set findByAgeAndName(Integer age, String name); + + Stream findByNameAndAgeOrderByName(String name, Integer age); + + Queue findByNameAndAgeOrderByAge(String name, Integer age); + + Set findByNameAndAgeGreaterThanEqual(String name, Integer age); + + Set findByAgeGreaterThan(Integer age); + + Set findByAgeLessThanEqual(Integer age); + + Set findByAgeLessThan(Integer age); + + Set findByAgeBetween(Integer ageA, Integer ageB); + + Set findByNameLike(String name); + + @Query("select * from Person") + Optional findByQuery(); + + @Query("select * from Person where id = @id") + Optional findByQuery(@Param("id") String id); + } + + public interface VendorRepository extends CrudRepository { + + Vendor findByPrefixes(String prefix); + + Vendor findByPrefixesIn(List prefix); + + } + + public interface AddressRepository extends CrudRepository { + + List
findByZipCodeZip(String zip); + + List
findByZipCodeZipOrderByZipCodeZip(String zip); + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/query/ColumnParameterBasedQueryTest.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/query/ColumnParameterBasedQueryTest.java new file mode 100644 index 000000000..23fe411bf --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/query/ColumnParameterBasedQueryTest.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.query; + +import jakarta.data.Sort; +import jakarta.data.page.PageRequest; +import jakarta.inject.Inject; +import org.assertj.core.api.SoftAssertions; +import org.eclipse.jnosql.communication.Condition; +import org.eclipse.jnosql.communication.TypeReference; +import org.eclipse.jnosql.communication.column.Column; +import org.eclipse.jnosql.communication.column.ColumnCondition; +import org.eclipse.jnosql.communication.column.ColumnQuery; +import org.eclipse.jnosql.mapping.column.ColumnEntityConverter; +import org.eclipse.jnosql.mapping.column.MockProducer; +import org.eclipse.jnosql.mapping.column.entities.Person; +import org.eclipse.jnosql.mapping.column.spi.ColumnExtension; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; +import org.eclipse.jnosql.mapping.metadata.EntityMetadata; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + + +@EnableAutoWeld +@AddPackages(value = {Converters.class, ColumnEntityConverter.class}) +@AddPackages(MockProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, ColumnExtension.class}) +class ColumnParameterBasedQueryTest { + + @Inject + private EntitiesMetadata entitiesMetadata; + + private EntityMetadata metadata; + @BeforeEach + void setUp(){ + this.metadata = entitiesMetadata.get(Person.class); + } + + @Test + void shouldCreateQuerySingleParameter(){ + Map params = Map.of("name", "Ada"); + ColumnQuery query = ColumnParameterBasedQuery.INSTANCE.toQuery(params, metadata); + + SoftAssertions.assertSoftly(soft ->{ + soft.assertThat(query.limit()).isEqualTo(0L); + soft.assertThat(query.skip()).isEqualTo(0L); + soft.assertThat(query.name()).isEqualTo("Person"); + soft.assertThat(query.sorts()).isEmpty(); + soft.assertThat(query.condition()).isNotEmpty(); + soft.assertThat(query.condition()).get().isEqualTo(ColumnCondition.eq(Column.of("name", "Ada"))); + }); + } + + @Test + void shouldCreateQueryMultipleParams(){ + Map params = Map.of("name", "Ada", "age", 10); + ColumnQuery query = ColumnParameterBasedQuery.INSTANCE.toQuery(params, metadata); + + SoftAssertions.assertSoftly(soft ->{ + soft.assertThat(query.limit()).isEqualTo(0L); + soft.assertThat(query.skip()).isEqualTo(0L); + soft.assertThat(query.name()).isEqualTo("Person"); + soft.assertThat(query.sorts()).isEmpty(); + soft.assertThat(query.condition()).isNotEmpty(); + var condition = query.condition().orElseThrow(); + soft.assertThat(condition.condition()).isEqualTo(Condition.AND); + soft.assertThat(condition.column().get(new TypeReference>() { + })).contains(ColumnCondition.eq(Column.of("name", "Ada")), + ColumnCondition.eq(Column.of("age", 10))); + }); + + } + + @Test + void shouldCreateQueryEmptyParams(){ + Map params = Collections.emptyMap(); + ColumnQuery query = ColumnParameterBasedQuery.INSTANCE.toQuery(params, metadata); + + SoftAssertions.assertSoftly(soft ->{ + soft.assertThat(query.limit()).isEqualTo(0L); + soft.assertThat(query.skip()).isEqualTo(0L); + soft.assertThat(query.name()).isEqualTo("Person"); + soft.assertThat(query.sorts()).isEmpty(); + soft.assertThat(query.condition()).isEmpty(); + }); + } + +} \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/query/ColumnRepositoryExtensionTest.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/query/ColumnRepositoryExtensionTest.java new file mode 100644 index 000000000..5523cc785 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/query/ColumnRepositoryExtensionTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.query; + +import jakarta.inject.Inject; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.Database; +import org.eclipse.jnosql.mapping.DatabaseType; +import org.eclipse.jnosql.mapping.column.ColumnEntityConverter; +import org.eclipse.jnosql.mapping.column.MockProducer; +import org.eclipse.jnosql.mapping.column.entities.Person; +import org.eclipse.jnosql.mapping.column.entities.PersonRepository; +import org.eclipse.jnosql.mapping.column.spi.ColumnExtension; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, ColumnEntityConverter.class}) +@AddPackages(MockProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, ColumnExtension.class}) +class ColumnRepositoryExtensionTest { + + @Inject + @Database(value = DatabaseType.COLUMN) + private PersonRepository repository; + + @Inject + @Database(value = DatabaseType.COLUMN, provider = "columnRepositoryMock") + private PersonRepository repositoryMock; + + @Test + void shouldInitiate() { + assertNotNull(repository); + Person person = repository.save(Person.builder().build()); + assertEquals("Default", person.getName()); + } + + @Test + void shouldUseInstantiation(){ + assertNotNull(repositoryMock); + Person person = repositoryMock.save(Person.builder().build()); + assertEquals("columnRepositoryMock", person.getName()); + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/query/ColumnRepositoryProxyPageRequestTest.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/query/ColumnRepositoryProxyPageRequestTest.java new file mode 100644 index 000000000..2db35b21a --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/query/ColumnRepositoryProxyPageRequestTest.java @@ -0,0 +1,658 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.query; + +import jakarta.data.Limit; +import jakarta.data.page.Page; +import jakarta.data.page.PageRequest; +import jakarta.data.repository.BasicRepository; +import jakarta.data.page.Slice; +import jakarta.data.Sort; +import jakarta.inject.Inject; +import org.eclipse.jnosql.communication.Condition; +import org.eclipse.jnosql.communication.TypeReference; +import org.eclipse.jnosql.communication.Value; +import org.eclipse.jnosql.communication.column.Column; +import org.eclipse.jnosql.communication.column.ColumnCondition; +import org.eclipse.jnosql.communication.column.ColumnQuery; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.NoSQLPage; +import org.eclipse.jnosql.mapping.column.ColumnEntityConverter; +import org.eclipse.jnosql.mapping.column.JNoSQLColumnTemplate; +import org.eclipse.jnosql.mapping.column.MockProducer; +import org.eclipse.jnosql.mapping.column.entities.Person; +import org.eclipse.jnosql.mapping.column.entities.Vendor; +import org.eclipse.jnosql.mapping.column.spi.ColumnExtension; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; + +import java.lang.reflect.Proxy; +import java.time.Duration; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.Queue; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.jnosql.communication.Condition.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, ColumnEntityConverter.class}) +@AddPackages(MockProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, ColumnExtension.class}) +public class ColumnRepositoryProxyPageRequestTest { + + private JNoSQLColumnTemplate template; + + @Inject + private EntitiesMetadata entities; + + @Inject + private Converters converters; + + private PersonRepository personRepository; + + private VendorRepository vendorRepository; + + + @BeforeEach + public void setUp() { + this.template = Mockito.mock(JNoSQLColumnTemplate.class); + + ColumnRepositoryProxy personHandler = new ColumnRepositoryProxy(template, + entities, PersonRepository.class, converters); + + ColumnRepositoryProxy vendorHandler = new ColumnRepositoryProxy(template, + entities, VendorRepository.class, converters); + + when(template.insert(any(Person.class))).thenReturn(Person.builder().build()); + when(template.insert(any(Person.class), any(Duration.class))).thenReturn(Person.builder().build()); + when(template.update(any(Person.class))).thenReturn(Person.builder().build()); + + personRepository = (PersonRepository) Proxy.newProxyInstance(PersonRepository.class.getClassLoader(), + new Class[]{PersonRepository.class}, + personHandler); + vendorRepository = (VendorRepository) Proxy.newProxyInstance(VendorRepository.class.getClassLoader(), + new Class[]{VendorRepository.class}, vendorHandler); + } + + + @Test + public void shouldFindByNameInstance() { + + when(template.singleResult(any(ColumnQuery.class))).thenReturn(Optional + .of(Person.builder().build())); + + PageRequest pageRequest = getPageRequest(); + personRepository.findByName("name", pageRequest); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).singleResult(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(Condition.EQUALS, condition.condition()); + assertEquals(pageRequest.size(), query.skip()); + assertEquals(NoSQLPage.skip(pageRequest), query.limit()); + + assertEquals(Column.of("name", "name"), condition.column()); + + assertNotNull(personRepository.findByName("name", pageRequest)); + when(template.singleResult(any(ColumnQuery.class))).thenReturn(Optional + .empty()); + + assertNull(personRepository.findByName("name", pageRequest)); + + + } + + @Test + public void shouldFindByNameANDAge() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + PageRequest pageRequest = getPageRequest(); + List persons = personRepository.findByNameAndAge("name", 20, pageRequest); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + assertThat(persons).contains(ada); + + ColumnQuery query = captor.getValue(); + assertEquals("Person", query.name()); + assertEquals(NoSQLPage.skip(pageRequest), query.limit()); + assertEquals(pageRequest.size(), query.limit()); + + } + + @Test + public void shouldFindByAgeANDName() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + PageRequest pageRequest = getPageRequest(); + Set persons = personRepository.findByAgeAndName(20, "name", pageRequest); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + assertThat(persons).contains(ada); + ColumnQuery query = captor.getValue(); + assertEquals("Person", query.name()); + assertEquals(NoSQLPage.skip(pageRequest), query.limit()); + assertEquals(pageRequest.size(), query.limit()); + + } + + @Test + public void shouldFindByNameANDAgeOrderByName() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + PageRequest pageRequest = getPageRequest(); + + Stream persons = personRepository.findByNameAndAgeOrderByName("name", 20, pageRequest); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + assertThat(persons.collect(Collectors.toList())).contains(ada); + ColumnQuery query = captor.getValue(); + assertEquals("Person", query.name()); + assertEquals(NoSQLPage.skip(pageRequest), query.limit()); + assertEquals(pageRequest.size(), query.limit()); + + } + + @Test + public void shouldFindByNameANDAgeOrderByAge() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + PageRequest pageRequest = getPageRequest(); + Queue persons = personRepository.findByNameAndAgeOrderByAge("name", 20, pageRequest); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + assertThat(persons).contains(ada); + ColumnQuery query = captor.getValue(); + assertEquals("Person", query.name()); + assertEquals(NoSQLPage.skip(pageRequest), query.limit()); + assertEquals(pageRequest.size(), query.limit()); + + + } + + + @Test + public void shouldFindAll() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.findAll(Person.class)) + .thenReturn(Stream.of(ada)); + + PageRequest pageRequest = getPageRequest(); + + List persons = personRepository.findAll(pageRequest).content(); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + assertFalse(query.condition().isPresent()); + assertEquals("Person", query.name()); + assertEquals(NoSQLPage.skip(pageRequest), query.limit()); + assertEquals(pageRequest.size(), query.limit()); + } + + + @Test + public void shouldFindByNameAndAgeGreaterEqualThan() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + PageRequest pageRequest = getPageRequest(); + personRepository.findByNameAndAgeGreaterThanEqual("Ada", 33, pageRequest); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(AND, condition.condition()); + List conditions = condition.column().get(new TypeReference<>() { + }); + ColumnCondition columnCondition = conditions.get(0); + ColumnCondition columnCondition2 = conditions.get(1); + + assertEquals(Condition.EQUALS, columnCondition.condition()); + assertEquals("Ada", columnCondition.column().get()); + assertEquals("name", columnCondition.column().name()); + + assertEquals(Condition.GREATER_EQUALS_THAN, columnCondition2.condition()); + assertEquals(33, columnCondition2.column().get()); + assertEquals("age", columnCondition2.column().name()); + assertEquals(NoSQLPage.skip(pageRequest), query.limit()); + assertEquals(pageRequest.size(), query.limit()); + } + + @Test + public void shouldFindByGreaterThan() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + PageRequest pageRequest = getPageRequest(); + personRepository.findByAgeGreaterThan(33, pageRequest); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(GREATER_THAN, condition.condition()); + assertEquals(Column.of("age", 33), condition.column()); + assertEquals(NoSQLPage.skip(pageRequest), query.limit()); + assertEquals(pageRequest.size(), query.limit()); + + } + + @Test + public void shouldFindByAgeLessThanEqual() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + PageRequest pageRequest = getPageRequest(); + personRepository.findByAgeLessThanEqual(33, pageRequest); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(LESSER_EQUALS_THAN, condition.condition()); + assertEquals(Column.of("age", 33), condition.column()); + assertEquals(NoSQLPage.skip(pageRequest), query.limit()); + assertEquals(pageRequest.size(), query.limit()); + + } + + @Test + public void shouldFindByAgeLessEqual() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + PageRequest pageRequest = getPageRequest(); + personRepository.findByAgeLessThan(33, pageRequest); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(LESSER_THAN, condition.condition()); + assertEquals(Column.of("age", 33), condition.column()); + assertEquals(NoSQLPage.skip(pageRequest), query.limit()); + assertEquals(pageRequest.size(), query.limit()); + + } + + @Test + public void shouldFindByAgeBetween() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + PageRequest pageRequest = getPageRequest(); + personRepository.findByAgeBetween(10, 15, pageRequest); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(BETWEEN, condition.condition()); + List values = condition.column().get(new TypeReference<>() { + }); + assertEquals(Arrays.asList(10, 15), values.stream().map(Value::get).collect(Collectors.toList())); + assertTrue(condition.column().name().contains("age")); + assertEquals(NoSQLPage.skip(pageRequest), query.limit()); + assertEquals(pageRequest.size(), query.limit()); + } + + + @Test + public void shouldFindByNameLike() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + PageRequest pageRequest = getPageRequest(); + personRepository.findByNameLike("Ada", pageRequest); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(LIKE, condition.condition()); + assertEquals(Column.of("name", "Ada"), condition.column()); + assertEquals(NoSQLPage.skip(pageRequest), query.limit()); + assertEquals(pageRequest.size(), query.limit()); + + } + + + @Test + public void shouldFindByStringWhenFieldIsSet() { + Vendor vendor = new Vendor("vendor"); + vendor.setPrefixes(Collections.singleton("prefix")); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(vendor)); + + PageRequest pageRequest = getPageRequest(); + vendorRepository.findByPrefixes("prefix", pageRequest); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).singleResult(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("vendors", query.name()); + assertEquals(EQUALS, condition.condition()); + assertEquals(Column.of("prefixes", "prefix"), condition.column()); + assertEquals(NoSQLPage.skip(pageRequest), query.limit()); + assertEquals(pageRequest.size(), query.limit()); + + } + + @Test + public void shouldFindByIn() { + Vendor vendor = new Vendor("vendor"); + vendor.setPrefixes(Collections.singleton("prefix")); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(vendor)); + + PageRequest pageRequest = getPageRequest(); + vendorRepository.findByPrefixesIn(singletonList("prefix"), pageRequest); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).singleResult(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("vendors", query.name()); + assertEquals(IN, condition.condition()); + assertEquals(NoSQLPage.skip(pageRequest), query.limit()); + assertEquals(pageRequest.size(), query.limit()); + + } + + @Test + public void shouldConvertFieldToTheType() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + PageRequest pageRequest = getPageRequest(); + Slice slice = personRepository.findByAge("120", pageRequest); + Assertions.assertNotNull(slice); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(EQUALS, condition.condition()); + assertEquals(Column.of("age", 120), condition.column()); + assertEquals(NoSQLPage.skip(pageRequest), query.limit()); + assertEquals(pageRequest.size(), query.limit()); + } + + @Test + public void shouldFindByNameOrderName() { + + when(template.singleResult(any(ColumnQuery.class))).thenReturn(Optional + .of(Person.builder().build())); + + PageRequest pageRequest = getPageRequest().sortBy(Sort.asc("name")); + personRepository.findByName("name", pageRequest); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).singleResult(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(EQUALS, condition.condition()); + assertEquals(NoSQLPage.skip(pageRequest), query.limit()); + assertEquals(pageRequest.size(), query.limit()); + assertThat(query.sorts()).hasSize(1) + .contains(Sort.asc("name")); + + assertEquals(Column.of("name", "name"), condition.column()); + + assertNotNull(personRepository.findByName("name", pageRequest)); + when(template.singleResult(any(ColumnQuery.class))).thenReturn(Optional + .empty()); + assertNull(personRepository.findByName("name", pageRequest)); + } + + @Test + public void shouldFindByNameOrderName2() { + + when(template.singleResult(any(ColumnQuery.class))).thenReturn(Optional + .of(Person.builder().build())); + + PageRequest pageRequest = getPageRequest().sortBy(Sort.asc("name")); + Page page = personRepository.findByNameOrderByAge("name", pageRequest); + + Assertions.assertNotNull(page); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(EQUALS, condition.condition()); + assertEquals(NoSQLPage.skip(pageRequest), query.limit()); + assertEquals(pageRequest.size(), query.limit()); + assertThat(query.sorts()).hasSize(2) + .containsExactly(Sort.asc("age"), Sort.asc("name")); + + assertEquals(Column.of("name", "name"), condition.column()); + + assertNotNull(personRepository.findByName("name", pageRequest)); + when(template.singleResult(any(ColumnQuery.class))).thenReturn(Optional + .empty()); + assertNull(personRepository.findByName("name", pageRequest)); + } + + @Test + public void shouldFindByNameSort() { + when(template.singleResult(any(ColumnQuery.class))).thenReturn(Optional + .of(Person.builder().build())); + + PageRequest pageRequest = getPageRequest().sortBy(Sort.desc("age")); + personRepository.findByName("name", Sort.asc("name"), pageRequest); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(EQUALS, condition.condition()); + assertThat(query.sorts()).hasSize(2) + .containsExactly(Sort.asc("name"), Sort.desc("age")); + assertEquals(Column.of("name", "name"), condition.column()); + } + + @Test + public void shouldFindByNameSortPagination() { + when(template.singleResult(any(ColumnQuery.class))).thenReturn(Optional + .of(Person.builder().build())); + + personRepository.findByName("name", Sort.asc("name")); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(EQUALS, condition.condition()); + assertThat(query.sorts()).hasSize(1) + .containsExactly(Sort.asc("name")); + assertEquals(Column.of("name", "name"), condition.column()); + } + + @Test + public void shouldFindByNameLimit() { + when(template.singleResult(any(ColumnQuery.class))).thenReturn(Optional + .of(Person.builder().build())); + + personRepository.findByName("name", Limit.of(3), Sort.asc("name")); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(0, query.skip()); + assertEquals(3, query.limit()); + assertEquals(EQUALS, condition.condition()); + assertThat(query.sorts()).hasSize(1) + .containsExactly(Sort.asc("name")); + assertEquals(Column.of("name", "name"), condition.column()); + } + + @Test + public void shouldFindByNameLimit2() { + when(template.singleResult(any(ColumnQuery.class))).thenReturn(Optional + .of(Person.builder().build())); + + personRepository.findByName("name", Limit.range(1, 3), Sort.asc("name")); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(0, query.skip()); + assertEquals(3, query.limit()); + assertEquals(EQUALS, condition.condition()); + assertThat(query.sorts()).hasSize(1) + .containsExactly(Sort.asc("name")); + assertEquals(Column.of("name", "name"), condition.column()); + } + + @Test + public void shouldFindByNameLimit3() { + when(template.singleResult(any(ColumnQuery.class))).thenReturn(Optional + .of(Person.builder().build())); + + personRepository.findByName("name", Limit.range(2, 3), Sort.asc("name")); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(1, query.skip()); + assertEquals(2, query.limit()); + assertEquals(EQUALS, condition.condition()); + assertThat(query.sorts()).hasSize(1) + .containsExactly(Sort.asc("name")); + assertEquals(Column.of("name", "name"), condition.column()); + } + + + private PageRequest getPageRequest() { + return PageRequest.ofPage(2).size(6); + } + + interface PersonRepository extends BasicRepository { + + Person findByName(String name, PageRequest pageRequest); + + List findByName(String name, Sort sort); + + List findByName(String name, Limit limit, Sort sort); + + List findByName(String name, Sort sort, PageRequest pageRequest); + + Page findByNameOrderByAge(String name, PageRequest pageRequest); + + Slice findByAge(String age, PageRequest pageRequest); + + List findByNameAndAge(String name, Integer age, PageRequest pageRequest); + + Set findByAgeAndName(Integer age, String name, PageRequest pageRequest); + + Stream findByNameAndAgeOrderByName(String name, Integer age, PageRequest pageRequest); + + Queue findByNameAndAgeOrderByAge(String name, Integer age, PageRequest pageRequest); + + Set findByNameAndAgeGreaterThanEqual(String name, Integer age, PageRequest pageRequest); + + Set findByAgeGreaterThan(Integer age, PageRequest pageRequest); + + Set findByAgeLessThanEqual(Integer age, PageRequest pageRequest); + + Set findByAgeLessThan(Integer age, PageRequest pageRequest); + + Set findByAgeBetween(Integer ageA, Integer ageB, PageRequest pageRequest); + + Set findByNameLike(String name, PageRequest pageRequest); + + } + + public interface VendorRepository extends BasicRepository { + + Vendor findByPrefixes(String prefix, PageRequest pageRequest); + + Vendor findByPrefixesIn(List prefix, PageRequest pageRequest); + + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/query/ColumnRepositoryProxyTest.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/query/ColumnRepositoryProxyTest.java new file mode 100644 index 000000000..54b23f76c --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/query/ColumnRepositoryProxyTest.java @@ -0,0 +1,971 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.query; + +import jakarta.data.exceptions.MappingException; +import jakarta.data.repository.By; +import jakarta.data.repository.Delete; +import jakarta.data.repository.Insert; +import jakarta.data.repository.OrderBy; +import jakarta.data.repository.BasicRepository; +import jakarta.data.repository.Param; +import jakarta.data.repository.Query; +import jakarta.data.Sort; +import jakarta.data.repository.Save; +import jakarta.data.repository.Update; +import jakarta.inject.Inject; +import jakarta.nosql.PreparedStatement; +import org.assertj.core.api.SoftAssertions; +import org.eclipse.jnosql.communication.Condition; +import org.eclipse.jnosql.communication.TypeReference; +import org.eclipse.jnosql.communication.Value; +import org.eclipse.jnosql.communication.column.Column; +import org.eclipse.jnosql.communication.column.ColumnCondition; +import org.eclipse.jnosql.communication.column.ColumnDeleteQuery; +import org.eclipse.jnosql.communication.column.ColumnQuery; +import org.eclipse.jnosql.mapping.NoSQLRepository; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.column.ColumnEntityConverter; +import org.eclipse.jnosql.mapping.column.JNoSQLColumnTemplate; +import org.eclipse.jnosql.mapping.column.MockProducer; +import org.eclipse.jnosql.mapping.column.entities.Person; +import org.eclipse.jnosql.mapping.column.entities.PersonStatisticRepository; +import org.eclipse.jnosql.mapping.column.entities.Vendor; +import org.eclipse.jnosql.mapping.column.spi.ColumnExtension; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; + +import java.lang.reflect.Proxy; +import java.math.BigDecimal; +import java.time.Duration; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Queue; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.jnosql.communication.Condition.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, ColumnEntityConverter.class}) +@AddPackages(MockProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, ColumnExtension.class}) +class ColumnRepositoryProxyTest { + + private JNoSQLColumnTemplate template; + + @Inject + private EntitiesMetadata entities; + + @Inject + private Converters converters; + + private PersonRepository personRepository; + + private VendorRepository vendorRepository; + + + @BeforeEach + void setUp() { + this.template = Mockito.mock(JNoSQLColumnTemplate.class); + + ColumnRepositoryProxy personHandler = new ColumnRepositoryProxy(template, + entities, PersonRepository.class, converters); + + ColumnRepositoryProxy vendorHandler = new ColumnRepositoryProxy(template, + entities, VendorRepository.class, converters); + + when(template.insert(any(Person.class))).thenReturn(Person.builder().build()); + when(template.insert(any(Person.class), any(Duration.class))).thenReturn(Person.builder().build()); + when(template.update(any(Person.class))).thenReturn(Person.builder().build()); + + personRepository = (PersonRepository) Proxy.newProxyInstance(PersonRepository.class.getClassLoader(), + new Class[]{PersonRepository.class}, + personHandler); + vendorRepository = (VendorRepository) Proxy.newProxyInstance(VendorRepository.class.getClassLoader(), + new Class[]{VendorRepository.class}, vendorHandler); + } + + + @Test + void shouldSaveUsingInsertWhenDataDoesNotExist() { + when(template.find(Person.class, 10L)).thenReturn(Optional.empty()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Person.class); + Person person = Person.builder().withName("Ada") + .withId(10L) + .withPhones(singletonList("123123")) + .build(); + assertNotNull(personRepository.save(person)); + verify(template).insert(captor.capture()); + Person value = captor.getValue(); + assertEquals(person, value); + } + + + @Test + void shouldSaveUsingUpdateWhenDataExists() { + + when(template.find(Person.class, 10L)).thenReturn(Optional.of(Person.builder().build())); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Person.class); + Person person = Person.builder().withName("Ada") + .withId(10L) + .withPhones(singletonList("123123")) + .build(); + assertNotNull(personRepository.save(person)); + verify(template).update(captor.capture()); + Person value = captor.getValue(); + assertEquals(person, value); + } + + + @Test + void shouldSaveIterable() { + when(personRepository.findById(10L)).thenReturn(Optional.empty()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Person.class); + Person person = Person.builder().withName("Ada") + .withId(10L) + .withPhones(singletonList("123123")) + .build(); + + personRepository.saveAll(singletonList(person)); + verify(template).insert(captor.capture()); + Person personCapture = captor.getValue(); + assertEquals(person, personCapture); + } + + + @Test + void shouldFindByNameInstance() { + + when(template.singleResult(any(ColumnQuery.class))).thenReturn(Optional + .of(Person.builder().build())); + + personRepository.findByName("name"); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).singleResult(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(Condition.EQUALS, condition.condition()); + assertEquals(Column.of("name", "name"), condition.column()); + + assertNotNull(personRepository.findByName("name")); + when(template.singleResult(any(ColumnQuery.class))).thenReturn(Optional + .empty()); + + assertNull(personRepository.findByName("name")); + + + } + + @Test + void shouldFindByNameANDAge() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + List persons = personRepository.findByNameAndAge("name", 20); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + assertThat(persons).contains(ada); + + } + + @Test + void shouldFindByAgeANDName() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + Set persons = personRepository.findByAgeAndName(20, "name"); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + assertThat(persons).contains(ada); + + } + + @Test + void shouldFindByNameANDAgeOrderByName() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + Stream persons = personRepository.findByNameAndAgeOrderByName("name", 20); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + assertThat(persons.collect(Collectors.toList())).contains(ada); + + } + + @Test + void shouldFindByNameANDAgeOrderByAge() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + Queue persons = personRepository.findByNameAndAgeOrderByAge("name", 20); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + assertThat(persons).contains(ada); + + } + + @Test + void shouldDeleteByName() { + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnDeleteQuery.class); + personRepository.deleteByName("Ada"); + verify(template).delete(captor.capture()); + ColumnDeleteQuery deleteQuery = captor.getValue(); + ColumnCondition condition = deleteQuery.condition().get(); + assertEquals("Person", deleteQuery.name()); + assertEquals(Condition.EQUALS, condition.condition()); + assertEquals(Column.of("name", "Ada"), condition.column()); + + } + + @Test + void shouldFindById() { + personRepository.findById(10L); + verify(template).find(Person.class, 10L); + } + + @Test + void shouldFindByIds() { + when(template.find(Mockito.eq(Person.class), Mockito.any(Long.class))) + .thenReturn(Optional.of(Person.builder().build())); + + personRepository.findByIdIn(singletonList(10L)).toList(); + verify(template).find(Person.class, 10L); + + personRepository.findByIdIn(asList(1L, 2L, 3L)).toList(); + verify(template, times(4)).find(Mockito.eq(Person.class), Mockito.any(Long.class)); + } + + @Test + void shouldDeleteById() { + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnDeleteQuery.class); + personRepository.deleteById(10L); + verify(template).delete(Person.class, 10L); + } + + @Test + void shouldDeleteByIds() { + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnDeleteQuery.class); + personRepository.deleteByIdIn(singletonList(10L)); + verify(template).delete(Person.class, 10L); + } + + + @Test + void shouldContainsById() { + when(template.find(Person.class, 10L)).thenReturn(Optional.of(Person.builder().build())); + + assertTrue(personRepository.existsById(10L)); + Mockito.verify(template).find(Person.class, 10L); + + when(template.find(Person.class, 10L)).thenReturn(Optional.empty()); + assertFalse(personRepository.existsById(10L)); + + } + + @Test + void shouldFindAll() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + personRepository.findAll().toList(); + ArgumentCaptor> captor = ArgumentCaptor.forClass(Class.class); + verify(template).findAll(captor.capture()); + assertEquals(captor.getValue(), Person.class); + + } + + @Test + void shouldDeleteAll() { + personRepository.deleteAll(); + ArgumentCaptor> captor = ArgumentCaptor.forClass(Class.class); + verify(template).deleteAll(captor.capture()); + assertEquals(captor.getValue(), Person.class); + + } + + @Test + void shouldDeleteEntity(){ + Person person = Person.builder().withId(1L).withAge(20).withName("Ada").build(); + personRepository.delete(person); + verify(template).delete(Person.class, 1L); + } + + @Test + void shouldDeleteEntities(){ + Person person = Person.builder().withId(1L).withAge(20).withName("Ada").build(); + personRepository.deleteAll(List.of(person)); + verify(template).delete(Person.class, 1L); + } + + @Test + void shouldReturnToString() { + assertNotNull(personRepository.toString()); + } + + @Test + void shouldReturnSameHashCode() { + assertEquals(personRepository.hashCode(), personRepository.hashCode()); + } + + @Test + void shouldReturnNotNull() { + assertNotNull(personRepository); + } + + @Test + void shouldFindByNameAndAgeGreaterEqualThan() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + personRepository.findByNameAndAgeGreaterThanEqual("Ada", 33); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(AND, condition.condition()); + List conditions = condition.column().get(new TypeReference<>() { + }); + ColumnCondition columnCondition = conditions.get(0); + ColumnCondition columnCondition2 = conditions.get(1); + + assertEquals(Condition.EQUALS, columnCondition.condition()); + assertEquals("Ada", columnCondition.column().get()); + assertEquals("name", columnCondition.column().name()); + + assertEquals(Condition.GREATER_EQUALS_THAN, columnCondition2.condition()); + assertEquals(33, columnCondition2.column().get()); + assertEquals("age", columnCondition2.column().name()); + } + + @Test + void shouldFindByGreaterThan() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + personRepository.findByAgeGreaterThan(33); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(GREATER_THAN, condition.condition()); + assertEquals(Column.of("age", 33), condition.column()); + + } + + @Test + void shouldFindByAgeLessThanEqual() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + personRepository.findByAgeLessThanEqual(33); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(LESSER_EQUALS_THAN, condition.condition()); + assertEquals(Column.of("age", 33), condition.column()); + + } + + @Test + void shouldFindByAgeLessEqual() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + personRepository.findByAgeLessThan(33); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(LESSER_THAN, condition.condition()); + assertEquals(Column.of("age", 33), condition.column()); + + } + + @Test + void shouldFindByAgeBetween() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + personRepository.findByAgeBetween(10, 15); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(BETWEEN, condition.condition()); + List values = condition.column().get(new TypeReference<>() { + }); + assertEquals(Arrays.asList(10, 15), values.stream().map(Value::get).collect(Collectors.toList())); + assertTrue(condition.column().name().contains("age")); + } + + + @Test + void shouldFindByNameLike() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + personRepository.findByNameLike("Ada"); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(LIKE, condition.condition()); + assertEquals(Column.of("name", "Ada"), condition.column()); + + } + + @Test + void shouldGotOrderException() { + Assertions.assertThrows(MappingException.class, () -> + personRepository.findBy()); + } + + @Test + void shouldGotOrderException2() { + Assertions.assertThrows(MappingException.class, () -> + personRepository.findByException()); + } + + + @Test + void shouldFindByStringWhenFieldIsSet() { + Vendor vendor = new Vendor("vendor"); + vendor.setPrefixes(Collections.singleton("prefix")); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(vendor)); + + vendorRepository.findByPrefixes("prefix"); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).singleResult(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("vendors", query.name()); + assertEquals(EQUALS, condition.condition()); + assertEquals(Column.of("prefixes", "prefix"), condition.column()); + + } + + @Test + void shouldFindByIn() { + Vendor vendor = new Vendor("vendor"); + vendor.setPrefixes(Collections.singleton("prefix")); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(vendor)); + + vendorRepository.findByPrefixesIn(singletonList("prefix")); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).singleResult(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("vendors", query.name()); + assertEquals(IN, condition.condition()); + + } + + + @Test + void shouldConvertFieldToTheType() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + personRepository.findByAge("120"); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(EQUALS, condition.condition()); + assertEquals(Column.of("age", 120), condition.column()); + } + + @Test + void shouldFindByActiveTrue() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + personRepository.findByActiveTrue(); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(EQUALS, condition.condition()); + assertEquals(Column.of("active", true), condition.column()); + } + + @Test + void shouldFindByActiveFalse() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + personRepository.findByActiveFalse(); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals("Person", query.name()); + assertEquals(EQUALS, condition.condition()); + assertEquals(Column.of("active", false), condition.column()); + } + + + @Test + void shouldExecuteJNoSQLQuery() { + personRepository.findByQuery(); + verify(template).query("select * from Person"); + } + + @Test + void shouldExecuteJNoSQLPrepare() { + PreparedStatement statement = Mockito.mock(PreparedStatement.class); + when(template.prepare(Mockito.anyString())).thenReturn(statement); + personRepository.findByQuery("Ada"); + verify(statement).bind("id", "Ada"); + } + + @Test + void shouldFindBySalary_Currency() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + personRepository.findBySalary_Currency("USD"); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + final Column column = condition.column(); + assertEquals("Person", query.name()); + assertEquals("salary.currency", column.name()); + + } + + @Test + void shouldFindBySalary_CurrencyAndSalary_Value() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + personRepository.findBySalary_CurrencyAndSalary_Value("USD", BigDecimal.TEN); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + final Column column = condition.column(); + final List conditions = column.get(new TypeReference<>() { + }); + final List names = conditions.stream().map(ColumnCondition::column) + .map(Column::name).collect(Collectors.toList()); + assertEquals("Person", query.name()); + assertThat(names).contains("salary.currency", "salary.value"); + + } + + @Test + void shouldFindBySalary_CurrencyOrderByCurrency_Name() { + Person ada = Person.builder() + .withAge(20).withName("Ada").build(); + + when(template.select(any(ColumnQuery.class))) + .thenReturn(Stream.of(ada)); + + personRepository.findBySalary_CurrencyOrderByCurrency_Name("USD"); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + final Sort sort = query.sorts().get(0); + final Column document = condition.column(); + assertEquals("Person", query.name()); + assertEquals("salary.currency", document.name()); + assertEquals("currency.name", sort.property()); + } + + @Test + void shouldCountByName() { + when(template.count(any(ColumnQuery.class))) + .thenReturn(10L); + + var result = personRepository.countByName("Poliana"); + Assertions.assertEquals(10L, result); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).count(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + final Column column = condition.column(); + assertEquals("Person", query.name()); + assertEquals("name", column.name()); + assertEquals("Poliana", column.get()); + } + + @Test + void shouldExistsByName() { + when(template.exists(any(ColumnQuery.class))) + .thenReturn(true); + + var result = personRepository.existsByName("Poliana"); + assertTrue(result); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).exists(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + final Column column = condition.column(); + assertEquals("Person", query.name()); + assertEquals("name", column.name()); + assertEquals("Poliana", column.get()); + } + + @Test + void shouldFindByNameNot() { + + when(template.singleResult(any(ColumnQuery.class))).thenReturn(Optional + .of(Person.builder().build())); + + personRepository.findByNameNot("name"); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).singleResult(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals(Condition.NOT, condition.condition()); + assertEquals("Person", query.name()); + ColumnCondition columnCondition = condition.column().get(ColumnCondition.class); + assertEquals(Condition.EQUALS, columnCondition.condition()); + assertEquals(Column.of("name", "name"), columnCondition.column()); + + assertNotNull(personRepository.findByName("name")); + when(template.singleResult(any(ColumnQuery.class))).thenReturn(Optional + .empty()); + + assertNull(personRepository.findByName("name")); + } + + @Test + void shouldFindByNameNotEquals() { + + when(template.singleResult(any(ColumnQuery.class))).thenReturn(Optional + .of(Person.builder().build())); + + personRepository.findByNameNotEquals("name"); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).singleResult(captor.capture()); + ColumnQuery query = captor.getValue(); + ColumnCondition condition = query.condition().get(); + assertEquals(Condition.NOT, condition.condition()); + assertEquals("Person", query.name()); + ColumnCondition columnCondition = condition.column().get(ColumnCondition.class); + assertEquals(Condition.EQUALS, columnCondition.condition()); + assertEquals(Column.of("name", "name"), columnCondition.column()); + + assertNotNull(personRepository.findByName("name")); + when(template.singleResult(any(ColumnQuery.class))).thenReturn(Optional + .empty()); + + assertNull(personRepository.findByName("name")); + } + + @Test + void shouldExecuteDefaultMethod() { + personRepository.partcionate("name"); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template, Mockito.times(2)).singleResult(captor.capture()); + List values = captor.getAllValues(); + assertThat(values).isNotNull().hasSize(2); + } + + @Test + void shouldUseQueriesFromOtherInterface() { + personRepository.findByNameLessThan("name"); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + assertEquals("Person", query.name()); + ColumnCondition condition = query.condition().get(); + assertEquals(LESSER_THAN, condition.condition()); + assertEquals(Column.of("name", "name"), condition.column()); + } + + @Test + void shouldUseDefaultMethodFromOtherInterface() { + personRepository.ada(); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + assertEquals("Person", query.name()); + ColumnCondition condition = query.condition().get(); + assertEquals(LESSER_THAN, condition.condition()); + assertEquals(Column.of("name", "Ada"), condition.column()); + } + + @Test + void shouldExecuteCustomRepository(){ + PersonStatisticRepository.PersonStatistic statistics = personRepository.statistics("Salvador"); + assertThat(statistics).isNotNull(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(statistics.average()).isEqualTo(26); + softly.assertThat(statistics.sum()).isEqualTo(26); + softly.assertThat(statistics.max()).isEqualTo(26); + softly.assertThat(statistics.min()).isEqualTo(26); + softly.assertThat(statistics.count()).isEqualTo(1); + softly.assertThat(statistics.city()).isEqualTo("Salvador"); + }); + } + + @Test + void shouldInsertUsingAnnotation(){ + Person person = Person.builder().withName("Ada") + .withId(10L) + .withPhones(singletonList("123123")) + .build(); + personRepository.insertPerson(person); + Mockito.verify(template).insert(person); + } + + @Test + void shouldUpdateUsingAnnotation(){ + Person person = Person.builder().withName("Ada") + .withId(10L) + .withPhones(singletonList("123123")) + .build(); + personRepository.updatePerson(person); + Mockito.verify(template).update(person); + } + + @Test + void shouldDeleteUsingAnnotation(){ + Person person = Person.builder().withName("Ada") + .withId(10L) + .withPhones(singletonList("123123")) + .build(); + personRepository.deletePerson(person); + Mockito.verify(template).delete(Person.class, 10L); + } + + @Test + void shouldSaveUsingAnnotation(){ + Person person = Person.builder().withName("Ada") + .withId(10L) + .withPhones(singletonList("123123")) + .build(); + personRepository.savePerson(person); + Mockito.verify(template).insert(person); + } + + @Test + void shouldExecuteMatchParameter(){ + personRepository.find("Ada"); + ArgumentCaptor captor = ArgumentCaptor.forClass(ColumnQuery.class); + verify(template).select(captor.capture()); + ColumnQuery query = captor.getValue(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(query.name()).isEqualTo("Person"); + var condition = query.condition().orElseThrow(); + softly.assertThat(condition.condition()).isEqualTo(Condition.EQUALS); + softly.assertThat(condition.column()).isEqualTo(Column.of("name", "Ada")); + }); + } + + public interface BaseQuery { + + List findByNameLessThan(String name); + + default List ada() { + return this.findByNameLessThan("Ada"); + } + } + + public interface PersonRepository extends NoSQLRepository, BaseQuery, PersonStatisticRepository { + + List findByActiveTrue(); + + List findByActiveFalse(); + + List findBySalary_Currency(String currency); + + List findBySalary_CurrencyAndSalary_Value(String currency, BigDecimal value); + + List findBySalary_CurrencyOrderByCurrency_Name(String currency); + + Person findByName(String name); + + Person findByNameNot(String name); + + Person findByNameNotEquals(String name); + + @Insert + Person insertPerson(Person person); + @Update + Person updatePerson(Person person); + + @Save + Person savePerson(Person person); + + @Delete + void deletePerson(Person person); + + default Map> partcionate(String name) { + Objects.requireNonNull(name, "name is required"); + + var person = Person.builder() + .withName("Ada Lovelace") + .withAge(20) + .withId(1L).build(); + findByName(name); + findByNameNot(name); + Map> map = new HashMap<>(); + map.put(true, List.of(person)); + map.put(false, List.of(person)); + return map; + } + + void deleteByName(String name); + + List findByAge(String age); + + List findByNameAndAge(String name, Integer age); + + Set findByAgeAndName(Integer age, String name); + + Stream findByNameAndAgeOrderByName(String name, Integer age); + + Queue findByNameAndAgeOrderByAge(String name, Integer age); + + Set findByNameAndAgeGreaterThanEqual(String name, Integer age); + + Set findByAgeGreaterThan(Integer age); + + Set findByAgeLessThanEqual(Integer age); + + Set findByAgeLessThan(Integer age); + + Set findByAgeBetween(Integer ageA, Integer ageB); + + Set findByNameLike(String name); + + @Query("select * from Person") + Optional findByQuery(); + + @Query("select * from Person where id = @id") + Optional findByQuery(@Param("id") String id); + + long countByName(String name); + + boolean existsByName(String name); + + @OrderBy("name") + List findBy(); + + @OrderBy("name") + @OrderBy("age") + List findByException(); + + List find(@By("name") String name); + } + + public interface VendorRepository extends BasicRepository { + + Vendor findByPrefixes(String prefix); + + Vendor findByPrefixesIn(List prefix); + + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/query/DefaultColumnRepositoryProducerTest.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/query/DefaultColumnRepositoryProducerTest.java new file mode 100644 index 000000000..62c3574f3 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/query/DefaultColumnRepositoryProducerTest.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.query; + +import jakarta.inject.Inject; +import org.eclipse.jnosql.communication.column.ColumnManager; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.column.ColumnEntityConverter; +import org.eclipse.jnosql.mapping.column.JNoSQLColumnTemplate; +import org.eclipse.jnosql.mapping.column.MockProducer; +import org.eclipse.jnosql.mapping.column.entities.PersonRepository; +import org.eclipse.jnosql.mapping.column.spi.ColumnExtension; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, ColumnEntityConverter.class}) +@AddPackages(MockProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, ColumnExtension.class}) +class DefaultColumnRepositoryProducerTest { + + @Inject + private ColumnRepositoryProducer producer; + + + @Test + void shouldCreateFromManager() { + ColumnManager manager= Mockito.mock(ColumnManager.class); + PersonRepository personRepository = producer.get(PersonRepository.class, manager); + assertNotNull(personRepository); + } + + + @Test + void shouldCreateFromTemplate() { + JNoSQLColumnTemplate template= Mockito.mock(JNoSQLColumnTemplate.class); + PersonRepository personRepository = producer.get(PersonRepository.class, template); + assertNotNull(personRepository); + } + + +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/query/DynamicQueryTest.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/query/DynamicQueryTest.java new file mode 100644 index 000000000..8f9d5f425 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/query/DynamicQueryTest.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.query; + +import jakarta.data.Limit; +import jakarta.data.page.PageRequest; +import jakarta.data.Sort; +import org.eclipse.jnosql.communication.column.ColumnQuery; +import org.eclipse.jnosql.mapping.core.repository.SpecialParameters; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + + +class DynamicQueryTest { + + @Mock + private SpecialParameters special; + + @Mock + private ColumnQuery query; + + @Mock + private Limit limit; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void shouldCreateDynamicQuery() { + when(special.isEmpty()).thenReturn(true); + when(query.condition()).thenReturn(Optional.empty()); + when(query.name()).thenReturn("sampleQuery"); + + DynamicQuery dynamicQuery = DynamicQuery.of(new Object[]{}, query); + + assertEquals(query, dynamicQuery.get()); + } + + @Test + void shouldCreateDynamicQueryWithSortsAndLimit() { + when(special.isEmpty()).thenReturn(false); + when(special.hasOnlySort()).thenReturn(true); + when(special.sorts()).thenReturn(List.of(mock(Sort.class))); + when(limit.startAt()).thenReturn(1L); + when(special.limit()).thenReturn(Optional.of(limit)); + when(query.condition()).thenReturn(Optional.empty()); + when(query.name()).thenReturn("sampleQuery"); + when(query.sorts()).thenReturn(List.of(mock(Sort.class))); + when(query.skip()).thenReturn(0L); + when(query.limit()).thenReturn(10L); + + DynamicQuery dynamicQuery = DynamicQuery.of(new Object[]{}, query); + + assertEquals("sampleQuery", dynamicQuery.get().name()); + assertEquals(0, dynamicQuery.get().skip()); + assertEquals(10, dynamicQuery.get().limit()); + assertEquals(1, dynamicQuery.get().sorts().size()); + } + + @Test + void shouldCreateDynamicQueryWithLimit() { + when(special.isEmpty()).thenReturn(false); + when(special.hasOnlySort()).thenReturn(false); + when(limit.startAt()).thenReturn(1L); + when(special.limit()).thenReturn(Optional.of(limit)); + when(query.condition()).thenReturn(Optional.empty()); + when(query.name()).thenReturn("sampleQuery"); + when(query.sorts()).thenReturn(List.of(mock(Sort.class))); + when(query.skip()).thenReturn(0L); + when(query.limit()).thenReturn(10L); + + DynamicQuery dynamicQuery = DynamicQuery.of(new Object[]{}, query); + + assertEquals("sampleQuery", dynamicQuery.get().name()); + assertEquals(0, dynamicQuery.get().skip()); + assertEquals(10, dynamicQuery.get().limit()); + assertEquals(1, dynamicQuery.get().sorts().size()); + } + + @Test + void shouldCreateDynamicQueryWithPageRequest() { + when(special.isEmpty()).thenReturn(false); + when(special.pageRequest()).thenReturn(Optional.of(mock(PageRequest.class))); + when(special.sorts()).thenReturn(List.of(mock(Sort.class))); + when(query.condition()).thenReturn(Optional.empty()); + when(query.name()).thenReturn("sampleQuery"); + when(query.sorts()).thenReturn(List.of(mock(Sort.class))); + when(query.skip()).thenReturn(0L); + when(query.limit()).thenReturn(10L); + + DynamicQuery dynamicQuery = DynamicQuery.of(new Object[]{}, query); + + assertEquals("sampleQuery", dynamicQuery.get().name()); + assertEquals(0, dynamicQuery.get().skip()); + assertEquals(10, dynamicQuery.get().limit()); + assertEquals(1, dynamicQuery.get().sorts().size()); + } + + @Test + void shouldReturnWhenThereIsLimitAndSort(){ + when(special.isEmpty()).thenReturn(false); + when(special.pageRequest()).thenReturn(Optional.of(mock(PageRequest.class))); + when(query.condition()).thenReturn(Optional.empty()); + when(query.name()).thenReturn("sampleQuery"); + when(query.sorts()).thenReturn(Collections.emptyList()); + when(query.skip()).thenReturn(0L); + when(query.limit()).thenReturn(10L); + + DynamicQuery dynamicQuery = DynamicQuery.of(new Object[]{Sort.asc("name"), Limit.of(20)} + , query); + + ColumnQuery columnQuery = dynamicQuery.get(); + assertEquals("sampleQuery", columnQuery.name()); + assertEquals(0, columnQuery.skip()); + assertEquals(20, columnQuery.limit()); + assertEquals(1, columnQuery.sorts().size()); + } +} \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/query/ParamsBinderTest.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/query/ParamsBinderTest.java new file mode 100644 index 000000000..4536d426d --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/query/ParamsBinderTest.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.query; + +import jakarta.inject.Inject; +import org.eclipse.jnosql.communication.Params; +import org.eclipse.jnosql.communication.TypeReference; +import org.eclipse.jnosql.communication.Value; +import org.eclipse.jnosql.communication.column.Column; +import org.eclipse.jnosql.communication.column.ColumnCondition; +import org.eclipse.jnosql.communication.column.ColumnQuery; +import org.eclipse.jnosql.communication.column.ColumnQueryParams; +import org.eclipse.jnosql.communication.column.SelectQueryParser; +import org.eclipse.jnosql.communication.query.SelectQuery; +import org.eclipse.jnosql.communication.query.method.SelectMethodProvider; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.column.ColumnEntityConverter; +import org.eclipse.jnosql.mapping.column.MockProducer; +import org.eclipse.jnosql.mapping.column.entities.Person; +import org.eclipse.jnosql.mapping.column.spi.ColumnExtension; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; +import org.eclipse.jnosql.mapping.metadata.EntityMetadata; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.eclipse.jnosql.mapping.core.util.ParamsBinder; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Method; +import java.util.List; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, ColumnEntityConverter.class}) +@AddPackages(MockProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, ColumnExtension.class}) +class ParamsBinderTest { + + + @Inject + private EntitiesMetadata mappings; + + @Inject + private Converters converters; + + private ParamsBinder paramsBinder; + + @Test + void shouldConvert() { + + Method method = Stream.of(PersonRepository.class.getMethods()) + .filter(m -> m.getName().equals("findByAge")).findFirst().get(); + EntityMetadata entityMetadata = mappings.get(Person.class); + RepositoryColumnObserverParser parser = new RepositoryColumnObserverParser(entityMetadata); + paramsBinder = new ParamsBinder(entityMetadata, converters); + + SelectMethodProvider selectMethodFactory = SelectMethodProvider.INSTANCE; + SelectQuery selectQuery = selectMethodFactory.apply(method, entityMetadata.name()); + SelectQueryParser queryParser = new SelectQueryParser(); + ColumnQueryParams columnQueryParams = queryParser.apply(selectQuery, parser); + Params params = columnQueryParams.params(); + Object[] args = {10}; + paramsBinder.bind(params, args, method); + ColumnQuery query = columnQueryParams.query(); + ColumnCondition columnCondition = query.condition().get(); + Value value = columnCondition.column().value(); + assertEquals(10, value.get()); + + } + + @Test + void shouldConvert2() { + + Method method = Stream.of(PersonRepository.class.getMethods()) + .filter(m -> m.getName().equals("findByAgeAndName")).findFirst().get(); + EntityMetadata entityMetadata = mappings.get(Person.class); + RepositoryColumnObserverParser parser = new RepositoryColumnObserverParser(entityMetadata); + paramsBinder = new ParamsBinder(entityMetadata, converters); + + SelectMethodProvider selectMethodFactory = SelectMethodProvider.INSTANCE; + SelectQuery selectQuery = selectMethodFactory.apply(method, entityMetadata.name()); + SelectQueryParser queryParser = new SelectQueryParser(); + ColumnQueryParams queryParams = queryParser.apply(selectQuery, parser); + Params params = queryParams.params(); + paramsBinder.bind(params, new Object[]{10L, "Ada"}, method); + ColumnQuery query = queryParams.query(); + ColumnCondition columnCondition = query.condition().get(); + List conditions = columnCondition.column().get(new TypeReference<>() { + }); + List values = conditions.stream().map(ColumnCondition::column) + .map(Column::value) + .map(Value::get).toList(); + assertEquals(10, values.get(0)); + assertEquals("Ada", values.get(1)); + + } + + + interface PersonRepository { + + List findByAge(Integer age); + + List findByAgeAndName(Long age, String name); + } + + +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/query/RepositoryColumnObserverParserTest.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/query/RepositoryColumnObserverParserTest.java new file mode 100644 index 000000000..e86293b2a --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/query/RepositoryColumnObserverParserTest.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.query; + +import org.eclipse.jnosql.communication.column.ColumnObserverParser; +import org.eclipse.jnosql.mapping.metadata.EntityMetadata; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import static org.junit.jupiter.api.Assertions.*; + +class RepositoryColumnObserverParserTest { + + @Test + void shouldCreateFromRepository() { + EntityMetadata entityMetadata = Mockito.mock(EntityMetadata.class); + ColumnObserverParser parser = RepositoryColumnObserverParser.of(entityMetadata); + assertNotNull(parser); + } +} \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/spi/ColumnExtensionTest.java b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/spi/ColumnExtensionTest.java new file mode 100644 index 000000000..afd557be1 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/java/org/eclipse/jnosql/mapping/column/spi/ColumnExtensionTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.mapping.column.spi; + +import jakarta.inject.Inject; +import jakarta.nosql.column.ColumnTemplate; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.Database; +import org.eclipse.jnosql.mapping.DatabaseType; +import org.eclipse.jnosql.mapping.column.ColumnEntityConverter; +import org.eclipse.jnosql.mapping.column.MockProducer; +import org.eclipse.jnosql.mapping.column.entities.Person; +import org.eclipse.jnosql.mapping.column.entities.PersonRepository; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, ColumnEntityConverter.class}) +@AddPackages(MockProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, ColumnExtension.class}) +class ColumnExtensionTest { + + @Inject + @Database(value = DatabaseType.COLUMN, provider = "columnRepositoryMock") + private ColumnTemplate templateMock; + + @Inject + private ColumnTemplate template; + + @Inject + @Database(value = DatabaseType.COLUMN) + private PersonRepository repository; + + @Inject + @Database(value = DatabaseType.COLUMN, provider = "columnRepositoryMock") + private PersonRepository repositoryMock; + + @Test + void shouldInstance() { + assertNotNull(template); + assertNotNull(templateMock); + } + + @Test + void shouldSave() { + Person person = template.insert(Person.builder().build()); + Person personMock = templateMock.insert(Person.builder().build()); + + assertEquals("Default", person.getName()); + assertEquals("columnRepositoryMock", personMock.getName()); + } + + @Test + void shouldInjectRepository() { + assertNotNull(repository); + assertNotNull(repositoryMock); + } + + @Test + void shouldInjectTemplate() { + assertNotNull(templateMock); + assertNotNull(template); + } +} diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/resources/META-INF/beans.xml b/jnosql-mapping/jnosql-mapping-semistructured/src/test/resources/META-INF/beans.xml new file mode 100644 index 000000000..0340ecdbc --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/resources/META-INF/beans.xml @@ -0,0 +1,21 @@ + + + + \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/resources/META-INF/microprofile-config.properties b/jnosql-mapping/jnosql-mapping-semistructured/src/test/resources/META-INF/microprofile-config.properties new file mode 100644 index 000000000..6b4547940 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/resources/META-INF/microprofile-config.properties @@ -0,0 +1,5 @@ +column=column +column.settings.key=value +column.settings.key2=value2 +column.database=database +column.provider=org.eclipse.jnosql.mapping.column.configuration.ColumnConfigurationMock \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-semistructured/src/test/resources/META-INF/services/org.eclipse.jnosql.communication.column.ColumnConfiguration b/jnosql-mapping/jnosql-mapping-semistructured/src/test/resources/META-INF/services/org.eclipse.jnosql.communication.column.ColumnConfiguration new file mode 100644 index 000000000..334651568 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-semistructured/src/test/resources/META-INF/services/org.eclipse.jnosql.communication.column.ColumnConfiguration @@ -0,0 +1 @@ +org.eclipse.jnosql.mapping.column.configuration.ColumnConfigurationMock2 \ No newline at end of file