mirror of
https://github.com/imgfloat/server.git
synced 2026-02-05 03:39:26 +00:00
Add sqlite
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,3 +5,4 @@
|
|||||||
local/
|
local/
|
||||||
*.log
|
*.log
|
||||||
.env
|
.env
|
||||||
|
*.db
|
||||||
|
|||||||
22
pom.xml
22
pom.xml
@@ -13,6 +13,7 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<java.version>17</java.version>
|
<java.version>17</java.version>
|
||||||
<spring.boot.version>3.2.5</spring.boot.version>
|
<spring.boot.version>3.2.5</spring.boot.version>
|
||||||
|
<hibernate.version>6.4.4.Final</hibernate.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
@@ -36,6 +37,10 @@
|
|||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||||
@@ -53,6 +58,23 @@
|
|||||||
<artifactId>spring-boot-starter-validation</artifactId>
|
<artifactId>spring-boot-starter-validation</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.session</groupId>
|
||||||
|
<artifactId>spring-session-jdbc</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.xerial</groupId>
|
||||||
|
<artifactId>sqlite-jdbc</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.hibernate.orm</groupId>
|
||||||
|
<artifactId>hibernate-community-dialects</artifactId>
|
||||||
|
<version>${hibernate.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.webjars</groupId>
|
<groupId>org.webjars</groupId>
|
||||||
<artifactId>sockjs-client</artifactId>
|
<artifactId>sockjs-client</artifactId>
|
||||||
|
|||||||
@@ -1,10 +1,25 @@
|
|||||||
package com.imgfloat.app.model;
|
package com.imgfloat.app.model;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.PrePersist;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "assets")
|
||||||
public class Asset {
|
public class Asset {
|
||||||
private final String id;
|
@Id
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private String broadcaster;
|
||||||
|
|
||||||
|
@Column(columnDefinition = "TEXT")
|
||||||
private String url;
|
private String url;
|
||||||
private double x;
|
private double x;
|
||||||
private double y;
|
private double y;
|
||||||
@@ -12,10 +27,14 @@ public class Asset {
|
|||||||
private double height;
|
private double height;
|
||||||
private double rotation;
|
private double rotation;
|
||||||
private boolean hidden;
|
private boolean hidden;
|
||||||
private final Instant createdAt;
|
private Instant createdAt;
|
||||||
|
|
||||||
public Asset(String url, double width, double height) {
|
public Asset() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Asset(String broadcaster, String url, double width, double height) {
|
||||||
this.id = UUID.randomUUID().toString();
|
this.id = UUID.randomUUID().toString();
|
||||||
|
this.broadcaster = normalize(broadcaster);
|
||||||
this.url = url;
|
this.url = url;
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
@@ -26,10 +45,29 @@ public class Asset {
|
|||||||
this.createdAt = Instant.now();
|
this.createdAt = Instant.now();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PrePersist
|
||||||
|
public void prepare() {
|
||||||
|
if (this.id == null) {
|
||||||
|
this.id = UUID.randomUUID().toString();
|
||||||
|
}
|
||||||
|
if (this.createdAt == null) {
|
||||||
|
this.createdAt = Instant.now();
|
||||||
|
}
|
||||||
|
this.broadcaster = normalize(broadcaster);
|
||||||
|
}
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getBroadcaster() {
|
||||||
|
return broadcaster;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBroadcaster(String broadcaster) {
|
||||||
|
this.broadcaster = normalize(broadcaster);
|
||||||
|
}
|
||||||
|
|
||||||
public String getUrl() {
|
public String getUrl() {
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
@@ -89,4 +127,12 @@ public class Asset {
|
|||||||
public Instant getCreatedAt() {
|
public Instant getCreatedAt() {
|
||||||
return createdAt;
|
return createdAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setCreatedAt(Instant createdAt) {
|
||||||
|
this.createdAt = createdAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String normalize(String value) {
|
||||||
|
return value == null ? null : value.toLowerCase(Locale.ROOT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,38 @@
|
|||||||
package com.imgfloat.app.model;
|
package com.imgfloat.app.model;
|
||||||
|
|
||||||
import java.util.Collections;
|
import jakarta.persistence.CollectionTable;
|
||||||
import java.util.Map;
|
import jakarta.persistence.Column;
|
||||||
import java.util.Set;
|
import jakarta.persistence.ElementCollection;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.FetchType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.PrePersist;
|
||||||
|
import jakarta.persistence.PreUpdate;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "channels")
|
||||||
public class Channel {
|
public class Channel {
|
||||||
private final String broadcaster;
|
@Id
|
||||||
private final Set<String> admins;
|
private String broadcaster;
|
||||||
private final Map<String, Asset> assets;
|
|
||||||
|
@ElementCollection(fetch = FetchType.EAGER)
|
||||||
|
@CollectionTable(name = "channel_admins", joinColumns = @JoinColumn(name = "channel_id"))
|
||||||
|
@Column(name = "admin_username")
|
||||||
|
private Set<String> admins = new HashSet<>();
|
||||||
|
|
||||||
|
public Channel() {
|
||||||
|
}
|
||||||
|
|
||||||
public Channel(String broadcaster) {
|
public Channel(String broadcaster) {
|
||||||
this.broadcaster = broadcaster.toLowerCase();
|
this.broadcaster = normalize(broadcaster);
|
||||||
this.admins = ConcurrentHashMap.newKeySet();
|
|
||||||
this.assets = new ConcurrentHashMap<>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getBroadcaster() {
|
public String getBroadcaster() {
|
||||||
@@ -24,15 +43,24 @@ public class Channel {
|
|||||||
return Collections.unmodifiableSet(admins);
|
return Collections.unmodifiableSet(admins);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Asset> getAssets() {
|
|
||||||
return assets;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean addAdmin(String username) {
|
public boolean addAdmin(String username) {
|
||||||
return admins.add(username.toLowerCase());
|
return admins.add(normalize(username));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean removeAdmin(String username) {
|
public boolean removeAdmin(String username) {
|
||||||
return admins.remove(username.toLowerCase());
|
return admins.remove(normalize(username));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PrePersist
|
||||||
|
@PreUpdate
|
||||||
|
public void normalizeFields() {
|
||||||
|
this.broadcaster = normalize(broadcaster);
|
||||||
|
this.admins = admins.stream()
|
||||||
|
.map(Channel::normalize)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String normalize(String value) {
|
||||||
|
return value == null ? null : value.toLowerCase(Locale.ROOT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.imgfloat.app.repository;
|
||||||
|
|
||||||
|
import com.imgfloat.app.model.Asset;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface AssetRepository extends JpaRepository<Asset, String> {
|
||||||
|
List<Asset> findByBroadcaster(String broadcaster);
|
||||||
|
List<Asset> findByBroadcasterAndHiddenFalse(String broadcaster);
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.imgfloat.app.repository;
|
||||||
|
|
||||||
|
import com.imgfloat.app.model.Channel;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
public interface ChannelRepository extends JpaRepository<Channel, String> {
|
||||||
|
}
|
||||||
@@ -5,6 +5,8 @@ import com.imgfloat.app.model.AssetEvent;
|
|||||||
import com.imgfloat.app.model.Channel;
|
import com.imgfloat.app.model.Channel;
|
||||||
import com.imgfloat.app.model.TransformRequest;
|
import com.imgfloat.app.model.TransformRequest;
|
||||||
import com.imgfloat.app.model.VisibilityRequest;
|
import com.imgfloat.app.model.VisibilityRequest;
|
||||||
|
import com.imgfloat.app.repository.AssetRepository;
|
||||||
|
import com.imgfloat.app.repository.ChannelRepository;
|
||||||
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
@@ -15,28 +17,34 @@ import java.io.IOException;
|
|||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class ChannelDirectoryService {
|
public class ChannelDirectoryService {
|
||||||
private final Map<String, Channel> channels = new ConcurrentHashMap<>();
|
private final ChannelRepository channelRepository;
|
||||||
|
private final AssetRepository assetRepository;
|
||||||
private final SimpMessagingTemplate messagingTemplate;
|
private final SimpMessagingTemplate messagingTemplate;
|
||||||
|
|
||||||
public ChannelDirectoryService(SimpMessagingTemplate messagingTemplate) {
|
public ChannelDirectoryService(ChannelRepository channelRepository,
|
||||||
|
AssetRepository assetRepository,
|
||||||
|
SimpMessagingTemplate messagingTemplate) {
|
||||||
|
this.channelRepository = channelRepository;
|
||||||
|
this.assetRepository = assetRepository;
|
||||||
this.messagingTemplate = messagingTemplate;
|
this.messagingTemplate = messagingTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Channel getOrCreateChannel(String broadcaster) {
|
public Channel getOrCreateChannel(String broadcaster) {
|
||||||
return channels.computeIfAbsent(broadcaster.toLowerCase(), Channel::new);
|
String normalized = normalize(broadcaster);
|
||||||
|
return channelRepository.findById(normalized)
|
||||||
|
.orElseGet(() -> channelRepository.save(new Channel(normalized)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean addAdmin(String broadcaster, String username) {
|
public boolean addAdmin(String broadcaster, String username) {
|
||||||
Channel channel = getOrCreateChannel(broadcaster);
|
Channel channel = getOrCreateChannel(broadcaster);
|
||||||
boolean added = channel.addAdmin(username);
|
boolean added = channel.addAdmin(username);
|
||||||
if (added) {
|
if (added) {
|
||||||
|
channelRepository.save(channel);
|
||||||
messagingTemplate.convertAndSend(topicFor(broadcaster), "Admin added: " + username);
|
messagingTemplate.convertAndSend(topicFor(broadcaster), "Admin added: " + username);
|
||||||
}
|
}
|
||||||
return added;
|
return added;
|
||||||
@@ -46,19 +54,18 @@ public class ChannelDirectoryService {
|
|||||||
Channel channel = getOrCreateChannel(broadcaster);
|
Channel channel = getOrCreateChannel(broadcaster);
|
||||||
boolean removed = channel.removeAdmin(username);
|
boolean removed = channel.removeAdmin(username);
|
||||||
if (removed) {
|
if (removed) {
|
||||||
|
channelRepository.save(channel);
|
||||||
messagingTemplate.convertAndSend(topicFor(broadcaster), "Admin removed: " + username);
|
messagingTemplate.convertAndSend(topicFor(broadcaster), "Admin removed: " + username);
|
||||||
}
|
}
|
||||||
return removed;
|
return removed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<Asset> getAssetsForAdmin(String broadcaster) {
|
public Collection<Asset> getAssetsForAdmin(String broadcaster) {
|
||||||
return getOrCreateChannel(broadcaster).getAssets().values();
|
return assetRepository.findByBroadcaster(normalize(broadcaster));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<Asset> getVisibleAssets(String broadcaster) {
|
public Collection<Asset> getVisibleAssets(String broadcaster) {
|
||||||
return getOrCreateChannel(broadcaster).getAssets().values().stream()
|
return assetRepository.findByBroadcasterAndHiddenFalse(normalize(broadcaster));
|
||||||
.filter(asset -> !asset.isHidden())
|
|
||||||
.toList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Asset> createAsset(String broadcaster, MultipartFile file) throws IOException {
|
public Optional<Asset> createAsset(String broadcaster, MultipartFile file) throws IOException {
|
||||||
@@ -70,46 +77,50 @@ public class ChannelDirectoryService {
|
|||||||
}
|
}
|
||||||
String contentType = Optional.ofNullable(file.getContentType()).orElse("application/octet-stream");
|
String contentType = Optional.ofNullable(file.getContentType()).orElse("application/octet-stream");
|
||||||
String dataUrl = "data:" + contentType + ";base64," + Base64.getEncoder().encodeToString(bytes);
|
String dataUrl = "data:" + contentType + ";base64," + Base64.getEncoder().encodeToString(bytes);
|
||||||
Asset asset = new Asset(dataUrl, image.getWidth(), image.getHeight());
|
Asset asset = new Asset(channel.getBroadcaster(), dataUrl, image.getWidth(), image.getHeight());
|
||||||
channel.getAssets().put(asset.getId(), asset);
|
assetRepository.save(asset);
|
||||||
messagingTemplate.convertAndSend(topicFor(broadcaster), AssetEvent.created(broadcaster, asset));
|
messagingTemplate.convertAndSend(topicFor(broadcaster), AssetEvent.created(broadcaster, asset));
|
||||||
return Optional.of(asset);
|
return Optional.of(asset);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Asset> updateTransform(String broadcaster, String assetId, TransformRequest request) {
|
public Optional<Asset> updateTransform(String broadcaster, String assetId, TransformRequest request) {
|
||||||
Channel channel = getOrCreateChannel(broadcaster);
|
String normalized = normalize(broadcaster);
|
||||||
Asset asset = channel.getAssets().get(assetId);
|
return assetRepository.findById(assetId)
|
||||||
if (asset == null) {
|
.filter(asset -> normalized.equals(asset.getBroadcaster()))
|
||||||
return Optional.empty();
|
.map(asset -> {
|
||||||
}
|
|
||||||
asset.setX(request.getX());
|
asset.setX(request.getX());
|
||||||
asset.setY(request.getY());
|
asset.setY(request.getY());
|
||||||
asset.setWidth(request.getWidth());
|
asset.setWidth(request.getWidth());
|
||||||
asset.setHeight(request.getHeight());
|
asset.setHeight(request.getHeight());
|
||||||
asset.setRotation(request.getRotation());
|
asset.setRotation(request.getRotation());
|
||||||
|
assetRepository.save(asset);
|
||||||
messagingTemplate.convertAndSend(topicFor(broadcaster), AssetEvent.updated(broadcaster, asset));
|
messagingTemplate.convertAndSend(topicFor(broadcaster), AssetEvent.updated(broadcaster, asset));
|
||||||
return Optional.of(asset);
|
return asset;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Asset> updateVisibility(String broadcaster, String assetId, VisibilityRequest request) {
|
public Optional<Asset> updateVisibility(String broadcaster, String assetId, VisibilityRequest request) {
|
||||||
Channel channel = getOrCreateChannel(broadcaster);
|
String normalized = normalize(broadcaster);
|
||||||
Asset asset = channel.getAssets().get(assetId);
|
return assetRepository.findById(assetId)
|
||||||
if (asset == null) {
|
.filter(asset -> normalized.equals(asset.getBroadcaster()))
|
||||||
return Optional.empty();
|
.map(asset -> {
|
||||||
}
|
|
||||||
asset.setHidden(request.isHidden());
|
asset.setHidden(request.isHidden());
|
||||||
|
assetRepository.save(asset);
|
||||||
messagingTemplate.convertAndSend(topicFor(broadcaster), AssetEvent.visibility(broadcaster, asset));
|
messagingTemplate.convertAndSend(topicFor(broadcaster), AssetEvent.visibility(broadcaster, asset));
|
||||||
return Optional.of(asset);
|
return asset;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean deleteAsset(String broadcaster, String assetId) {
|
public boolean deleteAsset(String broadcaster, String assetId) {
|
||||||
Channel channel = getOrCreateChannel(broadcaster);
|
String normalized = normalize(broadcaster);
|
||||||
Asset removed = channel.getAssets().remove(assetId);
|
return assetRepository.findById(assetId)
|
||||||
if (removed != null) {
|
.filter(asset -> normalized.equals(asset.getBroadcaster()))
|
||||||
|
.map(asset -> {
|
||||||
|
assetRepository.delete(asset);
|
||||||
messagingTemplate.convertAndSend(topicFor(broadcaster), AssetEvent.deleted(broadcaster, assetId));
|
messagingTemplate.convertAndSend(topicFor(broadcaster), AssetEvent.deleted(broadcaster, assetId));
|
||||||
return true;
|
return true;
|
||||||
}
|
})
|
||||||
return false;
|
.orElse(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isBroadcaster(String broadcaster, String username) {
|
public boolean isBroadcaster(String broadcaster, String username) {
|
||||||
@@ -117,8 +128,10 @@ public class ChannelDirectoryService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAdmin(String broadcaster, String username) {
|
public boolean isAdmin(String broadcaster, String username) {
|
||||||
Channel channel = channels.get(broadcaster.toLowerCase());
|
return channelRepository.findById(normalize(broadcaster))
|
||||||
return channel != null && channel.getAdmins().contains(username.toLowerCase());
|
.map(Channel::getAdmins)
|
||||||
|
.map(admins -> admins.contains(normalize(username)))
|
||||||
|
.orElse(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<String> adminChannelsFor(String username) {
|
public Collection<String> adminChannelsFor(String username) {
|
||||||
@@ -126,7 +139,7 @@ public class ChannelDirectoryService {
|
|||||||
return List.of();
|
return List.of();
|
||||||
}
|
}
|
||||||
String login = username.toLowerCase();
|
String login = username.toLowerCase();
|
||||||
return channels.values().stream()
|
return channelRepository.findAll().stream()
|
||||||
.filter(channel -> channel.getAdmins().contains(login))
|
.filter(channel -> channel.getAdmins().contains(login))
|
||||||
.map(Channel::getBroadcaster)
|
.map(Channel::getBroadcaster)
|
||||||
.toList();
|
.toList();
|
||||||
@@ -135,4 +148,8 @@ public class ChannelDirectoryService {
|
|||||||
private String topicFor(String broadcaster) {
|
private String topicFor(String broadcaster) {
|
||||||
return "/topic/channel/" + broadcaster.toLowerCase();
|
return "/topic/channel/" + broadcaster.toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String normalize(String value) {
|
||||||
|
return value == null ? null : value.toLowerCase();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,18 @@ spring:
|
|||||||
name: imgfloat
|
name: imgfloat
|
||||||
thymeleaf:
|
thymeleaf:
|
||||||
cache: false
|
cache: false
|
||||||
|
datasource:
|
||||||
|
url: jdbc:sqlite:imgfloat.db
|
||||||
|
driver-class-name: org.sqlite.JDBC
|
||||||
|
jpa:
|
||||||
|
hibernate:
|
||||||
|
ddl-auto: update
|
||||||
|
database-platform: org.hibernate.community.dialect.SQLiteDialect
|
||||||
|
session:
|
||||||
|
store-type: jdbc
|
||||||
|
jdbc:
|
||||||
|
initialize-schema: always
|
||||||
|
platform: sqlite
|
||||||
security:
|
security:
|
||||||
oauth2:
|
oauth2:
|
||||||
client:
|
client:
|
||||||
|
|||||||
@@ -2,6 +2,10 @@ package com.imgfloat.app;
|
|||||||
|
|
||||||
import com.imgfloat.app.model.TransformRequest;
|
import com.imgfloat.app.model.TransformRequest;
|
||||||
import com.imgfloat.app.model.VisibilityRequest;
|
import com.imgfloat.app.model.VisibilityRequest;
|
||||||
|
import com.imgfloat.app.model.Asset;
|
||||||
|
import com.imgfloat.app.model.Channel;
|
||||||
|
import com.imgfloat.app.repository.AssetRepository;
|
||||||
|
import com.imgfloat.app.repository.ChannelRepository;
|
||||||
import com.imgfloat.app.service.ChannelDirectoryService;
|
import com.imgfloat.app.service.ChannelDirectoryService;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@@ -12,22 +16,35 @@ import org.springframework.mock.web.MockMultipartFile;
|
|||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.Mockito.doAnswer;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
class ChannelDirectoryServiceTest {
|
class ChannelDirectoryServiceTest {
|
||||||
private ChannelDirectoryService service;
|
private ChannelDirectoryService service;
|
||||||
private SimpMessagingTemplate messagingTemplate;
|
private SimpMessagingTemplate messagingTemplate;
|
||||||
|
private ChannelRepository channelRepository;
|
||||||
|
private AssetRepository assetRepository;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setup() {
|
void setup() {
|
||||||
messagingTemplate = mock(SimpMessagingTemplate.class);
|
messagingTemplate = mock(SimpMessagingTemplate.class);
|
||||||
service = new ChannelDirectoryService(messagingTemplate);
|
channelRepository = mock(ChannelRepository.class);
|
||||||
|
assetRepository = mock(AssetRepository.class);
|
||||||
|
setupInMemoryPersistence();
|
||||||
|
service = new ChannelDirectoryService(channelRepository, assetRepository, messagingTemplate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -66,4 +83,42 @@ class ChannelDirectoryServiceTest {
|
|||||||
ImageIO.write(image, "png", out);
|
ImageIO.write(image, "png", out);
|
||||||
return out.toByteArray();
|
return out.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setupInMemoryPersistence() {
|
||||||
|
Map<String, Channel> channels = new ConcurrentHashMap<>();
|
||||||
|
Map<String, Asset> assets = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
when(channelRepository.findById(anyString()))
|
||||||
|
.thenAnswer(invocation -> Optional.ofNullable(channels.get(invocation.getArgument(0))));
|
||||||
|
when(channelRepository.save(any(Channel.class)))
|
||||||
|
.thenAnswer(invocation -> {
|
||||||
|
Channel channel = invocation.getArgument(0);
|
||||||
|
channels.put(channel.getBroadcaster(), channel);
|
||||||
|
return channel;
|
||||||
|
});
|
||||||
|
when(channelRepository.findAll())
|
||||||
|
.thenAnswer(invocation -> List.copyOf(channels.values()));
|
||||||
|
|
||||||
|
when(assetRepository.save(any(Asset.class)))
|
||||||
|
.thenAnswer(invocation -> {
|
||||||
|
Asset asset = invocation.getArgument(0);
|
||||||
|
assets.put(asset.getId(), asset);
|
||||||
|
return asset;
|
||||||
|
});
|
||||||
|
when(assetRepository.findById(anyString()))
|
||||||
|
.thenAnswer(invocation -> Optional.ofNullable(assets.get(invocation.getArgument(0))));
|
||||||
|
when(assetRepository.findByBroadcaster(anyString()))
|
||||||
|
.thenAnswer(invocation -> filterAssetsByBroadcaster(assets.values(), invocation.getArgument(0), false));
|
||||||
|
when(assetRepository.findByBroadcasterAndHiddenFalse(anyString()))
|
||||||
|
.thenAnswer(invocation -> filterAssetsByBroadcaster(assets.values(), invocation.getArgument(0), true));
|
||||||
|
doAnswer(invocation -> assets.remove(invocation.getArgument(0, Asset.class).getId()))
|
||||||
|
.when(assetRepository).delete(any(Asset.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Asset> filterAssetsByBroadcaster(Collection<Asset> assets, String broadcaster, boolean onlyVisible) {
|
||||||
|
return assets.stream()
|
||||||
|
.filter(asset -> asset.getBroadcaster().equalsIgnoreCase(broadcaster))
|
||||||
|
.filter(asset -> !onlyVisible || !asset.isHidden())
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user