diff --git a/generators/cleanup.js b/generators/cleanup.js index 30f34ea8245b..c5017ce3c8dc 100644 --- a/generators/cleanup.js +++ b/generators/cleanup.js @@ -483,16 +483,16 @@ function cleanupOldServerFiles(generator, javaDir, testDir, mainResourceDir, tes if (generator.searchEngine === 'elasticsearch') { generator.removeFile(`${testDir}config/ElasticsearchTestConfiguration.java`); } - } - if (generator.isJhipsterVersionLessThan('7.0.0-beta.1')) { - generator.removeFile(`${javaDir}config/CloudDatabaseConfiguration.java`); if (generator.databaseType === 'couchbase') { generator.removeFile(`${javaDir}repository/N1qlCouchbaseRepository.java`); generator.removeFile(`${testDir}config/DatabaseConfigurationIT.java`); if (generator.searchEngine !== 'couchbase') { - generator.removeFile(`${javaDir}Frepository/CustomN1qlCouchbaseRepository.java`); + generator.removeFile(`${javaDir}Frepository/CustomN1qlCouchbaseRepository.java`); } + } } + if (generator.isJhipsterVersionLessThan('7.0.0-beta.1')) { + generator.removeFile(`${javaDir}config/CloudDatabaseConfiguration.java`); } } diff --git a/generators/entity-server/templates/partials/save_template.ejs b/generators/entity-server/templates/partials/save_template.ejs index dc43b570e2ec..b3de7bc1ed6c 100644 --- a/generators/entity-server/templates/partials/save_template.ejs +++ b/generators/entity-server/templates/partials/save_template.ejs @@ -22,7 +22,7 @@ const mapper = entityInstance + 'Mapper'; const dtoToEntity = mapper + '.' + 'toEntity'; const entityToDto = mapper + '.' + 'toDto'; const entityToDtoReference = mapper + '::' + 'toDto'; -const returnPrefix = (returnDirectly && !searchEngine) ? 'return' : instanceType + ' result ='; +const returnPrefix = (returnDirectly && searchEngine !== 'elasticsearch') ? 'return' : instanceType + ' result ='; let resultEntity; let mapsIdEntityInstance; let mapsIdRepoInstance; @@ -56,7 +56,7 @@ if (isUsingMapsId) { <%_ } _%> <%= returnPrefix %> <%= entityInstance %>Repository.save(<%= persistInstance %>); <%_ } _%> - <%_ if (searchEngine) { _%> + <%_ if (searchEngine === 'elasticsearch') { _%> <%= entityInstance %>SearchRepository.save(<%= resultEntity %>); <%_ if (returnDirectly) { _%> return result; diff --git a/generators/entity-server/templates/src/main/java/package/domain/Entity.java.ejs b/generators/entity-server/templates/src/main/java/package/domain/Entity.java.ejs index 939c158e5675..30242c900d0e 100644 --- a/generators/entity-server/templates/src/main/java/package/domain/Entity.java.ejs +++ b/generators/entity-server/templates/src/main/java/package/domain/Entity.java.ejs @@ -248,9 +248,12 @@ public class <%= persistClass %> implements Serializable { @Column(name = "<%- fieldNameAsDatabaseColumn %>"<% if (fieldValidate === true) { %><% if (field.fieldValidationMaxLength) { %>, length = <%= fieldValidateRulesMaxlength %><% } %><% if (required) { %>, nullable = false<% } %><% if (unique) { %>, unique = true<% } %><% } %>) <%_ } _%> <%_ } _%> - <%_ if (databaseTypeMongodb || databaseTypeCouchbase) { _%> + <%_ if (databaseTypeMongodb) { _%> @Field("<%= fieldNameUnderscored %>") - <%_ } _%> + <%_ } _%> + <%_ if (databaseTypeCouchbase) { _%> + @Field + <%_ } _%> <%_ if (databaseTypeNeo4j) { _%> @Property("<%=fieldNameUnderscored %>") <%_ } _%> @@ -273,10 +276,13 @@ public class <%= persistClass %> implements Serializable { @NotNull <%_ } _%> <%_ } _%> - <%_ if (databaseTypeMongodb || databaseTypeCouchbase) { _%> + <%_ if (databaseTypeMongodb) { _%> @Field("<%= fieldNameUnderscored %>_content_type") - <%_ } _%> - <%_ if (databaseTypeNeo4j) { _%> + <%_ } _%> + <%_ if (databaseTypeCouchbase) { _%> + @Field + <%_ } _%> + <%_ if (databaseTypeNeo4j) { _%> @Property("<%=fieldNameUnderscored %>_content_type") <%_ } _%> private String <%= fieldName %>ContentType; @@ -322,13 +328,15 @@ public class <%= persistClass %> implements Serializable { @OneToMany(mappedBy = "<%= otherEntityRelationshipName %>") <%_ if (enableHibernateCache) { _%> @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) - <%_ } _%> - <%_ } else if (databaseTypeMongodb || databaseTypeCouchbase) { - if (databaseTypeMongodb && !otherEntityIsEmbedded) { _%> + <%_ } + } else if (databaseTypeMongodb) { + if (!otherEntityIsEmbedded) { _%> @DBRef - <%_ } _%> + <%_ } _%> @Field("<%= relationshipFieldName %>") - <%_ if (databaseTypeCouchbase && !otherEntityIsEmbedded) { _%> + <%_ } else if (databaseTypeCouchbase) { _%> + @Field + <%_ if (!otherEntityIsEmbedded) { _%> private Set <%= relationshipFieldName %>Ids = new HashSet<>(); @N1qlJoin(on = "lks.<%= relationshipFieldName %>=meta(rks).id", fetchType = FetchType.IMMEDIATE) @@ -356,9 +364,10 @@ public class <%= persistClass %> implements Serializable { <%_ } else if ((databaseTypeMongodb || databaseTypeCouchbase) && !otherEntityIsEmbedded) { if (databaseTypeMongodb) { _%> @DBRef - <%_ } _%> @Field("<%= relationshipFieldName %>") - <%_ if (databaseTypeCouchbase) { _%> + <%_ } _%> + <%_ if (databaseTypeCouchbase) { _%> + @Field private String <%= relationshipFieldName %>Id; @N1qlJoin(on = "lks.<%= relationshipFieldName %>=meta(rks).id", fetchType = FetchType.IMMEDIATE) @@ -411,9 +420,10 @@ public class <%= persistClass %> implements Serializable { <%_ } else if ((databaseTypeMongodb || databaseTypeCouchbase) && !otherEntityIsEmbedded) { _%> <%_ if (databaseTypeMongodb) { _%> @DBRef - <%_ } _%> @Field("<%= relationshipFieldNamePlural %>") - <%_ if (databaseTypeCouchbase) { _%> + <%_ } _%> + <%_ if (databaseTypeCouchbase) { _%> + @Field private Set <%= relationshipFieldName %>Ids = new HashSet<>(); @N1qlJoin(on = "lks.<%= relationshipFieldNamePlural %>=meta(rks).id", fetchType = FetchType.IMMEDIATE) @@ -461,9 +471,9 @@ public class <%= persistClass %> implements Serializable { <%_ } else if ((databaseTypeMongodb || databaseTypeCouchbase) && !otherEntityIsEmbedded) { _%> <%_ if (databaseTypeMongodb) { _%> @DBRef - <%_ } _%> @Field("<%= relationshipFieldName %>") - <%_ if (databaseTypeCouchbase) { _%> + <%_ } if (databaseTypeCouchbase) { _%> + @Field private String <%= relationshipFieldName %>Id; @N1qlJoin(on = "lks.<%= relationshipFieldName %>=meta(rks).id", fetchType = FetchType.IMMEDIATE) diff --git a/generators/entity-server/templates/src/main/java/package/repository/EntityRepository.java.ejs b/generators/entity-server/templates/src/main/java/package/repository/EntityRepository.java.ejs index 04d9be974930..ba192d21c07c 100644 --- a/generators/entity-server/templates/src/main/java/package/repository/EntityRepository.java.ejs +++ b/generators/entity-server/templates/src/main/java/package/repository/EntityRepository.java.ejs @@ -29,19 +29,24 @@ import org.springframework.data.jpa.repository.*; <%_ if (relationshipsContainEagerLoad) { _%> import org.springframework.data.repository.query.Param; <%_ } _%> -<%_ } _%> -<%_ if (databaseTypeMongodb) { _%> +<%_} else if (databaseTypeMongodb) { _%> import org.springframework.data.mongodb.repository.Query; import org.springframework.data.mongodb.repository.MongoRepository; -<%_ } _%> -<%_ if (databaseTypeNeo4j) { _%> +<%_} else if (databaseTypeNeo4j) { _%> import org.springframework.data.neo4j.repository.Neo4jRepository; +<%_} else if (databaseTypeCouchbase) { _%> +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import com.couchbase.client.java.query.QueryScanConsistency; +import org.springframework.data.couchbase.repository.CouchbaseRepository; +import org.springframework.data.couchbase.repository.ScanConsistency; <%_ } _%> -<%_ if (databaseTypeCouchbase) { _%> +<%_ if (searchEngineCouchbase) { _%> import <%= packageName %>.repository.search.SearchCouchbaseRepository; - <%_ if (relationshipsContainEagerLoad) { _%> +<%_ } _%> +<%_ if (relationshipsContainEagerLoad && databaseTypeCouchbase) { _%> import org.springframework.data.couchbase.repository.Query; - <%_ } _%> <%_ } _%> <%_ if (databaseTypeCassandra) { _%> import org.springframework.data.cassandra.repository.CassandraRepository; @@ -56,7 +61,7 @@ if (databaseTypeSql || databaseTypeMongodb || databaseTypeCouchbase) { } } _%> - <%_ if (importList) { _%> + <%_ if (importList || databaseTypeCouchbase) { _%> import java.util.List; <%_ } _%> <%_ if (relationshipsContainEagerLoad) { _%> @@ -75,7 +80,7 @@ import java.util.UUID; @SuppressWarnings("unused") <%_ } _%> @Repository -public interface <%= entityClass %>Repository extends <% if (databaseTypeSql) { %>JpaRepository<% } %><% if (databaseTypeMongodb) { %>MongoRepository<% } %><% if (databaseTypeNeo4j) { %>Neo4jRepository<% } %><% if (databaseTypeCassandra) { %>CassandraRepository<% } %><% if (databaseTypeCouchbase) { %>N1qlCouchbaseRepository<% } %><<%= persistClass %>, <%= primaryKey.type %>><% if (jpaMetamodelFiltering) { %>, JpaSpecificationExecutor<<%= persistClass %>><% } %><% if (searchEngineCouchbase) { %>, SearchCouchbaseRepository<<%= persistClass %>, <%= primaryKey.type %>><% } %> { +public interface <%= entityClass %>Repository extends <% if (databaseTypeSql) { %>JpaRepository<% } %><% if (databaseTypeMongodb) { %>MongoRepository<% } %><% if (databaseTypeNeo4j) { %>Neo4jRepository<% } %><% if (databaseTypeCassandra) { %>CassandraRepository<% } %><% if (databaseTypeCouchbase) { %>CouchbaseRepository<% } %><<%= persistClass %>, <%= primaryKey.type %>><% if (jpaMetamodelFiltering) { %>, JpaSpecificationExecutor<<%= persistClass %>><% } %><% if (searchEngineCouchbase) { %>, SearchCouchbaseRepository<<%= persistClass %>, <%= primaryKey.type %>><% } %> { <%_ for (const relationship of relationships) { if (relationship.relationshipManyToOne && relationship.otherEntityUser && databaseTypeSql) { _%> @@ -118,6 +123,21 @@ public interface <%= entityClass %>Repository extends <% if (databaseTypeSql) { @Query("<%- (databaseTypeMongodb) ? "{'id': ?0}" : "#{#n1ql.selectEntity} USE KEYS $1 WHERE #{#n1ql.filter}" %>") Optional<<%= persistClass %>> findOneWithEagerRelationships(<%= primaryKey.type %> id); <%_ - } - } _%> + } + } _%> + <%_ if (databaseTypeCouchbase) { _%> + @Override + // Add ScanConsistency to fix issue with Spring Data Couchbase + // https://github.com/spring-projects/spring-data-couchbase/issues/897 + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + List<<%= persistClass %>> findAll(); + + @Override + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + List<<%= persistClass %>> findAll(Sort sort); + + @Override + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + Page<<%= persistClass %>> findAll(Pageable pageable); + <%_ } _%> } diff --git a/generators/entity-server/templates/src/main/java/package/repository/EntityRepository_reactive.java.ejs b/generators/entity-server/templates/src/main/java/package/repository/EntityRepository_reactive.java.ejs index 48676395ac40..17ef13c7bd69 100644 --- a/generators/entity-server/templates/src/main/java/package/repository/EntityRepository_reactive.java.ejs +++ b/generators/entity-server/templates/src/main/java/package/repository/EntityRepository_reactive.java.ejs @@ -26,8 +26,11 @@ import org.springframework.data.cassandra.repository.ReactiveCassandraRepository import <%= packageName %>.repository.search.SearchCouchbaseRepository; <%_ } _%> <%_ if (databaseTypeCouchbase) { _%> +import com.couchbase.client.java.query.QueryScanConsistency; +import org.springframework.data.domain.Sort; import org.springframework.data.couchbase.repository.Query; -import org.springframework.data.couchbase.repository.ReactiveCouchbaseSortingRepository; +import org.springframework.data.repository.reactive.ReactiveSortingRepository; +import org.springframework.data.couchbase.repository.ScanConsistency; <%_ } _%> <%_ if (databaseTypeNeo4j) { _%> import org.springframework.data.neo4j.repository.ReactiveNeo4jRepository; @@ -74,12 +77,15 @@ public interface <%= entityClass %>Repository extends <% if (databaseTypeSql) { <%_ if (databaseTypeCouchbase || databaseTypeMongodb) { _%> @Query("<%= (databaseTypeMongodb) ? '{}' : '#{#n1ql.selectEntity} WHERE #{#n1ql.filter}' %>") + <% if (databaseTypeCouchbase) { %>@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)<% } %> Flux<<%= persistClass %>> findAllWithEagerRelationships(Pageable pageable); @Query("<%= (databaseTypeMongodb) ? '{}' : '#{#n1ql.selectEntity} WHERE #{#n1ql.filter}' %>") + <% if (databaseTypeCouchbase) { %>@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)<% } %> Flux<<%= persistClass %>> findAllWithEagerRelationships(); @Query("<%- (databaseTypeMongodb) ? "{'id': ?0}" : "#{#n1ql.selectEntity} USE KEYS $1 WHERE #{#n1ql.filter}" %>") + <% if (databaseTypeCouchbase) { %>@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)<% } %> Mono<<%= persistClass %>> findOneWithEagerRelationships(<%= primaryKey.type %> id); <%_ } _%> <%_ if (databaseTypeNeo4j) { _%> @@ -93,8 +99,18 @@ public interface <%= entityClass %>Repository extends <% if (databaseTypeSql) { Mono<<%= persistClass %>> findOneWithEagerRelationships(<%= primaryKey.type %> id); <%_ } _%> <%_ } _%> -<%_ -if (databaseTypeSql) { + <%_ if (databaseTypeCouchbase) { _%> + // Add ScanConsistency to fix issue with Spring Data Couchbase + // https://github.com/spring-projects/spring-data-couchbase/issues/897 + @Override + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + Flux<<%= asEntity(entityClass) %>> findAll(); + + @Override + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + Flux<<%= asEntity(entityClass) %>> findAll(Sort sort); + <%_ } _%> + <%_ if (databaseTypeSql) { if (fieldsContainOwnerManyToMany) { _%> @Override diff --git a/generators/entity-server/templates/src/main/resources/config/couchmove/changelog/entity.fts.ejs b/generators/entity-server/templates/src/main/resources/config/couchmove/changelog/entity.fts.ejs index c2ca13c95c18..0554017c577f 100644 --- a/generators/entity-server/templates/src/main/resources/config/couchmove/changelog/entity.fts.ejs +++ b/generators/entity-server/templates/src/main/resources/config/couchmove/changelog/entity.fts.ejs @@ -33,5 +33,6 @@ } }, "sourceType": "couchbase", - "sourceName": "${bucket}" + "sourceName": "${bucket}", + "name": "<%= entityClass %>" } diff --git a/generators/entity-server/templates/src/test/java/package/web/rest/EntityResourceIT.java.ejs b/generators/entity-server/templates/src/test/java/package/web/rest/EntityResourceIT.java.ejs index d4e98d5a5955..deb077d69c71 100644 --- a/generators/entity-server/templates/src/test/java/package/web/rest/EntityResourceIT.java.ejs +++ b/generators/entity-server/templates/src/test/java/package/web/rest/EntityResourceIT.java.ejs @@ -1739,7 +1739,8 @@ _%> <%_ } else if (searchEngineCouchbase) { _%> // Wait for the <%= entityInstance %> to be indexed - TestUtil.retryUntilNotEmpty(() -> <%= entityInstance %>Repository.search(<%= entityClass %>.PREFIX, "id:" + <%= entityInstance %>.get<%= primaryKey.nameCapitalized %>())<% if (reactive) { %>.collectList().block()<% } %>); + // TestUtil.retryUntilNotEmpty(() -> <%= entityInstance %>Repository.search(<%= entityClass %>.PREFIX, "id:" + <%= entityInstance %>.get<%= primaryKey.nameCapitalized %>())<% if (reactive) { %>.collectList().block()<% } %>); + <%= entityInstance %>Repository.search(<%= entityClass %>.PREFIX, "id:" + <%= entityInstance %>.get<%= primaryKey.nameCapitalized %>())<% if (reactive) { %>.collectList().block()<% } %>; <%_ } _%> // Search the <%= entityInstance %> diff --git a/generators/server/files.js b/generators/server/files.js index 2a709549fca0..a124178b54bb 100644 --- a/generators/server/files.js +++ b/generators/server/files.js @@ -901,50 +901,72 @@ const serverFiles = { ], }, { - condition: generator => !generator.reactive && generator.databaseType === COUCHBASE, + condition: generator => generator.databaseType === COUCHBASE, path: SERVER_MAIN_SRC_DIR, templates: [ { - file: 'package/repository/N1qlCouchbaseRepository.java', - renameTo: generator => `${generator.javaDir}repository/N1qlCouchbaseRepository.java`, + file: 'package/config/couchbase/CustomCouchbaseRepositoryFactory.java', + renameTo: generator => `${generator.javaDir}config/couchbase/CustomCouchbaseRepositoryFactory.java`, + }, + { + file: 'package/config/couchbase/CustomCouchbaseRepositoryFactoryBean.java', + renameTo: generator => `${generator.javaDir}config/couchbase/CustomCouchbaseRepositoryFactoryBean.java`, + }, + { + file: 'package/config/couchbase/CustomCouchbaseRepositoryQuery.java', + renameTo: generator => `${generator.javaDir}config/couchbase/CustomCouchbaseRepositoryQuery.java`, + }, + { + file: 'package/config/couchbase/CustomN1qlQueryCreator.java', + renameTo: generator => `${generator.javaDir}config/couchbase/CustomN1qlQueryCreator.java`, + }, + { + file: 'package/config/couchbase/CustomN1qlRepositoryQueryExecutor.java', + renameTo: generator => `${generator.javaDir}config/couchbase/CustomN1qlRepositoryQueryExecutor.java`, }, { - file: 'package/repository/CustomN1qlCouchbaseRepository.java', - renameTo: generator => `${generator.javaDir}repository/CustomN1qlCouchbaseRepository.java`, + file: 'package/config/couchbase/package-info.java', + renameTo: generator => `${generator.javaDir}config/couchbase/package-info.java`, }, ], }, { - condition: generator => generator.searchEngine === COUCHBASE, + condition: generator => !generator.reactive && generator.databaseType === COUCHBASE, path: SERVER_MAIN_SRC_DIR, templates: [ { - file: 'package/repository/search/SearchCouchbaseRepository.java', - renameTo: generator => `${generator.javaDir}repository/search/SearchCouchbaseRepository.java`, + file: 'package/repository/CustomCouchbaseRepository.java', + renameTo: generator => `${generator.javaDir}repository/CustomCouchbaseRepository.java`, }, ], }, { - condition: generator => generator.searchEngine === COUCHBASE, - path: SERVER_TEST_SRC_DIR, + condition: generator => generator.reactive && generator.databaseType === COUCHBASE, + path: SERVER_MAIN_SRC_DIR, templates: [ { - file: 'package/repository/CustomN1qlCouchbaseRepositoryTest.java', - renameTo: generator => `${generator.testDir}repository/CustomN1qlCouchbaseRepositoryTest.java`, + file: 'package/repository/CustomReactiveCouchbaseRepository.java', + renameTo: generator => `${generator.javaDir}repository/CustomReactiveCouchbaseRepository.java`, }, ], }, { - condition: generator => generator.reactive && generator.databaseType === COUCHBASE, + condition: generator => generator.searchEngine === COUCHBASE, path: SERVER_MAIN_SRC_DIR, templates: [ { - file: 'package/repository/ReactiveN1qlCouchbaseRepository.java', - renameTo: generator => `${generator.javaDir}repository/ReactiveN1qlCouchbaseRepository.java`, + file: 'package/repository/search/SearchCouchbaseRepository.java', + renameTo: generator => `${generator.javaDir}repository/search/SearchCouchbaseRepository.java`, }, + ], + }, + { + condition: generator => generator.searchEngine === COUCHBASE, + path: SERVER_TEST_SRC_DIR, + templates: [ { - file: 'package/repository/CustomReactiveN1qlCouchbaseRepository.java', - renameTo: generator => `${generator.javaDir}repository/CustomReactiveN1qlCouchbaseRepository.java`, + file: 'package/repository/CustomCouchbaseRepositoryTest.java', + renameTo: generator => `${generator.testDir}repository/CustomCouchbaseRepositoryTest.java`, }, ], }, @@ -1220,8 +1242,8 @@ const serverFiles = { path: SERVER_TEST_SRC_DIR, templates: [ { - file: 'package/config/DatabaseConfigurationIT.java', - renameTo: generator => `${generator.testDir}config/DatabaseConfigurationIT.java`, + file: 'package/CouchbaseTestContainerExtension.java', + renameTo: generator => `${generator.testDir}CouchbaseTestContainerExtension.java`, }, ], }, diff --git a/generators/server/templates/pom.xml.ejs b/generators/server/templates/pom.xml.ejs index 9014a3a9282a..e21b02e9f6ea 100644 --- a/generators/server/templates/pom.xml.ejs +++ b/generators/server/templates/pom.xml.ejs @@ -374,12 +374,6 @@ com.couchbase.client java-client - 3.1.4 - - - com.couchbase.client - encryption - 2.0.1 <%_ } _%> <%_ if (databaseTypeNeo4j) { _%> diff --git a/generators/server/templates/src/main/java/package/config/DatabaseConfiguration_couchbase.java.ejs b/generators/server/templates/src/main/java/package/config/DatabaseConfiguration_couchbase.java.ejs index cdebe5464b51..9606885dd73e 100644 --- a/generators/server/templates/src/main/java/package/config/DatabaseConfiguration_couchbase.java.ejs +++ b/generators/server/templates/src/main/java/package/config/DatabaseConfiguration_couchbase.java.ejs @@ -20,21 +20,27 @@ package <%= packageName %>.config; import tech.jhipster.config.JHipsterConstants; +import tech.jhipster.config.JHipsterProperties; import com.couchbase.client.java.Bucket; +import com.couchbase.client.java.Cluster; import com.github.couchmove.Couchmove; -import <%= packageName %>.repository.Custom<% if (reactive) { %>Reactive<% } %>N1qlCouchbaseRepository; -<%_ if (searchEngineElasticsearch) { _%> -import <%= packageName %>.repository.<% if (reactive) { %>Reactive<% } %>N1qlCouchbaseRepository; +<%_ if (searchEngineCouchbase) { _%> +import <%= packageName %>.repository.Custom<% if (reactive) { %>Reactive<% } %>CouchbaseRepository; +<%_ } else if (searchEngine === 'elasticsearch') { _%> +import org.springframework.data.couchbase.repository.<% if (reactive) { %>Reactive<% } %>CouchbaseRepository; <%_ } _%> + +import <%= packageName %>.config.couchbase.CustomCouchbaseRepositoryFactoryBean; + import org.apache.commons.codec.binary.Base64; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration; import org.springframework.boot.autoconfigure.couchbase.CouchbaseProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; <%_ if (searchEngineElasticsearch) { _%> import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.FilterType; @@ -46,6 +52,7 @@ import org.springframework.data.elasticsearch.repository.config.Enable<% if (rea <%_ } _%> import org.springframework.data.convert.ReadingConverter; import org.springframework.data.convert.WritingConverter; +import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration; import org.springframework.data.couchbase.config.BeanNames; import org.springframework.data.couchbase.core.convert.CouchbaseCustomConversions; import org.springframework.data.couchbase.core.mapping.event.ValidatingCouchbaseEventListener; @@ -64,22 +71,52 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.UUID; +<%_ if (reactive) { _%> +import java.util.Set; +import java.lang.reflect.Field; +import org.springframework.data.repository.util.QueryExecutionConverters; +import reactor.core.publisher.Flux; +<%_ } _%> @Configuration +@EnableConfigurationProperties(CouchbaseProperties.class) <%_ if (searchEngineElasticsearch) { _%> @Enable<% if (reactive) { %>Reactive<% } %>ElasticsearchRepositories("<%= packageName %>.repository.search") <%_ } _%> @Profile("!" + JHipsterConstants.SPRING_PROFILE_CLOUD) -@Enable<% if (reactive) { %>Reactive<% } %>CouchbaseRepositories(repositoryBaseClass = Custom<% if (reactive) { %>Reactive<% } %>N1qlCouchbaseRepository.class, basePackages = "<%= packageName %>.repository"<%_ if (searchEngine === 'elasticsearch') { %>, - includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, value = <% if (reactive) { %>Reactive<% } %>N1qlCouchbaseRepository.class)<%_ } _%>) +@Enable<% if (reactive) { %>Reactive<% } %>CouchbaseRepositories(<% if (searchEngineCouchbase) { %>repositoryBaseClass = Custom<% if (reactive) { %>Reactive<% } %>CouchbaseRepository.class, <% } %>basePackages = "<%= packageName %>.repository", repositoryFactoryBeanClass = CustomCouchbaseRepositoryFactoryBean.class<%_ if (searchEngineElasticsearch) { %>, + includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, value = <% if (reactive) { %>Reactive<% } %>CouchbaseRepository.class)<%_ } _%>) <%_ if (!reactive) { _%> -@EnableCouchbaseAuditing(auditorAwareRef = "springSecurityAuditorAware") +@EnableCouchbaseAuditing(auditorAwareRef = "springSecurityAuditorAware", dateTimeProviderRef = "") <%_ } _%> -@Import(value = CouchbaseAutoConfiguration.class) -public class DatabaseConfiguration { +public class DatabaseConfiguration extends AbstractCouchbaseConfiguration { private final Logger log = LoggerFactory.getLogger(DatabaseConfiguration.class); + private final JHipsterProperties jHipsterProperties; + private final CouchbaseProperties couchbaseProperties; + + public DatabaseConfiguration(JHipsterProperties jHipsterProperties, CouchbaseProperties couchbaseProperties) { + this.jHipsterProperties = jHipsterProperties; + this.couchbaseProperties = couchbaseProperties; + <% if (reactive) { %>allowPageable();<% } %> + } + +<%_ if (reactive) { _%> + // Temporary Hack to fix pageable + @SuppressWarnings("unchecked") + private void allowPageable() { + try { + Field allowed_pageable_types = QueryExecutionConverters.class.getDeclaredField("ALLOWED_PAGEABLE_TYPES"); + allowed_pageable_types.setAccessible(true); + Set> ALLOWED_PAGEABLE_TYPES = (Set>) allowed_pageable_types.get(QueryExecutionConverters.class); + ALLOWED_PAGEABLE_TYPES.add(Flux.class); + } catch (NoSuchFieldException | IllegalAccessException e) { + e.printStackTrace(); + } + } + +<%_ } _%> @Bean public ValidatingCouchbaseEventListener validatingCouchbaseEventListener() { return new ValidatingCouchbaseEventListener(validator()); @@ -90,6 +127,31 @@ public class DatabaseConfiguration { return new LocalValidatorFactoryBean(); } + @Override + public String getConnectionString() { + return couchbaseProperties.getConnectionString(); + } + + @Override + public String getUserName() { + return couchbaseProperties.getUsername(); + } + + @Override + public String getPassword() { + return couchbaseProperties.getPassword(); + } + + @Override + public String getBucketName() { + return jHipsterProperties.getDatabase().getCouchbase().getBucketName(); + } + + @Bean + public Bucket bucket(Cluster cluster) { + return cluster.bucket(jHipsterProperties.getDatabase().getCouchbase().getBucketName()); + } + @Bean(name = BeanNames.COUCHBASE_CUSTOM_CONVERSIONS) public CouchbaseCustomConversions customConversions() { List> converters = new ArrayList<>(); @@ -106,9 +168,10 @@ public class DatabaseConfiguration { } @Bean - public Couchmove couchmove(Bucket couchbaseBucket, CouchbaseProperties properties) { + public Couchmove couchmove(Cluster cluster) { log.debug("Configuring Couchmove"); - Couchmove couchmove = new Couchmove(couchbaseBucket, properties.getUsername(), properties.getPassword(), "config/couchmove/changelog"); + Bucket bucket = cluster.bucket(getBucketName()); + Couchmove couchmove = new Couchmove(bucket, cluster, "config/couchmove/changelog"); couchmove.migrate(); return couchmove; } @@ -201,9 +264,9 @@ public class DatabaseConfiguration { } } - /** - * Simple singleton to convert {@link UUID}s to their {@link String} representation. - */ + /** + * Simple singleton to convert {@link UUID}s to their {@link String} representation. + */ @WritingConverter public enum UUIDToStringConverter implements Converter { INSTANCE; @@ -215,8 +278,8 @@ public class DatabaseConfiguration { } /** - * Simple singleton to convert from {@link String} {@link UUID} representation. - */ + * Simple singleton to convert from {@link String} {@link UUID} representation. + */ @ReadingConverter public enum StringToUUIDConverter implements Converter { INSTANCE; diff --git a/generators/server/templates/src/main/java/package/config/couchbase/CustomCouchbaseRepositoryFactory.java.ejs b/generators/server/templates/src/main/java/package/config/couchbase/CustomCouchbaseRepositoryFactory.java.ejs new file mode 100644 index 000000000000..431f9a30b322 --- /dev/null +++ b/generators/server/templates/src/main/java/package/config/couchbase/CustomCouchbaseRepositoryFactory.java.ejs @@ -0,0 +1,83 @@ +<%# + Copyright 2013-2021 the original author or authors from the JHipster project. + + This file is part of the JHipster project, see https://www.jhipster.tech/ + for more information. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-%> +package <%= packageName %>.config.couchbase; + +import java.lang.reflect.Method; +import java.util.Optional; +import org.springframework.data.couchbase.core.CouchbaseOperations; +import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping; +import org.springframework.data.couchbase.repository.query.CouchbaseQueryMethod; +import org.springframework.data.couchbase.repository.support.CouchbaseRepositoryFactory; +import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.projection.ProjectionFactory; +import org.springframework.data.repository.core.NamedQueries; +import org.springframework.data.repository.core.RepositoryMetadata; +import org.springframework.data.repository.query.QueryLookupStrategy; +import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; +import org.springframework.data.repository.query.RepositoryQuery; + +public class CustomCouchbaseRepositoryFactory extends CouchbaseRepositoryFactory { + + private MappingContext mappingContext; + private RepositoryOperationsMapping couchbaseOperationsMapping; + + /** + * Create a new factory. + * + * @param couchbaseOperationsMapping the template for the underlying actions. + */ + public CustomCouchbaseRepositoryFactory(RepositoryOperationsMapping couchbaseOperationsMapping) { + super(couchbaseOperationsMapping); + this.couchbaseOperationsMapping = couchbaseOperationsMapping; + this.mappingContext = this.couchbaseOperationsMapping.getMappingContext(); + } + + @Override + protected Optional getQueryLookupStrategy( + QueryLookupStrategy.Key key, + QueryMethodEvaluationContextProvider contextProvider + ) { + return Optional.of(new CustomCouchbaseQueryLookupStrategy(contextProvider)); + } + + private class CustomCouchbaseQueryLookupStrategy implements QueryLookupStrategy { + + private final QueryMethodEvaluationContextProvider evaluationContextProvider; + + public CustomCouchbaseQueryLookupStrategy(QueryMethodEvaluationContextProvider evaluationContextProvider) { + this.evaluationContextProvider = evaluationContextProvider; + } + + @Override + public RepositoryQuery resolveQuery( + final Method method, + final RepositoryMetadata metadata, + final ProjectionFactory factory, + final NamedQueries namedQueries + ) { + final CouchbaseOperations couchbaseOperations = couchbaseOperationsMapping.resolve( + metadata.getRepositoryInterface(), + metadata.getDomainType() + ); + + CouchbaseQueryMethod queryMethod = new CouchbaseQueryMethod(method, metadata, factory, mappingContext); + return new CustomCouchbaseRepositoryQuery(couchbaseOperations, queryMethod, namedQueries); + } + } +} diff --git a/generators/server/templates/src/main/java/package/config/couchbase/CustomCouchbaseRepositoryFactoryBean.java.ejs b/generators/server/templates/src/main/java/package/config/couchbase/CustomCouchbaseRepositoryFactoryBean.java.ejs new file mode 100644 index 000000000000..e146f5eea8bf --- /dev/null +++ b/generators/server/templates/src/main/java/package/config/couchbase/CustomCouchbaseRepositoryFactoryBean.java.ejs @@ -0,0 +1,40 @@ +<%# + Copyright 2013-2021 the original author or authors from the JHipster project. + + This file is part of the JHipster project, see https://www.jhipster.tech/ + for more information. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-%> +package <%= packageName %>.config.couchbase; + +import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping; +import org.springframework.data.couchbase.repository.support.CouchbaseRepositoryFactory; +import org.springframework.data.couchbase.repository.support.CouchbaseRepositoryFactoryBean; + +public class CustomCouchbaseRepositoryFactoryBean extends CouchbaseRepositoryFactoryBean { + + /** + * Creates a new {@link CouchbaseRepositoryFactoryBean} for the given repository interface. + * + * @param repositoryInterface must not be {@literal null}. + */ + public CustomCouchbaseRepositoryFactoryBean(Class repositoryInterface) { + super(repositoryInterface); + } + + @Override + protected CouchbaseRepositoryFactory getFactoryInstance(RepositoryOperationsMapping operationsMapping) { + return new CustomCouchbaseRepositoryFactory(operationsMapping); + } +} diff --git a/generators/server/templates/src/main/java/package/config/couchbase/CustomCouchbaseRepositoryQuery.java.ejs b/generators/server/templates/src/main/java/package/config/couchbase/CustomCouchbaseRepositoryQuery.java.ejs new file mode 100644 index 000000000000..99f5ae1d77c8 --- /dev/null +++ b/generators/server/templates/src/main/java/package/config/couchbase/CustomCouchbaseRepositoryQuery.java.ejs @@ -0,0 +1,44 @@ +<%# + Copyright 2013-2021 the original author or authors from the JHipster project. + + This file is part of the JHipster project, see https://www.jhipster.tech/ + for more information. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-%> +package <%= packageName %>.config.couchbase; + +import org.springframework.data.couchbase.core.CouchbaseOperations; +import org.springframework.data.couchbase.repository.query.CouchbaseQueryMethod; +import org.springframework.data.couchbase.repository.query.CouchbaseRepositoryQuery; +import org.springframework.data.repository.core.NamedQueries; +import <%= packageName %>.config.couchbase.CustomN1qlRepositoryQueryExecutor; + +public class CustomCouchbaseRepositoryQuery extends CouchbaseRepositoryQuery { + + private CouchbaseOperations couchbaseOperations; + private CouchbaseQueryMethod queryMethod; + private NamedQueries namedQueries; + + public CustomCouchbaseRepositoryQuery(CouchbaseOperations operations, CouchbaseQueryMethod queryMethod, NamedQueries namedQueries) { + super(operations, queryMethod, namedQueries); + this.couchbaseOperations = operations; + this.queryMethod = queryMethod; + this.namedQueries = namedQueries; + } + + @Override + public Object execute(final Object[] parameters) { + return new CustomN1qlRepositoryQueryExecutor(couchbaseOperations, queryMethod, namedQueries).execute(parameters); + } +} diff --git a/generators/server/templates/src/main/java/package/config/couchbase/CustomN1qlQueryCreator.java.ejs b/generators/server/templates/src/main/java/package/config/couchbase/CustomN1qlQueryCreator.java.ejs new file mode 100644 index 000000000000..8b4583bbf9d7 --- /dev/null +++ b/generators/server/templates/src/main/java/package/config/couchbase/CustomN1qlQueryCreator.java.ejs @@ -0,0 +1,162 @@ +<%# + Copyright 2013-2021 the original author or authors from the JHipster project. + + This file is part of the JHipster project, see https://www.jhipster.tech/ + for more information. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-%> +package <%= packageName %>.config.couchbase; + +import static org.springframework.data.couchbase.core.query.N1QLExpression.*; + +import java.util.Iterator; +import org.springframework.core.convert.converter.Converter; +import org.springframework.data.couchbase.core.CouchbaseOperations; +import org.springframework.data.couchbase.core.convert.CouchbaseConverter; +import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty; +import org.springframework.data.couchbase.core.query.Query; +import org.springframework.data.couchbase.core.query.QueryCriteria; +import org.springframework.data.domain.Sort; +import org.springframework.data.mapping.PersistentPropertyPath; +import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.repository.query.ParameterAccessor; +import org.springframework.data.repository.query.QueryMethod; +import org.springframework.data.repository.query.parser.AbstractQueryCreator; +import org.springframework.data.repository.query.parser.Part; +import org.springframework.data.repository.query.parser.PartTree; + +public class CustomN1qlQueryCreator extends AbstractQueryCreator { + + private final CouchbaseOperations couchbaseOperations; + private final ParameterAccessor accessor; + private final MappingContext context; + private final QueryMethod queryMethod; + private final CouchbaseConverter converter; + + public CustomN1qlQueryCreator( + final CouchbaseOperations operations, + final PartTree tree, + final ParameterAccessor accessor, + QueryMethod queryMethod, + CouchbaseConverter converter + ) { + super(tree, accessor); + this.accessor = accessor; + this.context = converter.getMappingContext(); + this.queryMethod = queryMethod; + this.converter = converter; + this.couchbaseOperations = operations; + } + + static Converter cvtr = source -> + new StringBuilder(source.getFieldName().length() + 2).append('`').append(source.getFieldName()).append('`').toString(); + + @Override + protected QueryCriteria create(final Part part, final Iterator iterator) { + PersistentPropertyPath path = context.getPersistentPropertyPath(part.getProperty()); + CouchbasePersistentProperty property = path.getLeafProperty(); + return from(part, property, QueryCriteria.where(addMetaIfIdProperty(path, property)), iterator); + } + + @Override + protected QueryCriteria and(Part part, QueryCriteria base, Iterator iterator) { + if (base == null) { + return create(part, iterator); + } + PersistentPropertyPath path = context.getPersistentPropertyPath(part.getProperty()); + CouchbasePersistentProperty property = path.getLeafProperty(); + return from(part, property, base.and(addMetaIfIdProperty(path, property)), iterator); + } + + private String addMetaIfIdProperty( + final PersistentPropertyPath path, + final CouchbasePersistentProperty property + ) { + return path.toDotPath(cvtr); + } + + @Override + protected QueryCriteria or(QueryCriteria base, QueryCriteria criteria) { + return base.or(criteria); + } + + @Override + protected Query complete(QueryCriteria criteria, Sort sort) { + return (criteria == null ? new Query() : new Query().addCriteria(criteria)).with(sort); + } + + private QueryCriteria from( + final Part part, + final CouchbasePersistentProperty property, + final QueryCriteria criteria, + final Iterator parameters + ) { + final Part.Type type = part.getType(); + + switch (type) { + case GREATER_THAN: + case AFTER: + return criteria.gt(parameters.next()); + case GREATER_THAN_EQUAL: + return criteria.gte(parameters.next()); + case LESS_THAN: + case BEFORE: + return criteria.lt(parameters.next()); + case LESS_THAN_EQUAL: + return criteria.lte(parameters.next()); + case SIMPLE_PROPERTY: + return criteria.eq(parameters.next()); + case NEGATING_SIMPLE_PROPERTY: + return criteria.ne(parameters.next()); + case CONTAINING: + return criteria.containing(parameters.next()); + case NOT_CONTAINING: + return criteria.notContaining(parameters.next()); + case STARTING_WITH: + return criteria.startingWith(parameters.next()); + case ENDING_WITH: + return criteria.endingWith(parameters.next()); + case LIKE: + return criteria.like(parameters.next()); + case NOT_LIKE: + return criteria.notLike(parameters.next()); + case WITHIN: + return criteria.within(parameters.next()); + case IS_NULL: + return criteria.isNull(); + case IS_NOT_NULL: + return criteria.isNotNull(); + case IS_EMPTY: + return criteria.isNotValued(); + case IS_NOT_EMPTY: + return criteria.isValued(); + case EXISTS: + return criteria.isNotMissing(); + case REGEX: + return criteria.regex(parameters.next()); + case BETWEEN: + return criteria.between(parameters.next(), parameters.next()); + case IN: + return criteria.in(parameters.next()); + case NOT_IN: + return criteria.notIn(parameters.next()); + case TRUE: + return criteria.TRUE(); + case FALSE: + return criteria.FALSE(); + default: + throw new IllegalArgumentException("Unsupported keyword!"); + } + } +} diff --git a/generators/server/templates/src/main/java/package/config/couchbase/CustomN1qlRepositoryQueryExecutor.java.ejs b/generators/server/templates/src/main/java/package/config/couchbase/CustomN1qlRepositoryQueryExecutor.java.ejs new file mode 100644 index 000000000000..0981c0ce69c5 --- /dev/null +++ b/generators/server/templates/src/main/java/package/config/couchbase/CustomN1qlRepositoryQueryExecutor.java.ejs @@ -0,0 +1,104 @@ +<%# + Copyright 2013-2021 the original author or authors from the JHipster project. + + This file is part of the JHipster project, see https://www.jhipster.tech/ + for more information. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-%> +package <%= packageName %>.config.couchbase; + +import com.couchbase.client.java.query.QueryScanConsistency; +import org.springframework.data.couchbase.core.CouchbaseOperations; +import org.springframework.data.couchbase.core.ExecutableFindByQueryOperation; +import org.springframework.data.couchbase.core.query.Query; +import org.springframework.data.couchbase.repository.query.CouchbaseQueryMethod; +import org.springframework.data.couchbase.repository.query.ReactiveN1qlRepositoryQueryExecutor; +import org.springframework.data.couchbase.repository.query.StringN1qlQueryCreator; +import org.springframework.data.repository.core.NamedQueries; +import org.springframework.data.repository.query.ParameterAccessor; +import org.springframework.data.repository.query.ParametersParameterAccessor; +import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; +import org.springframework.data.repository.query.parser.PartTree; +import org.springframework.expression.spel.standard.SpelExpressionParser; + +public class CustomN1qlRepositoryQueryExecutor { + + private final CouchbaseOperations operations; + private final CouchbaseQueryMethod queryMethod; + private final NamedQueries namedQueries; + + public CustomN1qlRepositoryQueryExecutor( + final CouchbaseOperations operations, + final CouchbaseQueryMethod queryMethod, + final NamedQueries namedQueries + ) { + this.operations = operations; + this.queryMethod = queryMethod; + this.namedQueries = namedQueries; + } + + /** + * see also {@link ReactiveN1qlRepositoryQueryExecutor#execute(Object[] parameters) execute } + * + * @param parameters substitute values + * @return with data + */ + public Object execute(final Object[] parameters) { + final Class domainClass = queryMethod.getResultProcessor().getReturnedType().getDomainType(); + final ParameterAccessor accessor = new ParametersParameterAccessor(queryMethod.getParameters(), parameters); + + // this is identical to ReactiveN1qlRespositoryQueryExecutor, + // except for the type of 'q', and the call to oneValue() vs one() + Query query; + ExecutableFindByQueryOperation.ExecutableFindByQuery q; + if (queryMethod.hasN1qlAnnotation()) { + query = + new StringN1qlQueryCreator( + accessor, + queryMethod, + operations.getConverter(), + operations.getBucketName(), + new SpelExpressionParser(), + QueryMethodEvaluationContextProvider.DEFAULT, + namedQueries + ) + .createQuery(); + } else { + final PartTree tree = new PartTree(queryMethod.getName(), domainClass); + query = new CustomN1qlQueryCreator(operations, tree, accessor, queryMethod, operations.getConverter()).createQuery(); + } + q = + (ExecutableFindByQueryOperation.ExecutableFindByQuery) operations + .findByQuery(domainClass) + .consistentWith(buildQueryScanConsistency()) + .matching(query); + if (queryMethod.isCountQuery()) { + return q.count(); + } else if (queryMethod.isCollectionQuery()) { + return q.all(); + } else { + return q.oneValue(); + } + } + + private QueryScanConsistency buildQueryScanConsistency() { + QueryScanConsistency scanConsistency = QueryScanConsistency.NOT_BOUNDED; + if (queryMethod.hasConsistencyAnnotation()) { + scanConsistency = queryMethod.getConsistencyAnnotation().value(); + } else if (queryMethod.hasScanConsistencyAnnotation()) { + scanConsistency = queryMethod.getScanConsistencyAnnotation().query(); + } + return scanConsistency; + } +} diff --git a/generators/server/templates/src/main/java/package/config/couchbase/package-info.java.ejs b/generators/server/templates/src/main/java/package/config/couchbase/package-info.java.ejs new file mode 100644 index 000000000000..7c072ccbc4bb --- /dev/null +++ b/generators/server/templates/src/main/java/package/config/couchbase/package-info.java.ejs @@ -0,0 +1,25 @@ +<%# + Copyright 2013-2021 the original author or authors from the JHipster project. + + This file is part of the JHipster project, see https://www.jhipster.tech/ + for more information. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-%> +/** + * Classes in this package are meant to extend Spring Data Couchbase + * and fix some of the issues encountered during integration. + */ +package <%= packageName %>.config.couchbase; + + diff --git a/generators/server/templates/src/main/java/package/domain/User.java.ejs b/generators/server/templates/src/main/java/package/domain/User.java.ejs index dcbd91a7b6b4..5e85dc273aa8 100644 --- a/generators/server/templates/src/main/java/package/domain/User.java.ejs +++ b/generators/server/templates/src/main/java/package/domain/User.java.ejs @@ -212,9 +212,12 @@ public class <%= asEntity('User') %><% if (databaseTypeSql || databaseTypeMongod <%_ if (databaseTypeSql && reactive) { _%> @Column("first_name") <%_ } _%> -<%_ if (databaseTypeMongodb || databaseTypeCouchbase) { _%> +<%_ if (databaseTypeMongodb) { _%> @Field("first_name") <%_ } _%> +<%_ if (databaseTypeCouchbase) { _%> + @Field +<%_ } _%> <%_ if (databaseTypeCassandra && !reactive) { _%> @CqlName("firstname") <%_ } _%> @@ -230,9 +233,12 @@ public class <%= asEntity('User') %><% if (databaseTypeSql || databaseTypeMongod <%_ if (databaseTypeSql && reactive) { _%> @Column("last_name") <%_ } _%> -<%_ if (databaseTypeMongodb || databaseTypeCouchbase) { _%> +<%_ if (databaseTypeMongodb) { _%> @Field("last_name") <%_ } _%> +<%_ if (databaseTypeCouchbase) { _%> + @Field +<%_ } _%> <%_ if (databaseTypeCassandra && !reactive) { _%> @CqlName("lastname") <%_ } _%> @@ -266,9 +272,12 @@ public class <%= asEntity('User') %><% if (databaseTypeSql || databaseTypeMongod <%_ if (databaseTypeSql && reactive) { _%> @Column("lang_key") <%_ } _%> -<%_ if (databaseTypeMongodb || databaseTypeCouchbase) { _%> +<%_ if (databaseTypeMongodb) { _%> @Field("lang_key") <%_ } _%> +<%_ if (databaseTypeCouchbase) { _%> + @Field +<%_ } _%> <%_ if (databaseTypeNeo4j) { _%> @Property("lang_key") <%_ } _%> @@ -285,9 +294,12 @@ public class <%= asEntity('User') %><% if (databaseTypeSql || databaseTypeMongod <%_ if (databaseTypeSql && reactive) { _%> @Column("image_url") <%_ } _%> - <%_ if (databaseTypeMongodb || databaseTypeCouchbase) { _%> + <%_ if (databaseTypeMongodb) { _%> @Field("image_url") <%_ } _%> + <%_ if (databaseTypeCouchbase) { _%> + @Field + <%_ } _%> <%_ if (databaseTypeNeo4j) { _%> @Property("image_url") <%_ } _%> @@ -302,9 +314,12 @@ public class <%= asEntity('User') %><% if (databaseTypeSql || databaseTypeMongod <%_ if (databaseTypeSql && reactive) { _%> @Column("activation_key") <%_ } _%> - <%_ if (databaseTypeMongodb || databaseTypeCouchbase) { _%> + <%_ if (databaseTypeMongodb) { _%> @Field("activation_key") <%_ } _%> + <%_ if (databaseTypeCouchbase) { _%> + @Field + <%_ } _%> <%_ if (databaseTypeNeo4j) { _%> @Property("activation_key") <%_ } _%> @@ -321,9 +336,12 @@ public class <%= asEntity('User') %><% if (databaseTypeSql || databaseTypeMongod <%_ if (databaseTypeSql && reactive) { _%> @Column("reset_key") <%_ } _%> - <%_ if (databaseTypeMongodb || databaseTypeCouchbase) { _%> + <%_ if (databaseTypeMongodb) { _%> @Field("reset_key") <%_ } _%> + <%_ if (databaseTypeCouchbase) { _%> + @Field + <%_ } _%> <%_ if (databaseTypeNeo4j) { _%> @Property("reset_key") <%_ } _%> @@ -335,8 +353,10 @@ public class <%= asEntity('User') %><% if (databaseTypeSql || databaseTypeMongod <%_ if (databaseTypeSql || (databaseTypeCassandra && reactive)) { _%> @Column(<% if (!reactive) { %>name = <% } %>"reset_date") - <%_ } else if (databaseTypeMongodb || databaseTypeCouchbase) { _%> + <%_ } else if (databaseTypeMongodb) { _%> @Field("reset_date") + <%_ } else if (databaseTypeCouchbase) { _%> + @Field <%_ } else if (databaseTypeNeo4j) { _%> @Property("reset_date") <%_ } _%> diff --git a/generators/server/templates/src/main/java/package/repository/AuthorityRepository.java.ejs b/generators/server/templates/src/main/java/package/repository/AuthorityRepository.java.ejs index 191ebb61f1f9..0a7010acc6a7 100644 --- a/generators/server/templates/src/main/java/package/repository/AuthorityRepository.java.ejs +++ b/generators/server/templates/src/main/java/package/repository/AuthorityRepository.java.ejs @@ -19,6 +19,7 @@ package <%= packageName %>.repository; import <%= packageName %>.domain.Authority; +import org.springframework.stereotype.Repository; <%_ if (databaseTypeSql) { _%> <%_ if (!reactive) { _%> @@ -30,6 +31,13 @@ import org.springframework.data.r2dbc.repository.R2dbcRepository; <%_ if (databaseTypeMongodb) { _%> import org.springframework.data.mongodb.repository.<% if (reactive) { %>Reactive<% } %>MongoRepository; <%_ } _%> +<%_ if (databaseTypeCouchbase) { _%> +import org.springframework.data.couchbase.repository.<% if (reactive) { %>Reactive<% } %>CouchbaseRepository; +import org.springframework.data.couchbase.repository.Query; +import com.couchbase.client.java.query.QueryScanConsistency; +import org.springframework.data.couchbase.repository.ScanConsistency; +import java.util.List; +<%_ } _%> <%_ if (databaseTypeNeo4j) { _%> import org.springframework.data.neo4j.repository.<% if (reactive) { %>Reactive<% } %>Neo4jRepository; <% if (reactive) { %>import reactor.core.publisher.Flux;<% } else { %>import java.util.List;<% } %> @@ -55,9 +63,17 @@ import org.springframework.data.neo4j.repository.<% if (reactive) { %>Reactive<% * Spring Data Couchbase repository for the {@link Authority} entity. */ <%_ } _%> -public interface AuthorityRepository extends <% if (databaseTypeSql) { %><% if (reactive) { %>R2dbc<% } else { %>Jpa<% } %>Repository<% } else { %><% if (reactive) { %>Reactive<% } %><% if (databaseTypeMongodb) { %>MongoRepository<% } %><% if (databaseTypeNeo4j) { %>Neo4jRepository<% } %><% if (databaseTypeCouchbase) { %>N1qlCouchbaseRepository<% } %><% } %> { +@Repository +public interface AuthorityRepository extends <% if (databaseTypeSql) { %><% if (reactive) { %>R2dbc<% } else { %>Jpa<% } %>Repository<% } else { %><% if (reactive) { %>Reactive<% } %><% if (databaseTypeMongodb) { %>MongoRepository<% } %><% if (databaseTypeNeo4j) { %>Neo4jRepository<% } %><% if (databaseTypeCouchbase) { %>CouchbaseRepository<% } %><% } %> { <%_ if (databaseTypeNeo4j) { _%> <% if (!reactive) { %>// See https://github.com/neo4j/sdn-rx/issues/51<%_ } _%> <% if (reactive) { %>Flux<% } else { %>List<% } %><<%= asEntity('Authority') %>> findAll(); <%_ } _%> +<%_ if (databaseTypeCouchbase) { _%> + @Override + @Query("SELECT META(`<%= baseName %>`).id AS __id, META(`<%= baseName %>`).cas AS __cas FROM `<%= baseName %>` WHERE `_class` = \"<%= packageName %>.domain.Authority\"") + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + List<<%= asEntity('Authority') %>> findAll(); +<%_ } _%> + } diff --git a/generators/server/templates/src/main/java/package/repository/CustomCouchbaseRepository.java.ejs b/generators/server/templates/src/main/java/package/repository/CustomCouchbaseRepository.java.ejs new file mode 100644 index 000000000000..8cc0d4978745 --- /dev/null +++ b/generators/server/templates/src/main/java/package/repository/CustomCouchbaseRepository.java.ejs @@ -0,0 +1,140 @@ +<%# + Copyright 2013-2021 the original author or authors from the JHipster project. + + This file is part of the JHipster project, see https://www.jhipster.tech/ + for more information. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-%> +package <%= packageName %>.repository; + +<%_ if (searchEngine === 'couchbase') { _%> +import com.couchbase.client.java.search.SearchOptions; +import com.couchbase.client.java.search.SearchQuery; +import com.couchbase.client.java.search.queries.DocIdQuery; +import com.couchbase.client.java.search.queries.QueryStringQuery; +import com.couchbase.client.java.search.result.SearchResult; +import com.couchbase.client.java.search.result.SearchRow; +import <%= packageName %>.repository.search.SearchCouchbaseRepository; +<%_ } _%> +import org.springframework.data.couchbase.core.CouchbaseOperations; +import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity; +import org.springframework.data.couchbase.repository.query.CouchbaseEntityInformation; +import org.springframework.data.couchbase.repository.support.SimpleCouchbaseRepository; +<%_ if (searchEngine === 'couchbase') { _%> +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +<%_ } _%> + +import java.io.Serializable; +<%_ if (searchEngine === 'couchbase') { _%> +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +<%_ } _%> + +/** + * A custom implementation of {@code CouchbaseRepository}. + */ +public class CustomCouchbaseRepository extends SimpleCouchbaseRepository<%if (searchEngine === 'couchbase') { %> implements SearchCouchbaseRepository<% } %> { + + private final CouchbasePersistentEntity persistentEntity; + private final CouchbaseOperations couchbaseOperations; + + /** + * Create a new Repository. + * + * @param metadata the Metadata for the entity. + * @param couchbaseOperations the reference to the template used. + */ + public CustomCouchbaseRepository(CouchbaseEntityInformation metadata, CouchbaseOperations couchbaseOperations) { + super(metadata, couchbaseOperations); + this.couchbaseOperations = couchbaseOperations; + persistentEntity = couchbaseOperations.getConverter().getMappingContext().getPersistentEntity(getEntityInformation().getJavaType()); + } + + @Override + public S save(S entity) { + return super.save(populateIdIfNecessary(entity)); + } +<%_ if (searchEngine === 'couchbase') { _%> + + public Page search(String indexName, String request, Pageable pageable) { + SearchQuery searchQuery = SearchQuery.queryString(request); + SearchOptions searchOptions = SearchOptions.searchOptions() + .limit(pageable.getPageSize()) + .skip((int) pageable.getOffset()); + SearchResult result = couchbaseOperations.getCouchbaseClientFactory().getCluster().searchQuery(indexName, searchQuery, searchOptions); + List pageContent = extractResults(result); + return new PageImpl<>(pageContent, pageable, result.metaData().metrics().totalRows()); + } + + @Override + public List search(String indexName, String request) { + SearchQuery searchQuery = SearchQuery.queryString(request); + return extractResults(couchbaseOperations.getCouchbaseClientFactory().getCluster().searchQuery(indexName, searchQuery)); + } + + static SearchQuery queryString(String request) { + List ids = new LinkedList<>(); + for (String r : request.split(" ")) { + if (r.indexOf("id:") == 0) { + ids.add(r.substring(3)); + request = request.replace(r, "").replaceAll("[ ]+", " ").trim(); + } + } + QueryStringQuery queryString = SearchQuery.queryString(request); + if (ids.size() != 0) { + DocIdQuery docIdQuery = SearchQuery.docId(ids.toArray(new String[0])); + if (!request.isEmpty()) { + return SearchQuery.conjuncts(queryString, docIdQuery); + } + return docIdQuery; + } + return queryString; + } + + @SuppressWarnings("unchecked") + private List extractResults(SearchResult result) { + return result + .rows() + .stream() + .map(SearchRow::id) + .map(id -> (ID) id) + .map(this::findById) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toList()); + } +<%_ } _%> + + /** + * Add generated ID to entity if not already set. + * + * @param entity the entity to update. + * @return entity with ID set. + */ + private S populateIdIfNecessary(S entity) { + if (getEntityInformation().getId(entity) != null) { + return entity; + } + //setId(entity, getCouchbaseOperations().getGeneratedId(entity)); + return entity; + } + + private void setId(S entity, String generatedId) { + persistentEntity.getPropertyAccessor(entity).setProperty(persistentEntity.getIdProperty(), generatedId); + } +} diff --git a/generators/server/templates/src/main/java/package/repository/CustomN1qlCouchbaseRepository.java.ejs b/generators/server/templates/src/main/java/package/repository/CustomN1qlCouchbaseRepository.java.ejs index 2c033cfeb70e..fcb519b5cd6a 100644 --- a/generators/server/templates/src/main/java/package/repository/CustomN1qlCouchbaseRepository.java.ejs +++ b/generators/server/templates/src/main/java/package/repository/CustomN1qlCouchbaseRepository.java.ejs @@ -28,8 +28,11 @@ import com.couchbase.client.java.search.SearchOptions; import <%= packageName %>.repository.search.SearchCouchbaseRepository; <%_ } _%> -import org.springframework.data.couchbase.core.CouchbaseOperations; +import org.springframework.data.couchbase.core.CouchbaseTemplate; import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity; +import org.springframework.data.couchbase.core.mapping.CouchbaseDocument; +import org.springframework.data.couchbase.core.convert.CouchbaseConverter; +import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter; import org.springframework.data.couchbase.repository.query.CouchbaseEntityInformation; import org.springframework.data.couchbase.repository.support.SimpleCouchbaseRepository; <%_ if (searchEngineCouchbase) { _%> @@ -47,11 +50,12 @@ import java.util.stream.Collectors; <%_ } _%> /** - * A custom implementation of {@code CouchbaseRepository}. + * A custom implementation of {@code SimpleCouchbaseRepository}. */ public class CustomN1qlCouchbaseRepository extends SimpleCouchbaseRepository <%if (searchEngineCouchbase) { %> implements SearchCouchbaseRepository<% } %> { private final CouchbasePersistentEntity persistentEntity; + private final CouchbaseTemplate couchbaseOperations; /** * Create a new Repository. @@ -59,8 +63,9 @@ public class CustomN1qlCouchbaseRepository extends S * @param metadata the Metadata for the entity. * @param couchbaseOperations the reference to the template used. */ - public CustomN1qlCouchbaseRepository(CouchbaseEntityInformation metadata, CouchbaseOperations couchbaseOperations) { + public CustomN1qlCouchbaseRepository(CouchbaseEntityInformation metadata, CouchbaseTemplate couchbaseOperations) { super(metadata, couchbaseOperations); + this.couchbaseOperations = couchbaseOperations; persistentEntity = couchbaseOperations.getConverter().getMappingContext().getPersistentEntity(getEntityInformation().getJavaType()); } @@ -83,7 +88,7 @@ public class CustomN1qlCouchbaseRepository extends S @Override public List search(String indexName, String request) { SearchQuery searchQuery = SearchQuery.queryString(request); - return extractResults(getCouchbaseOperations().getCouchbaseBucket().searchQuery(indexName, searchQuery)); + return extractResults(couchbaseOperations.getCouchbaseClientFactory().getCluster().searchQuery(indexName, searchQuery)); } static SearchQuery queryString(String request) { @@ -127,7 +132,9 @@ public class CustomN1qlCouchbaseRepository extends S if (getEntityInformation().getId(entity) != null) { return entity; } - setId(entity, getCouchbaseOperations().getGeneratedId(entity)); + CouchbaseDocument convertedDoc = new CouchbaseDocument(); + couchbaseOperations.getConverter().write(entity, convertedDoc); + setId(entity, convertedDoc.getId()); return entity; } diff --git a/generators/server/templates/src/main/java/package/repository/CustomReactiveCouchbaseRepository.java.ejs b/generators/server/templates/src/main/java/package/repository/CustomReactiveCouchbaseRepository.java.ejs new file mode 100644 index 000000000000..cb385e944487 --- /dev/null +++ b/generators/server/templates/src/main/java/package/repository/CustomReactiveCouchbaseRepository.java.ejs @@ -0,0 +1,141 @@ +<%# + Copyright 2013-2021 the original author or authors from the JHipster project. + + This file is part of the JHipster project, see https://www.jhipster.tech/ + for more information. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-%> +package <%= packageName %>.repository; + +<%_ if (searchEngine === 'couchbase') { _%> +import com.couchbase.client.java.search.SearchQuery; +import com.couchbase.client.java.search.queries.AbstractFtsQuery; +import com.couchbase.client.java.search.queries.DocIdQuery; +import com.couchbase.client.java.search.queries.QueryStringQuery; +import com.couchbase.client.java.search.result.AsyncSearchQueryResult; +import com.couchbase.client.java.search.result.SearchQueryRow; +import <%= packageName %>.repository.search.SearchCouchbaseRepository; +<%_ } _%> +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.couchbase.core.ReactiveCouchbaseOperations; +import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate; +import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity; +import org.springframework.data.couchbase.repository.query.CouchbaseEntityInformation; +import org.springframework.data.couchbase.repository.support.SimpleReactiveCouchbaseRepository; +<%_ if (searchEngine === 'couchbase') { _%> +import org.springframework.data.domain.Pageable; +<%_ } _%> +import reactor.core.publisher.Mono; +<%_ if (searchEngine === 'couchbase') { _%> +import rx.Observable; +import rx.RxReactiveStreams; +<%_ } _%> + +import java.io.Serializable; +import java.util.Set; +<%_ if (searchEngine === 'couchbase') { _%> +import java.util.LinkedList; +import java.util.List; +<%_ } _%> + +/** + * A custom implementation of {@code CouchbaseRepository}. + */ +public class CustomReactiveCouchbaseRepository extends SimpleReactiveCouchbaseRepository<%if (searchEngine === 'couchbase') { %> implements SearchCouchbaseRepository<% } %> { + + private final CouchbasePersistentEntity persistentEntity; + + @Autowired + private final ReactiveCouchbaseTemplate template; + + /** + * Create a new Repository. + * + * @param metadata the Metadata for the entity. + * @param couchbaseOperations the reference to the operations used. + * @param couchbaseTemplate the reference to the template used. + */ + public CustomReactiveCouchbaseRepository(CouchbaseEntityInformation metadata, ReactiveCouchbaseOperations couchbaseOperations, ReactiveCouchbaseTemplate couchbaseTemplate) { + super(metadata, couchbaseOperations); + persistentEntity = couchbaseOperations.getConverter().getMappingContext().getPersistentEntity(getEntityInformation().getJavaType()); + template = couchbaseTemplate; + } + + @Override + public Mono save(S entity) { + return super.save(populateIdIfNecessary(entity)); + } +<%_ if (searchEngine === 'couchbase') { _%> + + public Flux search(String indexName, String request, Pageable pageable) { + SearchQuery searchQuery = new SearchQuery(indexName, queryString(request)) + .limit(pageable.getPageSize()) + .skip((int) pageable.getOffset()); + return search(searchQuery); + } + + @Override + public Flux search(String indexName, String request) { + SearchQuery searchQuery = new SearchQuery(indexName, queryString(request)); + return search(searchQuery); + } + + static AbstractFtsQuery queryString(String request) { + List ids = new LinkedList<>(); + for (String r : request.split(" ")) { + if (r.indexOf("id:") == 0) { + ids.add(r.substring(3)); + request = request.replace(r, "").replaceAll("[ ]+", " ").trim(); + } + } + QueryStringQuery queryString = SearchQuery.queryString(request); + if (ids.size() != 0) { + DocIdQuery docIdQuery = SearchQuery.docId(ids.toArray(new String[0])); + if (!request.isEmpty()) { + return SearchQuery.conjuncts(queryString, docIdQuery); + } + return docIdQuery; + } + return queryString; + } + + @SuppressWarnings("unchecked") + private Flux search(SearchQuery searchQuery) { + Observable resultIdObservable = getCouchbaseOperations().getCouchbaseBucket().async().query(searchQuery) + .flatMap(AsyncSearchQueryResult::hits) + .map(SearchQueryRow::id); + return Flux.from(RxReactiveStreams.toPublisher(resultIdObservable)) + .map(id -> (ID) id) + .flatMap(this::findById); + } +<%_ } _%> + + /** + * Add generated ID to entity if not already set. + * + * @param entity the entity to update. + * @return entity with ID set. + */ + private S populateIdIfNecessary(S entity) { + if (getEntityInformation().getId(entity) != null) { + return entity; + } + // setId(entity, template.getGeneratedId(entity)); + return entity; + } + + private void setId(S entity, String generatedId) { + persistentEntity.getPropertyAccessor(entity).setProperty(persistentEntity.getIdProperty(), generatedId); + } +} diff --git a/generators/server/templates/src/main/java/package/repository/PersistentTokenRepository.java.ejs b/generators/server/templates/src/main/java/package/repository/PersistentTokenRepository.java.ejs index c9a8c1a7e183..e0e361f65142 100644 --- a/generators/server/templates/src/main/java/package/repository/PersistentTokenRepository.java.ejs +++ b/generators/server/templates/src/main/java/package/repository/PersistentTokenRepository.java.ejs @@ -46,9 +46,12 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.mongodb.repository.MongoRepository; <%_ } _%> <%_ if (databaseTypeNeo4j) { _%> -import org.springframework.data.neo4j.repository.Neo4jRepository; +import org.neo4j.springframework.data.repository.Neo4jRepository; <%_ } _%> -<%_ if (databaseTypeCassandra) { _%> +<%_ } if (databaseTypeCoucbase) { _%> +import org.springframework.data.couchbase.repository.CouchbaseRepository; +<%_ } _%> +<%_ } if (databaseTypeCassandra) { _%> import org.springframework.boot.autoconfigure.cassandra.CassandraProperties; import org.springframework.stereotype.Repository; @@ -82,7 +85,7 @@ import static <%= packageName %>.config.Constants.ID_DELIMITER; <%_ } _%> */ <%_ if (databaseTypeSql || databaseTypeMongodb || databaseTypeNeo4j || databaseTypeCouchbase) { _%> -public interface PersistentTokenRepository extends <% if (databaseTypeSql) { %>JpaRepository<% } %><% if (databaseTypeMongodb) { %>MongoRepository<% } %><% if (databaseTypeNeo4j) { %>Neo4jRepository<% } %><% if (databaseTypeCouchbase) { %>N1qlCouchbaseRepository<% } %> { +public interface PersistentTokenRepository extends <% if (databaseTypeSql) { %>JpaRepository<% } %><% if (databaseTypeMongodb) { %>MongoRepository<% } %><% if (databaseTypeNeo4j) { %>Neo4jRepository<% } %><% if (databaseTypeCouchbase) { %>CouchbaseRepository<% } %> { <%_ if (databaseTypeCouchbase) { _%> default Optional findBySeries(String series) { diff --git a/generators/server/templates/src/main/java/package/repository/UserRepository.java.ejs b/generators/server/templates/src/main/java/package/repository/UserRepository.java.ejs index ef7a2e3dad79..a90c8cd43277 100644 --- a/generators/server/templates/src/main/java/package/repository/UserRepository.java.ejs +++ b/generators/server/templates/src/main/java/package/repository/UserRepository.java.ejs @@ -70,12 +70,14 @@ import org.springframework.data.domain.Page; <%_ } _%> import org.springframework.data.domain.Pageable; <%_ } _%> +<% if (databaseType === 'couchbase') { _%> +import org.springframework.data.domain.Sort; +<%_ } _%> <%_ if (databaseTypeSql) { _%> <%_ if (!reactive) { _%> import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; <%_ } else { _%> -import org.springframework.data.domain.Sort; import org.springframework.r2dbc.core.DatabaseClient; import org.springframework.data.r2dbc.convert.R2dbcConverter; import org.springframework.data.r2dbc.core.R2dbcEntityTemplate; @@ -95,6 +97,12 @@ import org.springframework.data.mongodb.repository.<% if (reactive) { %>Reactive <%_ if (databaseTypeNeo4j) { _%> import org.springframework.data.neo4j.repository.<% if (reactive) { %>Reactive<% } %>Neo4jRepository; <%_ } _%> +<%_ } if (databaseType === 'couchbase') { _%> +import com.couchbase.client.java.query.QueryScanConsistency; +import org.springframework.data.couchbase.repository.Query; +import org.springframework.data.couchbase.repository.ScanConsistency; +import org.springframework.data.couchbase.repository.<% if (reactive) { %>Reactive<% } %>CouchbaseRepository; +<%_ } _%> <%_ if (reactive && databaseTypeCassandra) { _%> import org.springframework.data.cassandra.ReactiveResultSet; import org.springframework.data.cassandra.ReactiveSession; @@ -164,24 +172,34 @@ import static <%= packageName %>.config.Constants.ID_DELIMITER; _%> <%_ if ((databaseTypeSql && !reactive) || databaseTypeMongodb || databaseTypeNeo4j || databaseTypeCouchbase) { _%> @Repository -public interface UserRepository extends <% if (databaseTypeSql) { %>JpaRepository<<%= asEntity('User') %>, <%= user.primaryKey.type %>><% } %><% if (reactive) { %>Reactive<% } %><% if (databaseTypeMongodb) { %>MongoRepository<<%= asEntity('User') %>, String><% } %><% if (databaseTypeNeo4j) { %>Neo4jRepository<<%= asEntity('User') %>, String><% } %><% if (databaseTypeCouchbase) { %>N1qlCouchbaseRepository<<%= asEntity('User') %>, String><%if (searchEngineCouchbase) { %>, SearchCouchbaseRepository<<%= asEntity('User') %>, String><% } } %> { +public interface UserRepository extends <% if (databaseTypeSql) { %>JpaRepository<<%= asEntity('User') %>, <%= user.primaryKey.type %>><% } %><% if (reactive) { %>Reactive<% } %><% if (databaseTypeMongodb) { %>MongoRepository<<%= asEntity('User') %>, String><% } %><% if (databaseTypeNeo4j) { %>Neo4jRepository<<%= asEntity('User') %>, String><% } %><% if (databaseTypeCouchbase) { %>CouchbaseRepository<<%= asEntity('User') %>, String><%if (searchEngineCouchbase) { %>, SearchCouchbaseRepository<<%= asEntity('User') %>, String><% } } %> { <%_ if (cacheManagerIsAvailable) { _%> String USERS_BY_LOGIN_CACHE = "usersByLogin"; String USERS_BY_EMAIL_CACHE = "usersByEmail"; <%_ } _%> + <%_ if (databaseType === 'couchbase') { _%> + // @ScanConsistency is to fix index issues with Spring Data Couchbase + // https://github.com/spring-projects/spring-data-couchbase/issues/897 + <%_ } _%> <%_ if (!authenticationTypeOauth2) { _%> - + <%_ if (databaseTypeCouchbase) { _%> + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + <%_ } _%> <%= optionalOrMono %><<%= asEntity('User') %>> findOneByActivationKey(String activationKey); <%_ } _%> <%_ if (!authenticationTypeOauth2) { _%> - + <%_ if (databaseTypeCouchbase) { _%> + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + <%_ } _%> <%= listOrFlux %><<%= asEntity('User') %>> findAllByActivatedIsFalseAndActivationKeyIsNotNullAndCreatedDateBefore(Instant dateTime); <%_ } _%> <%_ if (!authenticationTypeOauth2) { _%> - + <%_ if (databaseTypeCouchbase) { _%> + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + <%_ } _%> <%= optionalOrMono %><<%= asEntity('User') %>> findOneByResetKey(String resetKey); <%_ } _%> @@ -191,13 +209,20 @@ public interface UserRepository extends <% if (databaseTypeSql) { %>JpaRepositor @Cacheable(cacheNames = USERS_BY_EMAIL_CACHE) <%_ } _%> <%_ } _%> + <%_ if (databaseTypeCouchbase) { _%> + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + @Query("#{#n1ql.selectEntity} WHERE LOWER(email) = LOWER($1) AND #{#n1ql.filter}") + <%_ } _%> <%= optionalOrMono %><<%= asEntity('User') %>> findOneByEmailIgnoreCase(String email); <%_ } _%> <%_ if (databaseTypeCouchbase) { _%> - <%_ if (cacheManagerIsAvailable) { _%> + <%_ if (cacheManagerIsAvailable) { _%> @Cacheable(cacheNames = USERS_BY_LOGIN_CACHE) <%_ } _%> + <%_ if (databaseTypeCouchbase) { _%> + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + <%_ } _%> default <%= optionalOrMono %><<%= asEntity('User') %>> findOneByLogin(String login) { return findById(<%= asEntity('User') %>.PREFIX + ID_DELIMITER + login); } @@ -214,6 +239,18 @@ public interface UserRepository extends <% if (databaseTypeSql) { %>JpaRepositor <% if (!reactive) { %>// See https://github.com/neo4j/sdn-rx/issues/51<% } %> <%= listOrFlux %><<%= asEntity('User') %>> findAll(); + <%_ } _%> + <%_ if (databaseType === 'couchbase') { _%> + @Override + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + <%= listOrFlux %><<%= asEntity('User') %>> findAll(); + + <%_ } _%> + <%_ if (databaseType === 'couchbase') { _%> + @Override + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + <%= listOrFlux %><<%= asEntity('User') %>> findAll(Sort sort); + <%_ } _%> <%_ if (databaseTypeSql) { _%> @EntityGraph(attributePaths = "authorities") @@ -233,14 +270,35 @@ public interface UserRepository extends <% if (databaseTypeSql) { %>JpaRepositor <%_ } _%> <% if (reactive) { %> + <%_ if (databaseType === 'couchbase') { _%> + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + <%_ } _%> Flux<<%= asEntity('User') %>>findAllByIdNotNull(Pageable pageable); + <%_ if (databaseType === 'couchbase') { _%> + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + @Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND meta(#{#n1ql.bucket}).id is not null AND activated = true") + <%_ } _%> Flux<<%= asEntity('User') %>>findAllByIdNotNullAndActivatedIsTrue(Pageable pageable); Mono count(); <% } else { %> + <%_ if (databaseType === 'couchbase') { _%> + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + @Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND meta(#{#n1ql.bucket}).id is not null AND activated = true") + <%_ } _%> Page<<%= asEntity('User') %>> findAllByIdNotNullAndActivatedIsTrue(Pageable pageable); <% } %> + + <%_ if (databaseType === 'couchbase') { _%> + @Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND meta(#{#n1ql.bucket}).id is not null AND activated = true") + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + <%= listOrFlux %><<%= asEntity('User') %>> findAllByIdNotNullAndActivatedIsTrue(); + + @Query("SELECT COUNT(*) as count FROM #{#n1ql.bucket} WHERE #{#n1ql.filter} AND meta(#{#n1ql.bucket}).id is not null AND activated = true") + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + Long countAllByIdNotNullAndActivatedIsTrue(); + <%_ } _%> } <%_ } else if (databaseTypeSql && reactive) { _%> @Repository diff --git a/generators/server/templates/src/main/java/package/service/UserService.java.ejs b/generators/server/templates/src/main/java/package/service/UserService.java.ejs index e9da8862cfd8..895fad1c40e0 100644 --- a/generators/server/templates/src/main/java/package/service/UserService.java.ejs +++ b/generators/server/templates/src/main/java/package/service/UserService.java.ejs @@ -56,6 +56,9 @@ import org.springframework.cache.CacheManager; <%_ if (databaseTypeSql || databaseTypeMongodb || databaseTypeNeo4j || databaseTypeCouchbase) { _%> <%_ if (!reactive) { _%> import org.springframework.data.domain.Page; + <%_ if (databaseType === 'couchbase') { _%> +import org.springframework.data.domain.PageImpl; + <%_ } _%> <%_ } _%> import org.springframework.data.domain.Pageable; <%_ if (!authenticationTypeOauth2) { _%> @@ -286,7 +289,11 @@ public class UserService { <%_ if (databaseTypeSql || databaseTypeMongodb || databaseTypeCouchbase || databaseTypeNeo4j) { _%> newUser.setImageUrl(userDTO.getImageUrl()); <%_ } _%> - newUser.setLangKey(userDTO.getLangKey()); + if (userDTO.getLangKey() == null) { + newUser.setLangKey(Constants.DEFAULT_LANGUAGE); // default language + } else { + newUser.setLangKey(userDTO.getLangKey()); + } // new user is not active newUser.setActivated(false); // new user gets registration key @@ -756,7 +763,21 @@ public class UserService { @Transactional(readOnly = true) <%_ } _%> public <% if (reactive) { %>Flux<% } else { %>Page<% } %><<%= asDto('User') %>> getAllPublicUsers(Pageable pageable) { + <%_ if (databaseType === 'couchbase' && !reactive) { _%> + // FIXME: 2/1/21 Current version of Spring Data doesnt seem to support Pagination parameters. Changed the + // method to return a list and added a count query to construct the page. + // return userRepository.findAllByIdNotNullAndActivatedIsTrue(pageable).map(<%= asDto('User') %>::new); + + final Long count = userRepository.countAllByIdNotNullAndActivatedIsTrue(); + if (count == 0) { + return <% if (reactive) { %>Flux<% } else { %>Page<% } %>.empty(); + } + + final List users = userRepository.findAllByIdNotNullAndActivatedIsTrue(); + return new PageImpl<>(users.stream().map(UserDTO::new).collect(Collectors.toList()), pageable, count); + <%_ } else { _%> return userRepository.findAllByIdNotNullAndActivatedIsTrue(pageable).map(<%= asDto('User') %>::new); + <%_ } _%> } <%_ if (reactive) { _%> diff --git a/generators/server/templates/src/main/java/package/web/rest/PublicUserResource.java.ejs b/generators/server/templates/src/main/java/package/web/rest/PublicUserResource.java.ejs index 33ff621e5b80..dfe6721ba9af 100644 --- a/generators/server/templates/src/main/java/package/web/rest/PublicUserResource.java.ejs +++ b/generators/server/templates/src/main/java/package/web/rest/PublicUserResource.java.ejs @@ -20,6 +20,9 @@ package <%= packageName %>.web.rest; <%_ if (searchEngineElasticsearch) { _%> import <%= packageName %>.repository.search.UserSearchRepository; +<%_ } else if (searchEngine != false) { _%> +import <%= packageName %>.repository.UserRepository; +import <%= packageName %>.domain.User; <%_ } _%> <%_ if (!authenticationTypeOauth2) { _%> import org.springframework.data.domain.Sort; @@ -69,10 +72,11 @@ import java.util.ArrayList; import java.util.List; import java.util.Arrays; <%_ } _%> -<%_ if (searchEngineElasticsearch && !reactive) { _%> +<%_ if (!reactive) { _%> import java.util.stream.Collectors; import java.util.stream.StreamSupport; - +<%_ } _%> +<%_ if (searchEngineElasticsearch && !reactive) { _%> import static org.elasticsearch.index.query.QueryBuilders.*; <%_ } _%> @@ -87,14 +91,17 @@ public class PublicUserResource { private final UserService userService; <%_ if (searchEngineElasticsearch) { _%> - private final UserSearchRepository userSearchRepository; +<%_ } else if (searchEngine != false) { _%> + private final UserRepository userRepository; <%_ } _%> - public PublicUserResource(UserService userService<% if (searchEngineElasticsearch) { %>, UserSearchRepository userSearchRepository<% } %>) { + public PublicUserResource(UserService userService<% if (searchEngineElasticsearch) { %>, UserSearchRepository userSearchRepository<% } else if (searchEngine != false) { %>, UserRepository userRepository<% } %> ) { this.userService = userService; <%_ if (searchEngineElasticsearch) { _%> this.userSearchRepository = userSearchRepository; +<%_ } else if (searchEngine != false) { _%> + this.userRepository = userRepository; <%_ } _%> } @@ -172,14 +179,21 @@ public class PublicUserResource { <%_ if (searchEngineElasticsearch) { _%> <%_ if (!reactive) { _%> return StreamSupport - .stream(userSearchRepository.search(query).spliterator(), false) + .stream(userSearchRepository.search(queryStringQuery(query)).spliterator(), false) .map(<%= asDto('User') %>::new) .collect(Collectors.toList()); <%_ } else { _%> return userSearchRepository.search(query).map(<%= asDto('User') %>::new).collectList(); <%_ } _%> <%_ } else { _%> - return userRepository.search(User.PREFIX, query)<% if (reactive) { %>.collectList()<% } %>; + <%_ if (!reactive) { _%> + return StreamSupport + .stream(userRepository.search(User.PREFIX, query).spliterator(), false) + .map(<%= asDto('User') %>::new) + .collect(Collectors.toList()); + <%_ } else { _%> + return userRepository.search(User.PREFIX, query).map(<%= asDto('User') %>::new).collectList(); + <%_ } _%> <%_ } _%> } <%_ } _%> diff --git a/generators/server/templates/src/main/resources/config/application-dev.yml.ejs b/generators/server/templates/src/main/resources/config/application-dev.yml.ejs index 7e3165ef26bf..8908a8c10bdb 100644 --- a/generators/server/templates/src/main/resources/config/application-dev.yml.ejs +++ b/generators/server/templates/src/main/resources/config/application-dev.yml.ejs @@ -200,10 +200,9 @@ spring: <%_ } _%> <%_ if (databaseTypeCouchbase) { _%> couchbase: - bootstrap-hosts: localhost - bucket: - name: <%= baseName %> - password: password + connection-string: couchbase://localhost + username: Administrator + password: password <%_ } _%> <%_ if (databaseTypeSql) { _%> liquibase: @@ -295,6 +294,11 @@ jhipster: app1: /api,/v3/api-docs # recommended dev configuration <%_ } _%> <%_ } _%> +<%_ if (databaseTypeCouchbase) { _%> + database: + couchbase: + bucket-name: <%= baseName %> +<%_ } _%> <%_ if (!cacheProviderNo) { _%> cache: # Cache configuration <%_ if (cacheProviderHazelcast) { _%> diff --git a/generators/server/templates/src/main/resources/config/application-prod.yml.ejs b/generators/server/templates/src/main/resources/config/application-prod.yml.ejs index dedab861c9f5..3fb613aa7a58 100644 --- a/generators/server/templates/src/main/resources/config/application-prod.yml.ejs +++ b/generators/server/templates/src/main/resources/config/application-prod.yml.ejs @@ -170,10 +170,9 @@ spring: <%_ } _%> <%_ if (databaseTypeCouchbase) { _%> couchbase: - bootstrap-hosts: localhost - bucket: - name: <%= baseName %> - password: password + connection-string: couchbase://localhost + username: Administrator + password: password <%_ } _%> <%_ if (databaseTypeSql) { _%> # Replace by 'prod, faker' to add the faker context and have sample data loaded in production @@ -274,6 +273,11 @@ jhipster: http: cache: # Used by the CachingHttpHeadersFilter timeToLiveInDays: 1461 +<%_ if (databaseType === 'couchbase') { _%> + database: + couchbase: + bucket-name: <%= baseName %> +<%_ } _%> <%_ if (!cacheProviderNo) { _%> cache: # Cache configuration <%_ if (cacheProviderHazelcast) { _%> diff --git a/generators/server/templates/src/test/java/package/CouchbaseTestContainerExtension.java.ejs b/generators/server/templates/src/test/java/package/CouchbaseTestContainerExtension.java.ejs new file mode 100644 index 000000000000..05bb183296d8 --- /dev/null +++ b/generators/server/templates/src/test/java/package/CouchbaseTestContainerExtension.java.ejs @@ -0,0 +1,55 @@ +<%# + Copyright 2013-2020 the original author or authors from the JHipster project. + + This file is part of the JHipster project, see https://www.jhipster.tech/ + for more information. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-%> +package <%= packageName %>; + +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.testcontainers.couchbase.BucketDefinition; +import org.testcontainers.couchbase.CouchbaseContainer; +import org.testcontainers.utility.DockerImageName; + +import java.util.concurrent.atomic.AtomicBoolean; + +public class CouchbaseTestContainerExtension implements BeforeAllCallback { + + private static AtomicBoolean started = new AtomicBoolean(false); + + private static CouchbaseContainer couchbase; + + private String getBucketName() { + return "testBucket"; + } + + @Override + public void beforeAll(ExtensionContext extensionContext) throws Exception { + if (!started.get()) { + DockerImageName dockerImage = DockerImageName.parse("<%= DOCKER_COUCHBASE %>") + .asCompatibleSubstituteFor("couchbase/server"); + couchbase = new CouchbaseContainer(dockerImage) + .withBucket(new BucketDefinition(getBucketName()).withQuota(100)) + .withCredentials("user", "secret"); + couchbase.start(); + System.setProperty("spring.couchbase.connection-string", couchbase.getConnectionString()); + System.setProperty("spring.couchbase.username", couchbase.getUsername()); + System.setProperty("spring.couchbase.password", couchbase.getPassword()); + System.setProperty("jhipster.database.couchbase.bucket-name", getBucketName()); + started.set(true); + } + } +} diff --git a/generators/server/templates/src/test/java/package/IntegrationTest.java.ejs b/generators/server/templates/src/test/java/package/IntegrationTest.java.ejs index 38cf7f8c9605..94781f3b154c 100644 --- a/generators/server/templates/src/test/java/package/IntegrationTest.java.ejs +++ b/generators/server/templates/src/test/java/package/IntegrationTest.java.ejs @@ -19,6 +19,9 @@ package <%= packageName %>; import <%= packageName %>.<%= mainClass %>; +<%_ if (databaseTypeCouchbase) { _%> +import <%= packageName %>.CouchbaseTestContainerExtension; +<%_ } _%> <%_ if (databaseTypeNeo4j) { _%> import <%= packageName %>.AbstractNeo4jIT; <%_ } _%> @@ -31,7 +34,7 @@ import <%= packageName %>.RedisTestContainerExtension; <%_ if (authenticationTypeOauth2) { _%> import <%= packageName %>.config.TestSecurityConfiguration; <%_ } _%> -<%_ if (cacheProviderRedis || databaseTypeNeo4j || reactiveSqlTestContainers) { _%> +<%_ if (cacheProviderRedis || databaseTypeNeo4j || databaseTypeCouchbase || reactiveSqlTestContainers) { _%> import org.junit.jupiter.api.extension.ExtendWith; <%_ } _%> @@ -55,6 +58,8 @@ import java.lang.annotation.Target; <%_ if (cacheProviderRedis) { _%> @ExtendWith(RedisTestContainerExtension.class) <%_ } _%> +<%_ if (databaseTypeCouchbase) { _%> +@ExtendWith(CouchbaseTestContainerExtension.class) <%_ if (databaseTypeNeo4j) { _%> @ExtendWith(AbstractNeo4jIT.class) <%_ } _%> diff --git a/generators/server/templates/src/test/java/package/config/DatabaseConfigurationIT.java.ejs b/generators/server/templates/src/test/java/package/config/DatabaseConfigurationIT.java.ejs deleted file mode 100644 index cf7378b25d2f..000000000000 --- a/generators/server/templates/src/test/java/package/config/DatabaseConfigurationIT.java.ejs +++ /dev/null @@ -1,112 +0,0 @@ -<%# - Copyright 2013-2021 the original author or authors from the JHipster project. - - This file is part of the JHipster project, see https://www.jhipster.tech/ - for more information. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. --%> -package <%= packageName %>.config; - -import com.couchbase.client.java.Cluster; -import com.couchbase.client.java.CouchbaseCluster; -import com.couchbase.client.java.env.CouchbaseEnvironment; -import com.couchbase.client.java.env.DefaultCouchbaseEnvironment; -import org.assertj.core.util.Lists; -import org.springframework.boot.autoconfigure.couchbase.CouchbaseProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration; -import org.springframework.data.couchbase.config.BeanNames; -import org.springframework.data.couchbase.config.CouchbaseConfigurer; -import org.testcontainers.couchbase.BucketDefinition; -import org.testcontainers.couchbase.CouchbaseContainer; - -import java.util.List; - -@Configuration -public class DatabaseConfigurationIT extends AbstractCouchbaseConfiguration { - - private CouchbaseProperties couchbaseProperties; - - private static CouchbaseContainer couchbaseContainer; - - private static CouchbaseEnvironment couchbaseEnvironment; - - private static CouchbaseCluster couchbaseCluster; - - public DatabaseConfigurationIT(CouchbaseProperties couchbaseProperties) { - this.couchbaseProperties = couchbaseProperties; - } - - @Override - @Bean(destroyMethod = "", name = BeanNames.COUCHBASE_ENV) - public CouchbaseEnvironment couchbaseEnvironment() { - return getCouchbaseEnvironment(); - } - - @Override - public Cluster couchbaseCluster() throws Exception { - return getCouchbaseCluster(); - } - - @Override - protected List getBootstrapHosts() { - return Lists.newArrayList(getCouchbaseContainer().getContainerIpAddress()); - } - - @Override - protected String getBucketName() { - return couchbaseProperties.getBucket().getName(); - } - - @Override - protected String getBucketPassword() { - return couchbaseProperties.getBucket().getPassword(); - } - - @Override - protected CouchbaseConfigurer couchbaseConfigurer() { - return this; - } - - private CouchbaseContainer getCouchbaseContainer() { - if (couchbaseContainer == null) { - couchbaseContainer = new CouchbaseContainer("<%= DOCKER_COUCHBASE %>") - .withBucket(new BucketDefinition(getBucketName()).withQuota(100)) - .withCredentials(getUsername(), getBucketPassword()); - couchbaseContainer.start(); - } - return couchbaseContainer; - } - - private CouchbaseEnvironment getCouchbaseEnvironment() { - if (couchbaseEnvironment == null) { - couchbaseEnvironment = DefaultCouchbaseEnvironment - .builder() - .bootstrapCarrierDirectPort(getCouchbaseContainer().getBootstrapCarrierDirectPort()) - .bootstrapHttpDirectPort(getCouchbaseContainer().getBootstrapHttpDirectPort()) - .build(); - } - return couchbaseEnvironment; - } - - private CouchbaseCluster getCouchbaseCluster() { - if (couchbaseCluster == null) { - couchbaseCluster = CouchbaseCluster.create(couchbaseEnvironment(), - getCouchbaseContainer().getContainerIpAddress() - ); - } - return couchbaseCluster; - } -} diff --git a/generators/server/templates/src/test/java/package/repository/CustomCouchbaseRepositoryTest.java.ejs b/generators/server/templates/src/test/java/package/repository/CustomCouchbaseRepositoryTest.java.ejs new file mode 100644 index 000000000000..2a1f264e7e46 --- /dev/null +++ b/generators/server/templates/src/test/java/package/repository/CustomCouchbaseRepositoryTest.java.ejs @@ -0,0 +1,48 @@ +<%# + Copyright 2013-2021 the original author or authors from the JHipster project. + + This file is part of the JHipster project, see https://www.jhipster.tech/ + for more information. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-%> +package <%= packageName %>.repository; + +import com.couchbase.client.java.search.SearchQuery; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + +class CustomCouchbaseRepositoryTest { + + @MethodSource + @ParameterizedTest + void queryString(String query, SearchQuery ftsQuery) { + assertThat(Custom<% if (reactive) { %>Reactive<% } %>CouchbaseRepository.queryString(query).toString()).isEqualTo(ftsQuery.toString()); + } + + @SuppressWarnings("unused") + private static Stream queryString() { + return Stream.of( + Arguments.of("id:A", SearchQuery.docId("A")), + Arguments.of("id:A id:B", SearchQuery.docId("A", "B")), + Arguments.of("hello id:A", SearchQuery.conjuncts(SearchQuery.queryString("hello"), SearchQuery.docId("A"))), + Arguments.of("hello id:A kitty id:B", SearchQuery.conjuncts(SearchQuery.queryString("hello kitty"), SearchQuery.docId("A", "B"))), + Arguments.of("hello kitty", SearchQuery.queryString("hello kitty")) + ); + } +} diff --git a/generators/server/templates/src/test/java/package/repository/CustomN1qlCouchbaseRepositoryTest.java.ejs b/generators/server/templates/src/test/java/package/repository/CustomN1qlCouchbaseRepositoryTest.java.ejs index a252c05a0eaa..082fad357d4f 100644 --- a/generators/server/templates/src/test/java/package/repository/CustomN1qlCouchbaseRepositoryTest.java.ejs +++ b/generators/server/templates/src/test/java/package/repository/CustomN1qlCouchbaseRepositoryTest.java.ejs @@ -19,7 +19,6 @@ package <%= packageName %>.repository; import com.couchbase.client.java.search.SearchQuery; -import com.couchbase.client.java.search.queries.AbstractFtsQuery; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -32,7 +31,7 @@ class CustomN1qlCouchbaseRepositoryTest { @MethodSource @ParameterizedTest - void queryString(String query, AbstractFtsQuery ftsQuery) { + void queryString(String query, SearchQuery ftsQuery) { assertThat(Custom<% if (reactive) { %>Reactive<% } %>N1qlCouchbaseRepository.queryString(query).toString()).isEqualTo(ftsQuery.toString()); } diff --git a/generators/server/templates/src/test/java/package/web/rest/AccountResourceIT.java.ejs b/generators/server/templates/src/test/java/package/web/rest/AccountResourceIT.java.ejs index 4c88c3bbf652..19706876631c 100644 --- a/generators/server/templates/src/test/java/package/web/rest/AccountResourceIT.java.ejs +++ b/generators/server/templates/src/test/java/package/web/rest/AccountResourceIT.java.ejs @@ -20,6 +20,10 @@ package <%= packageName %>.web.rest; <%_ if (databaseTypeCassandra) { _%> import <%= packageName %>.AbstractCassandraTest; +<%_ } else if (databaseTypeCouchbase) { _%> +import <%= packageName %>.CouchbaseTestContainerExtension; +<%_ } else if (databaseTypeNeo4j) { _%> +import <%= packageName %>.AbstractNeo4jIT; <%_ } _%> import <%= packageName %>.IntegrationTest; import <%= packageName %>.config.Constants; @@ -49,6 +53,9 @@ import org.apache.commons.lang3.RandomStringUtils; import org.junit.jupiter.api.BeforeEach; <%_ } _%> import org.junit.jupiter.api.Test; +<%_ if (databaseTypeCouchbase || databaseTypeNeo4j) { _%> +import org.junit.jupiter.api.extension.ExtendWith; +<%_ } _%> import org.springframework.beans.factory.annotation.Autowired; <%_ if (!reactive) { _%> import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; diff --git a/generators/server/templates/src/test/java/package/web/rest/TestUtil.java.ejs b/generators/server/templates/src/test/java/package/web/rest/TestUtil.java.ejs index 597f120e4d13..0ec51b58c0dd 100644 --- a/generators/server/templates/src/test/java/package/web/rest/TestUtil.java.ejs +++ b/generators/server/templates/src/test/java/package/web/rest/TestUtil.java.ejs @@ -18,10 +18,6 @@ -%> package <%= packageName %>.web.rest; -<%_ if (searchEngineCouchbase) { _%> -import com.couchbase.client.core.time.Delay; -import com.couchbase.client.java.util.retry.Retry; -<%_ } _%> import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; @@ -38,10 +34,6 @@ import org.springframework.format.support.FormattingConversionService; import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.test.context.TestSecurityContextHolder; <%_ } _%> -<%_ if (searchEngineCouchbase) { _%> -import rx.Observable; -import rx.internal.util.UtilityFunctions; -<%_ } _%> import java.io.IOException; import java.math.BigDecimal; @@ -72,7 +64,7 @@ public final class TestUtil { private static final ObjectMapper mapper = createObjectMapper(); <%_ if (searchEngineCouchbase) { _%> private static final int MAX_ATTEMPTS = 3; - private static final Delay DEFAULT_DELAY = Delay.exponential(TimeUnit.SECONDS); + // private static final Delay DEFAULT_DELAY = Delay.exponential(TimeUnit.SECONDS); <%_ } _%> private static ObjectMapper createObjectMapper() { @@ -111,7 +103,7 @@ public final class TestUtil { <%_ if (databaseTypeCouchbase) { _%> /** - * Mock user authentication for Spring SpEL expression used in {@link <%= packageName %>.repository.N1qlCouchbaseRepository}. + * Mock user authentication */ public static void mockAuthentication() { TestSecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("user", null)); @@ -265,15 +257,15 @@ public final class TestUtil { <%_ } _%> <%_ if (searchEngineCouchbase) { _%> - public static void retryUntilNotEmpty(Callable> iterableCallable) { - Retry.wrapForRetry( - Observable.fromCallable(iterableCallable) - .flatMapIterable(UtilityFunctions.identity()) - .switchIfEmpty(Observable.error(new NoSuchElementException())), - MAX_ATTEMPTS, DEFAULT_DELAY) - .toBlocking() - .subscribe(); - } +// public static void retryUntilNotEmpty(Callable> iterableCallable) { +// Retry.wrapForRetry( +// Observable.fromCallable(iterableCallable) +// .flatMapIterable(UtilityFunctions.identity()) +// .switchIfEmpty(Observable.error(new NoSuchElementException())), +// MAX_ATTEMPTS, DEFAULT_DELAY) +// .toBlocking() +// .subscribe(); +// } <%_ } _%> private TestUtil() {}