|
18 | 18 | import org.springframework.transaction.annotation.Transactional; |
19 | 19 | import reactor.core.publisher.Flux; |
20 | 20 | import reactor.core.publisher.Mono; |
| 21 | +import reactor.core.scheduler.Schedulers; |
21 | 22 | import reactor.util.function.Tuples; |
22 | 23 |
|
23 | 24 | import java.time.Instant; |
@@ -50,7 +51,7 @@ public Mono<ContentNode> findById(UUID uuid) { |
50 | 51 | return contentNodeRepository.findById(uuid).map(contentNodeMapper::fromEntity); |
51 | 52 | } |
52 | 53 |
|
53 | | - public Mono<ContentNode> save(ContentNode contentNode) throws CloneNotSupportedException { |
| 54 | + public Mono<ContentNode> save(ContentNode contentNode) { |
54 | 55 | return Mono.just(contentNode).filter(model -> ObjectUtils.isEmpty(model.getId())) |
55 | 56 | .flatMap(model -> |
56 | 57 | contentNodeRepository.findByCodeAndStatus(contentNode.getCode(), StatusEnum.SNAPSHOT.name()) |
@@ -110,89 +111,84 @@ public Flux<ContentNode> findByNodeCodeAndStatus(String nodeCode, String status) |
110 | 111 | } |
111 | 112 |
|
112 | 113 | public Mono<ContentNode> publish(String code, Boolean publish, String modifiedBy) { |
113 | | - return this.contentNodeRepository.findByCodeAndStatus(code, StatusEnum.PUBLISHED.name()) |
114 | | - .flatMap(alreadyPublished -> { |
115 | | - // Cas où un PUBLISHED existe |
116 | | - alreadyPublished.setStatus(StatusEnum.ARCHIVE); |
117 | | - alreadyPublished.setModificationDate(Instant.now().toEpochMilli()); |
118 | | - |
119 | | - return this.contentNodeRepository.save(alreadyPublished) |
120 | | - .flatMap(archived -> this.contentNodeRepository.findByCodeAndStatus(archived.getCode(), StatusEnum.SNAPSHOT.name())) |
121 | | - .flatMap(snapshotNode -> { |
122 | | - snapshotNode.setStatus(StatusEnum.PUBLISHED); |
123 | | - snapshotNode.setModifiedBy(modifiedBy); |
124 | | - snapshotNode.setModificationDate(Instant.now().toEpochMilli()); |
125 | | - snapshotNode.setPublicationDate(snapshotNode.getModificationDate()); |
126 | | - |
127 | | - String content = snapshotNode.getContent(); |
128 | | - String snapshotContent = content != null ? new String(content) : ""; |
129 | | - |
130 | | - snapshotNode.setContent(SnapshotUtils.clearSnapshotIfCode(snapshotNode.getContent())); |
131 | | - |
132 | | - return this.contentNodeRepository.save(snapshotNode) |
133 | | - .flatMap(publishedContent -> { |
134 | | - try { |
135 | | - // Création du SNAPSHOT en arrière-plan |
136 | | - long version = ObjectUtils.isNotEmpty(publishedContent.getVersion()) |
137 | | - ? Long.parseLong(publishedContent.getVersion()) + 1L |
138 | | - : 1L; |
139 | | - com.itexpert.content.lib.entities.ContentNode newSnapshot = publishedContent.clone(); |
140 | | - newSnapshot.setStatus(StatusEnum.SNAPSHOT); |
141 | | - newSnapshot.setModifiedBy(modifiedBy); |
142 | | - newSnapshot.setId(UUID.randomUUID()); |
143 | | - newSnapshot.setVersion(Long.toString(version)); |
144 | | - newSnapshot.setContent(snapshotContent); |
145 | | - |
146 | | - return this.contentNodeRepository.save(newSnapshot) |
147 | | - .then(Mono.just(publishedContent)); |
148 | | - |
149 | | - } catch (CloneNotSupportedException e) { |
150 | | - return Mono.error(e); |
151 | | - } |
152 | | - }); |
153 | | - }); |
154 | | - }) |
155 | | - .switchIfEmpty(Mono.defer(() -> createFirstPublication(code, publish))) |
156 | | - |
157 | | - .map(contentNodeMapper::fromEntity) |
158 | | - .flatMap(model -> this.notify(model, NotificationEnum.DEPLOYMENT)); |
| 114 | + return Mono.fromCallable(() -> |
| 115 | + publishContentSync(code, publish, modifiedBy) |
| 116 | + ).subscribeOn(Schedulers.boundedElastic()); |
159 | 117 | } |
160 | 118 |
|
161 | | - private Mono<com.itexpert.content.lib.entities.ContentNode> createFirstPublication(String code, Boolean publish) { |
162 | | - if (publish) { |
163 | | - return this.contentNodeRepository.findByCodeAndStatus(code, StatusEnum.SNAPSHOT.name()) |
164 | | - .flatMap(contentNode -> { |
165 | | - contentNode.setStatus(StatusEnum.PUBLISHED); |
166 | | - contentNode.setModificationDate(Instant.now().toEpochMilli()); |
167 | | - contentNode.setPublicationDate(contentNode.getModificationDate()); |
168 | | - |
169 | | - String content = contentNode.getContent(); |
170 | | - String snapshotContent = content != null ? new String(content) : ""; |
171 | | - |
172 | | - contentNode.setContent(SnapshotUtils.clearSnapshotIfCode(contentNode.getContent())); |
173 | | - |
174 | | - |
175 | | - return this.contentNodeRepository.save(contentNode) |
176 | | - .flatMap(savedPublishedNode -> { |
177 | | - // Crée le SNAPSHOT en flux réactif |
178 | | - return Mono.fromCallable(() -> { |
179 | | - com.itexpert.content.lib.entities.ContentNode newSnapshot = savedPublishedNode.clone(); |
180 | | - newSnapshot.setId(UUID.randomUUID()); |
181 | | - newSnapshot.setStatus(StatusEnum.SNAPSHOT); |
182 | | - long version = ObjectUtils.isNotEmpty(savedPublishedNode.getVersion()) |
183 | | - ? Long.parseLong(savedPublishedNode.getVersion()) + 1L |
184 | | - : 1L; |
185 | | - newSnapshot.setVersion(Long.toString(version)); |
186 | | - newSnapshot.setContent(snapshotContent); |
187 | | - return newSnapshot; |
188 | | - }) |
189 | | - .flatMap(newSnapshot -> this.contentNodeRepository.save(newSnapshot)) |
190 | | - // On ignore le SNAPSHOT créé et on retourne le PUBLISHED |
191 | | - .thenReturn(savedPublishedNode); |
192 | | - }); |
193 | | - }); |
| 119 | + @Transactional |
| 120 | + public synchronized ContentNode publishContentSync( |
| 121 | + String code, |
| 122 | + boolean publish, |
| 123 | + String modifiedBy |
| 124 | + ) { |
| 125 | + |
| 126 | + // 1️⃣ Chercher un PUBLISHED existant |
| 127 | + com.itexpert.content.lib.entities.ContentNode alreadyPublished = |
| 128 | + contentNodeRepository |
| 129 | + .findByCodeAndStatus(code, StatusEnum.PUBLISHED.name()) |
| 130 | + .block(); |
| 131 | + |
| 132 | + if (alreadyPublished != null) { |
| 133 | + // 2️⃣ Archiver le PUBLISHED existant |
| 134 | + alreadyPublished.setStatus(StatusEnum.ARCHIVE); |
| 135 | + alreadyPublished.setModificationDate(Instant.now().toEpochMilli()); |
| 136 | + contentNodeRepository.save(alreadyPublished).block(); |
| 137 | + } |
| 138 | + |
| 139 | + // 3️⃣ Chercher le SNAPSHOT à publier |
| 140 | + com.itexpert.content.lib.entities.ContentNode snapshot = |
| 141 | + contentNodeRepository |
| 142 | + .findByCodeAndStatus(code, StatusEnum.SNAPSHOT.name()) |
| 143 | + .block(); |
| 144 | + |
| 145 | + if (snapshot == null) { |
| 146 | + throw new IllegalStateException("Aucun SNAPSHOT à publier pour le content " + code); |
| 147 | + } |
| 148 | + |
| 149 | + // 4️⃣ Publier le SNAPSHOT |
| 150 | + snapshot.setStatus(StatusEnum.PUBLISHED); |
| 151 | + snapshot.setModifiedBy(modifiedBy); |
| 152 | + snapshot.setModificationDate(Instant.now().toEpochMilli()); |
| 153 | + snapshot.setPublicationDate(snapshot.getModificationDate()); |
| 154 | + |
| 155 | + String originalContent = snapshot.getContent(); |
| 156 | + String snapshotContent = originalContent != null ? new String(originalContent) : ""; |
| 157 | + |
| 158 | + snapshot.setContent(SnapshotUtils.clearSnapshotIfCode(snapshot.getContent())); |
| 159 | + |
| 160 | + com.itexpert.content.lib.entities.ContentNode published = |
| 161 | + contentNodeRepository.save(snapshot).block(); |
| 162 | + |
| 163 | + if (published == null) { |
| 164 | + throw new IllegalStateException("Échec publication content " + code); |
| 165 | + } |
| 166 | + |
| 167 | + // 5️⃣ Créer le NOUVEAU SNAPSHOT |
| 168 | + try { |
| 169 | + long version = |
| 170 | + ObjectUtils.isNotEmpty(published.getVersion()) |
| 171 | + ? Long.parseLong(published.getVersion()) + 1L |
| 172 | + : 1L; |
| 173 | + |
| 174 | + com.itexpert.content.lib.entities.ContentNode newSnapshot = published.clone(); |
| 175 | + newSnapshot.setId(UUID.randomUUID()); |
| 176 | + newSnapshot.setStatus(StatusEnum.SNAPSHOT); |
| 177 | + newSnapshot.setModifiedBy(modifiedBy); |
| 178 | + newSnapshot.setVersion(Long.toString(version)); |
| 179 | + newSnapshot.setContent(snapshotContent); |
| 180 | + |
| 181 | + contentNodeRepository.save(newSnapshot).block(); |
| 182 | + |
| 183 | + } catch (CloneNotSupportedException e) { |
| 184 | + throw new IllegalStateException("Impossible de cloner ContentNode", e); |
194 | 185 | } |
195 | | - return Mono.empty(); |
| 186 | + |
| 187 | + // 6️⃣ Notification |
| 188 | + ContentNode model = contentNodeMapper.fromEntity(published); |
| 189 | + notify(model, NotificationEnum.DEPLOYMENT).block(); |
| 190 | + |
| 191 | + return model; |
196 | 192 | } |
197 | 193 |
|
198 | 194 |
|
@@ -396,6 +392,7 @@ public Mono<Boolean> deleteDefinitively(String code) { |
396 | 392 | .map(unused -> Boolean.TRUE) |
397 | 393 | .onErrorContinue((throwable, o) -> log.error(throwable.getMessage(), throwable)); |
398 | 394 | } |
| 395 | + |
399 | 396 | public Mono<Boolean> deleteDefinitivelyVersion(String code, String version) { |
400 | 397 | return contentNodeRepository.findByCodeAndVersion(code, version) |
401 | 398 | .flatMap(node -> this.contentNodeRepository.delete(node) |
@@ -504,5 +501,16 @@ public Mono<Boolean> publishVersion(String code, String version, String user) { |
504 | 501 | ) |
505 | 502 | .defaultIfEmpty(false); // si la version n’existe pas |
506 | 503 | } |
| 504 | + |
| 505 | + protected Mono<Boolean> setMaxHostoryToKeep(String code, Integer maxVersionsToKeep) { |
| 506 | + return this.contentNodeRepository |
| 507 | + .findByNodeCodeAndStatus(code, StatusEnum.SNAPSHOT.name()) |
| 508 | + .flatMap(contentNode -> { |
| 509 | + contentNode.setMaxVersionsToKeep(maxVersionsToKeep); |
| 510 | + return this.contentNodeRepository.save(contentNode); |
| 511 | + }) |
| 512 | + .then(Mono.just(Boolean.TRUE)); |
| 513 | + } |
| 514 | + |
507 | 515 | } |
508 | 516 |
|
0 commit comments