Compare commits

34 Commits

Author SHA1 Message Date
c5a9a5d167 added ScoringApi 2025-06-26 15:00:18 -04:00
d5d96fd235 added season handicap computation endpoints 2025-06-25 16:41:00 -04:00
259f5655b4 added round removal by admins 2025-06-25 16:40:39 -04:00
07e505f206 added document API 2024-06-14 16:36:38 -04:00
f853010793 fix round documentation 2024-06-14 16:36:25 -04:00
7dfcc45b2e added round API 2024-05-27 12:44:45 -04:00
a0b6ff4153 added parameter to pointIndex recompute 2024-05-27 12:44:37 -04:00
aa9df77208 fix GolferApi 2024-05-27 12:44:22 -04:00
5ea6403f04 added point rating computation access 2024-04-16 11:47:53 -04:00
cc8f681308 added CourseTee access 2024-04-16 11:47:39 -04:00
5da8830c74 secured/documented AdminApi 2024-04-16 11:46:57 -04:00
07fd0d82e4 sorting generated endpoints 2024-04-16 11:46:26 -04:00
5480a951b7 added AdministrativeApi 2024-03-27 10:52:44 -04:00
f302b65f47 minor updates due to refactoring 2024-03-11 23:37:01 -04:00
df1f1a2e28 refactored packages 2023-10-04 17:05:53 -04:00
dfca5f639d split api/impl; v2.x 2023-10-04 16:55:58 -04:00
360165c64e further handicap impl 2023-10-02 13:02:37 -04:00
b571db1f4e another fixes for @PathParam 2023-07-05 12:30:58 -04:00
9627dba941 fixed resource-scoped @PathParam 2023-07-05 10:42:10 -04:00
59d40ecba9 added new handicapping services 2023-07-04 13:03:00 -04:00
e258ff50b1 split REST API/impl; reformatted all 2023-07-04 13:02:42 -04:00
6471493170 added pairing CSV; fixed agenda 2023-06-19 17:31:26 -04:00
03fa19734b using DS release to close 2023-06-14 11:57:11 -04:00
ffaea04b86 added finance API methods 2023-06-04 22:21:15 -04:00
b684b80ea9 fixed agenda job for autolists 2023-06-04 21:03:41 -04:00
81c479eb3a added agenda job/api/support 2023-06-03 17:03:56 -04:00
693dac38a7 updated provider closure formatting 2023-06-03 16:08:49 -04:00
c6fdab2588 updated enum formatting 2023-06-03 16:08:15 -04:00
3403cee51a added swagger auth; fixed connection handling 2023-02-18 10:07:04 -05:00
987e5c3547 various fixes; added nines/pairings 2023-02-15 11:19:56 -05:00
1fdb9fac53 refactored telegram 2023-02-13 20:25:51 -05:00
2bb68c6039 transactional refactoring 2023-02-13 11:47:52 -05:00
7900841d4a extended photo model for golf 2023-02-12 12:32:56 -05:00
8b80d3ce9a moved cache merge into the JAXRS layer 2023-02-11 12:04:50 -05:00
104 changed files with 2691 additions and 2812 deletions

44
pom.xml
View File

@@ -2,8 +2,8 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.poststats.golf</groupId>
<artifactId>golf-api</artifactId>
<version>1.0-SNAPSHOT</version>
<artifactId>golf-rs-api</artifactId>
<version>2.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
@@ -20,42 +20,50 @@
<dependencies>
<dependency>
<groupId>com.poststats</groupId>
<artifactId>poststats-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.poststats.golf</groupId>
<artifactId>golf-core</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>1.5</version>
<artifactId>poststats-rs-api</artifactId>
<version>2.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>
<!-- for unit testing -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.9.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.20.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.1.2</version>
</plugin>
<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>
<resourcePackage>com.poststats.golf.rs.api</resourcePackage>
</resourcePackages>
<outputPath>${project.build.outputDirectory}/META-INF</outputPath>
<outputFileName>golf-swagger</outputFileName>
<outputFormat>JSONANDYAML</outputFormat>
<prettyPrint>true</prettyPrint>
<sortOutput>true</sortOutput>
</configuration>
<executions>
<execution>

View File

@@ -1,65 +0,0 @@
package com.poststats.golf.api;
import com.brianlong.util.FlexMap;
import com.poststats.golf.api.model.Course;
import com.poststats.golf.service.CourseService;
import com.poststats.transformer.impl.DaoConverter;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.RequestScoped;
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.WebApplicationException;
import jakarta.ws.rs.core.Response.Status;
import org.slf4j.Logger;
/**
* @author brian.long@poststats.com
*/
@RequestScoped
@Path("/golf/course/{courseId}")
@Tag(name = "Course API")
@ApiResponse(responseCode = "200", description = "Success")
@ApiResponse(responseCode = "404", description = "A golf course with the specified ID could not be found")
public class CourseApi {
@Parameter(description = "A unique identifier for a golf course")
@PathParam("courseId")
private int courseId;
@Inject
private Logger logger;
@Inject
private CourseService courseService;
@Inject
private DaoConverter converter;
@PostConstruct
public void init() {
this.logger.debug("CourseApi init: {}", this.courseId);
}
@GET
@Produces(Constants.V1_JSON)
@Operation(
summary = "Retrieves limited meta-data about a course.",
description = "Retreives name, location, and other direct meta-data about the specified course."
)
public Course get() {
FlexMap row = this.courseService.get(this.courseId);
if (row == null)
throw new WebApplicationException("Course not found", Status.NOT_FOUND);
this.logger.trace("found: {}", this.courseId);
return this.converter.convertValue(row, Course.class);
}
}

View File

@@ -1,86 +0,0 @@
package com.poststats.golf.api;
import com.brianlong.util.FlexMap;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.poststats.api.Constants;
import com.poststats.golf.api.model.Event;
import com.poststats.golf.service.EventService;
import com.poststats.transformer.impl.DaoConverter;
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.enterprise.context.RequestScoped;
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.WebApplicationException;
import jakarta.ws.rs.core.Response.Status;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author brian.long@poststats.com
*/
@RequestScoped
@Path("/golf/event/{eventId}")
@Tag(name = "Event API")
public class EventApi {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@PathParam("eventId")
private long eventId;
@Inject
private EventService eventService;
@Inject
private DaoConverter converter;
@PostConstruct
public void init() {
this.logger.debug("EventApi init: {}", this.eventId);
}
@GET
@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 {
FlexMap row = this.eventService.get(this.eventId);
if (row == null)
throw new WebApplicationException("Event not found", Status.NOT_FOUND);
return this.converter.convertValue(row, Event.class);
}
@GET
@Path("/detail")
@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 Event getDetail() throws JsonProcessingException {
FlexMap row = this.eventService.get(this.eventId);
if (row == null)
throw new WebApplicationException("Event not found", Status.NOT_FOUND);
return this.converter.convertValue(row, Event.class);
}
}

View File

@@ -1,108 +0,0 @@
package com.poststats.golf.api;
import com.brianlong.sql.DataSet;
import com.fasterxml.jackson.core.JsonProcessingException;
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.enterprise.context.RequestScoped;
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.WebApplicationException;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.SecurityContext;
import jakarta.ws.rs.core.StreamingOutput;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;
@RequestScoped
@Path("/golf/event/{eventId}/finance")
@Tag(name = "Event Finance API")
public class EventFinanceApi {
@PathParam("eventId")
private long eventId;
@Inject
private EventFinanceService eventFinanceService;
@GET
@Path("/balance/persons")
@RolesAllowed(Constants.EVENT_ROLE_PREFIX + "member")
@Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves the balances of all participants in an event.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "401", description = "Not authenticated"),
@ApiResponse(responseCode = "403", description = "Authenticated, but not permitted"),
@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 {
List<DataSet> personsBalances = this.eventFinanceService.getPersonsBalances(this.eventId);
List<Map<String, Object>> personsBalancesJson = new ArrayList<>(personsBalances.size());
for (DataSet personBalance : personsBalances) {
Map<String, Object> personBalanceJson = new HashMap<>(5);
personBalanceJson.put("personId", personBalance.getLong("personID"));
personBalanceJson.put("expense", personBalance.getFloat("expense"));
personBalanceJson.put("paid", personBalance.getFloat("paid"));
personBalanceJson.put("balance", personBalance.getFloat("balance"));
personsBalancesJson.add(personBalanceJson);
}
return personsBalancesJson;
}
@GET
@Path("/balance/persons/csv")
@RolesAllowed(Constants.EVENT_ROLE_PREFIX + "finance")
@Produces("text/csv")
@Operation(summary = "Retrieves the balances of all participants in an event.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "401", description = "Not authenticated"),
@ApiResponse(responseCode = "403", description = "Authenticated, but not permitted"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
})
public StreamingOutput getBalanceByPersonsAsCsv(@Context SecurityContext securityContext) throws IOException {
List<DataSet> personsBalances = this.eventFinanceService.getPersonsBalances(this.eventId);
return new StreamingOutput() {
@Override
public void write(OutputStream output) throws IOException, WebApplicationException {
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) {
personsBalancesCsvPrinter.printRecord(personBalance.getLong("personID"),
personBalance.getString("lname"), personBalance.getString("fname"),
personBalance.getString("suffix"), personBalance.getFloat("expense"),
personBalance.getFloat("paid"), personBalance.getFloat("balance"));
}
personsBalancesCsvPrinter.flush();
} finally {
personsBalancesCsvPrinter.close();
}
}
};
}
}

View File

@@ -1,175 +0,0 @@
package com.poststats.golf.api;
import com.brianlong.util.FlexMap;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.poststats.api.model.Person;
import com.poststats.golf.service.EventPersonService;
import com.poststats.golf.service.EventService;
import com.poststats.transformer.impl.DaoConverter;
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.enterprise.context.RequestScoped;
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.core.StreamingOutput;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;
@RequestScoped
@Path("/golf/event/{eventId}")
@Tag(name = "Event Participant API")
public class EventPersonApi {
@PathParam("eventId")
private long eventId;
@Inject
private EventService eventService;
@Inject
private EventPersonService eventPersonService;
@Inject
private DaoConverter converter;
@GET
@Path("/people")
@RolesAllowed(Constants.EVENT_ROLE_PREFIX + "member")
@Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves limited meta-data about all the participants and administrators in an event.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "401", description = "Not authenticated"),
@ApiResponse(responseCode = "403", description = "Authenticated, but not permitted"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
})
public List<? extends Person> get() {
return this.converter.convertValue(this.eventPersonService.getPeople(this.eventId, false), Person.class);
}
@GET
@Path("/people/detail")
@RolesAllowed(Constants.EVENT_ROLE_PREFIX + "member")
@Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves detailed meta-data about all the participants and administrators in an event.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "401", description = "Not authenticated"),
@ApiResponse(responseCode = "403", description = "Authenticated, but not permitted"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
})
public List<? extends Person> getDetail() {
return this.converter.convertValue(this.eventPersonService.getPeople(this.eventId, true), Person.class);
}
@GET
@Path("/people/csv")
@RolesAllowed(Constants.EVENT_ROLE_PREFIX + "membership")
@Produces("text/csv")
@Operation(summary = "Retrieves address book meta-data about all the participants and administrators in an event.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "401", description = "Not authenticated"),
@ApiResponse(responseCode = "403", description = "Authenticated, but not permitted"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
})
public StreamingOutput getAsCsv() {
List<FlexMap> persons = this.eventPersonService.getPeople(this.eventId, true);
return this.toCsv(persons);
}
@GET
@Path("/participants")
@Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves limited meta-data about all 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<? extends Person> getParticipants() {
return this.converter.convertValue(this.eventPersonService.getParticipants(this.eventId, false), Person.class);
}
@GET
@Path("/participants/csv")
@RolesAllowed(Constants.EVENT_ROLE_PREFIX + "membership")
@Produces("text/csv")
@Operation(summary = "Retrieves address book meta-data about all the participants in an event.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "401", description = "Not authenticated"),
@ApiResponse(responseCode = "403", description = "Authenticated, but not permitted"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
})
public StreamingOutput getParticipantsAsCsv() {
List<FlexMap> persons = this.eventPersonService.getParticipants(this.eventId, true);
return this.toCsv(persons);
}
@GET
@Path("/series/participants")
@RolesAllowed(Constants.EVENT_ROLE_PREFIX + "membership")
@Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves limited meta-data about all the participants in an event series.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "401", description = "Not authenticated"),
@ApiResponse(responseCode = "403", description = "Authenticated, but not permitted"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
})
public List<? extends Person> getSeriesParticipants() throws JsonProcessingException, IOException {
int seriesId = this.eventService.getSeriesId(this.eventId);
Set<Long> eventIds = this.eventService.getIdsBySeriesId(seriesId);
Set<Long> personIds = new HashSet<>();
List<FlexMap> persons = new LinkedList<>();
for (long eventId : eventIds) {
List<FlexMap> tmpPersons = this.eventPersonService.getParticipants(eventId, false);
for (FlexMap person : tmpPersons)
if (personIds.add(person.getLong(com.poststats.sql.Constants.PERSON_ID)))
persons.add(person);
}
return this.converter.convertValue(persons, Person.class);
}
private StreamingOutput toCsv(List<FlexMap> persons) {
return new StreamingOutput() {
@Override
public void write(OutputStream output) throws IOException {
PrintStream pstream = new PrintStream(output);
CSVPrinter personsCsvPrinter = new CSVPrinter(pstream, CSVFormat.DEFAULT);
try {
personsCsvPrinter.printRecord("ID", "Prefix", "Last Name", "First Name", "Suffix", "Email Address",
"Mobile Phone", "Address Lines", "City", "State", "Country", "Postal Code");
for (FlexMap person : persons) {
personsCsvPrinter.printRecord(person.getLong("personID"), person.getString("prefix"),
person.getString("lname"), person.getString("fname"), person.getString("suffix"),
person.getString("email"), person.getString("cellphone"), person.getString("addrlines"),
person.getString("addrcity"), person.getString("addrstate"),
person.getString("addrcountry"), person.getString("addrzip"));
}
personsCsvPrinter.flush();
} finally {
personsCsvPrinter.close();
}
}
};
}
}

View File

@@ -1,112 +0,0 @@
package com.poststats.golf.api;
import com.brianlong.util.FlexMap;
import com.poststats.api.Constants;
import com.poststats.golf.api.model.EventRound;
import com.poststats.golf.service.EventRoundService;
import com.poststats.transformer.impl.DaoConverter;
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.enterprise.context.RequestScoped;
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.WebApplicationException;
import jakarta.ws.rs.core.Response.Status;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author brian.long@poststats.com
*/
@RequestScoped
@Path("/golf/event/{eventId}")
@Tag(name = "Event Round API")
public class EventRoundApi {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@PathParam("eventId")
private long eventId;
@Inject
private EventRoundService roundService;
@Inject
private DaoConverter converter;
@PostConstruct
public void init() {
this.logger.debug("EventRoundApi init: {}", this.eventId);
}
@GET
@Path("/r{number}")
@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 EventRound getOne(@PathParam("number") short roundNumber) {
FlexMap row = this.roundService.get(this.eventId, roundNumber);
if (row == null)
throw new WebApplicationException("Event round not found", Status.NOT_FOUND);
return this.converter.convertValue(row, EventRound.class);
}
@GET
@Path("/round/{eroundId}")
@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 EventRound getOne(@PathParam("eroundId") long eroundId) {
FlexMap row = this.roundService.get(eroundId);
if (row == null)
throw new WebApplicationException("Event round not found", Status.NOT_FOUND);
if (this.eventId != row.getLong("eventID")) {
this.logger.warn("The event round {} was requested without the appropriate event ID {}", eroundId,
this.eventId);
throw new WebApplicationException("Event round not found", Status.NOT_FOUND);
}
return this.converter.convertValue(row, EventRound.class);
}
@GET
@Path("/rounds")
@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 List<EventRound> getAll() {
Map<Long, ? extends FlexMap> rows = this.roundService.getByEventId(this.eventId);
if (rows.isEmpty())
throw new WebApplicationException("No event rounds found", Status.NOT_FOUND);
return this.converter.convertValue(rows.values(), EventRound.class);
}
}

View File

@@ -1,67 +0,0 @@
package com.poststats.golf.api;
import com.brianlong.util.FlexMap;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.poststats.api.Constants;
import com.poststats.golf.api.model.Golfer;
import com.poststats.golf.service.PersonService;
import com.poststats.transformer.impl.DaoConverter;
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.enterprise.context.RequestScoped;
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.WebApplicationException;
import jakarta.ws.rs.core.Response.Status;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author brian.long@poststats.com
*/
@RequestScoped
@Path("/golfer/{personId}")
@Tag(name = "Golfer API")
public class GolferApi {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@PathParam("personId")
private long personId;
@Inject
private PersonService personService;
@Inject
private DaoConverter converter;
@PostConstruct
public void init() {
this.logger.debug("GolferApi init: {}", this.personId);
}
@GET
@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 Golfer get() throws JsonProcessingException {
FlexMap row = this.personService.get(this.personId);
if (row == null)
throw new WebApplicationException("Event not found", Status.NOT_FOUND);
return this.converter.convertValue(row, Golfer.class);
}
}

View File

