major refactor; reformatting

This commit is contained in:
2023-02-04 11:42:11 -05:00
parent bf7e93670e
commit e69b343f99
22 changed files with 564 additions and 449 deletions

View File

@@ -1,16 +1,13 @@
package com.poststats.golf.api; package com.poststats.golf.api;
import com.brianlong.sql.DataSet; import com.brianlong.util.FlexMap;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.poststats.api.Constants; import com.poststats.api.Constants;
import com.poststats.golf.api.model.Event; import com.poststats.golf.api.model.Event;
import com.poststats.golf.api.model.EventDetail; import com.poststats.golf.api.model.EventDetail;
import com.poststats.golf.service.EventService; import com.poststats.golf.service.EventService;
import com.poststats.golf.transformer.EventTransformer; import com.poststats.golf.transformer.EventTransformer;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.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.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
@@ -32,13 +29,6 @@ import org.slf4j.LoggerFactory;
@RequestScoped @RequestScoped
@Path("/golf/event/{eventId}") @Path("/golf/event/{eventId}")
@Tag(name = "Event API") @Tag(name = "Event API")
@OpenAPIDefinition(
info = @Info(
contact = @Contact(name = "Brian Long", email = "brian.long@poststats.com"),
title = "PostStats Golf API",
description = "An API providing access to PostStats Golf objects."
)
)
public class EventApi { public class EventApi {
private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final Logger logger = LoggerFactory.getLogger(this.getClass());
@@ -68,8 +58,9 @@ public class EventApi {
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found") @ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
}) })
public Event get() throws JsonProcessingException { public Event get() throws JsonProcessingException {
DataSet row = this.eventService.get(this.eventId); FlexMap row = this.eventService.get(this.eventId);
if (row == null) throw new WebApplicationException("Event not found", Status.NOT_FOUND); if (row == null)
throw new WebApplicationException("Event not found", Status.NOT_FOUND);
return this.eventTransformer.toModel(row); return this.eventTransformer.toModel(row);
} }
@@ -86,8 +77,9 @@ public class EventApi {
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found") @ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
}) })
public EventDetail getDetail() throws JsonProcessingException { public EventDetail getDetail() throws JsonProcessingException {
DataSet row = this.eventService.getDetail(this.eventId); FlexMap row = this.eventService.get(this.eventId);
if (row == null) throw new WebApplicationException("Event not found", Status.NOT_FOUND); if (row == null)
throw new WebApplicationException("Event not found", Status.NOT_FOUND);
return (EventDetail) this.eventTransformer.toModel(row, new EventDetail()); return (EventDetail) this.eventTransformer.toModel(row, new EventDetail());
} }

View File

