diff --git a/db/docker-compose.yml b/db/docker-compose.yml index 1fe002c..e2c9b7d 100644 --- a/db/docker-compose.yml +++ b/db/docker-compose.yml @@ -13,4 +13,18 @@ services: ports: - '4306:3306' volumes: - - ./mysql:/var/lib/mysql \ No newline at end of file + - ./mysql:/var/lib/mysql + + test-database: + container_name: test_database_myticket + image: mysql:8.0 + command: --default-authentication-plugin=mysql_native_password --log_bin_trust_function_creators=1 + environment: + MYSQL_ROOT_PASSWORD: test + MYSQL_DATABASE: test + MYSQL_USER: test + MYSQL_PASSWORD: test + ports: + - '5306:3306' + volumes: + - ./testmysql:/var/lib/mysql diff --git a/src/main/java/me/jweissen/aeticket/controller/CartController.java b/src/main/java/me/jweissen/aeticket/controller/CartController.java index 1c52067..f892119 100644 --- a/src/main/java/me/jweissen/aeticket/controller/CartController.java +++ b/src/main/java/me/jweissen/aeticket/controller/CartController.java @@ -1,5 +1,11 @@ package me.jweissen.aeticket.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import me.jweissen.aeticket.aspect.UserOnly; import me.jweissen.aeticket.dto.request.CartAddRequestDto; import me.jweissen.aeticket.dto.response.CartEventResponseDto; @@ -23,10 +29,27 @@ public class CartController { this.authService = authService; } + + @Operation( + summary = "Add tickets to your cart" + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "Success" + ), + @ApiResponse( + responseCode = "400", + description = "You either provided non-existent category ids or wanted more tickets than available, or your request body was malformed/insufficient." + ), + @ApiResponse( + responseCode = "401", + description = "You didn't provide proper authentication via a bearer token" + ) + }) @PostMapping("/add") @UserOnly public ResponseEntity addEntry(@RequestBody CartAddRequestDto dto) { - System.out.println(authService.getCurrentUser()); if (!cartService.add(dto, authService.getCurrentUser().getCurrentCart())) { // user gave invalid category id(s) or wanted more tickets than available return ResponseEntity.status(HttpStatus.BAD_REQUEST).build(); @@ -34,12 +57,41 @@ public class CartController { return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); } + + @Operation( + summary = "View your cart" + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "Success", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = CartEventResponseDto.class))) + ), + @ApiResponse( + responseCode = "401", + description = "You didn't provide proper authentication via a bearer token" + ) + }) @GetMapping("/list") @UserOnly public ResponseEntity> getCartEntries() { return new ResponseEntity<>(cartService.toDto(authService.getCurrentUser().getCurrentCart()), HttpStatus.OK); } + @Operation( + summary = "Checkout your cart and view the price" + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "Success", + content = @Content(schema = @Schema(implementation = CheckoutResponseDto.class)) + ), + @ApiResponse( + responseCode = "401", + description = "You didn't provide proper authentication via a bearer token" + ) + }) @GetMapping("/checkout") @UserOnly public ResponseEntity checkout() { diff --git a/src/main/java/me/jweissen/aeticket/controller/CategoryController.java b/src/main/java/me/jweissen/aeticket/controller/CategoryController.java index 0d18b9d..9385e2a 100644 --- a/src/main/java/me/jweissen/aeticket/controller/CategoryController.java +++ b/src/main/java/me/jweissen/aeticket/controller/CategoryController.java @@ -1,5 +1,11 @@ package me.jweissen.aeticket.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import me.jweissen.aeticket.aspect.AdminOnly; import me.jweissen.aeticket.aspect.UserOnly; import me.jweissen.aeticket.dto.request.CategoryRequestDto; @@ -21,6 +27,27 @@ public class CategoryController { this.categoryService = categoryService; } + @Operation( + summary = "Create a new category" + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "201", + description = "Success" + ), + @ApiResponse( + responseCode = "400", + description = "Your request body was malformed/insufficient." + ), + @ApiResponse( + responseCode = "401", + description = "You didn't provide proper authentication via a bearer token" + ), + @ApiResponse( + responseCode = "403", + description = "You're not authorized to perform this operation." + ), + }) @PostMapping("/create") @AdminOnly public ResponseEntity create(@RequestBody CategoryRequestDto dto) { @@ -28,6 +55,27 @@ public class CategoryController { return ResponseEntity.status(HttpStatus.CREATED).build(); } + @Operation( + summary = "Update a category" + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "204", + description = "Success" + ), + @ApiResponse( + responseCode = "400", + description = "Your request body was malformed/insufficient." + ), + @ApiResponse( + responseCode = "401", + description = "You didn't provide proper authentication via a bearer token" + ), + @ApiResponse( + responseCode = "403", + description = "You're not authorized to perform this operation." + ), + }) @PutMapping("/update") @AdminOnly public ResponseEntity update(@RequestBody CategoryUpdateRequestDto dto) { @@ -37,6 +85,23 @@ public class CategoryController { return ResponseEntity.status(HttpStatus.CREATED).build(); } + @Operation( + summary = "Delete a category" + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "204", + description = "Success" + ), + @ApiResponse( + responseCode = "401", + description = "You didn't provide proper authentication via a bearer token" + ), + @ApiResponse( + responseCode = "403", + description = "You're not authorized to perform this operation." + ), + }) @DeleteMapping("/delete/{id}") @AdminOnly public ResponseEntity delete(@PathVariable Long id) { @@ -44,6 +109,25 @@ public class CategoryController { return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); } + + @Operation( + summary = "Load a category" + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "Success", + content = @Content(schema = @Schema(implementation = CategoryResponseDto.class)) + ), + @ApiResponse( + responseCode = "401", + description = "You didn't provide proper authentication via a bearer token" + ), + @ApiResponse( + responseCode = "404", + description = "No category found with the given id" + ), + }) @GetMapping("/{id}") @UserOnly public ResponseEntity getById(@PathVariable Long id) { @@ -52,6 +136,20 @@ public class CategoryController { .orElseGet(() -> ResponseEntity.status(HttpStatus.NOT_FOUND).build()); } + @Operation( + summary = "List all categories" + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "Success", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = CategoryResponseDto.class))) + ), + @ApiResponse( + responseCode = "401", + description = "You didn't provide proper authentication via a bearer token" + ), + }) @GetMapping("/list") @UserOnly public ResponseEntity> getAll() { diff --git a/src/main/java/me/jweissen/aeticket/controller/EventController.java b/src/main/java/me/jweissen/aeticket/controller/EventController.java index 34bdfa7..aa652a2 100644 --- a/src/main/java/me/jweissen/aeticket/controller/EventController.java +++ b/src/main/java/me/jweissen/aeticket/controller/EventController.java @@ -1,5 +1,11 @@ package me.jweissen.aeticket.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import me.jweissen.aeticket.aspect.AdminOnly; import me.jweissen.aeticket.aspect.UserOnly; import me.jweissen.aeticket.dto.request.EventRequestDto; @@ -21,6 +27,27 @@ public class EventController { this.eventService = eventService; } + @Operation( + summary = "Create a new event" + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "201", + description = "Success" + ), + @ApiResponse( + responseCode = "400", + description = "Your request body was malformed/insufficient." + ), + @ApiResponse( + responseCode = "401", + description = "You didn't provide proper authentication via a bearer token" + ), + @ApiResponse( + responseCode = "403", + description = "You're not authorized to perform this operation." + ), + }) @PostMapping("/create") @AdminOnly public ResponseEntity create(@RequestBody EventRequestDto event) { @@ -28,6 +55,27 @@ public class EventController { return ResponseEntity.status(HttpStatus.CREATED).build(); } + @Operation( + summary = "Update an event" + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "204", + description = "Success" + ), + @ApiResponse( + responseCode = "400", + description = "Your request body was malformed/insufficient." + ), + @ApiResponse( + responseCode = "401", + description = "You didn't provide proper authentication via a bearer token" + ), + @ApiResponse( + responseCode = "403", + description = "You're not authorized to perform this operation." + ), + }) @PutMapping("/update") @AdminOnly public ResponseEntity update(@RequestBody EventUpdateRequestDto event) { @@ -35,6 +83,23 @@ public class EventController { return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); } + @Operation( + summary = "Delete an event" + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "204", + description = "Success" + ), + @ApiResponse( + responseCode = "401", + description = "You didn't provide proper authentication via a bearer token" + ), + @ApiResponse( + responseCode = "403", + description = "You're not authorized to perform this operation." + ), + }) @DeleteMapping("/delete/{id}") @AdminOnly public ResponseEntity delete(@PathVariable Long id) { @@ -42,6 +107,20 @@ public class EventController { return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); } + @Operation( + summary = "Load an event by id" + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "Success", + content = @Content(schema = @Schema(implementation = EventResponseDto.class)) + ), + @ApiResponse( + responseCode = "401", + description = "You didn't provide proper authentication via a bearer token" + ), + }) @GetMapping("/{id}") @UserOnly public ResponseEntity getById(@PathVariable Long id) { @@ -50,6 +129,20 @@ public class EventController { .orElseGet(() -> ResponseEntity.status(HttpStatus.NOT_FOUND).build()); } + @Operation( + summary = "List all future events" + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "Success", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = EventResponseDto.class))) + ), + @ApiResponse( + responseCode = "401", + description = "You didn't provide proper authentication via a bearer token" + ) + }) @GetMapping("/list") @UserOnly public ResponseEntity> getAllFuture() { diff --git a/src/main/java/me/jweissen/aeticket/controller/UserController.java b/src/main/java/me/jweissen/aeticket/controller/UserController.java index b9a5574..6e53c8e 100644 --- a/src/main/java/me/jweissen/aeticket/controller/UserController.java +++ b/src/main/java/me/jweissen/aeticket/controller/UserController.java @@ -1,5 +1,11 @@ package me.jweissen.aeticket.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import me.jweissen.aeticket.aspect.AdminOnly; import me.jweissen.aeticket.dto.request.LoginRequestDto; import me.jweissen.aeticket.dto.request.SignupRequestDto; @@ -22,11 +28,39 @@ public class UserController { this.userService = userService; } + @Operation( + summary = "Sign up as a new user" + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "201", + description = "Success", + content = @Content(schema = @Schema(implementation = TokenResponseDto.class)) + ), + @ApiResponse( + responseCode = "400", + description = "Your request body was malformed/insufficient." + ) + }) @PostMapping("/signup") public ResponseEntity signUp(@RequestBody SignupRequestDto user) { return new ResponseEntity<>(userService.create(user), HttpStatus.CREATED); } + @Operation( + summary = "Sign in as an existing user" + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "Success", + content = @Content(schema = @Schema(implementation = TokenResponseDto.class)) + ), + @ApiResponse( + responseCode = "400", + description = "Your request body was malformed/insufficient." + ) + }) @PostMapping("/signin") public ResponseEntity signIn(@RequestBody LoginRequestDto user) { return userService.login(user) @@ -34,6 +68,27 @@ public class UserController { .orElseGet(() -> ResponseEntity.status(HttpStatus.UNAUTHORIZED).build()); } + @Operation( + summary = "Update a user" + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "204", + description = "Success" + ), + @ApiResponse( + responseCode = "400", + description = "Your request body was malformed/insufficient." + ), + @ApiResponse( + responseCode = "401", + description = "You didn't provide proper authentication via a bearer token" + ), + @ApiResponse( + responseCode = "403", + description = "You're not authorized to perform this operation." + ), + }) @PutMapping("/update") @AdminOnly public ResponseEntity update(@RequestBody UserUpdateRequestDto user) { @@ -43,6 +98,23 @@ public class UserController { return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); } + @Operation( + summary = "Delete a user" + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "204", + description = "Success" + ), + @ApiResponse( + responseCode = "401", + description = "You didn't provide proper authentication via a bearer token" + ), + @ApiResponse( + responseCode = "403", + description = "You're not authorized to perform this operation." + ), + }) @DeleteMapping("/delete/{id}") @AdminOnly public ResponseEntity delete(@PathVariable Long id) { @@ -50,12 +122,52 @@ public class UserController { return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); } + @Operation( + summary = "List all users" + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "Success", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = UserResponseDto.class))) + ), + @ApiResponse( + responseCode = "401", + description = "You didn't provide proper authentication via a bearer token" + ), + @ApiResponse( + responseCode = "403", + description = "You're not authorized to perform this operation." + ), + }) @GetMapping("/list") @AdminOnly public ResponseEntity> getAll() { return new ResponseEntity<>(userService.getAll(), HttpStatus.OK); } + @Operation( + summary = "Load a user by id" + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "Success", + content = @Content(schema = @Schema(implementation = UserResponseDto.class)) + ), + @ApiResponse( + responseCode = "401", + description = "You didn't provide proper authentication via a bearer token" + ), + @ApiResponse( + responseCode = "403", + description = "You're not authorized to perform this operation." + ), + @ApiResponse( + responseCode = "404", + description = "No user found with the given id" + ), + }) @GetMapping("/load/{id}") @AdminOnly public ResponseEntity getById(@PathVariable Long id) { diff --git a/src/test/java/me/jweissen/aeticket/controller/CartControllerTest.java b/src/test/java/me/jweissen/aeticket/controller/CartControllerTest.java new file mode 100644 index 0000000..2d98ec6 --- /dev/null +++ b/src/test/java/me/jweissen/aeticket/controller/CartControllerTest.java @@ -0,0 +1,44 @@ +package me.jweissen.aeticket.controller; + +import me.jweissen.aeticket.model.Cart; +import me.jweissen.aeticket.service.CartService; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.web.servlet.MockMvc; + +@WebMvcTest(CartController.class) +public class CartControllerTest { + @Autowired + private MockMvc mockMvc; + + @MockBean + private CartService cartService; + + /* + @Test + void addEntry() { + CartAddRequestDto dto = new CartAddRequestDto( + 999L, + Arrays.asList( + new CartEntryRequestDto(1L, 10), + new CartEntryRequestDto(2L, 10) + ) + ); + Mockito.when(cartService.add()).thenReturn(true); + } + */ + + @Test + void getCartEntries() { + + } + + @Test + void checkout() { + Cart cart = new Cart(); + Mockito.when(cartService.checkout(cart)); + } +} diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml new file mode 100644 index 0000000..9b2cc27 --- /dev/null +++ b/src/test/resources/application.yml @@ -0,0 +1,20 @@ +spring: + jpa: + hibernate: + ddl-auto: create-drop + properties: + hibernate: + enable_lazy_load_no_trans: true + hbm2ddl: + import_files: data.sql + show-sql: true + datasource: + username: test + password: test + url: 'jdbc:mysql://localhost:5306/test' +server: + servlet: + context-path: '/api/v1' +token: + secret: "RATP loves Laravel more than Symfony" + validForHours: 24 \ No newline at end of file diff --git a/src/test/resources/data.sql b/src/test/resources/data.sql new file mode 100644 index 0000000..43c7c68 --- /dev/null +++ b/src/test/resources/data.sql @@ -0,0 +1,12 @@ +INSERT INTO user (id, email, firstname, lastname, password, role, token, token_valid_until) VALUES + (1, 'admin@email.com', 'Admin', 'Chef', 'geheim', 0, 'token', DATE('2023-12-31')); +INSERT INTO cart (id, checked_out, user_id) VALUES (1, false, 1); +UPDATE user SET current_cart_id = 1 WHERE id = 1; + +INSERT INTO event (id, description, end, name, start) VALUES (1, 'Maturaball der Abteilungen EL, IT, ME, Y', '2023-03-03 06:00:00.000000', 'Maturaball HTL Steyr 2024', '2023-03-02 21:00:00.000000'); +INSERT INTO category (id, name, price, stock, event_id) VALUES (1, 'Normal', 2500, 500, 1); +INSERT INTO category (id, name, price, stock, event_id) VALUES (2, 'Maturanten', 0, 500, 1); +INSERT INTO category (id, name, price, stock, event_id) VALUES (3, 'Prechtl', 5000, 1, 1); + +INSERT INTO cart_entry (id, amount, cart_id, category_id) VALUES (3, 1, 2, 3); +