@@ -1,107 +0,0 @@
package com.poststats.golf.api;
import com.brianlong.util.FlexMap;
import com.poststats.api.Constants;
import com.poststats.golf.api.model.Event;
import com.poststats.golf.api.model.Series;
import com.poststats.golf.service.EventService;
import com.poststats.golf.service.SeriesService;
import com.poststats.transformer.impl.DaoConverter;
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.enterprise.context.RequestScoped;
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.WebApplicationException;
import jakarta.ws.rs.core.Response.Status;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author brian.long@poststats.com
*/
@RequestScoped
@Path("/golf/series/{seriesId}")
@Tag(name = "Event Series 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 SeriesApi {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@PathParam("seriesId")
private int seriesId;
@Inject
private SeriesService seriesService;
@Inject
private EventService eventService;
@Inject
private DaoConverter converter;
@PostConstruct
public void init() {
this.logger.debug("SeriesApi init: {}", this.seriesId);
}
@GET
@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() {
FlexMap row = this.seriesService.get(this.seriesId);
if (row == null)
throw new WebApplicationException("Series not found", Status.NOT_FOUND);
return this.converter.convertValue(row, Series.class);
}
@GET
@Path("/events")
@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) {
Map<Long, ? extends FlexMap> rows = this.eventService.getBySeriesId(this.seriesId);
if (rows.isEmpty())
throw new WebApplicationException("Series or events not found", Status.NOT_FOUND);
List<Event> events = this.converter.convertValue(rows.values(), Event.class);
if (Boolean.TRUE.equals(reverse))
Collections.reverse(events);
return events;
}
}

View File

@@ -1,63 +0,0 @@
package com.poststats.golf.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.api.model.Facility;
import com.poststats.transformer.MapEntry;
@JsonIgnoreProperties(ignoreUnknown = true)
public class Course extends BaseCourse<Course> implements ReferenceableCourse {
@JsonProperty(required = true, access = com.fasterxml.jackson.annotation.JsonProperty.Access.READ_ONLY)
@MapEntry("courseID")
private int id;
@JsonProperty
@MapEntry
private Facility facility;
@JsonProperty
@MapEntry("facilityID")
private Integer facilityId;
@Override
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Facility getFacility() {
return facility;
}
public void setFacility(Facility facility) {
this.facility = facility;
}
public Integer getFacilityId() {
return facilityId;
}
public void setFacilityId(Integer facilityId) {
this.facilityId = facilityId;
}
public Course withId(int id) {
this.id = id;
return this;
}
public Course withFacility(Facility facility) {
this.facility = facility;
return this;
}
public Course withFacilityId(Integer facilityId) {
this.facilityId = facilityId;
return this;
}
}

View File

@@ -1,38 +0,0 @@
package com.poststats.golf.provider.impl;
import com.poststats.golf.api.Constants;
import com.poststats.provider.ConnectionProvider;
import com.poststats.provider.DataSourceProvider;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import java.sql.Connection;
@RequestScoped
@Named(Constants.CONNECTION_PROVIDER_GOLF)
public class DefaultConnectionProvider implements ConnectionProvider {
@Inject
@Named(Constants.DATASOURCE_PROVIDER_GOLF)
private DataSourceProvider dsp;
private Connection dbcon;
@PostConstruct
public void acquire() {
this.dbcon = this.dsp.get().acquire(true);
}
@PreDestroy
public void release() {
this.dsp.get().release(this.dbcon);
}
@Override
public Connection get() {
return this.dbcon;
}
}

View File

@@ -1,18 +0,0 @@
package com.poststats.golf.provider.impl;
import com.brianlong.sql.DataSource;
import com.poststats.golf.api.Constants;
import com.poststats.golf.sql.GolfDataSource;
import com.poststats.provider.impl.AbstractDataSourceProvider;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Named;
@ApplicationScoped
@Named(Constants.DATASOURCE_PROVIDER_GOLF)
public class DefaultDataSourceProvider extends AbstractDataSourceProvider {
public DataSource createDataSource() {
return GolfDataSource.getInstance();
}
}

View File

@@ -1,29 +0,0 @@
package com.poststats.golf.provider.impl;
import com.poststats.golf.api.Constants;
import com.poststats.golf.sql.GolfSQL;
import com.poststats.provider.ConnectionProvider;
import com.poststats.provider.impl.AbstractStatementProvider;
import jakarta.enterprise.context.Dependent;
import jakarta.inject.Inject;
import jakarta.inject.Named;
@Dependent
@Named(Constants.STATEMENT_PROVIDER_GOLF)
public class DefaultStatementProvider extends AbstractStatementProvider {
@Inject
@Named(Constants.CONNECTION_PROVIDER_GOLF)
private ConnectionProvider cp;
@Override
protected ConnectionProvider getConnectionProvider() {
return this.cp;
}
@Override
protected String transformSchema(String sql) {
return GolfSQL.changeSchema(sql);
}
}

View File

@@ -1,38 +0,0 @@
package com.poststats.golf.provider.impl;
import com.poststats.golf.api.Constants;
import com.poststats.provider.ConnectionProvider;
import com.poststats.provider.DataSourceProvider;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import java.sql.Connection;
@RequestScoped
@Named(Constants.CONNECTION_PROVIDER_GOLF_TX)
public class TxConnectionProvider implements ConnectionProvider {
@Inject
@Named(Constants.DATASOURCE_PROVIDER_GOLF)
private DataSourceProvider dsp;
private Connection dbcon;
@PostConstruct
public void acquire() {
this.dbcon = this.dsp.get().acquireTX(true);
}
@PreDestroy
public void release() {
this.dsp.get().releaseTX(this.dbcon);
}
@Override
public Connection get() {
return this.dbcon;
}
}

View File

@@ -1,29 +0,0 @@
package com.poststats.golf.provider.impl;
import com.poststats.golf.api.Constants;
import com.poststats.golf.sql.GolfSQL;
import com.poststats.provider.ConnectionProvider;
import com.poststats.provider.impl.AbstractStatementProvider;
import jakarta.enterprise.context.Dependent;
import jakarta.inject.Inject;
import jakarta.inject.Named;
@Dependent
@Named(Constants.STATEMENT_PROVIDER_GOLF_TX)
public class TxStatementProvider extends AbstractStatementProvider {
@Inject
@Named(Constants.CONNECTION_PROVIDER_GOLF_TX)
private ConnectionProvider cp;
@Override
protected ConnectionProvider getConnectionProvider() {
return this.cp;
}
@Override
protected String transformSchema(String sql) {
return GolfSQL.changeSchema(sql);
}
}

View File

@@ -0,0 +1,76 @@
package com.poststats.golf.rs.api;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.security.RolesAllowed;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
/**
* @author brian.long@poststats.com
*/
@Path("/golf/admin")
@Tag(name = "Administrative API")
@RolesAllowed(Constants.ADMIN_ROLE)
@SecurityRequirement(name = "basic")
public interface AdministrativeApi {
@PUT
@Path("/init/golfer/{personId}/season")
@Operation(description = "Sets up the current season handicap of the specified person for the virtual tournament.")
@ApiResponses({
@ApiResponse(responseCode = "204", description = "Success"),
@ApiResponse(responseCode = "401", description = "Not authenticated"),
@ApiResponse(responseCode = "403", description = "Authenticated, but not permitted"),
@ApiResponse(responseCode = "404", description = "Person not found"),
})
void setupScoringPointSeason(@Parameter(description = "A unique idenifier of a golfer")
@NotNull @Positive @PathParam("personId") long personId);
@PUT
@Path("/init/season")
@Produces(Constants.V1_JSON)
@Operation(description = "Sets up the current season handicaps for the virtual tournament.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "401", description = "Not authenticated"),
@ApiResponse(responseCode = "403", description = "Authenticated, but not permitted"),
})
long setupScoringPointSeason();
@PUT
@Path("/init/pointIndex/season/{season:[0-9]{4}}")
@Produces(Constants.V1_JSON)
@Deprecated
@Operation(description = "Transforms the legacy pointID to the new pointIndexID for the virtual tournament.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "401", description = "Not authenticated"),
@ApiResponse(responseCode = "403", description = "Authenticated, but not permitted"),
@ApiResponse(responseCode = "404", description = "Season not found"),
})
long transformScoringPointSeason(
@Parameter(description = "A year") @NotNull @Min(2000) @PathParam("season") int season);
@PUT
@Path("/init/pointIndex/season")
@Produces(Constants.V1_JSON)
@Deprecated
@Operation(description = "Transforms the legacy pointID to the new pointIndexID for the virtual tournament.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "401", description = "Not authenticated"),
@ApiResponse(responseCode = "403", description = "Authenticated, but not permitted"),
})
long transformScoringPointSeason();
}

View File

@@ -0,0 +1,25 @@
package com.poststats.golf.rs.api;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
import io.swagger.v3.oas.annotations.info.Contact;
import io.swagger.v3.oas.annotations.info.Info;
import io.swagger.v3.oas.annotations.security.SecurityScheme;
import jakarta.ws.rs.Path;
/**
* @author brian.long@poststats.com
*/
@Path("/")
@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.",
version = "v1"
)
)
@SecurityScheme(name = "basic", type = SecuritySchemeType.HTTP, scheme = "BASIC")
public interface BaseApi {
}

View File

@@ -1,18 +1,13 @@
package com.poststats.golf.api;
package com.poststats.golf.rs.api;
public class Constants extends com.poststats.api.Constants {
public static final String DATASOURCE_PROVIDER_GOLF = "dbds-golf";
public static final String CONNECTION_PROVIDER_GOLF = "dbcon-golf";
public static final String CONNECTION_PROVIDER_GOLF_TX = "dbcon-golf-tx";
public static final String STATEMENT_PROVIDER_GOLF = "dbstmt-golf";
public static final String STATEMENT_PROVIDER_GOLF_TX = "dbstmt-golf-tx";
public class Constants extends com.poststats.rs.Constants {
public static final String BUDDY_ROLE = "buddy";
public static final String EVENT_ROLE = "event";
public static final String EVENT_SERIES_ROLE = "series";
public static final String COURSE_ROLE = "course";
public static final String BUDDY_ROLE_PREFIX = BUDDY_ROLE + ":";
public static final String BUDDY_ROLE_PREFIX = BUDDY_ROLE
+ ":";
public static final String EVENT_ROLE_PREFIX = "event:";
public static final String EVENT_SERIES_ROLE_PREFIX = "series:";
public static final String COURSE_ROLE_PREFIX = "course:";

View File

@@ -0,0 +1,42 @@
package com.poststats.golf.rs.api;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.security.RolesAllowed;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
/**
* @author brian.long@poststats.com
*/
@Path("/golf/course/{courseId:[0-9]+}")
@Tag(name = "Course API")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "401", description = "Not authenticated"),
@ApiResponse(responseCode = "403", description = "Authenticated, but not permitted"),
@ApiResponse(responseCode = "404", description = "A golf course with the specified ID could not be found")
})
public interface CourseAdminApi {
@PUT
@Path("/pointIndex")
@Produces(Constants.V1_JSON)
@RolesAllowed(Constants.ADMIN_ROLE)
@SecurityRequirement(name = "basic")
@Operation(
summary = "Intializes a golf course point rating",
description = "Computes the point rating/adjustment for a single golf course and stores the result in the database. The result is a count of updated records."
)
byte initGolfCoursePointRatings(
@Parameter(description = "A golf course ID") @NotNull @Positive @PathParam("courseId") int courseId);
}

View File

@@ -0,0 +1,67 @@
package com.poststats.golf.rs.api;
import com.poststats.golf.rs.api.model.Course;
import com.poststats.golf.rs.api.model.CourseNine;
import com.poststats.golf.rs.api.model.CourseTee;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
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.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
/**
* @author brian.long@poststats.com
*/
@Path("/golf/course/{courseId:[0-9]+}")
@Tag(name = "Course API")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "404", description = "A golf course with the specified ID could not be found")
})
public interface CourseApi {
@GET
@Produces(Constants.V1_JSON)
@Operation(
summary = "Retrieves meta-data about a course.",
description = "Retreives name, location, and other direct meta-data about the specified course."
)
Course get(@Parameter(description = "A unique identifier for a golf course")
@NotNull @Positive @PathParam("courseId") int courseId);
@GET
@Path("/nine/byName/{name}")
@Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves meta-data about a course nine.")
CourseNine getNine(@Parameter(description = "A unique identifier for a golf course")
@NotNull @Positive @PathParam("courseId") int courseId, @PathParam("name") String name);
@GET
@Path("/nine/{nineId:[0-9]+}")
@Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves limited meta-data about a course nine.")
CourseNine getNine(@Parameter(description = "A unique identifier for a golf course")
@NotNull @Positive @PathParam("courseId") int courseId, @PathParam("nineId") long courseNineId);
@GET
@Path("/nine/byName/{name}")
@Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves meta-data about a course tee.")
CourseTee getTee(@Parameter(description = "A unique identifier for a golf course")
@NotNull @Positive @PathParam("courseId") int courseId, @PathParam("name") String name);
@GET
@Path("/nine/{nineId:[0-9]+}")
@Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves limited meta-data about a course tee.")
CourseTee getTee(@Parameter(description = "A unique identifier for a golf course")
@NotNull @Positive @PathParam("courseId") int courseId, @PathParam("teeId") long courseTeeId);
}

View File

@@ -0,0 +1,73 @@
package com.poststats.golf.rs.api;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.security.RolesAllowed;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
/**
* @author brian.long@poststats.com
*/
@Path("/golf/courses")
@Tag(name = "Course API")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "401", description = "Not authenticated"),
@ApiResponse(responseCode = "403", description = "Authenticated, but not permitted"),
})
public interface CoursesAdminApi {
@PUT
@Path("/etrating/{etratingId:[0-9]+}/pointIndex")
@Produces(Constants.V1_JSON)
@RolesAllowed(Constants.ADMIN_ROLE)
@SecurityRequirement(name = "basic")
@Operation(
summary = "Intializes a golf course eighteen/tee point rating",
description = "Computes the point rating/adjustment for a single golf course eighteen/tee rating and stores the result in the database. The result is the computed point rating."
)
@ApiResponses({
@ApiResponse(responseCode = "404", description = "Eighteen/tee rating not found"),
})
byte initGolfCourseEighteenTeeRatingPointRating(@Parameter(description = "A golf course eighteen/tee rating ID")
@NotNull @Positive @PathParam("etratingId") long etratingId);
@PUT
@Path("/ntrating/{ntratingId:[0-9]+}/pointIndex")
@Produces(Constants.V1_JSON)
@RolesAllowed(Constants.ADMIN_ROLE)
@SecurityRequirement(name = "basic")
@Operation(
summary = "Intializes a golf course nine/tee point rating",
description = "Computes the point rating/adjustment for a single golf course nine/tee rating and stores the result in the database. The result is the computed point rating."
)
@ApiResponses({
@ApiResponse(responseCode = "404", description = "Nine/tee rating not found"),
})
byte initGolfCourseNineTeeRatingPointRating(@Parameter(description = "A golf course nine/tee rating ID")
@NotNull @Positive @PathParam("ntratingId") long ntratingId);
@PUT
@Path("/pointIndex")
@Produces(Constants.V1_JSON)
@RolesAllowed(Constants.ADMIN_ROLE)
@SecurityRequirement(name = "basic")
@Operation(
summary = "Intializes golf course point ratings",
description = "Computes the point rating/adjustment for all golf courses and stores the result in the database. The result is a count of updated records."
)
long initGolfCoursePointRatings(
@Parameter(description = "false to compute missing ratings only; true will compute all ratings")
@QueryParam("overwrite") boolean overwrite);
}

View File

@@ -1,21 +1,17 @@
package com.poststats.golf.api;
package com.poststats.golf.rs.api;
import java.util.List;
import com.poststats.golf.rs.api.model.Course;
import com.poststats.rs.api.model.PagedCollection;
import com.poststats.rs.api.model.Pagination;
import com.brianlong.util.FlexMap;
import com.brianlong.util.SubList;
import com.poststats.api.model.PagedCollection;
import com.poststats.api.model.Pagination;
import com.poststats.golf.api.model.Course;
import com.poststats.golf.service.CourseService;
import com.poststats.transformer.impl.DaoConverter;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
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.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.validation.Valid;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
@@ -27,15 +23,10 @@ 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.Response.Status;
import java.util.List;
import org.slf4j.Logger;
/**
* @author brian.long@poststats.com
*/
@RequestScoped
@Path("/golf/courses")
@Tag(name = "Course API")
@ApiResponses({
@@ -43,21 +34,7 @@ import org.slf4j.Logger;
@ApiResponse(responseCode = "400", description = "A query parameter is not valid"),
@ApiResponse(responseCode = "404", description = "No matching golf courses were found")
})
public class CoursesApi {
@Inject
private Logger logger;
@Inject
private CourseService courseService;
@Inject
private DaoConverter converter;
@PostConstruct
public void init() {
this.logger.debug("CoursesApi init");
}
public interface CoursesApi {
@GET
@Path("/byName")
@@ -74,17 +51,7 @@ public class CoursesApi {
)
})
@Tag(name = "Search API")
public PagedCollection<List<Course>> searchByName(@QueryParam("name") String name,
@BeanParam @Valid Pagination paging) {
SubList<? extends FlexMap> rows = this.courseService.findByName(name, paging.getPage(), paging.getPerPage());
if (rows.isEmpty())
throw new WebApplicationException("No matching courses found", Status.NOT_FOUND);
this.logger.trace("found: {} [{}-{}] of {}", rows.size(), rows.getStartIndex() + 1, rows.getEndIndex(),
rows.getTotal());
return this.converter.convertValue(rows, Course.class).withPage(paging.getPage())
.withPerPage(paging.getPerPage());
}
PagedCollection<List<Course>> searchByName(@QueryParam("name") String name, @BeanParam @Valid Pagination paging);
@GET
@Path("/byLocation/{country}/{state}")
@@ -98,18 +65,8 @@ public class CoursesApi {
@Parameter(name = "state", description = "A State or high-level jurisdiction", example = "FL")
})
@Tag(name = "Search API")
public PagedCollection<List<Course>> searchByJurisdiction(@PathParam("country") String country,
@PathParam("state") String state, @BeanParam @Valid Pagination paging) {
SubList<? extends FlexMap> rows = this.courseService.findByJurisdiction(country, state, paging.getPage(),
paging.getPerPage());
if (rows.isEmpty())
throw new WebApplicationException("No matching golf courses found", Status.NOT_FOUND);
this.logger.trace("found: {} [{}-{}] of {}", rows.size(), rows.getStartIndex() + 1, rows.getEndIndex(),
rows.getTotal());
return this.converter.convertValue(rows, Course.class).withPage(paging.getPage())
.withPerPage(paging.getPerPage());
}
PagedCollection<List<Course>> searchByJurisdiction(@PathParam("country") String country,
@PathParam("state") String state, @BeanParam @Valid Pagination paging);
@GET
@Path("/byGeo")
@@ -133,19 +90,9 @@ public class CoursesApi {
), @Parameter(name = "radius", description = "A search radius in miles", example = "10")
})
@Tag(name = "Search API")
public PagedCollection<List<Course>> searchByJurisdiction(
PagedCollection<List<Course>> searchByJurisdiction(
@QueryParam("latitude") @DecimalMin("-90.0") @DecimalMax("90.0") double latitude,
@QueryParam("longitude") @DecimalMin("-180.0") @DecimalMax("180.0") double longitude,
@QueryParam("radius") @Positive @Max(100) Integer radiusInMiles, @BeanParam @Valid Pagination paging) {
SubList<? extends FlexMap> rows = this.courseService.findByLocation(latitude, longitude, radiusInMiles,
paging.getPage(), paging.getPerPage());
if (rows.isEmpty())
throw new WebApplicationException("No matching facilities found", Status.NOT_FOUND);
this.logger.trace("found: {} [{}-{}] of {}", rows.size(), rows.getStartIndex() + 1, rows.getEndIndex(),
rows.getTotal());
return this.converter.convertValue(rows, Course.class).withPage(paging.getPage())
.withPerPage(paging.getPerPage());
}
@QueryParam("radius") @Positive @Max(100) Integer radiusInMiles, @BeanParam @Valid Pagination paging);
}

