Skip to content

Commit 9820a34

Browse files
authored
Merge pull request #107 from AZIRARM/feat/cleans
feat auto clean node and contents done, fix date view on plugin, webs…
2 parents 4b9542d + a6bdb65 commit 9820a34

File tree

39 files changed

+743
-263
lines changed

39 files changed

+743
-263
lines changed

itexpert-content-api/src/main/resources/application.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,4 @@ spring:
1919
jackson:
2020
default-property-inclusion: non_empty
2121
server:
22-
port: 1080
22+
port: 9080

itexpert-content-core/src/main/java/com/itexpert/content/core/config/WebSocketConfig.java

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,45 @@
11
package com.itexpert.content.core.config;
2-
import com.itexpert.content.core.handlers.NotificationSocketHandler;
2+
3+
import com.itexpert.content.core.handlers.websockets.DatasSocketHandler;
4+
import com.itexpert.content.core.handlers.websockets.NotificationSocketHandler;
5+
import com.itexpert.content.core.handlers.websockets.RedisSocketHandler;
36
import org.springframework.context.annotation.Bean;
47
import org.springframework.context.annotation.Configuration;
58
import org.springframework.web.reactive.HandlerMapping;
69
import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
10+
import org.springframework.web.reactive.socket.WebSocketHandler;
711
import org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter;
812

13+
import java.util.HashMap;
914
import java.util.Map;
1015

1116

