License3Resource.java
/*
* Copyright © 2023-2025 The CTAN Team and individual authors
*
* This file is distributed under the 3-clause BSD license.
* See file LICENSE for details.
*/
package org.ctan.site.resources.catalogue;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
import org.ctan.site.domain.catalogue.License;
import org.ctan.site.services.content.ContentService;
import org.ctan.site.services.content.ContentService.ContentPageTo;
import org.ctan.site.stores.LicenseStore;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.dropwizard.hibernate.UnitOfWork;
import jakarta.annotation.security.PermitAll;
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.WebApplicationException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response.Status;
import lombok.Builder;
import lombok.Getter;
import lombok.NonNull;
/**
* The class <code>License3Resource</code> contains the controller for the
* license resource.
*
* @author <a href="mailto:gene@ctan.org">Gerd Neugebauer</a>
*/
@Path("/3.0")
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public class License3Resource {
/**
* The class <code>LicenseTo</code> contains the transport object for the
* license resource in the licenses list.
*/
@Getter
@Builder
protected static class LicenseTo {
private String key;
private String name;
private String intro;
private String text;
private String type;
private Boolean selectable;
private String url;
}
/**
* The field <code>store</code> contains the underlying repository.
*/
private LicenseStore store;
/**
* The field <code>contentService</code> contains the underlying content
* service.
*/
private ContentService contentService;
/**
* This is the constructor for the class <code>Pkg3Resource</code>.
*
* @param store the underlying store
* @param contentService the underlying content service
*/
@SuppressFBWarnings(value = {"CT_CONSTRUCTOR_THROW", "EI_EXPOSE_REP2"})
public License3Resource(@NonNull LicenseStore store,
@NonNull ContentService contentService) {
this.store = store;
this.contentService = contentService;
}
/**
* The method <code>getAllLicenses</code> provides means to retrieve a list
* of all licenses.
*
* @return a list of matching license summaries
*/
@GET
@Path("/licenses")
@PermitAll
@UnitOfWork(value = "siteDb")
public List<LicenseTo> getAllLicenses() {
return getLicenses("");
}
/**
* The method <code>getLicenseByKey</code> contains the endpoint for
* /license/key. It returns the license object.
*
* @param key the key
* @param lang the locale
* @return the license object
*/
@GET
@Path("/license/{key}")
@PermitAll
@UnitOfWork(value = "siteDb")
public LicenseTo getLicenseByKey(@NonNull @PathParam("key") String key,
@QueryParam("lang") String lang) {
var license = store.getByKey(key);
if (license == null) {
throw new WebApplicationException(Status.NOT_FOUND);
}
ContentPageTo intro;
ContentPageTo text;
try {
intro = contentService.getText("terms/" + key, lang);
text = contentService.getText("terms/" + key + "-text", lang);
} catch (IOException e) {
throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
}
return LicenseTo.builder()
.key(license.getKey())
.name(license.getName())
.selectable(license.getSelectable())
.intro(intro != null ? intro.getContent() : null)
.text(text != null ? text.getContent() : null)
.type(licenseType(license))
.url(license.getUrl())
.build();
}
/**
* The method <code>getLicenses</code> provides means to retrieve a list of
* licenses starting with a given pattern.
*
* @param pattern the initial string of the key
* @return a list of matching license summaries
*/
@GET
@Path("/licenses/{pattern}")
@PermitAll
@UnitOfWork(value = "siteDb")
public List<LicenseTo> getLicenses(@PathParam("pattern") String pattern) {
return store.findAllByKeyStartingWith(pattern != null ? pattern : "")
.stream()
.map(license -> {
return LicenseTo.builder()
.key(license.getKey())
.name(license.getName())
.url(license.getUrl())
.type(licenseType(license))
.selectable(license.getSelectable())
.build();
})
.collect(Collectors.toList());
}
/**
* The method <code>licenseType</code> provides means to determine the
* license type.
*
* @param license the license
* @return the type: <br/>
* <code>free</code> <br/>
* <code>nonfree</code> <br/>
* <code>unknown</code>
*/
private String licenseType(License license) {
var f = license.getFree();
return f == null
? "unknown"
: f ? "free" : "nonfree";
}
}