View File

@@ -0,0 +1,103 @@
package com.poststats.golf.rs.api;
import com.poststats.golf.rs.api.model.Event;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
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.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
/**
* @author brian.long@poststats.com
*/
@Path("/golf/event/{eventId}")
@Tag(name = "Event API")
public interface EventApi {
@GET
@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")
})
Event get(@Parameter(description = "A unique identifier for an event")
@NotNull @Positive @PathParam("eventId") long eventId);
@GET
@Path("/detail")
@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")
})
Event getDetail(@Parameter(description = "A unique identifier for an event")
@NotNull @Positive @PathParam("eventId") long eventId);
@POST
@Path("/document/{documentId}/send")
@RolesAllowed(
Constants.EVENT_ROLE_PREFIX
+ "communicate"
)
@Produces(Constants.V1_JSON)
@Operation(
summary = "Sends the specified document.",
description = "Sends the specified document off-schedule, regardless of when it is configured to send."
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(
responseCode = "404",
description = "An event or document with the specified ID could not be found"
)
})
void sendDocument(
@Parameter(description = "A unique identifier for an event")
@NotNull @Positive @PathParam("eventId") long eventId,
@Parameter(description = "A unique identifier for an event document")
@NotNull @Positive @PathParam("documentId") long documentId);
@POST
@Path("/document/{documentId}/sendTest/{personId}")
@RolesAllowed(
Constants.EVENT_ROLE_PREFIX
+ "communicate"
)
@Produces(Constants.V1_JSON)
@Operation(
summary = "Sends the specified document to the specified person.",
description = "Sends the specified document to only the specified person off-schedule."
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(
responseCode = "404",
description = "An event, document, or person with the specified ID could not be found"
)
})
void sendTestDocument(
@Parameter(description = "A unique identifier for an event")
@NotNull @Positive @PathParam("eventId") long eventId,
@Parameter(description = "A unique identifier for an event document")
@NotNull @Positive @PathParam("documentId") long documentId,
@Parameter(description = "A unique identifier for a person")
@NotNull @Positive @PathParam("personId") long personId);
}

View File

@@ -0,0 +1,100 @@
package com.poststats.golf.rs.api;
import java.util.List;
import com.poststats.golf.rs.api.model.EventDocument;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
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.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
/**
* @author brian.long@poststats.com
*/
@Path("/golf/event/{eventId}")
@Tag(name = "Event Round API")
public interface EventDocumentApi {
@GET
@Path("/document/{documentId:[0-9]+}")
@Produces(Constants.V1_JSON)
@Operation(
summary = "Retrieves limited meta-data about the specified document.",
description = "Retreives name, location, dates, and other direct meta-data about the specified document."
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(
responseCode = "404",
description = "An event or event round with the specified ID could not be found"
)
})
EventDocument getOne(
@Parameter(description = "A unique identifier for an event")
@NotNull @Positive @PathParam("eventId") long eventId,
@Parameter(description = "A unique identifier for an event document")
@NotNull @Positive @PathParam("documentId") long documentId);
@GET
@Path("/documents")
@Produces(Constants.V1_JSON)
@Operation(
summary = "Retrieves limited meta-data about all the documents in the specified event.",
description = "Retreives name, location, dates, and other direct meta-data about all the documents in the specified event."
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(
responseCode = "404",
description = "An event with the specified ID or any event documents could not be found"
)
})
List<EventDocument> getAll(@Parameter(description = "A unique identifier for an event")
@NotNull @Positive @PathParam("eventId") long eventId);
@POST
@Path("/document/{documentId:[0-9]+}/sendAgenda")
@Produces(Constants.V1_JSON)
@Operation(summary = "Sends the specified agenda immediately.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(
responseCode = "404",
description = "A document with the specified ID in the specified event could not be found"
)
})
void sendAgenda(
@Parameter(description = "A unique identifier for an event")
@NotNull @Positive @PathParam("eventId") long eventId,
@Parameter(description = "A unique identifier for an event document")
@NotNull @Positive @PathParam("documentId") long documentId);
@POST
@Path("/document/{documentId:[0-9]+}/sendAgenda/{personId:[0-9]+}")
@Produces(Constants.V1_JSON)
@Operation(summary = "Sends the specified agenda immediately.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(
responseCode = "404",
description = "A document with the specified ID in the specified event could not be found"
)
})
void sendAgenda(
@Parameter(description = "A unique identifier for an event")
@NotNull @Positive @PathParam("eventId") long eventId,
@Parameter(description = "A unique identifier for an event document")
@NotNull @Positive @PathParam("documentId") long documentId,
@Parameter(description = "A unique identifier for a person")
@NotNull @Positive @PathParam("personId") long personId);
}

View File

@@ -0,0 +1,107 @@
package com.poststats.golf.rs.api;
import java.util.List;
import java.util.Map;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.security.RolesAllowed;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
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.StreamingOutput;
@Path("/golf/event/{eventId}/finance")
@Tag(name = "Event Finance API")
public interface EventFinanceApi {
@GET
@Path("/balance/persons")
@RolesAllowed(
Constants.EVENT_ROLE_PREFIX
+ "member"
)
@Produces(Constants.V1_JSON)
@SecurityRequirement(name = "basic")
@Operation(summary = "Retrieves the balances of all persons associated with an event.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "401", description = "Not authenticated"),
@ApiResponse(responseCode = "403", description = "Authenticated, but not permitted"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
})
List<Map<String, Object>> getBalanceByPersonsAsJson(
@Parameter(description = "A unique identifier for an event")
@NotNull @Positive @PathParam("eventId") long eventId, @QueryParam("minBalance") Float minBalance,
@QueryParam("maxBalance") Float maxBalance);
@GET
@Path("/balance/persons/csv")
@RolesAllowed(
Constants.EVENT_ROLE_PREFIX
+ "finance"
)
@Produces("text/csv")
@SecurityRequirement(name = "basic")
@Operation(summary = "Retrieves the balances of all persons associated with an event.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "401", description = "Not authenticated"),
@ApiResponse(responseCode = "403", description = "Authenticated, but not permitted"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
})
StreamingOutput getBalanceByPersonsAsCsv(@Parameter(description = "A unique identifier for an event")
@NotNull @Positive @PathParam("eventId") long eventId);
@GET
@Path("/balance/person/{personId}")
@RolesAllowed(
Constants.EVENT_ROLE_PREFIX
+ "member"
)
@Produces(Constants.V1_JSON)
@SecurityRequirement(name = "basic")
@Operation(summary = "Retrieves the balance of a person in an event.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "401", description = "Not authenticated"),
@ApiResponse(responseCode = "403", description = "Authenticated, but not permitted"),
@ApiResponse(
responseCode = "404",
description = "An event or person with the specified IDs could not be found"
)
})
Map<String, Object> getBalanceByPersonsAsJson(
@Parameter(description = "A unique identifier for an event")
@NotNull @Positive @PathParam("eventId") long eventId,
@Parameter(description = "A unique identifier for a person")
@NotNull @Positive @PathParam("personId") long personId);
@GET
@Path("/series/balance/persons")
@RolesAllowed(
Constants.EVENT_ROLE_PREFIX
+ "member"
)
@Produces(Constants.V1_JSON)
@SecurityRequirement(name = "basic")
@Operation(summary = "Retrieves the cumulative balances of all persons associated with a series prior to an event.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "401", description = "Not authenticated"),
@ApiResponse(responseCode = "403", description = "Authenticated, but not permitted"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
})
List<Map<String, Object>> getSeriesBalanceByPersonsAsJson(
@Parameter(description = "A unique identifier for an event")
@NotNull @Positive @PathParam("eventId") long eventId);
}

View File

@@ -0,0 +1,128 @@
package com.poststats.golf.rs.api;
import java.util.List;
import java.util.Set;
import com.poststats.golf.rs.api.model.EventPerson;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.security.RolesAllowed;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.StreamingOutput;
@Path("/golf/event/{eventId}")
@Tag(name = "Event Participant API")
public interface EventPersonApi {
@GET
@Path("/people")
@RolesAllowed(
Constants.EVENT_ROLE_PREFIX
+ "member"
)
@Produces(Constants.V1_JSON)
@SecurityRequirement(name = "basic")
@Operation(summary = "Retrieves limited meta-data about all the participants and administrators in an event.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "401", description = "Not authenticated"),
@ApiResponse(responseCode = "403", description = "Authenticated, but not permitted"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
})
List<? extends EventPerson> get(@Parameter(description = "A unique identifier for an event")
@NotNull @Positive @PathParam("eventId") long eventId);
@GET
@Path("/people/detail")
@RolesAllowed(
Constants.EVENT_ROLE_PREFIX
+ "member"
)
@Produces(Constants.V1_JSON)
@SecurityRequirement(name = "basic")
@Operation(summary = "Retrieves detailed meta-data about all the participants and administrators in an event.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "401", description = "Not authenticated"),
@ApiResponse(responseCode = "403", description = "Authenticated, but not permitted"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
})
List<? extends EventPerson> getDetail(@Parameter(description = "A unique identifier for an event")
@NotNull @Positive @PathParam("eventId") long eventId);
@GET
@Path("/people/csv")
@RolesAllowed(
Constants.EVENT_ROLE_PREFIX
+ "membership"
)
@Produces("text/csv")
@SecurityRequirement(name = "basic")
@Operation(summary = "Retrieves address book meta-data about all the participants and administrators in an event.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "401", description = "Not authenticated"),
@ApiResponse(responseCode = "403", description = "Authenticated, but not permitted"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
})
StreamingOutput getAsCsv(@Parameter(description = "A unique identifier for an event")
@NotNull @Positive @PathParam("eventId") long eventId);
@GET
@Path("/participants")
@Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves limited meta-data about all 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")
})
List<? extends EventPerson> getParticipants(@Parameter(description = "A unique identifier for an event")
@NotNull @Positive @PathParam("eventId") long eventId);
@GET
@Path("/participants/csv")
@RolesAllowed(
Constants.EVENT_ROLE_PREFIX
+ "membership"
)
@Produces("text/csv")
@SecurityRequirement(name = "basic")
@Operation(summary = "Retrieves address book meta-data about all the participants in an event.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "401", description = "Not authenticated"),
@ApiResponse(responseCode = "403", description = "Authenticated, but not permitted"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
})
StreamingOutput getParticipantsAsCsv(@Parameter(description = "A unique identifier for an event")
@NotNull @Positive @PathParam("eventId") long eventId);
@GET
@Path("/series/participants")
@RolesAllowed(
Constants.EVENT_ROLE_PREFIX
+ "membership"
)
@Produces(Constants.V1_JSON)
@SecurityRequirement(name = "basic")
@Operation(summary = "Retrieves limited meta-data about all the participants in an event series.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "401", description = "Not authenticated"),
@ApiResponse(responseCode = "403", description = "Authenticated, but not permitted"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
})
Set<Long> getSeriesParticipants(@Parameter(description = "A unique identifier for an event")
@NotNull @Positive @PathParam("eventId") long eventId);
}

View File

@@ -0,0 +1,138 @@
package com.poststats.golf.rs.api;
import java.math.BigInteger;
import java.util.List;
import com.poststats.golf.rs.api.model.EventRound;
import com.poststats.golf.rs.api.model.EventRoundPairing;
import com.poststats.golf.rs.api.model.EventRoundPairingOrder;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
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.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
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.StreamingOutput;
/**
* @author brian.long@poststats.com
*/
@Path("/golf/event/{eventId}")
@Tag(name = "Event Round API")
public interface EventRoundApi {
@GET
@Path("/rounds/next")
@Produces(Constants.V1_JSON)
@Operation(
summary = "Retrieves limited meta-data about the next round in the specified event.",
description = "Retreives name, location, dates, and other direct meta-data about the upcoming round in the specified event."
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(
responseCode = "404",
description = "An event with the specified ID or upcoming event rounds could not be found"
)
})
List<EventRound> getNext(@Parameter(description = "A unique identifier for an event")
@NotNull @Positive @PathParam("eventId") long eventId);
@GET
@Path("/round/{eroundId:[0-9]+}")
@Produces(Constants.V1_JSON)
@Operation(
summary = "Retrieves limited meta-data about the specified event round.",
description = "Retreives name, location, dates, and other direct meta-data about the specified event round."
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(
responseCode = "404",
description = "An event or event round with the specified ID could not be found"
)
})
EventRound getOne(
@Parameter(description = "A unique identifier for an event")
@NotNull @Positive @PathParam("eventId") long eventId,
@Parameter(description = "A unique identifier for an event round")
@NotNull @Positive @PathParam("eroundId") long eroundId);
@GET
@Path("/rounds")
@Produces(Constants.V1_JSON)
@Operation(
summary = "Retrieves limited meta-data about all the rounds in the specified event.",
description = "Retreives name, location, dates, and other direct meta-data about all the rounds in the specified event."
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(
responseCode = "404",
description = "An event with the specified ID or any event rounds could not be found"
)
})
List<EventRound> getAll(@Parameter(description = "A unique identifier for an event")
@NotNull @Positive @PathParam("eventId") long eventId);
@GET
@Path("/round/{eroundId:[0-9]+}/pairings")
@Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves limited meta-data about all pairings for a single event round.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(
responseCode = "404",
description = "An event or its round with the specified ID could not be found"
)
})
List<EventRoundPairing> getPairings(
@Parameter(description = "A unique identifier for an event")
@NotNull @Positive @PathParam("eventId") long eventId,
@Parameter(description = "A unique identifier for an event round")
@NotNull @Positive @PathParam("eroundId") long eroundId);
@GET
@Path("/round/{eroundId:[0-9]+}/pairings/csv")
@Produces("text/csv")
@Operation(summary = "Retrieves limited meta-data about all pairings for a single event round in the CSV format.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(
responseCode = "404",
description = "An event or its round with the specified ID could not be found"
)
})
StreamingOutput getPairingsAsCsv(
@Parameter(description = "A unique identifier for an event")
@NotNull @Positive @PathParam("eventId") long eventId,
@Parameter(description = "A unique identifier for an event round")
@NotNull @Positive @PathParam("eroundId") long eroundId,
@QueryParam("orderBy") List<EventRoundPairingOrder> orderBys,
@QueryParam("lastNameFirst") boolean lastNameFirst);
@GET
@Path("/round/{eroundId:[0-9]+}/pairing/{pairingID:[0-9]+}")
@Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves limited meta-data about an event round pairing.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(
responseCode = "404",
description = "An event with the specified ID or upcoming event rounds could not be found"
)
})
EventRoundPairing getPairing(
@Parameter(description = "A unique identifier for an event")
@NotNull @Positive @PathParam("eventId") long eventId,
@Parameter(description = "A unique identifier for an event round")
@NotNull @Positive @PathParam("eroundId") long eroundId, @PathParam("pairingID") BigInteger pairingId);
}

View File

@@ -0,0 +1,35 @@
package com.poststats.golf.rs.api;
import com.poststats.golf.rs.api.model.Golfer;
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.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
/**
* @author brian.long@poststats.com
*/
@Path("/golfer/{personId:[0-9]+}")
@Tag(name = "Golfer API")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "404", description = "A golfer with the specified ID could not be found")
})
public interface GolferApi {
@GET
@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."
)
Golfer get(@NotNull @Positive @PathParam("personId") long personId);
}

View File

@@ -0,0 +1,50 @@
package com.poststats.golf.rs.api;
import java.math.BigInteger;
import com.poststats.golf.rs.api.model.Round;
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.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.security.RolesAllowed;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
/**
* @author brian.long@poststats.com
*/
@Path("/golf/round/{roundId}")
@Tag(name = "Round API")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "404", description = "A round with the specified ID could not be found")
})
public interface RoundApi {
@GET
@Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves limited meta-data about a round.")
Round get(@NotNull @Positive @PathParam("roundId") BigInteger roundId);
@PUT
@Path("/recompute")
@RolesAllowed(Constants.ADMIN_ROLE)
@SecurityRequirement(name = "basic")
@Operation(summary = "Recomputes a round's score and its impact on leaderboards and future handicaps.")
void recompute(@NotNull @Positive @PathParam("roundId") BigInteger roundId);
@DELETE
@SecurityRequirement(name = "basic")
@Operation(summary = "Deletes a round.")
void remove(@NotNull @Positive @PathParam("roundId") BigInteger roundId);
}

View File

