Skip to content

Commit

Permalink
fix: improve domain, enhance load test, more optionals
Browse files Browse the repository at this point in the history
  • Loading branch information
querwurzel committed Dec 14, 2024
1 parent 3037924 commit 9f3e44a
Show file tree
Hide file tree
Showing 9 changed files with 86 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,21 +47,7 @@ public Mono<DetailView> viewPaste(String id, String remoteAddress) {

public Mono<DetailView> viewOneTimePaste(String id) {
return pasteService.findAndBurn(id)
.map(paste -> new DetailView(
paste.getId(),
paste.getTitle(),
paste.getContent(),
paste.getContent().getBytes().length,
paste.isPublic(),
false, // paste just burnt
paste.isEncrypted(),
paste.isOneTime(),
paste.isPermanent(),
paste.getDateCreated(),
paste.getDateOfExpiry(),
paste.getLastViewed(),
paste.getViews()
));
.map(paste -> DetailView.of(paste, null));
}

public Mono<ListView> viewAllPastes() {
Expand Down
43 changes: 24 additions & 19 deletions backend/src/main/java/com/github/binpastes/paste/domain/Paste.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import static com.github.binpastes.paste.domain.Paste.PasteSchema;
import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;

@Table(PasteSchema.TABLE_NAME)
public class Paste {
Expand Down Expand Up @@ -50,19 +51,23 @@ public class Paste {
private Long views;

public static Paste newInstance(
String title,
String content,
LocalDateTime dateOfExpiry,
boolean isEncrypted,
PasteExposure exposure,
String remoteAddress
final String title,
final String content,
final boolean isEncrypted,
final PasteExposure exposure,
final LocalDateTime dateOfExpiry,
final String remoteAddress
) {
if (nonNull(dateOfExpiry) && LocalDateTime.now().isAfter(dateOfExpiry)) {
throw new IllegalArgumentException("dateOfExpiry must be in the future!");
}

return new Paste()
.setContent(Objects.requireNonNull(content))
.setExposure(Objects.requireNonNull(exposure))
.setId(IdGenerator.newStringId())
.setTitle(title)
.setContent(Objects.requireNonNull(content))
.setIsEncrypted(isEncrypted)
.setExposure(Objects.requireNonNull(exposure))
.setRemoteAddress(remoteAddress)
.setDateOfExpiry(dateOfExpiry)
.setViews(0);
Expand Down Expand Up @@ -92,8 +97,8 @@ private PasteExposure getExposure() {
return exposure;
}

private String getRemoteAddress() {
return remoteAddress;
private Optional<String> getRemoteAddress() {
return Optional.ofNullable(remoteAddress);
}

public Optional<LocalDateTime> getLastViewed() {
Expand Down Expand Up @@ -125,12 +130,12 @@ public boolean isPermanent() {
}

public boolean isErasable(String remoteAddress) {
if (isUnlisted() || isOneTime()) {
if (isUnlisted() || (isOneTime() && !isExpired())) {
return true;
}

if (isPublic()) {
final var createdBySameAuthor = Objects.equals(remoteAddress, getRemoteAddress());
final var createdBySameAuthor = Objects.equals(remoteAddress, getRemoteAddress().orElse(null));

if (createdBySameAuthor) {
return LocalDateTime.now().minusHours(1).isBefore(getDateCreated());
Expand All @@ -149,17 +154,17 @@ public Paste trackView(LocalDateTime lastViewed) {
return setViews(getViews() + 1);
}

public Paste markAsExpired() {
return markAsExpired(LocalDateTime.now());
public boolean isExpired() {
var currentExpiry = getDateOfExpiry();
return currentExpiry.isPresent() && currentExpiry.get().isBefore(LocalDateTime.now());
}

public Paste markAsExpired(LocalDateTime dateOfExpiry) {
var currentExpiry = getDateOfExpiry();
if (currentExpiry.isEmpty() || dateOfExpiry.isBefore(currentExpiry.get())) {
return setDateOfExpiry(dateOfExpiry);
public Paste markAsExpired() {
if (isExpired()) {
return this;
}

throw new IllegalStateException("Paste has already expired: " + currentExpiry.get());
return setDateOfExpiry(LocalDateTime.now());
}

protected Paste setId(final String id) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public Mono<Paste> create(
String remoteAddress
) {
return pasteRepository
.save(Paste.newInstance(title, content, dateOfExpiry, isEncrypted, exposure, remoteAddress))
.save(Paste.newInstance(title, content, isEncrypted, exposure, dateOfExpiry, remoteAddress))
.doOnSuccess(newPaste -> log.info("Created new paste {}", newPaste.getId()))
.doOnError(throwable -> log.error("Failed to create new paste", throwable));
}
Expand All @@ -53,7 +53,8 @@ public Mono<Paste> findAndBurn(String id) {
.filter(Paste::isOneTime)
.map(Paste::markAsExpired)
.flatMap(pasteRepository::save)
.doOnNext(paste -> log.info("OneTime paste {} viewed and burnt", paste.getId()));
.doOnNext(paste -> log.info("OneTime paste {} viewed and burnt", paste.getId()))
.onErrorComplete(OptimisticLockingFailureException.class);
}

public Flux<Paste> findAll() {
Expand All @@ -78,11 +79,10 @@ public void trackView(String id, LocalDateTime viewedAt) {
}

public Mono<Void> requestDeletion(String id, String remoteAddress) {
var now = LocalDateTime.now();
return pasteRepository
.findOneLegitById(id)
.filter(paste -> paste.isErasable(remoteAddress))
.map(paste -> paste.markAsExpired(now))
.map(Paste::markAsExpired)
.flatMap(pasteRepository::save)
.retryWhen(Retry
.backoff(5, Duration.ofMillis(500))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,9 @@ void getOneTimePasteHidesContent() {
void viewOneTimePasteConcurrently() {
var oneTimePaste = givenOneTimePaste();
var okCount = new AtomicInteger();
var notFoundCount = new AtomicInteger();

final Runnable call = () -> {
final Runnable call = new Thread(() -> {
try {
webClient.post()
.uri("/api/v1/paste/{id}", oneTimePaste.getId())
Expand All @@ -91,15 +92,21 @@ void viewOneTimePasteConcurrently() {
));

okCount.incrementAndGet();
} catch (AssertionError ignored) {}
};
} catch (AssertionError ex) {
if (ex.getMessage().contains("404")) {
notFoundCount.incrementAndGet();
}
}
});

Stream.generate(() -> call)
.parallel()
.limit(100)
.toList()
.parallelStream()
.forEach(Runnable::run);

assertThat(okCount.get()).isOne();
assertThat(notFoundCount.get()).isEqualTo(100 - 1);
}

@Test
Expand Down Expand Up @@ -193,9 +200,9 @@ private Paste givenOneTimePaste() {
Paste.newInstance(
"someTitle",
"Lorem ipsum dolor sit amet",
null,
false,
PasteExposure.ONCE,
null,
"1.1.1.1"
)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ void getExpiringPublicPaste() {
var paste = givenPaste(Paste.newInstance(
"someTitle",
"Lorem ipsum dolor sit amet",
LocalDateTime.now().plusMinutes(1).minusSeconds(3), // expiry before max-age
false,
PasteExposure.PUBLIC,
LocalDateTime.now().plusMinutes(1).minusSeconds(3), // expiry before max-age
"1.1.1.1"
));

Expand Down Expand Up @@ -185,9 +185,9 @@ private Paste givenPublicPaste() {
Paste.newInstance(
"someTitle",
"Lorem ipsum dolor sit amet",
null,
false,
PasteExposure.PUBLIC,
null,
"someRemoteAddress"
)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ private Paste givenPublicPaste() {
Paste.newInstance(
"someTitle",
"Lorem ipsum dolor sit amet",
null,
false,
Paste.PasteExposure.PUBLIC,
null,
"someRemoteAddress"
)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,12 @@ void trackConcurrentPasteViews() {
Flux.fromStream(Stream.generate(intialPaste::getId))
.take(concurrency)
.doOnNext(trackingService::trackView)
// simulate a concurrent update
.doOnNext(id -> pasteRepository.findById(id)
.doOnNext(paste -> setField(paste, "remoteAddress", String.valueOf(random.nextInt())))
.flatMap(paste -> pasteRepository.save(paste))
.flatMap(paste -> pasteRepository.save(paste)) // simulate a concurrent update
.retry()
.subscribe())
.subscribe()
)
.subscribeOn(Schedulers.parallel())
.subscribe();

Expand Down Expand Up @@ -114,9 +114,9 @@ private Paste givenPublicPaste() {
Paste.newInstance(
"someTitle",
"Lorem ipsum dolor sit amet",
null,
false,
PasteExposure.PUBLIC,
null,
"someRemoteAddress"
)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ void getExpiringUnlistedPaste() {
var unlistedPaste = givenPaste(Paste.newInstance(
"someTitle",
"Lorem ipsum dolor sit amet",
LocalDateTime.now().plusMinutes(1).minusSeconds(3), // expiry before max-age
false,
PasteExposure.UNLISTED,
LocalDateTime.now().plusMinutes(1).minusSeconds(3), // expiry before max-age
"1.1.1.1"
));

Expand Down Expand Up @@ -165,9 +165,9 @@ private Paste givenUnlistedPaste() {
Paste.newInstance(
"someTitle",
"Lorem ipsum dolor sit amet",
null,
false,
PasteExposure.UNLISTED,
null,
"1.1.1.1"
)
);
Expand Down
Loading

0 comments on commit 9f3e44a

Please sign in to comment.