Skip to content

Commit

Permalink
#74 add a rest-endpoint (with caching) for dailyImages
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreKoepke committed Nov 4, 2022
1 parent 3336228 commit b09bdf0
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 9 deletions.
2 changes: 2 additions & 0 deletions src/main/java/ch/akop/homesystem/HomeSystemApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableJpaRepositories
@EnableScheduling
@EnableAsync
@ConfigurationPropertiesScan("ch.akop.homesystem.config.properties")
public class HomeSystemApplication {

Expand Down
11 changes: 9 additions & 2 deletions src/main/java/ch/akop/homesystem/controller/ImageController.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@

import ch.akop.homesystem.services.ImageCreatorService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.CacheControl;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;

import java.time.Duration;
import java.time.temporal.ChronoUnit;

@Slf4j
@RestController
@RequestMapping("/v1/images")
@RequiredArgsConstructor
Expand All @@ -21,16 +24,20 @@ public class ImageController {

private final ImageCreatorService imageCreatorService;

// @Cacheable("dailyImage")
@GetMapping("daily.jpg")
public ResponseEntity<byte[]> getDailyImage() {
public ResponseEntity<StreamingResponseBody> getDailyImage() {
var image = imageCreatorService.getLastImage();
imageCreatorService.increaseDownloadCounter();

return ResponseEntity.ok()
.cacheControl(CacheControl.maxAge(Duration.of(12, ChronoUnit.HOURS)))
.eTag(image.getCreated().toString())
.contentType(MediaType.IMAGE_JPEG)
.headers(header -> header.add("prompt", image.getPrompt()))
.body(image.getImage());
.body(imageCreatorService::writeLastImageToStream);

}


}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import lombok.Setter;

import javax.persistence.*;
import java.sql.Blob;
import java.time.LocalDateTime;

@Entity
Expand All @@ -24,6 +25,6 @@ public class ImageOfOpenAI {
private String prompt;

@Lob
private byte[] image;
private Blob image;

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
import ch.akop.homesystem.persistence.model.ImageOfOpenAI;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Optional;

@Repository
public interface OpenAIImageRepository extends JpaRepository<ImageOfOpenAI, LocalDateTime> {

@Transactional
Optional<ImageOfOpenAI> findFirstByOrderByCreatedDesc();
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@

import ch.akop.homesystem.persistence.model.ImageOfOpenAI;

import java.io.OutputStream;

public interface ImageCreatorService {
void generateAndSendDailyImage();

ImageOfOpenAI getLastImage();

void writeLastImageToStream(OutputStream outputStream);

void increaseDownloadCounter();

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,23 @@
import ch.akop.homesystem.services.WeatherService;
import ch.akop.weathercloud.Weather;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.engine.jdbc.BlobProxy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import java.io.OutputStream;
import java.util.List;
import java.util.NoSuchElementException;

import static ch.akop.homesystem.util.RandomUtil.pickRandomElement;
import static ch.akop.weathercloud.rain.RainUnit.MILLIMETER_PER_HOUR;
import static ch.akop.weathercloud.temperature.TemperatureUnit.DEGREE;

@Slf4j
@Service
@RequiredArgsConstructor
public class ImageCreatorServiceImpl implements ImageCreatorService {
Expand All @@ -26,6 +33,9 @@ public class ImageCreatorServiceImpl implements ImageCreatorService {
private final OpenAIImageRepository imageRepository;
private final MessageService messageService;
private final WeatherService weatherService;
private final TransactionTemplate transactionTemplate;

byte[] lastImage;


@Override
Expand All @@ -34,19 +44,46 @@ public void generateAndSendDailyImage() {
imageService.requestImage(prompt)
.subscribe(image -> {
messageService.sendImageToMainChannel(image, prompt);
imageRepository.save(new ImageOfOpenAI().setPrompt(prompt).setImage(image));
imageRepository.save(new ImageOfOpenAI().setPrompt(prompt).setImage(BlobProxy.generateProxy(image)));
lastImage = image;
});
}

@Override
@Transactional(readOnly = true)
@Transactional
public ImageOfOpenAI getLastImage() {
var last = imageRepository.findFirstByOrderByCreatedDesc()
.orElseThrow(() -> new NoSuchElementException("There are no images right now."));
return getLastImageOrThrow();
}

@Override
@SneakyThrows
public void writeLastImageToStream(OutputStream outputStream) {
outputStream.write(getLastImageAsBytes());
}

last.setDownloaded(last.getDownloaded() + 1);
public synchronized byte[] getLastImageAsBytes() {
if (lastImage == null) {
lastImage = transactionTemplate.execute(status -> getLastImageAsBytesFromDatabase());
}

return last;
return lastImage;
}

@SneakyThrows
private byte[] getLastImageAsBytesFromDatabase() {
return getLastImageOrThrow().getImage().getBinaryStream().readAllBytes();
}

@Override
@Async
public void increaseDownloadCounter() {
imageRepository.findFirstByOrderByCreatedDesc()
.ifPresent(image -> image.setDownloaded(image.getDownloaded() + 1));
}

private ImageOfOpenAI getLastImageOrThrow() {
return imageRepository.findFirstByOrderByCreatedDesc()
.orElseThrow(() -> new NoSuchElementException("There are no images right now."));
}

private String generatePrompt() {
Expand Down

0 comments on commit b09bdf0

Please sign in to comment.