@@ -0,0 +1,38 @@
package com.poststats.golf.rs.api;
import java.util.List;
import com.poststats.golf.rs.api.model.Round;
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.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
/**
* @author brian.long@poststats.com
*/
@Path("/golf/rounds")
@Tag(name = "Round API")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "400", description = "A query parameter is not valid"),
@ApiResponse(responseCode = "404", description = "No matching rounds were found")
})
public interface RoundsApi {
// FIXME String to LocalDate
@GET
@Path("/byDate")
@Produces(Constants.V1_JSON)
@Operation(summary = "Finds rounds before a date.", description = "Searches for rounds by date, exclusive.")
List<Round> findByDate(@NotNull @Positive @QueryParam("personId") long personId,
@QueryParam("cutoffDate") String cutoffDate);
}

View File

@@ -0,0 +1,66 @@
package com.poststats.golf.rs.api;
import java.util.List;
import com.poststats.golf.rs.api.model.ScoringSystem;
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.validation.constraints.Positive;
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;
/**
* @author brian.long@poststats.com
*/
@Path("/golf/scoring")
@Tag(name = "Scoring API")
public abstract class ScoringApi {
@GET
@Path("systems")
@Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves meta-data about all scoring systems.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success", useReturnTypeSchema = true)
})
public abstract List<ScoringSystem> getSystems(@QueryParam("stableford") Boolean stableford);
@GET
@Path("/system/{id}")
@Produces(Constants.V1_JSON)
@Operation(
summary = "Retrieves meta-data about a scoring system.",
description = "Retreives name and other direct meta-data about the specified scoring system."
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success", useReturnTypeSchema = true),
@ApiResponse(
responseCode = "404",
description = "A scoring system with the specified ID could not be found"
)
})
public abstract ScoringSystem getSystemById(@PathParam("id") @Positive short id);
@GET
@Path("/system/bySid/{sid}")
@Produces(Constants.V1_JSON)
@Operation(
summary = "Retrieves meta-data about a scoring system.",
description = "Retreives name and other direct meta-data about the specified scoring system."
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success", useReturnTypeSchema = true),
@ApiResponse(
responseCode = "404",
description = "A scoring system with the specified SID could not be found"
)
})
public abstract ScoringSystem getSystemBySid(@PathParam("sid") String sid);
}

View File

@@ -0,0 +1,61 @@
package com.poststats.golf.rs.api;
import java.util.List;
import com.poststats.golf.rs.api.model.Event;
import com.poststats.golf.rs.api.model.Series;
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.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
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;
/**
* @author brian.long@poststats.com
*/
@Path("/golf/series/{seriesId}")
@Tag(name = "Event Series API")
public abstract class SeriesApi {
@NotNull
@Positive
@PathParam("seriesId")
private int seriesId;
protected int getSeriesId() {
return this.seriesId;
}
@GET
@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 abstract Series get();
@GET
@Path("/events")
@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 abstract List<Event> getEvents(@QueryParam("reverse") Boolean reverse);
}

View File

