generator-jhipster 7.7.0 → 7.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/README.md +1 -2
  2. package/generators/app/index.js +10 -0
  3. package/generators/ci-cd/index.js +1 -1
  4. package/generators/ci-cd/templates/github-actions.yml.ejs +4 -4
  5. package/generators/client/files-common.js +1 -2
  6. package/generators/client/files-react.js +25 -2
  7. package/generators/client/index.js +1 -1
  8. package/generators/client/needle-api/needle-client-react.js +11 -8
  9. package/generators/client/templates/angular/angular.json.ejs +1 -9
  10. package/generators/client/templates/angular/jest.conf.js.ejs +1 -1
  11. package/generators/client/templates/angular/package.json +17 -18
  12. package/generators/client/templates/angular/package.json.ejs +1 -1
  13. package/generators/client/templates/angular/src/main/webapp/app/admin/user-management/list/user-management.component.ts.ejs +1 -1
  14. package/generators/client/templates/angular/src/main/webapp/app/core/util/data-util.service.ts.ejs +1 -1
  15. package/generators/client/templates/angular/src/main/webapp/app/core/util/parse-links.service.ts.ejs +1 -1
  16. package/generators/client/templates/angular/webpack/webpack.custom.js.ejs +12 -1
  17. package/generators/client/templates/common/package.json +7 -6
  18. package/generators/client/templates/common/webpack/webpack.microfrontend.js.jhi.ejs +2 -3
  19. package/generators/client/templates/react/.eslintrc.json.ejs +1 -1
  20. package/generators/client/templates/react/jest.conf.js.ejs +1 -1
  21. package/generators/client/templates/react/package.json +30 -31
  22. package/generators/client/templates/react/package.json.ejs +1 -0
  23. package/generators/client/templates/react/src/main/webapp/app/app.scss.ejs +1 -1
  24. package/generators/client/templates/react/src/main/webapp/app/config/store.ts.ejs +53 -6
  25. package/generators/client/templates/react/src/main/webapp/app/config/translation-middleware.ts.ejs +58 -0
  26. package/generators/client/templates/react/src/main/webapp/app/entities/menu.tsx.ejs +45 -0
  27. package/generators/client/templates/react/src/main/webapp/app/entities/reducers.ts.ejs +25 -0
  28. package/generators/client/templates/react/src/main/webapp/app/entities/routes.tsx.ejs +47 -0
  29. package/generators/client/templates/react/src/main/webapp/app/index.tsx.ejs +7 -7
  30. package/generators/client/templates/react/src/main/webapp/app/main.tsx.ejs +19 -0
  31. package/generators/client/templates/react/src/main/webapp/app/modules/administration/logs/logs.tsx.ejs +1 -1
  32. package/generators/client/templates/react/src/main/webapp/app/modules/administration/user-management/user-management.tsx.ejs +2 -1
  33. package/generators/client/templates/react/src/main/webapp/app/modules/login/login-modal.tsx.ejs +5 -1
  34. package/generators/client/templates/react/src/main/webapp/app/routes.tsx.ejs +24 -7
  35. package/generators/client/templates/react/src/main/webapp/app/shared/error/error-loading.tsx.ejs +15 -0
  36. package/generators/client/templates/react/src/main/webapp/app/shared/layout/menus/entities.tsx.ejs +34 -6
  37. package/generators/client/templates/react/src/main/webapp/app/shared/reducers/index.ts.ejs +9 -2
  38. package/generators/client/templates/react/src/main/webapp/app/shared/reducers/locale.spec.ts.ejs +9 -3
  39. package/generators/client/templates/react/src/main/webapp/app/shared/reducers/locale.ts.ejs +21 -5
  40. package/generators/client/templates/react/src/main/webapp/app/typings.d.ts.ejs +15 -0
  41. package/generators/client/templates/react/src/main/webapp/microfrontends/entities-menu.tsx.ejs +3 -0
  42. package/generators/client/templates/react/src/main/webapp/microfrontends/entities-routes.tsx.ejs +3 -0
  43. package/generators/client/templates/react/tsconfig.json.ejs +3 -3
  44. package/generators/client/templates/react/tsconfig.test.json.ejs +9 -1
  45. package/generators/client/templates/react/webpack/webpack.common.js.ejs +6 -13
  46. package/generators/client/templates/react/webpack/webpack.dev.js.ejs +7 -8
  47. package/generators/client/templates/react/webpack/webpack.microfrontend.js.jhi.react.ejs +99 -0
  48. package/generators/client/templates/react/webpack/webpack.prod.js.ejs +31 -21
  49. package/generators/client/templates/vue/package.json +16 -17
  50. package/generators/client/templates/vue/package.json.ejs +1 -1
  51. package/generators/client/templates/vue/src/main/webapp/app/core/jhi-navbar/jhi-navbar.component.ts.ejs +1 -0
  52. package/generators/client/templates/vue/src/main/webapp/app/shared/data/data-utils.service.ts.ejs +1 -1
  53. package/generators/client/templates/vue/src/test/javascript/jest.conf.js.ejs +2 -2
  54. package/generators/client/templates/vue/tsconfig.json.ejs +0 -3
  55. package/generators/client/templates/vue/webpack/webpack.common.js.ejs +6 -2
  56. package/generators/common/templates/package.json +2 -2
  57. package/generators/docker-compose/index.js +9 -5
  58. package/generators/entity/index.js +34 -19
  59. package/generators/entity-client/index.js +8 -0
  60. package/generators/entity-client/templates/angular/src/main/webapp/app/entities/list/entity-management.component.ts.ejs +1 -1
  61. package/generators/entity-client/templates/angular/src/main/webapp/app/entities/update/entity-management-update.component.ts.ejs +1 -1
  62. package/generators/entity-client/templates/common/src/test/javascript/cypress/integration/entity/entity.spec.ts.ejs +1 -0
  63. package/generators/entity-client/templates/react/src/main/webapp/app/entities/entity-delete-dialog.tsx.ejs +3 -3
  64. package/generators/entity-client/templates/react/src/main/webapp/app/entities/entity-detail.tsx.ejs +5 -4
  65. package/generators/entity-client/templates/react/src/main/webapp/app/entities/entity-update.tsx.ejs +19 -18
  66. package/generators/entity-client/templates/react/src/main/webapp/app/entities/entity.reducer.ts.ejs +2 -2
  67. package/generators/entity-client/templates/react/src/main/webapp/app/entities/entity.tsx.ejs +21 -20
  68. package/generators/entity-i18n/templates/i18n/entity_fr.json.ejs +1 -1
  69. package/generators/entity-server/files.js +30 -10
  70. package/generators/entity-server/index.js +19 -1
  71. package/generators/entity-server/templates/couchbase/src/main/java/package/domain/Entity.java.jhi.spring_data_couchbase.ejs +1 -1
  72. package/generators/entity-server/templates/couchbase/src/main/java/package/repository/EntityRepository.java.ejs +31 -8
  73. package/generators/entity-server/templates/partials/update_template.ejs +77 -0
  74. package/generators/entity-server/templates/reactive/partials/save_template.ejs +1 -1
  75. package/generators/entity-server/templates/reactive/partials/update_template.ejs +41 -0
  76. package/generators/entity-server/templates/src/main/java/package/common/get_all_template.ejs +5 -5
  77. package/generators/entity-server/templates/src/main/java/package/common/get_template.ejs +1 -1
  78. package/generators/entity-server/templates/src/main/java/package/common/inject_template.ejs +13 -4
  79. package/generators/entity-server/templates/src/main/java/package/common/patch_template.ejs +2 -2
  80. package/generators/entity-server/templates/src/main/java/package/domain/Entity.java.jhi.ejs +2 -1
  81. package/generators/entity-server/templates/src/main/java/package/domain/Entity.java.jhi.javax_lifecycle_events.ejs +31 -0
  82. package/generators/entity-server/templates/src/main/java/package/domain/Entity.java.jhi.spring_data_persistable.ejs +54 -0
  83. package/generators/entity-server/templates/src/main/java/package/domain/Entity.java.jhi.spring_data_reactive.ejs +1 -1
  84. package/generators/entity-server/templates/src/main/java/package/domain/EntityCallback.java.ejs +43 -0
  85. package/generators/entity-server/templates/src/main/java/package/repository/EntityRepository.java.ejs +6 -6
  86. package/generators/entity-server/templates/src/main/java/package/repository/EntityRepositoryInternalImpl_reactive.java.ejs +13 -16
  87. package/generators/entity-server/templates/src/main/java/package/repository/EntityRepositoryWithBagRelationshipsImpl.java.ejs +4 -3
  88. package/generators/entity-server/templates/src/main/java/package/repository/EntityRepository_reactive.java.ejs +15 -23
  89. package/generators/entity-server/templates/src/main/java/package/repository/EntitySqlHelper_reactive.java.ejs +2 -2
  90. package/generators/entity-server/templates/src/main/java/package/repository/rowmapper/EntityRowMapper.java.ejs +2 -2
  91. package/generators/entity-server/templates/src/main/java/package/repository/search/EntitySearchRepository.java.ejs +12 -25
  92. package/generators/entity-server/templates/src/main/java/package/service/EntityService.java.ejs +10 -2
  93. package/generators/entity-server/templates/src/main/java/package/service/impl/EntityServiceImpl.java.ejs +21 -5
  94. package/generators/entity-server/templates/src/main/java/package/service/mapper/EntityMapper.java.ejs +61 -90
  95. package/generators/entity-server/templates/src/main/java/package/web/rest/EntityResource.java.ejs +6 -4
  96. package/generators/entity-server/templates/src/test/java/package/web/rest/EntityResourceIT.java.ejs +28 -15
  97. package/generators/generator-base-private.js +1 -1
  98. package/generators/generator-base.js +29 -0
  99. package/generators/generator-constants.js +5 -5
  100. package/generators/generator-transforms.js +0 -1
  101. package/generators/gradle/constants.cjs +1 -1
  102. package/generators/maven/files.cjs +0 -1
  103. package/generators/maven/templates/.mvn/wrapper/maven-wrapper.jar +0 -0
  104. package/generators/maven/templates/.mvn/wrapper/maven-wrapper.properties +18 -2
  105. package/generators/maven/templates/mvnw +13 -7
  106. package/generators/maven/templates/mvnw.cmd +19 -13
  107. package/generators/maven/templates/pom.xml.jhi.ejs +1 -1
  108. package/generators/server/cleanup.js +19 -3
  109. package/generators/server/files.js +101 -17
  110. package/generators/server/index.js +11 -5
  111. package/generators/server/needle-api/needle-server-gradle.js +38 -0
  112. package/generators/server/templates/.mvn/jvm.config +1 -0
  113. package/generators/server/templates/.mvn/wrapper/maven-wrapper.jar +0 -0
  114. package/generators/server/templates/.mvn/wrapper/maven-wrapper.properties +18 -2
  115. package/generators/server/templates/build.gradle.ejs +7 -5
  116. package/generators/server/templates/couchbase/src/main/java/package/repository/JHipsterCouchbaseRepository.java.ejs +24 -1
  117. package/generators/server/templates/devcontainer/Dockerfile.ejs +25 -0
  118. package/generators/server/templates/devcontainer/devcontainer.json.ejs +64 -0
  119. package/generators/server/templates/gradle/wrapper/gradle-wrapper.jar +0 -0
  120. package/generators/server/templates/gradle.properties.ejs +4 -4
  121. package/generators/server/templates/mvnw +13 -7
  122. package/generators/server/templates/mvnw.cmd +19 -13
  123. package/generators/server/templates/pom.xml.ejs +19 -17
  124. package/generators/server/templates/settings.gradle.ejs +6 -4
  125. package/generators/server/templates/src/main/docker/app.yml.ejs +2 -6
  126. package/generators/server/templates/src/main/docker/config/realm-config/jhipster-realm.json.ejs +2 -1
  127. package/generators/server/templates/src/main/docker/mysql.yml.ejs +1 -0
  128. package/generators/server/templates/src/main/java/package/config/AsyncConfiguration.java.ejs +6 -0
  129. package/generators/{client/templates/react/src/main/webapp/app/entities/index.tsx.ejs → server/templates/src/main/java/package/config/KafkaSseConsumer.java.ejs} +8 -14
  130. package/generators/server/templates/src/main/java/package/config/KafkaSseProducer.java.ejs +30 -0
  131. package/generators/server/templates/src/main/java/package/config/SecurityConfiguration.java.ejs +12 -10
  132. package/generators/server/templates/src/main/java/package/repository/EntityManager.java.ejs +3 -3
  133. package/generators/server/templates/src/main/java/package/security/jwt/JWTFilter.java.ejs +1 -1
  134. package/generators/server/templates/src/main/java/package/security/jwt/TokenProvider.java.ejs +1 -1
  135. package/generators/server/templates/src/main/java/package/web/rest/KafkaResource.java.ejs +60 -108
  136. package/generators/server/templates/src/main/java/package/web/rest/KafkaResource_reactive.java.ejs +74 -0
  137. package/generators/server/templates/src/main/resources/config/application.yml.ejs +26 -22
  138. package/generators/server/templates/src/test/java/package/CassandraKeyspaceIT.java.ejs +3 -4
  139. package/generators/server/templates/src/test/java/package/IntegrationTest.java.ejs +9 -3
  140. package/generators/server/templates/src/test/java/package/TechnicalStructureTest.java.ejs +0 -3
  141. package/generators/server/templates/src/test/java/package/config/CassandraTestContainer.java.ejs +122 -0
  142. package/generators/server/templates/src/test/java/package/config/EmbeddedCassandra.java.ejs +11 -0
  143. package/generators/server/templates/src/test/java/package/config/EmbeddedKafka.java.ejs +11 -0
  144. package/generators/server/templates/src/test/java/package/config/EmbeddedMongo.java.ejs +11 -0
  145. package/generators/server/templates/src/test/java/package/config/KafkaTestContainer.java.ejs +38 -0
  146. package/generators/server/templates/src/test/java/package/config/MongoDbTestContainer.java.ejs +67 -0
  147. package/generators/server/templates/src/test/java/package/config/TestContainersSpringContextCustomizerFactory.java.ejs +114 -0
  148. package/generators/server/templates/src/test/java/package/cucumber/CucumberIT.java.ejs +3 -10
  149. package/generators/server/templates/src/test/java/package/security/DomainUserDetailsServiceIT.java.ejs +1 -4
  150. package/generators/server/templates/src/test/java/package/security/jwt/JWTFilterTest.java.ejs +5 -5
  151. package/generators/server/templates/src/test/java/package/service/MailServiceIT.java.ejs +2 -4
  152. package/generators/server/templates/src/test/java/package/service/UserServiceIT.java.ejs +1 -4
  153. package/generators/server/templates/src/test/java/package/web/rest/AccountResourceIT.java.ejs +3 -3
  154. package/generators/server/templates/src/test/java/package/web/rest/AccountResourceIT_oauth2.java.ejs +1 -4
  155. package/generators/server/templates/src/test/java/package/web/rest/AccountResourceIT_skipUserManagement.java.ejs +1 -4
  156. package/generators/server/templates/src/test/java/package/web/rest/KafkaResourceIT.java.ejs +52 -123
  157. package/generators/server/templates/src/test/java/package/web/rest/KafkaResourceIT_reactive.java.ejs +99 -0
  158. package/generators/server/templates/src/test/java/package/web/rest/PublicUserResourceIT.java.ejs +7 -10
  159. package/generators/server/templates/src/test/java/package/web/rest/UserJWTControllerIT.java.ejs +1 -4
  160. package/generators/server/templates/src/test/java/package/web/rest/UserResourceIT.java.ejs +2 -5
  161. package/generators/server/templates/src/test/java/package/web/rest/errors/ExceptionTranslatorIT.java.ejs +1 -4
  162. package/generators/server/templates/src/test/java/package/web/rest/errors/ExceptionTranslatorIT_reactive.java.ejs +1 -4
  163. package/generators/server/templates/src/test/resources/META-INF/spring.factories.ejs +1 -1
  164. package/generators/server/templates/src/test/resources/config/application.yml.ejs +21 -20
  165. package/generators/server/templates/src/test/resources/testcontainers.properties.ejs +1 -0
  166. package/generators/workspaces/index.js +2 -1
  167. package/package.json +15 -15
  168. package/utils/entity.js +2 -0
  169. package/utils/field.js +8 -3
  170. package/utils/relationship.js +10 -1
  171. package/generators/entity-server/templates/src/main/java/package/repository/search/SortToSortBuilderListConverter.java.ejs +0 -25
  172. package/generators/maven/templates/.mvn/wrapper/MavenWrapperDownloader.java +0 -117
  173. package/generators/server/templates/.mvn/wrapper/MavenWrapperDownloader.java +0 -117
  174. package/generators/server/templates/src/main/java/package/config/KafkaProperties.java.ejs +0 -68
  175. package/generators/server/templates/src/test/java/package/AbstractCassandraTest.java.ejs +0 -125
  176. package/generators/server/templates/src/test/java/package/MongoDbTestContainerExtension.java.ejs +0 -37
  177. package/generators/server/templates/src/test/java/package/TestContainersSpringContextCustomizerFactory.java.ejs +0 -24