1217
@Configuration
1318
public class WebSocketConfig {
14-
private final NotificationSocketHandler handler;
19+
private final NotificationSocketHandler notificationSocketHandler;
20+
private final RedisSocketHandler redisSocketHandler;
21+
private final DatasSocketHandler datasSocketHandler;
22+
1523

16-
public WebSocketConfig(NotificationSocketHandler handler) {
17-
this.handler = handler;
24+
public WebSocketConfig(NotificationSocketHandler notificationSocketHandler, RedisSocketHandler redisSocketHandler, DatasSocketHandler datasSocketHandler) {
25+
this.notificationSocketHandler = notificationSocketHandler;
26+
this.redisSocketHandler = redisSocketHandler;
27+
this.datasSocketHandler = datasSocketHandler;
1828
}
1929

2030
@Bean
2131
public HandlerMapping webSocketMapping() {
22-
return new SimpleUrlHandlerMapping(Map.of("/ws/notifications", handler), -1);
32+
Map<String, WebSocketHandler> map = new HashMap<>();
33+
34+
map.put("/ws/notifications", notificationSocketHandler);
35+
map.put("/ws/owner/**", redisSocketHandler);
36+
map.put("/ws/datas/contentCode/**", datasSocketHandler);
37+
38+
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
39+
mapping.setUrlMap(map);
40+
mapping.setOrder(-1); // priorité max
41+
42+
return mapping;
2343
}
2444

2545
@Bean

itexpert-content-core/src/main/java/com/itexpert/content/core/endpoints/ContentNodeEndPoint.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -242,11 +242,8 @@ public Mono<ResponseEntity<ContentNode>> save(@RequestBody ContentNode contentNo
242242
contentNode.setModifiedBy(user);
243243

244244
return Mono.defer(() -> {
245-
try {
246245
return contentNodeHandler.save(contentNode);
247-
} catch (CloneNotSupportedException ex) {
248-
return Mono.error(new RuntimeException("Erreur lors du clone du contentNode", ex));
249-
}
246+
250247
})
251248
.flatMap(contentNodeHandler::setPublicationStatus)
252249
.flatMap(saved -> redisHandler.releaseLock(resourceKey, user).thenReturn(saved))

itexpert-content-core/src/main/java/com/itexpert/content/core/endpoints/NodeEndPoint.java

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,6 @@ public Mono<ResponseEntity<Boolean>> deleteDefinitivelyVersion(@PathVariable Str
174174
}
175175

176176

177-
178177
@PostMapping(value = "/code/{code}/activate")
179178
public Mono<ResponseEntity<Boolean>> activate(@PathVariable String code, Authentication authentication) {
180179
String user = authentication.getPrincipal().toString();
@@ -371,8 +370,8 @@ public Flux<Node> deploy(@PathVariable String code,
371370
Optional.ofNullable(node.getContents()).orElse(List.of())
372371
.forEach(contentNode -> {
373372
log.debug(
374-
"[EXPORT] ContentNode: code={}, status={}",
375-
contentNode.getCode(), contentNode.getStatus());
373+
"[EXPORT] ContentNode: code={}, status={}",
374+
contentNode.getCode(), contentNode.getStatus());
376375

377376
contentNode.setModifiedBy(authentication.getPrincipal().toString());
378377
});
@@ -393,10 +392,10 @@ public Flux<Node> deploy(@PathVariable String code,
393392

394393
@PostMapping(value = "/code/{code}/version/{version}/deploy")
395394
public Mono<ResponseEntity<Boolean>> deployVersion(
396-
@PathVariable String code,
397-
@PathVariable String version,
398-
@RequestParam(name = "environment", required = false) String environmentCode,
399-
Authentication authentication) {
395+
@PathVariable String code,
396+
@PathVariable String version,
397+
@RequestParam(name = "environment", required = false) String environmentCode,
398+
Authentication authentication) {
400399
String user = authentication.getPrincipal().toString();
401400
Duration ttl = Duration.ofMinutes(30);
402401

@@ -486,4 +485,10 @@ private String cleanStatusSnapshot(String input) {
486485

487486
return base + "?" + String.join("&", kept);
488487
}
488+
489+
@PostMapping(value = "/propagateMaxHistoryToKeep/{nodeCodePatent}")
490+
public Mono<Boolean> propagateMaxHistoryToKeep(
491+
@PathVariable String nodeCodePatent) {
492+
return nodeHandler.propagateMaxHistoryToKeep(nodeCodePatent);
493+
}
489494
}

itexpert-content-core/src/main/java/com/itexpert/content/core/endpoints/RedisLockController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public Mono<Boolean> refresh(@PathVariable String code, Authentication authentic
3737

3838
@GetMapping("/owner/{code}")
3939
public Mono<LockInfo> getOwner(@PathVariable String code, Authentication authentication) {
40-
return redisHandler.getLockInfo(code, authentication);
40+
return redisHandler.getLockInfo(code, authentication.getPrincipal().toString());
4141
}
4242

4343
@PostMapping("/admin/release/{code}")

itexpert-content-core/src/main/java/com/itexpert/content/core/handlers/ContentNodeHandler.java

Lines changed: 89 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.springframework.transaction.annotation.Transactional;
1919
import reactor.core.publisher.Flux;
2020
import reactor.core.publisher.Mono;
21+
import reactor.core.scheduler.Schedulers;
2122
import reactor.util.function.Tuples;
2223

2324
import java.time.Instant;
@@ -50,7 +51,7 @@ public Mono<ContentNode> findById(UUID uuid) {
5051
return contentNodeRepository.findById(uuid).map(contentNodeMapper::fromEntity);
5152
}
5253

53-
public Mono<ContentNode> save(ContentNode contentNode) throws CloneNotSupportedException {
54+
public Mono<ContentNode> save(ContentNode contentNode) {
5455
return Mono.just(contentNode).filter(model -> ObjectUtils.isEmpty(model.getId()))
5556
.flatMap(model ->
5657
contentNodeRepository.findByCodeAndStatus(contentNode.getCode(), StatusEnum.SNAPSHOT.name())
@@ -110,89 +111,84 @@ public Flux<ContentNode> findByNodeCodeAndStatus(String nodeCode, String status)
110111
}
111112

112113
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());
159117
}
160118

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);
194185
}
195-
return Mono.empty();
186+
187+
// 6️⃣ Notification
188+
ContentNode model = contentNodeMapper.fromEntity(published);
189+
notify(model, NotificationEnum.DEPLOYMENT).block();
190+
191+
return model;
196192
}
197193

198194

@@ -396,6 +392,7 @@ public Mono<Boolean> deleteDefinitively(String code) {
396392
.map(unused -> Boolean.TRUE)
397393
.onErrorContinue((throwable, o) -> log.error(throwable.getMessage(), throwable));
398394
}
395+
399396
public Mono<Boolean> deleteDefinitivelyVersion(String code, String version) {
400397
return contentNodeRepository.findByCodeAndVersion(code, version)
401398
.flatMap(node -> this.contentNodeRepository.delete(node)
@@ -504,5 +501,16 @@ public Mono<Boolean> publishVersion(String code, String version, String user) {
504501
)
505502
.defaultIfEmpty(false); // si la version n’existe pas
506503
}
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+
507515
}
508516

0 commit comments

Comments
 (0)