@@ -1,10 +1,11 @@
package com.poststats.golf.api.model;
package com.poststats.golf.rs.api.model;
import java.time.Year;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.api.model.BaseModel;
import com.poststats.transformer.MapEntry;
import java.time.Year;
import com.poststats.rs.api.annotation.MapEntry;
import com.poststats.rs.api.model.BaseModel;
@JsonIgnoreProperties(ignoreUnknown = true)
public abstract class BaseCourse<ConcreteT extends BaseCourse<ConcreteT>> extends BaseModel<ConcreteT> {
@@ -12,11 +13,14 @@ public abstract class BaseCourse<ConcreteT extends BaseCourse<ConcreteT>> extend
public enum Access {
@JsonProperty("private")
@MapEntry("private")
Private, @JsonProperty("military")
Private,
@JsonProperty("military")
@MapEntry("military")
Military, @JsonProperty("semi-private")
Military,
@JsonProperty("semi-private")
@MapEntry("semi-private")
SemiPrivate, @JsonProperty("public")
SemiPrivate,
@JsonProperty("public")
@MapEntry("public")
Public,
}
@@ -26,7 +30,7 @@ public abstract class BaseCourse<ConcreteT extends BaseCourse<ConcreteT>> extend
private String name;
@JsonProperty
@MapEntry("coursePrefix")
@MapEntry("prefix")
private String namePrefix;
@JsonProperty

View File

@@ -0,0 +1,28 @@
package com.poststats.golf.rs.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.rs.api.annotation.MapEntry;
import com.poststats.rs.api.model.BaseModel;
@JsonIgnoreProperties(ignoreUnknown = true)
public abstract class BaseCourseNine<ConcreteT extends BaseCourseNine<ConcreteT>> extends BaseModel<ConcreteT> {
@JsonProperty
@MapEntry("nine")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ConcreteT withName(String name) {
this.name = name;
return this.withThis();
}
}

View File

@@ -0,0 +1,62 @@
package com.poststats.golf.rs.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.rs.api.annotation.MapEntry;
import com.poststats.rs.api.model.BaseModel;
@JsonIgnoreProperties(ignoreUnknown = true)
public abstract class BaseCourseTee<ConcreteT extends BaseCourseTee<ConcreteT>> extends BaseModel<ConcreteT> {
@JsonProperty
@MapEntry("tee")
private String name;
@JsonProperty
@MapEntry("teeColor")
private String color;
@JsonProperty
@MapEntry("primaryGender")
private char primaryGender;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public char getPrimaryGender() {
return primaryGender;
}
public void setPrimaryGender(char gender) {
this.primaryGender = gender;
}
public ConcreteT withName(String name) {
this.name = name;
return this.withThis();
}
public ConcreteT withColor(String color) {
this.color = color;
return this.withThis();
}
public ConcreteT withPrimaryGender(char gender) {
this.primaryGender = gender;
return this.withThis();
}
}

View File

@@ -1,9 +1,9 @@
package com.poststats.golf.api.model;
package com.poststats.golf.rs.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.api.model.BaseModel;
import com.poststats.transformer.MapEntry;
import com.poststats.rs.api.annotation.MapEntry;
import com.poststats.rs.api.model.BaseModel;
/**
* @author brian.long@poststats.com
@@ -14,11 +14,14 @@ public abstract class BaseEvent<ConcreteT extends BaseEvent<ConcreteT>> extends
public enum EventType {
@JsonProperty("outing")
@MapEntry("outing")
Outing, @JsonProperty("tourney")
Outing,
@JsonProperty("tourney")
@MapEntry("tourney")
Tourney, @JsonProperty("trip")
Tourney,
@JsonProperty("trip")
@MapEntry("trip")
Trip, @JsonProperty("league")
Trip,
@JsonProperty("league")
@MapEntry("league")
League
}

View File

@@ -0,0 +1,127 @@
package com.poststats.golf.rs.api.model;
import java.time.LocalDate;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.rs.api.annotation.MapEntry;
import com.poststats.rs.api.model.BaseModel;
@JsonIgnoreProperties(ignoreUnknown = true)
public abstract class BaseEventDocument<ConcreteT extends BaseEventDocument<ConcreteT>> extends BaseModel<ConcreteT> {
@JsonProperty
@MapEntry
private String type;
@JsonProperty
@MapEntry
private LocalDate day;
@JsonProperty
@MapEntry("liveline")
private LocalDate publicationDate;
@JsonProperty
@MapEntry
private boolean sendEmailWithLink;
@JsonProperty
@MapEntry
private boolean sendEmailWithContent;
@JsonProperty
@MapEntry
private boolean sendTextWithLink;
@JsonProperty
@MapEntry
private boolean sendTextWithContent;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public LocalDate getDay() {
return day;
}
public void setDay(LocalDate day) {
this.day = day;
}
public LocalDate getPublicationDate() {
return publicationDate;
}
public void setPublicationDate(LocalDate publicationDate) {
this.publicationDate = publicationDate;
}
public boolean isSendEmailWithLink() {
return sendEmailWithLink;
}
public void setSendEmailWithLink(boolean sendEmailWithLink) {
this.sendEmailWithLink = sendEmailWithLink;
}
public boolean isSendEmailWithContent() {
return sendEmailWithContent;
}
public void setSendEmailWithContent(boolean sendEmailWithContent) {
this.sendEmailWithContent = sendEmailWithContent;
}
public boolean isSendTextWithLink() {
return sendTextWithLink;
}
public void setSendTextWithLink(boolean sendTextWithLink) {
this.sendTextWithLink = sendTextWithLink;
}
public boolean isSendTextWithContent() {
return sendTextWithContent;
}
public void setSendTextWithContent(boolean sendTextWithContent) {
this.sendTextWithContent = sendTextWithContent;
}
public ConcreteT withDay(LocalDate day) {
this.day = day;
return this.withThis();
}
public ConcreteT withPublicationDate(LocalDate publicationDate) {
this.publicationDate = publicationDate;
return this.withThis();
}
public ConcreteT withSendEmailWithLink(boolean sendEmailWithLink) {
this.sendEmailWithLink = sendEmailWithLink;
return this.withThis();
}
public ConcreteT withSendEmailWithContent(boolean sendEmailWithContent) {
this.sendEmailWithContent = sendEmailWithContent;
return this.withThis();
}
public ConcreteT withSendTextWithLink(boolean sendTextWithLink) {
this.sendTextWithLink = sendTextWithLink;
return this.withThis();
}
public ConcreteT withSendTextWithContent(boolean sendTextWithContent) {
this.sendTextWithContent = sendTextWithContent;
return this.withThis();
}
}

View File

@@ -1,10 +1,11 @@
package com.poststats.golf.api.model;
package com.poststats.golf.rs.api.model;
import java.time.LocalDate;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.api.model.BaseModel;
import com.poststats.transformer.MapEntry;
import java.time.LocalDate;
import com.poststats.rs.api.annotation.MapEntry;
import com.poststats.rs.api.model.BaseModel;
/**
* @author brian.long@poststats.com
@@ -15,11 +16,14 @@ public abstract class BaseEventRound<ConcreteT extends BaseEventRound<ConcreteT>
public enum TeeFormat {
@JsonProperty("straight")
@MapEntry("straight")
Straight, @JsonProperty("dual")
Straight,
@JsonProperty("dual")
@MapEntry("dual")
Dual, @JsonProperty("shotgun-9")
Dual,
@JsonProperty("shotgun-9")
@MapEntry("shotgun-9")
Shotgun9Hole, @JsonProperty("shotgun-18")
Shotgun9Hole,
@JsonProperty("shotgun-18")
@MapEntry("shotgun-18")
Shotgun18Hole
}

View File

@@ -0,0 +1,51 @@
package com.poststats.golf.rs.api.model;
import java.time.LocalTime;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.rs.api.annotation.MapEntry;
import com.poststats.rs.api.model.BaseModel;
/**
* @author brian.long@poststats.com
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public abstract class BaseEventRoundPairing<ConcreteT extends BaseEventRoundPairing<ConcreteT>>
extends BaseModel<ConcreteT> {
@JsonProperty
@MapEntry
private LocalTime teetime;
@JsonProperty
@MapEntry("number")
private Short holeNumber;
public LocalTime getTeetime() {
return teetime;
}
public void setTeetime(LocalTime teetime) {
this.teetime = teetime;
}
public Short getHoleNumber() {
return holeNumber;
}
public void setHoleNumber(Short holeNumber) {
this.holeNumber = holeNumber;
}
public ConcreteT withTeetime(LocalTime teetime) {
this.teetime = teetime;
return this.withThis();
}
public ConcreteT withHoleNumber(Short holeNumber) {
this.holeNumber = holeNumber;
return this.withThis();
}
}

View File

@@ -1,10 +1,11 @@
package com.poststats.golf.api.model;
package com.poststats.golf.rs.api.model;
import java.time.Year;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.api.model.BaseModel;
import com.poststats.transformer.MapEntry;
import java.time.Year;
import com.poststats.rs.api.annotation.MapEntry;
import com.poststats.rs.api.model.BaseModel;
@JsonIgnoreProperties(ignoreUnknown = true)
public abstract class BaseGolfer<ConcreteT extends BaseGolfer<ConcreteT>> extends BaseModel<ConcreteT> {

View File

@@ -0,0 +1,68 @@
package com.poststats.golf.rs.api.model;
import java.util.LinkedList;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* @author brian.long@poststats.com
*/
public abstract class BasePhoto<ConcreteT extends BasePhoto<ConcreteT>>
extends com.poststats.rs.api.model.BasePhoto<ConcreteT> {
@JsonProperty
private List<Integer> courseIds;
@JsonProperty
private List<Long> eventIds;
@JsonProperty
private List<Integer> seriesIds;
public List<Integer> getCourseIds() {
return courseIds;
}
public void setCourseIds(List<Integer> courseIds) {
this.courseIds = courseIds;
}
public List<Long> getEventIds() {
return eventIds;
}
public void setEventIds(List<Long> eventIds) {
this.eventIds = eventIds;
}
public List<Integer> getSeriesIds() {
return seriesIds;
}
public void setSeriesIds(List<Integer> seriesIds) {
this.seriesIds = seriesIds;
}
public ConcreteT withCourseId(int courseId) {
if (this.courseIds == null)
this.courseIds = new LinkedList<>();
this.courseIds.add(courseId);
return this.withThis();
}
public ConcreteT withEventId(long eventId) {
if (this.eventIds == null)
this.eventIds = new LinkedList<>();
this.eventIds.add(eventId);
return this.withThis();
}
public ConcreteT withSeriesId(int seriesId) {
if (this.seriesIds == null)
this.seriesIds = new LinkedList<>();
this.seriesIds.add(seriesId);
return this.withThis();
}
}

View File

@@ -0,0 +1,48 @@
package com.poststats.golf.rs.api.model;
import java.time.LocalDate;
import java.time.LocalTime;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.rs.api.annotation.MapEntry;
import com.poststats.rs.api.model.BaseModel;
@JsonIgnoreProperties(ignoreUnknown = true)
public abstract class BaseRound<ConcreteT extends BaseRound<ConcreteT>> extends BaseModel<ConcreteT> {
@JsonProperty
@MapEntry
private LocalDate teedate;
@JsonProperty
@MapEntry
private LocalTime teetime;
public LocalDate getTeedate() {
return teedate;
}
public void setTeedate(LocalDate teedate) {
this.teedate = teedate;
}
public LocalTime getTeetime() {
return teetime;
}
public void setTeetime(LocalTime teetime) {
this.teetime = teetime;
}
public ConcreteT withTeedate(LocalDate teedate) {
this.teedate = teedate;
return this.withThis();
}
public ConcreteT withTeetime(LocalTime teetime) {
this.teetime = teetime;
return this.withThis();
}
}

View File

@@ -1,9 +1,9 @@
package com.poststats.golf.api.model;
package com.poststats.golf.rs.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.api.model.BaseModel;
import com.poststats.transformer.MapEntry;
import com.poststats.rs.api.annotation.MapEntry;
import com.poststats.rs.api.model.BaseModel;
/**
* @author brian.long@poststats.com

View File

@@ -0,0 +1,133 @@
package com.poststats.golf.rs.api.model;
import java.time.LocalDate;
import java.time.OffsetDateTime;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.golf.rs.api.Constants;
import com.poststats.rs.api.annotation.MapCondition;
import com.poststats.rs.api.annotation.MapEntry;
import com.poststats.rs.api.model.Facility;
import com.poststats.rs.api.model.PeriodConstrainable;
@JsonIgnoreProperties(ignoreUnknown = true)
public class Course extends BaseCourse<Course> implements ReferenceableCourse, PeriodConstrainable<LocalDate> {
@JsonProperty(required = true, access = JsonProperty.Access.READ_ONLY)
@MapEntry("courseID")
private int id;
@JsonProperty
@MapEntry("liveline")
@MapCondition(rolesAllowed = {
Constants.ADMIN_ROLE, "course"
})
private LocalDate liveline;
@JsonProperty
@MapEntry("deadline")
@MapCondition(rolesAllowed = {
Constants.ADMIN_ROLE, "course"
})
private LocalDate deadline;
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
@MapEntry("created")
@MapCondition(rolesAllowed = {
Constants.ADMIN_ROLE, "course"
})
private OffsetDateTime created;
@JsonProperty
@MapEntry
private Facility facility;
@JsonProperty
@MapEntry("facilityID")
private Integer facilityId;
@Override
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public LocalDate getLiveline() {
return liveline;
}
@Override
public void setLiveline(LocalDate liveline) {
this.liveline = liveline;
}
@Override
public LocalDate getDeadline() {
return deadline;
}
@Override
public void setDeadline(LocalDate deadline) {
this.deadline = deadline;
}
public OffsetDateTime getCreated() {
return created;
}
public void setCreated(OffsetDateTime created) {
this.created = created;
}
public Facility getFacility() {
return facility;
}
public void setFacility(Facility facility) {
this.facility = facility;
}
public Integer getFacilityId() {
return facilityId;
}
public void setFacilityId(Integer facilityId) {
this.facilityId = facilityId;
}
public Course withId(int id) {
this.id = id;
return this;
}
public Course withLiveline(LocalDate liveline) {
this.liveline = liveline;
return this;
}
public Course withDeadline(LocalDate deadline) {
this.deadline = deadline;
return this;
}
public Course withCreated(OffsetDateTime created) {
this.created = created;
return this;
}
public Course withFacility(Facility facility) {
this.facility = facility;
return this;
}
public Course withFacilityId(Integer facilityId) {
this.facilityId = facilityId;
return this;
}
}

View File

@@ -0,0 +1,112 @@
package com.poststats.golf.rs.api.model;
import java.time.LocalDate;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.golf.rs.api.Constants;
import com.poststats.rs.api.annotation.MapCondition;
import com.poststats.rs.api.annotation.MapEntry;
import com.poststats.rs.api.model.PeriodConstrainable;
@JsonIgnoreProperties(ignoreUnknown = true)
public class CourseNine extends BaseCourseNine<CourseNine>
implements ReferenceableCourseNine, PeriodConstrainable<LocalDate> {
@JsonProperty(required = true, access = JsonProperty.Access.READ_ONLY)
@MapEntry("nineID")
private long id;
@JsonProperty
@MapEntry("liveline")
@MapCondition(rolesAllowed = {
Constants.ADMIN_ROLE, "course"
})
private LocalDate liveline;
@JsonProperty
@MapEntry("deadline")
@MapCondition(rolesAllowed = {
Constants.ADMIN_ROLE, "course"
})
private LocalDate deadline;
@JsonProperty
@MapEntry
private Course course;
@JsonProperty
@MapEntry("courseID")
private Integer courseId;
@Override
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
@Override
public LocalDate getLiveline() {
return liveline;
}
@Override
public void setLiveline(LocalDate liveline) {
this.liveline = liveline;
}
@Override
public LocalDate getDeadline() {
return deadline;
}
@Override
public void setDeadline(LocalDate deadline) {
this.deadline = deadline;
}
public Course getCourse() {
return course;
}
public void setCourse(Course course) {
this.course = course;
}
public Integer getCourseId() {
return courseId;
}
public void setCourseId(Integer courseId) {
this.courseId = courseId;
}
public CourseNine withId(int id) {
this.id = id;
return this;
}
public CourseNine withLiveline(LocalDate liveline) {
this.liveline = liveline;
return this;
}
public CourseNine withDeadline(LocalDate deadline) {
this.deadline = deadline;
return this;
}
public CourseNine withCourse(Course course) {
this.course = course;
return this;
}
public CourseNine withCourseId(Integer courseId) {
this.courseId = courseId;
return this;
}
}

View File

@@ -0,0 +1,112 @@
package com.poststats.golf.rs.api.model;
import java.time.LocalDate;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.golf.rs.api.Constants;
import com.poststats.rs.api.annotation.MapCondition;
import com.poststats.rs.api.annotation.MapEntry;
import com.poststats.rs.api.model.PeriodConstrainable;
@JsonIgnoreProperties(ignoreUnknown = true)
public class CourseTee extends BaseCourseTee<CourseTee>
implements ReferenceableCourseTee, PeriodConstrainable<LocalDate> {
@JsonProperty(required = true, access = JsonProperty.Access.READ_ONLY)
@MapEntry("teeID")
private long id;
@JsonProperty
@MapEntry("liveline")
@MapCondition(rolesAllowed = {
Constants.ADMIN_ROLE, "course"
})
private LocalDate liveline;
@JsonProperty
@MapEntry("deadline")
@MapCondition(rolesAllowed = {
Constants.ADMIN_ROLE, "course"
})
private LocalDate deadline;
@JsonProperty
@MapEntry
private Course course;
@JsonProperty
@MapEntry("courseID")
private Integer courseId;
@Override
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
@Override
public LocalDate getLiveline() {
return liveline;
}
@Override
public void setLiveline(LocalDate liveline) {
this.liveline = liveline;
}
@Override
public LocalDate getDeadline() {
return deadline;
}
@Override
public void setDeadline(LocalDate deadline) {
this.deadline = deadline;
}
public Course getCourse() {
return course;
}
public void setCourse(Course course) {
this.course = course;
}
public Integer getCourseId() {
return courseId;
}
public void setCourseId(Integer courseId) {
this.courseId = courseId;
}
public CourseTee withId(int id) {
this.id = id;
return this;
}
public CourseTee withLiveline(LocalDate liveline) {
this.liveline = liveline;
return this;
}
public CourseTee withDeadline(LocalDate deadline) {
this.deadline = deadline;
return this;
}
public CourseTee withCourse(Course course) {
this.course = course;
return this;
}
public CourseTee withCourseId(Integer courseId) {
this.courseId = courseId;
return this;
}
}

View File

@@ -1,16 +1,16 @@
package com.poststats.golf.api.model;
package com.poststats.golf.rs.api.model;
import java.time.LocalDate;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import com.poststats.golf.api.Constants;
import com.poststats.service.impl.DefaultFormattingService;
import com.poststats.transformer.GeocodeSource;
import com.poststats.transformer.GeocodeTarget;
import com.poststats.transformer.GeocodeTarget.GeocodeField;
import com.poststats.transformer.MapCondition;
import com.poststats.transformer.MapEntry;
import java.time.LocalDate;
import com.poststats.golf.rs.api.Constants;
import com.poststats.rs.api.annotation.GeocodeSource;
import com.poststats.rs.api.annotation.GeocodeTarget;
import com.poststats.rs.api.annotation.GeocodeTarget.GeocodeField;
import com.poststats.rs.api.annotation.MapCondition;
import com.poststats.rs.api.annotation.MapEntry;
/**
* @author brian.long@poststats.com
@@ -42,6 +42,10 @@ public class Event extends BaseEvent<Event> implements ReferenceableEvent {
@GeocodeTarget(GeocodeField.Country)
private String addrcountry;
@MapEntry
@GeocodeTarget(GeocodeField.FuzzyPostalAddress)
private String addrfuzzy;
@JsonProperty
@MapEntry
private Series series;
@@ -97,14 +101,16 @@ public class Event extends BaseEvent<Event> implements ReferenceableEvent {
@JsonProperty
@MapEntry
@MapCondition(rolesAllowed = {
Constants.EVENT_ROLE_PREFIX + "admin"
Constants.EVENT_ROLE_PREFIX
+ "admin"
})
private LocalDate registrationLiveline;
@JsonProperty
@MapEntry
@MapCondition(rolesAllowed = {
Constants.EVENT_ROLE_PREFIX + "admin"
Constants.EVENT_ROLE_PREFIX
+ "admin"
})
private LocalDate registrationHideline;
@@ -115,42 +121,60 @@ public class Event extends BaseEvent<Event> implements ReferenceableEvent {
@JsonProperty
@MapEntry
@MapCondition(rolesAllowed = {
Constants.EVENT_ROLE_PREFIX + "admin", Constants.EVENT_ROLE_PREFIX + "finance"
Constants.EVENT_ROLE_PREFIX
+ "admin",
Constants.EVENT_ROLE_PREFIX
+ "finance"
})
private LocalDate financesLiveline;
@JsonProperty
@MapEntry
@MapCondition(rolesAllowed = {
Constants.EVENT_ROLE_PREFIX + "admin", Constants.EVENT_ROLE_PREFIX + "pairing"
Constants.EVENT_ROLE_PREFIX
+ "admin",
Constants.EVENT_ROLE_PREFIX
+ "pairing"
})
private LocalDate pairingsLiveline;
@JsonProperty
@MapEntry
@MapCondition(rolesAllowed = {
Constants.EVENT_ROLE_PREFIX + "admin", Constants.EVENT_ROLE_PREFIX + "pairing"
Constants.EVENT_ROLE_PREFIX
+ "admin",
Constants.EVENT_ROLE_PREFIX
+ "pairing"
})
private LocalDate teamsLiveline;
@JsonProperty
@MapEntry
@MapCondition(rolesAllowed = {
Constants.EVENT_ROLE_PREFIX + "admin", Constants.EVENT_ROLE_PREFIX + "options"
Constants.EVENT_ROLE_PREFIX
+ "admin",
Constants.EVENT_ROLE_PREFIX
+ "options"
})
private LocalDate lodgingLiveline;
@JsonProperty
@MapEntry
@MapCondition(rolesAllowed = {
Constants.EVENT_ROLE_PREFIX + "admin", Constants.EVENT_ROLE_PREFIX + "pairing"
Constants.EVENT_ROLE_PREFIX
+ "admin",
Constants.EVENT_ROLE_PREFIX
+ "pairing"
})
private LocalDate documentsLiveline;
@JsonProperty
@MapEntry
@MapCondition(rolesAllowed = {
Constants.EVENT_ROLE_PREFIX + "admin", Constants.EVENT_ROLE_PREFIX + "pairing"
Constants.EVENT_ROLE_PREFIX
+ "admin",
Constants.EVENT_ROLE_PREFIX
+ "pairing"
})
private LocalDate specialHolesLiveline;
@@ -179,18 +203,10 @@ public class Event extends BaseEvent<Event> implements ReferenceableEvent {
this.deadline = deadline;
}
@Override
@JsonProperty(access = Access.READ_ONLY)
@GeocodeSource
public String getFuzzyLocation() {
synchronized (this) {
if (super.getFuzzyLocation() == null) {
String address = new DefaultFormattingService().formatFuzzyPostalAddress(null, this.addrcity,
this.addrstate, this.addrcountry, null);
super.setFuzzyLocation(address);
}
}
return super.getFuzzyLocation();
return this.addrfuzzy;
}
public Series getSeries() {

View File

@@ -0,0 +1,66 @@
package com.poststats.golf.rs.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import com.poststats.rs.api.annotation.MapEntry;
/**
* @author brian.long@poststats.com
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class EventDocument extends BaseEventDocument<EventDocument> implements ReferenceableEventDocument {
@JsonProperty(required = true, access = Access.READ_ONLY)
@MapEntry("documentID")
private long id;
@JsonProperty
@MapEntry
private Event event;
@JsonProperty
@MapEntry("eventID")
private Long eventId;
@Override
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public Event getEvent() {
return event;
}
public void setEvent(Event event) {
this.event = event;
}
public Long getEventId() {
return eventId;
}
public void setEventId(Long eventId) {
this.eventId = eventId;
}
public EventDocument withId(long id) {
this.id = id;
return this;
}
public EventDocument withEvent(Event event) {
this.event = event;
return this;
}
public EventDocument withEventId(Long eventId) {
this.eventId = eventId;
return this;
}
}

View File

@@ -0,0 +1,68 @@
package com.poststats.golf.rs.api.model;
import java.math.BigInteger;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import com.poststats.rs.api.annotation.MapEntry;
import com.poststats.rs.api.model.BaseModel;
/**
* @author brian.long@poststats.com
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class EventPerson extends BaseModel<EventPerson> {
@JsonProperty(required = true, access = Access.READ_ONLY)
@MapEntry("epersonID")
private BigInteger id;
@JsonProperty
@MapEntry
private Golfer golfer;
@JsonProperty(required = true, access = Access.READ_ONLY)
@MapEntry("personID")
private long personId;
public BigInteger getId() {
return id;
}
public void setId(BigInteger id) {
this.id = id;
}
public Golfer getGolfer() {
return golfer;
}
public void setGolfer(Golfer golfer) {
this.golfer = golfer;
}
public long getPersonId() {
return personId;
}
public void setPersonId(long personId) {
this.personId = personId;
}
public EventPerson withId(BigInteger id) {
this.id = id;
return this;
}
public EventPerson withGolfer(Golfer golfer) {
this.golfer = golfer;
return this;
}
public EventPerson withPersonId(long personId) {
this.personId = personId;
return this;
}
}

View File

@@ -1,9 +1,9 @@
package com.poststats.golf.api.model;
package com.poststats.golf.rs.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import com.poststats.transformer.MapEntry;
import com.poststats.rs.api.annotation.MapEntry;
/**
* @author brian.long@poststats.com

View File

@@ -0,0 +1,134 @@
package com.poststats.golf.rs.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import com.poststats.rs.api.annotation.MapEntry;
/**
* @author brian.long@poststats.com
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class EventRoundPairing extends BaseEventRoundPairing<EventRoundPairing> implements ReferenceableEventRound {
@JsonProperty(required = true, access = Access.READ_ONLY)
@MapEntry("pairingID")
private long id;
@JsonProperty
@MapEntry
private EventRound round;
@JsonProperty
@MapEntry("eroundID")
private Long eroundId;
@JsonProperty
@MapEntry
private Course course;
@JsonProperty
@MapEntry("courseID")
private Integer courseId;
@JsonProperty
@MapEntry("nine")
private CourseNine courseNine;
@JsonProperty
@MapEntry("nineID")
private Long courseNineId;
@Override
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public EventRound getRound() {
return round;
}
public void setRound(EventRound round) {
this.round = round;
}
public Long getEroundId() {
return eroundId;
}
public void setEroundId(Long eroundId) {
this.eroundId = eroundId;
}
public Course getCourse() {
return course;
}
public void setCourse(Course course) {
this.course = course;
}
public Integer getCourseId() {
return courseId;
}
public void setCourseId(Integer courseId) {
this.courseId = courseId;
}
public CourseNine getCourseNine() {
return courseNine;
}
public void setCourseNine(CourseNine courseNine) {
this.courseNine = courseNine;
}
public Long getCourseNineId() {
return courseNineId;
}
public void setCourseNineId(Long courseNineId) {
this.courseNineId = courseNineId;
}
public EventRoundPairing withId(long id) {
this.id = id;
return this;
}
public EventRoundPairing withRound(EventRound round) {
this.round = round;
return this;
}
public EventRoundPairing withRoundId(Long eroundId) {
this.eroundId = eroundId;
return this;
}
public EventRoundPairing withCourse(Course course) {
this.course = course;
return this;
}
public EventRoundPairing withCourseId(Integer courseId) {
this.courseId = courseId;
return this;
}
public EventRoundPairing withCourseNine(CourseNine courseNine) {
this.courseNine = courseNine;
return this;
}
public EventRoundPairing withCourseNineId(Long courseNineId) {
this.courseNineId = courseNineId;
return this.withThis();
}
}

View File

@@ -0,0 +1,20 @@
package com.poststats.golf.rs.api.model;
import com.fasterxml.jackson.annotation.JsonProperty;
public enum EventRoundPairingOrder {
@JsonProperty("name")
FormattedName, @JsonProperty("lastName")
LastName, @JsonProperty("firstName")
FirstName, @JsonProperty("teetime")
TeeTime, @JsonProperty("course")
Course, @JsonProperty("nine")
CourseNine, @JsonProperty("holeNumber")
HoleNumber,
// an alias for teetime,course,nine,holeNumber
@JsonProperty("pairing")
Pairing
}

View File

@@ -1,11 +1,11 @@
package com.poststats.golf.api.model;
package com.poststats.golf.rs.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import com.poststats.api.model.Person;
import com.poststats.api.model.ReferenceablePerson;
import com.poststats.transformer.MapEntry;
import com.poststats.rs.api.annotation.MapEntry;
import com.poststats.rs.api.model.Person;
import com.poststats.rs.api.model.ReferenceablePerson;
@JsonIgnoreProperties(ignoreUnknown = true)
public class Golfer extends BaseGolfer<Golfer> implements ReferenceablePerson {

View File

@@ -1,4 +1,4 @@
package com.poststats.golf.api.model;
package com.poststats.golf.rs.api.model;
public interface ReferenceableCourse {

View File

@@ -0,0 +1,7 @@
package com.poststats.golf.rs.api.model;
public interface ReferenceableCourseNine {
long getId();
}

View File

@@ -0,0 +1,7 @@
package com.poststats.golf.rs.api.model;
public interface ReferenceableCourseTee {
long getId();
}

View File

@@ -1,4 +1,4 @@
package com.poststats.golf.api.model;
package com.poststats.golf.rs.api.model;
public interface ReferenceableEvent {

View File

@@ -0,0 +1,7 @@
package com.poststats.golf.rs.api.model;
public interface ReferenceableEventDocument {
long getId();
}

View File

@@ -1,4 +1,4 @@
package com.poststats.golf.api.model;
package com.poststats.golf.rs.api.model;
public interface ReferenceableEventRound {

View File

@@ -0,0 +1,9 @@
package com.poststats.golf.rs.api.model;
import java.math.BigInteger;
public interface ReferenceableEventRoundPairing {
BigInteger getId();
}

View File

@@ -0,0 +1,9 @@
package com.poststats.golf.rs.api.model;
import java.math.BigInteger;
public interface ReferenceableRound {
BigInteger getId();
}

View File

@@ -1,4 +1,4 @@
package com.poststats.golf.api.model;
package com.poststats.golf.rs.api.model;
public interface ReferenceableSeries {

View File

@@ -1,9 +1,9 @@
package com.poststats.golf.api.model;
package com.poststats.golf.rs.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import com.poststats.transformer.MapEntry;
import com.poststats.rs.api.annotation.MapEntry;
@JsonIgnoreProperties(ignoreUnknown = true)
public class ReferencedCourse implements ReferenceableCourse {

View File

@@ -0,0 +1,29 @@
package com.poststats.golf.rs.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import com.poststats.rs.api.annotation.MapEntry;
@JsonIgnoreProperties(ignoreUnknown = true)
public class ReferencedCourseNine implements ReferenceableCourseNine {
@JsonProperty(required = true, access = Access.READ_ONLY)
@MapEntry("nineID")
private long id;
@Override
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public ReferencedCourseNine withId(long id) {
this.id = id;
return this;
}
}

View File

@@ -1,9 +1,9 @@
package com.poststats.golf.api.model;
package com.poststats.golf.rs.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import com.poststats.transformer.MapEntry;
import com.poststats.rs.api.annotation.MapEntry;
/**
* @author brian.long@poststats.com

View File

@@ -1,15 +1,15 @@
package com.poststats.golf.api.model;
package com.poststats.golf.rs.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import com.poststats.transformer.MapEntry;
import com.poststats.rs.api.annotation.MapEntry;
/**
* @author brian.long@poststats.com
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class ReferencedEventRound implements ReferenceableEvent {
public class ReferencedEventRound implements ReferenceableEventRound {
@JsonProperty(required = true, access = Access.READ_ONLY)
@MapEntry("eroundID")

View File

@@ -0,0 +1,34 @@
package com.poststats.golf.rs.api.model;
import java.math.BigInteger;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import com.poststats.rs.api.annotation.MapEntry;
/**
* @author brian.long@poststats.com
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class ReferencedEventRoundPairing implements ReferenceableEventRoundPairing {
@JsonProperty(required = true, access = Access.READ_ONLY)
@MapEntry("pairingID")
private BigInteger id;
@Override
public BigInteger getId() {
return id;
}
public void setId(BigInteger id) {
this.id = id;
}
public ReferencedEventRoundPairing withId(BigInteger id) {
this.id = id;
return this;
}
}

View File

@@ -1,8 +1,8 @@
package com.poststats.golf.api.model;
package com.poststats.golf.rs.api.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import com.poststats.transformer.MapEntry;
import com.poststats.rs.api.annotation.MapEntry;
public class ReferencedSeries implements ReferenceableSeries {

View File

@@ -0,0 +1,48 @@
package com.poststats.golf.rs.api.model;
import java.math.BigInteger;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import com.poststats.rs.api.annotation.MapEntry;
@JsonIgnoreProperties(ignoreUnknown = true)
public class Round extends BaseRound<Round> implements ReferenceableRound {
@JsonProperty(required = true, access = Access.READ_ONLY)
@MapEntry("roundID")
private BigInteger roundId;
@JsonProperty
@MapEntry
private Golfer golfer;
@Override
public BigInteger getId() {
return roundId;
}
public void setId(BigInteger roundId) {
this.roundId = roundId;
}
public Golfer getGolfer() {
return golfer;
}
public void setGolfer(Golfer golfer) {
this.golfer = golfer;
}
public Round withId(BigInteger roundId) {
this.roundId = roundId;
return this;
}
public Round withGolfer(Golfer golfer) {
this.golfer = golfer;
return this;
}
}

View File

@@ -0,0 +1,116 @@
package com.poststats.golf.rs.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.rs.api.annotation.MapEntry;
import com.poststats.rs.api.model.BaseModel;
/**
* @author brian.long@poststats.com
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class ScoringSystem extends BaseModel<ScoringSystem> {
@JsonProperty(required = true)
@MapEntry("scoringSystemID")
private short id;
@JsonProperty(required = true)
@MapEntry("scoringSystemSID")
private String sid;
@JsonProperty(required = true)
@MapEntry("scoringSystem")
private String name;
@JsonProperty(required = true)
@MapEntry
private String display;
@JsonProperty(required = true)
@MapEntry
private boolean stableford;
@JsonProperty
@MapEntry
private Short baseTarget;
public short getId() {
return id;
}
public void setId(short id) {
this.id = id;
}
public ScoringSystem withId(short id) {
this.id = id;
return this.withThis();
}
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
public ScoringSystem withSid(String sid) {
this.sid = sid;
return this.withThis();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ScoringSystem withName(String name) {
this.name = name;
return this.withThis();
}
public String getDisplay() {
return display;
}
public void setDisplay(String display) {
this.display = display;
}
public ScoringSystem withDisplay(String display) {
this.display = display;
return this.withThis();
}
public boolean isStableford() {
return stableford;
}
public void setStableford(boolean stableford) {
this.stableford = stableford;
}
public ScoringSystem withStableford(boolean stableford) {
this.stableford = stableford;
return this.withThis();
}
public Short getBaseTarget() {
return baseTarget;
}
public void setBaseTarget(Short baseTarget) {
this.baseTarget = baseTarget;
}
public ScoringSystem withBaseTarget(Short baseTarget) {
this.baseTarget = baseTarget;
return this.withThis();
}
}

View File

@@ -1,9 +1,9 @@
package com.poststats.golf.api.model;
package com.poststats.golf.rs.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import com.poststats.transformer.MapEntry;
import com.poststats.rs.api.annotation.MapEntry;
/**
* @author brian.long@poststats.com

View File

@@ -1,9 +1,9 @@
package com.poststats.golf.api.model;
package com.poststats.golf.rs.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.api.model.ReferenceableFacility;
import com.poststats.transformer.MapEntry;
import com.poststats.rs.api.annotation.MapEntry;
import com.poststats.rs.api.model.ReferenceableFacility;
@JsonIgnoreProperties(ignoreUnknown = true)
public class TransientCourse extends BaseCourse<TransientCourse> {

View File

@@ -0,0 +1,27 @@
package com.poststats.golf.rs.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.rs.api.annotation.MapEntry;
@JsonIgnoreProperties(ignoreUnknown = true)
public class TransientCourseNine extends BaseCourseNine<TransientCourseNine> {
@JsonProperty
@MapEntry
private ReferenceableCourse course;
public ReferenceableCourse getCourse() {
return course;
}
public void setCourse(ReferenceableCourse course) {
this.course = course;
}
public TransientCourseNine withCourse(ReferenceableCourse course) {
this.course = course;
return this;
}
}

View File

@@ -1,8 +1,8 @@
package com.poststats.golf.api.model;
package com.poststats.golf.rs.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.transformer.MapEntry;
import com.poststats.rs.api.annotation.MapEntry;
/**
* @author brian.long@poststats.com

View File

@@ -0,0 +1,30 @@
package com.poststats.golf.rs.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.rs.api.annotation.MapEntry;
/**
* @author brian.long@poststats.com
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class TransientEventDocument extends BaseEventDocument<TransientEventDocument> {
@JsonProperty
@MapEntry
private ReferenceableEvent event;
public ReferenceableEvent getEvent() {
return event;
}
public void setEvent(ReferenceableEvent event) {
this.event = event;
}
public TransientEventDocument withCourse(ReferenceableEvent event) {
this.event = event;
return this;
}
}

View File

@@ -1,8 +1,8 @@
package com.poststats.golf.api.model;
package com.poststats.golf.rs.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.transformer.MapEntry;
import com.poststats.rs.api.annotation.MapEntry;
/**
* @author brian.long@poststats.com

View File

@@ -0,0 +1,47 @@
package com.poststats.golf.rs.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.rs.api.annotation.MapEntry;
/**
* @author brian.long@poststats.com
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class TransientEventRoundPairing extends BaseEventRoundPairing<TransientEventRoundPairing> {
@JsonProperty
@MapEntry
private ReferenceableCourse course;
@JsonProperty
@MapEntry
private ReferenceableCourseNine courseNine;
public ReferenceableCourse getCourse() {
return course;
}
public void setCourse(ReferenceableCourse course) {
this.course = course;
}
public ReferenceableCourseNine getCourseNine() {
return courseNine;
}
public void setCourseNine(ReferenceableCourseNine courseNine) {
this.courseNine = courseNine;
}
public TransientEventRoundPairing withCourse(ReferenceableCourse course) {
this.course = course;
return this;
}
public TransientEventRoundPairing withCourseNine(ReferenceableCourseNine courseNine) {
this.courseNine = courseNine;
return this;
}
}

View File

@@ -1,9 +1,9 @@
package com.poststats.golf.api.model;
package com.poststats.golf.rs.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.api.model.ReferenceablePerson;
import com.poststats.transformer.MapEntry;
import com.poststats.rs.api.annotation.MapEntry;
import com.poststats.rs.api.model.ReferenceablePerson;
@JsonIgnoreProperties(ignoreUnknown = true)
public class TransientGolfer extends BaseGolfer<TransientGolfer> {

View File

@@ -1,4 +1,4 @@
package com.poststats.golf.api.model;
package com.poststats.golf.rs.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

View File

@@ -1,59 +0,0 @@
package com.poststats.golf.security;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class AuthenticatedPerson extends com.poststats.security.AuthenticatedPerson {
private final Set<Long> buddyIds;
private final Set<String> acSids;
private final Map<Long, Set<String>> eventAcSids;
public AuthenticatedPerson(com.poststats.security.AuthenticatedPerson person, Set<Long> buddyIds,
Set<String> acSids, Map<Long, Set<String>> eventAcSids) {
super(person);
this.buddyIds = buddyIds;
this.acSids = acSids;
this.eventAcSids = eventAcSids;
}
public Set<Long> getBuddyIds() {
return Collections.unmodifiableSet(this.buddyIds);
}
public Set<String> getAccessControls() {
return Collections.unmodifiableSet(this.acSids);
}
public Set<String> getEventAccessControls(long eventId) {
Set<String> roles = this.eventAcSids.get(eventId);
return roles == null ? null : Collections.unmodifiableSet(roles);
}
public Set<String> getAllAccessControls() {
Set<String> roles = new HashSet<>();
roles.addAll(this.acSids);
for (Entry<Long, Set<String>> eroles : this.eventAcSids.entrySet())
for (String role : eroles.getValue())
roles.add(eroles.getKey() + ":" + role);
return roles == null ? null : Collections.unmodifiableSet(roles);
}
@Override
public boolean hasAccessControl(String ac) {
return this.acSids.contains(ac);
}
public boolean hasBuddy(long buddyId) {
return this.buddyIds.contains(buddyId);
}
public boolean hasAccessControl(String ac, long eventId) {
Set<String> sids = this.eventAcSids.get(eventId);
return sids != null && sids.contains(ac);
}
}

View File

@@ -1,48 +0,0 @@
package com.poststats.golf.security;
import jakarta.ws.rs.core.SecurityContext;
import java.security.Principal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AuthenticatedSecurityContext implements SecurityContext {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final SecurityContext securityContext;
private final AuthenticatedPerson authPerson;
public AuthenticatedSecurityContext(SecurityContext securityContext, AuthenticatedPerson authPerson) {
if (securityContext == null)
throw new IllegalArgumentException();
this.securityContext = securityContext;
this.authPerson = authPerson;
}
@Override
public Principal getUserPrincipal() {
return this.authPerson;
}
@Override
public boolean isUserInRole(String role) {
this.logger.trace("isUserInRole({}, {})", this.getUserPrincipal(), role);
if (this.authPerson.hasAccessControl(role)) {
this.logger.debug("user '{}' is in role '{}'", this.authPerson, role);
return true;
}
return this.securityContext.isUserInRole(role);
}
@Override
public String getAuthenticationScheme() {
return this.securityContext.getAuthenticationScheme();
}
@Override
public boolean isSecure() {
return this.securityContext.isSecure();
}
}

View File

@@ -1,57 +0,0 @@
package com.poststats.golf.security;
import com.poststats.golf.api.Constants;
import jakarta.ws.rs.core.SecurityContext;
import java.security.Principal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class EventSecurityContext implements SecurityContext {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final SecurityContext securityContext;
private final long eventId;
public EventSecurityContext(SecurityContext securityContext, long eventId) {
if (securityContext == null)
throw new IllegalArgumentException();
this.securityContext = securityContext;
this.eventId = eventId;
}
@Override
public Principal getUserPrincipal() {
return this.securityContext.getUserPrincipal();
}
@Override
public boolean isUserInRole(String role) {
this.logger.trace("isUserInRole({}, {}, {})", this.getUserPrincipal(), role, this.eventId);
if (role.startsWith(Constants.EVENT_ROLE_PREFIX)) {
if (this.getUserPrincipal() instanceof AuthenticatedPerson) {
AuthenticatedPerson authPerson = (AuthenticatedPerson) this.getUserPrincipal();
this.logger.trace("checking if user '{}' has role '{}' in event {}", this.getUserPrincipal(), role,
this.eventId);
if (authPerson.hasAccessControl(Constants.EVENT_ROLE, this.eventId)) {
this.logger.debug("user '{}' has role '{}' in event {}", this.getUserPrincipal(), role,
this.eventId);
return true;
}
}
}
return this.securityContext.isUserInRole(role);
}
@Override
public String getAuthenticationScheme() {
return this.securityContext.getAuthenticationScheme();
}
@Override
public boolean isSecure() {
return this.securityContext.isSecure();
}
}

View File

@@ -1,55 +0,0 @@
package com.poststats.golf.security;
import com.poststats.golf.api.Constants;
import jakarta.ws.rs.core.SecurityContext;
import java.security.Principal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PersonSecurityContext implements SecurityContext {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final SecurityContext securityContext;
private final long personId;
public PersonSecurityContext(SecurityContext securityContext, long personId) {
if (securityContext == null)
throw new IllegalArgumentException();
this.securityContext = securityContext;
this.personId = personId;
}
@Override
public Principal getUserPrincipal() {
return this.securityContext.getUserPrincipal();
}
@Override
public boolean isUserInRole(String role) {
this.logger.trace("isUserInRole({}, {}, {})", this.getUserPrincipal(), role, this.personId);
if (role.equals(Constants.BUDDY_ROLE)) {
if (this.getUserPrincipal() instanceof AuthenticatedPerson) {
AuthenticatedPerson authPerson = (AuthenticatedPerson) this.getUserPrincipal();
this.logger.trace("checking if user '{}' is buddy of {}", this.getUserPrincipal(), this.personId);
if (authPerson.hasBuddy(this.personId)) {
this.logger.debug("user '{}' is buddy of {}", this.getUserPrincipal(), this.personId);
return true;
}
}
}
return this.securityContext.isUserInRole(role);
}
@Override
public String getAuthenticationScheme() {
return this.securityContext.getAuthenticationScheme();
}
@Override
public boolean isSecure() {
return this.securityContext.isSecure();
}
}

View File

@@ -1,54 +0,0 @@
package com.poststats.golf.service;
import com.brianlong.util.FlexMap;
import com.brianlong.util.SubList;
import com.poststats.service.CacheableService;
import java.util.Collection;
import java.util.Map;
public interface CourseService extends CacheableService<Integer> {
/**
* This method retrieves meta-data about the specified course.
*
* This retrieves the facility meta-data as well.
*
* @param courseId A unique identifier for the course.
* @return A map of meta-data specific to the course.
*/
FlexMap get(int courseId);
/**
* This method retrieves meta-data about the specified courses.
*
* This retrieves the facility meta-data as well.
*
* @param courseIds Unique identifier for the coursees.
* @return A map of unique identifiers to meta-data specific to the coursees.
*/
Map<Integer, ? extends FlexMap> get(Collection<Integer> courseIds);
default SubList<? extends FlexMap> findByName(String name) {
return this.findByName(name, 1, 10);
}
SubList<? extends FlexMap> findByName(String name, int page, int perPage);
default SubList<? extends FlexMap> findByJurisdiction(String country, String state) {
return this.findByJurisdiction(country, state, 1, 50);
}
SubList<? extends FlexMap> findByJurisdiction(String country, String state, int page, int perPage);
default SubList<? extends FlexMap> findByLocation(double latitude, double longitude) {
return this.findByLocation(latitude, longitude, 10, 1, 25);
}
default SubList<? extends FlexMap> findByLocation(double latitude, double longitude, int radiusInMiles) {
return this.findByLocation(latitude, longitude, radiusInMiles, 1, 25);
}
SubList<? extends FlexMap> findByLocation(double latitude, double longitude, int radiusInMiles, int page,
int perPage);
}

