From 7f53578b539f2b2a60764a9717478a298f730f07 Mon Sep 17 00:00:00 2001 From: Thomas Turrell-Croft Date: Tue, 23 May 2023 16:25:45 +0100 Subject: [PATCH 1/4] Update eclipse settings --- .../xapi-server/.settings/org.eclipse.jdt.core.prefs | 10 +++++++++- xapi-client/.settings/org.eclipse.jdt.core.prefs | 10 +++++++++- xapi-model/.settings/org.eclipse.jdt.core.prefs | 3 ++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/samples/xapi-server/.settings/org.eclipse.jdt.core.prefs b/samples/xapi-server/.settings/org.eclipse.jdt.core.prefs index 89df520e..e70d5c24 100644 --- a/samples/xapi-server/.settings/org.eclipse.jdt.core.prefs +++ b/samples/xapi-server/.settings/org.eclipse.jdt.core.prefs @@ -1,11 +1,19 @@ eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.annotation.nonnull=org.springframework.lang.NonNull +org.eclipse.jdt.core.compiler.annotation.nullable=org.springframework.lang.Nullable +org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled org.eclipse.jdt.core.compiler.codegen.methodParameters=generate org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 org.eclipse.jdt.core.compiler.compliance=17 org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=warning +org.eclipse.jdt.core.compiler.problem.nullReference=warning +org.eclipse.jdt.core.compiler.problem.nullSpecViolation=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore -org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.processAnnotations=disabled +org.eclipse.jdt.core.compiler.release=enabled org.eclipse.jdt.core.compiler.source=17 org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 diff --git a/xapi-client/.settings/org.eclipse.jdt.core.prefs b/xapi-client/.settings/org.eclipse.jdt.core.prefs index 89df520e..f5301f16 100644 --- a/xapi-client/.settings/org.eclipse.jdt.core.prefs +++ b/xapi-client/.settings/org.eclipse.jdt.core.prefs @@ -1,11 +1,19 @@ eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.annotation.nonnull=org.springframework.lang.NonNull +org.eclipse.jdt.core.compiler.annotation.nullable=org.springframework.lang.Nullable +org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled org.eclipse.jdt.core.compiler.codegen.methodParameters=generate org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 org.eclipse.jdt.core.compiler.compliance=17 org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=warning +org.eclipse.jdt.core.compiler.problem.nullReference=warning +org.eclipse.jdt.core.compiler.problem.nullSpecViolation=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore -org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.processAnnotations=enabled +org.eclipse.jdt.core.compiler.release=enabled org.eclipse.jdt.core.compiler.source=17 org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 diff --git a/xapi-model/.settings/org.eclipse.jdt.core.prefs b/xapi-model/.settings/org.eclipse.jdt.core.prefs index 89df520e..a4cb91c9 100644 --- a/xapi-model/.settings/org.eclipse.jdt.core.prefs +++ b/xapi-model/.settings/org.eclipse.jdt.core.prefs @@ -5,7 +5,8 @@ org.eclipse.jdt.core.compiler.compliance=17 org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore -org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.processAnnotations=enabled +org.eclipse.jdt.core.compiler.release=enabled org.eclipse.jdt.core.compiler.source=17 org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 From 8dda127f80a6d40d2592340b431f9ff82271b888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20R=C3=A1tkai?= Date: Wed, 24 May 2023 09:22:37 +0100 Subject: [PATCH 2/4] resolve warnings --- .../client/MissingResponseBodyException.java | 18 ++++++++++++++++++ .../dev/learning/xapi/client/XapiClient.java | 9 ++++++++- .../xapi/client/XapiClientMultipartTests.java | 2 ++ .../StrictObjectTypeResolverBuilder.java | 2 +- 4 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 xapi-client/src/main/java/dev/learning/xapi/client/MissingResponseBodyException.java diff --git a/xapi-client/src/main/java/dev/learning/xapi/client/MissingResponseBodyException.java b/xapi-client/src/main/java/dev/learning/xapi/client/MissingResponseBodyException.java new file mode 100644 index 00000000..8e9af431 --- /dev/null +++ b/xapi-client/src/main/java/dev/learning/xapi/client/MissingResponseBodyException.java @@ -0,0 +1,18 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.client; + +/** + *

+ * MissingResponseBodyException class. + *

+ * + * @author István Rátkai (Selindek) + */ +public class MissingResponseBodyException extends RuntimeException { + + private static final long serialVersionUID = -5731953477307546047L; + +} diff --git a/xapi-client/src/main/java/dev/learning/xapi/client/XapiClient.java b/xapi-client/src/main/java/dev/learning/xapi/client/XapiClient.java index 7f7466a3..e3fb9812 100644 --- a/xapi-client/src/main/java/dev/learning/xapi/client/XapiClient.java +++ b/xapi-client/src/main/java/dev/learning/xapi/client/XapiClient.java @@ -18,6 +18,7 @@ import java.util.List; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Optional; import java.util.UUID; import java.util.function.Consumer; import java.util.stream.Stream; @@ -123,6 +124,8 @@ public Mono> getStatement( *

* * @return the ResponseEntity + * + * @throws MissingResponseBodyException if the response body is missing */ public Mono> postStatement(PostStatementRequest request) { @@ -140,7 +143,8 @@ public Mono> postStatement(PostStatementRequest request) { .toEntity(LIST_UUID_TYPE) - .map(i -> ResponseEntity.ok().headers(i.getHeaders()).body(i.getBody().get(0))); + .map(i -> ResponseEntity.ok().headers(i.getHeaders()).body(Optional.ofNullable(i.getBody()) + .map(l -> l.get(0)).orElseThrow(MissingResponseBodyException::new))); } @@ -1432,6 +1436,9 @@ public Stream toStream() { private void init(ResponseEntity response) { final var statementResult = response.getBody(); + if (statementResult == null) { + throw new MissingResponseBodyException(); + } more = statementResult.hasMore() ? statementResult.getMore() : null; final var s = statementResult.getStatements(); statements = s == null ? Collections.emptyIterator() : s.iterator(); diff --git a/xapi-client/src/test/java/dev/learning/xapi/client/XapiClientMultipartTests.java b/xapi-client/src/test/java/dev/learning/xapi/client/XapiClientMultipartTests.java index 84e90bcf..5a2ee097 100644 --- a/xapi-client/src/test/java/dev/learning/xapi/client/XapiClientMultipartTests.java +++ b/xapi-client/src/test/java/dev/learning/xapi/client/XapiClientMultipartTests.java @@ -326,6 +326,7 @@ void whenPostingStatementsWithTimestampAndAttachmentThenNoExceptionIsThrown() } + @SuppressWarnings("null") @Test void whenGettingStatementWithAttachmentThenResponseIsExpected() throws InterruptedException { @@ -367,6 +368,7 @@ void whenGettingStatementWithAttachmentThenResponseIsExpected() throws Interrupt "Statement(id=183aabbe-ef9e-49c9-82a3-16ce5135b25b, actor=Agent(super=Actor(name=A N Other, mbox=mailto:another@example.com, mboxSha1sum=null, openid=null, account=null)), verb=Verb(id=http://adlnet.gov/expapi/verbs/attempted, display={und=attempted}), object=Activity(id=https://example.com/activity/simplestatement, definition=ActivityDefinition(name={en=Simple Statement}, description=null, type=null, moreInfo=null, interactionType=null, correctResponsesPattern=null, choices=null, scale=null, source=null, target=null, steps=null, extensions=null)), result=null, context=null, timestamp=2023-03-29T12:42:27.923571Z, stored=2023-03-29T12:42:27.923571Z, authority=Agent(super=Actor(name=null, mbox=null, mboxSha1sum=null, openid=null, account=Account(homePage=http://localhost, name=admin))), version=null, attachments=[Attachment(usageType=http://adlnet.gov/expapi/attachments/code, display={en=binary attachment}, description=null, contentType=application/octet-stream, length=6, sha2=0ff3c6749b3eeaae17254fdf0e2de1f32b21c592f474bf39b62b398e8a787eef, fileUrl=null, content=[64, 65, 66, 67, 68, 69]), Attachment(usageType=http://adlnet.gov/expapi/attachments/text, display={en=text attachment}, description=null, contentType=text/plain, length=17, sha2=b154d3fd46a5068da42ba05a8b9c971688ab5a57eb5c3a0e50a23c42a86786e5, fileUrl=null, content=[83, 105, 109, 112, 108, 101, 32, 97, 116, 116, 97, 99, 104, 109, 101, 110, 116])])")); } + @SuppressWarnings("null") @Test void whenGettingStatementsWithAttachmentsThenResponseIsExpected() throws InterruptedException { diff --git a/xapi-model/src/main/java/dev/learning/xapi/jackson/StrictObjectTypeResolverBuilder.java b/xapi-model/src/main/java/dev/learning/xapi/jackson/StrictObjectTypeResolverBuilder.java index 835919c3..d05e2a58 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/jackson/StrictObjectTypeResolverBuilder.java +++ b/xapi-model/src/main/java/dev/learning/xapi/jackson/StrictObjectTypeResolverBuilder.java @@ -50,7 +50,7 @@ public static class StrictObjectTypePropertyDeserializer extends AsPropertyTypeD public StrictObjectTypePropertyDeserializer(JavaType baseType, TypeIdResolver idRes, String typeProperty, boolean typeIdVisible, JavaType defaultImpl, As includeAs) { - super(baseType, idRes, typeProperty, typeIdVisible, defaultImpl, includeAs); + super(baseType, idRes, typeProperty, typeIdVisible, defaultImpl, includeAs, true); } public StrictObjectTypePropertyDeserializer(AsPropertyTypeDeserializer src, From 293a11afaa1fc51cda66d8ae91713e224f4f1e6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20R=C3=A1tkai?= Date: Wed, 24 May 2023 15:52:33 +0100 Subject: [PATCH 3/4] add tests --- .../learning/xapi/client/XapiClientTests.java | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/xapi-client/src/test/java/dev/learning/xapi/client/XapiClientTests.java b/xapi-client/src/test/java/dev/learning/xapi/client/XapiClientTests.java index 85f4252d..c0a0430f 100644 --- a/xapi-client/src/test/java/dev/learning/xapi/client/XapiClientTests.java +++ b/xapi-client/src/test/java/dev/learning/xapi/client/XapiClientTests.java @@ -36,6 +36,8 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException.BadRequest; +import org.springframework.web.reactive.function.client.WebClientResponseException.InternalServerError; /** * XapiClient Tests. @@ -379,6 +381,73 @@ void whenPostingStatementThenContentTypeHeaderIsApplicationJson() throws Interru assertThat(recordedRequest.getHeader("content-type"), is("application/json")); } + @Test + void givenApiResponseIsEmptyWhenPostingStatementThenMissingResponseBodyExceptionIsThrown() { + + // Given Api Response Is Empty + mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 OK").setHeader("Content-Type", + "application/json")); + + // When Posting Statement + // Then MissingResponseBodyException Is Thrown + assertThrows(MissingResponseBodyException.class, + () -> client + .postStatement( + r -> r.statement( + s -> s.agentActor(a -> a.name("A N Other").mbox("mailto:another@example.com")) + + .verb(Verb.ATTEMPTED) + + .activityObject(o -> o.id("https://example.com/activity/simplestatement") + .definition(d -> d.addName(Locale.ENGLISH, "Simple Statement"))))) + .block()); + + } + + @Test + void givenApiResponseIsBadRequestWhenPostingStatementThenBadRequestIsThrown() { + + // Given Api Response Is Bad Request + mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 400 Bad Request")); + + // When Posting Statement + // Then BadRequest Is Thrown + assertThrows(BadRequest.class, + () -> client + .postStatement( + r -> r.statement( + s -> s.agentActor(a -> a.name("A N Other").mbox("mailto:another@example.com")) + + .verb(Verb.ATTEMPTED) + + .activityObject(o -> o.id("https://example.com/activity/simplestatement") + .definition(d -> d.addName(Locale.ENGLISH, "Simple Statement"))))) + .block()); + + } + + @Test + void givenApiResponseIsInternalServerErrorWhenPostingStatementThenInternalServerErrorIsThrown() { + + // Given Api Response Is Internal Server Error + mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 500 Internal Server Error")); + + // When Posting Statement + // Then InternalServerError Is Thrown + assertThrows(InternalServerError.class, + () -> client + .postStatement( + r -> r.statement( + s -> s.agentActor(a -> a.name("A N Other").mbox("mailto:another@example.com")) + + .verb(Verb.ATTEMPTED) + + .activityObject(o -> o.id("https://example.com/activity/simplestatement") + .definition(d -> d.addName(Locale.ENGLISH, "Simple Statement"))))) + .block()); + + } + // Posting a Signed Statement @Test From 3ae642b0b772b915a046f8de64019f4dbea382da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20R=C3=A1tkai?= Date: Thu, 25 May 2023 09:34:13 +0100 Subject: [PATCH 4/4] fsi --- .../learning/xapi/client/XapiClientTests.java | 54 +++++++++---------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/xapi-client/src/test/java/dev/learning/xapi/client/XapiClientTests.java b/xapi-client/src/test/java/dev/learning/xapi/client/XapiClientTests.java index c0a0430f..969ec96a 100644 --- a/xapi-client/src/test/java/dev/learning/xapi/client/XapiClientTests.java +++ b/xapi-client/src/test/java/dev/learning/xapi/client/XapiClientTests.java @@ -389,18 +389,16 @@ void givenApiResponseIsEmptyWhenPostingStatementThenMissingResponseBodyException "application/json")); // When Posting Statement - // Then MissingResponseBodyException Is Thrown - assertThrows(MissingResponseBodyException.class, - () -> client - .postStatement( - r -> r.statement( - s -> s.agentActor(a -> a.name("A N Other").mbox("mailto:another@example.com")) + final var response = client.postStatement(r -> r + .statement(s -> s.agentActor(a -> a.name("A N Other").mbox("mailto:another@example.com")) - .verb(Verb.ATTEMPTED) + .verb(Verb.ATTEMPTED) - .activityObject(o -> o.id("https://example.com/activity/simplestatement") - .definition(d -> d.addName(Locale.ENGLISH, "Simple Statement"))))) - .block()); + .activityObject(o -> o.id("https://example.com/activity/simplestatement") + .definition(d -> d.addName(Locale.ENGLISH, "Simple Statement"))))); + + // Then MissingResponseBodyException Is Thrown + assertThrows(MissingResponseBodyException.class, () -> response.block()); } @@ -411,18 +409,16 @@ void givenApiResponseIsBadRequestWhenPostingStatementThenBadRequestIsThrown() { mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 400 Bad Request")); // When Posting Statement - // Then BadRequest Is Thrown - assertThrows(BadRequest.class, - () -> client - .postStatement( - r -> r.statement( - s -> s.agentActor(a -> a.name("A N Other").mbox("mailto:another@example.com")) + final var response = client.postStatement(r -> r + .statement(s -> s.agentActor(a -> a.name("A N Other").mbox("mailto:another@example.com")) - .verb(Verb.ATTEMPTED) + .verb(Verb.ATTEMPTED) - .activityObject(o -> o.id("https://example.com/activity/simplestatement") - .definition(d -> d.addName(Locale.ENGLISH, "Simple Statement"))))) - .block()); + .activityObject(o -> o.id("https://example.com/activity/simplestatement") + .definition(d -> d.addName(Locale.ENGLISH, "Simple Statement"))))); + + // Then BadRequest Is Thrown + assertThrows(BadRequest.class, () -> response.block()); } @@ -433,18 +429,16 @@ void givenApiResponseIsInternalServerErrorWhenPostingStatementThenInternalServer mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 500 Internal Server Error")); // When Posting Statement - // Then InternalServerError Is Thrown - assertThrows(InternalServerError.class, - () -> client - .postStatement( - r -> r.statement( - s -> s.agentActor(a -> a.name("A N Other").mbox("mailto:another@example.com")) + final var response = client.postStatement(r -> r + .statement(s -> s.agentActor(a -> a.name("A N Other").mbox("mailto:another@example.com")) - .verb(Verb.ATTEMPTED) + .verb(Verb.ATTEMPTED) - .activityObject(o -> o.id("https://example.com/activity/simplestatement") - .definition(d -> d.addName(Locale.ENGLISH, "Simple Statement"))))) - .block()); + .activityObject(o -> o.id("https://example.com/activity/simplestatement") + .definition(d -> d.addName(Locale.ENGLISH, "Simple Statement"))))); + + // Then InternalServerError Is Thrown + assertThrows(InternalServerError.class, () -> response.block()); }