@@ -192,11 +192,11 @@ public class EntityManager {
192
192
  * @param referencedIds the id of the referred entities.
193
193
  * @return the number of inserted rows.
194
194
  */
195
- public Mono<Integer> updateLinkTable(LinkTable table, Long entityId, Stream<Long> referencedIds) {
195
+ public Mono<Integer> updateLinkTable(LinkTable table, Object entityId, Stream<?> referencedIds) {
196
196
  return deleteFromLinkTable(table, entityId)
197
197
  .then(
198
198
  Flux.fromStream(referencedIds)
199
- .flatMap((Long referenceId) -> {
199
+ .flatMap((Object referenceId) -> {
200
200
  StatementMapper.InsertSpec insert = r2dbcEntityTemplate
201
201
  .getDataAccessStrategy()
202
202
  .getStatementMapper()
@@ -214,7 +214,7 @@ public class EntityManager {
214
214
  .map((List<Integer> updates) -> updates.stream().reduce(Integer::sum).orElse(0)));
215
215
  }
216
216
 
217
- public Mono<Void> deleteFromLinkTable(LinkTable table, Long entityId) {
217
+ public Mono<Void> deleteFromLinkTable(LinkTable table, Object entityId) {
218
218
  Assert.notNull(entityId, "entityId is null");
219
219
  StatementMapper.DeleteSpec deleteSpec = r2dbcEntityTemplate.getDataAccessStrategy().getStatementMapper()
220
220
  .createDelete(table.tableName)
@@ -75,7 +75,7 @@ public class JWTFilter <% if (reactive) { %>implements WebFilter<% } else { %>ex
75
75
  if (StringUtils.hasText(jwt) && this.tokenProvider.validateToken(jwt)) {
76
76
  Authentication authentication = this.tokenProvider.getAuthentication(jwt);
77
77
  <%_ if (reactive) { _%>
78
- return chain.filter(exchange).subscriberContext(ReactiveSecurityContextHolder.withAuthentication(authentication));
78
+ return chain.filter(exchange).contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication));
79
79
  }
80
80
  return chain.filter(exchange);
81
81
  <%_ } else { _%>
@@ -106,7 +106,7 @@ public class TokenProvider {
106
106
  .signWith(key, SignatureAlgorithm.HS512)
107
107
  .setExpiration(validity)
108
108
  <%_ if (reactive) { _%>
109
- .serializeToJsonWith(new JacksonSerializer())
109
+ .serializeToJsonWith(new JacksonSerializer<>())
110
110
  <%_ } _%>
111
111
  .compact();
112
112
  }
@@ -18,133 +18,85 @@
18
18
  -%>
19
19
  package <%= packageName %>.web.rest;
20
20
 
21
- import <%= packageName %>.config.KafkaProperties;
22
- import org.apache.kafka.clients.consumer.ConsumerRecord;
23
- <%_ if (!reactive) { _%>
24
- import org.apache.kafka.clients.consumer.ConsumerRecords;
25
- import org.apache.kafka.clients.consumer.KafkaConsumer;
26
- import org.apache.kafka.clients.producer.KafkaProducer;
27
- import org.apache.kafka.clients.producer.ProducerRecord;
28
- import org.apache.kafka.clients.producer.RecordMetadata;
29
- <%_ } _%>
21
+ import static org.springframework.web.servlet.mvc.method.annotation.SseEmitter.event;
22
+
23
+ import java.io.IOException;
24
+ import java.security.Principal;
25
+ import java.util.HashMap;
26
+ import java.util.Map;
27
+ import java.util.Optional;
28
+
30
29
  import org.slf4j.Logger;
31
30
  import org.slf4j.LoggerFactory;
31
+ import org.springframework.beans.factory.annotation.Qualifier;
32
+ import org.springframework.cloud.stream.annotation.StreamListener;
33
+ import org.springframework.http.MediaType;
34
+ import org.springframework.messaging.Message;
35
+ import org.springframework.messaging.MessageChannel;
36
+ import org.springframework.messaging.MessageHeaders;
37
+ import org.springframework.messaging.support.GenericMessage;
38
+ import org.springframework.util.MimeTypeUtils;
39
+
32
40
  import org.springframework.web.bind.annotation.*;
33
- <%_ if (reactive) { _%>
34
- import reactor.core.publisher.Flux;
35
- import reactor.core.publisher.Mono;
36
- import reactor.kafka.receiver.KafkaReceiver;
37
- import reactor.kafka.receiver.ReceiverOptions;
38
- import reactor.kafka.sender.KafkaSender;
39
- import reactor.kafka.sender.SenderOptions;
40
- import reactor.kafka.sender.SenderRecord;
41
- import reactor.kafka.sender.SenderResult;
42
- <%_ } _%>
43
- <%_ if (!reactive) { _%>
41
+ import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter;
44
42
  import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
45
- <%_ } _%>
46
43
 
47
- <%_ if (!reactive) { _%>
48
- import java.time.Duration;
49
- <%_ } _%>
50
- import java.time.Instant;
51
- import java.util.List;
52
- import java.util.Map;
53
- import java.util.concurrent.ExecutionException;
54
- <%_ if (!reactive) { _%>
55
- import java.util.concurrent.ExecutorService;
56
- import java.util.concurrent.Executors;
57
- <%_ } _%>
44
+ import <%= packageName %>.config.KafkaSseConsumer;
45
+ import <%= packageName %>.config.KafkaSseProducer;
58
46
 
59
47
  @RestController
60
48
  @RequestMapping("/api/<%= dasherizedBaseName %>-kafka")
61
49
  public class <%= upperFirstCamelCase(baseName) %>KafkaResource {
62
50
 
63
51
  private final Logger log = LoggerFactory.getLogger(<%= upperFirstCamelCase(baseName) %>KafkaResource.class);
52
+ private final MessageChannel output;
53
+
54
+ // TODO implement state of the art emitter repository to become 12 factor
55
+ private Map<String, SseEmitter> emitters = new HashMap<>();
64
56
 
65
- private final KafkaProperties kafkaProperties;
66
- <%_ if (reactive) { _%>
67
- private KafkaSender<String, String> sender;
68
- <%_ } else { _%>
69
- private KafkaProducer<String, String> producer;
70
- private ExecutorService sseExecutorService = Executors.newCachedThreadPool();
71
- <%_ } _%>
72
-
73
- public <%= upperFirstCamelCase(baseName) %>KafkaResource(KafkaProperties kafkaProperties) {
74
- this.kafkaProperties = kafkaProperties;
75
- <%_ if (reactive) { _%>
76
- this.sender = KafkaSender.create(SenderOptions.create(kafkaProperties.getProducerProps()));
77
- <%_ } else { _%>
78
- this.producer = new KafkaProducer<>(kafkaProperties.getProducerProps());
79
- <%_ } _%>
57
+ public <%= upperFirstCamelCase(baseName) %>KafkaResource(@Qualifier(KafkaSseProducer.CHANNELNAME) MessageChannel output) {
58
+ this.output = output;
80
59
  }
81
60
 
82
- @PostMapping("/publish/{topic}")
83
- public <% if (reactive) { %>Mono<PublishResult><% } else { %>PublishResult<% } %> publish(@PathVariable String topic, @RequestParam String message, @RequestParam(required = false) String key)<% if (!reactive) { %> throws ExecutionException, InterruptedException<% } %> {
84
- log.debug("REST request to send to Kafka topic {} with key {} the message : {}", topic, key, message);
85
- <%_ if (reactive) { _%>
86
- return Mono.just(SenderRecord.create(topic, null, null, key, message, null))
87
- .as(sender::send)
88
- .next()
89
- .map(SenderResult::recordMetadata)
90
- .map(metadata -> new PublishResult(metadata.topic(), metadata.partition(), metadata.offset(), Instant.ofEpochMilli(metadata.timestamp())));
91
- <%_ } else { _%>
92
- RecordMetadata metadata = producer.send(new ProducerRecord<>(topic, key, message)).get();
93
- return new PublishResult(metadata.topic(), metadata.partition(), metadata.offset(), Instant.ofEpochMilli(metadata.timestamp()));
94
- <%_ } _%>
61
+ @PostMapping("/publish")
62
+ public void publish(@RequestParam String message) {
63
+ log.debug("REST request the message : {} to send to Kafka topic ", message);
64
+ Map<String, Object> map = new HashMap<>();
65
+ map.put(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.TEXT_PLAIN_VALUE);
66
+ MessageHeaders headers = new MessageHeaders(map);
67
+ output.send(new GenericMessage<>(message, headers));
95
68
  }
96
69
 
97
- @GetMapping("/consume")
98
- public <% if (reactive) { %>Flux<String><% } else { %>SseEmitter<% } %> consume(@RequestParam("topic") List<String> topics, @RequestParam Map<String, String> consumerParams) {
99
- log.debug("REST request to consume records from Kafka topics {}", topics);
100
- Map<String, Object> consumerProps = kafkaProperties.getConsumerProps();
101
- consumerProps.putAll(consumerParams);
102
- consumerProps.remove("topic");
103
-
104
- <%_ if (reactive) { _%>
105
- ReceiverOptions<String, String> receiverOptions = ReceiverOptions.<String, String>create(consumerProps)
106
- .subscription(topics);
107
- return KafkaReceiver.create(receiverOptions)
108
- .receive()
109
- .map(ConsumerRecord::value);
110
- <%_ } else { _%>
111
- SseEmitter emitter = new SseEmitter(0L);
112
- sseExecutorService.execute(() -> {
113
- KafkaConsumer<String, String> consumer = new KafkaConsumer<>(consumerProps);
114
- emitter.onCompletion(consumer::close);
115
- consumer.subscribe(topics);
116
- boolean exitLoop = false;
117
- while(!exitLoop) {
70
+ @GetMapping("/register")
71
+ public ResponseBodyEmitter register(Principal principal) {
72
+ log.debug("Registering sse client for {}", principal.getName());
73
+ SseEmitter emitter = new SseEmitter();
74
+ emitter.onCompletion(() -> emitters.remove(emitter));
75
+ emitters.put(principal.getName(), emitter);
76
+ return emitter;
77
+ }
78
+
79
+ @GetMapping("/unregister")
80
+ public void unregister(Principal principal) {
81
+ String user = principal.getName();
82
+ log.debug("Unregistering sse emitter for user: {}", user);
83
+ Optional.ofNullable(emitters.get(user))
84
+ .ifPresent(ResponseBodyEmitter::complete);
85
+ }
86
+
87
+ @StreamListener(value = KafkaSseConsumer.CHANNELNAME, copyHeaders = "false")
88
+ public void consume(Message<String> message) {
89
+ log.debug("Got message from kafka stream: {}", message.getPayload());
90
+ emitters.entrySet().stream().map(Map.Entry::getValue)
91
+ .forEach((SseEmitter emitter) -> {
118
92
  try {
119
- ConsumerRecords<String, String> records = consumer.poll(Duration.ofSeconds(5));
120
- for (ConsumerRecord<String, String> record : records) {
121
- emitter.send(record.value());
122
- }
123
- emitter.send(SseEmitter.event().comment(""));
124
- } catch (Exception ex) {
125
- log.trace("Complete with error {}", ex.getMessage(), ex);
126
- emitter.completeWithError(ex);
127
- exitLoop = true;
128
- }
93
+ emitter.send(event().data(message.getPayload(), MediaType.TEXT_PLAIN));
94
+ } catch (IOException e) {
95
+ log.debug("error sending sse message, {}", message.getPayload());
129
96
  }
130
- consumer.close();
131
- emitter.complete();
132
97
  });
133
- return emitter;
134
- <%_ } _%>
135
98
  }
136
99
 
137
- private static class PublishResult {
138
- public final String topic;
139
- public final int partition;
140
- public final long offset;
141
- public final Instant timestamp;
142
-
143
- private PublishResult(String topic, int partition, long offset, Instant timestamp) {
144
- this.topic = topic;
145
- this.partition = partition;
146
- this.offset = offset;
147
- this.timestamp = timestamp;
148
- }
149
- }
100
+
150
101
  }
102
+
@@ -0,0 +1,74 @@
1
+ <%#
2
+ Copyright 2013-2022 the original author or authors from the JHipster project.
3
+
4
+ This file is part of the JHipster project, see https://www.jhipster.tech/
5
+ for more information.
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ https://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ See the License for the specific language governing permissions and
17
+ limitations under the License.
18
+ -%>
19
+ package <%= packageName %>.web.rest;
20
+
21
+ import java.util.HashMap;
22
+ import java.util.Map;
23
+ import org.slf4j.Logger;
24
+ import org.slf4j.LoggerFactory;
25
+ import org.springframework.beans.factory.annotation.Qualifier;
26
+ import org.springframework.cloud.stream.annotation.StreamListener;
27
+ import org.springframework.http.ResponseEntity;
28
+ import org.springframework.messaging.Message;
29
+ import org.springframework.messaging.MessageChannel;
30
+ import org.springframework.messaging.MessageHeaders;
31
+ import org.springframework.messaging.support.GenericMessage;
32
+ import org.springframework.util.MimeTypeUtils;
33
+ import org.springframework.web.bind.annotation.*;
34
+ import reactor.core.publisher.Flux;
35
+ import reactor.core.publisher.Mono;
36
+ import reactor.core.publisher.Sinks;
37
+ import <%= packageName %>.config.KafkaSseConsumer;
38
+ import <%= packageName %>.config.KafkaSseProducer;
39
+
40
+ @RestController
41
+ @RequestMapping("/api/<%= dasherizedBaseName %>-kafka")
42
+ public class <%= upperFirstCamelCase(baseName) %>KafkaResource {
43
+
44
+ private final Logger log = LoggerFactory.getLogger(<%= upperFirstCamelCase(baseName) %>KafkaResource.class);
45
+
46
+ private final MessageChannel output;
47
+ private Sinks.Many<Message<String>> sink = Sinks.many().unicast().onBackpressureBuffer();
48
+
49
+ public <%= upperFirstCamelCase(baseName) %>KafkaResource(@Qualifier(KafkaSseProducer.CHANNELNAME) MessageChannel output) {
50
+ this.output = output;
51
+ }
52
+
53
+ @PostMapping("/publish")
54
+ public Mono<ResponseEntity<Void>> publish(@RequestParam String message) {
55
+ log.debug("REST request the message : {} to send to Kafka topic", message);
56
+ Map<String, Object> map = new HashMap<>();
57
+ map.put(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.TEXT_PLAIN_VALUE);
58
+ MessageHeaders headers = new MessageHeaders(map);
59
+ output.send(new GenericMessage<>(message, headers));
60
+ return Mono.just(ResponseEntity.noContent().build());
61
+ }
62
+
63
+ @GetMapping("/consume")
64
+ public Flux<String> consume() {
65
+ log.debug("REST request to consume records from Kafka topics");
66
+ return sink.asFlux().map(m -> m.getPayload());
67
+ }
68
+
69
+ @StreamListener(value = KafkaSseConsumer.CHANNELNAME, copyHeaders = "false")
70
+ public void consume(Message<String> message) {
71
+ log.debug("Got message from kafka stream: {}", message.getPayload());
72
+ sink.emitNext(message, Sinks.EmitFailureHandler.FAIL_FAST);
73
+ }
74
+ }
@@ -156,7 +156,7 @@ spring:
156
156
  <%_ } _%>
157
157
  application:
158
158
  name: <%= baseName %>
159
- <%_ if (serviceDiscoveryConsul || (applicationTypeGateway && reactive)) { _%>
159
+ <%_ if (serviceDiscoveryConsul || (applicationTypeGateway && reactive) || messageBrokerKafka) { _%>
160
160
  cloud:
161
161
  <%_ if (serviceDiscoveryConsul) { _%>
162
162
  consul:
@@ -189,6 +189,23 @@ spring:
189
189
  pool:
190
190
  max-connections: 1000
191
191
  <%_ } _%>
192
+ <%_ if (messageBrokerKafka) { _%>
193
+ stream:
194
+ kafka:
195
+ binder:
196
+ replicationFactor: 1
197
+ auto-create-topics: true
198
+ brokers: localhost:9092
199
+ bindings:
200
+ binding-in-sse:
201
+ destination: sse-topic
202
+ content-type: text/plain
203
+ group: <%= dasherizedBaseName %>
204
+ binding-out-sse:
205
+ destination: sse-topic
206
+ content-type: text/plain
207
+ group: <%= dasherizedBaseName %>
208
+ <%_ } _%>
192
209
  <%_ } _%>
193
210
  profiles:
194
211
  # The commented value for `active` can be replaced with valid Spring profiles to load.
@@ -252,7 +269,6 @@ spring:
252
269
  basename: i18n/messages
253
270
  main:
254
271
  allow-bean-definition-overriding: true
255
- allow-circular-references: true
256
272
  <%_ if (!reactive) { _%>
257
273
  mvc:
258
274
  pathmatch:
@@ -299,7 +315,7 @@ spring:
299
315
  client-id: web_app
300
316
  client-secret: web_app
301
317
  <%_ } _%>
302
- scope: openid,profile,email
318
+ scope: openid, profile, email, offline_access # last one for refresh tokens
303
319
  <%_ } _%>
304
320
 
305
321
  server:
@@ -330,11 +346,11 @@ jhipster:
330
346
  # allowed-origins: "http://localhost:8100,http://localhost:9000"
331
347
  # allowed-methods: "*"
332
348
  # allowed-headers: "*"
333
- <%_ if (authenticationType === 'session') { _%>
349
+ <%_ if (authenticationTypeSession) { _%>
334
350
  # exposed-headers: "Link,X-Total-Count,X-${jhipster.clientApp.name}-alert,X-${jhipster.clientApp.name}-error,X-${jhipster.clientApp.name}-params"
335
- <%_ } else { _%>
351
+ <%_ } else { _%>
336
352
  # exposed-headers: "Authorization,Link,X-Total-Count,X-${jhipster.clientApp.name}-alert,X-${jhipster.clientApp.name}-error,X-${jhipster.clientApp.name}-params"
337
- <%_ } _%>
353
+ <%_ } _%>
338
354
  # allow-credentials: true
339
355
  # max-age: 1800
340
356
  mail:
@@ -352,28 +368,16 @@ jhipster:
352
368
  license: unlicensed
353
369
  license-url:
354
370
  security:
355
- <%_ if (clientTheme !== 'none') { _%>
371
+ <%_ if (clientTheme !== 'none') { _%>
356
372
  content-security-policy: "default-src 'self'; frame-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://storage.googleapis.com; style-src 'self' https://fonts.googleapis.com 'unsafe-inline'; img-src 'self' data:; font-src 'self' https://fonts.gstatic.com data:"
357
- <%_ } else { _%>
373
+ <%_ } else { _%>
358
374
  content-security-policy: "default-src 'self'; frame-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://storage.googleapis.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:"
359
- <%_ } _%>
360
- <%_ if (authenticationType === 'oauth2') { _%>
375
+ <%_ } _%>
376
+ <%_ if (authenticationTypeOauth2) { _%>
361
377
  oauth2:
362
378
  audience:
363
379
  - account
364
380
  - api://default
365
- <%_ } _%>
366
- <%_ if (messageBroker === 'kafka') { _%>
367
- kafka:
368
- bootstrap-servers: localhost:9092
369
- consumer:
370
- key.deserializer: org.apache.kafka.common.serialization.StringDeserializer
371
- value.deserializer: org.apache.kafka.common.serialization.StringDeserializer
372
- group.id: <%= dasherizedBaseName %>
373
- auto.offset.reset: earliest
374
- producer:
375
- key.serializer: org.apache.kafka.common.serialization.StringSerializer
376
- value.serializer: org.apache.kafka.common.serialization.StringSerializer
377
381
  <%_ } _%>
378
382
 
379
383
  # ===================================================================
@@ -18,17 +18,16 @@
18
18
  -%>
19
19
  package <%= packageName %>;
20
20
 
21
- import <%= packageName %>.IntegrationTest;
22
21
  import com.datastax.oss.driver.api.core.CqlSession;
23
22
  import com.datastax.oss.driver.api.core.metadata.Metadata;
24
23
  import org.junit.jupiter.api.Test;
25
24
  import org.springframework.beans.factory.annotation.Autowired;
26
- import org.springframework.boot.test.context.SpringBootTest;
25
+ import org.cassandraunit.CQLDataLoader;
27
26
 
28
27
  import static org.assertj.core.api.Assertions.assertThat;
29
28
 
30
29
  @IntegrationTest
31
- public class CassandraKeyspaceIT extends AbstractCassandraTest {
30
+ public class CassandraKeyspaceIT {
32
31
 
33
32
  @Autowired
34
33
  private CqlSession session;
@@ -36,6 +35,6 @@ public class CassandraKeyspaceIT extends AbstractCassandraTest {
36
35
  @Test
37
36
  void shouldListCassandraUnitKeyspace() {
38
37
  Metadata metadata = session.getMetadata();
39
- assertThat(metadata.getKeyspace(CASSANDRA_UNIT_KEYSPACE)).isNotNull();
38
+ assertThat(metadata.getKeyspace(CQLDataLoader.DEFAULT_KEYSPACE_NAME)).isNotNull();
40
39
  }
41
40
  }
@@ -27,7 +27,10 @@ import tech.jhipster.config.JHipsterConstants;
27
27
  import <%= packageName %>.AbstractNeo4jIT;
28
28
  <%_ } _%>
29
29
  <%_ if (databaseTypeMongodb) { _%>
30
- import <%= packageName %>.MongoDbTestContainerExtension;
30
+ import <%= packageName %>.config.EmbeddedMongo;
31
+ <%_ } _%>
32
+ <%_ if (databaseTypeCassandra) { _%>
33
+ import <%= packageName %>.config.EmbeddedCassandra;
31
34
  <%_ } _%>
32
35
  <%_ if (reactiveSqlTestContainers) { _%>
33
36
  import <%= packageName %>.ReactiveSqlTestContainerExtension;
@@ -38,7 +41,7 @@ import <%= packageName %>.RedisTestContainerExtension;
38
41
  <%_ if (authenticationTypeOauth2) { _%>
39
42
  import <%= packageName %>.config.TestSecurityConfiguration;
40
43
  <%_ } _%>
41
- <%_ if (cacheProviderRedis || databaseTypeNeo4j || databaseTypeCouchbase || reactiveSqlTestContainers || databaseTypeMongodb) { _%>
44
+ <%_ if (cacheProviderRedis || databaseTypeNeo4j || databaseTypeCouchbase || reactiveSqlTestContainers) { _%>
42
45
  import org.junit.jupiter.api.extension.ExtendWith;
43
46
  <%_ } _%>
44
47
 
@@ -67,7 +70,10 @@ import java.lang.annotation.Target;
67
70
  @ActiveProfiles(JHipsterConstants.SPRING_PROFILE_TEST)
68
71
  <%_ } _%>
69
72
  <%_ if (databaseTypeMongodb) { _%>
70
- @ExtendWith(MongoDbTestContainerExtension.class)
73
+ @EmbeddedMongo
74
+ <%_ } _%>
75
+ <%_ if (databaseTypeCassandra) { _%>
76
+ @EmbeddedCassandra
71
77
  <%_ } _%>
72
78
  <%_ if (databaseTypeNeo4j) { _%>
73
79
  @ExtendWith(AbstractNeo4jIT.class)
@@ -66,9 +66,6 @@ class TechnicalStructureTest {
66
66
  .ignoreDependency(alwaysTrue(), belongToAnyOf(
67
67
  <%_ if (hasConstants) { _%>
68
68
  <%= packageName %>.config.Constants.class,
69
- <%_ } _%>
70
- <%_ if (messageBrokerKafka) { _%>
71
- <%= packageName %>.config.KafkaProperties.class,
72
69
  <%_ } _%>
73
70
  <%= packageName %>.config.ApplicationProperties.class
74
71
  ));
@@ -0,0 +1,122 @@
1
+ package <%= packageName %>.config;
2
+
3
+ import static java.nio.file.Files.newDirectoryStream;
4
+ import static java.nio.file.Paths.get;
5
+ import static java.util.Spliterator.SORTED;
6
+ import static java.util.Spliterators.spliteratorUnknownSize;
7
+ import static java.util.stream.StreamSupport.stream;
8
+
9
+ import com.datastax.driver.core.Cluster;
10
+ import com.datastax.driver.core.Session;
11
+ import com.datastax.oss.driver.api.core.CqlSession;
12
+ import java.io.IOException;
13
+ import java.net.InetSocketAddress;
14
+ import java.net.URISyntaxException;
15
+ import java.net.URL;
16
+ import java.nio.file.Path;
17
+ import java.time.Duration;
18
+ import java.time.temporal.ChronoUnit;
19
+ import java.util.Iterator;
20
+ import java.util.Spliterator;
21
+
22
+ import com.datastax.oss.driver.api.core.CqlSessionBuilder;
23
+ import com.datastax.oss.driver.api.core.config.DefaultDriverOption;
24
+ import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
25
+ import org.cassandraunit.CQLDataLoader;
26
+ import org.cassandraunit.dataset.cql.ClassPathCQLDataSet;
27
+ import org.springframework.beans.factory.DisposableBean;
28
+ import org.springframework.beans.factory.InitializingBean;
29
+ import org.springframework.test.context.junit.jupiter.SpringExtension;
30
+ import org.slf4j.Logger;
31
+ import org.slf4j.LoggerFactory;
32
+ import org.testcontainers.containers.CassandraContainer;
33
+ import org.testcontainers.containers.output.Slf4jLogConsumer;
34
+
35
+ /**
36
+ * Base class for starting/stopping Cassandra during tests.
37
+ */
38
+ public class CassandraTestContainer implements InitializingBean, DisposableBean {
39
+
40
+ private static final Logger log = LoggerFactory.getLogger(CassandraTestContainer.class);
41
+ private static final Integer DATABASE_REQUEST_TIMEOUT = 20;
42
+ private static final Integer CONTAINER_STARTUP_TIMEOUT_MINUTES = 10;
43
+ private CassandraContainer cassandraContainer;
44
+
45
+ @Override
46
+ public void destroy() {
47
+ if (null != cassandraContainer && cassandraContainer.isRunning()) {
48
+ cassandraContainer.stop();
49
+ }
50
+ }
51
+
52
+ @Override
53
+ public void afterPropertiesSet() {
54
+ if (null == cassandraContainer) {
55
+ cassandraContainer = (CassandraContainer) new CassandraContainer("<%= DOCKER_CASSANDRA %>")
56
+ .withStartupTimeout(Duration.of(CONTAINER_STARTUP_TIMEOUT_MINUTES, ChronoUnit.MINUTES))
57
+ .withLogConsumer(new Slf4jLogConsumer(log))
58
+ .withReuse(true);
59
+ }
60
+ if (!cassandraContainer.isRunning()) {
61
+ cassandraContainer.start();
62
+ Cluster cluster = cassandraContainer.getCluster();
63
+
64
+ try(Session session = cluster.connect()) {
65
+ createTestKeyspace(session);
66
+ }
67
+ CqlSession cqlSession = new CqlSessionBuilder()
68
+ .addContactPoint(
69
+ new InetSocketAddress(
70
+ cassandraContainer.getHost(),
71
+ cassandraContainer.getMappedPort(CassandraContainer.CQL_PORT))
72
+ ).withLocalDatacenter(cluster.getMetadata().getAllHosts().iterator().next().getDatacenter())
73
+ .withKeyspace(CQLDataLoader.DEFAULT_KEYSPACE_NAME)
74
+ .withConfigLoader(getConfigLoader())
75
+ .build();
76
+ CQLDataLoader dataLoader = new CQLDataLoader(cqlSession);
77
+ applyScripts(dataLoader, "config/cql/changelog/", "*.cql");
78
+ cqlSession.close();
79
+ }
80
+ }
81
+
82
+ public CassandraContainer getCassandraContainer() {
83
+ return cassandraContainer;
84
+ }
85
+
86
+ private DriverConfigLoader getConfigLoader() {
87
+ return DriverConfigLoader.programmaticBuilder()
88
+ .withDuration(DefaultDriverOption.REQUEST_TIMEOUT, Duration.ofSeconds(DATABASE_REQUEST_TIMEOUT))
89
+ .build();
90
+ }
91
+
92
+ private void createTestKeyspace(Session session) {
93
+ String createQuery =
94
+ "CREATE KEYSPACE " + CQLDataLoader.DEFAULT_KEYSPACE_NAME + " WITH replication={'class' : 'SimpleStrategy', 'replication_factor':1}";
95
+ session.execute(createQuery);
96
+ }
97
+
98
+ private void applyScripts(CQLDataLoader dataLoader, String cqlDir, String pattern) {
99
+ URL dirUrl = ClassLoader.getSystemResource(cqlDir);
100
+ if (dirUrl == null) { // protect for empty directory
101
+ return;
102
+ }
103
+
104
+ Iterator<Path> pathIterator = null;
105
+ try {
106
+ pathIterator = newDirectoryStream(get(dirUrl.toURI()), pattern).iterator();
107
+ } catch (IOException e) {
108
+ log.error("error trying to reading CQL chagelog", e);
109
+ } catch (URISyntaxException e) {
110
+ log.error("error trying to get CQL chagelog uri", e);
111
+ }
112
+
113
+ Spliterator<Path> pathSpliterator = spliteratorUnknownSize(pathIterator, SORTED);
114
+ stream(pathSpliterator, false)
115
+ .map(Path::getFileName)
116
+ .map(Path::toString)
117
+ .sorted()
118
+ .map(file -> cqlDir + file)
119
+ .map(dataSetLocation -> new ClassPathCQLDataSet(dataSetLocation, false, false, dataLoader.getSession().getKeyspace().get().toString()))
120
+ .forEach(dataLoader::load);
121
+ }
122
+ }
@@ -0,0 +1,11 @@
1
+ package <%= packageName %>.config;
2
+
3
+ import java.lang.annotation.ElementType;
4
+ import java.lang.annotation.Retention;
5
+ import java.lang.annotation.RetentionPolicy;
6
+ import java.lang.annotation.Target;
7
+
8
+ @Target(ElementType.TYPE)
9
+ @Retention(RetentionPolicy.RUNTIME)
10
+ public @interface EmbeddedCassandra {
11
+ }
@@ -0,0 +1,11 @@
1
+ package <%= packageName %>.config;
2
+
3
+ import java.lang.annotation.ElementType;
4
+ import java.lang.annotation.Retention;
5
+ import java.lang.annotation.RetentionPolicy;
6
+ import java.lang.annotation.Target;
7
+
8
+ @Target(ElementType.TYPE)
9
+ @Retention(RetentionPolicy.RUNTIME)
10
+ public @interface EmbeddedKafka {
11
+ }
@@ -0,0 +1,11 @@
1
+ package <%= packageName %>.config;
2
+
3
+ import java.lang.annotation.ElementType;
4
+ import java.lang.annotation.Retention;
5
+ import java.lang.annotation.RetentionPolicy;
6
+ import java.lang.annotation.Target;
7
+
8
+ @Target(ElementType.TYPE)
9
+ @Retention(RetentionPolicy.RUNTIME)
10
+ public @interface EmbeddedMongo {
11
+ }