View File

@@ -1,10 +0,0 @@
package com.poststats.golf.service;
import com.brianlong.sql.DataSet;
import java.util.List;
public interface EventFinanceService {
List<DataSet> getPersonsBalances(long eventId);
}

View File

@@ -1,15 +0,0 @@
package com.poststats.golf.service;
import com.brianlong.util.FlexMap;
import java.util.List;
import java.util.Set;
public interface EventPersonService {
List<FlexMap> getPeople(long eventId, boolean fullDetails);
List<FlexMap> getParticipants(long eventId, boolean fullDetails);
Set<Long> getSeriesEventIdsAsParticipant(int seriesId, long personId);
}

View File

@@ -1,34 +0,0 @@
package com.poststats.golf.service;
import com.brianlong.util.FlexMap;
import java.util.Map;
public interface EventRoundService {
/**
* This method retrieves meta-data about the specified event round.
*
* @param eroundId A unique identifier for the event round.
* @return A map of meta-data specific to the event round.
*/
FlexMap get(long eroundId);
/**
* This method retrieves meta-data about the specified event round.
*
* @param eventId A unique identifier for the event.
* @param number A sequential number for the event round, starting with 1.
* @return A map of meta-data specific to the event round.
*/
FlexMap get(long eventId, short number);
/**
* This method retrieves meta-data about the specified event rounds.
*
* @param eventId A unique identifier for the event.
* @return A map of unique identifiers to meta-data specific to each event
* round.
*/
Map<Long, ? extends FlexMap> getByEventId(long eventId);
}

View File

@@ -1,33 +0,0 @@
package com.poststats.golf.service;
import com.brianlong.util.FlexMap;
import com.poststats.service.CacheableService;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
public interface EventService extends CacheableService<Long> {
/**
* This method retrieves meta-data about the specified event.
*
* @param eventId A unique identifier for the event.
* @return A map of meta-data specific to the event.
*/
FlexMap get(long eventId);
/**
* This method retrieves meta-data about the specified event.
*
* @param eventIds Unique identifiers for the event.
* @return A map of unique identifiers to meta-data specific to each event.
*/
Map<Long, ? extends FlexMap> get(Collection<Long> eventIds);
Set<Long> getIdsBySeriesId(int seriesId);
Map<Long, ? extends FlexMap> getBySeriesId(int seriesId);
Integer getSeriesId(long eventId);
}

View File

@@ -1,52 +0,0 @@
package com.poststats.golf.service;
import com.brianlong.util.FlexMap;
import com.poststats.golf.security.AuthenticatedPerson;
import com.poststats.service.CacheableService;
import java.util.Collection;
import java.util.Map;
public interface PersonService extends CacheableService<Long> {
/**
* This method builds a `UserPrincipal` object about the golfer.
*
* Unlike the non-golf `PersonService`, this will include golfer and event
* roles.
*
* @param person A non-golf `UserPrincipal` object.
* @return A golfer `UserPrincipal` object.
*/
AuthenticatedPerson getUserPrincipal(com.poststats.security.AuthenticatedPerson person);
/**
* This method retrieves meta-data about the specified golfer.
*
* If you want meta-data information about only the person, use the non-golf
* `PersonService`.
*
* This retrieves everything from the non-golf `PersonService` and supplements
* it with simple golfer meta-data, like stroke handicaps. It does not include
* related data like point handicap or round counts.
*
* @param personId A unique identifier for the golfer.
* @return A map of meta-data specific to the golfer.
*/
FlexMap get(long personId);
/**
* This method retrieves meta-data about the specified golfers.
*
* If you want meta-data information about only the person, use the non-golf
* `PersonService`.
*
* This retrieves everything from the non-golf `PersonService` and supplements
* it with simple golfer meta-data, like stroke handicaps. It does not include
* related data like point handicap or round counts.
*
* @param personIds Unique identifiers for the golfers.
* @return A map of unique identifiers to meta-data specific to each golfer.
*/
Map<Long, ? extends FlexMap> get(Collection<Long> personIds);
}

