mirror of
https://github.com/imgfloat/server.git
synced 2026-05-08 10:19:35 +00:00
fix: update test to use MarketplaceService.listScripts after method was moved
This commit is contained in:
Executable
+19
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
if [[ ! -d "/home/ges/doc/src/github.com/imgfloat/server" ]]; then
|
||||
echo "Cannot find source directory; Did you move it?"
|
||||
echo "(Looking for "/home/ges/doc/src/github.com/imgfloat/server")"
|
||||
echo 'Cannot force reload with this script - use "direnv reload" manually and then try again'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# rebuild the cache forcefully
|
||||
_nix_direnv_force_reload=1 direnv exec "/home/ges/doc/src/github.com/imgfloat/server" true
|
||||
|
||||
# Update the mtime for .envrc.
|
||||
# This will cause direnv to reload again - but without re-building.
|
||||
touch "/home/ges/doc/src/github.com/imgfloat/server/.envrc"
|
||||
|
||||
# Also update the timestamp of whatever profile_rc we have.
|
||||
# This makes sure that we know we are up to date.
|
||||
touch -r "/home/ges/doc/src/github.com/imgfloat/server/.envrc" "/home/ges/doc/src/github.com/imgfloat/server/.direnv"/*.rc
|
||||
+1
@@ -0,0 +1 @@
|
||||
/nix/store/p2i4wywz453chyxw93dp1iq3f0gbskx0-nix-shell-env
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,7 @@ import dev.kruhlmann.imgfloat.model.api.response.ScriptMarketplaceEntry;
|
||||
import dev.kruhlmann.imgfloat.model.api.request.ScriptMarketplaceImportRequest;
|
||||
import dev.kruhlmann.imgfloat.service.AuthorizationService;
|
||||
import dev.kruhlmann.imgfloat.service.ChannelDirectoryService;
|
||||
import dev.kruhlmann.imgfloat.service.MarketplaceService;
|
||||
import dev.kruhlmann.imgfloat.util.LogSanitizer;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import jakarta.validation.Valid;
|
||||
@@ -34,13 +35,16 @@ import org.springframework.web.server.ResponseStatusException;
|
||||
public class ScriptMarketplaceController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ScriptMarketplaceController.class);
|
||||
private final MarketplaceService marketplaceService;
|
||||
private final ChannelDirectoryService channelDirectoryService;
|
||||
private final AuthorizationService authorizationService;
|
||||
|
||||
public ScriptMarketplaceController(
|
||||
MarketplaceService marketplaceService,
|
||||
ChannelDirectoryService channelDirectoryService,
|
||||
AuthorizationService authorizationService
|
||||
) {
|
||||
this.marketplaceService = marketplaceService;
|
||||
this.channelDirectoryService = channelDirectoryService;
|
||||
this.authorizationService = authorizationService;
|
||||
}
|
||||
@@ -51,15 +55,15 @@ public class ScriptMarketplaceController {
|
||||
OAuth2AuthenticationToken oauthToken
|
||||
) {
|
||||
String sessionUsername = oauthToken == null ? null : OauthSessionUser.from(oauthToken).login();
|
||||
return channelDirectoryService.listMarketplaceScripts(query, sessionUsername);
|
||||
return marketplaceService.listScripts(query, sessionUsername);
|
||||
}
|
||||
|
||||
@GetMapping("/scripts/{scriptId}/logo")
|
||||
public ResponseEntity<byte[]> getMarketplaceLogo(@PathVariable("scriptId") String scriptId) {
|
||||
String logScriptId = LogSanitizer.sanitize(scriptId);
|
||||
LOG.debug("Serving marketplace logo for script {}", logScriptId);
|
||||
return channelDirectoryService
|
||||
.getMarketplaceLogo(scriptId)
|
||||
return marketplaceService
|
||||
.getLogo(scriptId)
|
||||
.map((content) ->
|
||||
ResponseEntity.ok()
|
||||
.header("X-Content-Type-Options", "nosniff")
|
||||
@@ -96,8 +100,8 @@ public class ScriptMarketplaceController {
|
||||
OAuth2AuthenticationToken oauthToken
|
||||
) {
|
||||
String sessionUsername = OauthSessionUser.from(oauthToken).login();
|
||||
return channelDirectoryService
|
||||
.toggleMarketplaceHeart(scriptId, sessionUsername)
|
||||
return marketplaceService
|
||||
.toggleHeart(scriptId, sessionUsername)
|
||||
.map(ResponseEntity::ok)
|
||||
.orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Marketplace script not found"));
|
||||
}
|
||||
|
||||
@@ -483,215 +483,6 @@ public class ChannelDirectoryService {
|
||||
);
|
||||
}
|
||||
|
||||
public List<ScriptMarketplaceEntry> listMarketplaceScripts(String query, String sessionUsername) {
|
||||
String q = normalizeDescription(query);
|
||||
String normalizedQuery = q == null ? null : q.toLowerCase(Locale.ROOT);
|
||||
List<ScriptMarketplaceEntry> entries = new ArrayList<>(marketplaceScriptSeedLoader.listEntriesForQuery(normalizedQuery));
|
||||
List<ScriptAsset> scripts;
|
||||
try {
|
||||
scripts = scriptAssetRepository.findByIsPublicTrue();
|
||||
} catch (DataAccessException ex) {
|
||||
logger.warn("Unable to load marketplace scripts", ex);
|
||||
return applyMarketplaceHearts(entries, sessionUsername);
|
||||
}
|
||||
if (normalizedQuery != null && !normalizedQuery.isBlank()) {
|
||||
scripts =
|
||||
scripts
|
||||
.stream()
|
||||
.filter((script) -> {
|
||||
String name = Optional.ofNullable(script.getName()).orElse("");
|
||||
String description = Optional.ofNullable(script.getDescription()).orElse("");
|
||||
return name.toLowerCase(Locale.ROOT).contains(normalizedQuery) ||
|
||||
description.toLowerCase(Locale.ROOT).contains(normalizedQuery);
|
||||
})
|
||||
.toList();
|
||||
}
|
||||
Map<String, Asset> assets = assetRepository
|
||||
.findAllById(scripts.stream().map(ScriptAsset::getId).toList())
|
||||
.stream()
|
||||
.collect(Collectors.toMap(Asset::getId, (asset) -> asset));
|
||||
|
||||
entries.addAll(
|
||||
scripts
|
||||
.stream()
|
||||
.map((script) -> {
|
||||
Asset asset = assets.get(script.getId());
|
||||
String broadcaster = asset != null ? asset.getBroadcaster() : "";
|
||||
String logoUrl = script.getLogoFileId() == null
|
||||
? null
|
||||
: "/api/marketplace/scripts/" + script.getId() + "/logo";
|
||||
return new ScriptMarketplaceEntry(
|
||||
script.getId(),
|
||||
script.getName(),
|
||||
script.getDescription(),
|
||||
logoUrl,
|
||||
broadcaster,
|
||||
normalizeAllowedDomainsLenient(script.getAllowedDomains()),
|
||||
0,
|
||||
false
|
||||
);
|
||||
})
|
||||
.toList()
|
||||
);
|
||||
|
||||
return applyMarketplaceHearts(entries, sessionUsername);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Optional<ScriptMarketplaceEntry> toggleMarketplaceHeart(String scriptId, String sessionUsername) {
|
||||
if (scriptId == null || scriptId.isBlank() || sessionUsername == null || sessionUsername.isBlank()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
try {
|
||||
if (marketplaceScriptHeartRepository.existsByScriptIdAndUsername(scriptId, sessionUsername)) {
|
||||
marketplaceScriptHeartRepository.deleteByScriptIdAndUsername(scriptId, sessionUsername);
|
||||
} else {
|
||||
marketplaceScriptHeartRepository.save(new MarketplaceScriptHeart(scriptId, sessionUsername));
|
||||
}
|
||||
} catch (DataAccessException ex) {
|
||||
logger.warn("Unable to update marketplace heart for {}", scriptId, ex);
|
||||
return Optional.empty();
|
||||
}
|
||||
return loadMarketplaceEntryWithHearts(scriptId, sessionUsername);
|
||||
}
|
||||
|
||||
private Optional<ScriptMarketplaceEntry> loadMarketplaceEntryWithHearts(String scriptId, String sessionUsername) {
|
||||
Optional<MarketplaceScriptSeedLoader.SeedScript> seedScript = marketplaceScriptSeedLoader.findById(scriptId);
|
||||
if (seedScript.isPresent()) {
|
||||
return Optional.of(applyMarketplaceHearts(seedScript.get().entry(), sessionUsername));
|
||||
}
|
||||
ScriptAsset script;
|
||||
try {
|
||||
script = scriptAssetRepository.findById(scriptId).filter(ScriptAsset::isPublic).orElse(null);
|
||||
} catch (DataAccessException ex) {
|
||||
logger.warn("Unable to load marketplace script {}", scriptId, ex);
|
||||
return Optional.empty();
|
||||
}
|
||||
if (script == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
Asset asset = assetRepository.findById(scriptId).orElse(null);
|
||||
String broadcaster = asset != null ? asset.getBroadcaster() : "";
|
||||
String logoUrl = script.getLogoFileId() == null ? null : "/api/marketplace/scripts/" + script.getId() + "/logo";
|
||||
ScriptMarketplaceEntry entry = new ScriptMarketplaceEntry(
|
||||
script.getId(),
|
||||
script.getName(),
|
||||
script.getDescription(),
|
||||
logoUrl,
|
||||
broadcaster,
|
||||
normalizeAllowedDomainsLenient(script.getAllowedDomains()),
|
||||
0,
|
||||
false
|
||||
);
|
||||
return Optional.of(applyMarketplaceHearts(entry, sessionUsername));
|
||||
}
|
||||
|
||||
private ScriptMarketplaceEntry applyMarketplaceHearts(ScriptMarketplaceEntry entry, String sessionUsername) {
|
||||
if (entry == null || entry.id() == null) {
|
||||
return entry;
|
||||
}
|
||||
long heartCount;
|
||||
boolean hearted;
|
||||
try {
|
||||
heartCount = marketplaceScriptHeartRepository.countByScriptId(entry.id());
|
||||
hearted =
|
||||
sessionUsername != null &&
|
||||
marketplaceScriptHeartRepository.existsByScriptIdAndUsername(entry.id(), sessionUsername);
|
||||
} catch (DataAccessException ex) {
|
||||
logger.warn("Unable to load marketplace heart summary for {}", entry.id(), ex);
|
||||
heartCount = 0;
|
||||
hearted = false;
|
||||
}
|
||||
return new ScriptMarketplaceEntry(
|
||||
entry.id(),
|
||||
entry.name(),
|
||||
entry.description(),
|
||||
entry.logoUrl(),
|
||||
entry.broadcaster(),
|
||||
entry.allowedDomains(),
|
||||
heartCount,
|
||||
hearted
|
||||
);
|
||||
}
|
||||
|
||||
private List<ScriptMarketplaceEntry> applyMarketplaceHearts(
|
||||
List<ScriptMarketplaceEntry> entries,
|
||||
String sessionUsername
|
||||
) {
|
||||
if (entries == null || entries.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
List<String> scriptIds = entries.stream().map(ScriptMarketplaceEntry::id).filter(Objects::nonNull).toList();
|
||||
Map<String, Long> counts = new HashMap<>();
|
||||
Set<String> heartedIds = new HashSet<>();
|
||||
try {
|
||||
if (!scriptIds.isEmpty()) {
|
||||
counts.putAll(
|
||||
marketplaceScriptHeartRepository
|
||||
.countByScriptIds(scriptIds)
|
||||
.stream()
|
||||
.collect(
|
||||
Collectors.toMap(
|
||||
MarketplaceScriptHeartRepository.ScriptHeartCount::getScriptId,
|
||||
MarketplaceScriptHeartRepository.ScriptHeartCount::getHeartCount
|
||||
)
|
||||
)
|
||||
);
|
||||
if (sessionUsername != null && !sessionUsername.isBlank()) {
|
||||
heartedIds.addAll(
|
||||
marketplaceScriptHeartRepository
|
||||
.findByUsernameAndScriptIdIn(sessionUsername, scriptIds)
|
||||
.stream()
|
||||
.map(MarketplaceScriptHeart::getScriptId)
|
||||
.collect(Collectors.toSet())
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (DataAccessException ex) {
|
||||
logger.warn("Unable to load marketplace heart summaries", ex);
|
||||
}
|
||||
Comparator<ScriptMarketplaceEntry> comparator = Comparator
|
||||
.comparingLong((ScriptMarketplaceEntry entry) -> counts.getOrDefault(entry.id(), 0L))
|
||||
.reversed()
|
||||
.thenComparing(ScriptMarketplaceEntry::name, Comparator.nullsLast(String::compareToIgnoreCase));
|
||||
return entries
|
||||
.stream()
|
||||
.map((entry) ->
|
||||
new ScriptMarketplaceEntry(
|
||||
entry.id(),
|
||||
entry.name(),
|
||||
entry.description(),
|
||||
entry.logoUrl(),
|
||||
entry.broadcaster(),
|
||||
entry.allowedDomains(),
|
||||
counts.getOrDefault(entry.id(), 0L),
|
||||
heartedIds.contains(entry.id())
|
||||
)
|
||||
)
|
||||
.sorted(comparator)
|
||||
.toList();
|
||||
}
|
||||
|
||||
public Optional<AssetContent> getMarketplaceLogo(String scriptId) {
|
||||
Optional<MarketplaceScriptSeedLoader.SeedScript> seedScript = marketplaceScriptSeedLoader.findById(scriptId);
|
||||
if (seedScript.isPresent()) {
|
||||
return seedScript.get().loadLogo();
|
||||
}
|
||||
try {
|
||||
return scriptAssetRepository
|
||||
.findById(scriptId)
|
||||
.filter(ScriptAsset::isPublic)
|
||||
.map(ScriptAsset::getLogoFileId)
|
||||
.flatMap(scriptAssetFileRepository::findById)
|
||||
.flatMap((file) ->
|
||||
assetStorageService.loadAssetFileSafely(file.getBroadcaster(), file.getId(), file.getMediaType())
|
||||
);
|
||||
} catch (DataAccessException ex) {
|
||||
logger.warn("Unable to load marketplace logo", ex);
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Optional<AssetView> importMarketplaceScript(String targetBroadcaster, String scriptId, String actor) {
|
||||
Optional<MarketplaceScriptSeedLoader.SeedScript> seedScript = marketplaceScriptSeedLoader.findById(scriptId);
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
package dev.kruhlmann.imgfloat.service;
|
||||
|
||||
import dev.kruhlmann.imgfloat.model.api.response.ScriptMarketplaceEntry;
|
||||
import dev.kruhlmann.imgfloat.model.db.imgfloat.Asset;
|
||||
import dev.kruhlmann.imgfloat.model.db.imgfloat.MarketplaceScriptHeart;
|
||||
import dev.kruhlmann.imgfloat.model.db.imgfloat.ScriptAsset;
|
||||
import dev.kruhlmann.imgfloat.model.db.imgfloat.ScriptAssetFile;
|
||||
import dev.kruhlmann.imgfloat.repository.AssetRepository;
|
||||
import dev.kruhlmann.imgfloat.repository.MarketplaceScriptHeartRepository;
|
||||
import dev.kruhlmann.imgfloat.repository.ScriptAssetFileRepository;
|
||||
import dev.kruhlmann.imgfloat.repository.ScriptAssetRepository;
|
||||
import dev.kruhlmann.imgfloat.service.media.AssetContent;
|
||||
import dev.kruhlmann.imgfloat.util.AllowedDomainNormalizer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Handles the public marketplace: listing scripts, toggling hearts, and
|
||||
* serving logos. The import workflow (which creates new channel assets)
|
||||
* remains in {@link ChannelDirectoryService} because it shares asset-creation
|
||||
* infrastructure with the rest of that class.
|
||||
*/
|
||||
@Service
|
||||
public class MarketplaceService {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MarketplaceService.class);
|
||||
private static final String LOGO_URL_PREFIX = "/api/marketplace/scripts/";
|
||||
|
||||
private final MarketplaceScriptSeedLoader seedLoader;
|
||||
private final ScriptAssetRepository scriptAssetRepository;
|
||||
private final AssetRepository assetRepository;
|
||||
private final ScriptAssetFileRepository scriptAssetFileRepository;
|
||||
private final AssetStorageService assetStorageService;
|
||||
private final MarketplaceScriptHeartRepository heartRepository;
|
||||
|
||||
public MarketplaceService(
|
||||
MarketplaceScriptSeedLoader seedLoader,
|
||||
ScriptAssetRepository scriptAssetRepository,
|
||||
AssetRepository assetRepository,
|
||||
ScriptAssetFileRepository scriptAssetFileRepository,
|
||||
AssetStorageService assetStorageService,
|
||||
MarketplaceScriptHeartRepository heartRepository
|
||||
) {
|
||||
this.seedLoader = seedLoader;
|
||||
this.scriptAssetRepository = scriptAssetRepository;
|
||||
this.assetRepository = assetRepository;
|
||||
this.scriptAssetFileRepository = scriptAssetFileRepository;
|
||||
this.assetStorageService = assetStorageService;
|
||||
this.heartRepository = heartRepository;
|
||||
}
|
||||
|
||||
public List<ScriptMarketplaceEntry> listScripts(String query, String sessionUsername) {
|
||||
String normalized = query == null ? null : query.strip().toLowerCase(Locale.ROOT);
|
||||
if (normalized != null && normalized.isBlank()) {
|
||||
normalized = null;
|
||||
}
|
||||
|
||||
List<ScriptMarketplaceEntry> entries = new ArrayList<>(seedLoader.listEntriesForQuery(normalized));
|
||||
|
||||
List<ScriptAsset> publicScripts;
|
||||
try {
|
||||
publicScripts = scriptAssetRepository.findByIsPublicTrue();
|
||||
} catch (DataAccessException ex) {
|
||||
LOG.warn("Unable to load marketplace scripts from database", ex);
|
||||
return applyHearts(entries, sessionUsername);
|
||||
}
|
||||
|
||||
final String queryFilter = normalized;
|
||||
if (queryFilter != null) {
|
||||
publicScripts = publicScripts.stream()
|
||||
.filter(script -> {
|
||||
String name = Optional.ofNullable(script.getName()).orElse("");
|
||||
String desc = Optional.ofNullable(script.getDescription()).orElse("");
|
||||
return name.toLowerCase(Locale.ROOT).contains(queryFilter)
|
||||
|| desc.toLowerCase(Locale.ROOT).contains(queryFilter);
|
||||
})
|
||||
.toList();
|
||||
}
|
||||
|
||||
Map<String, Asset> assets = assetRepository
|
||||
.findAllById(publicScripts.stream().map(ScriptAsset::getId).toList())
|
||||
.stream()
|
||||
.collect(Collectors.toMap(Asset::getId, a -> a));
|
||||
|
||||
entries.addAll(
|
||||
publicScripts.stream()
|
||||
.map(script -> toEntry(script, assets.get(script.getId())))
|
||||
.toList()
|
||||
);
|
||||
|
||||
return applyHearts(entries, sessionUsername);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Optional<ScriptMarketplaceEntry> toggleHeart(String scriptId, String sessionUsername) {
|
||||
if (scriptId == null || scriptId.isBlank() || sessionUsername == null || sessionUsername.isBlank()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
try {
|
||||
if (heartRepository.existsByScriptIdAndUsername(scriptId, sessionUsername)) {
|
||||
heartRepository.deleteByScriptIdAndUsername(scriptId, sessionUsername);
|
||||
} else {
|
||||
heartRepository.save(new MarketplaceScriptHeart(scriptId, sessionUsername));
|
||||
}
|
||||
} catch (DataAccessException ex) {
|
||||
LOG.warn("Unable to update marketplace heart for {}", scriptId, ex);
|
||||
return Optional.empty();
|
||||
}
|
||||
return loadEntryWithHearts(scriptId, sessionUsername);
|
||||
}
|
||||
|
||||
public Optional<AssetContent> getLogo(String scriptId) {
|
||||
Optional<MarketplaceScriptSeedLoader.SeedScript> seed = seedLoader.findById(scriptId);
|
||||
if (seed.isPresent()) {
|
||||
return seed.get().loadLogo();
|
||||
}
|
||||
try {
|
||||
return scriptAssetRepository.findById(scriptId)
|
||||
.filter(ScriptAsset::isPublic)
|
||||
.map(ScriptAsset::getLogoFileId)
|
||||
.flatMap(scriptAssetFileRepository::findById)
|
||||
.flatMap(file -> assetStorageService.loadAssetFileSafely(
|
||||
file.getBroadcaster(), file.getId(), file.getMediaType()));
|
||||
} catch (DataAccessException ex) {
|
||||
LOG.warn("Unable to load marketplace logo for script {}", scriptId, ex);
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
// ---- helpers ----
|
||||
|
||||
private Optional<ScriptMarketplaceEntry> loadEntryWithHearts(String scriptId, String sessionUsername) {
|
||||
Optional<MarketplaceScriptSeedLoader.SeedScript> seed = seedLoader.findById(scriptId);
|
||||
if (seed.isPresent()) {
|
||||
return Optional.of(applyHearts(seed.get().entry(), sessionUsername));
|
||||
}
|
||||
ScriptAsset script;
|
||||
try {
|
||||
script = scriptAssetRepository.findById(scriptId).filter(ScriptAsset::isPublic).orElse(null);
|
||||
} catch (DataAccessException ex) {
|
||||
LOG.warn("Unable to load marketplace script {}", scriptId, ex);
|
||||
return Optional.empty();
|
||||
}
|
||||
if (script == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
Asset asset = assetRepository.findById(scriptId).orElse(null);
|
||||
return Optional.of(applyHearts(toEntry(script, asset), sessionUsername));
|
||||
}
|
||||
|
||||
private ScriptMarketplaceEntry toEntry(ScriptAsset script, Asset asset) {
|
||||
String broadcaster = asset != null ? asset.getBroadcaster() : "";
|
||||
String logoUrl = script.getLogoFileId() == null
|
||||
? null
|
||||
: LOGO_URL_PREFIX + script.getId() + "/logo";
|
||||
return new ScriptMarketplaceEntry(
|
||||
script.getId(),
|
||||
script.getName(),
|
||||
script.getDescription(),
|
||||
logoUrl,
|
||||
broadcaster,
|
||||
AllowedDomainNormalizer.normalizeLenient(script.getAllowedDomains()),
|
||||
0,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
private ScriptMarketplaceEntry applyHearts(ScriptMarketplaceEntry entry, String sessionUsername) {
|
||||
if (entry == null || entry.id() == null) {
|
||||
return entry;
|
||||
}
|
||||
long count;
|
||||
boolean hearted;
|
||||
try {
|
||||
count = heartRepository.countByScriptId(entry.id());
|
||||
hearted = sessionUsername != null
|
||||
&& heartRepository.existsByScriptIdAndUsername(entry.id(), sessionUsername);
|
||||
} catch (DataAccessException ex) {
|
||||
LOG.warn("Unable to load heart summary for script {}", entry.id(), ex);
|
||||
count = 0;
|
||||
hearted = false;
|
||||
}
|
||||
return withHearts(entry, count, hearted);
|
||||
}
|
||||
|
||||
private List<ScriptMarketplaceEntry> applyHearts(List<ScriptMarketplaceEntry> entries, String sessionUsername) {
|
||||
if (entries == null || entries.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
List<String> ids = entries.stream().map(ScriptMarketplaceEntry::id).filter(Objects::nonNull).toList();
|
||||
Map<String, Long> counts = new HashMap<>();
|
||||
Set<String> heartedIds = new HashSet<>();
|
||||
try {
|
||||
if (!ids.isEmpty()) {
|
||||
counts.putAll(
|
||||
heartRepository.countByScriptIds(ids).stream()
|
||||
.collect(Collectors.toMap(
|
||||
MarketplaceScriptHeartRepository.ScriptHeartCount::getScriptId,
|
||||
MarketplaceScriptHeartRepository.ScriptHeartCount::getHeartCount
|
||||
))
|
||||
);
|
||||
if (sessionUsername != null && !sessionUsername.isBlank()) {
|
||||
heartedIds.addAll(
|
||||
heartRepository.findByUsernameAndScriptIdIn(sessionUsername, ids).stream()
|
||||
.map(MarketplaceScriptHeart::getScriptId)
|
||||
.collect(Collectors.toSet())
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (DataAccessException ex) {
|
||||
LOG.warn("Unable to load bulk heart summaries", ex);
|
||||
}
|
||||
|
||||
Comparator<ScriptMarketplaceEntry> byHeartsThenName = Comparator
|
||||
.comparingLong((ScriptMarketplaceEntry e) -> counts.getOrDefault(e.id(), 0L))
|
||||
.reversed()
|
||||
.thenComparing(ScriptMarketplaceEntry::name, Comparator.nullsLast(String::compareToIgnoreCase));
|
||||
|
||||
return entries.stream()
|
||||
.map(e -> withHearts(e, counts.getOrDefault(e.id(), 0L), heartedIds.contains(e.id())))
|
||||
.sorted(byHeartsThenName)
|
||||
.toList();
|
||||
}
|
||||
|
||||
private static ScriptMarketplaceEntry withHearts(ScriptMarketplaceEntry e, long count, boolean hearted) {
|
||||
return new ScriptMarketplaceEntry(
|
||||
e.id(), e.name(), e.description(), e.logoUrl(), e.broadcaster(),
|
||||
e.allowedDomains(), count, hearted
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -58,6 +58,7 @@ import org.springframework.web.server.ResponseStatusException;
|
||||
class ChannelDirectoryServiceTest {
|
||||
|
||||
private ChannelDirectoryService service;
|
||||
private dev.kruhlmann.imgfloat.service.MarketplaceService marketplaceService;
|
||||
private SimpMessagingTemplate messagingTemplate;
|
||||
private ChannelRepository channelRepository;
|
||||
private AssetRepository assetRepository;
|
||||
@@ -126,6 +127,14 @@ class ChannelDirectoryServiceTest {
|
||||
marketplaceScriptSeedLoader,
|
||||
auditLogService
|
||||
);
|
||||
marketplaceService = new dev.kruhlmann.imgfloat.service.MarketplaceService(
|
||||
marketplaceScriptSeedLoader,
|
||||
scriptAssetRepository,
|
||||
assetRepository,
|
||||
scriptAssetFileRepository,
|
||||
assetStorageService,
|
||||
marketplaceScriptHeartRepository
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -215,7 +224,7 @@ class ChannelDirectoryServiceTest {
|
||||
void includesDefaultMarketplaceScript() {
|
||||
when(scriptAssetRepository.findByIsPublicTrue()).thenReturn(List.of());
|
||||
|
||||
List<dev.kruhlmann.imgfloat.model.api.response.ScriptMarketplaceEntry> entries = service.listMarketplaceScripts(null, null);
|
||||
List<dev.kruhlmann.imgfloat.model.api.response.ScriptMarketplaceEntry> entries = marketplaceService.listScripts(null, null);
|
||||
|
||||
assertThat(entries)
|
||||
.anyMatch((entry) -> "rotating-logo".equals(entry.id()));
|
||||
|
||||
Reference in New Issue
Block a user