Skip to content

Commit a31fd62

Browse files
committed
feat: adds more test to my media items
1 parent cf513ef commit a31fd62

File tree

7 files changed

+67
-41
lines changed

7 files changed

+67
-41
lines changed

lesson_26/api/java/api_app/build.gradle.kts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ plugins {
33
application
44
eclipse
55
id("com.diffplug.spotless") version "6.25.0"
6-
id("org.springframework.boot") version "3.4.0"
6+
id("org.springframework.boot") version "3.2.3"
77
id("com.adarshr.test-logger") version "4.0.0"
88
}
99

@@ -21,7 +21,6 @@ dependencies {
2121
testImplementation("org.springframework.boot:spring-boot-starter-test")
2222
testImplementation("org.assertj:assertj-core:3.26.3")
2323
testImplementation("at.favre.lib:bcrypt:0.10.2")
24-
testImplementation("org.springframework.boot:spring-boot-starter-test")
2524

2625
// This dependency is used by the application.
2726
implementation("com.codedifferently.instructional:instructional-lib")

lesson_26/api/java/api_app/src/main/java/com/codedifferently/lesson26/web/MediaItemsController.java

Lines changed: 52 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,28 @@
44
import com.codedifferently.lesson26.library.Library;
55
import com.codedifferently.lesson26.library.MediaItem;
66
import com.codedifferently.lesson26.library.search.SearchCriteria;
7+
import jakarta.validation.Valid;
78
import java.io.IOException;
89
import java.util.List;
10+
import java.util.Map;
911
import java.util.Set;
12+
import java.util.UUID;
1013
import org.springframework.http.ResponseEntity;
14+
import org.springframework.validation.FieldError;
15+
import org.springframework.web.bind.MethodArgumentNotValidException;
1116
import org.springframework.web.bind.annotation.CrossOrigin;
1217
import org.springframework.web.bind.annotation.DeleteMapping;
18+
import org.springframework.web.bind.annotation.ExceptionHandler;
1319
import org.springframework.web.bind.annotation.GetMapping;
20+
import org.springframework.web.bind.annotation.PathVariable;
1421
import org.springframework.web.bind.annotation.PostMapping;
22+
import org.springframework.web.bind.annotation.RequestBody;
23+
import org.springframework.web.bind.annotation.RequestMapping;
1524
import org.springframework.web.bind.annotation.RestController;
1625

1726
@RestController
1827
@CrossOrigin
28+
@RequestMapping("/api")
1929
public class MediaItemsController {
2030

2131
private final Library library;
@@ -34,35 +44,53 @@ public ResponseEntity<GetMediaItemsResponse> getItems() {
3444
return ResponseEntity.ok(response);
3545
}
3646

37-
/**
38-
* @param id
39-
* @return
40-
*/
41-
@PostMapping("/items/{id}")
42-
public ResponseEntity<MediaItemResponse> getItemById(String id) {
43-
MediaItem item = librarian.getItemById(id);
44-
MediaItemResponse response = MediaItemResponse.from(item);
45-
return ResponseEntity.ok(response);
47+
@GetMapping("/items/{id}")
48+
public ResponseEntity<MediaItemResponse> getItemById(@PathVariable String id) {
49+
try {
50+
Set<MediaItem> items = library.search(SearchCriteria.builder().id(id).build());
51+
if (items.isEmpty()) {
52+
return ResponseEntity.notFound().build();
53+
}
54+
MediaItemResponse response = MediaItemResponse.from(items.iterator().next());
55+
return ResponseEntity.ok(response);
56+
} catch (IllegalArgumentException e) {
57+
return ResponseEntity.notFound().build();
58+
}
59+
}
60+
61+
@PostMapping("/items")
62+
public ResponseEntity<CreateMediaItemResponse> createItem(
63+
@Valid @RequestBody CreateMediaItemRequest request) {
64+
try {
65+
MediaItem item = MediaItemRequest.asMediaItem(request.getItem());
66+
library.addMediaItem(item, librarian);
67+
var response = CreateMediaItemResponse.builder().item(MediaItemResponse.from(item)).build();
68+
return ResponseEntity.ok(response);
69+
} catch (IllegalArgumentException e) {
70+
return ResponseEntity.badRequest().build();
71+
}
4672
}
4773

48-
/**
49-
* @param id
50-
* @return
51-
*/
5274
@DeleteMapping("/items/{id}")
53-
public ResponseEntity<Void> deleteItemById(String id) {
54-
SearchCriteria criteria = SearchCriteria.builder().id(id).build();
55-
Set<MediaItem> items = library.search(criteria);
56-
if (items.isEmpty()) {
75+
public ResponseEntity<Void> deleteItemById(@PathVariable String id) {
76+
try {
77+
UUID uuid = UUID.fromString(id);
78+
Set<MediaItem> items = library.search(SearchCriteria.builder().id(id).build());
79+
if (items.isEmpty()) {
80+
return ResponseEntity.notFound().build();
81+
}
82+
library.removeMediaItem(uuid, librarian);
83+
return ResponseEntity.noContent().build();
84+
} catch (IllegalArgumentException e) {
5785
return ResponseEntity.notFound().build();
5886
}
59-
MediaItem item = items.iterator().next();
60-
61-
librarian.deleteItemById(id);
62-
return ResponseEntity.noContent().build();
63-
64-
6587
}
6688

89+
@ExceptionHandler(MethodArgumentNotValidException.class)
90+
public ResponseEntity<Map<String, List<String>>> handleValidationErrors(
91+
MethodArgumentNotValidException ex) {
92+
List<String> errors =
93+
ex.getBindingResult().getFieldErrors().stream().map(FieldError::getDefaultMessage).toList();
94+
return ResponseEntity.badRequest().body(Map.of("errors", errors));
95+
}
6796
}
68-

lesson_26/api/java/api_app/src/test/java/com/codedifferently/lesson26/web/MediaItemsControllerTest.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ static void setUp(WebApplicationContext wac) {
3737
@Test
3838
void testController_getsAllItems() throws Exception {
3939
mockMvc
40-
.perform(get("/items").contentType(MediaType.APPLICATION_JSON))
40+
.perform(get("/api/items").contentType(MediaType.APPLICATION_JSON))
4141
.andExpect(status().isOk())
4242
.andExpect(jsonPath("$.items").isArray())
4343
.andExpect(jsonPath("$.items.length()").value(31));
@@ -47,7 +47,7 @@ void testController_getsAllItems() throws Exception {
4747
void testController_getsAnItem() throws Exception {
4848
mockMvc
4949
.perform(
50-
get("/items/31616162-3831-3832-2d34-3334352d3465")
50+
get("/api/items/31616162-3831-3832-2d34-3334352d3465")
5151
.contentType(MediaType.APPLICATION_JSON))
5252
.andExpect(status().isOk());
5353
}
@@ -56,7 +56,7 @@ void testController_getsAnItem() throws Exception {
5656
void testController_returnsNotFoundOnGetItem() throws Exception {
5757
mockMvc
5858
.perform(
59-
get("/items/00000000-0000-0000-0000-000000000000")
59+
get("/api/items/00000000-0000-0000-0000-000000000000")
6060
.contentType(MediaType.APPLICATION_JSON))
6161
.andExpect(status().isNotFound());
6262
}
@@ -66,7 +66,7 @@ void testController_reportsBadRequestOnAddItem() throws Exception {
6666
String json = "{}";
6767

6868
mockMvc
69-
.perform(post("/items").contentType(MediaType.APPLICATION_JSON).content(json))
69+
.perform(post("/api/items").contentType(MediaType.APPLICATION_JSON).content(json))
7070
.andExpect(status().isBadRequest())
7171
.andExpect(jsonPath("$.errors").isArray())
7272
.andExpect(jsonPath("$.errors.length()").value(1));
@@ -89,7 +89,7 @@ void testController_addsItem() throws Exception {
8989
""";
9090

9191
mockMvc
92-
.perform(post("/items").contentType(MediaType.APPLICATION_JSON).content(json))
92+
.perform(post("/api/items").contentType(MediaType.APPLICATION_JSON).content(json))
9393
.andExpect(status().isOk())
9494
.andExpect(jsonPath("$.item.id").value("e27a4e0d-9664-420d-955e-c0e295d0ce02"));
9595

@@ -105,7 +105,7 @@ void testController_addsItem() throws Exception {
105105
void testController_returnsNotFoundOnDeleteItem() throws Exception {
106106
mockMvc
107107
.perform(
108-
delete("/items/00000000-0000-0000-0000-000000000000")
108+
delete("/api/items/00000000-0000-0000-0000-000000000000")
109109
.contentType(MediaType.APPLICATION_JSON))
110110
.andExpect(status().isNotFound());
111111
}
@@ -114,7 +114,7 @@ void testController_returnsNotFoundOnDeleteItem() throws Exception {
114114
void testController_deletesItem() throws Exception {
115115
mockMvc
116116
.perform(
117-
delete("/items/32623932-6566-3364-2d62-3232342d3435")
117+
delete("/api/items/32623932-6566-3364-2d62-3232342d3435")
118118
.contentType(MediaType.APPLICATION_JSON))
119119
.andExpect(status().isNoContent());
120120

181 Bytes
Binary file not shown.

lesson_26/api/java/gradle/wrapper/gradle-wrapper.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
44
networkTimeout=10000
55
validateDistributionUrl=true
66
zipStoreBase=GRADLE_USER_HOME

lesson_26/api/java/gradlew

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,7 @@ done
8686
# shellcheck disable=SC2034
8787
APP_BASE_NAME=${0##*/}
8888
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
89-
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
90-
' "$PWD" ) || exit
89+
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
9190

9291
# Use the maximum available, or set MAX_FD != -1 to use that value.
9392
MAX_FD=maximum
@@ -115,7 +114,7 @@ case "$( uname )" in #(
115114
NONSTOP* ) nonstop=true ;;
116115
esac
117116

118-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
117+
CLASSPATH="\\\"\\\""
119118

120119

121120
# Determine the Java command to use to start the JVM.
@@ -206,15 +205,15 @@ fi
206205
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
207206

208207
# Collect all arguments for the java command:
209-
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
208+
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
210209
# and any embedded shellness will be escaped.
211210
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
212211
# treated as '${Hostname}' itself on the command line.
213212

214213
set -- \
215214
"-Dorg.gradle.appname=$APP_BASE_NAME" \
216215
-classpath "$CLASSPATH" \
217-
org.gradle.wrapper.GradleWrapperMain \
216+
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
218217
"$@"
219218

220219
# Stop when "xargs" is not available.

lesson_26/api/java/gradlew.bat

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,11 @@ goto fail
7070
:execute
7171
@rem Setup the command line
7272

73-
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73+
set CLASSPATH=
7474

7575

7676
@rem Execute Gradle
77-
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
77+
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
7878

7979
:end
8080
@rem End local scope for the variables with windows NT shell

0 commit comments

Comments
 (0)