added swagger docs

This commit is contained in:
2023-02-02 22:42:38 -05:00
parent d9dbdaace0
commit 1e43da7b41
6 changed files with 205 additions and 153 deletions

30
pom.xml
View File

@@ -13,6 +13,8 @@
<maven.compiler.release>11</maven.compiler.release> <maven.compiler.release>11</maven.compiler.release>
<maven.compiler.debug>true</maven.compiler.debug> <maven.compiler.debug>true</maven.compiler.debug>
<maven.compiler.debuglevel>lines,vars,source</maven.compiler.debuglevel> <maven.compiler.debuglevel>lines,vars,source</maven.compiler.debuglevel>
<swagger.version>2.2.8</swagger.version>
</properties> </properties>
<dependencies> <dependencies>
@@ -31,11 +33,6 @@
<artifactId>commons-csv</artifactId> <artifactId>commons-csv</artifactId>
<version>1.5</version> <version>1.5</version>
</dependency> </dependency>
<dependency>
<groupId>com.webcohesion.enunciate</groupId>
<artifactId>enunciate-core-annotations</artifactId>
<version>2.14.0</version>
</dependency>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
@@ -47,6 +44,29 @@
<build> <build>
<plugins> <plugins>
<plugin>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-maven-plugin-jakarta</artifactId>
<version>${swagger.version}</version>
<configuration>
<resourcePackages>
<resourcePackage>com.poststats.golf.api</resourcePackage>
</resourcePackages>
<outputPath>${project.build.outputDirectory}/META-INF</outputPath>
<outputFileName>golf-swagger</outputFileName>
<outputFormat>JSONANDYAML</outputFormat>
<prettyPrint>true</prettyPrint>
</configuration>
<executions>
<execution>
<id>swagger-generate</id>
<goals>
<goal>resolve</goal>
</goals>
<phase>compile</phase>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>io.repaint.maven</groupId> <groupId>io.repaint.maven</groupId>
<artifactId>tiles-maven-plugin</artifactId> <artifactId>tiles-maven-plugin</artifactId>

View File

