Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SaveTo action to save to the database #15

Merged
merged 1 commit into from
Dec 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions TODO
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* I'm not happy about the database package name, I should find a better one
7 changes: 7 additions & 0 deletions db/tables/Song.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
create sequence song_seq;
CREATE TABLE Song (
id BIGINT NOT NULL DEFAULT nextval('song_seq'),
artist VARCHAR,
title VARCHAR,
raw_data VARCHAR
);
15 changes: 15 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>dev.migwel.icyreader</groupId>
<artifactId>IcyReader</artifactId>
Expand All @@ -46,6 +50,17 @@
<version>4.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.7.1</version>
<scope>runtime</scope>
</dependency>
</dependencies>

<build>
Expand Down
37 changes: 37 additions & 0 deletions src/main/java/dev/migwel/sts/database/DatabaseSaveService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package dev.migwel.sts.database;

import dev.migwel.sts.database.entities.Converter;
import dev.migwel.sts.exception.SaveToException;
import dev.migwel.sts.model.Song;
import dev.migwel.sts.service.SaveService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;

import javax.annotation.ParametersAreNonnullByDefault;

@ParametersAreNonnullByDefault
@Service
public class DatabaseSaveService implements SaveService {
private static final Logger log = LogManager.getLogger(DatabaseSaveService.class);

private final Converter converter;
private final SongRepository songRepository;

public DatabaseSaveService(Converter converter, SongRepository songRepository) {
this.converter = converter;
this.songRepository = songRepository;
}

@Override
public void save(Song song) {
dev.migwel.sts.database.entities.Song entitySong = converter.convert(song);
try {
songRepository.save(entitySong);
} catch (DataAccessException e) {
log.warn("Could not persist song", e);
throw new SaveToException("Cold not persist song: " + e.getMessage());
}
}
}
12 changes: 12 additions & 0 deletions src/main/java/dev/migwel/sts/database/SongRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package dev.migwel.sts.database;

import dev.migwel.sts.database.entities.Song;
import org.springframework.data.repository.Repository;

import java.util.Optional;

public interface SongRepository extends Repository<Song, Long> {
Song save(Song song);

Optional<Song> findByArtistAndTitle(String artist, String title);
}
18 changes: 18 additions & 0 deletions src/main/java/dev/migwel/sts/database/entities/Converter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package dev.migwel.sts.database.entities;

import org.springframework.stereotype.Component;

import javax.annotation.ParametersAreNonnullByDefault;

@Component
@ParametersAreNonnullByDefault
public class Converter {
public Song convert(dev.migwel.sts.model.Song model) {
return new Song(model.artist(), model.title(), model.rawData());
}

public dev.migwel.sts.model.Song convert(Song entity) {
return new dev.migwel.sts.model.Song(
entity.getArtist(), entity.getTitle(), entity.getRawData());
}
}
68 changes: 68 additions & 0 deletions src/main/java/dev/migwel/sts/database/entities/Song.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package dev.migwel.sts.database.entities;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.SequenceGenerator;

@Entity
public class Song {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "song_generator")
@SequenceGenerator(name = "song_generator", sequenceName = "song_seq", allocationSize = 1)
private Long id;

private String artist;
private String title;
private String rawData;

public Song() {
// Needed for Spring JPA
}

public Song(Long id, String artist, String title, String rawData) {
this.id = id;
this.artist = artist;
this.title = title;
this.rawData = rawData;
}

public Song(String artist, String title, String rawData) {
this.artist = artist;
this.title = title;
this.rawData = rawData;
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getArtist() {
return artist;
}

public void setArtist(String artist) {
this.artist = artist;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public String getRawData() {
return rawData;
}

public void setRawData(String rawData) {
this.rawData = rawData;
}
}
11 changes: 11 additions & 0 deletions src/main/java/dev/migwel/sts/exception/SaveFromException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package dev.migwel.sts.exception;

public class SaveFromException extends RuntimeException {
public SaveFromException(String message) {
super(message);
}

public SaveFromException(String message, Throwable cause) {
super(message, cause);
}
}
11 changes: 11 additions & 0 deletions src/main/java/dev/migwel/sts/exception/SaveToException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package dev.migwel.sts.exception;

public class SaveToException extends RuntimeException {
public SaveToException(String message) {
super(message);
}

public SaveToException(String message, Throwable cause) {
super(message, cause);
}
}
7 changes: 7 additions & 0 deletions src/main/java/dev/migwel/sts/service/SaveService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package dev.migwel.sts.service;

import dev.migwel.sts.model.Song;

public interface SaveService {
void save(Song song);
}
5 changes: 4 additions & 1 deletion src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@

spring.datasource.url=jdbc:postgresql://localhost:5432/savethatsong
spring.datasource.username=username
spring.datasource.password=password
spring.datasource.driver-class-name=org.postgresql.Driver
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package dev.migwel.sts.database;

import dev.migwel.sts.model.Song;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.Optional;

@SpringBootTest
class DatabaseSaveServiceManualTest {

private final SongRepository songRepository;
private final DatabaseSaveService saveService;

@Autowired
DatabaseSaveServiceManualTest(SongRepository songRepository, DatabaseSaveService saveService) {
this.songRepository = songRepository;
this.saveService = saveService;
}

@Test
void save_success() {
Song song = new Song("artist", "title", "artist - title");
saveService.save(song);
Optional<dev.migwel.sts.database.entities.Song> persistedSong =
songRepository.findByArtistAndTitle("artist", "title");
Assertions.assertTrue(persistedSong.isPresent());
}
}
43 changes: 43 additions & 0 deletions src/test/java/dev/migwel/sts/database/DatabaseSaveServiceTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package dev.migwel.sts.database;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;

import dev.migwel.sts.database.entities.Converter;
import dev.migwel.sts.exception.SaveToException;
import dev.migwel.sts.model.Song;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.dao.InvalidDataAccessResourceUsageException;

@ExtendWith(MockitoExtension.class)
class DatabaseSaveServiceTest {

@Mock private SongRepository songRepository;
@Spy private Converter converter;
@InjectMocks private DatabaseSaveService saveService;

@Test
void save_success() {
Song song = new Song("artist", "title", "artist - title");
dev.migwel.sts.database.entities.Song entitySong =
new dev.migwel.sts.database.entities.Song(1L, "artist", "title", "artist - title");
doReturn(entitySong).when(songRepository).save(any());
saveService.save(song);
}

@Test
void save_persistFailure() {
doThrow(new InvalidDataAccessResourceUsageException("Could not write song"))
.when(songRepository)
.save(any());
Song song = new Song("artist", "title", "artist - title");
assertThrows(SaveToException.class, () -> saveService.save(song));
}
}
5 changes: 5 additions & 0 deletions src/test/resources/application.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=username
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect