Skip to content

Commit

Permalink
Merge pull request #210 from yrodiere/reference
Browse files Browse the repository at this point in the history
Add endpoints listing reference data
  • Loading branch information
yrodiere authored Mar 28, 2024
2 parents 35527a1 + 6a43947 commit 4433e54
Show file tree
Hide file tree
Showing 14 changed files with 224 additions and 25 deletions.
4 changes: 4 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-cache</artifactId>
</dependency>
<dependency>
<groupId>io.quarkiverse.helm</groupId>
<artifactId>quarkus-helm</artifactId>
Expand Down
23 changes: 23 additions & 0 deletions src/main/java/io/quarkus/search/app/QuarkusVersions.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,33 @@
package io.quarkus.search.app;

import java.util.Comparator;

public final class QuarkusVersions {
private QuarkusVersions() {
}

public static final String LATEST = "latest";
public static final String MAIN = "main";
public static final String V3_2 = "3.2";

public static final Comparator<String> COMPARATOR = new Comparator<String>() {
@Override
public int compare(String left, String right) {
if (left.equals(right)) {
return 0;
} else if (left.equals(MAIN)) {
return 1;
} else if (right.equals(MAIN)) {
return -1;
} else if (left.equals(LATEST)) {
// "latest" actually means "latest non-snapshot", so it's older than main.
return 1;
} else if (right.equals(LATEST)) {
return -1;
} else {
return left.compareTo(right);
}
}
};

}
82 changes: 82 additions & 0 deletions src/main/java/io/quarkus/search/app/ReferenceService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package io.quarkus.search.app;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

import io.quarkus.search.app.cache.MethodNameCacheKeyGenerator;
import io.quarkus.search.app.entity.Guide;
import io.quarkus.search.app.entity.Language;

import io.quarkus.cache.Cache;
import io.quarkus.cache.CacheName;
import io.quarkus.cache.CacheResult;

import org.hibernate.search.engine.search.aggregation.AggregationKey;
import org.hibernate.search.mapper.orm.session.SearchSession;

import org.eclipse.microprofile.openapi.annotations.Operation;

@ApplicationScoped
@Path("/")
@Transactional
@org.jboss.resteasy.reactive.Cache(maxAge = 120)
public class ReferenceService {

private static final String REFERENCE_CACHE = "reference-cache";

@CacheName(REFERENCE_CACHE)
Cache cache;

@Inject
SearchSession session;

@GET
@Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "List available versions")
@Path("/versions")
@CacheResult(cacheName = REFERENCE_CACHE, keyGenerator = MethodNameCacheKeyGenerator.class)
public List<String> versions() {
return listAllValues("quarkusVersion", QuarkusVersions.COMPARATOR.reversed());
}

@GET
@Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "List available languages")
@Path("/languages")
@CacheResult(cacheName = REFERENCE_CACHE, keyGenerator = MethodNameCacheKeyGenerator.class)
public List<String> languages() {
return Arrays.stream(Language.values()).map(lang -> lang.code).toList();
}

@GET
@Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "List available categories")
@Path("/categories")
@CacheResult(cacheName = REFERENCE_CACHE, keyGenerator = MethodNameCacheKeyGenerator.class)
public List<String> categories() {
return listAllValues("categories", Comparator.naturalOrder());
}

public void invalidateCaches() {
cache.invalidateAll().subscribe().asCompletionStage().join();
}

private List<String> listAllValues(String fieldName, Comparator<String> comparator) {
var aggKey = AggregationKey.<Map<String, Long>> of("versions");
var result = session.search(Guide.class)
.where(f -> f.matchAll())
.aggregation(aggKey, f -> f.terms().field(fieldName, String.class))
.fetch(0);
return result.aggregation(aggKey).keySet().stream().sorted(comparator).toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.quarkus.search.app.cache;

import java.lang.reflect.Method;

import io.quarkus.cache.CacheKeyGenerator;

public class MethodNameCacheKeyGenerator implements CacheKeyGenerator {
@Override
public Object generate(Method method, Object... methodParams) {
return method.getName();
}
}
6 changes: 3 additions & 3 deletions src/main/java/io/quarkus/search/app/entity/Guide.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import org.hibernate.search.engine.backend.types.Sortable;
import org.hibernate.search.engine.backend.types.TermVector;
import org.hibernate.search.mapper.pojo.automaticindexing.ReindexOnUpdate;
import org.hibernate.search.mapper.pojo.bridge.builtin.annotation.AlternativeDiscriminator;
import org.hibernate.search.mapper.pojo.bridge.mapping.annotation.RoutingBinderRef;
import org.hibernate.search.mapper.pojo.bridge.mapping.annotation.ValueBridgeRef;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed;
Expand All @@ -42,10 +41,11 @@ public class Guide {
@JavaType(URIType.class)
public URI url;

@AlternativeDiscriminator
@Enumerated(EnumType.STRING)
@KeywordField(searchable = Searchable.NO, aggregable = Aggregable.YES)
public Language language;

@KeywordField(searchable = Searchable.NO, aggregable = Aggregable.YES)
public String quarkusVersion;

@KeywordField
Expand Down Expand Up @@ -76,7 +76,7 @@ public class Guide {
@IndexingDependency(reindexOnUpdate = ReindexOnUpdate.NO)
public I18nData<InputProvider> htmlFullContentProvider = new I18nData<>();

@KeywordField(name = "categories")
@KeywordField(name = "categories", aggregable = Aggregable.YES)
public Set<String> categories = Set.of();

@I18nFullTextField(name = "topics", analyzerPrefix = AnalysisConfigurer.DEFAULT, searchAnalyzerPrefix = AnalysisConfigurer.DEFAULT_SEARCH)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject;

import io.quarkus.search.app.ReferenceService;
import io.quarkus.search.app.fetching.FetchingService;
import io.quarkus.search.app.quarkusio.QuarkusIO;
import io.quarkus.search.app.util.SimpleExecutor;
Expand Down Expand Up @@ -57,6 +58,9 @@ public class IndexingService {
@Inject
IndexingConfig indexingConfig;

@Inject
ReferenceService referenceService;

private final AtomicBoolean reindexingInProgress = new AtomicBoolean();

void registerManagementRoutes(@Observes ManagementInterface mi) {
Expand Down Expand Up @@ -237,6 +241,7 @@ private void indexAll(FailureCollector failureCollector) {
searchMapping.scope(Object.class).workspace().refresh();

rollover.commit();
referenceService.invalidateCaches();
Log.info("Indexing success");
} catch (RuntimeException | IOException e) {
throw new IllegalStateException("Failed to index data: " + e.getMessage(), e);
Expand Down
67 changes: 67 additions & 0 deletions src/test/java/io/quarkus/search/app/ReferenceServiceTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package io.quarkus.search.app;

import static io.restassured.RestAssured.when;
import static org.assertj.core.api.Assertions.assertThat;

import java.util.List;

import io.quarkus.search.app.testsupport.QuarkusIOSample;
import io.quarkus.search.app.testsupport.SetupUtil;

import io.quarkus.test.common.http.TestHTTPEndpoint;
import io.quarkus.test.junit.QuarkusTest;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;

import io.restassured.RestAssured;
import io.restassured.common.mapper.TypeRef;
import io.restassured.filter.log.LogDetail;

@QuarkusTest
@TestHTTPEndpoint(SearchService.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@QuarkusIOSample.Setup
class ReferenceServiceTest {
private static final TypeRef<List<String>> LIST_OF_STRINGS = new TypeRef<>() {
};

private List<String> get(String referenceName) {
return when().get("/" + referenceName)
.then()
.statusCode(200)
.extract().body().as(LIST_OF_STRINGS);
}

@BeforeAll
void setup() {
SetupUtil.waitForIndexing(getClass());
RestAssured.enableLoggingOfRequestAndResponseIfValidationFails(LogDetail.BODY);
}

@Test
void versions() {
assertThat(get("versions")).containsExactly("main", "latest", "3.2");
}

@Test
void languages() {
assertThat(get("languages")).containsExactly("en", "es", "pt", "cn", "ja");
}

@Test
void categories() {
assertThat(get("categories")).containsExactly(
"alt-languages",
"architecture",
"cloud",
"compatibility",
"core",
"data",
"miscellaneous",
"security",
"web",
"writing-extensions");
}
}
6 changes: 3 additions & 3 deletions src/test/java/io/quarkus/search/app/SearchServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ void projections() {
void version() {
var result = given()
.queryParam("q", "orm")
.queryParam("version", QuarkusIOSample.SAMPLED_NON_LATEST_VERSION)
.queryParam("version", QuarkusVersions.MAIN)
.when().get(GUIDES_SEARCH)
.then()
.statusCode(200)
Expand All @@ -270,7 +270,7 @@ void version() {
.asString()
.satisfiesAnyOf(
uri -> assertThat(uri).startsWith("https://quarkus.io/version/"
+ QuarkusIOSample.SAMPLED_NON_LATEST_VERSION + "/guides/"),
+ QuarkusVersions.MAIN + "/guides/"),
uri -> assertThat(uri).startsWith("https://quarkiverse.github.io/quarkiverse-docs")));
result = given()
.queryParam("q", "orm")
Expand All @@ -292,7 +292,7 @@ void version() {
void quarkiverse() {
var result = given()
.queryParam("q", "amazon")
.queryParam("version", QuarkusIOSample.SAMPLED_NON_LATEST_VERSION)
.queryParam("version", QuarkusVersions.MAIN)
.when().get(GUIDES_SEARCH)
.then()
.statusCode(200)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ public final class QuarkusIOSample {
private QuarkusIOSample() {
}

public static final String SAMPLED_NON_LATEST_VERSION = QuarkusVersions.MAIN;
public static final List<String> SAMPLED_VERSIONS = List.of(QuarkusVersions.LATEST, QuarkusVersions.MAIN,
QuarkusVersions.V3_2);

private static Path testResourcesSamplePath() {
return Path.of(System.getProperty("maven.project.testResourceDirectory", "src/test/resources"))
Expand Down Expand Up @@ -382,12 +383,14 @@ protected AbstractGuideRefSetFilterDefinition(String name, GuideRef... guides) {

@Override
public void define(FilterDefinitionCollector c) {
c.addMetadata(QuarkusVersions.LATEST, guides);
c.addMetadata(SAMPLED_NON_LATEST_VERSION, guides);
c.addQuarkiverseMetadata(SAMPLED_NON_LATEST_VERSION);
for (GuideRef guideRef : guides) {
c.addGuide(guideRef);
c.addGuide(guideRef, SAMPLED_NON_LATEST_VERSION);
for (String version : SAMPLED_VERSIONS) {
c.addMetadata(version, guides);
if (QuarkusVersions.MAIN.equals(version)) {
c.addQuarkiverseMetadata(version);
}
for (GuideRef guideRef : guides) {
c.addGuide(guideRef, version);
}
}
}
}
Expand All @@ -402,12 +405,14 @@ public AllLocalizedFilterDefinition(Language language) {

@Override
public void define(FilterDefinitionCollector c) {
c.addLocalizedMetadata(language, QuarkusVersions.LATEST);
c.addLocalizedMetadata(language, SAMPLED_NON_LATEST_VERSION);
c.addLocalizedQuarkiverseMetadata(language, SAMPLED_NON_LATEST_VERSION);
for (GuideRef guideRef : GuideRef.local()) {
c.addLocalizedGuide(language, guideRef, QuarkusVersions.LATEST);
c.addLocalizedGuide(language, guideRef, SAMPLED_NON_LATEST_VERSION);
for (String version : SAMPLED_VERSIONS) {
c.addLocalizedMetadata(language, version);
if (QuarkusVersions.MAIN.equals(version)) {
c.addLocalizedQuarkiverseMetadata(language, version);
}
for (GuideRef guideRef : GuideRef.local()) {
c.addLocalizedGuide(language, guideRef, version);
}
}
}
}
Expand Down Expand Up @@ -453,15 +458,16 @@ public FilterDefinitionCollector addLocalizedGuide(Language language, GuideRef r
}

public FilterDefinitionCollector addLocalizedMetadata(Language language, String version) {
return addLocalizedMetadata(language, version, "quarkus.yaml.po");
String metadataPath = Path.of("l10n", "po", language.locale)
.resolve(QuarkusIO.yamlMetadataPath(version) + ".po")
.toString();
addOnSourceBranch(metadataPath, metadataPath);
return this;
}

public FilterDefinitionCollector addLocalizedQuarkiverseMetadata(Language language, String version) {
return addLocalizedMetadata(language, version, "quarkiverse.yaml.po");
}

private FilterDefinitionCollector addLocalizedMetadata(Language language, String version, String filename) {
String metadataPath = Path.of("l10n", "po", language.locale, "_data", "versioned", version, "index", filename)
String metadataPath = Path.of("l10n", "po", language.locale)
.resolve(QuarkusIO.yamlQuarkiverseMetadataPath(version) + ".po")
.toString();
addOnSourceBranch(metadataPath, metadataPath);
return this;
Expand Down
Binary file modified src/test/resources/quarkusio-sample-cn.zip
Binary file not shown.
Binary file modified src/test/resources/quarkusio-sample-es.zip
Binary file not shown.
Binary file modified src/test/resources/quarkusio-sample-ja.zip
Binary file not shown.
Binary file modified src/test/resources/quarkusio-sample-pt.zip
Binary file not shown.
Binary file modified src/test/resources/quarkusio-sample.zip
Binary file not shown.

0 comments on commit 4433e54

Please sign in to comment.