Fix redis, mask kodik id
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
package com.backend.unifier.title.model;
|
||||
|
||||
public enum ContentProviderSource {
|
||||
KODIK
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.backend.unifier.title.model;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class IdentifierPointer {
|
||||
private UUID key;
|
||||
private ContentIdentifier identifier;
|
||||
|
||||
public IdentifierPointer(UUID key, ContentIdentifier identifier) {
|
||||
this.key = key;
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
||||
IdentifierPointer() {
|
||||
}
|
||||
|
||||
public UUID getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public ContentIdentifier getIdentifier() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,15 +2,15 @@ package com.backend.unifier.title.resource;
|
||||
|
||||
import com.backend.unifier.title.model.ContentDetailEntry;
|
||||
|
||||
import io.smallrye.openapi.internal.models.media.Content;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
|
||||
@Path("/")
|
||||
public class DetailResource {
|
||||
@GET
|
||||
@Path("/detail")
|
||||
public ContentDetailEntry detail(String id) {
|
||||
public ContentDetailEntry detail(@QueryParam("id") String id) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import com.backend.unifier.title.api.KodikSearchService;
|
||||
import com.backend.unifier.title.model.SearchEntries;
|
||||
import com.backend.unifier.title.service.KodikResponseConvertService;
|
||||
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
@@ -24,14 +23,6 @@ public class SearchResource {
|
||||
|
||||
@GET
|
||||
@Path("/search")
|
||||
public Uni<SearchEntries> search(@QueryParam("title") String title) {
|
||||
return kodikSearchService.searchAsync(title)
|
||||
.onItem().ifNotNull()
|
||||
.transformToUni(response -> kodikConvertService.convertAsync(response));
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/search-fast")
|
||||
public SearchEntries searchFast(@QueryParam("title") String title) {
|
||||
var result = kodikSearchService.search(title);
|
||||
return kodikConvertService.convert(result);
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.backend.unifier.title.service;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import com.backend.unifier.title.model.ContentIdentifier;
|
||||
import com.backend.unifier.title.model.ContentProviderSource;
|
||||
|
||||
import io.quarkus.redis.datasource.RedisDataSource;
|
||||
import io.quarkus.redis.datasource.value.ValueCommands;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
|
||||
@ApplicationScoped
|
||||
public class IdentifierMaskService {
|
||||
private ValueCommands<String, ContentIdentifier> countCommands;
|
||||
|
||||
public IdentifierMaskService(RedisDataSource ds) {
|
||||
countCommands = ds.value(ContentIdentifier.class);
|
||||
}
|
||||
|
||||
public UUID createMask(String id, ContentProviderSource source) {
|
||||
ContentIdentifier contentIdentifier = new ContentIdentifier(source.name(), id);
|
||||
UUID pointerID = UUID.randomUUID();
|
||||
countCommands.set(pointerID.toString(), contentIdentifier);
|
||||
return pointerID;
|
||||
}
|
||||
|
||||
public ContentIdentifier realIdentifier(UUID id) {
|
||||
return countCommands.get(id.toString());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package com.backend.unifier.title.service;
|
||||
|
||||
import com.backend.unifier.title.model.ContentIdentifier;
|
||||
|
||||
import io.quarkus.redis.datasource.RedisDataSource;
|
||||
import io.quarkus.redis.datasource.value.ValueCommands;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
|
||||
@ApplicationScoped
|
||||
public class IdentifierPointerService {
|
||||
private ValueCommands<String, ContentIdentifier> countCommands;
|
||||
|
||||
public IdentifierPointerService(RedisDataSource ds) {
|
||||
countCommands = ds.value(ContentIdentifier.class);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,60 +1,49 @@
|
||||
package com.backend.unifier.title.service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import com.backend.metadata.kodik.api.model.KodikResponse;
|
||||
import com.backend.unifier.title.mapper.KodikResponseMapper;
|
||||
import com.backend.unifier.title.model.ContentSearchEntry;
|
||||
import com.backend.unifier.title.model.ContentProviderSource;
|
||||
import com.backend.unifier.title.model.SearchEntries;
|
||||
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
|
||||
@ApplicationScoped
|
||||
public class KodikResponseConvertService {
|
||||
|
||||
private final KodikResponseMapper responseMapper = Mappers.getMapper(KodikResponseMapper.class);
|
||||
private final PosterUrlValidator posterUrlValidator;
|
||||
private final PosterUrlNormalizer posterUrlNormalizer;
|
||||
private final IdentifierMaskService identifierMaskService;
|
||||
|
||||
public KodikResponseConvertService(PosterUrlValidator posterUrlValidator, PosterUrlNormalizer posterUrlNormalizer) {
|
||||
this.posterUrlValidator = posterUrlValidator;
|
||||
public KodikResponseConvertService(PosterUrlNormalizer posterUrlNormalizer,
|
||||
IdentifierMaskService identifierMaskService) {
|
||||
this.posterUrlNormalizer = posterUrlNormalizer;
|
||||
this.identifierMaskService = identifierMaskService;
|
||||
}
|
||||
|
||||
public SearchEntries convert(KodikResponse response) {
|
||||
return convertSimple(response);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Uni<SearchEntries> convertAsync(KodikResponse response) {
|
||||
if (response == null || response.results == null) {
|
||||
return Uni.createFrom().item(new SearchEntries(List.of()));
|
||||
}
|
||||
|
||||
List<Uni<ContentSearchEntry>> entries = convertSimple(response).result()
|
||||
.stream()
|
||||
.map(this::resolveEntryPosters)
|
||||
.toList();
|
||||
|
||||
if (entries.isEmpty()) {
|
||||
return Uni.createFrom().item(new SearchEntries(List.of()));
|
||||
}
|
||||
|
||||
return Uni.combine().all().unis(entries)
|
||||
.with(list -> new SearchEntries((List<ContentSearchEntry>) list));
|
||||
}
|
||||
|
||||
private Uni<ContentSearchEntry> resolveEntryPosters(ContentSearchEntry entry) {
|
||||
return posterUrlValidator.resolvePosters(entry.posterURLs())
|
||||
.map(entry::withPosterURLs)
|
||||
.onFailure().recoverWithItem(entry);
|
||||
}
|
||||
|
||||
private SearchEntries convertSimple(KodikResponse response) {
|
||||
return normalizeUrls(responseMapper.toSearchEntries(response));
|
||||
SearchEntries withNormalizedUrls = normalizeUrls(responseMapper.toSearchEntries(response));
|
||||
SearchEntries result = maskIDs(withNormalizedUrls);
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
private SearchEntries maskIDs(SearchEntries response) {
|
||||
var normalizedResults = response.result().stream()
|
||||
.map(r -> {
|
||||
String originalID = r.id();
|
||||
UUID maskedID = identifierMaskService.createMask(originalID, ContentProviderSource.KODIK);
|
||||
return r.withId(maskedID.toString());
|
||||
})
|
||||
.toList();
|
||||
return response.withResult(normalizedResults);
|
||||
}
|
||||
|
||||
private SearchEntries normalizeUrls(SearchEntries response) {
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
package com.backend.unifier.title.service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import io.vertx.core.Vertx;
|
||||
import io.vertx.ext.web.client.WebClient;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
|
||||
@ApplicationScoped
|
||||
public class PosterUrlValidator {
|
||||
|
||||
@ConfigProperty(name = "poster.url.timeoutMs", defaultValue = "3000")
|
||||
long timeoutMs;
|
||||
|
||||
@Inject
|
||||
Vertx vertx;
|
||||
|
||||
private WebClient webClient;
|
||||
|
||||
@PostConstruct
|
||||
void init() {
|
||||
webClient = WebClient.create(vertx);
|
||||
}
|
||||
|
||||
public Uni<List<String>> resolvePosters(List<String> urls) {
|
||||
if (urls == null || urls.isEmpty()) {
|
||||
return Uni.createFrom().item(List.of());
|
||||
}
|
||||
|
||||
return findFirstReachable(urls, 0)
|
||||
.map(firstIndex -> reorderToFront(urls, firstIndex));
|
||||
}
|
||||
|
||||
private Uni<Integer> findFirstReachable(List<String> urls, int index) {
|
||||
if (index >= urls.size()) {
|
||||
return Uni.createFrom().item(-1);
|
||||
}
|
||||
return isReachable(urls.get(index))
|
||||
.flatMap(ok -> ok ? Uni.createFrom().item(index) : findFirstReachable(urls, index + 1));
|
||||
}
|
||||
|
||||
private List<String> reorderToFront(List<String> urls, int firstIndex) {
|
||||
if (firstIndex <= 0)
|
||||
return urls;
|
||||
List<String> reordered = new ArrayList<>(urls);
|
||||
reordered.add(0, reordered.remove(firstIndex));
|
||||
return reordered;
|
||||
}
|
||||
|
||||
private Uni<Boolean> isReachable(String url) {
|
||||
if (url == null || url.isEmpty()) {
|
||||
return Uni.createFrom().item(false);
|
||||
}
|
||||
return Uni.createFrom().completionStage(
|
||||
webClient.headAbs(url).timeout(timeoutMs).send().toCompletionStage())
|
||||
.map(response -> response.statusCode() >= 200 && response.statusCode() < 400)
|
||||
.onFailure().recoverWithItem(false);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user