TextApiResource.java
package info.textgrid.services.textapi;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
import info.textgrid.namespaces.middleware.tgsearch.ResultType;
import info.textgrid.services.textapi.apiv1.Actor;
import info.textgrid.services.textapi.apiv1.Collection;
import info.textgrid.services.textapi.apiv1.ContentItem;
import info.textgrid.services.textapi.apiv1.Item;
import info.textgrid.services.textapi.apiv1.Manifest;
import info.textgrid.services.textapi.apiv1.Repository;
import info.textgrid.services.textapi.apiv1.SequenceItem;
import info.textgrid.services.textapi.apiv1.Title;
import info.textgrid.services.textapi.services.SearchClientService;
import io.quarkus.qute.Template;
import io.quarkus.qute.TemplateInstance;
import io.quarkus.runtime.LaunchMode;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;
@Path("/textapi")
public class TextApiResource {
@Inject
SearchClientService searchClient;
@ConfigProperty(name = "textgrid.host")
String textgridHost;
@ConfigProperty(name = "service.url")
String serviceUrl;
@ConfigProperty(name = "validator.url")
String validatorUrl;
@ConfigProperty(name = "quarkus.application.version")
String version;
@Inject
LaunchMode launchMode;
@Inject
Template index;
/**
* Check if the given format is a valid aggregation format.
* Accepts: text/tg.aggregation+xml, text/tg.collection+tg.aggregation+xml, text/tg.edition+tg.aggregation+xml
*/
private boolean isAggregationFormat(String format) {
return format != null && (
format.equals("text/tg.aggregation+xml") ||
format.equals("text/tg.collection+tg.aggregation+xml") ||
format.equals("text/tg.edition+tg.aggregation+xml")
);
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/{id}/manifest.json")
@Operation(summary = "Generate a TextAPI manifest", description = "Use TextGridURI to list all items of an aggregation as TextAPI Items, "
+ "utilizing the aggregator for TEI->HTML transformation")
public Manifest manifest(
@Parameter(description = "the TextGridURI to generate a manifest for", example = "textgrid:wp1j.0") @PathParam("id") String id) {
var meta = searchClient.infoQuery().getMetadata(id);
if (meta.getResult().isEmpty()) {
throw new jakarta.ws.rs.WebApplicationException(
"Resource not found: " + id,
jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR
);
}
var label = meta.getResult().get(0).getObject().getGeneric().getProvided().getTitle().get(0);
var format = meta.getResult().get(0).getObject().getGeneric().getProvided().getFormat();
var r = new Repository().setId(id);
var m = new Manifest().setId(serviceUrl + "/" + id + "/manifest.json").setLabel(label).addRepository(r);
// every item of an for aggregations
if (isAggregationFormat(format)) {
for (ResultType res : searchClient.navigationQuery().listAggregation(id).getResult()) {
var s = new SequenceItem()
.setId(serviceUrl + "/" + res.getTextgridUri() + "/item.json")
.setType("item")
.setLabel(res.getObject().getGeneric().getProvided().getTitle().get(0));
m.addSequenceItem(s);
}
} else {
var s = new SequenceItem().setId(serviceUrl + "/" + id + "/item.json").setLabel(label);
m.addSequenceItem(s);
}
return m;
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/{id}/item.json")
@Operation(summary = "Generate a TextAPI item", description = "Get aggregator URL for given TextGridUri for TEI -> HTML transformation")
public Item item(
@Parameter(description = "textgridUri of a single item (normally listed inside a manifest)", example = "textgrid:wmd3.0") @PathParam("id") String id) {
var _id = serviceUrl + "/" + id + "/item.json";
var contentItem = new ContentItem().setUrl(textgridHost + "/1.0/aggregator/html/" + id + "?embedded=true");
// TODO: lang from textgrid metadata, what if not set? set to und (undetermined)
// https://de.wikipedia.org/wiki/ISO_639#Spezielle_Kennungen
var item = new Item().addContent(contentItem).setId(_id).addLang("und");
return item;
}
@GET
@Produces(MediaType.TEXT_PLAIN)
@Path("/info")
@Operation(summary = "Build info & config of this service")
public String info() {
return """
tg-textapi %s
textgrid host: %s
devmode: %s
""".formatted(version, textgridHost, launchMode.isDevOrTest());
}
@GET
@Produces(MediaType.TEXT_HTML)
@Path("/")
@Operation(hidden = true) // hide operation from OpenAPI
public TemplateInstance index() {
return index.data("version", version)
.data("serviceUrl", serviceUrl)
.data("validatorUrl", validatorUrl)
.data("devmode", launchMode.isDevOrTest());
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/search/collection.json")
public Collection search(@QueryParam("q") String query) {
return searchToCollection(query);
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/search/{q}/collection.json")
public Collection searchPath(@PathParam("q") String query) {
return searchToCollection(query);
}
Collection searchToCollection(String query) {
var results = searchClient.searchQuery().setQuery(query).execute();
var description = "Search results for " + query;
var title = new Title().setTitle("Query: " + query);
var collector = new Actor().addRole("collector").setName("you via tgsearch").setId("yoursearch");
var collection = new Collection()
.setId("todo")
.setDescription(description)
.addTitle(title)
.addCollector(collector);
for (ResultType res : results.getResult()) {
var id = res.getObject().getGeneric().getGenerated().getTextgridUri().getValue();
var SequenceItem = new SequenceItem()
.setId(serviceUrl + "/" + id + "/manifest.json")
.setType("manifest")
.setLabel(res.getObject().getGeneric().getProvided().getTitle().get(0));
collection.addSequenceItem(SequenceItem);
}
return collection;
}
}