@@ -2,11 +2,18 @@ package com.poststats.golf.api;
import com.brianlong.sql.DataSet; import com.brianlong.sql.DataSet;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.poststats.api.BaseApi; import com.poststats.api.Constants;
import com.poststats.golf.api.model.Event; import com.poststats.golf.api.model.Event;
import com.poststats.golf.api.model.EventDetail; import com.poststats.golf.api.model.EventDetail;
import com.poststats.golf.service.EventService; import com.poststats.golf.service.EventService;
import com.poststats.golf.transformer.EventTransformer; import com.poststats.golf.transformer.EventTransformer;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.info.Contact;
import io.swagger.v3.oas.annotations.info.Info;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.RequestScoped; import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject; import jakarta.inject.Inject;
@@ -15,19 +22,18 @@ import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam; import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces; import jakarta.ws.rs.Produces;
import jakarta.ws.rs.WebApplicationException; import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response.Status; import jakarta.ws.rs.core.Response.Status;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* This API provides access to PostStats Golf Event meta-data and features.
*
* @author brian.long@poststats.com * @author brian.long@poststats.com
*/ */
@RequestScoped @RequestScoped
@Path(BaseApi.BASE_PATH + "/golf/event/{eventId}") @Path("/golf/event/{eventId}")
public class EventApi extends BaseApi { @Tag(name = "Event API")
@OpenAPIDefinition(info = @Info(contact = @Contact(name = "Brian Long", email = "brian.long@poststats.com"), title = "PostStats Golf API", description = "An API providing access to PostStats Golf objects."))
public class EventApi {
private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final Logger logger = LoggerFactory.getLogger(this.getClass());
@@ -45,13 +51,11 @@ public class EventApi extends BaseApi {
this.logger.debug("EventApi init: {}", this.eventId); this.logger.debug("EventApi init: {}", this.eventId);
} }
/**
* @return An event model object.
* @throws JsonProcessingException A JSON parsing issue occurred.
* @throws http-404 The specified event was not found.
*/
@GET @GET
@Produces(MediaType.APPLICATION_JSON) @Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves limited meta-data about an event.", description = "Retreives name, location, dates, and other direct meta-data about the specified event.")
@ApiResponses({ @ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found") })
public Event get() throws JsonProcessingException { public Event get() throws JsonProcessingException {
DataSet row = this.eventService.get(this.eventId); DataSet row = this.eventService.get(this.eventId);
if (row == null) throw new WebApplicationException("Event not found", Status.NOT_FOUND); if (row == null) throw new WebApplicationException("Event not found", Status.NOT_FOUND);
@@ -59,14 +63,12 @@ public class EventApi extends BaseApi {
return this.eventTransformer.toModel(row); return this.eventTransformer.toModel(row);
} }
/**
* @return An event model object.
* @throws JsonProcessingException A JSON parsing issue occurred.
* @throws http-404 The specified event was not found.
*/
@GET @GET
@Path("/detail") @Path("/detail")
@Produces(MediaType.APPLICATION_JSON) @Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves detailed meta-data about an event.", description = "Retreives name, location, dates, courses, and other indirect meta-data about the specified event.")
@ApiResponses({ @ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found") })
public EventDetail getDetail() throws JsonProcessingException { public EventDetail getDetail() throws JsonProcessingException {
DataSet row = this.eventService.getDetail(this.eventId); DataSet row = this.eventService.getDetail(this.eventId);
if (row == null) throw new WebApplicationException("Event not found", Status.NOT_FOUND); if (row == null) throw new WebApplicationException("Event not found", Status.NOT_FOUND);

View File

@@ -2,9 +2,12 @@ package com.poststats.golf.api;
import com.brianlong.sql.DataSet; import com.brianlong.sql.DataSet;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.poststats.api.Constants;
import com.poststats.api.BaseApi;
import com.poststats.golf.service.EventFinanceService; import com.poststats.golf.service.EventFinanceService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.security.RolesAllowed; import jakarta.annotation.security.RolesAllowed;
import jakarta.enterprise.context.RequestScoped; import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject; import jakarta.inject.Inject;
@@ -12,10 +15,13 @@ import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path; import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam; import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces; import jakarta.ws.rs.Produces;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.SecurityContext; import jakarta.ws.rs.core.SecurityContext;
import jakarta.ws.rs.core.StreamingOutput;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@@ -24,10 +30,9 @@ import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter; import org.apache.commons.csv.CSVPrinter;
@RequestScoped @RequestScoped
@Path(BaseApi.BASE_PATH + "/golf/event/{eventId}/finance") @Path("/golf/event/{eventId}/finance")
public class EventFinanceApi extends BaseApi { @Tag(name = "Event Finance API")
public class EventFinanceApi {
private final ObjectMapper mapper = new ObjectMapper();
@PathParam("eventId") @PathParam("eventId")
private long eventId; private long eventId;
@@ -37,9 +42,12 @@ public class EventFinanceApi extends BaseApi {
@GET @GET
@Path("/balance/persons") @Path("/balance/persons")
@Produces(MediaType.APPLICATION_JSON)
@RolesAllowed("member") @RolesAllowed("member")
public String getBalanceByPersonsAsJson(@Context SecurityContext securityContext) throws JsonProcessingException { @Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves the balances of all participants in an event.")
@ApiResponses({ @ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found") })
public List<Map<String, Object>> getBalanceByPersonsAsJson(@Context SecurityContext securityContext) throws JsonProcessingException {
if (!securityContext.isUserInRole(this.eventId + "~finance")) throw new SecurityException("Not permitted"); if (!securityContext.isUserInRole(this.eventId + "~finance")) throw new SecurityException("Not permitted");
List<DataSet> personsBalances = this.eventFinanceService.getPersonsBalances(this.eventId); List<DataSet> personsBalances = this.eventFinanceService.getPersonsBalances(this.eventId);
@@ -54,35 +62,41 @@ public class EventFinanceApi extends BaseApi {
personsBalancesJson.add(personBalanceJson); personsBalancesJson.add(personBalanceJson);
} }
return this.mapper.writeValueAsString(personsBalancesJson); return personsBalancesJson;
} }
@GET @GET
@Path("/balance/persons/csv") @Path("/balance/persons")
@Produces("text/csv")
@RolesAllowed("member") @RolesAllowed("member")
public String getBalanceByPersonsAsCsv(@Context SecurityContext securityContext) throws IOException { @Produces("text/csv")
@Operation(summary = "Retrieves the balances of all participants in an event.")
@ApiResponses({ @ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found") })
public StreamingOutput getBalanceByPersonsAsCsv(@Context SecurityContext securityContext) throws IOException {
if (!securityContext.isUserInRole(this.eventId + "~finance")) throw new SecurityException("Not permitted"); if (!securityContext.isUserInRole(this.eventId + "~finance")) throw new SecurityException("Not permitted");
List<DataSet> personsBalances = this.eventFinanceService.getPersonsBalances(this.eventId); List<DataSet> personsBalances = this.eventFinanceService.getPersonsBalances(this.eventId);
StringBuilder personsBalancesCsvBuilder = new StringBuilder(); return new StreamingOutput() {
CSVPrinter personsBalancesCsvPrinter = new CSVPrinter(personsBalancesCsvBuilder, CSVFormat.DEFAULT); @Override
try { public void write(OutputStream output) throws IOException, WebApplicationException {
personsBalancesCsvPrinter.printRecord("personID", "lname", "fname", "suffix", "expense", "paid", "balance"); PrintStream pstream = new PrintStream(output);
CSVPrinter personsBalancesCsvPrinter = new CSVPrinter(pstream, CSVFormat.DEFAULT);
try {
personsBalancesCsvPrinter.printRecord("personID", "lname", "fname", "suffix", "expense", "paid", "balance");
for (DataSet personBalance : personsBalances) { for (DataSet personBalance : personsBalances) {
personsBalancesCsvPrinter.printRecord(personBalance.getLong("personID"), personBalance.getString("lname"), personBalance.getString("fname"), personsBalancesCsvPrinter.printRecord(personBalance.getLong("personID"), personBalance.getString("lname"),
personBalance.getString("suffix"), personBalance.getFloat("expense"), personBalance.getFloat("paid"), personBalance.getString("fname"), personBalance.getString("suffix"), personBalance.getFloat("expense"),
personBalance.getFloat("balance")); personBalance.getFloat("paid"), personBalance.getFloat("balance"));
}
personsBalancesCsvPrinter.flush();
} finally {
personsBalancesCsvPrinter.close();
}
} }
};
personsBalancesCsvPrinter.flush();
} finally {
personsBalancesCsvPrinter.close();
}
return personsBalancesCsvBuilder.toString();
} }
} }

View File

@@ -2,11 +2,15 @@ package com.poststats.golf.api;
import com.brianlong.sql.DataSet; import com.brianlong.sql.DataSet;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.poststats.api.Constants;
import com.poststats.api.BaseApi; import com.poststats.api.model.Person;
import com.poststats.golf.service.EventPersonService; import com.poststats.golf.service.EventPersonService;
import com.poststats.golf.service.EventService; import com.poststats.golf.service.EventService;
import com.poststats.security.Person; import com.poststats.transformer.PersonTransformer;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.security.RolesAllowed; import jakarta.annotation.security.RolesAllowed;
import jakarta.enterprise.context.RequestScoped; import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject; import jakarta.inject.Inject;
@@ -15,25 +19,25 @@ import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam; import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces; import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.SecurityContext; import jakarta.ws.rs.core.SecurityContext;
import jakarta.ws.rs.core.StreamingOutput;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter; import org.apache.commons.csv.CSVPrinter;
@RequestScoped @RequestScoped
@Path(BaseApi.BASE_PATH + "/golf/event/{eventId}") @Path("/golf/event/{eventId}")
public class EventPersonApi extends BaseApi { @Tag(name = "Event Participant API")
public class EventPersonApi {
private final ObjectMapper mapper = new ObjectMapper();
@PathParam("eventId") @PathParam("eventId")
private long eventId; private long eventId;
@@ -44,44 +48,75 @@ public class EventPersonApi extends BaseApi {
@Inject @Inject
private EventPersonService eventPersonService; private EventPersonService eventPersonService;
@Inject
private PersonTransformer personTransformer;
@GET @GET
@Path("/people") @Path("/people")
@Produces({ MediaType.APPLICATION_JSON, "text/csv" })
@RolesAllowed("member") @RolesAllowed("member")
public String get(@Context SecurityContext securityContext, @QueryParam("format") String format) throws JsonProcessingException, IOException { @Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves the administrators and participants in an event.")
@ApiResponses({ @ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found") })
public List<Person> get(@Context SecurityContext securityContext, @QueryParam("format") String format) throws JsonProcessingException, IOException {
if (!securityContext.isUserInRole(this.eventId + "~member")) throw new SecurityException("Not permitted"); if (!securityContext.isUserInRole(this.eventId + "~member")) throw new SecurityException("Not permitted");
List<DataSet> persons = this.eventPersonService.getPeople(this.eventId); List<DataSet> persons = this.eventPersonService.getPeople(this.eventId);
return this.toAddressBookAsJson(persons);
}
if ("csv".equals(format)) { @GET
return this.toAddressBookAsCsv(persons); @Path("/people")
} else { @RolesAllowed("member")
return this.toAddressBookAsJson(persons); @Produces("text/csv")
} @Operation(summary = "Retrieves the administrators and participants in an event.")
@ApiResponses({ @ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found") })
public StreamingOutput getAsCsv(@Context SecurityContext securityContext, @QueryParam("format") String format) throws JsonProcessingException, IOException {
if (!securityContext.isUserInRole(this.eventId + "~member")) throw new SecurityException("Not permitted");
List<DataSet> persons = this.eventPersonService.getPeople(this.eventId);
return this.toAddressBookAsCsv(persons);
} }
@GET @GET
@Path("/participants") @Path("/participants")
@Produces({ MediaType.APPLICATION_JSON, "text/csv" })
@RolesAllowed("member") @RolesAllowed("member")
public String getParticipants(@Context SecurityContext securityContext, @Context @QueryParam("format") String format) @Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves the participants in an event.")
@ApiResponses({ @ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found") })
public List<Person> getParticipants(@Context SecurityContext securityContext, @Context @QueryParam("format") String format)
throws JsonProcessingException, IOException { throws JsonProcessingException, IOException {
if (!securityContext.isUserInRole(this.eventId + "~member")) throw new SecurityException("Not permitted"); if (!securityContext.isUserInRole(this.eventId + "~member")) throw new SecurityException("Not permitted");
List<DataSet> persons = this.eventPersonService.getParticipants(this.eventId); List<DataSet> persons = this.eventPersonService.getParticipants(this.eventId);
return this.toAddressBookAsJson(persons);
if ("csv".equals(format)) {
return this.toAddressBookAsCsv(persons);
} else {
return this.toAddressBookAsJson(persons);
}
} }
@GET @GET
@Path("/participants") @Path("/participants")
@Produces({ MediaType.APPLICATION_JSON, "text/csv" })
@RolesAllowed("member") @RolesAllowed("member")
public String getSeriesParticipants(@Context SecurityContext securityContext, @QueryParam("format") String format) @Produces("text/csv")
@Operation(summary = "Retrieves the participants in an event.")
@ApiResponses({ @ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found") })
public StreamingOutput getParticipantsAsCsv(@Context SecurityContext securityContext, @Context @QueryParam("format") String format)
throws JsonProcessingException, IOException {
if (!securityContext.isUserInRole(this.eventId + "~member")) throw new SecurityException("Not permitted");
List<DataSet> persons = this.eventPersonService.getParticipants(this.eventId);
return this.toAddressBookAsCsv(persons);
}
@GET
@Path("/series/participants")
@RolesAllowed("member")
@Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves all the participants in an event series.")
@ApiResponses({ @ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found") })
public List<Person> getSeriesParticipants(@Context SecurityContext securityContext, @QueryParam("format") String format)
throws JsonProcessingException, IOException { throws JsonProcessingException, IOException {
if (!securityContext.isUserInRole(this.eventId + "~member")) throw new SecurityException("Not permitted"); if (!securityContext.isUserInRole(this.eventId + "~member")) throw new SecurityException("Not permitted");
@@ -98,46 +133,36 @@ public class EventPersonApi extends BaseApi {
if (personIds.add(person.getLong("personID"))) persons.add(person); if (personIds.add(person.getLong("personID"))) persons.add(person);
} }
if ("csv".equals(format)) { return this.toAddressBookAsJson(persons);
return this.toAddressBookAsCsv(persons);
} else {
return this.toAddressBookAsJson(persons);
}
} }
private String toAddressBookAsJson(List<DataSet> persons) throws JsonProcessingException { private List<Person> toAddressBookAsJson(List<DataSet> persons) throws JsonProcessingException {
List<Map<String, Object>> personsJson = new ArrayList<>(persons.size()); List<Person> personsJson = new ArrayList<>(persons.size());
for (DataSet person : persons) { for (DataSet person : persons)
Map<String, Object> personJson = new HashMap<>(5); personsJson.add(this.personTransformer.toModel(person));
personJson.put("personId", person.getLong("personID")); return personsJson;
personJson.put("prefix", person.getString("prefix"));
personJson.put("lastName", person.getString("lname"));
personJson.put("firstName", person.getString("fname"));
personJson.put("suffix", person.getString("suffix"));
personJson.put("emailAddress", person.getString("email"));
personsJson.add(personJson);
}
return this.mapper.writeValueAsString(personsJson);
} }
private String toAddressBookAsCsv(List<DataSet> persons) throws IOException { private StreamingOutput toAddressBookAsCsv(List<DataSet> persons) throws IOException {
StringBuilder personsCsvBuilder = new StringBuilder(); return new StreamingOutput() {
CSVPrinter personsCsvPrinter = new CSVPrinter(personsCsvBuilder, CSVFormat.DEFAULT); @Override
try { public void write(OutputStream output) throws IOException, WebApplicationException {
personsCsvPrinter.printRecord("ID", "Prefix", "Last Name", "First Name", "Suffix", "Email Address"); PrintStream pstream = new PrintStream(output);
CSVPrinter personsCsvPrinter = new CSVPrinter(pstream, CSVFormat.DEFAULT);
try {
personsCsvPrinter.printRecord("ID", "Prefix", "Last Name", "First Name", "Suffix", "Email Address");
for (DataSet person : persons) { for (DataSet person : persons) {
personsCsvPrinter.printRecord(person.getLong("personID"), person.getString("prefix"), person.getString("lname"), person.getString("fname"), personsCsvPrinter.printRecord(person.getLong("personID"), person.getString("prefix"), person.getString("lname"),
person.getString("suffix"), person.getString("email")); person.getString("fname"), person.getString("suffix"), person.getString("email"));
}
personsCsvPrinter.flush();
} finally {
personsCsvPrinter.close();
}
} }
};
personsCsvPrinter.flush();
} finally {
personsCsvPrinter.close();
}
return personsCsvBuilder.toString();
} }
} }

View File

@@ -2,10 +2,14 @@ package com.poststats.golf.api;
import com.brianlong.sql.DataSet; import com.brianlong.sql.DataSet;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.poststats.api.BaseApi; import com.poststats.api.Constants;
import com.poststats.api.model.Person; import com.poststats.api.model.Person;
import com.poststats.golf.transformer.GolferTransformer; import com.poststats.golf.transformer.GolferTransformer;
import com.poststats.service.PersonService; import com.poststats.service.PersonService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.RequestScoped; import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject; import jakarta.inject.Inject;
@@ -14,19 +18,17 @@ import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam; import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces; import jakarta.ws.rs.Produces;
import jakarta.ws.rs.WebApplicationException; import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response.Status; import jakarta.ws.rs.core.Response.Status;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* This API provides access to PostStats Golfer meta-data and features.
*
* @author brian.long@poststats.com * @author brian.long@poststats.com
*/ */
@RequestScoped @RequestScoped
@Path(BaseApi.BASE_PATH + "/golfer/{personId}") @Path("/golfer/{personId}")
public class GolferApi extends BaseApi { @Tag(name = "Golfer API")
public class GolferApi {
private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final Logger logger = LoggerFactory.getLogger(this.getClass());
@@ -44,13 +46,11 @@ public class GolferApi extends BaseApi {
this.logger.debug("GolferApi init: {}", this.personId); this.logger.debug("GolferApi init: {}", this.personId);
} }
/**
* @return An person model object.
* @throws JsonProcessingException A JSON parsing issue occurred.
* @throws http-404 The specified event was not found.
*/
@GET @GET
@Produces(MediaType.APPLICATION_JSON) @Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves limited meta-data about a golfer.", description = "Retreives name, location, and other direct meta-data about the specified golfer.")
@ApiResponses({ @ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "404", description = "A golfer with the specified ID could not be found") })
public Person get() throws JsonProcessingException { public Person get() throws JsonProcessingException {
DataSet row = this.personService.get(this.personId); DataSet row = this.personService.get(this.personId);
if (row == null) throw new WebApplicationException("Event not found", Status.NOT_FOUND); if (row == null) throw new WebApplicationException("Event not found", Status.NOT_FOUND);

View File

@@ -2,13 +2,17 @@ package com.poststats.golf.api;
import com.brianlong.sql.DataSet; import com.brianlong.sql.DataSet;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.poststats.api.BaseApi; import com.poststats.api.Constants;
import com.poststats.golf.api.model.Event; import com.poststats.golf.api.model.Event;
import com.poststats.golf.api.model.Series; import com.poststats.golf.api.model.Series;
import com.poststats.golf.service.EventService; import com.poststats.golf.service.EventService;
import com.poststats.golf.service.SeriesService; import com.poststats.golf.service.SeriesService;
import com.poststats.golf.transformer.EventTransformer; import com.poststats.golf.transformer.EventTransformer;
import com.poststats.golf.transformer.SeriesTransformer; import com.poststats.golf.transformer.SeriesTransformer;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.RequestScoped; import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject; import jakarta.inject.Inject;
@@ -18,7 +22,6 @@ import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces; import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.WebApplicationException; import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response.Status; import jakarta.ws.rs.core.Response.Status;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@@ -28,14 +31,12 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* This API provides access to PostStats Golf Event Series meta-data and
* features.
*
* @author brian.long@poststats.com * @author brian.long@poststats.com
*/ */
@RequestScoped @RequestScoped
@Path(BaseApi.BASE_PATH + "/golf/series/{seriesId}") @Path("/golf/series/{seriesId}")
public class SeriesApi extends BaseApi { @Tag(name = "Event Series API")
public class SeriesApi {
private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final Logger logger = LoggerFactory.getLogger(this.getClass());
@@ -59,13 +60,11 @@ public class SeriesApi extends BaseApi {
this.logger.debug("SeriesApi init: {}", this.seriesId); this.logger.debug("SeriesApi init: {}", this.seriesId);
} }
/**
* @return An event series model object.
* @throws JsonProcessingException A JSON parsing issue occurred.
* @throws http-404 The specified series was not found.
*/
@GET @GET
@Produces(MediaType.APPLICATION_JSON) @Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves meta-data about an event series.", description = "Retreives name and other direct meta-data about the specified event series.")
@ApiResponses({ @ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "404", description = "An event series with the specified ID could not be found") })
public Series get() throws JsonProcessingException { public Series get() throws JsonProcessingException {
DataSet row = this.seriesService.get(this.seriesId); DataSet row = this.seriesService.get(this.seriesId);
if (row == null) throw new WebApplicationException("Series not found", Status.NOT_FOUND); if (row == null) throw new WebApplicationException("Series not found", Status.NOT_FOUND);
@@ -73,32 +72,24 @@ public class SeriesApi extends BaseApi {
return this.seriesTransformer.toModel(row); return this.seriesTransformer.toModel(row);
} }
/**
* @return An event series model object.
* @throws JsonProcessingException A JSON parsing issue occurred.
* @throws http-404 The specified series was not found or it had
* no associated events.
*/
@GET @GET
@Path("/eventIds") @Path("/eventIds")
@Produces(MediaType.APPLICATION_JSON) @Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves event IDs under an event series.")
@ApiResponses({ @ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "404", description = "An event series with the specified ID could not be found") })
public Set<Long> getEventIds() throws JsonProcessingException { public Set<Long> getEventIds() throws JsonProcessingException {
Set<Long> eventIds = this.eventService.getIds(this.seriesId); Set<Long> eventIds = this.eventService.getIds(this.seriesId);
if (eventIds.isEmpty()) throw new WebApplicationException("Series or events not found", Status.NOT_FOUND); if (eventIds.isEmpty()) throw new WebApplicationException("Series or events not found", Status.NOT_FOUND);
return eventIds; return eventIds;
} }
/**
* @param reverse `true` to sort events in reverse chronological order; `false`
* (default) otherwise.
* @return A list of event model objects.
* @throws JsonProcessingException A JSON parsing issue occurred.
* @throws http-404 The specified series was not found or it had
* no associated events.
*/
@GET @GET
@Path("/events") @Path("/events")
@Produces(MediaType.APPLICATION_JSON) @Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves limited event meta-data about all events in an event series.", description = "Retreives name, location, dates, and other direct meta-data about all events in the specified event series.")
@ApiResponses({ @ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "404", description = "An event series with the specified ID could not be found") })
public List<Event> getEvents(@QueryParam("reverse") Boolean reverse) throws JsonProcessingException { public List<Event> getEvents(@QueryParam("reverse") Boolean reverse) throws JsonProcessingException {
Map<Long, DataSet> rows = this.eventService.get(this.seriesId, !Boolean.TRUE.equals(reverse)); Map<Long, DataSet> rows = this.eventService.get(this.seriesId, !Boolean.TRUE.equals(reverse));
if (rows.isEmpty()) throw new WebApplicationException("Series or events not found", Status.NOT_FOUND); if (rows.isEmpty()) throw new WebApplicationException("Series or events not found", Status.NOT_FOUND);