@@ -4,7 +4,6 @@ import com.brianlong.sql.DataSet;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.poststats.golf.service.EventFinanceService; import com.poststats.golf.service.EventFinanceService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
@@ -42,15 +41,17 @@ public class EventFinanceApi {
@GET @GET
@Path("/balance/persons") @Path("/balance/persons")
@RolesAllowed(Constants.EVENT_ROLE_PREFIX + "finance") @RolesAllowed(Constants.EVENT_ROLE_PREFIX + "member")
@Produces(Constants.V1_JSON) @Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves the balances of all participants in an event.") @Operation(summary = "Retrieves the balances of all participants in an event.")
@ApiResponses({ @ApiResponses({
@ApiResponse(responseCode = "200", description = "Success", content = { @ApiResponse(responseCode = "200", description = "Success"),
@Content(mediaType = Constants.V1_JSON), @Content(mediaType = "text/csv") @ApiResponse(responseCode = "401", description = "Not authenticated"),
}), @ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found") @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 { public List<Map<String, Object>> getBalanceByPersonsAsJson(@Context SecurityContext securityContext)
throws JsonProcessingException {
List<DataSet> personsBalances = this.eventFinanceService.getPersonsBalances(this.eventId); List<DataSet> personsBalances = this.eventFinanceService.getPersonsBalances(this.eventId);
List<Map<String, Object>> personsBalancesJson = new ArrayList<>(personsBalances.size()); List<Map<String, Object>> personsBalancesJson = new ArrayList<>(personsBalances.size());
@@ -67,9 +68,16 @@ public class EventFinanceApi {
} }
@GET @GET
@Path("/balance/persons") @Path("/balance/persons/csv")
@RolesAllowed(Constants.EVENT_ROLE_PREFIX + "finance") @RolesAllowed(Constants.EVENT_ROLE_PREFIX + "finance")
@Produces("text/csv") @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 { public StreamingOutput getBalanceByPersonsAsCsv(@Context SecurityContext securityContext) throws IOException {
List<DataSet> personsBalances = this.eventFinanceService.getPersonsBalances(this.eventId); List<DataSet> personsBalances = this.eventFinanceService.getPersonsBalances(this.eventId);
@@ -79,11 +87,13 @@ public class EventFinanceApi {
PrintStream pstream = new PrintStream(output); PrintStream pstream = new PrintStream(output);
CSVPrinter personsBalancesCsvPrinter = new CSVPrinter(pstream, CSVFormat.DEFAULT); CSVPrinter personsBalancesCsvPrinter = new CSVPrinter(pstream, CSVFormat.DEFAULT);
try { try {
personsBalancesCsvPrinter.printRecord("personID", "lname", "fname", "suffix", "expense", "paid", "balance"); personsBalancesCsvPrinter.printRecord("personID", "lname", "fname", "suffix", "expense", "paid",
"balance");
for (DataSet personBalance : personsBalances) { for (DataSet personBalance : personsBalances) {
personsBalancesCsvPrinter.printRecord(personBalance.getLong("personID"), personBalance.getString("lname"), personsBalancesCsvPrinter.printRecord(personBalance.getLong("personID"),
personBalance.getString("fname"), personBalance.getString("suffix"), personBalance.getFloat("expense"), personBalance.getString("lname"), personBalance.getString("fname"),
personBalance.getString("suffix"), personBalance.getFloat("expense"),
personBalance.getFloat("paid"), personBalance.getFloat("balance")); personBalance.getFloat("paid"), personBalance.getFloat("balance"));
} }

View File

@@ -1,8 +1,7 @@
package com.poststats.golf.api; package com.poststats.golf.api;
import com.brianlong.sql.DataSet; import com.brianlong.util.FlexMap;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.poststats.api.Constants;
import com.poststats.api.model.Person; import com.poststats.api.model.Person;
import com.poststats.golf.service.EventPersonService; import com.poststats.golf.service.EventPersonService;
import com.poststats.golf.service.EventService; import com.poststats.golf.service.EventService;
@@ -18,15 +17,10 @@ import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path; import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam; import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces; import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.SecurityContext;
import jakarta.ws.rs.core.StreamingOutput; import jakarta.ws.rs.core.StreamingOutput;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.PrintStream; import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@@ -53,118 +47,121 @@ public class EventPersonApi {
@GET @GET
@Path("/people") @Path("/people")
@RolesAllowed("member") @RolesAllowed(Constants.EVENT_ROLE_PREFIX + "member")
@Produces(Constants.V1_JSON) @Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves the administrators and participants in an event.") @Operation(summary = "Retrieves limited meta-data about all the participants and administrators in an event.")
@ApiResponses({ @ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"), @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") @ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
}) })
public List<Person> get(@Context SecurityContext securityContext, @QueryParam("format") String format) throws JsonProcessingException, IOException { public List<Person> get() {
if (!securityContext.isUserInRole(this.eventId + "~member")) throw new SecurityException("Not permitted"); return this.personTransformer.toModel(this.eventPersonService.getPeople(this.eventId, false));
List<DataSet> persons = this.eventPersonService.getPeople(this.eventId);
return this.toAddressBookAsJson(persons);
} }
@GET @GET
@Path("/people") @Path("/people/detail")
@RolesAllowed("member") @RolesAllowed(Constants.EVENT_ROLE_PREFIX + "member")
@Produces("text/csv") @Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves the administrators and participants in an event.") @Operation(summary = "Retrieves detailed meta-data about all the participants and administrators in an event.")
@ApiResponses({ @ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"), @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") @ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
}) })
public StreamingOutput getAsCsv(@Context SecurityContext securityContext, @QueryParam("format") String format) throws JsonProcessingException, IOException { public List<Person> getDetail() {
if (!securityContext.isUserInRole(this.eventId + "~member")) throw new SecurityException("Not permitted"); return this.personTransformer.toModel(this.eventPersonService.getPeople(this.eventId, true));
}
List<DataSet> persons = this.eventPersonService.getPeople(this.eventId); @GET
return this.toAddressBookAsCsv(persons); @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 @GET
@Path("/participants") @Path("/participants")
@RolesAllowed("member")
@Produces(Constants.V1_JSON) @Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves the participants in an event.") @Operation(summary = "Retrieves limited meta-data about all the participants in an event.")
@ApiResponses({ @ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"), @ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found") @ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
}) })
public List<Person> getParticipants(@Context SecurityContext securityContext, @Context @QueryParam("format") String format) public List<Person> getParticipants() {
throws JsonProcessingException, IOException { return this.personTransformer.toModel(this.eventPersonService.getParticipants(this.eventId, false));
if (!securityContext.isUserInRole(this.eventId + "~member")) throw new SecurityException("Not permitted");
List<DataSet> persons = this.eventPersonService.getParticipants(this.eventId);
return this.toAddressBookAsJson(persons);
} }
@GET @GET
@Path("/participants") @Path("/participants/csv")
@RolesAllowed("member") @RolesAllowed(Constants.EVENT_ROLE_PREFIX + "membership")
@Produces("text/csv") @Produces("text/csv")
@Operation(summary = "Retrieves the participants in an event.") @Operation(summary = "Retrieves address book meta-data about all the participants in an event.")
@ApiResponses({ @ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"), @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") @ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
}) })
public StreamingOutput getParticipantsAsCsv(@Context SecurityContext securityContext, @Context @QueryParam("format") String format) public StreamingOutput getParticipantsAsCsv() {
throws JsonProcessingException, IOException { List<FlexMap> persons = this.eventPersonService.getParticipants(this.eventId, true);
if (!securityContext.isUserInRole(this.eventId + "~member")) throw new SecurityException("Not permitted"); return this.toCsv(persons);
List<DataSet> persons = this.eventPersonService.getParticipants(this.eventId);
return this.toAddressBookAsCsv(persons);
} }
@GET @GET
@Path("/series/participants") @Path("/series/participants")
@RolesAllowed("member") @RolesAllowed(Constants.EVENT_ROLE_PREFIX + "membership")
@Produces(Constants.V1_JSON) @Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves all the participants in an event series.") @Operation(summary = "Retrieves limited meta-data about all the participants in an event series.")
@ApiResponses({ @ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"), @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") @ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
}) })
public List<Person> getSeriesParticipants(@Context SecurityContext securityContext, @QueryParam("format") String format) public List<Person> getSeriesParticipants() throws JsonProcessingException, IOException {
throws JsonProcessingException, IOException {
if (!securityContext.isUserInRole(this.eventId + "~member")) throw new SecurityException("Not permitted");
int seriesId = this.eventService.getSeriesId(this.eventId); int seriesId = this.eventService.getSeriesId(this.eventId);
Set<Long> eventIds = this.eventService.getIdsBySeriesId(seriesId);
Person principal = (Person) securityContext.getUserPrincipal();
Set<Long> eventIds = this.eventPersonService.getSeriesEventIdsAsParticipant(seriesId, principal.getId());
Set<Long> personIds = new HashSet<>(); Set<Long> personIds = new HashSet<>();
List<DataSet> persons = new LinkedList<>(); List<FlexMap> persons = new LinkedList<>();
for (long eventId : eventIds) { for (long eventId : eventIds) {
List<DataSet> tmpPersons = this.eventPersonService.getParticipants(eventId); List<FlexMap> tmpPersons = this.eventPersonService.getParticipants(eventId, false);
for (DataSet person : tmpPersons) for (FlexMap person : tmpPersons)
if (personIds.add(person.getLong("personID"))) persons.add(person); if (personIds.add(person.getLong(com.poststats.sql.Constants.PERSON_ID)))
persons.add(person);
} }
return this.toAddressBookAsJson(persons); return this.personTransformer.toModel(persons);
} }
private List<Person> toAddressBookAsJson(List<DataSet> persons) throws JsonProcessingException { private StreamingOutput toCsv(List<FlexMap> persons) {
List<Person> personsJson = new ArrayList<>(persons.size());
for (DataSet person : persons)
personsJson.add(this.personTransformer.toModel(person));
return personsJson;
}
private StreamingOutput toAddressBookAsCsv(List<DataSet> persons) throws IOException {
return new StreamingOutput() { return new StreamingOutput() {
@Override @Override
public void write(OutputStream output) throws IOException, WebApplicationException { public void write(OutputStream output) throws IOException {
PrintStream pstream = new PrintStream(output); PrintStream pstream = new PrintStream(output);
CSVPrinter personsCsvPrinter = new CSVPrinter(pstream, CSVFormat.DEFAULT); CSVPrinter personsCsvPrinter = new CSVPrinter(pstream, CSVFormat.DEFAULT);
try { try {
personsCsvPrinter.printRecord("ID", "Prefix", "Last Name", "First Name", "Suffix", "Email Address"); personsCsvPrinter.printRecord("ID", "Prefix", "Last Name", "First Name", "Suffix", "Email Address",
"Mobile Phone", "Address Lines", "City", "State", "Country", "Postal Code");
for (DataSet person : persons) { for (FlexMap person : persons) {
personsCsvPrinter.printRecord(person.getLong("personID"), person.getString("prefix"), person.getString("lname"), personsCsvPrinter.printRecord(person.getLong("personID"), person.getString("prefix"),
person.getString("fname"), person.getString("suffix"), person.getString("email")); 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(); personsCsvPrinter.flush();

View File

@@ -1,6 +1,6 @@
package com.poststats.golf.api; package com.poststats.golf.api;
import com.brianlong.sql.DataSet; import com.brianlong.util.FlexMap;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.poststats.api.Constants; import com.poststats.api.Constants;
import com.poststats.api.model.Person; import com.poststats.api.model.Person;
@@ -57,8 +57,9 @@ public class GolferApi {
@ApiResponse(responseCode = "404", description = "A golfer with the specified ID could not be found") @ApiResponse(responseCode = "404", description = "A golfer with the specified ID could not be found")
}) })
public Person get() throws JsonProcessingException { public Person get() throws JsonProcessingException {
DataSet row = this.personService.get(this.personId); FlexMap row = this.personService.get(this.personId);
if (row == null) throw new WebApplicationException("Event not found", Status.NOT_FOUND); if (row == null)
throw new WebApplicationException("Event not found", Status.NOT_FOUND);
return this.golferTransformer.toModel(row); return this.golferTransformer.toModel(row);
} }

View File

@@ -1,6 +1,6 @@
package com.poststats.golf.api; package com.poststats.golf.api;
import com.brianlong.sql.DataSet; import com.brianlong.util.FlexMap;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.poststats.api.Constants; import com.poststats.api.Constants;
import com.poststats.golf.api.model.Event; import com.poststats.golf.api.model.Event;
@@ -9,7 +9,10 @@ import com.poststats.golf.service.EventService;
import com.poststats.golf.service.SeriesService; import com.poststats.golf.service.SeriesService;
import com.poststats.golf.transformer.EventTransformer; import com.poststats.golf.transformer.EventTransformer;
import com.poststats.golf.transformer.SeriesTransformer; import com.poststats.golf.transformer.SeriesTransformer;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.Operation; 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.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
@@ -36,6 +39,13 @@ import org.slf4j.LoggerFactory;
@RequestScoped @RequestScoped
@Path("/golf/series/{seriesId}") @Path("/golf/series/{seriesId}")
@Tag(name = "Event Series API") @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 { public class SeriesApi {
private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final Logger logger = LoggerFactory.getLogger(this.getClass());
@@ -71,8 +81,9 @@ public class SeriesApi {
@ApiResponse(responseCode = "404", description = "An event series with the specified ID could not be found") @ApiResponse(responseCode = "404", description = "An event series with the specified ID could not be found")
}) })
public Series get() throws JsonProcessingException { public Series get() throws JsonProcessingException {
DataSet row = this.seriesService.get(this.seriesId); FlexMap row = this.seriesService.get(this.seriesId);
if (row == null) throw new WebApplicationException("Series not found", Status.NOT_FOUND); if (row == null)
throw new WebApplicationException("Series not found", Status.NOT_FOUND);
return this.seriesTransformer.toModel(row); return this.seriesTransformer.toModel(row);
} }
@@ -86,8 +97,9 @@ public class SeriesApi {
@ApiResponse(responseCode = "404", description = "An event series with the specified ID could not be found") @ApiResponse(responseCode = "404", description = "An event series with the specified ID could not be found")
}) })
public Set<Long> getEventIds() throws JsonProcessingException { public Set<Long> getEventIds() throws JsonProcessingException {
Set<Long> eventIds = this.eventService.getIds(this.seriesId); Set<Long> eventIds = this.eventService.getIdsBySeriesId(this.seriesId);
if (eventIds.isEmpty()) throw new WebApplicationException("Series or events not found", Status.NOT_FOUND); if (eventIds.isEmpty())
throw new WebApplicationException("Series or events not found", Status.NOT_FOUND);
return eventIds; return eventIds;
} }
@@ -103,11 +115,13 @@ public class SeriesApi {
@ApiResponse(responseCode = "404", description = "An event series with the specified ID could not be found") @ApiResponse(responseCode = "404", description = "An event series with the specified ID could not be found")
}) })
public List<Event> getEvents(@QueryParam("reverse") Boolean reverse) throws JsonProcessingException { public List<Event> getEvents(@QueryParam("reverse") Boolean reverse) throws JsonProcessingException {
Map<Long, DataSet> rows = this.eventService.get(this.seriesId, !Boolean.TRUE.equals(reverse)); Map<Long, ? extends FlexMap> rows = this.eventService.getBySeriesId(this.seriesId,
if (rows.isEmpty()) throw new WebApplicationException("Series or events not found", Status.NOT_FOUND); !Boolean.TRUE.equals(reverse));
if (rows.isEmpty())
throw new WebApplicationException("Series or events not found", Status.NOT_FOUND);
List<Event> events = new LinkedList<Event>(); List<Event> events = new LinkedList<Event>();
for (DataSet row : rows.values()) { for (FlexMap row : rows.values()) {
events.add(this.eventTransformer.toModel(row)); events.add(this.eventTransformer.toModel(row));
} }

View File

@@ -24,8 +24,8 @@ public class EventPersonSecurityContext implements SecurityContext {
@Override @Override
public boolean isUserInRole(String role) { public boolean isUserInRole(String role) {
this.logger.trace("Checking if user {} in is in role {} for event {}", this.securityContext.getUserPrincipal() this.logger.trace("Checking if user {} in is in role {} for event {}",
.getName(), role, this.eventId); this.securityContext.getUserPrincipal().getName(), role, this.eventId);
Person person = (Person) this.securityContext.getUserPrincipal(); Person person = (Person) this.securityContext.getUserPrincipal();
if (person == null) { if (person == null) {
return false; return false;

View File

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

View File

@@ -1,19 +1,32 @@
package com.poststats.golf.service; package com.poststats.golf.service;
import com.brianlong.sql.DataSet; import com.brianlong.util.FlexMap;
import java.util.Collection;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
public interface EventService { public interface EventService {
DataSet get(long eventId); /**
* 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);
DataSet getDetail(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);
Map<Long, DataSet> get(int seriesId, boolean chronological); Set<Long> getIdsBySeriesId(int seriesId);
Set<Long> getIds(int seriesId); Map<Long, ? extends FlexMap> getBySeriesId(int seriesId, boolean chronological);
int getSeriesId(long eventId); Integer getSeriesId(long eventId);
} }

View File

@@ -1,12 +1,51 @@
package com.poststats.golf.service; package com.poststats.golf.service;
import com.brianlong.sql.DataSet; import com.brianlong.util.FlexMap;
import com.poststats.golf.security.Person; import com.poststats.golf.security.Person;
import java.util.Collection;
import java.util.Map;
public interface PersonService { public interface PersonService {
Person buildUserPrincipal(com.poststats.security.Person person); /**
* 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.
*/
Person getUserPrincipal(com.poststats.security.Person person);
DataSet get(long personId); /**
* 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,11 +1,33 @@
package com.poststats.golf.service; package com.poststats.golf.service;
import com.brianlong.sql.DataSet; import com.brianlong.util.FlexMap;
import java.util.Collection;
import java.util.Map;
public interface SeriesService { public interface SeriesService {
DataSet get(int seriesId); /**
* 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);
DataSet getByEventId(long eventId); /**
* 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

@@ -0,0 +1,18 @@
package com.poststats.golf.service.db;
import com.poststats.golf.sql.GolfDataSource;
import java.sql.Connection;
public abstract class CacheableServiceDAO<T> extends com.poststats.service.db.CacheableServiceDAO<T> {
@Override
protected Connection acquireConnection() {
return GolfDataSource.getInstance().acquire(true);
}
@Override
protected void releaseConnection(Connection dbcon) {
GolfDataSource.getInstance().release(dbcon);
}
}

View File

@@ -6,7 +6,6 @@ import com.poststats.golf.service.EventFinanceService;
import com.poststats.golf.sql.GolfSQL; import com.poststats.golf.sql.GolfSQL;
import com.poststats.service.ServiceException; import com.poststats.service.ServiceException;
import com.poststats.sql.PostStatsDataSource; import com.poststats.sql.PostStatsDataSource;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.ApplicationScoped;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
@@ -15,22 +14,15 @@ import java.util.List;
@ApplicationScoped @ApplicationScoped
public class EventFinanceServiceDAO implements EventFinanceService { public class EventFinanceServiceDAO implements EventFinanceService {
@PostConstruct
public void init() {
System.out.println("EventFinanceServiceDAO init");
}
@Override @Override
public List<DataSet> getPersonsBalances(long eventId) { public List<DataSet> getPersonsBalances(long eventId) {
Connection dbcon = PostStatsDataSource.getInstance() Connection dbcon = PostStatsDataSource.getInstance().acquire(true);
.acquire(true);
try { try {
return this.queryPersonsBalances(dbcon, eventId); return this.queryPersonsBalances(dbcon, eventId);
} catch (SQLException se) { } catch (SQLException se) {
throw new ServiceException(se); throw new ServiceException(se);
} finally { } finally {
PostStatsDataSource.getInstance() PostStatsDataSource.getInstance().release(dbcon);
.release(dbcon);
} }
} }
@@ -38,9 +30,8 @@ public class EventFinanceServiceDAO implements EventFinanceService {
FlexPreparedStatement fps = new FlexPreparedStatement(dbcon, SQL_SELECT_BALANCES); FlexPreparedStatement fps = new FlexPreparedStatement(dbcon, SQL_SELECT_BALANCES);
try { try {
for (int i = 1; i <= 5; i++) for (int i = 1; i <= 5; i++)
fps.setIntegerU(1, eventId); fps.setIntegerU(i, eventId);
return fps.executeQuery() return fps.executeQuery().getAllRows();
.getAllRows();
} finally { } finally {
fps.close(); fps.close();
} }

View File

@@ -1,97 +1,136 @@
package com.poststats.golf.service.db; package com.poststats.golf.service.db;
import com.brianlong.cache.CacheRetrievalException;
import com.brianlong.sql.DataSet; import com.brianlong.sql.DataSet;
import com.brianlong.sql.FlexPreparedStatement;
import com.brianlong.util.FlexMap;
import com.poststats.golf.cache.GolferCache;
import com.poststats.golf.service.EventPersonService; import com.poststats.golf.service.EventPersonService;
import com.poststats.golf.sql.GolfSQL;
import com.poststats.service.ServiceException; import com.poststats.service.ServiceException;
import com.poststats.sql.PostStatsDataSource; import com.poststats.sql.PostStatsDataSource;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.ApplicationScoped;
import jakarta.ws.rs.WebApplicationException; import java.math.BigInteger;
import jakarta.ws.rs.core.Response.Status;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
@ApplicationScoped @ApplicationScoped
public class EventPersonServiceDAO implements EventPersonService { public class EventPersonServiceDAO implements EventPersonService {
@PostConstruct
public void init() {
System.out.println("EventFinanceServiceDAO init");
}
@Override @Override
public List<DataSet> getPeople(long eventId) { public List<FlexMap> getPeople(long eventId, boolean includeDetails) {
Connection dbcon = PostStatsDataSource.getInstance() Connection dbcon = PostStatsDataSource.getInstance().acquire(true);
.acquire(true);
try { try {
return this.queryPersons(dbcon, eventId); return this.queryPersons(dbcon, eventId, includeDetails);
} catch (CacheRetrievalException cre) {
throw new ServiceException(cre);
} catch (SQLException se) { } catch (SQLException se) {
throw new ServiceException(se); throw new ServiceException(se);
} finally { } finally {
PostStatsDataSource.getInstance() PostStatsDataSource.getInstance().release(dbcon);
.release(dbcon);
} }
} }
@Override @Override
public List<DataSet> getParticipants(long eventId) { public List<FlexMap> getParticipants(long eventId, boolean includeDetails) {
Connection dbcon = PostStatsDataSource.getInstance() Connection dbcon = PostStatsDataSource.getInstance().acquire(true);
.acquire(true);
try { try {
return this.queryParticipants(dbcon, eventId); return this.queryParticipants(dbcon, eventId, includeDetails);
} catch (CacheRetrievalException cre) {
throw new ServiceException(cre);
} catch (SQLException se) { } catch (SQLException se) {
throw new ServiceException(se); throw new ServiceException(se);
} finally { } finally {
PostStatsDataSource.getInstance() PostStatsDataSource.getInstance().release(dbcon);
.release(dbcon);
} }
} }
@Override @Override
public Set<Long> getSeriesEventIdsAsParticipant(int seriesId, long personId) { public Set<Long> getSeriesEventIdsAsParticipant(int seriesId, long personId) {
Connection dbcon = PostStatsDataSource.getInstance() Connection dbcon = PostStatsDataSource.getInstance().acquire(true);
.acquire(true);
try { try {
return this.querySeriesEventIdsAsParticipant(dbcon, seriesId, personId); return this.querySeriesEventIdsAsParticipant(dbcon, seriesId, personId);
} catch (SQLException se) { } catch (SQLException se) {
throw new ServiceException(se); throw new ServiceException(se);
} finally { } finally {
PostStatsDataSource.getInstance() PostStatsDataSource.getInstance().release(dbcon);
.release(dbcon);
} }
} }
private List<DataSet> queryPersons(Connection dbcon, long eventId) throws SQLException { private List<FlexMap> queryPersons(Connection dbcon, long eventId, boolean includeDetails)
throw new WebApplicationException(Status.NOT_IMPLEMENTED); throws CacheRetrievalException, SQLException {
/* return this.query(dbcon, SQL_SELECT_PERSONS, eventId, 2, includeDetails);
* FlexPreparedStatement fps = new FlexPreparedStatement(dbcon,
* SQL_SELECT_PERSONS); try { fps.setIntegerU(1, eventId); return
* fps.executeQuery().getAllRows(); } finally { fps.close(); }
*/
} }
private List<DataSet> queryParticipants(Connection dbcon, long eventId) throws SQLException { private List<FlexMap> queryParticipants(Connection dbcon, long eventId, boolean includeDetails)
throw new WebApplicationException(Status.NOT_IMPLEMENTED); throws CacheRetrievalException, SQLException {
/* return this.query(dbcon, SQL_SELECT_PARTICIPANTS, eventId, 1, includeDetails);
* FlexPreparedStatement fps = new FlexPreparedStatement(dbcon,
* SQL_SELECT_PARTICIPANTS); try { fps.setIntegerU(1, eventId); return
* fps.executeQuery().getAllRows(); } finally { fps.close(); }
*/
} }
private Set<Long> querySeriesEventIdsAsParticipant(Connection dbcon, int seriesId, long personId) throws SQLException { private List<FlexMap> query(Connection dbcon, String sql, long eventId, int parameterCount, boolean includeDetails)
throw new WebApplicationException(Status.NOT_IMPLEMENTED); throws CacheRetrievalException, SQLException {
/* Map<BigInteger, Long> epersonIdMap;
* FlexPreparedStatement fps = new FlexPreparedStatement(dbcon,
* SQL_SELECT_PARTICIPANTS); try { fps.setSmallintU(1, seriesId); FlexPreparedStatement fps = new FlexPreparedStatement(dbcon, sql);
* fps.setIntegerU(2, personId); try {
* for (int i = 1; i <= parameterCount; i++)
* Set<Long> eventIds = new HashSet<>(); fps.setIntegerU(i, eventId);
* fps.executeQuery().getFirstColumn(Long.class, eventIds); return eventIds; } epersonIdMap = fps.executeQuery().getFirstTwoColumns(BigInteger.class, Long.class);
* finally { fps.close(); } } finally {
*/ fps.close();
}
Map<Long, DataSet> personMap = GolferCache.getInstance().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;
} }
private Set<Long> querySeriesEventIdsAsParticipant(Connection dbcon, int seriesId, long personId)
throws SQLException {
FlexPreparedStatement fps = new FlexPreparedStatement(dbcon, SQL_SELECT_SERIES_PARTICIPANTS);
try {
fps.setSmallintU(1, seriesId);
fps.setIntegerU(2, personId);
Set<Long> set = new HashSet<>();
fps.executeQuery().getFirstColumn(Long.class, set);
return set;
} finally {
fps.close();
}
}
private static final String SQL_SELECT_PARTICIPANTS = GolfSQL.changeSchema(
"SELECT epersonID, personID FROM ~g~.EventPerson WHERE eventID=?");
private static final String SQL_SELECT_SERIES_PARTICIPANTS = GolfSQL.changeSchema(
"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 static final String SQL_SELECT_PERSONS = GolfSQL.changeSchema(
"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 ");
} }

View File

@@ -2,169 +2,142 @@ package com.poststats.golf.service.db;
import com.brianlong.sql.DataSet; import com.brianlong.sql.DataSet;
import com.brianlong.sql.FlexPreparedStatement; import com.brianlong.sql.FlexPreparedStatement;
import com.brianlong.util.FlexMap;
import com.poststats.golf.service.EventService; import com.poststats.golf.service.EventService;
import com.poststats.golf.sql.GolfDataSource; import com.poststats.golf.sql.GolfDataSource;
import com.poststats.golf.sql.GolfSQL; import com.poststats.golf.sql.GolfSQL;
import com.poststats.service.ServiceException; import com.poststats.service.ServiceException;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.ApplicationScoped;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ApplicationScoped @ApplicationScoped
public class EventServiceDAO implements EventService { public class EventServiceDAO extends CacheableServiceDAO<Long> implements EventService {
private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final long defaultCacheExpirationInSeconds = 3600;
@PostConstruct @Override
public void init() { public FlexMap get(long eventId) {
this.logger.debug("EventFinanceServiceDAO init"); return this.get(Long.valueOf(eventId));
} }
@Override @Override
public DataSet get(long eventId) { public Map<Long, ? extends FlexMap> getBySeriesId(int seriesId, boolean chronological) {
Connection dbcon = GolfDataSource.getInstance() List<Long> eventIds = new LinkedList<>();
.acquire(true);
Connection dbcon = GolfDataSource.getInstance().acquire(true);
try { try {
return this.queryEvent(dbcon, eventId, false); this.queryEventIds(dbcon, seriesId, chronological, eventIds);
} catch (SQLException se) { } catch (SQLException se) {
throw new ServiceException(se); throw new ServiceException(se);
} finally { } finally {
GolfDataSource.getInstance() GolfDataSource.getInstance().release(dbcon);
.release(dbcon);
} }
return this.get(eventIds);
} }
@Override @Override
public DataSet getDetail(long eventId) { public Set<Long> getIdsBySeriesId(int seriesId) {
Connection dbcon = GolfDataSource.getInstance() Set<Long> eventIds = new HashSet<>();
.acquire(true);
Connection dbcon = GolfDataSource.getInstance().acquire(true);
try { try {
return this.queryEvent(dbcon, eventId, true); this.queryEventIds(dbcon, seriesId, null, eventIds);
} catch (SQLException se) { } catch (SQLException se) {
throw new ServiceException(se); throw new ServiceException(se);
} finally { } finally {
GolfDataSource.getInstance() GolfDataSource.getInstance().release(dbcon);
.release(dbcon);
} }
return eventIds;
} }
@Override @Override
public Map<Long, DataSet> get(int seriesId, boolean chronological) { public Integer getSeriesId(long eventId) {
Connection dbcon = GolfDataSource.getInstance() FlexMap event = this.getIfHit(eventId);
.acquire(true); if (event != null)
try { return event.getInteger("seriesID");
return this.queryEvents(dbcon, seriesId, chronological);
} catch (SQLException se) {
throw new ServiceException(se);
} finally {
GolfDataSource.getInstance()
.release(dbcon);
}
}
@Override Connection dbcon = GolfDataSource.getInstance().acquire(true);
public Set<Long> getIds(int seriesId) {
Connection dbcon = GolfDataSource.getInstance()
.acquire(true);
try {
return this.queryEventIds(dbcon, seriesId);
} catch (SQLException se) {
throw new ServiceException(se);
} finally {
GolfDataSource.getInstance()
.release(dbcon);
}
}
@Override
public int getSeriesId(long eventId) {
Connection dbcon = GolfDataSource.getInstance()
.acquire(true);
try { try {
return this.querySeriesId(dbcon, eventId); return this.querySeriesId(dbcon, eventId);
} catch (SQLException se) { } catch (SQLException se) {
throw new ServiceException(se); throw new ServiceException(se);
} finally { } finally {
GolfDataSource.getInstance() GolfDataSource.getInstance().release(dbcon);
.release(dbcon);
} }
} }
private static final String SQL_SELECT_EVENT_BRIEF = GolfSQL.changeSchema( @Override
"SELECT E.* " protected long getCacheExpirationInSeconds() {
+ "FROM ~g~.Event E " return this.defaultCacheExpirationInSeconds;
+ "WHERE E.eventID=? "); }
private static final String SQL_SELECT_EVENT_DETAIL = GolfSQL.changeSchema(
"SELECT E.*, EF.* "
+ "FROM ~g~.Event E "
+ " INNER JOIN ~g~.EventFeature EF ON (E.eventID=EF.eventID) "
+ "WHERE E.eventID=? ");
private DataSet queryEvent(Connection dbcon, long eventId, boolean includeExtraDetail) throws SQLException { @Override
FlexPreparedStatement fps = new FlexPreparedStatement(dbcon, includeExtraDetail ? SQL_SELECT_EVENT_DETAIL : SQL_SELECT_EVENT_BRIEF); protected DataSet fetchOne(Connection dbcon, Long eventId) throws SQLException {
String sql = SQL_SELECT + "WHERE E.eventID=?";
FlexPreparedStatement fps = new FlexPreparedStatement(dbcon, sql);
try { try {
fps.setIntegerU(1, eventId); fps.setIntegerU(1, eventId);
return fps.executeQuery() return fps.executeQuery().getNextRow();
.getNextRow(); } finally {
fps.close();
}
}
@Override
protected Map<Long, DataSet> fetchBulk(Connection dbcon, Collection<Long> eventIds) throws SQLException {
String sql = SQL_SELECT + "WHERE E.eventID IN (??)";
FlexPreparedStatement fps = new FlexPreparedStatement(dbcon, sql, eventIds);
try {
return fps.executeQuery().getAllRows("eventID", Long.class);
} finally {
fps.close();
}
}
private static final String SQL_SELECT = GolfSQL.changeSchema(
"SELECT E.*, EF.* "
+ "FROM ~g~.Event E "
+ " INNER JOIN ~g~.EventFeature EF ON (E.eventID=EF.eventID) ");
private void queryEventIds(Connection dbcon, int seriesId, Boolean chronological, Collection<Long> eventIds)
throws SQLException {
String sql = SQL_SELECT_EVENTS;
if (chronological != null)
sql += chronological ? SQL_ORDER_EVENTS_ASC : SQL_ORDER_EVENTS_DESC;
FlexPreparedStatement fps = new FlexPreparedStatement(dbcon, sql);
try {
fps.setSmallintU(1, seriesId);
fps.executeQuery().getFirstColumn(Long.class, eventIds);
} finally { } finally {
fps.close(); fps.close();
} }
} }
private static final String SQL_SELECT_EVENTS = GolfSQL.changeSchema( private static final String SQL_SELECT_EVENTS = GolfSQL.changeSchema(
"SELECT E.* " "SELECT E.eventID FROM ~g~.Event E WHERE E.seriesID=? ");
+ "FROM ~g~.Event E "
+ "WHERE E.seriesID=? ");
private static final String SQL_ORDER_EVENTS_ASC = "ORDER BY E.liveline ASC"; private static final String SQL_ORDER_EVENTS_ASC = "ORDER BY E.liveline ASC";
private static final String SQL_ORDER_EVENTS_DESC = "ORDER BY E.liveline DESC"; private static final String SQL_ORDER_EVENTS_DESC = "ORDER BY E.liveline DESC";
private Map<Long, DataSet> queryEvents(Connection dbcon, int seriesId, boolean chronological) throws SQLException { private Integer querySeriesId(Connection dbcon, long eventId) throws SQLException {
FlexPreparedStatement fps = new FlexPreparedStatement(dbcon, SQL_SELECT_EVENTS + (chronological ? SQL_ORDER_EVENTS_ASC : SQL_ORDER_EVENTS_DESC)); FlexPreparedStatement fps = new FlexPreparedStatement(dbcon, SQL_SELECT_SERIES_REF);
try { try {
fps.setSmallintU(1, seriesId); fps.setIntegerU(1, eventId);
return fps.executeQuery() return fps.executeQuery().getOne(Integer.class);
.getAllRows("eventID", Long.class);
} finally { } finally {
fps.close(); fps.close();
} }
} }
private static final String SQL_SELECT_EVENT_IDS = GolfSQL.changeSchema(
"SELECT eventID FROM ~g~.Event WHERE seriesID=? ");
private Set<Long> queryEventIds(Connection dbcon, int seriesId) throws SQLException {
Set<Long> eventIds = new HashSet<>(32);
FlexPreparedStatement fps = new FlexPreparedStatement(dbcon, SQL_SELECT_EVENT_IDS);
try {
fps.setSmallintU(1, seriesId);
fps.executeQuery()
.getFirstColumn(Long.class, eventIds);
} finally {
fps.close();
}
return eventIds;
}
private static final String SQL_SELECT_SERIES_REF = GolfSQL.changeSchema( private static final String SQL_SELECT_SERIES_REF = GolfSQL.changeSchema(
"SELECT seriesID FROM ~g~.Event WHERE eventID=?"); "SELECT seriesID FROM ~g~.Event WHERE eventID=?");
private int querySeriesId(Connection dbcon, long eventId) throws SQLException {
FlexPreparedStatement fps = new FlexPreparedStatement(dbcon, SQL_SELECT_SERIES_REF);
try {
fps.setIntegerU(1, eventId);
return fps.executeQuery()
.getOne(Integer.class);
} finally {
fps.close();
}
}
} }

View File

@@ -1,82 +1,127 @@
package com.poststats.golf.service.db; 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.DataSet;
import com.brianlong.sql.FlexPreparedStatement; import com.brianlong.sql.FlexPreparedStatement;
import com.brianlong.util.FlexMap;
import com.poststats.golf.security.Person; import com.poststats.golf.security.Person;
import com.poststats.golf.service.PersonService; import com.poststats.golf.service.PersonService;
import com.poststats.golf.sql.GolfDataSource; import com.poststats.golf.sql.GolfDataSource;
import com.poststats.golf.sql.GolfSQL; import com.poststats.golf.sql.GolfSQL;
import com.poststats.service.ServiceException; import com.poststats.service.ServiceException;
import com.poststats.sql.Constants;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.ApplicationScoped;
import java.io.IOException;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ApplicationScoped @ApplicationScoped
public class PersonServiceDAO implements PersonService { public class PersonServiceDAO extends CacheableServiceDAO<Long> implements PersonService {
private final int roleCacheExpirationInSeconds = 300;
private final int infoCacheExpirationInSeconds = 120;
private ClusterAwareMemoryCacher<Long, Person> principalCacher;
@PostConstruct
private void initCache() {
this.principalCacher = new ClusterAwareMemoryCacher<Long, Person>(this.roleCacheExpirationInSeconds * 1000L,
this.clusterService.getHazelcastInstance());
}
@Override @Override
public Person buildUserPrincipal(com.poststats.security.Person person) { protected long getCacheExpirationInSeconds() {
Connection dbcon = GolfDataSource.getInstance() return this.infoCacheExpirationInSeconds;
.acquire(true); }
@Override
public Person getUserPrincipal(com.poststats.security.Person person) {
try { try {
return new Person(person, this.queryAccessControls(dbcon, person.getId()), this.queryEventAccessControls(dbcon, person.getId())); return this.principalCacher.get(person.getId(), new PrincipalCacheableFetcher() {
} catch (SQLException se) { @Override
throw new ServiceException(se); public com.poststats.security.Person getPerson() {
} finally { return person;
GolfDataSource.getInstance() }
.release(dbcon); });
} catch (CacheException ce) {
throw new ServiceException(ce);
} }
} }
@Override @Override
public DataSet get(long personId) { public FlexMap get(long personId) {
Connection dbcon = GolfDataSource.getInstance() return this.get(Long.valueOf(personId));
.acquire(true);
try {
return this.queryBasics(dbcon, personId);
} catch (SQLException se) {
throw new ServiceException(se);
} finally {
GolfDataSource.getInstance()
.release(dbcon);
}
} }
private DataSet queryBasics(Connection dbcon, long personId) throws SQLException { private abstract class PrincipalCacheableFetcher implements CacheableFetcher<Long, Person> {
FlexPreparedStatement fps = new FlexPreparedStatement(dbcon, GolfSQL.changeSchema(
"SELECT * FROM ~g~.Person WHERE personID=?")); public abstract com.poststats.security.Person getPerson();
@Override
public Person get(Long personId) throws SQLException {
Connection dbcon = GolfDataSource.getInstance().acquire(true);
try {
return new Person(this.getPerson(), queryAccessControls(dbcon, personId),
queryEventAccessControls(dbcon, personId));
} finally {
GolfDataSource.getInstance().release(dbcon);
}
}
@Override
public Map<Long, Person> get(Collection<Long> personIds) throws SQLException, IOException {
throw new UnsupportedOperationException();
}
private Set<String> queryAccessControls(Connection dbcon, long personId) throws SQLException {
FlexPreparedStatement fps = new FlexPreparedStatement(dbcon, SQL_SELECT_ACS);
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(Connection dbcon, long personId) throws SQLException {
FlexPreparedStatement fps = new FlexPreparedStatement(dbcon, SQL_SELECT_EVENT_ACS);
try {
fps.setIntegerU(1, personId);
return fps.executeQuery().getFirstTwoColumnsOneToMany(Long.class, String.class);
} finally {
fps.close();
}
}
};
@Override
protected DataSet fetchOne(Connection dbcon, Long personId) throws SQLException {
String sql = SQL_SELECT_PERSON + "WHERE personID=?";
FlexPreparedStatement fps = new FlexPreparedStatement(dbcon, sql);
try { try {
fps.setIntegerU(1, personId); fps.setIntegerU(1, personId);
return fps.executeQuery() return fps.executeQuery().getNextRow();
.getNextRow();
} finally { } finally {
fps.close(); fps.close();
} }
} }
private Set<String> queryAccessControls(Connection dbcon, long personId) throws SQLException { @Override
FlexPreparedStatement fps = new FlexPreparedStatement(dbcon, SQL_SELECT_ACS); protected Map<Long, DataSet> fetchBulk(Connection dbcon, Collection<Long> personIds) throws SQLException {
String sql = SQL_SELECT_PERSON + "WHERE personID IN (??)";
FlexPreparedStatement fps = new FlexPreparedStatement(dbcon, sql, personIds);
try { try {
fps.setIntegerU(1, personId); return fps.executeQuery().getAllRows(Constants.PERSON_ID, Long.class);
Set<String> set = new HashSet<>();
fps.executeQuery()
.getFirstColumn(String.class, set);
return set;
} finally {
fps.close();
}
}
private Map<Long, Set<String>> queryEventAccessControls(Connection dbcon, long personId) throws SQLException {
FlexPreparedStatement fps = new FlexPreparedStatement(dbcon, SQL_SELECT_EVENT_ACS);
try {
fps.setIntegerU(1, personId);
return fps.executeQuery()
.getFirstTwoColumnsOneToMany(Long.class, String.class);
} finally { } finally {
fps.close(); fps.close();
} }
@@ -84,6 +129,9 @@ public class PersonServiceDAO implements PersonService {
private static final String SQL_SELECT_PERSON = GolfSQL.changeSchema(
"SELECT P.* FROM ~g~.Person P ");
private static final String SQL_SELECT_ACS = GolfSQL.changeSchema( private static final String SQL_SELECT_ACS = GolfSQL.changeSchema(
"SELECT AC.acSID " "SELECT AC.acSID "
+ "FROM ~g~.PersonAccessControl PAC " + "FROM ~g~.PersonAccessControl PAC "

View File

@@ -2,81 +2,80 @@ package com.poststats.golf.service.db;
import com.brianlong.sql.DataSet; import com.brianlong.sql.DataSet;
import com.brianlong.sql.FlexPreparedStatement; import com.brianlong.sql.FlexPreparedStatement;
import com.brianlong.util.FlexMap;
import com.poststats.golf.service.SeriesService; import com.poststats.golf.service.SeriesService;
import com.poststats.golf.sql.GolfDataSource; import com.poststats.golf.sql.GolfDataSource;
import com.poststats.golf.sql.GolfSQL; import com.poststats.golf.sql.GolfSQL;
import com.poststats.service.ServiceException; import com.poststats.service.ServiceException;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.ApplicationScoped;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import org.slf4j.Logger; import java.util.Collection;
import org.slf4j.LoggerFactory; import java.util.Map;
@ApplicationScoped @ApplicationScoped
public class SeriesServiceDAO implements SeriesService { public class SeriesServiceDAO extends CacheableServiceDAO<Integer> implements SeriesService {
private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final long defaultCacheExpirationInSeconds = 3600;
@PostConstruct
public void init() {
this.logger.debug("SeriesServiceDAO init");
}
@Override @Override
public DataSet get(int seriesId) { public FlexMap get(int seriesId) {
Connection dbcon = GolfDataSource.getInstance() return this.get(Integer.valueOf(seriesId));
.acquire(true);
try {
return this.querySeries(dbcon, seriesId);
} catch (SQLException se) {
throw new ServiceException(se);
} finally {
GolfDataSource.getInstance()
.release(dbcon);
}
} }
@Override @Override
public DataSet getByEventId(long eventId) { public DataSet getByEventId(long eventId) {
Connection dbcon = GolfDataSource.getInstance() Connection dbcon = GolfDataSource.getInstance().acquire(true);
.acquire(true);
try { try {
return this.querySeries(dbcon, eventId); return this.querySeries(dbcon, eventId);
} catch (SQLException se) { } catch (SQLException se) {
throw new ServiceException(se); throw new ServiceException(se);
} finally { } finally {
GolfDataSource.getInstance() GolfDataSource.getInstance().release(dbcon);
.release(dbcon);
} }
} }
private static final String SQL_SELECT_SERIES = GolfSQL.changeSchema( @Override
"SELECT * FROM ~g~.Series WHERE seriesID=? "); protected long getCacheExpirationInSeconds() {
return this.defaultCacheExpirationInSeconds;
}
private DataSet querySeries(Connection dbcon, int seriesId) throws SQLException { @Override
FlexPreparedStatement fps = new FlexPreparedStatement(dbcon, SQL_SELECT_SERIES); protected DataSet fetchOne(Connection dbcon, Integer seriesId) throws SQLException {
String sql = SQL_SELECT + "WHERE seriesID=?";
FlexPreparedStatement fps = new FlexPreparedStatement(dbcon, sql);
try { try {
fps.setSmallintU(1, seriesId); fps.setSmallintU(1, seriesId);
return fps.executeQuery() return fps.executeQuery().getNextRow();
.getNextRow();
} finally { } finally {
fps.close(); fps.close();
} }
} }
private static final String SQL_SELECT_SERIES_BY_EVENT_ID = GolfSQL.changeSchema( @Override
"SELECT * FROM ~g~.Series WHERE eventID=? "); protected Map<Integer, DataSet> fetchBulk(Connection dbcon, Collection<Integer> seriesIds) throws SQLException {
String sql = SQL_SELECT + "WHERE seriesID IN (??)";
FlexPreparedStatement fps = new FlexPreparedStatement(dbcon, sql, seriesIds);
try {
return fps.executeQuery().getAllRows("seriesID", Integer.class);
} finally {
fps.close();
}
}
private DataSet querySeries(Connection dbcon, long eventId) throws SQLException { private DataSet querySeries(Connection dbcon, long eventId) throws SQLException {
FlexPreparedStatement fps = new FlexPreparedStatement(dbcon, SQL_SELECT_SERIES_BY_EVENT_ID); FlexPreparedStatement fps = new FlexPreparedStatement(dbcon, SQL_SELECT_SERIES_BY_EVENT_ID);
try { try {
fps.setIntegerU(1, eventId); fps.setIntegerU(1, eventId);
return fps.executeQuery() return fps.executeQuery().getNextRow();
.getNextRow();
} finally { } finally {
fps.close(); fps.close();
} }
} }
private static final String SQL_SELECT = GolfSQL.changeSchema(
"SELECT * FROM ~g~.Series ");
private static final String SQL_SELECT_SERIES_BY_EVENT_ID = GolfSQL.changeSchema(
"SELECT * FROM ~g~.Series WHERE eventID=? ");
} }

View File

@@ -24,21 +24,16 @@ public class EventFilter implements ContainerRequestFilter {
@Override @Override
public void filter(ContainerRequestContext requestContext) throws IOException { public void filter(ContainerRequestContext requestContext) throws IOException {
String eventIdStr = requestContext.getUriInfo() String eventIdStr = requestContext.getUriInfo().getPathParameters().getFirst(Constants.EVENT_ID);
.getPathParameters() eventIdStr = StringUtil.getInstance().trim(eventIdStr);
.getFirst(Constants.EVENT_ID);
eventIdStr = StringUtil.getInstance()
.trim(eventIdStr);
if (eventIdStr == null) { if (eventIdStr == null) {
eventIdStr = requestContext.getUriInfo() eventIdStr = requestContext.getUriInfo().getQueryParameters().getFirst(Constants.EVENT_ID);
.getQueryParameters() eventIdStr = StringUtil.getInstance().trim(eventIdStr);
.getFirst(Constants.EVENT_ID);
eventIdStr = StringUtil.getInstance()
.trim(eventIdStr);
} }
if (eventIdStr == null) return; if (eventIdStr == null)
return;
this.logger.debug("Entering event context: {}", eventIdStr); this.logger.debug("Entering event context: {}", eventIdStr);
@@ -46,7 +41,8 @@ public class EventFilter implements ContainerRequestFilter {
requestContext.setProperty(Constants.EVENT_ID, eventId); requestContext.setProperty(Constants.EVENT_ID, eventId);
SecurityContext scontext = requestContext.getSecurityContext(); SecurityContext scontext = requestContext.getSecurityContext();
if (scontext.getUserPrincipal() == null) return; if (scontext.getUserPrincipal() == null)
return;
this.logger.debug("Narrowing authorization for event: {} => {}", scontext.getUserPrincipal(), eventId); this.logger.debug("Narrowing authorization for event: {} => {}", scontext.getUserPrincipal(), eventId);
@@ -54,7 +50,8 @@ public class EventFilter implements ContainerRequestFilter {
if (this.logger.isTraceEnabled()) { if (this.logger.isTraceEnabled()) {
Person person = (Person) epscontext.getUserPrincipal(); Person person = (Person) epscontext.getUserPrincipal();
this.logger.trace("Authorized event roles: {} => {}", person.getId(), person.getEventAccessControls(eventId)); this.logger.trace("Authorized event roles: {} => {}", person.getId(),
person.getEventAccessControls(eventId));
} }
requestContext.setSecurityContext(epscontext); requestContext.setSecurityContext(epscontext);

View File

@@ -1,9 +1,5 @@
package com.poststats.golf.servlet; package com.poststats.golf.servlet;
import com.brianlong.cache.CacheException;
import com.brianlong.cache.CacheRetrievalException;
import com.brianlong.cache.MemoryCacher;
import com.brianlong.cache.Reaper;
import com.poststats.golf.service.PersonService; import com.poststats.golf.service.PersonService;
import com.poststats.security.Person; import com.poststats.security.Person;
import com.poststats.security.PersonSecurityContext; import com.poststats.security.PersonSecurityContext;
@@ -26,42 +22,26 @@ public class PersonFilter implements ContainerRequestFilter {
private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final Logger logger = LoggerFactory.getLogger(this.getClass());
private MemoryCacher<Long, SecurityContext> cacher = new MemoryCacher<>(60000L, new Reaper(30000L));
@Inject @Inject
private PersonService personService; private PersonService personService;
@Override @Override
public void filter(ContainerRequestContext requestContext) throws IOException { public void filter(ContainerRequestContext requestContext) throws IOException {
SecurityContext scontext = requestContext.getSecurityContext(); SecurityContext scontext = requestContext.getSecurityContext();
if (scontext.getUserPrincipal() == null) return; if (scontext.getUserPrincipal() == null)
return;
Person person = (Person) scontext.getUserPrincipal(); Person person = (Person) scontext.getUserPrincipal();
try {
scontext = this.cacher.get(person.getId()); this.logger.debug("Gathering roles for golf: {}", person);
} catch (CacheRetrievalException cre) {
this.logger.warn("This should never happen; if it does, skip cache", cre); com.poststats.golf.security.Person gperson = this.personService.getUserPrincipal(person);
scontext = null;
if (this.logger.isTraceEnabled()) {
this.logger.trace("Authorized roles: {} => {}", gperson.getId(), gperson.getAllAccessControls());
} }
if (scontext != null) { scontext = new PersonSecurityContext(gperson);
this.logger.debug("Using cached security context: {}", scontext.getUserPrincipal());
} else {
this.logger.debug("Gathering roles for golf: {}", person);
com.poststats.golf.security.Person gperson = this.personService.buildUserPrincipal(person);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Authorized roles: {} => {}", gperson.getId(), gperson.getAllAccessControls());
}
scontext = new PersonSecurityContext(gperson);
try {
this.cacher.add(person.getId(), scontext, false);
} catch (CacheException ce) {
this.logger.warn("This should never happen; if it does, caching failed", ce);
}
}
requestContext.setSecurityContext(scontext); requestContext.setSecurityContext(scontext);
} }

View File

@@ -21,21 +21,16 @@ public class SeriesFilter implements ContainerRequestFilter {
@Override @Override
public void filter(ContainerRequestContext requestContext) throws IOException { public void filter(ContainerRequestContext requestContext) throws IOException {
String seriesIdStr = requestContext.getUriInfo() String seriesIdStr = requestContext.getUriInfo().getPathParameters().getFirst(Constants.EVENT_SERIES_ID);
.getPathParameters() seriesIdStr = StringUtil.getInstance().trim(seriesIdStr);
.getFirst(Constants.EVENT_SERIES_ID);
seriesIdStr = StringUtil.getInstance()
.trim(seriesIdStr);
if (seriesIdStr == null) { if (seriesIdStr == null) {
seriesIdStr = requestContext.getUriInfo() seriesIdStr = requestContext.getUriInfo().getQueryParameters().getFirst(Constants.EVENT_SERIES_ID);
.getQueryParameters() seriesIdStr = StringUtil.getInstance().trim(seriesIdStr);
.getFirst(Constants.EVENT_SERIES_ID);
seriesIdStr = StringUtil.getInstance()
.trim(seriesIdStr);
} }
if (seriesIdStr == null) return; if (seriesIdStr == null)
return;
this.logger.debug("Entering series context: {}", seriesIdStr); this.logger.debug("Entering series context: {}", seriesIdStr);

View File

@@ -1,6 +1,6 @@
package com.poststats.golf.transformer; package com.poststats.golf.transformer;
import com.brianlong.sql.DataSet; import com.brianlong.util.FlexMap;
import com.poststats.golf.api.model.Event; import com.poststats.golf.api.model.Event;
import com.poststats.transformer.Transformer; import com.poststats.transformer.Transformer;
import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.ApplicationScoped;
@@ -13,25 +13,22 @@ public class EventTransformer implements Transformer<Event> {
private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override @Override
public Event toModel(DataSet row) { public Event toModel(FlexMap row) {
return this.toModel(row, new Event()); return this.toModel(row, new Event());
} }
@Override @Override
public Event toModel(DataSet row, Event event) { public Event toModel(FlexMap row, Event event) {
event.withId(row.getLong("eventID")) event.withId(row.getLong("eventID")).withName(row.getString("event")).withLiveline(row.getDate("liveline"))
.withName(row.getString("event")) .withDeadline(row.getDate("deadline")).withSeriesId(row.getInteger("seriesID"));
.withLiveline(row.getDate("liveline"))
.withDeadline(row.getDate("deadline"))
.withSeriesId(row.getInteger("seriesID"));
StringBuilder location = new StringBuilder(); StringBuilder location = new StringBuilder();
if (row.isNotEmpty("addrcountry")) location.append(' ') if (row.isNotEmpty("addrcountry"))
.append(row.getString("addrcountry")); location.append(' ').append(row.getString("addrcountry"));
if (row.isNotEmpty("addrstate")) location.insert(0, ' ') if (row.isNotEmpty("addrstate"))
.insert(1, row.getString("addrstate")); location.insert(0, ' ').insert(1, row.getString("addrstate"));
if (row.isNotEmpty("addrcity")) location.insert(0, ',') if (row.isNotEmpty("addrcity"))
.insert(0, row.getString("addrcity")); location.insert(0, ',').insert(0, row.getString("addrcity"));
this.logger.debug("Formulated event {} location: {}", row.getLong("eventID"), location); this.logger.debug("Formulated event {} location: {}", row.getLong("eventID"), location);
event.setLocation(location.toString()); event.setLocation(location.toString());

View File

@@ -1,26 +1,21 @@
package com.poststats.golf.transformer; package com.poststats.golf.transformer;
import com.brianlong.sql.DataSet; import com.brianlong.util.FlexMap;
import com.poststats.api.model.Person; import com.poststats.api.model.Person;
import com.poststats.transformer.Transformer; import com.poststats.transformer.Transformer;
import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.ApplicationScoped;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ApplicationScoped @ApplicationScoped
public class GolferTransformer implements Transformer<Person> { public class GolferTransformer implements Transformer<Person> {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override @Override
public Person toModel(DataSet row) { public Person toModel(FlexMap row) {
return this.toModel(row, new Person()); return this.toModel(row, new Person());
} }
@Override @Override
public Person toModel(DataSet row, Person person) { public Person toModel(FlexMap row, Person person) {
person.withId(row.getLong("personID")) person.withId(row.getLong("personID")).withFirstName(row.getString("fname"))
.withFirstName(row.getString("fname"))
.withLastName(row.getString("lname")); .withLastName(row.getString("lname"));
return person; return person;

View File

@@ -1,26 +1,21 @@
package com.poststats.golf.transformer; package com.poststats.golf.transformer;
import com.brianlong.sql.DataSet; import com.brianlong.util.FlexMap;
import com.poststats.golf.api.model.Series; import com.poststats.golf.api.model.Series;
import com.poststats.transformer.Transformer; import com.poststats.transformer.Transformer;
import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.ApplicationScoped;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ApplicationScoped @ApplicationScoped
public class SeriesTransformer implements Transformer<Series> { public class SeriesTransformer implements Transformer<Series> {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override @Override
public Series toModel(DataSet row) { public Series toModel(FlexMap row) {
return this.toModel(row, new Series()); return this.toModel(row, new Series());
} }
@Override @Override
public Series toModel(DataSet row, Series series) { public Series toModel(FlexMap row, Series series) {
series.withId(row.getInteger("seriesID")) series.withId(row.getInteger("seriesID")).withName(row.getString("series"));
.withName(row.getString("series"));
return series; return series;
} }