View File

@@ -1,12 +0,0 @@
package com.poststats.golf.service;
import com.brianlong.sql.DataSet;
import java.util.List;
public interface SeriesPersonService {
List<DataSet> getSeriesPeople(int seriesId);
List<DataSet> getSeriesParticipants(int seriesId);
}

View File

@@ -1,34 +0,0 @@
package com.poststats.golf.service;
import com.brianlong.util.FlexMap;
import com.poststats.service.CacheableService;
import java.util.Collection;
import java.util.Map;
public interface SeriesService extends CacheableService<Integer> {
/**
* This method retrieves meta-data about the specified series.
*
* @param seriesId A unique identifier for the series.
* @return A map of meta-data specific to the series.
*/
FlexMap get(int seriesId);
/**
* This method retrieves meta-data about the specified series.
*
* @param seriesIds Unique identifiers for the series.
* @return A map of unique identifiers to meta-data specific to each series.
*/
Map<Integer, ? extends FlexMap> get(Collection<Integer> seriesIds);
/**
* This method retrieves series meta-data about the specified event.
*
* @param eventId A unique identifier for the event.
* @return A map of meta-data specific to the series.
*/
FlexMap getByEventId(long eventId);
}

View File

@@ -1,217 +0,0 @@
package com.poststats.golf.service.db;
import com.brianlong.sql.DataSet;
import com.brianlong.sql.FlexPreparedStatement;
import com.brianlong.sql.ResultSubSetFeature;
import com.brianlong.util.FlexMap;
import com.brianlong.util.SubList;
import com.poststats.golf.api.Constants;
import com.poststats.golf.service.CourseService;
import com.poststats.provider.Statement;
import com.poststats.provider.StatementProvider;
import com.poststats.service.FacilityService;
import com.poststats.service.ServiceException;
import com.poststats.service.db.CacheableServiceDAO;
import com.poststats.sql.PostStatsSQL;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Map;
@ApplicationScoped
public class CourseServiceDAO extends CacheableServiceDAO<Integer> implements CourseService {
private final int defaultCacheExpirationInSeconds = 600;
@Inject
private FacilityService facilityService;
@Override
public FlexMap get(int courseId) {
return this.get(Integer.valueOf(courseId));
}
@Override
public FlexMap get(Integer courseId) {
FlexMap row = super.get(Integer.valueOf(courseId));
if (row == null)
return null;
this.injectCache(row, this.facilityService, "facilityID", "facility");
return row;
}
@Override
public Map<Integer, ? extends FlexMap> get(Collection<Integer> courseIds) {
Map<Integer, ? extends FlexMap> rows = super.get(courseIds);
this.injectCache(rows.values(), this.facilityService, "facilityID", "facility");
return rows;
}
@Override
public SubList<? extends FlexMap> findByName(String name, int page, int perPage) {
SubList<? extends FlexMap> rows;
try {
FlexPreparedStatement fps = this.sqlSelectByName.buildPreparedStatement();
try {
fps.setVarchar(1, "%" + name + "%");
fps.setVarchar(2, "%" + name + "%");
fps.setSmallintU(3, (page - 1) * perPage);
fps.setSmallintU(4, perPage);
rows = (SubList<? extends FlexMap>) fps.executeQuery().getAllRows();
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
this.injectCache(rows, this.facilityService, "facilityID", "facility");
return rows;
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(
feature = ResultSubSetFeature.class,
sql = "SELECT CP.*, C.* "
+ "FROM ~g~.Course C "
+ " LEFT JOIN ~g~.CoursePrefix CP ON (C.prefixID=CP.prefixID) "
+ " INNER JOIN ~p~.Facility F ON (C.facilityID=F.facilityID) "
+ "WHERE course LIKE ? OR facility LIKE ? "
)
private StatementProvider sqlSelectByName;
@Override
public SubList<? extends FlexMap> findByJurisdiction(String country, String state, int page, int perPage) {
SubList<? extends FlexMap> rows;
try {
FlexPreparedStatement fps = this.sqlSelectByJurisdiction.buildPreparedStatement();
try {
fps.setVarchar(1, country);
fps.setVarchar(2, state);
fps.setSmallintU(3, (page - 1) * perPage);
fps.setSmallintU(4, perPage);
rows = (SubList<? extends FlexMap>) fps.executeQuery().getAllRows();
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
this.injectCache(rows, this.facilityService, "facilityID", "facility");
return rows;
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(
feature = ResultSubSetFeature.class,
sql = "SELECT CP.*, C.* "
+ "FROM ~g~.Course C "
+ " LEFT JOIN ~g~.CoursePrefix CP ON (C.prefixID=CP.prefixID) "
+ " INNER JOIN ~p~.Facility F ON (C.facilityID=F.facilityID) "
+ "WHERE F.addrcountry=? AND F.addrstate=? "
+ " AND C.deadline IS NULL "
)
private StatementProvider sqlSelectByJurisdiction;
@Override
public SubList<? extends FlexMap> findByLocation(double latitude, double longitude, int radiusInMiles, int page,
int perPage) {
double degrees = PostStatsSQL.miles2degrees(radiusInMiles);
SubList<? extends FlexMap> rows;
try {
FlexPreparedStatement fps = this.sqlSelectByGeolocation.buildPreparedStatement();
try {
fps.setDouble(1, latitude - degrees);
fps.setDouble(2, latitude + degrees);
fps.setDouble(3, longitude - degrees);
fps.setDouble(4, longitude + degrees);
fps.setSmallintU(5, (page - 1) * perPage);
fps.setSmallintU(6, perPage);
rows = (SubList<? extends FlexMap>) fps.executeQuery().getAllRows();
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
this.injectCache(rows, this.facilityService, "facilityID", "facility");
return rows;
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(
feature = ResultSubSetFeature.class,
sql = "SELECT CP.*, C.* "
+ "FROM ~g~.Course C "
+ " LEFT JOIN ~g~.CoursePrefix CP ON (C.prefixID=CP.prefixID) "
+ " INNER JOIN ~p~.Facility F ON (C.facilityID=F.facilityID) "
+ "WHERE F.latitude>=? AND F.latitude<=? AND F.longitude>=? AND F.longitude<=? "
+ " AND C.deadline IS NULL "
)
private StatementProvider sqlSelectByGeolocation;
@Override
protected long getCacheExpirationInSeconds() {
return this.defaultCacheExpirationInSeconds;
}
@Override
protected DataSet fetchOne(Integer courseId) throws SQLException {
FlexPreparedStatement fps = this.sqlSelectCourse.buildPreparedStatement();
try {
fps.setSmallintU(1, courseId);
return fps.executeQuery().getNextRow();
} finally {
fps.close();
}
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(
sql = "SELECT FP.*, FS.*, F.*, CP.prefix coursePrefix, C.* "
+ "FROM ~g~.Course C "
+ " LEFT JOIN ~g~.CoursePrefix CP ON (C.prefixID=CP.prefixID) "
+ " INNER JOIN ~p~.Facility F ON (C.facilityID=F.facilityID) "
+ " LEFT JOIN ~p~.FacilityPrefix FP ON (F.prefixID=FP.prefixID) "
+ " LEFT JOIN ~p~.FacilitySuffix FS ON (F.suffixID=FS.suffixID) "
+ "WHERE courseID=? "
)
private StatementProvider sqlSelectCourse;
@Override
protected Map<Integer, DataSet> fetchBulk(Collection<Integer> courseIds) throws SQLException {
FlexPreparedStatement fps = this.sqlSelectCourses.buildPreparedStatement(courseIds);
try {
return fps.executeQuery().getAllRows("courseID", Integer.class);
} finally {
fps.close();
}
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_POSTSTATS)
@Statement(
sql = "SELECT FP.*, FS.*, F.*, CP.prefix coursePrefix, C.* "
+ "FROM ~g~.Course C "
+ " LEFT JOIN ~g~.CoursePrefix CP ON (C.prefixID=CP.prefixID) "
+ " INNER JOIN ~p~.Facility F ON (C.facilityID=F.facilityID) "
+ " LEFT JOIN ~p~.FacilityPrefix FP ON (F.prefixID=FP.prefixID) "
+ " LEFT JOIN ~p~.FacilitySuffix FS ON (F.suffixID=FS.suffixID) "
+ "WHERE courseID IN (??) "
)
private StatementProvider sqlSelectCourses;
}

View File

@@ -1,77 +0,0 @@
package com.poststats.golf.service.db;
import com.brianlong.sql.DataSet;
import com.brianlong.sql.FlexPreparedStatement;
import com.poststats.golf.api.Constants;
import com.poststats.golf.service.EventFinanceService;
import com.poststats.provider.Statement;
import com.poststats.provider.StatementProvider;
import com.poststats.service.ServiceException;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import java.sql.SQLException;
import java.util.List;
@ApplicationScoped
public class EventFinanceServiceDAO implements EventFinanceService {
@Override
public List<DataSet> getPersonsBalances(long eventId) {
try {
FlexPreparedStatement fps = this.sqlSelectBalances.buildPreparedStatement();
try {
for (int i = 1; i <= 5; i++)
fps.setIntegerU(i, eventId);
return fps.executeQuery().getAllRows();
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(
sql = "SELECT P.personID, P.prefix, P.fname, P.lname, P.suffix, "
+ " TT.epersonID, SUM(TT.expenses) expenses, SUM(TT.paid) paid, (SUM(TT.paid)-SUM(TT.expenses)) balance "
+ "FROM ("
+ " SELECT ALL EP.personID, EP.epersonID, IF(EB.projectedValue IS NULL, 0, EB.projectedValue) expenses, 0 paid "
+ " FROM ~g~.EventPerson EP "
+ " LEFT JOIN ~g~.EventBudget EB ON (EP.eventID=EB.eventID) "
+ " WHERE EP.eventID=? "
+ " UNION ALL "
+ " SELECT ALL EP.personID, EP.epersonID, EO.amount expenses, 0 paid "
+ " FROM ~g~.EventPerson EP "
+ " INNER JOIN ~g~.EventPersonOption EPO ON (EP.epersonID=EPO.epersonID) "
+ " INNER JOIN ~g~.EventOption EO ON (EPO.optionID=EO.optionID) "
+ " WHERE EP.eventID=? AND EPO.answer='Y' AND EO.amount IS NOT NULL AND EO.waive IS FALSE "
+ " UNION ALL "
+ " SELECT ALL EP.personID, EP.epersonID, EO.amount expenses, 0 paid "
+ " FROM ~g~.EventPerson EP "
+ " INNER JOIN ~g~.EventPersonOption EPO ON (EP.epersonID=EPO.epersonID) "
+ " INNER JOIN ~g~.EventOption EO ON (EPO.optionID=EO.optionID) "
+ " WHERE EP.eventID=? AND EPO.itemID IS NOT NULL "
+ " AND (EO.multiple IS FALSE OR EO.funded='option' OR EO.static IS NOT NULL) "
+ " AND EO.amount IS NOT NULL AND EO.waive IS FALSE "
+ " UNION ALL "
+ " SELECT ALL EP.personID, EP.epersonID, EOI.amount expenses, 0 paid "
+ " FROM ~g~.EventPerson EP "
+ " INNER JOIN ~g~.EventPersonOption EPO ON (EP.epersonID=EPO.epersonID) "
+ " INNER JOIN ~g~.EventOption EO ON (EPO.optionID=EO.optionID) "
+ " INNER JOIN ~g~.EventOptionItem EOI ON (EPO.itemID=EOI.itemID) "
+ " WHERE EP.eventID=? AND EO.waive IS FALSE AND EO.funded='selection' AND EOI.amount IS NOT NULL "
+ " UNION ALL "
+ " SELECT ALL EPP.personID, EP.epersonID, 0 expenses, EPP.amount paid "
+ " FROM ~g~.EventPersonPayment EPP "
+ " LEFT JOIN ~g~.EventPerson EP ON (EPP.eventID=EP.eventID AND EPP.personID=EP.personID) "
+ " WHERE EPP.eventID=?) TT "
+ " INNER JOIN ~p~.Person P "
+ "GROUP BY P.personID "
+ "ORDER BY P.lname, P.fname, P.suffix"
)
private StatementProvider sqlSelectBalances;
}

View File

@@ -1,126 +0,0 @@
package com.poststats.golf.service.db;
import com.brianlong.cache.CacheRetrievalException;
import com.brianlong.sql.FlexPreparedStatement;
import com.brianlong.util.FlexMap;
import com.poststats.golf.api.Constants;
import com.poststats.golf.service.EventPersonService;
import com.poststats.golf.service.PersonService;
import com.poststats.provider.Statement;
import com.poststats.provider.StatementProvider;
import com.poststats.service.ServiceException;
import com.poststats.sql.PostStatsDataSource;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import java.math.BigInteger;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
@ApplicationScoped
public class EventPersonServiceDAO implements EventPersonService {
@Inject
private PersonService golferService;
@Override
public List<FlexMap> getPeople(long eventId, boolean includeDetails) {
try {
return this.query(this.sqlSelectPersonIds, eventId, 2, includeDetails);
} catch (CacheRetrievalException cre) {
throw new ServiceException(cre);
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(
sql = "SELECT epersonID, personID FROM ~g~.EventPerson WHERE eventID=? "
+ "UNION "
+ "SELECT DISTINCT NULL, personID "
+ "FROM ~g~.EventPersonAccessControl EPAC "
+ " LEFT JOIN ~g~.EventPerson EP ON (EPAC.eventID=EP.eventID AND EPAC.personID=EP.personID) "
+ "WHERE eventID=? AND EP.personID IS NULL "
)
private StatementProvider sqlSelectPersonIds;
@Override
public List<FlexMap> getParticipants(long eventId, boolean includeDetails) {
Connection dbcon = PostStatsDataSource.getInstance().acquire(true);
try {
return this.query(this.sqlSelectParticipantIds, eventId, 1, includeDetails);
} catch (CacheRetrievalException cre) {
throw new ServiceException(cre);
} catch (SQLException se) {
throw new ServiceException(se);
} finally {
PostStatsDataSource.getInstance().release(dbcon);
}
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(sql = "SELECT epersonID, personID FROM ~g~.EventPerson WHERE eventID=?")
private StatementProvider sqlSelectParticipantIds;
@Override
public Set<Long> getSeriesEventIdsAsParticipant(int seriesId, long personId) {
try {
FlexPreparedStatement fps = this.sqlSelectFellowParticipants.buildPreparedStatement();
try {
fps.setSmallintU(1, seriesId);
fps.setIntegerU(2, personId);
return fps.executeQuery().getFirstColumn(Long.class, new HashSet<>());
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(
sql = "SELECT DISTINCT EP2.personID "
+ "FROM ~g~.EventPerson EP1 "
+ " INNER JOIN Event E ON (EP1.eventID=E.eventID) "
+ " INNER JOIN EventPerson EP2 ON (E.eventID=EP2.eventID) "
+ "WHERE EP1.personID=? AND E.seriesID=?"
)
private StatementProvider sqlSelectFellowParticipants;
private List<FlexMap> query(StatementProvider provider, long eventId, int parameterCount, boolean includeDetails)
throws CacheRetrievalException, SQLException {
Map<BigInteger, Long> epersonIdMap;
FlexPreparedStatement fps = provider.buildPreparedStatement();
try {
for (int i = 1; i <= parameterCount; i++)
fps.setIntegerU(i, eventId);
epersonIdMap = fps.executeQuery().getFirstTwoColumns(BigInteger.class, Long.class);
} finally {
fps.close();
}
Map<Long, ? extends FlexMap> personMap = this.golferService.get(epersonIdMap.values());
List<FlexMap> rows = new ArrayList<>(personMap.size());
for (Entry<BigInteger, Long> eperson : epersonIdMap.entrySet()) {
FlexMap row = (FlexMap) personMap.get(eperson.getValue()).clone();
row.put("epersonID", eperson.getKey());
rows.add(row);
}
return rows;
}
}

View File

@@ -1,141 +0,0 @@
package com.poststats.golf.service.db;
import com.brianlong.sql.DataSet;
import com.brianlong.sql.FlexPreparedStatement;
import com.brianlong.util.FlexMap;
import com.poststats.golf.api.Constants;
import com.poststats.golf.service.CourseService;
import com.poststats.golf.service.EventRoundService;
import com.poststats.provider.Statement;
import com.poststats.provider.StatementProvider;
import com.poststats.service.ServiceException;
import com.poststats.service.db.CacheableServiceDAO;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Map;
@ApplicationScoped
public class EventRoundServiceDAO extends CacheableServiceDAO<Long> implements EventRoundService {
private final long defaultCacheExpirationInSeconds = 3600;
@Inject
private CourseService courseService;
@Override
public FlexMap get(long eroundId) {
return this.get(Long.valueOf(eroundId));
}
@Override
public FlexMap get(Long eventId) {
FlexMap row = super.get(Long.valueOf(eventId));
if (row == null)
return null;
this.injectCache(row, this.courseService, "courseID", "course");
row.put("course", this.courseService.get(row.getInteger("courseID")));
return row;
}
@Override
public FlexMap get(long eventId, short number) {
try {
FlexPreparedStatement fps = this.sqlSelectByNumber.buildPreparedStatement();
try {
fps.setIntegerU(1, eventId);
fps.setTinyintU(2, number);
return fps.executeQuery().getNextRow();
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(
sql = "SELECT TF.*, ER.* "
+ "FROM ~g~.EventRound ER "
+ " LEFT JOIN ~g~.TeeFormat TF ON (ER.teeFormatID=TF.teeFormatID) "
+ "WHERE ER.eventID=? AND ER.round=? "
)
private StatementProvider sqlSelectByNumber;
@Override
public Map<Long, DataSet> getByEventId(long eventId) {
try {
FlexPreparedStatement fps = this.sqlSelectByEventId.buildPreparedStatement();
try {
fps.setIntegerU(1, eventId);
return fps.executeQuery().getAllRows("eroundID", Long.class);
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(
sql = "SELECT TF.*, ER.* "
+ "FROM ~g~.EventRound ER "
+ " LEFT JOIN ~g~.TeeFormat TF ON (ER.teeFormatID=TF.teeFormatID) "
+ "WHERE ER.eventID=? "
)
private StatementProvider sqlSelectByEventId;
@Override
protected long getCacheExpirationInSeconds() {
return this.defaultCacheExpirationInSeconds;
}
@Override
protected DataSet fetchOne(Long eroundId) throws SQLException {
FlexPreparedStatement fps = this.sqlSelect.buildPreparedStatement();
try {
fps.setIntegerU(1, eroundId);
return fps.executeQuery().getNextRow();
} finally {
fps.close();
}
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(
sql = "SELECT TF.*, ER.* "
+ "FROM ~g~.EventRound ER "
+ " LEFT JOIN ~g~.TeeFormat TF ON (ER.teeFormatID=TF.teeFormatID) "
+ "WHERE ER.eroundID=? "
)
private StatementProvider sqlSelect;
@Override
protected Map<Long, DataSet> fetchBulk(Collection<Long> eroundIds) throws SQLException {
FlexPreparedStatement fps = this.sqlSelectManys.buildPreparedStatement(eroundIds);
try {
return fps.executeQuery().getAllRows("eroundID", Long.class);
} finally {
fps.close();
}
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(
sql = "SELECT TF.*, ER.* "
+ "FROM ~g~.EventRound ER "
+ " LEFT JOIN ~g~.TeeFormat TF ON (ER.teeFormatID=TF.teeFormatID) "
+ "WHERE ER.eroundID IN (??) "
)
private StatementProvider sqlSelectManys;
}

View File

@@ -1,163 +0,0 @@
package com.poststats.golf.service.db;
import com.brianlong.sql.DataSet;
import com.brianlong.sql.FlexPreparedStatement;
import com.brianlong.util.FlexMap;
import com.poststats.golf.api.Constants;
import com.poststats.golf.service.EventService;
import com.poststats.golf.service.SeriesService;
import com.poststats.provider.Statement;
import com.poststats.provider.StatementProvider;
import com.poststats.service.ServiceException;
import com.poststats.service.db.CacheableServiceDAO;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import java.sql.SQLException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@ApplicationScoped
public class EventServiceDAO extends CacheableServiceDAO<Long> implements EventService {
private final long defaultCacheExpirationInSeconds = 3600;
@Inject
private SeriesService seriesService;
@Override
public FlexMap get(long eventId) {
return this.get(Long.valueOf(eventId));
}
@Override
public FlexMap get(Long eventId) {
FlexMap row = super.get(Long.valueOf(eventId));
if (row == null)
return null;
this.injectCache(row, this.seriesService, "seriesID", "series");
return row;
}
@Override
public Map<Long, ? extends FlexMap> get(Collection<Long> eventIds) {
Map<Long, ? extends FlexMap> rows = super.get(eventIds);
this.injectCache(rows.values(), this.seriesService, "seriesID", "series");
return rows;
}
@Override
public Set<Long> getIdsBySeriesId(int seriesId) {
try {
FlexPreparedStatement fps = this.sqlSelectIdsBySeriesId.buildPreparedStatement();
try {
fps.setSmallintU(1, seriesId);
return fps.executeQuery().getFirstColumn(Long.class, new HashSet<>());
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(sql = "SELECT eventId FROM ~g~.Event WHERE seriesID=? ")
private StatementProvider sqlSelectIdsBySeriesId;
@Override
public Map<Long, DataSet> getBySeriesId(int seriesId) {
try {
FlexPreparedStatement fps = this.sqlSelectBySeriesId.buildPreparedStatement();
try {
fps.setSmallintU(1, seriesId);
return fps.executeQuery().getAllRows("eventID", Long.class);
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(sql = "SELECT * FROM ~g~.Event WHERE seriesID=? ORDER BY liveline DESC ")
private StatementProvider sqlSelectBySeriesId;
@Override
public Integer getSeriesId(long eventId) {
FlexMap event = this.getIfHit(eventId);
if (event != null)
return event.getInteger("seriesID");
try {
FlexPreparedStatement fps = this.sqlSelectSeriesId.buildPreparedStatement();
try {
fps.setSmallintU(1, eventId);
return fps.executeQuery().getOne(Integer.class);
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(sql = "SELECT seriesID FROM ~g~.Event WHERE eventID=? ")
private StatementProvider sqlSelectSeriesId;
@Override
protected long getCacheExpirationInSeconds() {
return this.defaultCacheExpirationInSeconds;
}
@Override
protected DataSet fetchOne(Long eventId) throws SQLException {
FlexPreparedStatement fps = this.sqlSelectEvent.buildPreparedStatement();
try {
fps.setIntegerU(1, eventId);
return fps.executeQuery().getNextRow();
} finally {
fps.close();
}
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(
sql = "SELECT EF.*, E.* "
+ "FROM ~g~.Event E "
+ " INNER JOIN ~g~.EventFeature EF ON (E.eventID=EF.eventID) "
+ "WHERE E.eventID=? "
)
private StatementProvider sqlSelectEvent;
@Override
protected Map<Long, DataSet> fetchBulk(Collection<Long> eventIds) throws SQLException {
FlexPreparedStatement fps = this.sqlSelectEvents.buildPreparedStatement(eventIds);
try {
return fps.executeQuery().getAllRows("eventID", Long.class);
} finally {
fps.close();
}
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(
sql = "SELECT EF.*, E.* "
+ "FROM ~g~.Event E "
+ " INNER JOIN ~g~.EventFeature EF ON (E.eventID=EF.eventID) "
+ "WHERE E.eventID IN (??) "
)
private StatementProvider sqlSelectEvents;
}

View File

@@ -1,207 +0,0 @@
package com.poststats.golf.service.db;
import com.brianlong.cache.CacheException;
import com.brianlong.cache.CacheableFetcher;
import com.brianlong.cache.ClusterAwareMemoryCacher;
import com.brianlong.sql.DataSet;
import com.brianlong.sql.FlexPreparedStatement;
import com.brianlong.sql.FlexStatement;
import com.brianlong.util.FlexMap;
import com.poststats.golf.api.Constants;
import com.poststats.golf.security.AuthenticatedPerson;
import com.poststats.golf.service.PersonService;
import com.poststats.golf.sql.GolfDataSource;
import com.poststats.provider.Statement;
import com.poststats.provider.StatementProvider;
import com.poststats.service.ServiceException;
import com.poststats.service.db.CacheableServiceDAO;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@ApplicationScoped
public class PersonServiceDAO extends CacheableServiceDAO<Long> implements PersonService {
@Inject
private com.poststats.service.PersonService poststatsPersonService;
private final int roleCacheExpirationInSeconds = 300;
private final int infoCacheExpirationInSeconds = 120;
private ClusterAwareMemoryCacher<Long, AuthenticatedPerson> principalCacher;
@PostConstruct
private void initCache() {
this.principalCacher = new ClusterAwareMemoryCacher<Long, AuthenticatedPerson>(
this.roleCacheExpirationInSeconds * 1000L, this.clusterService.getHazelcastInstance());
}
@Override
protected long getCacheExpirationInSeconds() {
return this.infoCacheExpirationInSeconds;
}
@Override
public AuthenticatedPerson getUserPrincipal(com.poststats.security.AuthenticatedPerson person) {
try {
return this.principalCacher.get(person.getId(), new PrincipalCacheableFetcher() {
@Override
public com.poststats.security.AuthenticatedPerson getPerson() {
return person;
}
});
} catch (CacheException ce) {
throw new ServiceException(ce);
}
}
@Override
public FlexMap get(long personId) {
return this.get(Long.valueOf(personId));
}
@Override
public FlexMap get(Long personId) {
FlexMap row = this.get(Long.valueOf(personId));
if (row == null)
return null;
this.injectCache(row, this.poststatsPersonService, "personID", "person");
return row;
}
@Override
public Map<Long, ? extends FlexMap> get(Collection<Long> ids) {
Map<Long, ? extends FlexMap> rows = super.get(ids);
this.injectCache(rows.values(), this.poststatsPersonService, "personID", "person");
return rows;
}
private abstract class PrincipalCacheableFetcher implements CacheableFetcher<Long, AuthenticatedPerson> {
public abstract com.poststats.security.AuthenticatedPerson getPerson();
@Override
public AuthenticatedPerson get(Long personId) throws SQLException {
Connection dbcon = GolfDataSource.getInstance().acquire(true);
try {
return new AuthenticatedPerson(this.getPerson(), queryBuddies(personId), queryAccessControls(personId),
queryEventAccessControls(personId));
} finally {
GolfDataSource.getInstance().release(dbcon);
}
}
@Override
public Map<Long, AuthenticatedPerson> get(Collection<Long> personIds) throws SQLException, IOException {
throw new UnsupportedOperationException();
}
private Set<Long> queryBuddies(long personId) throws SQLException {
FlexPreparedStatement fps = sqlSelectBuddyIds.buildPreparedStatement();
try {
fps.setIntegerU(1, personId);
Set<Long> set = new HashSet<>();
fps.executeQuery().getFirstColumn(Long.class, set);
return set;
} finally {
fps.close();
}
}
private Set<String> queryAccessControls(long personId) throws SQLException {
FlexPreparedStatement fps = sqlSelectAcs.buildPreparedStatement();
try {
fps.setIntegerU(1, personId);
Set<String> set = new HashSet<>();
fps.executeQuery().getFirstColumn(String.class, set);
return set;
} finally {
fps.close();
}
}
private Map<Long, Set<String>> queryEventAccessControls(long personId) throws SQLException {
FlexPreparedStatement fps = sqlSelectEventAcs.buildPreparedStatement();
try {
fps.setIntegerU(1, personId);
return fps.executeQuery().getFirstTwoColumnsOneToMany(Long.class, String.class);
} finally {
fps.close();
}
}
};
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(
sql = "SELECT PS1.personID "
+ "FROM ~g~.PersonSpectator PS1 "
+ " INNER JOIN ~g~.PersonSpectator PS2 ON (PS1.personID=PS2.watchedPersonID AND PS1.watchedPersonID=PS2.personID) "
+ "WHERE PS1.watchedPersonID=?"
)
private StatementProvider sqlSelectBuddyIds;
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(
sql = "SELECT AC.acSID "
+ "FROM ~g~.PersonAccessControl PAC "
+ " INNER JOIN ~p~.AccessControl AC ON (PAC.acID=AC.acID) "
+ "WHERE PAC.personID=?"
)
private StatementProvider sqlSelectAcs;
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(
sql = "SELECT EPAC.eventID, AC.acSID "
+ "FROM ~g~.EventPersonAccessControl EPAC "
+ " INNER JOIN ~p~.AccessControl AC ON (EPAC.acID=AC.acID) "
+ "WHERE EPAC.personID=?"
)
private StatementProvider sqlSelectEventAcs;
@Override
protected DataSet fetchOne(Long personId) throws SQLException {
FlexPreparedStatement fps = this.sqlSelectPerson.buildPreparedStatement();
try {
fps.setIntegerU(1, personId);
return fps.executeQuery().getNextRow();
} finally {
fps.close();
}
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(sql = "SELECT GP.* FROM ~g~.Person GP WHERE GP.personID=?")
private StatementProvider sqlSelectPerson;
@Override
protected Map<Long, DataSet> fetchBulk(Collection<Long> personIds) throws SQLException {
FlexStatement fs = this.sqlSelectPersons.buildStatement(personIds);
try {
return fs.executeQuery().getAllRows("personID", Long.class);
} finally {
fs.close();
}
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(sql = "SELECT GP.* FROM ~g~.Person GP WHERE GP.personID IN (??)")
private StatementProvider sqlSelectPersons;
}

View File

@@ -1,85 +0,0 @@
package com.poststats.golf.service.db;
import com.brianlong.sql.DataSet;
import com.brianlong.sql.FlexPreparedStatement;
import com.brianlong.util.FlexMap;
import com.poststats.golf.api.Constants;
import com.poststats.golf.service.SeriesService;
import com.poststats.provider.Statement;
import com.poststats.provider.StatementProvider;
import com.poststats.service.ServiceException;
import com.poststats.service.db.CacheableServiceDAO;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Map;
@ApplicationScoped
public class SeriesServiceDAO extends CacheableServiceDAO<Integer> implements SeriesService {
private final long defaultCacheExpirationInSeconds = 3600;
@Override
public FlexMap get(int seriesId) {
return this.get(Integer.valueOf(seriesId));
}
@Override
public DataSet getByEventId(long eventId) {
try {
FlexPreparedStatement fps = this.sqlSelectByEventId.buildPreparedStatement();
try {
fps.setIntegerU(1, eventId);
return fps.executeQuery().getNextRow();
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(sql = "SELECT * FROM ~g~.Series WHERE eventID=? ")
private StatementProvider sqlSelectByEventId;
@Override
protected long getCacheExpirationInSeconds() {
return this.defaultCacheExpirationInSeconds;
}
@Override
protected DataSet fetchOne(Integer seriesId) throws SQLException {
FlexPreparedStatement fps = this.sqlSelectSeries.buildPreparedStatement();
try {
fps.setSmallintU(1, seriesId);
return fps.executeQuery().getNextRow();
} finally {
fps.close();
}
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(sql = "SELECT S.* FROM ~g~.Series S WHERE seriesID=? ")
private StatementProvider sqlSelectSeries;
@Override
protected Map<Integer, DataSet> fetchBulk(Collection<Integer> seriesIds) throws SQLException {
FlexPreparedStatement fps = this.sqlSelectSerieses.buildPreparedStatement(seriesIds);
try {
return fps.executeQuery().getAllRows("seriesID", Integer.class);
} finally {
fps.close();
}
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(sql = "SELECT S.* FROM ~g~.Series S WHERE seriesID IN (??) ")
private StatementProvider sqlSelectSerieses;
}

View File

@@ -1,48 +0,0 @@
package com.poststats.golf.servlet;
import com.poststats.golf.security.AuthenticatedSecurityContext;
import com.poststats.golf.service.PersonService;
import com.poststats.security.AuthenticatedPerson;
import jakarta.annotation.Priority;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.Priorities;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.core.SecurityContext;
import jakarta.ws.rs.ext.Provider;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ApplicationScoped
@Provider
@Priority(Priorities.AUTHENTICATION + 5)
public class AuthenticationFilter implements ContainerRequestFilter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Inject
private PersonService personService;
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
SecurityContext scontext = requestContext.getSecurityContext();
if (scontext == null || scontext.getUserPrincipal() == null)
return;
AuthenticatedPerson authPerson = (AuthenticatedPerson) scontext.getUserPrincipal();
this.logger.debug("Gathering roles for golf: {}", authPerson);
com.poststats.golf.security.AuthenticatedPerson gauthPerson = this.personService.getUserPrincipal(authPerson);
if (this.logger.isTraceEnabled())
this.logger.trace("Authorized roles: {} => {}", gauthPerson.getId(), gauthPerson.getAllAccessControls());
scontext = new AuthenticatedSecurityContext(scontext, gauthPerson);
requestContext.setSecurityContext(scontext);
}
}

View File

@@ -1,60 +0,0 @@
package com.poststats.golf.servlet;
import com.brianlong.util.StringUtil;
import com.poststats.golf.api.Constants;
import com.poststats.golf.security.AuthenticatedPerson;
import com.poststats.golf.security.EventSecurityContext;
import jakarta.annotation.Priority;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.ws.rs.Priorities;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.core.SecurityContext;
import jakarta.ws.rs.ext.Provider;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ApplicationScoped
@Provider
@Priority(Priorities.AUTHORIZATION - 5)
public class EventFilter implements ContainerRequestFilter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
String eventIdStr = requestContext.getUriInfo().getPathParameters().getFirst(Constants.EVENT_ID);
eventIdStr = StringUtil.getInstance().trim(eventIdStr);
if (eventIdStr == null) {
eventIdStr = requestContext.getUriInfo().getQueryParameters().getFirst(Constants.EVENT_ID);
eventIdStr = StringUtil.getInstance().trim(eventIdStr);
}
if (eventIdStr == null)
return;
this.logger.debug("Entering event context: {}", eventIdStr);
long eventId = Long.valueOf(eventIdStr);
requestContext.setProperty(Constants.EVENT_ID, eventId);
SecurityContext scontext = requestContext.getSecurityContext();
if (scontext.getUserPrincipal() == null)
return;
this.logger.debug("Narrowing authorization for event: {} => {}", scontext.getUserPrincipal(), eventId);
EventSecurityContext epscontext = new EventSecurityContext(scontext, eventId);
if (this.logger.isTraceEnabled()) {
AuthenticatedPerson person = (AuthenticatedPerson) epscontext.getUserPrincipal();
this.logger.trace("Authorized event roles: {} => {}", person.getId(),
person.getEventAccessControls(eventId));
}
requestContext.setSecurityContext(epscontext);
}
}

View File

@@ -1,44 +0,0 @@
package com.poststats.golf.servlet;
import com.poststats.golf.api.Constants;
import com.poststats.golf.security.PersonSecurityContext;
import com.poststats.golf.service.PersonService;
import jakarta.annotation.Priority;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.Priorities;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.core.SecurityContext;
import jakarta.ws.rs.ext.Provider;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ApplicationScoped
@Provider
@Priority(Priorities.AUTHORIZATION - 8)
public class PersonFilter implements ContainerRequestFilter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Inject
private PersonService personService;
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
Long personId = (Long) requestContext.getProperty(Constants.PERSON_ID);
if (personId == null)
return;
SecurityContext scontext = requestContext.getSecurityContext();
if (scontext == null || scontext.getUserPrincipal() == null)
return;
this.logger.debug("Entering golfer context: {}", personId);
scontext = new PersonSecurityContext(scontext, personId);
requestContext.setSecurityContext(scontext);
}
}

Some files were not shown because too many files have changed in this diff Show More