From 181aabaac8b61caf64fcb9e7589eec1c55d8b00d Mon Sep 17 00:00:00 2001 From: Andrew Lalis Date: Sat, 21 Jan 2023 14:55:10 +0100 Subject: [PATCH] Added working gym api. --- .../gymboard_api/config/WebConfig.java | 26 +++++++++++ .../controller/dto/GeoPointResponse.java | 15 +++++++ .../controller/dto/GymResponse.java | 8 ++-- .../src/main/resources/application.properties | 2 + gymboard-app/quasar.config.js | 7 ++- gymboard-app/src/api/gymboard-api.ts | 40 +++++++++++++++++ gymboard-app/src/boot/axios.ts | 14 ------ gymboard-app/src/pages/GymPage.vue | 43 +++++++++++++++---- gymboard-app/src/router/routes.ts | 2 +- 9 files changed, 127 insertions(+), 30 deletions(-) create mode 100644 gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/config/WebConfig.java create mode 100644 gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/controller/dto/GeoPointResponse.java create mode 100644 gymboard-app/src/api/gymboard-api.ts diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/config/WebConfig.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/config/WebConfig.java new file mode 100644 index 0000000..6563483 --- /dev/null +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/config/WebConfig.java @@ -0,0 +1,26 @@ +package nl.andrewlalis.gymboard_api.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; + +import java.util.Arrays; + +@Configuration +public class WebConfig { + + @Bean + public CorsFilter corsFilter() { + final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + final CorsConfiguration config = new CorsConfiguration(); + config.setAllowCredentials(true); + // Don't do this in production, use a proper list of allowed origins + config.addAllowedOriginPattern("*"); + config.setAllowedHeaders(Arrays.asList("Origin", "Content-Type", "Accept")); + config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "OPTIONS", "DELETE", "PATCH")); + source.registerCorsConfiguration("/**", config); + return new CorsFilter(source); + } +} diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/controller/dto/GeoPointResponse.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/controller/dto/GeoPointResponse.java new file mode 100644 index 0000000..0f3348c --- /dev/null +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/controller/dto/GeoPointResponse.java @@ -0,0 +1,15 @@ +package nl.andrewlalis.gymboard_api.controller.dto; + +import nl.andrewlalis.gymboard_api.model.GeoPoint; + +public record GeoPointResponse( + double latitude, + double longitude +) { + public GeoPointResponse(GeoPoint p) { + this( + p.getLatitude().doubleValue(), + p.getLongitude().doubleValue() + ); + } +} diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/controller/dto/GymResponse.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/controller/dto/GymResponse.java index f16cb24..a11c25d 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/controller/dto/GymResponse.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/controller/dto/GymResponse.java @@ -10,10 +10,10 @@ public record GymResponse ( String cityShortName, String cityName, String createdAt, + String shortName, String displayName, String websiteUrl, - double locationLatitude, - double locationLongitude, + GeoPointResponse location, String streetAddress ) { public GymResponse(Gym gym) { @@ -23,10 +23,10 @@ public record GymResponse ( gym.getCity().getShortName(), gym.getCity().getName(), gym.getCreatedAt().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME), + gym.getShortName(), gym.getDisplayName(), gym.getWebsiteUrl(), - gym.getLocation().getLatitude().doubleValue(), - gym.getLocation().getLongitude().doubleValue(), + new GeoPointResponse(gym.getLocation()), gym.getStreetAddress() ); } diff --git a/gymboard-api/src/main/resources/application.properties b/gymboard-api/src/main/resources/application.properties index 91d36ec..06653f8 100644 --- a/gymboard-api/src/main/resources/application.properties +++ b/gymboard-api/src/main/resources/application.properties @@ -1 +1,3 @@ spring.jpa.open-in-view=false + +spring.servlet.multipart.enabled=false diff --git a/gymboard-app/quasar.config.js b/gymboard-app/quasar.config.js index 68f4419..59ee271 100644 --- a/gymboard-app/quasar.config.js +++ b/gymboard-app/quasar.config.js @@ -11,8 +11,9 @@ const { configure } = require('quasar/wrappers'); const path = require('path'); +const { withCtx } = require('vue'); -module.exports = configure(function (/* ctx */) { +module.exports = configure(function (ctx) { return { eslint: { // fix: true, @@ -69,7 +70,9 @@ module.exports = configure(function (/* ctx */) { // publicPath: '/', // analyze: true, - // env: {}, + env: { + API: ctx.dev ? 'http://localhost:8080' : 'https://api.gymboard.com' + }, // rawDefine: {} // ignorePublicFolder: true, // minify: false, diff --git a/gymboard-app/src/api/gymboard-api.ts b/gymboard-app/src/api/gymboard-api.ts new file mode 100644 index 0000000..eda8572 --- /dev/null +++ b/gymboard-app/src/api/gymboard-api.ts @@ -0,0 +1,40 @@ +import axios from "axios"; +import process from "process"; + +const api = axios.create({ + baseURL: 'http://localhost:8080' +}); + +export type Gym = { + countryCode: string, + countryName: string, + cityShortName: string, + cityName: string, + createdAt: Date, + shortName: string, + displayName: string, + websiteUrl: string | null, + location: { + latitude: number, + longitude: number + }, + streetAddress: string +}; + +export async function getGym(countryCode: string, cityShortName: string, gymShortName: string): Promise { + const response = await api.get(`/gyms/${countryCode}/${cityShortName}/${gymShortName}`); + const d = response.data; + const gym: Gym = { + countryCode: d.countryCode, + countryName: d.countryName, + cityShortName: d.cityShortName, + cityName: d.cityName, + createdAt: new Date(d.createdAt), + shortName: d.shortName, + displayName: d.displayName, + websiteUrl: d.websiteUrl, + location: d.location, + streetAddress: d.streetAddress + }; + return gym; +} \ No newline at end of file diff --git a/gymboard-app/src/boot/axios.ts b/gymboard-app/src/boot/axios.ts index 408a99d..46f3fb0 100644 --- a/gymboard-app/src/boot/axios.ts +++ b/gymboard-app/src/boot/axios.ts @@ -7,24 +7,10 @@ declare module '@vue/runtime-core' { } } -// Be careful when using SSR for cross-request state pollution -// due to creating a Singleton instance here; -// If any client changes this (global) instance, it might be a -// good idea to move this instance creation inside of the -// "export default () => {}" function below (which runs individually -// for each client) -const api = axios.create({ baseURL: 'https://api.example.com' }); - export default boot(({ app }) => { // for use inside Vue files (Options API) through this.$axios and this.$api app.config.globalProperties.$axios = axios; // ^ ^ ^ this will allow you to use this.$axios (for Vue Options API form) // so you won't necessarily have to import axios in each vue file - - app.config.globalProperties.$api = api; - // ^ ^ ^ this will allow you to use this.$api (for Vue Options API form) - // so you can easily perform requests against your app's API }); - -export { api }; diff --git a/gymboard-app/src/pages/GymPage.vue b/gymboard-app/src/pages/GymPage.vue index b838230..4e60860 100644 --- a/gymboard-app/src/pages/GymPage.vue +++ b/gymboard-app/src/pages/GymPage.vue @@ -1,17 +1,42 @@ \ No newline at end of file diff --git a/gymboard-app/src/router/routes.ts b/gymboard-app/src/router/routes.ts index cb445ff..0e2f990 100644 --- a/gymboard-app/src/router/routes.ts +++ b/gymboard-app/src/router/routes.ts @@ -13,7 +13,7 @@ const routes: RouteRecordRaw[] = [ component: IndexPage }, { - path: 'g/:countryId/:cityId/:gymName', + path: 'g/:countryCode/:cityShortName/:gymShortName', component: GymPage } ],