Added starter model.
This commit is contained in:
parent
7b6610a01a
commit
4f7f17f6b0
|
@ -30,3 +30,5 @@ build/
|
|||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
.sample_data
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package nl.andrewlalis.gymboard_api.controller;
|
||||
|
||||
import nl.andrewlalis.gymboard_api.controller.dto.GymResponse;
|
||||
import nl.andrewlalis.gymboard_api.service.GymService;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* Controller for accessing a particular gym.
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(path = "/gyms/{countryCode}/{cityCode}/{gymName}")
|
||||
public class GymController {
|
||||
private final GymService gymService;
|
||||
|
||||
public GymController(GymService gymService) {
|
||||
this.gymService = gymService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public GymResponse getGym(
|
||||
@PathVariable String countryCode,
|
||||
@PathVariable String cityCode,
|
||||
@PathVariable String gymName
|
||||
) {
|
||||
return gymService.getGym(countryCode, cityCode, gymName);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package nl.andrewlalis.gymboard_api.controller.dto;
|
||||
|
||||
import nl.andrewlalis.gymboard_api.model.Gym;
|
||||
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
public record GymResponse (
|
||||
String countryCode,
|
||||
String countryName,
|
||||
String cityShortName,
|
||||
String cityName,
|
||||
String createdAt,
|
||||
String displayName,
|
||||
String websiteUrl,
|
||||
double locationLatitude,
|
||||
double locationLongitude,
|
||||
String streetAddress
|
||||
) {
|
||||
public GymResponse(Gym gym) {
|
||||
this(
|
||||
gym.getCity().getCountry().getCode(),
|
||||
gym.getCity().getCountry().getName(),
|
||||
gym.getCity().getShortName(),
|
||||
gym.getCity().getName(),
|
||||
gym.getCreatedAt().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME),
|
||||
gym.getDisplayName(),
|
||||
gym.getWebsiteUrl(),
|
||||
gym.getLocation().getLatitude().doubleValue(),
|
||||
gym.getLocation().getLongitude().doubleValue(),
|
||||
gym.getStreetAddress()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package nl.andrewlalis.gymboard_api.controller.dto;
|
||||
|
||||
public record RawGymId(String countryCode, String cityCode, String gymName) {}
|
|
@ -0,0 +1,10 @@
|
|||
package nl.andrewlalis.gymboard_api.dao;
|
||||
|
||||
import nl.andrewlalis.gymboard_api.model.City;
|
||||
import nl.andrewlalis.gymboard_api.model.CityId;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface CityRepository extends JpaRepository<City, CityId> {
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package nl.andrewlalis.gymboard_api.dao;
|
||||
|
||||
import nl.andrewlalis.gymboard_api.model.Country;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface CountryRepository extends JpaRepository<Country, String> {
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package nl.andrewlalis.gymboard_api.dao;
|
||||
|
||||
import nl.andrewlalis.gymboard_api.model.Gym;
|
||||
import nl.andrewlalis.gymboard_api.model.GymId;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
public interface GymRepository extends JpaRepository<Gym, GymId> {
|
||||
@Query("SELECT g FROM Gym g " +
|
||||
"WHERE g.id.shortName = :gym AND " +
|
||||
"g.id.city.id.shortName = :city AND " +
|
||||
"g.id.city.id.country.code = :country")
|
||||
Optional<Gym> findByRawId(String gym, String city, String country);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package nl.andrewlalis.gymboard_api.dao.exercise;
|
||||
|
||||
import nl.andrewlalis.gymboard_api.model.exercise.Exercise;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface ExerciseRepository extends JpaRepository<Exercise, String> {
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package nl.andrewlalis.gymboard_api.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
@Entity
|
||||
@Table(name = "city")
|
||||
public class City {
|
||||
@EmbeddedId
|
||||
private CityId id;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String name;
|
||||
|
||||
public City() {}
|
||||
|
||||
public City(String shortName, String name, Country country) {
|
||||
this.id = new CityId(shortName, country);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getShortName() {
|
||||
return id.getShortName();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Country getCountry() {
|
||||
return id.getCountry();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package nl.andrewlalis.gymboard_api.model;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
@Embeddable
|
||||
public class CityId implements Serializable {
|
||||
@Column(nullable = false, length = 127)
|
||||
private String shortName;
|
||||
|
||||
@ManyToOne(optional = false, fetch = FetchType.LAZY)
|
||||
private Country country;
|
||||
|
||||
public CityId() {}
|
||||
|
||||
public CityId(String shortName, Country country) {
|
||||
this.shortName = shortName;
|
||||
this.country = country;
|
||||
}
|
||||
|
||||
public String getShortName() {
|
||||
return shortName;
|
||||
}
|
||||
|
||||
public Country getCountry() {
|
||||
return country;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
CityId cityId = (CityId) o;
|
||||
return shortName.equals(cityId.shortName) && country.equals(cityId.country);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(shortName, country);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package nl.andrewlalis.gymboard_api.model;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "country")
|
||||
public class Country {
|
||||
@Id
|
||||
@Column(nullable = false, length = 2, unique = true)
|
||||
private String code;
|
||||
|
||||
@Column(nullable = false, unique = true)
|
||||
private String name;
|
||||
|
||||
public Country() {}
|
||||
|
||||
public Country(String code, String name) {
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package nl.andrewlalis.gymboard_api.model;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Objects;
|
||||
|
||||
@Embeddable
|
||||
public class GeoPoint implements Serializable {
|
||||
@Column(nullable = false, precision = 9, scale = 6)
|
||||
private BigDecimal latitude;
|
||||
@Column(nullable = false, precision = 9, scale = 6)
|
||||
private BigDecimal longitude;
|
||||
|
||||
public GeoPoint() {}
|
||||
|
||||
public GeoPoint(BigDecimal latitude, BigDecimal longitude) {
|
||||
this.latitude = latitude;
|
||||
this.longitude = longitude;
|
||||
}
|
||||
|
||||
public BigDecimal getLatitude() {
|
||||
return latitude;
|
||||
}
|
||||
|
||||
public BigDecimal getLongitude() {
|
||||
return longitude;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
GeoPoint geoPoint = (GeoPoint) o;
|
||||
return geoPoint.latitude.equals(this.latitude) &&
|
||||
geoPoint.longitude.equals(this.longitude);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(latitude, longitude);
|
||||
}
|
||||
}
|
|
@ -1,12 +1,13 @@
|
|||
package nl.andrewlalis.gymboard_api.model;
|
||||
|
||||
import jakarta.persistence.EmbeddedId;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.persistence.*;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* Represents a single gym location, at which local leaderboards are held.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "gym")
|
||||
public class Gym {
|
||||
|
@ -16,23 +17,57 @@ public class Gym {
|
|||
@CreationTimestamp
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Column(nullable = false, length = 127)
|
||||
private String displayName;
|
||||
|
||||
@Column(length = 1024)
|
||||
private String websiteUrl;
|
||||
|
||||
@Embedded
|
||||
private GeoPoint location;
|
||||
|
||||
@Column(nullable = false, length = 1024)
|
||||
private String streetAddress;
|
||||
|
||||
public Gym() {}
|
||||
|
||||
public Gym(City city, String shortName, String displayName, String websiteUrl, GeoPoint location, String streetAddress) {
|
||||
this.id = new GymId(shortName, city);
|
||||
this.displayName = displayName;
|
||||
this.websiteUrl = websiteUrl;
|
||||
this.location = location;
|
||||
this.streetAddress = streetAddress;
|
||||
}
|
||||
|
||||
public GymId getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return id.getName();
|
||||
public City getCity() {
|
||||
return id.getCity();
|
||||
}
|
||||
|
||||
public String getCityId() {
|
||||
return id.getCityId();
|
||||
}
|
||||
|
||||
public String getCountryId() {
|
||||
return id.getCountryId();
|
||||
public String getShortName() {
|
||||
return id.getShortName();
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public String getWebsiteUrl() {
|
||||
return websiteUrl;
|
||||
}
|
||||
|
||||
public GeoPoint getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public String getStreetAddress() {
|
||||
return streetAddress;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ package nl.andrewlalis.gymboard_api.model;
|
|||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
@ -12,45 +14,35 @@ import java.util.Objects;
|
|||
@Embeddable
|
||||
public class GymId implements Serializable {
|
||||
@Column(nullable = false, length = 127)
|
||||
private String name;
|
||||
@Column(nullable = false, length = 127)
|
||||
private String cityId;
|
||||
@Column(nullable = false, length = 2)
|
||||
private String countryId;
|
||||
private String shortName;
|
||||
|
||||
@ManyToOne(optional = false, fetch = FetchType.LAZY)
|
||||
private City city;
|
||||
|
||||
public GymId() {}
|
||||
|
||||
public GymId(String name, String cityId, String countryId) {
|
||||
this.name = name;
|
||||
this.cityId = cityId;
|
||||
this.countryId = countryId;
|
||||
public GymId(String shortName, City city) {
|
||||
this.shortName = shortName;
|
||||
this.city = city;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
public String getShortName() {
|
||||
return shortName;
|
||||
}
|
||||
|
||||
public String getCityId() {
|
||||
return cityId;
|
||||
}
|
||||
|
||||
public String getCountryId() {
|
||||
return countryId;
|
||||
public City getCity() {
|
||||
return city;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o instanceof GymId gymId) {
|
||||
return getName().equals(gymId.getName()) &&
|
||||
getCityId().equals(gymId.getCityId()) &&
|
||||
getCountryId().equals(gymId.getCountryId());
|
||||
}
|
||||
return false;
|
||||
if (!(o instanceof GymId gymId)) return false;
|
||||
return getShortName().equals(gymId.getShortName()) && getCity().equals(gymId.getCity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getName(), getCityId(), getCountryId());
|
||||
return Objects.hash(getShortName(), getCity());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
package nl.andrewlalis.gymboard_api.model;
|
||||
|
||||
import nl.andrewlalis.gymboard_api.dao.CityRepository;
|
||||
import nl.andrewlalis.gymboard_api.dao.CountryRepository;
|
||||
import nl.andrewlalis.gymboard_api.dao.GymRepository;
|
||||
import nl.andrewlalis.gymboard_api.dao.exercise.ExerciseRepository;
|
||||
import nl.andrewlalis.gymboard_api.model.exercise.Exercise;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
@Component
|
||||
public class SampleDataLoader implements ApplicationListener<ContextRefreshedEvent> {
|
||||
private static final Logger log = LoggerFactory.getLogger(SampleDataLoader.class);
|
||||
private final CountryRepository countryRepository;
|
||||
private final CityRepository cityRepository;
|
||||
private final GymRepository gymRepository;
|
||||
private final ExerciseRepository exerciseRepository;
|
||||
|
||||
public SampleDataLoader(
|
||||
CountryRepository countryRepository,
|
||||
CityRepository cityRepository,
|
||||
GymRepository gymRepository,
|
||||
ExerciseRepository exerciseRepository
|
||||
) {
|
||||
this.countryRepository = countryRepository;
|
||||
this.cityRepository = cityRepository;
|
||||
this.gymRepository = gymRepository;
|
||||
this.exerciseRepository = exerciseRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ContextRefreshedEvent event) {
|
||||
Path markerFile = Path.of(".sample_data");
|
||||
if (Files.exists(markerFile)) return;
|
||||
|
||||
log.info("Generating sample data.");
|
||||
generateSampleData();
|
||||
try {
|
||||
Files.writeString(markerFile, "Yes");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
protected void generateSampleData() {
|
||||
Exercise benchPress = exerciseRepository.save(new Exercise("barbell-bench-press", "Barbell Bench Press"));
|
||||
Exercise squat = exerciseRepository.save(new Exercise("barbell-squat", "Barbell Squat"));
|
||||
Exercise deadlift = exerciseRepository.save(new Exercise("deadlift", "Deadlift"));
|
||||
|
||||
Country nl = countryRepository.save(new Country("nl", "Netherlands"));
|
||||
City groningen = cityRepository.save(new City("groningen", "Groningen", nl));
|
||||
Gym g1 = gymRepository.save(new Gym(
|
||||
groningen,
|
||||
"trainmore-munnekeholm",
|
||||
"Trainmore Munnekeholm",
|
||||
"https://trainmore.nl/clubs/munnekeholm/",
|
||||
new GeoPoint(new BigDecimal("53.215939"), new BigDecimal("6.561549")),
|
||||
"Munnekeholm 1, 9711 JA Groningen"
|
||||
));
|
||||
Gym g2 = gymRepository.save(new Gym(
|
||||
groningen,
|
||||
"trainmore-oude-ebbinge",
|
||||
"Trainmore Oude Ebbinge Non-Stop",
|
||||
"https://trainmore.nl/clubs/oude-ebbinge/",
|
||||
new GeoPoint(new BigDecimal("53.220900"), new BigDecimal("6.565976")),
|
||||
"Oude Ebbingestraat 54-58, 9712 HL Groningen"
|
||||
));
|
||||
|
||||
|
||||
Country us = countryRepository.save(new Country("us", "United States"));
|
||||
City tampa = cityRepository.save(new City("tampa", "Tampa", us));
|
||||
Gym g3 = gymRepository.save(new Gym(
|
||||
tampa,
|
||||
"powerhouse-gym",
|
||||
"Powerhouse Gym Athletic Club",
|
||||
"http://www.pgathleticclub.com/",
|
||||
new GeoPoint(new BigDecimal("27.997223"), new BigDecimal("-82.496237")),
|
||||
"3251-A W Hillsborough Ave, Tampa, FL 33614, United States"
|
||||
));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package nl.andrewlalis.gymboard_api.model.exercise;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
/**
|
||||
* An exercise for which attempts can be submitted, and lifts are tracked.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "exercise")
|
||||
public class Exercise {
|
||||
@Id
|
||||
@Column(nullable = false, length = 127)
|
||||
private String shortname;
|
||||
|
||||
@Column(nullable = false, unique = true)
|
||||
private String name;
|
||||
|
||||
public Exercise() {}
|
||||
|
||||
public Exercise(String shortname, String name) {
|
||||
this.shortname = shortname;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getShortname() {
|
||||
return shortname;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
|
@ -1,12 +1,14 @@
|
|||
package nl.andrewlalis.gymboard_api.model;
|
||||
package nl.andrewlalis.gymboard_api.model.exercise;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import nl.andrewlalis.gymboard_api.model.Gym;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Entity
|
||||
@Table(name = "gym_exercise_submission")
|
||||
@Table(name = "exercise_submission")
|
||||
public class ExerciseSubmission {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
|
@ -15,11 +17,18 @@ public class ExerciseSubmission {
|
|||
@CreationTimestamp
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Column(nullable = false, updatable = false, length = 63)
|
||||
private String submitterName;
|
||||
|
||||
@ManyToOne(optional = false, fetch = FetchType.LAZY)
|
||||
private Gym gym;
|
||||
|
||||
@ManyToOne(optional = false, fetch = FetchType.LAZY)
|
||||
private Exercise exercise;
|
||||
|
||||
@Column(nullable = false, updatable = false, length = 63)
|
||||
private String submitterName;
|
||||
|
||||
@Column(nullable = false)
|
||||
private boolean verified;
|
||||
|
||||
@Column(nullable = false, precision = 7, scale = 2)
|
||||
private BigDecimal weight;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package nl.andrewlalis.gymboard_api.service;
|
||||
|
||||
import nl.andrewlalis.gymboard_api.controller.dto.GymResponse;
|
||||
import nl.andrewlalis.gymboard_api.dao.GymRepository;
|
||||
import nl.andrewlalis.gymboard_api.model.Gym;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
@Service
|
||||
public class GymService {
|
||||
private final GymRepository gymRepository;
|
||||
|
||||
public GymService(GymRepository gymRepository) {
|
||||
this.gymRepository = gymRepository;
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public GymResponse getGym(String countryCode, String city, String gymName) {
|
||||
Gym gym = gymRepository.findByRawId(gymName, city, countryCode)
|
||||
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
||||
return new GymResponse(gym);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue