From 9acbac9f53cdf2efdf5ad516306da62804c8feb6 Mon Sep 17 00:00:00 2001 From: Thomas Turrell-Croft Date: Tue, 14 Mar 2023 15:22:50 +0000 Subject: [PATCH 01/34] Add validators --- pom.xml | 4 +- xapi-model/pom.xml | 8 ++-- .../java/dev/learning/xapi/model/Score.java | 17 ------- .../java/dev/learning/xapi/model/Verb.java | 2 + .../validation/constraints/HasScheme.java | 45 +++++++++++++++++++ .../validators/HasSchemeValidatorForUri.java | 25 +++++++++++ 6 files changed, 78 insertions(+), 23 deletions(-) create mode 100644 xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/HasScheme.java create mode 100644 xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/HasSchemeValidatorForUri.java diff --git a/pom.xml b/pom.xml index 8492f437..dcacde67 100644 --- a/pom.xml +++ b/pom.xml @@ -79,7 +79,7 @@ xapi-model xapi-client - samples + samples @@ -266,7 +266,7 @@ dev.learning.xapi xapi-client 1.0.9-SNAPSHOT - + diff --git a/xapi-model/pom.xml b/xapi-model/pom.xml index 4c32faac..0960e432 100644 --- a/xapi-model/pom.xml +++ b/xapi-model/pom.xml @@ -10,10 +10,6 @@ xAPI Model learning.dev xAPI Model - - org.springframework.boot - spring-boot-starter-validation - com.fasterxml.jackson.core jackson-databind @@ -27,6 +23,10 @@ com.fasterxml.jackson.datatype jackson-datatype-jsr310 + + jakarta.validation + jakarta.validation-api + org.springframework.boot spring-boot-starter-test diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Score.java b/xapi-model/src/main/java/dev/learning/xapi/model/Score.java index 267677df..ad4f878b 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/Score.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/Score.java @@ -8,7 +8,6 @@ import com.fasterxml.jackson.annotation.JsonInclude.Include; import lombok.Builder; import lombok.Value; -import org.springframework.util.Assert; /** * This class represents the xAPI Score object. @@ -51,22 +50,6 @@ public class Score { */ public static class Builder { - protected Float scaled; - - /** - * Sets the scaled score. - * - * @param scaled The scaled score. - * - * @return This builder - */ - public Builder scaled(Float scaled) { - Assert.isTrue(scaled == null || (scaled >= -1.0 && scaled <= 1.0), - "Scaled score vaule must be between -1.0 and 1.0"); - this.scaled = scaled; - return this; - } - // This static class extends the lombok builder. } diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Verb.java b/xapi-model/src/main/java/dev/learning/xapi/model/Verb.java index 8cd20038..8adbeb31 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/Verb.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/Verb.java @@ -7,6 +7,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; +import dev.learning.xapi.model.validation.constraints.HasScheme; import jakarta.validation.constraints.NotNull; import java.net.URI; import java.util.Locale; @@ -342,6 +343,7 @@ public class Verb { * not the word. */ @NotNull + @HasScheme private URI id; /** diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/HasScheme.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/HasScheme.java new file mode 100644 index 00000000..89d0f189 --- /dev/null +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/HasScheme.java @@ -0,0 +1,45 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.constraints; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * The annotated element must have a scheme. + * + * @author Thomas Turrell-Croft + */ +@Documented +@Constraint(validatedBy = {}) +@Target({METHOD, FIELD}) +@Retention(RUNTIME) +public @interface HasScheme { + + /** + * Error Message. + */ + String message() default "must have a scheme"; + + /** + * Groups. + */ + Class[] groups() default {}; + + /** + * Payload. + */ + Class[] payload() default {}; + + boolean schemaRequired() default false; + + +} diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/HasSchemeValidatorForUri.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/HasSchemeValidatorForUri.java new file mode 100644 index 00000000..1ca38811 --- /dev/null +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/HasSchemeValidatorForUri.java @@ -0,0 +1,25 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.internal.validators; + +import dev.learning.xapi.model.validation.constraints.HasScheme; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import java.net.URI; + +public class HasSchemeValidatorForUri implements ConstraintValidator { + + @Override + public boolean isValid(URI value, ConstraintValidatorContext context) { + + if (value == null) { + return true; + } + + return value.getScheme() != null; + + } + +} From 698f050a96f309ddffeeb135db26405ca0e1f4e0 Mon Sep 17 00:00:00 2001 From: Thomas Turrell-Croft Date: Tue, 14 Mar 2023 17:30:42 +0000 Subject: [PATCH 02/34] tip --- samples/xapi-server/pom.xml | 7 ++++++- xapi-model/pom.xml | 2 +- .../src/main/java/dev/learning/xapi/model/About.java | 3 ++- .../dev/learning/xapi/model/ActivityDefinition.java | 3 ++- .../src/main/java/dev/learning/xapi/model/Context.java | 3 ++- .../src/main/java/dev/learning/xapi/model/Result.java | 3 ++- .../main/java/dev/learning/xapi/model/Statement.java | 6 ++++++ .../xapi/model/validation/constraints/HasScheme.java | 10 ++++++---- .../internal/validators/HasSchemeValidatorForUri.java | 5 +++++ .../services/jakarta.validation.ConstraintValidator | 1 + 10 files changed, 33 insertions(+), 10 deletions(-) create mode 100644 xapi-model/src/main/resources/META-INF/services/jakarta.validation.ConstraintValidator diff --git a/samples/xapi-server/pom.xml b/samples/xapi-server/pom.xml index e9002673..eca14f48 100644 --- a/samples/xapi-server/pom.xml +++ b/samples/xapi-server/pom.xml @@ -13,7 +13,12 @@ org.springframework.boot spring-boot-starter-web - + + + + org.springframework.boot + spring-boot-starter-validation + dev.learning.xapi xapi-model diff --git a/xapi-model/pom.xml b/xapi-model/pom.xml index 0960e432..ca190d1d 100644 --- a/xapi-model/pom.xml +++ b/xapi-model/pom.xml @@ -26,7 +26,7 @@ jakarta.validation jakarta.validation-api - + org.springframework.boot spring-boot-starter-test diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/About.java b/xapi-model/src/main/java/dev/learning/xapi/model/About.java index f73a02ab..2765b44a 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/About.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/About.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; +import dev.learning.xapi.model.validation.constraints.HasScheme; import java.net.URI; import java.util.LinkedHashMap; import java.util.List; @@ -29,7 +30,7 @@ public class About { private List version; - private LinkedHashMap extensions; + private LinkedHashMap<@HasScheme URI, Object> extensions; // **Warning** do not add fields that are not required by the xAPI specification. diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/ActivityDefinition.java b/xapi-model/src/main/java/dev/learning/xapi/model/ActivityDefinition.java index ec9c9d49..a0f67459 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/ActivityDefinition.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/ActivityDefinition.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; +import dev.learning.xapi.model.validation.constraints.HasScheme; import java.net.URI; import java.util.ArrayList; import java.util.List; @@ -89,7 +90,7 @@ public class ActivityDefinition { /** * A map of other properties as needed. */ - private Map extensions; + private Map<@HasScheme URI, Object> extensions; // **Warning** do not add fields that are not required by the xAPI specification. diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Context.java b/xapi-model/src/main/java/dev/learning/xapi/model/Context.java index eaf903e6..33e0bea0 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/Context.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/Context.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; +import dev.learning.xapi.model.validation.constraints.HasScheme; import jakarta.validation.Valid; import java.net.URI; import java.util.LinkedHashMap; @@ -76,7 +77,7 @@ public class Context { /** * A map of any other domain-specific context relevant to this Statement. */ - private LinkedHashMap extensions; + private LinkedHashMap<@HasScheme URI, Object> extensions; // **Warning** do not add fields that are not required by the xAPI specification. diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Result.java b/xapi-model/src/main/java/dev/learning/xapi/model/Result.java index 0f9d5a89..aef4516b 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/Result.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/Result.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; +import dev.learning.xapi.model.validation.constraints.HasScheme; import jakarta.validation.Valid; import jakarta.validation.constraints.Pattern; import java.net.URI; @@ -58,7 +59,7 @@ public class Result { message = "Must be a valid ISO 8601:2004 duration format.") private String duration; - private LinkedHashMap extensions; + private LinkedHashMap<@HasScheme URI, Object> extensions; // **Warning** do not add fields that are not required by the xAPI specification. diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Statement.java b/xapi-model/src/main/java/dev/learning/xapi/model/Statement.java index b989554a..0dd26cb1 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/Statement.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/Statement.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; +import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; import java.time.Instant; @@ -46,28 +47,33 @@ public class Statement { * Whom the Statement is about, as an Agent or Group Object. */ @NotNull + @Valid private Actor actor; /** * Action taken by the Actor. */ @NotNull + @Valid private Verb verb; /** * Activity, Agent, or another Statement that is the Object of the Statement. */ @NotNull + @Valid private StatementObject object; /** * Result Object, further details representing a measured outcome. */ + @Valid private Result result; /** * Context that gives the Statement more meaning. */ + @Valid private Context context; /** diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/HasScheme.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/HasScheme.java index 89d0f189..4f9a2733 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/HasScheme.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/HasScheme.java @@ -4,9 +4,14 @@ package dev.learning.xapi.model.validation.constraints; +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; import static java.lang.annotation.RetentionPolicy.RUNTIME; + import jakarta.validation.Constraint; import jakarta.validation.Payload; import java.lang.annotation.Documented; @@ -20,7 +25,7 @@ */ @Documented @Constraint(validatedBy = {}) -@Target({METHOD, FIELD}) +@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) @Retention(RUNTIME) public @interface HasScheme { @@ -39,7 +44,4 @@ */ Class[] payload() default {}; - boolean schemaRequired() default false; - - } diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/HasSchemeValidatorForUri.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/HasSchemeValidatorForUri.java index 1ca38811..9ead1319 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/HasSchemeValidatorForUri.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/HasSchemeValidatorForUri.java @@ -9,6 +9,11 @@ import jakarta.validation.ConstraintValidatorContext; import java.net.URI; +/** + * The URI being validated must have a schema. + * + * @author Thomas Turrell-Croft + */ public class HasSchemeValidatorForUri implements ConstraintValidator { @Override diff --git a/xapi-model/src/main/resources/META-INF/services/jakarta.validation.ConstraintValidator b/xapi-model/src/main/resources/META-INF/services/jakarta.validation.ConstraintValidator new file mode 100644 index 00000000..cdc68d5f --- /dev/null +++ b/xapi-model/src/main/resources/META-INF/services/jakarta.validation.ConstraintValidator @@ -0,0 +1 @@ + dev.learning.xapi.model.validation.internal.validators.HasSchemeValidatorForUri \ No newline at end of file From be2d94960281a1334a1a1740ba0cad7239608f0d Mon Sep 17 00:00:00 2001 From: Thomas Turrell-Croft Date: Tue, 14 Mar 2023 20:35:09 +0000 Subject: [PATCH 03/34] tip --- .../learning/xapi/model/ActivityState.java | 2 + .../java/dev/learning/xapi/model/Context.java | 2 + .../dev/learning/xapi/model/Statement.java | 2 + .../xapi/model/StatementReference.java | 2 + .../model/validation/constraints/Variant.java | 53 +++++++++++++++++++ .../validators/VariantValidatorForUuid.java | 38 +++++++++++++ .../jakarta.validation.ConstraintValidator | 3 +- 7 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/Variant.java create mode 100644 xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/VariantValidatorForUuid.java diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/ActivityState.java b/xapi-model/src/main/java/dev/learning/xapi/model/ActivityState.java index 3a91c547..8d4ee606 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/ActivityState.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/ActivityState.java @@ -7,6 +7,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.databind.node.ObjectNode; +import dev.learning.xapi.model.validation.constraints.Variant; import jakarta.validation.Valid; import java.util.UUID; import lombok.Builder; @@ -32,6 +33,7 @@ public class ActivityState { @Valid private Agent agent; + @Variant(2) private UUID registration; private ObjectNode state; diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Context.java b/xapi-model/src/main/java/dev/learning/xapi/model/Context.java index 33e0bea0..a64d7e85 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/Context.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/Context.java @@ -7,6 +7,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import dev.learning.xapi.model.validation.constraints.HasScheme; +import dev.learning.xapi.model.validation.constraints.Variant; import jakarta.validation.Valid; import java.net.URI; import java.util.LinkedHashMap; @@ -32,6 +33,7 @@ public class Context { /** * The registration that the Statement is associated with. */ + @Variant(2) private UUID registration; diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Statement.java b/xapi-model/src/main/java/dev/learning/xapi/model/Statement.java index 0dd26cb1..775aa554 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/Statement.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/Statement.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; +import dev.learning.xapi.model.validation.constraints.Variant; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; @@ -41,6 +42,7 @@ public class Statement { /** * UUID assigned by LRS if not set by the Learning Record Provider. */ + @Variant(2) private UUID id; /** diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/StatementReference.java b/xapi-model/src/main/java/dev/learning/xapi/model/StatementReference.java index ebe4b071..2499e688 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/StatementReference.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/StatementReference.java @@ -4,6 +4,7 @@ package dev.learning.xapi.model; +import dev.learning.xapi.model.validation.constraints.Variant; import jakarta.validation.constraints.NotNull; import java.util.UUID; import lombok.Builder; @@ -25,6 +26,7 @@ public class StatementReference implements StatementObject, SubStatementObject { /** * The UUID of a Statement. */ + @Variant(2) @NotNull private UUID id; diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/Variant.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/Variant.java new file mode 100644 index 00000000..eba167f1 --- /dev/null +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/Variant.java @@ -0,0 +1,53 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.constraints; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * The annotated element must have a scheme. + * + * @author Thomas Turrell-Croft + */ +@Documented +@Constraint(validatedBy = {}) +@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) +@Retention(RUNTIME) +public @interface Variant { + + /** + * Error Message. + */ + String message() default "variant must be {variant}"; + + /** + * Groups. + */ + Class[] groups() default {}; + + /** + * Payload. + */ + Class[] payload() default {}; + + /** + * The valid variant, defaults to 2. + */ + int value() default 2; + + +} diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/VariantValidatorForUuid.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/VariantValidatorForUuid.java new file mode 100644 index 00000000..71fa37b3 --- /dev/null +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/VariantValidatorForUuid.java @@ -0,0 +1,38 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.internal.validators; + +import dev.learning.xapi.model.validation.constraints.Variant; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import java.util.UUID; + +/** + * The UUID being validated must have the specified variant. + * + * @author Thomas Turrell-Croft + */ +public class VariantValidatorForUuid implements ConstraintValidator { + + private int variant; + + @Override + public void initialize(Variant constraintAnnotation) { + + variant = constraintAnnotation.value(); + } + + @Override + public boolean isValid(UUID value, ConstraintValidatorContext context) { + + if (value == null) { + return true; + } + + return value.variant() == variant; + + } + +} diff --git a/xapi-model/src/main/resources/META-INF/services/jakarta.validation.ConstraintValidator b/xapi-model/src/main/resources/META-INF/services/jakarta.validation.ConstraintValidator index cdc68d5f..6f307c80 100644 --- a/xapi-model/src/main/resources/META-INF/services/jakarta.validation.ConstraintValidator +++ b/xapi-model/src/main/resources/META-INF/services/jakarta.validation.ConstraintValidator @@ -1 +1,2 @@ - dev.learning.xapi.model.validation.internal.validators.HasSchemeValidatorForUri \ No newline at end of file +dev.learning.xapi.model.validation.internal.validators.HasSchemeValidatorForUri +dev.learning.xapi.model.validation.internal.validators.VariantValidatorForUuid \ No newline at end of file From c4dd79c6f29821ea5b11fe9d44417054a3ce4f65 Mon Sep 17 00:00:00 2001 From: Thomas Turrell-Croft Date: Tue, 14 Mar 2023 22:30:54 +0000 Subject: [PATCH 04/34] tip --- xapi-model/pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/xapi-model/pom.xml b/xapi-model/pom.xml index ca190d1d..26cb0af9 100644 --- a/xapi-model/pom.xml +++ b/xapi-model/pom.xml @@ -32,6 +32,11 @@ spring-boot-starter-test test + + org.springframework.boot + spring-boot-starter-validation + test + From 9a67a6efd656f660d75dbb0146bba6a8a29d6c37 Mon Sep 17 00:00:00 2001 From: Thomas Turrell-Croft Date: Wed, 15 Mar 2023 00:20:01 +0000 Subject: [PATCH 05/34] Apply suggestions from code review --- xapi-model/src/main/java/dev/learning/xapi/model/About.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/About.java b/xapi-model/src/main/java/dev/learning/xapi/model/About.java index 2765b44a..f73a02ab 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/About.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/About.java @@ -6,7 +6,6 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; -import dev.learning.xapi.model.validation.constraints.HasScheme; import java.net.URI; import java.util.LinkedHashMap; import java.util.List; @@ -30,7 +29,7 @@ public class About { private List version; - private LinkedHashMap<@HasScheme URI, Object> extensions; + private LinkedHashMap extensions; // **Warning** do not add fields that are not required by the xAPI specification. From fd10bdf207058872d9e01b76f467550e87b2709f Mon Sep 17 00:00:00 2001 From: Thomas Turrell-Croft Date: Wed, 15 Mar 2023 10:13:03 +0000 Subject: [PATCH 06/34] tip --- .../learning/xapi/model/validation/constraints/Variant.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/Variant.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/Variant.java index eba167f1..a288cb9a 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/Variant.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/Variant.java @@ -11,7 +11,6 @@ import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.TYPE_USE; import static java.lang.annotation.RetentionPolicy.RUNTIME; - import jakarta.validation.Constraint; import jakarta.validation.Payload; import java.lang.annotation.Documented; @@ -19,7 +18,7 @@ import java.lang.annotation.Target; /** - * The annotated element must have a scheme. + * The annotated UUID must have the specified variant. * * @author Thomas Turrell-Croft */ From 5cf185ed971f1478e3d9427ffabb2473ec1e631e Mon Sep 17 00:00:00 2001 From: Thomas Turrell-Croft Date: Wed, 15 Mar 2023 15:23:32 +0000 Subject: [PATCH 07/34] tip --- .../dev/learning/xapi/model/validation/constraints/Variant.java | 1 + 1 file changed, 1 insertion(+) diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/Variant.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/Variant.java index a288cb9a..1e561a23 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/Variant.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/Variant.java @@ -11,6 +11,7 @@ import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.TYPE_USE; import static java.lang.annotation.RetentionPolicy.RUNTIME; + import jakarta.validation.Constraint; import jakarta.validation.Payload; import java.lang.annotation.Documented; From 88ef3a2e152c10c1df818febfb244feb53bdf2ef Mon Sep 17 00:00:00 2001 From: Thomas Turrell-Croft Date: Wed, 15 Mar 2023 16:17:45 +0000 Subject: [PATCH 08/34] tip --- .../dev/learning/xapi/model/Statement.java | 2 + .../dev/learning/xapi/model/SubStatement.java | 2 + .../constraints/ValidStatement.java | 44 ++++++++ .../validators/ValidatorForStatement.java | 106 ++++++++++++++++++ .../validators/ValidatorForSubStatement.java | 59 ++++++++++ .../jakarta.validation.ConstraintValidator | 4 +- .../learning/xapi/model/StatementTests.java | 1 + 7 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ValidStatement.java create mode 100644 xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ValidatorForStatement.java create mode 100644 xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ValidatorForSubStatement.java diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Statement.java b/xapi-model/src/main/java/dev/learning/xapi/model/Statement.java index 775aa554..651feb57 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/Statement.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/Statement.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; +import dev.learning.xapi.model.validation.constraints.ValidStatement; import dev.learning.xapi.model.validation.constraints.Variant; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; @@ -37,6 +38,7 @@ @JsonIgnoreProperties("inProgress") @JsonInclude(Include.NON_EMPTY) @EqualsAndHashCode(of = {"actor", "verb", "object", "result", "context"}) +@ValidStatement public class Statement { /** diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/SubStatement.java b/xapi-model/src/main/java/dev/learning/xapi/model/SubStatement.java index 176548f5..3229e8c4 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/SubStatement.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/SubStatement.java @@ -4,6 +4,7 @@ package dev.learning.xapi.model; +import dev.learning.xapi.model.validation.constraints.ValidStatement; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import java.time.Instant; @@ -24,6 +25,7 @@ @Value @Builder @EqualsAndHashCode(exclude = {"timestamp", "attachments"}) +@ValidStatement public class SubStatement implements StatementObject { /** diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ValidStatement.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ValidStatement.java new file mode 100644 index 00000000..8d3843eb --- /dev/null +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ValidStatement.java @@ -0,0 +1,44 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.constraints; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * The annotated element must be a valid statement. + * + * @author Thomas Turrell-Croft + * @see Statement + */ +@Documented +@Constraint(validatedBy = {}) +@Target(TYPE) +@Retention(RUNTIME) +public @interface ValidStatement { + + /** + * Error Message. + */ + String message() default "must be a valid statement"; + + /** + * Groups. + */ + Class[] groups() default {}; + + /** + * Payload. + */ + Class[] payload() default {}; + +} diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ValidatorForStatement.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ValidatorForStatement.java new file mode 100644 index 00000000..4ec25049 --- /dev/null +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ValidatorForStatement.java @@ -0,0 +1,106 @@ +/* + * Copyright 2016-2019 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.internal.validators; + +import dev.learning.xapi.model.Activity; +import dev.learning.xapi.model.Agent; +import dev.learning.xapi.model.Context; +import dev.learning.xapi.model.Group; +import dev.learning.xapi.model.Statement; +import dev.learning.xapi.model.StatementReference; +import dev.learning.xapi.model.Verb; +import dev.learning.xapi.model.validation.constraints.ValidStatement; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; + +/** + * The Statement being validated must be valid. + */ +public class ValidatorForStatement implements ConstraintValidator { + + @Override + public boolean isValid(Statement value, ConstraintValidatorContext context) { + + if (value == null) { + // Can not happen with annotation target set to type + return true; + } + + // TODO consider adding conversion logic here + + context.disableDefaultConstraintViolation(); + + return isVoidingVerbValid(value, context) && isValidRevision(value, context) + && isValidPlatform(value, context) && isValidAuthority(value, context); + } + + private boolean isVoidingVerbValid(Statement value, ConstraintValidatorContext context) { + + Verb verb = value.getVerb(); + + if (verb == null || !verb.isVoided() + || (verb.isVoided() && value.getObject() instanceof StatementReference)) { + return true; + } + + context + .buildConstraintViolationWithTemplate( + "invalid voiding statement, object must be of type statement reference") + .addConstraintViolation(); + + return false; + } + + private boolean isValidRevision(Statement value, ConstraintValidatorContext context) { + + Context c = value.getContext(); + + if (c == null || c.getRevision() == null || value.getObject() instanceof Activity) { + return true; + } + + context.buildConstraintViolationWithTemplate( + "invalid revision property, object must of type activity").addConstraintViolation(); + + return false; + } + + private boolean isValidPlatform(Statement value, ConstraintValidatorContext context) { + + Context c = value.getContext(); + + if (c == null || c.getPlatform() == null || value.getObject() instanceof Activity) { + return true; + } + + context.buildConstraintViolationWithTemplate( + "invalid platform property, object must be of type activity").addConstraintViolation(); + + return false; + } + + private boolean isValidAuthority(Statement value, ConstraintValidatorContext context) { + + // can be null or Agent + if (value.getAuthority() == null || value.getAuthority() instanceof Agent) { + return true; + } + + final var group = (Group) value.getAuthority(); + // ... or must be an anonymous Group with exactly two members + if (group.getAccount() == null && group.getMbox() == null && group.getMboxSha1sum() == null + && group.getOpenid() == null && group.getMember() != null + && group.getMember().size() == 2) { + return true; + + } + + context.buildConstraintViolationWithTemplate("invalid authority property") + .addConstraintViolation(); + + return false; + } + +} diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ValidatorForSubStatement.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ValidatorForSubStatement.java new file mode 100644 index 00000000..92903357 --- /dev/null +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ValidatorForSubStatement.java @@ -0,0 +1,59 @@ +/* + * Copyright 2016-2019 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.internal.validators; + +import dev.learning.xapi.model.Activity; +import dev.learning.xapi.model.Context; +import dev.learning.xapi.model.SubStatement; +import dev.learning.xapi.model.validation.constraints.ValidStatement; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; + +/** + * The SubStatement being validated must be valid. + */ +public class ValidatorForSubStatement implements ConstraintValidator { + + @Override + public boolean isValid(SubStatement value, ConstraintValidatorContext context) { + + if (value == null) { + return true; + } + + context.disableDefaultConstraintViolation(); + + return isValidRevision(value, context) && isValidPlatform(value, context); + } + + private boolean isValidRevision(SubStatement value, ConstraintValidatorContext context) { + + Context c = value.getContext(); + + if (c == null || c.getRevision() == null || value.getObject() instanceof Activity) { + return true; + } + + context.buildConstraintViolationWithTemplate( + "invalid revision property, object must of type activity").addConstraintViolation(); + + return false; + } + + private boolean isValidPlatform(SubStatement value, ConstraintValidatorContext context) { + + Context c = value.getContext(); + + if (c == null || c.getPlatform() == null || value.getObject() instanceof Activity) { + return true; + } + + context.buildConstraintViolationWithTemplate( + "invalid platform property, object must be of type activity").addConstraintViolation(); + + return false; + } + +} diff --git a/xapi-model/src/main/resources/META-INF/services/jakarta.validation.ConstraintValidator b/xapi-model/src/main/resources/META-INF/services/jakarta.validation.ConstraintValidator index 6f307c80..a095133a 100644 --- a/xapi-model/src/main/resources/META-INF/services/jakarta.validation.ConstraintValidator +++ b/xapi-model/src/main/resources/META-INF/services/jakarta.validation.ConstraintValidator @@ -1,2 +1,4 @@ dev.learning.xapi.model.validation.internal.validators.HasSchemeValidatorForUri -dev.learning.xapi.model.validation.internal.validators.VariantValidatorForUuid \ No newline at end of file +dev.learning.xapi.model.validation.internal.validators.VariantValidatorForUuid +dev.learning.xapi.model.validation.internal.validators.ValidatorForSubStatement +dev.learning.xapi.model.validation.internal.validators.ValidatorForStatement \ No newline at end of file diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/StatementTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/StatementTests.java index e3fe087b..0fc125f8 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/StatementTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/StatementTests.java @@ -8,6 +8,7 @@ import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; + import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.validation.ConstraintViolation; From c3f81d9cc1ac230bc210bed8992fce2b6b33b96d Mon Sep 17 00:00:00 2001 From: Thomas Turrell-Croft Date: Thu, 16 Mar 2023 00:31:49 +0000 Subject: [PATCH 09/34] tip --- .../java/dev/learning/xapi/model/Group.java | 6 ++ .../dev/learning/xapi/model/Statement.java | 4 +- .../dev/learning/xapi/model/SubStatement.java | 2 - ...alidStatement.java => ValidAuthority.java} | 17 ++-- .../validators/AuthorityValidator.java | 40 +++++++++ .../validators/ValidatorForStatement.java | 30 +------ .../validators/ValidatorForSubStatement.java | 4 +- .../validators/AuthorityValidatorTests.java | 89 +++++++++++++++++++ .../HasSchemeValidatorForUriTests.java | 33 +++++++ 9 files changed, 185 insertions(+), 40 deletions(-) rename xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/{ValidStatement.java => ValidAuthority.java} (52%) create mode 100644 xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/AuthorityValidator.java create mode 100644 xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/AuthorityValidatorTests.java create mode 100644 xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/HasSchemeValidatorForUriTests.java diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Group.java b/xapi-model/src/main/java/dev/learning/xapi/model/Group.java index 15f61da9..ca384fb6 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/Group.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/Group.java @@ -38,6 +38,12 @@ public class Group extends Actor { // **Warning** do not add fields that are not required by the xAPI specification. + public boolean isAnonymous() { + + return account == null && mbox == null && mboxSha1sum == null && openid == null + && member != null && !member.isEmpty(); + } + /** * Builder for Group. */ diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Statement.java b/xapi-model/src/main/java/dev/learning/xapi/model/Statement.java index 651feb57..8a16c8c5 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/Statement.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/Statement.java @@ -8,7 +8,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; -import dev.learning.xapi.model.validation.constraints.ValidStatement; +import dev.learning.xapi.model.validation.constraints.ValidAuthority; import dev.learning.xapi.model.validation.constraints.Variant; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; @@ -38,7 +38,6 @@ @JsonIgnoreProperties("inProgress") @JsonInclude(Include.NON_EMPTY) @EqualsAndHashCode(of = {"actor", "verb", "object", "result", "context"}) -@ValidStatement public class Statement { /** @@ -93,6 +92,7 @@ public class Statement { /** * Agent or Group who is asserting this Statement is true. */ + @ValidAuthority private Actor authority; /** diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/SubStatement.java b/xapi-model/src/main/java/dev/learning/xapi/model/SubStatement.java index 3229e8c4..176548f5 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/SubStatement.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/SubStatement.java @@ -4,7 +4,6 @@ package dev.learning.xapi.model; -import dev.learning.xapi.model.validation.constraints.ValidStatement; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import java.time.Instant; @@ -25,7 +24,6 @@ @Value @Builder @EqualsAndHashCode(exclude = {"timestamp", "attachments"}) -@ValidStatement public class SubStatement implements StatementObject { /** diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ValidStatement.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ValidAuthority.java similarity index 52% rename from xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ValidStatement.java rename to xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ValidAuthority.java index 8d3843eb..ef54bf1c 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ValidStatement.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ValidAuthority.java @@ -4,7 +4,12 @@ package dev.learning.xapi.model.validation.constraints; -import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; import static java.lang.annotation.RetentionPolicy.RUNTIME; import jakarta.validation.Constraint; @@ -14,22 +19,20 @@ import java.lang.annotation.Target; /** - * The annotated element must be a valid statement. + * The annotated element must have a scheme. * * @author Thomas Turrell-Croft - * @see Statement */ @Documented @Constraint(validatedBy = {}) -@Target(TYPE) +@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) @Retention(RUNTIME) -public @interface ValidStatement { +public @interface ValidAuthority { /** * Error Message. */ - String message() default "must be a valid statement"; + String message() default "must be of type agent or a group with two agents"; /** * Groups. diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/AuthorityValidator.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/AuthorityValidator.java new file mode 100644 index 00000000..5c2fb270 --- /dev/null +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/AuthorityValidator.java @@ -0,0 +1,40 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.internal.validators; + +import dev.learning.xapi.model.Actor; +import dev.learning.xapi.model.Agent; +import dev.learning.xapi.model.Group; +import dev.learning.xapi.model.validation.constraints.ValidAuthority; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; + +/** + * The Actor being validated must be null, an agent or an anonymous group with two agents. + * + * @author István Rátkai (Selindek) + * @author Thomas Turrell-Croft + */ +public class AuthorityValidator implements ConstraintValidator { + + @Override + public boolean isValid(Actor value, ConstraintValidatorContext context) { + + // can be null or Agent + if (value == null || value instanceof Agent) { + return true; + } + + final var group = (Group) value; + // ... or must be an anonymous Group with exactly two members + if (group.isAnonymous() && group.getMember().size() == 2) { + return true; + } + + return false; + + } + +} diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ValidatorForStatement.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ValidatorForStatement.java index 4ec25049..eff78359 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ValidatorForStatement.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ValidatorForStatement.java @@ -5,20 +5,18 @@ package dev.learning.xapi.model.validation.internal.validators; import dev.learning.xapi.model.Activity; -import dev.learning.xapi.model.Agent; import dev.learning.xapi.model.Context; -import dev.learning.xapi.model.Group; import dev.learning.xapi.model.Statement; import dev.learning.xapi.model.StatementReference; import dev.learning.xapi.model.Verb; -import dev.learning.xapi.model.validation.constraints.ValidStatement; import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Valid; /** * The Statement being validated must be valid. */ -public class ValidatorForStatement implements ConstraintValidator { +public class ValidatorForStatement implements ConstraintValidator { @Override public boolean isValid(Statement value, ConstraintValidatorContext context) { @@ -33,7 +31,7 @@ public boolean isValid(Statement value, ConstraintValidatorContext context) { context.disableDefaultConstraintViolation(); return isVoidingVerbValid(value, context) && isValidRevision(value, context) - && isValidPlatform(value, context) && isValidAuthority(value, context); + && isValidPlatform(value, context); } private boolean isVoidingVerbValid(Statement value, ConstraintValidatorContext context) { @@ -81,26 +79,4 @@ private boolean isValidPlatform(Statement value, ConstraintValidatorContext cont return false; } - private boolean isValidAuthority(Statement value, ConstraintValidatorContext context) { - - // can be null or Agent - if (value.getAuthority() == null || value.getAuthority() instanceof Agent) { - return true; - } - - final var group = (Group) value.getAuthority(); - // ... or must be an anonymous Group with exactly two members - if (group.getAccount() == null && group.getMbox() == null && group.getMboxSha1sum() == null - && group.getOpenid() == null && group.getMember() != null - && group.getMember().size() == 2) { - return true; - - } - - context.buildConstraintViolationWithTemplate("invalid authority property") - .addConstraintViolation(); - - return false; - } - } diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ValidatorForSubStatement.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ValidatorForSubStatement.java index 92903357..3659eb12 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ValidatorForSubStatement.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ValidatorForSubStatement.java @@ -7,14 +7,14 @@ import dev.learning.xapi.model.Activity; import dev.learning.xapi.model.Context; import dev.learning.xapi.model.SubStatement; -import dev.learning.xapi.model.validation.constraints.ValidStatement; import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Valid; /** * The SubStatement being validated must be valid. */ -public class ValidatorForSubStatement implements ConstraintValidator { +public class ValidatorForSubStatement implements ConstraintValidator { @Override public boolean isValid(SubStatement value, ConstraintValidatorContext context) { diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/AuthorityValidatorTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/AuthorityValidatorTests.java new file mode 100644 index 00000000..e3b54b38 --- /dev/null +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/AuthorityValidatorTests.java @@ -0,0 +1,89 @@ +package dev.learning.xapi.model.validation.internal.validators; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; + +import dev.learning.xapi.model.Agent; +import dev.learning.xapi.model.Group; +import org.junit.jupiter.api.Test; + +class AuthorityValidatorTests { + + private AuthorityValidator constraintValidator = new AuthorityValidator(); + + @Test + void whenCallingIsValidWithNullThenResultIsTrue() { + + // When Calling Is Valid With Null + boolean result = constraintValidator.isValid(null, null); + + // Then Result Is True + assertThat(result, is(true)); + } + + @Test + void whenCallingIsValidWithAgentThenResultIsTrue() { + + Agent agent = Agent.builder().name("A N Other").mbox("mailto:another@example.com").build(); + + // When Calling Is Valid With Agent + boolean result = constraintValidator.isValid(agent, null); + + // Then Result Is True + assertThat(result, is(true)); + } + + @Test + void whenCallingIsValidWithIdentifiedGroupThenResultIsFalse() { + + Group group = Group.builder().name("A N Other").mbox("mailto:another@example.com").build(); + + // When Calling Is Valid With Identified Group + boolean result = constraintValidator.isValid(group, null); + + // Then Result Is False + assertThat(result, is(false)); + } + + @Test + void whenCallingIsValidWithIdentifiedGroupWithMembersThenResultIsFalse() { + + Group group = Group.builder().name("A N Other").mbox("mailto:another@example.com") + .addMember(m -> m.name("A N Other").mbox("mailto:another@example.com")).build(); + + // When Calling Is Valid With Identified Group + boolean result = constraintValidator.isValid(group, null); + + // Then Result Is False + assertThat(result, is(false)); + } + + @Test + void whenCallingIsValidWithGroupWithOneMemberThenResultIsFalse() { + + Group group = Group.builder() + .addMember(m -> m.name("A N Other").mbox("mailto:another@example.com")).build(); + + // When Calling Is Valid With Group With One Member + boolean result = constraintValidator.isValid(group, null); + + // Then Result Is False + assertThat(result, is(false)); + } + + @Test + void whenCallingIsValidWithGroupWithTwoMembersThenResultIsTrue() { + + Group group = + Group.builder().addMember(m -> m.name("A N Other").mbox("mailto:another@example.com")) + .addMember(m -> m.name("A N Other").mbox("mailto:another@example.com")).build(); + + // When Calling IsValid With Group With Two Members + boolean result = constraintValidator.isValid(group, null); + + // Then Result Is True + assertThat(result, is(true)); + + } + +} diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/HasSchemeValidatorForUriTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/HasSchemeValidatorForUriTests.java new file mode 100644 index 00000000..880592d9 --- /dev/null +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/HasSchemeValidatorForUriTests.java @@ -0,0 +1,33 @@ +package dev.learning.xapi.model.validation.internal.validators; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; + +import java.net.URI; +import org.junit.jupiter.api.Test; + +class HasSchemeValidatorForUriTests { + + private HasSchemeValidatorForUri constraintValidator = new HasSchemeValidatorForUri(); + + @Test + void whenCallingIsValidWithURIWithoutSchemeThenResultIsFalse() { + + // When Calling IsValid With URI Without Scheme + boolean valid = constraintValidator.isValid(URI.create("example.com"), null); + + // Then Result Is False + assertThat(valid, is(false)); + } + + @Test + void whenCallingIsValidWithURIWithSchemeThenResultIsTrue() { + + // When Calling IsValid With URI With Scheme + boolean result = constraintValidator.isValid(URI.create("https://example.com"), null); + + // Then Result Is True + assertThat(result, is(true)); + } + +} From bfeb10cb1af2512144b1e815ba88e8ba292e152f Mon Sep 17 00:00:00 2001 From: Thomas Turrell-Croft Date: Thu, 16 Mar 2023 16:45:04 +0000 Subject: [PATCH 10/34] tip --- .../java/dev/learning/xapi/model/Group.java | 2 + .../dev/learning/xapi/model/GroupTests.java | 94 +++++++++++++++++++ .../validators/AuthorityValidatorTests.java | 4 + .../HasSchemeValidatorForUriTests.java | 4 + 4 files changed, 104 insertions(+) diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Group.java b/xapi-model/src/main/java/dev/learning/xapi/model/Group.java index ca384fb6..671c6a38 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/Group.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/Group.java @@ -5,6 +5,7 @@ package dev.learning.xapi.model; import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.validation.Valid; import java.util.ArrayList; import java.util.List; @@ -38,6 +39,7 @@ public class Group extends Actor { // **Warning** do not add fields that are not required by the xAPI specification. + @JsonIgnore public boolean isAnonymous() { return account == null && mbox == null && mboxSha1sum == null && openid == null diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/GroupTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/GroupTests.java index f706aa70..2fa35142 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/GroupTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/GroupTests.java @@ -7,6 +7,9 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.File; @@ -106,4 +109,95 @@ void whenCallingToStringThenResultIsExpected() throws IOException { } + @Test + void givenGroupWithNameAndMembersWhenCallingIsAnonymousThenResultIsTrue() throws IOException { + + // Given Group With Name And Members + final Group group = Group.builder() + + .name("Example Group") + + .addMember(a -> a.name("Member 1").mbox("mailto:member1@example.com")) + + .addMember(a -> a.name("Member 2").openid(URI.create("https://example.com/openId"))) + + .build(); + + // When Calling Is Anonymous + boolean result = group.isAnonymous(); + + // Then Result Is True + assertTrue(result); + + } + + @Test + void givenGroupWithMboxAndMembersWhenCallingIsAnonymousThenResultIsFalse() throws IOException { + + // Given Group With MBox And Members + final Group group = Group.builder() + + .mbox("mailto:another@example.com") + + .addMember(a -> a.name("Member 1").mbox("mailto:member1@example.com")) + + .addMember(a -> a.name("Member 2").openid(URI.create("https://example.com/openId"))) + + .build(); + + // When Calling Is Anonymous + boolean result = group.isAnonymous(); + + // Then Result Is False + assertFalse(result); + + } + + @Test + void givenGroupWithMboxSha1sumAndMembersWhenCallingIsAnonymousThenResultIsFalse() + throws IOException { + + // Given Group With MboxSha1sum And Members + final Group group = Group.builder() + + .mboxSha1sum("mailto:another@example.com") + + .addMember(a -> a.name("Member 1").mbox("mailto:member1@example.com")) + + .addMember(a -> a.name("Member 2").openid(URI.create("https://example.com/openId"))) + + .build(); + + // When Calling Is Anonymous + boolean result = group.isAnonymous(); + + // Then Result Is False + assertFalse(result); + + } + + @Test + void givenGroupWithAccountAndMembersWhenCallingIsAnonymousThenResultIsFalse() throws IOException { + + // Given Group With Account And Members + final Group group = Group.builder() + + .addMember(a -> a.name("Member 1").mbox("mailto:member1@example.com")) + + .addMember(a -> a.name("Member 2").openid(URI.create("https://example.com/openId"))) + + .account(a -> a.name("name").homePage(URI.create("https://example.com"))) + + .build(); + + // When Calling Is Anonymous + boolean result = group.isAnonymous(); + + // Then Result Is False + assertFalse(result); + + } + + + } diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/AuthorityValidatorTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/AuthorityValidatorTests.java index e3b54b38..1bdfaff2 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/AuthorityValidatorTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/AuthorityValidatorTests.java @@ -1,3 +1,7 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + package dev.learning.xapi.model.validation.internal.validators; import static org.hamcrest.MatcherAssert.assertThat; diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/HasSchemeValidatorForUriTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/HasSchemeValidatorForUriTests.java index 880592d9..d54336ab 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/HasSchemeValidatorForUriTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/HasSchemeValidatorForUriTests.java @@ -1,3 +1,7 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + package dev.learning.xapi.model.validation.internal.validators; import static org.hamcrest.MatcherAssert.assertThat; From a36e55dcbb78a2dd4c98526a4729db6ab856e12a Mon Sep 17 00:00:00 2001 From: Thomas Turrell-Croft Date: Thu, 16 Mar 2023 16:53:53 +0000 Subject: [PATCH 11/34] tip --- .../dev/learning/xapi/model/GroupTests.java | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/GroupTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/GroupTests.java index 2fa35142..9ef55caa 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/GroupTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/GroupTests.java @@ -15,6 +15,7 @@ import java.io.File; import java.io.IOException; import java.net.URI; +import java.util.ArrayList; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.util.ResourceUtils; @@ -131,6 +132,47 @@ void givenGroupWithNameAndMembersWhenCallingIsAnonymousThenResultIsTrue() throws } + @Test + void givenGroupWithNameAndNoMembersWhenCallingIsAnonymousThenResultIsFalse() throws IOException { + + // Given Group With Name And Empty Members + final Group group = Group.builder() + + .name("Example Group") + + .member(new ArrayList()) + + .build(); + + // When Calling Is Anonymous + boolean result = group.isAnonymous(); + + // Then Result Is False + assertFalse(result); + + } + + @Test + void givenGroupWithNameAndNullMembersWhenCallingIsAnonymousThenResultIsFalse() + throws IOException { + + // Given Group With Name And Null Members + final Group group = Group.builder() + + .name("Example Group") + + .member(null) + + .build(); + + // When Calling Is Anonymous + boolean result = group.isAnonymous(); + + // Then Result Is False + assertFalse(result); + + } + @Test void givenGroupWithMboxAndMembersWhenCallingIsAnonymousThenResultIsFalse() throws IOException { @@ -176,6 +218,30 @@ void givenGroupWithMboxSha1sumAndMembersWhenCallingIsAnonymousThenResultIsFalse( } + @Test + void givenGroupWithOpenIDAndMembersWhenCallingIsAnonymousThenResultIsFalse() throws IOException { + + // Given Group With OpenID And Members + final Group group = Group.builder() + + .openid(URI.create("https://example.com")) + + .addMember(a -> a.name("Member 1").mbox("mailto:member1@example.com")) + + .addMember(a -> a.name("Member 2").openid(URI.create("https://example.com/openId"))) + + .build(); + + // When Calling Is Anonymous + boolean result = group.isAnonymous(); + + // Then Result Is False + assertFalse(result); + + } + + + @Test void givenGroupWithAccountAndMembersWhenCallingIsAnonymousThenResultIsFalse() throws IOException { From 5efc061ec6d39107ffa353ef98ebe0e5e3b885be Mon Sep 17 00:00:00 2001 From: Thomas Turrell-Croft Date: Thu, 16 Mar 2023 18:06:01 +0000 Subject: [PATCH 12/34] tip --- xapi-model/src/main/java/dev/learning/xapi/model/Group.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Group.java b/xapi-model/src/main/java/dev/learning/xapi/model/Group.java index 671c6a38..676ae0bb 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/Group.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/Group.java @@ -39,6 +39,9 @@ public class Group extends Actor { // **Warning** do not add fields that are not required by the xAPI specification. + /** + * Returns true if the group is anonymous. + */ @JsonIgnore public boolean isAnonymous() { From b888404041ce676f73b2553efa72c39c473d2706 Mon Sep 17 00:00:00 2001 From: Thomas Turrell-Croft Date: Thu, 16 Mar 2023 18:10:07 +0000 Subject: [PATCH 13/34] tip fix --- .../META-INF/services/jakarta.validation.ConstraintValidator | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/xapi-model/src/main/resources/META-INF/services/jakarta.validation.ConstraintValidator b/xapi-model/src/main/resources/META-INF/services/jakarta.validation.ConstraintValidator index a095133a..3b87ca48 100644 --- a/xapi-model/src/main/resources/META-INF/services/jakarta.validation.ConstraintValidator +++ b/xapi-model/src/main/resources/META-INF/services/jakarta.validation.ConstraintValidator @@ -1,4 +1,3 @@ +dev.learning.xapi.model.validation.internal.validators.AuthorityValidator dev.learning.xapi.model.validation.internal.validators.HasSchemeValidatorForUri dev.learning.xapi.model.validation.internal.validators.VariantValidatorForUuid -dev.learning.xapi.model.validation.internal.validators.ValidatorForSubStatement -dev.learning.xapi.model.validation.internal.validators.ValidatorForStatement \ No newline at end of file From 606c59a2df1c41eeaea15e132f2b344b2f323265 Mon Sep 17 00:00:00 2001 From: Thomas Turrell-Croft Date: Thu, 16 Mar 2023 19:07:05 +0000 Subject: [PATCH 14/34] Update AuthorityValidator.java --- .../validation/internal/validators/AuthorityValidator.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/AuthorityValidator.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/AuthorityValidator.java index 5c2fb270..c732bdae 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/AuthorityValidator.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/AuthorityValidator.java @@ -29,11 +29,7 @@ public boolean isValid(Actor value, ConstraintValidatorContext context) { final var group = (Group) value; // ... or must be an anonymous Group with exactly two members - if (group.isAnonymous() && group.getMember().size() == 2) { - return true; - } - - return false; + return group.isAnonymous() && group.getMember().size() == 2); } From 271976da73de864f2a854a87ab6d79517849fd01 Mon Sep 17 00:00:00 2001 From: Thomas Turrell-Croft Date: Thu, 16 Mar 2023 19:15:23 +0000 Subject: [PATCH 15/34] Update xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/AuthorityValidator.java --- .../validation/internal/validators/AuthorityValidator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/AuthorityValidator.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/AuthorityValidator.java index c732bdae..b3804ecc 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/AuthorityValidator.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/AuthorityValidator.java @@ -29,7 +29,7 @@ public boolean isValid(Actor value, ConstraintValidatorContext context) { final var group = (Group) value; // ... or must be an anonymous Group with exactly two members - return group.isAnonymous() && group.getMember().size() == 2); + return group.isAnonymous() && group.getMember().size() == 2; } From f16325f79dfc50bd664df0ab999c163c3377740b Mon Sep 17 00:00:00 2001 From: Thomas Turrell-Croft Date: Thu, 16 Mar 2023 23:27:16 +0000 Subject: [PATCH 16/34] tip --- .../validators/ValidatorForStatement.java | 82 ------------------- .../validators/ValidatorForSubStatement.java | 59 ------------- 2 files changed, 141 deletions(-) delete mode 100644 xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ValidatorForStatement.java delete mode 100644 xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ValidatorForSubStatement.java diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ValidatorForStatement.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ValidatorForStatement.java deleted file mode 100644 index eff78359..00000000 --- a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ValidatorForStatement.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2016-2019 Berry Cloud Ltd. All rights reserved. - */ - -package dev.learning.xapi.model.validation.internal.validators; - -import dev.learning.xapi.model.Activity; -import dev.learning.xapi.model.Context; -import dev.learning.xapi.model.Statement; -import dev.learning.xapi.model.StatementReference; -import dev.learning.xapi.model.Verb; -import jakarta.validation.ConstraintValidator; -import jakarta.validation.ConstraintValidatorContext; -import jakarta.validation.Valid; - -/** - * The Statement being validated must be valid. - */ -public class ValidatorForStatement implements ConstraintValidator { - - @Override - public boolean isValid(Statement value, ConstraintValidatorContext context) { - - if (value == null) { - // Can not happen with annotation target set to type - return true; - } - - // TODO consider adding conversion logic here - - context.disableDefaultConstraintViolation(); - - return isVoidingVerbValid(value, context) && isValidRevision(value, context) - && isValidPlatform(value, context); - } - - private boolean isVoidingVerbValid(Statement value, ConstraintValidatorContext context) { - - Verb verb = value.getVerb(); - - if (verb == null || !verb.isVoided() - || (verb.isVoided() && value.getObject() instanceof StatementReference)) { - return true; - } - - context - .buildConstraintViolationWithTemplate( - "invalid voiding statement, object must be of type statement reference") - .addConstraintViolation(); - - return false; - } - - private boolean isValidRevision(Statement value, ConstraintValidatorContext context) { - - Context c = value.getContext(); - - if (c == null || c.getRevision() == null || value.getObject() instanceof Activity) { - return true; - } - - context.buildConstraintViolationWithTemplate( - "invalid revision property, object must of type activity").addConstraintViolation(); - - return false; - } - - private boolean isValidPlatform(Statement value, ConstraintValidatorContext context) { - - Context c = value.getContext(); - - if (c == null || c.getPlatform() == null || value.getObject() instanceof Activity) { - return true; - } - - context.buildConstraintViolationWithTemplate( - "invalid platform property, object must be of type activity").addConstraintViolation(); - - return false; - } - -} diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ValidatorForSubStatement.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ValidatorForSubStatement.java deleted file mode 100644 index 3659eb12..00000000 --- a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ValidatorForSubStatement.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2016-2019 Berry Cloud Ltd. All rights reserved. - */ - -package dev.learning.xapi.model.validation.internal.validators; - -import dev.learning.xapi.model.Activity; -import dev.learning.xapi.model.Context; -import dev.learning.xapi.model.SubStatement; -import jakarta.validation.ConstraintValidator; -import jakarta.validation.ConstraintValidatorContext; -import jakarta.validation.Valid; - -/** - * The SubStatement being validated must be valid. - */ -public class ValidatorForSubStatement implements ConstraintValidator { - - @Override - public boolean isValid(SubStatement value, ConstraintValidatorContext context) { - - if (value == null) { - return true; - } - - context.disableDefaultConstraintViolation(); - - return isValidRevision(value, context) && isValidPlatform(value, context); - } - - private boolean isValidRevision(SubStatement value, ConstraintValidatorContext context) { - - Context c = value.getContext(); - - if (c == null || c.getRevision() == null || value.getObject() instanceof Activity) { - return true; - } - - context.buildConstraintViolationWithTemplate( - "invalid revision property, object must of type activity").addConstraintViolation(); - - return false; - } - - private boolean isValidPlatform(SubStatement value, ConstraintValidatorContext context) { - - Context c = value.getContext(); - - if (c == null || c.getPlatform() == null || value.getObject() instanceof Activity) { - return true; - } - - context.buildConstraintViolationWithTemplate( - "invalid platform property, object must be of type activity").addConstraintViolation(); - - return false; - } - -} From 26c99c1d8c2f8e0814d5ed73e7b5bf63b3be8a0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20R=C3=A1tkai?= Date: Fri, 17 Mar 2023 10:20:18 +0000 Subject: [PATCH 17/34] Add Mbox, AllowedLocale and ScaledScore validators (#86) --- xapi-model/pom.xml | 5 + .../dev/learning/xapi/model/Activity.java | 2 + .../learning/xapi/model/ActivityState.java | 2 + .../java/dev/learning/xapi/model/Actor.java | 2 + .../dev/learning/xapi/model/AgentProfile.java | 2 + .../dev/learning/xapi/model/Attachment.java | 4 +- .../java/dev/learning/xapi/model/Context.java | 5 + .../java/dev/learning/xapi/model/Group.java | 3 +- .../java/dev/learning/xapi/model/Person.java | 3 +- .../java/dev/learning/xapi/model/Score.java | 2 + .../dev/learning/xapi/model/Statement.java | 4 + .../dev/learning/xapi/model/SubStatement.java | 3 + .../model/validation/constraints/Mbox.java | 47 ++++ .../constraints/NotUndetermined.java | 48 ++++ .../validation/constraints/ScaledScore.java | 47 ++++ .../constraints/ValidActivityDefinition.java | 49 ++++ .../validation/constraints/ValidActor.java | 49 ++++ .../ActivityDefinitionValidator.java | 36 +++ .../internal/validators/ActorValidator.java | 63 +++++ .../internal/validators/MboxValidator.java | 42 +++ .../validators/NotUndeterminedValidator.java | 30 +++ .../validators/ScaledScoreValidator.java | 29 +++ .../jakarta.validation.ConstraintValidator | 5 + .../ActivityDefinitionValidatorTest.java | 122 +++++++++ .../validators/ActorValidatorTest.java | 246 ++++++++++++++++++ .../validators/MboxValidatorTest.java | 103 ++++++++ .../NotUndeterminedValidatorTest.java | 54 ++++ .../validators/ScaledScoreValidatorTest.java | 93 +++++++ 28 files changed, 1096 insertions(+), 4 deletions(-) create mode 100644 xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/Mbox.java create mode 100644 xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/NotUndetermined.java create mode 100644 xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ScaledScore.java create mode 100644 xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ValidActivityDefinition.java create mode 100644 xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ValidActor.java create mode 100644 xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ActivityDefinitionValidator.java create mode 100644 xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ActorValidator.java create mode 100644 xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/MboxValidator.java create mode 100644 xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/NotUndeterminedValidator.java create mode 100644 xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ScaledScoreValidator.java create mode 100644 xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActivityDefinitionValidatorTest.java create mode 100644 xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActorValidatorTest.java create mode 100644 xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/MboxValidatorTest.java create mode 100644 xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/NotUndeterminedValidatorTest.java create mode 100644 xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ScaledScoreValidatorTest.java diff --git a/xapi-model/pom.xml b/xapi-model/pom.xml index 26cb0af9..3e032a76 100644 --- a/xapi-model/pom.xml +++ b/xapi-model/pom.xml @@ -37,6 +37,11 @@ spring-boot-starter-validation test + + org.hibernate.validator + hibernate-validator + provided + diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Activity.java b/xapi-model/src/main/java/dev/learning/xapi/model/Activity.java index 6e7ead1d..87e33e2c 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/Activity.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/Activity.java @@ -4,6 +4,7 @@ package dev.learning.xapi.model; +import dev.learning.xapi.model.validation.constraints.ValidActivityDefinition; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import java.net.URI; @@ -38,6 +39,7 @@ public class Activity implements StatementObject, SubStatementObject { * Metadata. */ @Valid + @ValidActivityDefinition private ActivityDefinition definition; // **Warning** do not add fields that are not required by the xAPI specification. diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/ActivityState.java b/xapi-model/src/main/java/dev/learning/xapi/model/ActivityState.java index 8d4ee606..a70c6269 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/ActivityState.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/ActivityState.java @@ -7,6 +7,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.databind.node.ObjectNode; +import dev.learning.xapi.model.validation.constraints.ValidActor; import dev.learning.xapi.model.validation.constraints.Variant; import jakarta.validation.Valid; import java.util.UUID; @@ -31,6 +32,7 @@ public class ActivityState { private String stateId; @Valid + @ValidActor private Agent agent; @Variant(2) diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Actor.java b/xapi-model/src/main/java/dev/learning/xapi/model/Actor.java index 9da1bedf..9a767aad 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/Actor.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/Actor.java @@ -10,6 +10,7 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import dev.learning.xapi.model.validation.constraints.Mbox; import jakarta.validation.Valid; import java.net.URI; import java.util.function.Consumer; @@ -48,6 +49,7 @@ public abstract class Actor implements StatementObject, SubStatementObject { /** * An email address. The required format is "mailto:email address". */ + @Mbox String mbox; /** diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/AgentProfile.java b/xapi-model/src/main/java/dev/learning/xapi/model/AgentProfile.java index 46df4a3d..5b88f93f 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/AgentProfile.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/AgentProfile.java @@ -7,6 +7,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.databind.node.ObjectNode; +import dev.learning.xapi.model.validation.constraints.ValidActor; import jakarta.validation.Valid; import lombok.Builder; import lombok.Value; @@ -25,6 +26,7 @@ public class AgentProfile { @Valid + @ValidActor private Agent agent; private String profileId; diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Attachment.java b/xapi-model/src/main/java/dev/learning/xapi/model/Attachment.java index d7191459..48dfb75b 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/Attachment.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/Attachment.java @@ -86,7 +86,7 @@ public static class Builder { * * @return This builder * - * @see ActivityDefinition#description + * @see Attachment#description */ public Builder addDisplay(Locale key, String value) { @@ -106,7 +106,7 @@ public Builder addDisplay(Locale key, String value) { * * @return This builder * - * @see ActivityDefinition#description + * @see Attachment#description */ public Builder addDescription(Locale key, String value) { if (this.description == null) { diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Context.java b/xapi-model/src/main/java/dev/learning/xapi/model/Context.java index a64d7e85..8b4fd44d 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/Context.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/Context.java @@ -7,6 +7,8 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import dev.learning.xapi.model.validation.constraints.HasScheme; +import dev.learning.xapi.model.validation.constraints.NotUndetermined; +import dev.learning.xapi.model.validation.constraints.ValidActor; import dev.learning.xapi.model.validation.constraints.Variant; import jakarta.validation.Valid; import java.net.URI; @@ -41,12 +43,14 @@ public class Context { * Instructor that the Statement relates to, if not included as the Actor of the Statement. */ @Valid + @ValidActor private Actor instructor; /** * Team that this Statement relates to, if not included as the Actor of the Statement. */ @Valid + @ValidActor private Group team; /** @@ -68,6 +72,7 @@ public class Context { /** * The language in which the experience being recorded in this Statement (mainly) occurred in. */ + @NotUndetermined private Locale language; /** diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Group.java b/xapi-model/src/main/java/dev/learning/xapi/model/Group.java index 676ae0bb..6b2e5715 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/Group.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/Group.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnore; +import dev.learning.xapi.model.validation.constraints.ValidActor; import jakarta.validation.Valid; import java.util.ArrayList; import java.util.List; @@ -35,7 +36,7 @@ public class Group extends Actor { */ @Valid @JsonFormat(without = {JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY}) - private final List member; + private final List<@ValidActor Agent> member; // **Warning** do not add fields that are not required by the xAPI specification. diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Person.java b/xapi-model/src/main/java/dev/learning/xapi/model/Person.java index 936e380c..68522a42 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/Person.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/Person.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; +import dev.learning.xapi.model.validation.constraints.Mbox; import jakarta.validation.Valid; import java.net.URI; import java.util.ArrayList; @@ -43,7 +44,7 @@ public class Person { /** * List of e-mail addresses. */ - private List mbox; + private List<@Mbox String> mbox; /** * List of the SHA1 hashes of mailto IRIs. diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Score.java b/xapi-model/src/main/java/dev/learning/xapi/model/Score.java index ad4f878b..1dafbff6 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/Score.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/Score.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; +import dev.learning.xapi.model.validation.constraints.ScaledScore; import lombok.Builder; import lombok.Value; @@ -25,6 +26,7 @@ public class Score { /** * The score related to the experience as modified by scaling and/or normalization. */ + @ScaledScore private Float scaled; /** diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Statement.java b/xapi-model/src/main/java/dev/learning/xapi/model/Statement.java index 8a16c8c5..8608e348 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/Statement.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/Statement.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; +import dev.learning.xapi.model.validation.constraints.ValidActor; import dev.learning.xapi.model.validation.constraints.ValidAuthority; import dev.learning.xapi.model.validation.constraints.Variant; import jakarta.validation.Valid; @@ -51,6 +52,7 @@ public class Statement { */ @NotNull @Valid + @ValidActor private Actor actor; /** @@ -65,6 +67,7 @@ public class Statement { */ @NotNull @Valid + @ValidActor private StatementObject object; /** @@ -92,6 +95,7 @@ public class Statement { /** * Agent or Group who is asserting this Statement is true. */ + @ValidActor @ValidAuthority private Actor authority; diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/SubStatement.java b/xapi-model/src/main/java/dev/learning/xapi/model/SubStatement.java index 176548f5..f03b05cf 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/SubStatement.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/SubStatement.java @@ -4,6 +4,7 @@ package dev.learning.xapi.model; +import dev.learning.xapi.model.validation.constraints.ValidActor; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import java.time.Instant; @@ -31,6 +32,7 @@ public class SubStatement implements StatementObject { */ @NotNull @Valid + @ValidActor private Actor actor; /** @@ -45,6 +47,7 @@ public class SubStatement implements StatementObject { */ @NotNull @Valid + @ValidActor private SubStatementObject object; /** diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/Mbox.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/Mbox.java new file mode 100644 index 00000000..11b16e6c --- /dev/null +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/Mbox.java @@ -0,0 +1,47 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.constraints; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * The annotated element must be a valid Mbox. + * + * @author Thomas Turrell-Croft + * @author István Rátkai (Selindek) + */ +@Documented +@Constraint(validatedBy = {}) +@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) +@Retention(RUNTIME) +public @interface Mbox { + + /** + * Error Message. + */ + String message() default "must be a valid mbox"; + + /** + * Groups. + */ + Class[] groups() default {}; + + /** + * Payload. + */ + Class[] payload() default {}; +} diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/NotUndetermined.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/NotUndetermined.java new file mode 100644 index 00000000..24ef0e0f --- /dev/null +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/NotUndetermined.java @@ -0,0 +1,48 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.constraints; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.util.Locale; + +/** + * The annotated element must be a not undetermined {@link Locale}. + * + * @author István Rátkai (Selindek) + */ +@Documented +@Constraint(validatedBy = {}) +@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) +@Retention(RUNTIME) +public @interface NotUndetermined { + + /** + * Error Message. + */ + String message() default "undetermined (und) locale is not allowed"; + + /** + * Groups. + */ + Class[] groups() default {}; + + /** + * Payload. + */ + Class[] payload() default {}; + +} diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ScaledScore.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ScaledScore.java new file mode 100644 index 00000000..970c34dc --- /dev/null +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ScaledScore.java @@ -0,0 +1,47 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.constraints; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * The annotated element must be a valid scaled score. + * + * @author István Rátkai (Selindek) + * @see xAPI Score details + */ +@Documented +@Constraint(validatedBy = {}) +@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) +@Retention(RUNTIME) +public @interface ScaledScore { + + /** + * Error Message. + */ + String message() default "scaled score must be between -1 and 1"; + + /** + * Groups. + */ + Class[] groups() default {}; + + /** + * Payload. + */ + Class[] payload() default {}; +} diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ValidActivityDefinition.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ValidActivityDefinition.java new file mode 100644 index 00000000..ba5c4046 --- /dev/null +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ValidActivityDefinition.java @@ -0,0 +1,49 @@ +/* + * Copyright 2016-2019 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.constraints; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * The annotated element must be a valid Activity Definition. + * + * @author Thomas Turrell-Croft + * @author István Rátkai (Selindek) + * @see Activity + * Definition + */ +@Documented +@Constraint(validatedBy = {}) +@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) +@Retention(RUNTIME) +public @interface ValidActivityDefinition { + + /** + * Error Message. + */ + String message() default "must be a valid Activity Definition"; + + /** + * Groups. + */ + Class[] groups() default {}; + + /** + * Payload. + */ + Class[] payload() default {}; +} diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ValidActor.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ValidActor.java new file mode 100644 index 00000000..d9c8c3c1 --- /dev/null +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ValidActor.java @@ -0,0 +1,49 @@ +/* + * Copyright 2016-2019 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.constraints; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import dev.learning.xapi.model.Actor; +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * The annotated element must be a valid {@link Actor}. + * + * @author Thomas Turrell-Croft + * @author István Rátkai (Selindek) + * @see Actor + */ +@Documented +@Constraint(validatedBy = {}) +@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) +@Retention(RUNTIME) +public @interface ValidActor { + + /** + * Error Message. + */ + String message() default "actor must be valid"; + + /** + * Groups. + */ + Class[] groups() default {}; + + /** + * Payload. + */ + Class[] payload() default {}; +} diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ActivityDefinitionValidator.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ActivityDefinitionValidator.java new file mode 100644 index 00000000..5e4546b4 --- /dev/null +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ActivityDefinitionValidator.java @@ -0,0 +1,36 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.internal.validators; + +import dev.learning.xapi.model.ActivityDefinition; +import dev.learning.xapi.model.validation.constraints.ValidActivityDefinition; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; + +/** + * The {@link ActivityDefinition} being validated must be valid. + * + * @author István Rátkai (Selindek) + * @see Activity + * Definition + */ +public class ActivityDefinitionValidator + implements ConstraintValidator { + + @Override + public boolean isValid(ActivityDefinition value, ConstraintValidatorContext context) { + + if (value == null) { + return true; + } + + return !(value.getInteractionType() == null + && (value.getCorrectResponsesPattern() != null || value.getChoices() != null + || value.getScale() != null || value.getSource() != null || value.getTarget() != null + || value.getSteps() != null)); + + } + +} diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ActorValidator.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ActorValidator.java new file mode 100644 index 00000000..bc45b1ed --- /dev/null +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ActorValidator.java @@ -0,0 +1,63 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.internal.validators; + +import dev.learning.xapi.model.Actor; +import dev.learning.xapi.model.Agent; +import dev.learning.xapi.model.Group; +import dev.learning.xapi.model.StatementObject; +import dev.learning.xapi.model.validation.constraints.ValidActor; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; + +/** + * The {@link StatementObject} being validated must be valid. + * + * @author István Rátkai (Selindek) + * @see Actor + */ +public class ActorValidator implements ConstraintValidator { + + /** + * Checks if this {@link Actor} contains exactly one identifier. + * + * @return true if this object is valid. + */ + @Override + public boolean isValid(StatementObject value, ConstraintValidatorContext context) { + + if (value instanceof final Group group) { + return group.getAccount() == null && group.getMbox() == null && group.getMboxSha1sum() == null + && group.getOpenid() == null ? group.getMember() != null && !group.getMember().isEmpty() + : hasSingleIdentifier(group); + } else if (value instanceof final Agent agent) { + return hasSingleIdentifier(agent); + } + + return true; + + } + + private boolean hasSingleIdentifier(Actor value) { + + var n = 0; + + if (value.getMbox() != null) { + n++; + } + if (value.getMboxSha1sum() != null) { + n++; + } + if (value.getOpenid() != null) { + n++; + } + if (value.getAccount() != null) { + n++; + } + + return n == 1; + } + +} diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/MboxValidator.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/MboxValidator.java new file mode 100644 index 00000000..157c6aea --- /dev/null +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/MboxValidator.java @@ -0,0 +1,42 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.internal.validators; + +import dev.learning.xapi.model.validation.constraints.Mbox; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import org.hibernate.validator.internal.constraintvalidators.bv.EmailValidator; + +/** + * The String being validated must be a valid mbox. + * + * @author Thomas Turrell-Croft + * @author István Rátkai (Selindek) + * @see Mbox + */ +public class MboxValidator implements ConstraintValidator { + + public static final String PREFIX = "mailto:"; + + EmailValidator emailValidator; + + @Override + public void initialize(Mbox mbox) { + + emailValidator = new EmailValidator(); + } + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + + if (value == null) { + return true; + } + + return value.startsWith(PREFIX) + && emailValidator.isValid(value.substring(PREFIX.length()), context); + } + +} diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/NotUndeterminedValidator.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/NotUndeterminedValidator.java new file mode 100644 index 00000000..c3632f18 --- /dev/null +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/NotUndeterminedValidator.java @@ -0,0 +1,30 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.internal.validators; + +import dev.learning.xapi.model.validation.constraints.NotUndetermined; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import java.util.Locale; + +/** + * The Locale being validated must be a non undetermined {@link Locale}. + * + * @author István Rátkai (Selindek) + */ +public class NotUndeterminedValidator implements ConstraintValidator { + + @Override + public boolean isValid(Locale value, ConstraintValidatorContext context) { + + if (value == null) { + return true; + } + + return !value.toLanguageTag().equalsIgnoreCase("und"); + + } + +} diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ScaledScoreValidator.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ScaledScoreValidator.java new file mode 100644 index 00000000..8ab73cf7 --- /dev/null +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ScaledScoreValidator.java @@ -0,0 +1,29 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.internal.validators; + +import dev.learning.xapi.model.validation.constraints.ScaledScore; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; + +/** + * The Float being validated must be a valid scaled score. + * + * @author István Rátkai (Selindek) + * @see xAPI Score details + */ +public class ScaledScoreValidator implements ConstraintValidator { + + @Override + public boolean isValid(Float value, ConstraintValidatorContext context) { + + if (value == null) { + return true; + } + + return value >= -1F && value <= 1F; + } + +} diff --git a/xapi-model/src/main/resources/META-INF/services/jakarta.validation.ConstraintValidator b/xapi-model/src/main/resources/META-INF/services/jakarta.validation.ConstraintValidator index 3b87ca48..9e253511 100644 --- a/xapi-model/src/main/resources/META-INF/services/jakarta.validation.ConstraintValidator +++ b/xapi-model/src/main/resources/META-INF/services/jakarta.validation.ConstraintValidator @@ -1,3 +1,8 @@ dev.learning.xapi.model.validation.internal.validators.AuthorityValidator dev.learning.xapi.model.validation.internal.validators.HasSchemeValidatorForUri dev.learning.xapi.model.validation.internal.validators.VariantValidatorForUuid +dev.learning.xapi.model.validation.internal.validators.NotUndeterminedValidator +dev.learning.xapi.model.validation.internal.validators.MboxValidator +dev.learning.xapi.model.validation.internal.validators.ScaledScoreValidator +dev.learning.xapi.model.validation.internal.validators.ActivityDefinitionValidator +dev.learning.xapi.model.validation.internal.validators.ActorValidator diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActivityDefinitionValidatorTest.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActivityDefinitionValidatorTest.java new file mode 100644 index 00000000..4d5946e3 --- /dev/null +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActivityDefinitionValidatorTest.java @@ -0,0 +1,122 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.internal.validators; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import dev.learning.xapi.model.ActivityDefinition; +import dev.learning.xapi.model.InteractionType; + +/** + * ActivityDefinitionValidator Tests. + * + * @author István Rátkai (Selindek) + */ +@DisplayName("ActivityDefinitionValidator tests") +public class ActivityDefinitionValidatorTest { + + private static final ActivityDefinitionValidator validator = new ActivityDefinitionValidator(); + + @Test + void whenValueIsNullThenResultIsTrue() { + + // When Value Is Null + var result = validator.isValid(null, null); + + // Then Result Is True + assertTrue(result); + } + + + @Test + void whenValueHasInteractionTypeThenResultIsTrue() { + + // When Value Has InteractionType + var value = ActivityDefinition.builder().interactionType(InteractionType.CHOICE).build(); + + var result = validator.isValid(value, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenValueHasNoInteractionTypeButHasCorrectResponsepatternThenResultIsFalse() { + + // When Value Has No InteractionType But Has CorrectResponsePatters + var value = ActivityDefinition.builder().correctResponsesPattern(new ArrayList<>()).build(); + + var result = validator.isValid(value, null); + + // Then Result Is False + assertFalse(result); + } + + @Test + void whenValueHasNoInteractionTypeButHasChoicesThenResultIsFalse() { + + // When Value Has No InteractionType But Has Choices + var value = ActivityDefinition.builder().choices(new ArrayList<>()).build(); + + var result = validator.isValid(value, null); + + // Then Result Is False + assertFalse(result); + } + + @Test + void whenValueHasNoInteractionTypeButHasScaleThenResultIsFalse() { + + // When Value Has No InteractionType But Has Scale + var value = ActivityDefinition.builder().scale(new ArrayList<>()).build(); + + var result = validator.isValid(value, null); + + // Then Result Is False + assertFalse(result); + } + + @Test + void whenValueHasNoInteractionTypeButHasSourceThenResultIsFalse() { + + // When Value Has No InteractionType But Has Source + var value = ActivityDefinition.builder().source(new ArrayList<>()).build(); + + var result = validator.isValid(value, null); + + // Then Result Is False + assertFalse(result); + } + + @Test + void whenValueHasNoInteractionTypeButHasTargetThenResultIsFalse() { + + // When Value Has No InteractionType But Has Target + var value = ActivityDefinition.builder().target(new ArrayList<>()).build(); + + var result = validator.isValid(value, null); + + // Then Result Is False + assertFalse(result); + } + + @Test + void whenValueHasNoInteractionTypeButHasStepsThenResultIsFalse() { + + // When Value Has No InteractionType But Has Steps + var value = ActivityDefinition.builder().steps(new ArrayList<>()).build(); + + var result = validator.isValid(value, null); + + // Then Result Is False + assertFalse(result); + } + + +} diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActorValidatorTest.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActorValidatorTest.java new file mode 100644 index 00000000..5fdb29c5 --- /dev/null +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActorValidatorTest.java @@ -0,0 +1,246 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.internal.validators; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.net.URI; +import java.util.ArrayList; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import dev.learning.xapi.model.Activity; +import dev.learning.xapi.model.Agent; +import dev.learning.xapi.model.Group; +import dev.learning.xapi.model.StatementReference; +import dev.learning.xapi.model.SubStatement; + +/** + * ActorValidator Tests. + * + * @author István Rátkai (Selindek) + */ +@DisplayName("ActorValidator tests") +public class ActorValidatorTest { + + private static final ActorValidator validator = new ActorValidator(); + + @Test + void whenValueIsNullThenResultIsTrue() { + + // When Value Is Null + var result = validator.isValid(null, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenValueIsActivityThenResultIsTrue() { + + // When Value Is Activity + var value = Activity.builder().build(); + + var result = validator.isValid(value, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenValueIsSubStatementThenResultIsTrue() { + + // When Value Is SubStatement + var value = SubStatement.builder().build(); + + var result = validator.isValid(value, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenValueIsStatementReferenceThenResultIsTrue() { + + // When Value Is StatementReference + var value = StatementReference.builder().build(); + + var result = validator.isValid(value, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenAgentValueHasNoIdentifierThenResultIsFalse() { + + // When Agent Value Has No Identifier + var value = Agent.builder().build(); + + var result = validator.isValid(value, null); + + // Then Result Is False + assertFalse(result); + } + + @Test + void whenAgentValueHasOnlyMboxThenResultIsTrue() { + + // When Agent Value Has Only Mbox + var value = Agent.builder().mbox("mailto:fred@example.com").build(); + + var result = validator.isValid(value, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenAgentValueHasOnlyMboxSha1sumThenResultIsTrue() { + + // When Agent Value Has Only MboxSha1sum + var value = Agent.builder().mboxSha1sum("121212121212121212121212").build(); + + var result = validator.isValid(value, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenAgentValueHasOnlyOpenIdThenResultIsTrue() { + + // When Agent Value Has Only MboxSha1sum + var value = Agent.builder().openid(URI.create("http://example.com/openid/121212121212121212")).build(); + + var result = validator.isValid(value, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenAgentValueHasOnlyAccountThenResultIsTrue() { + + // When Agent Value Has Only Account + var value = Agent.builder().account(a->a.homePage(URI.create("http://example.com")).name("fred")).build(); + + var result = validator.isValid(value, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenAgentValueHasMoreThanOneIdentifierThenResultIsFalse() { + + // When Agent Value Has More Than One Identifier + var value = Agent.builder().mbox("mailto:fred@example.com").mboxSha1sum("121212121212121212121212").build(); + + var result = validator.isValid(value, null); + + // Then Result Is False + assertFalse(result); + } + + @Test + void whenGroupValueHasNoIdentifierThenResultIsFalse() { + + // When Group Value Has No Identifier + var value = Group.builder().build(); + + var result = validator.isValid(value, null); + + // Then Result Is False + assertFalse(result); + } + + @Test + void whenGroupValueHasNoIdentifierAndEmptyMembersThenResultIsFalse() { + + // When Group Value Has No Identifier And Empty Members + var value = Group.builder().member(new ArrayList<>()).build(); + + var result = validator.isValid(value, null); + + // Then Result Is False + assertFalse(result); + } + + @Test + void whenGroupValueHasNoIdentifierButHasMemberThenResultIsTrue() { + + // When Group Value Has No Identifier But Has Member + var value = Group.builder().addMember(a->a.mbox("mailto:fred@example.com")).build(); + + var result = validator.isValid(value, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenGroupValueHasOnlyMboxThenResultIsTrue() { + + // When Group Value Has Only Mbox + var value = Group.builder().mbox("mailto:fred@example.com").build(); + + var result = validator.isValid(value, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenGroupValueHasOnlyMboxSha1sumThenResultIsTrue() { + + // When Group Value Has Only MboxSha1sum + var value = Group.builder().mboxSha1sum("121212121212121212121212").build(); + + var result = validator.isValid(value, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenGroupValueHasOnlyOpenIdThenResultIsTrue() { + + // When Group Value Has Only MboxSha1sum + var value = Group.builder().openid(URI.create("http://example.com/openid/121212121212121212")).build(); + + var result = validator.isValid(value, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenGroupValueHasOnlyAccountThenResultIsTrue() { + + // When Group Value Has Only Account + var value = Group.builder().account(a->a.homePage(URI.create("http://example.com")).name("fred")).build(); + + var result = validator.isValid(value, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenGroupValueHasMoreThanOneIdentifierThenResultIsFalse() { + + // When Group Value Has More Than One Identifier + var value = Group.builder().mbox("mailto:fred@example.com").mboxSha1sum("121212121212121212121212").build(); + + var result = validator.isValid(value, null); + + // Then Result Is False + assertFalse(result); + } + +} diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/MboxValidatorTest.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/MboxValidatorTest.java new file mode 100644 index 00000000..7f76a14a --- /dev/null +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/MboxValidatorTest.java @@ -0,0 +1,103 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.internal.validators; + +import jakarta.validation.Payload; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import java.lang.annotation.Annotation; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import dev.learning.xapi.model.validation.constraints.Mbox; + +/** + * MboxValidator Tests. + * + * @author István Rátkai (Selindek) + */ +@DisplayName("MboxValidator tests") +public class MboxValidatorTest { + + private static final MboxValidator validator = new MboxValidator(); + + @BeforeAll + static void init() { + validator.initialize(new Mbox() { + + @Override + public Class annotationType() { + return null; + } + + @Override + public Class[] payload() { + return null; + } + + @Override + public String message() { + return null; + } + + @Override + public Class[] groups() { + return null; + } + }); + } + + @Test + void whenValueIsNullThenResultIsTrue() { + + // When Value Is Null + var result = validator.isValid(null, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenValueIsValidMboxThenResultIsTrue() { + + // When Value Is Valid Mbox + var result = validator.isValid("mailto:fred@example.com", null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenValueHasInvalidPrefixThenResultIsFalse() { + + // When Value Has Invalid Prefix + var result = validator.isValid("email:fred@example.com", null); + + // Then Result Is False + assertFalse(result); + } + + @Test + void whenValueHasNoPrefixThenResultIsFalse() { + + // When Value Has No Prefix + var result = validator.isValid("fred@example.com", null); + + // Then Result Is False + assertFalse(result); + } + + @Test + void whenValueHasInvalidEmailThenResultIsFalse() { + + // When Value Has Invalid Email + var result = validator.isValid("mailto:fred@example@com", null); + + // Then Result Is False + assertFalse(result); + } + +} diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/NotUndeterminedValidatorTest.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/NotUndeterminedValidatorTest.java new file mode 100644 index 00000000..18a843c2 --- /dev/null +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/NotUndeterminedValidatorTest.java @@ -0,0 +1,54 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.internal.validators; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Locale; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +/** + * NotUndeterminedValidator Tests. + * + * @author István Rátkai (Selindek) + */ +@DisplayName("NotUndeterminedValidator tests") +public class NotUndeterminedValidatorTest { + + private static final NotUndeterminedValidator validator = new NotUndeterminedValidator(); + + @Test + void whenValueIsNullThenResultIsTrue() { + + // When Value Is Null + var result = validator.isValid(null, null); + + //Then Result Is True + assertTrue(result); + } + + @Test + void whenValueIsUndeterminedLocaleThenResultIsFalse() throws NoSuchFieldException, SecurityException { + + // When Value Is Undetermined Locale + var result = validator.isValid(Locale.forLanguageTag("und"), null); + + // Then Result Is False + assertFalse(result); + } + + @Test + void whenValueIsNotUndeterminedLocaleThenResultIsTrue() throws NoSuchFieldException, SecurityException { + + // When Value Is Not Undetermined Locale + var result = validator.isValid(Locale.forLanguageTag("en-US"), null); + + // Then Result Is True + assertTrue(result); + } + +} diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ScaledScoreValidatorTest.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ScaledScoreValidatorTest.java new file mode 100644 index 00000000..de11ac9f --- /dev/null +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ScaledScoreValidatorTest.java @@ -0,0 +1,93 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.internal.validators; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +/** + * ScaledScoreValidator Tests. + * + * @author István Rátkai (Selindek) + */ +@DisplayName("ScaledSoreValidator tests") +public class ScaledScoreValidatorTest { + + private static final ScaledScoreValidator validator = new ScaledScoreValidator(); + + @Test + void whenValueIsNullThenResultIsTrue() { + + // When Value Is Null + var result = validator.isValid(null, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenValueIsValidScaledScoreThenResultIsTrue() { + + // When Value Is Valid Score + var result = validator.isValid(0F, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenValueIsValidMinimumScoreThenResultIsTrue() { + + // When Value Is Valid Minimum Score + var result = validator.isValid(-1F, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenValueIsValidMaximumScoreThenResultIsTrue() { + + // When Value Is Valid Maximum Score + var result = validator.isValid(1F, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void WhenValueIsOverMaximumScoreThenResultIsFalse() { + + // when Value Is Over Maximum Score + var result = validator.isValid(1.001F, null); + + // Then Result Is False + assertFalse(result); + } + + @Test + void whenValueIsBelowMinimumScoreThenResultIsFalse() { + + // When Value Is Below Minimum Score + var result = validator.isValid(-1.001F, null); + + // Then Result Is False + assertFalse(result); + } + + @Test + void whenValueIsNanThenResultIsFalse() { + + // When Value Is NaN + var result = validator.isValid(Float.NaN, null); + + // Then Result Is False + assertFalse(result); + } + +} From fa1acfe2b0290a72e145773f94b983489d0d29bd Mon Sep 17 00:00:00 2001 From: Thomas Turrell-Croft Date: Fri, 17 Mar 2023 12:48:45 +0000 Subject: [PATCH 18/34] tip --- .../VariantValidatorForUuidTest.java | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/VariantValidatorForUuidTest.java diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/VariantValidatorForUuidTest.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/VariantValidatorForUuidTest.java new file mode 100644 index 00000000..4a45b35f --- /dev/null +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/VariantValidatorForUuidTest.java @@ -0,0 +1,51 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.internal.validators; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.UUID; +import org.junit.jupiter.api.Test; + +class VariantValidatorForUuidTest { + + private VariantValidatorForUuid constraintValidator = new VariantValidatorForUuid(); + + @Test + void whenIsValidIsCalledWithNullUUIDThenValidIsTrue() { + + // When IsValid Is Called With Null UUID + boolean valid = constraintValidator.isValid(null, null); + + // Then Valid Is True + assertTrue(valid); + } + + @Test + void whenIsValidIsCalledWithVersion4Variant2UUIDThenValidIsTrue() { + + // When IsValid Is Called With Version 4 Variant 2 UUID + boolean valid = + constraintValidator.isValid(UUID.fromString("3441c47b-c098-4245-b22e-db3d1242098f"), null); + + // Then Valid Is True + assertTrue(valid); + } + + @Test + void whenIsValidIsCalledWithVersion4VariantMicrosoftGUIDUUIDThenResultIsFalse() { + + // When IsValid Is Called With Version 4 Variant Microsoft GUID UUID + boolean valid = + constraintValidator.isValid(UUID.fromString("4c30eeb1-15ac-4833-cb0d-88613ba65267"), null); + + // Then Valid Is False + assertFalse(valid); + } + + + // +} From f1d329f2644e3e188ebc93e858b33194698d271a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20R=C3=A1tkai?= Date: Fri, 17 Mar 2023 13:43:30 +0000 Subject: [PATCH 19/34] add more validators ValidStatementVerb ValidStatementPlatform ValidStatementRevision --- .../dev/learning/xapi/model/Statement.java | 10 +- .../learning/xapi/model/StatementObject.java | 2 +- .../dev/learning/xapi/model/SubStatement.java | 6 +- .../xapi/model/ValidableStatement.java | 21 ++++ .../constraints/ValidStatementPlatform.java | 42 +++++++ .../constraints/ValidStatementRevision.java | 42 +++++++ .../constraints/ValidStatementVerb.java | 42 +++++++ .../StatementPlatformValidator.java | 34 ++++++ .../StatementRevisionValidator.java | 34 ++++++ .../validators/StatementVerbValidator.java | 35 ++++++ .../jakarta.validation.ConstraintValidator | 3 + .../StatementPlatformValidatorTest.java | 105 +++++++++++++++++ .../StatementRevisionValidatorTest.java | 106 ++++++++++++++++++ .../StatementVerbValidatorTest.java | 105 +++++++++++++++++ 14 files changed, 582 insertions(+), 5 deletions(-) create mode 100644 xapi-model/src/main/java/dev/learning/xapi/model/ValidableStatement.java create mode 100644 xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ValidStatementPlatform.java create mode 100644 xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ValidStatementRevision.java create mode 100644 xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ValidStatementVerb.java create mode 100644 xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/StatementPlatformValidator.java create mode 100644 xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/StatementRevisionValidator.java create mode 100644 xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/StatementVerbValidator.java create mode 100644 xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/StatementPlatformValidatorTest.java create mode 100644 xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/StatementRevisionValidatorTest.java create mode 100644 xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/StatementVerbValidatorTest.java diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Statement.java b/xapi-model/src/main/java/dev/learning/xapi/model/Statement.java index 8608e348..9b071c68 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/Statement.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/Statement.java @@ -5,11 +5,13 @@ package dev.learning.xapi.model; import com.fasterxml.jackson.annotation.JsonFormat; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import dev.learning.xapi.model.validation.constraints.ValidActor; import dev.learning.xapi.model.validation.constraints.ValidAuthority; +import dev.learning.xapi.model.validation.constraints.ValidStatementPlatform; +import dev.learning.xapi.model.validation.constraints.ValidStatementRevision; +import dev.learning.xapi.model.validation.constraints.ValidStatementVerb; import dev.learning.xapi.model.validation.constraints.Variant; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; @@ -35,11 +37,13 @@ */ @With @Value +@ValidStatementPlatform +@ValidStatementRevision +@ValidStatementVerb @Builder(toBuilder = true) -@JsonIgnoreProperties("inProgress") @JsonInclude(Include.NON_EMPTY) @EqualsAndHashCode(of = {"actor", "verb", "object", "result", "context"}) -public class Statement { +public class Statement implements ValidableStatement { /** * UUID assigned by LRS if not set by the Learning Record Provider. diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/StatementObject.java b/xapi-model/src/main/java/dev/learning/xapi/model/StatementObject.java index ce38625a..f381c5ab 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/StatementObject.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/StatementObject.java @@ -14,7 +14,7 @@ * This interface represents the xAPI statement object. * * @author Thomas Turrell-Croft - * + * * @see xAPI * Object */ diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/SubStatement.java b/xapi-model/src/main/java/dev/learning/xapi/model/SubStatement.java index f03b05cf..a13ac425 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/SubStatement.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/SubStatement.java @@ -5,6 +5,8 @@ package dev.learning.xapi.model; import dev.learning.xapi.model.validation.constraints.ValidActor; +import dev.learning.xapi.model.validation.constraints.ValidStatementPlatform; +import dev.learning.xapi.model.validation.constraints.ValidStatementRevision; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import java.time.Instant; @@ -24,8 +26,10 @@ */ @Value @Builder +@ValidStatementPlatform +@ValidStatementRevision @EqualsAndHashCode(exclude = {"timestamp", "attachments"}) -public class SubStatement implements StatementObject { +public class SubStatement implements StatementObject, ValidableStatement { /** * Whom the Statement is about, as an Agent or Group Object. diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/ValidableStatement.java b/xapi-model/src/main/java/dev/learning/xapi/model/ValidableStatement.java new file mode 100644 index 00000000..7aaeeb2d --- /dev/null +++ b/xapi-model/src/main/java/dev/learning/xapi/model/ValidableStatement.java @@ -0,0 +1,21 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model; + +/** + * This is a helper interface for signing Statement-like objects which can be targets of + * class level Statement-validators. + * + * @author István Rátkai (Selindek) + */ +public interface ValidableStatement { + + Verb getVerb(); + + Object getObject(); + + Context getContext(); + +} diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ValidStatementPlatform.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ValidStatementPlatform.java new file mode 100644 index 00000000..edfae752 --- /dev/null +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ValidStatementPlatform.java @@ -0,0 +1,42 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.constraints; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * The annotated element must have a valid platform. + * + * @author István Rátkai (Selindek) + */ +@Documented +@Constraint(validatedBy = {}) +@Target({TYPE}) +@Retention(RUNTIME) +public @interface ValidStatementPlatform { + + /** + * Error Message. + */ + String message() default "invalid Statement Platform (Object must be an Activity)"; + + /** + * Groups. + */ + Class[] groups() default {}; + + /** + * Payload. + */ + Class[] payload() default {}; + +} diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ValidStatementRevision.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ValidStatementRevision.java new file mode 100644 index 00000000..054aee45 --- /dev/null +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ValidStatementRevision.java @@ -0,0 +1,42 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.constraints; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * The annotated element must have a valid revision. + * + * @author István Rátkai (Selindek) + */ +@Documented +@Constraint(validatedBy = {}) +@Target({TYPE}) +@Retention(RUNTIME) +public @interface ValidStatementRevision { + + /** + * Error Message. + */ + String message() default "invalid Statement Revision (Object must be an Activity)"; + + /** + * Groups. + */ + Class[] groups() default {}; + + /** + * Payload. + */ + Class[] payload() default {}; + +} diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ValidStatementVerb.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ValidStatementVerb.java new file mode 100644 index 00000000..b59e8d0a --- /dev/null +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/ValidStatementVerb.java @@ -0,0 +1,42 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.constraints; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * The annotated element must have a valid verb. + * + * @author István Rátkai (Selindek) + */ +@Documented +@Constraint(validatedBy = {}) +@Target({TYPE}) +@Retention(RUNTIME) +public @interface ValidStatementVerb { + + /** + * Error Message. + */ + String message() default "voiding statement must have a statement reference"; + + /** + * Groups. + */ + Class[] groups() default {}; + + /** + * Payload. + */ + Class[] payload() default {}; + +} diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/StatementPlatformValidator.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/StatementPlatformValidator.java new file mode 100644 index 00000000..388c99cd --- /dev/null +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/StatementPlatformValidator.java @@ -0,0 +1,34 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.internal.validators; + +import dev.learning.xapi.model.Activity; +import dev.learning.xapi.model.ValidableStatement; +import dev.learning.xapi.model.validation.constraints.ValidStatementPlatform; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; + +/** + * The Statement being validated must have a valid platform. + *

+ * If context.platform present, then object must be an {@link Activity}. + *

+ * + * @author Thomas Turrell-Croft + * @author István Rátkai (Selindek) + * @see + * Statement Context Requirements + */ +public class StatementPlatformValidator implements + ConstraintValidator { + + @Override + public boolean isValid(ValidableStatement value, ConstraintValidatorContext context) { + + return value == null || value.getContext() == null || value.getContext().getPlatform() == null + || value.getObject() instanceof Activity; + } + +} diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/StatementRevisionValidator.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/StatementRevisionValidator.java new file mode 100644 index 00000000..417e39c6 --- /dev/null +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/StatementRevisionValidator.java @@ -0,0 +1,34 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.internal.validators; + +import dev.learning.xapi.model.Activity; +import dev.learning.xapi.model.ValidableStatement; +import dev.learning.xapi.model.validation.constraints.ValidStatementRevision; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; + +/** + * The Statement being validated must have a valid revision. + *

+ * If context.revision present, then object must be an {@link Activity}. + *

+ * + * @author Thomas Turrell-Croft + * @author István Rátkai (Selindek) + * @see + * Statement Context Requirements + */ +public class StatementRevisionValidator implements + ConstraintValidator { + + @Override + public boolean isValid(ValidableStatement value, ConstraintValidatorContext context) { + + return value == null || value.getContext() == null || value.getContext().getRevision() == null + || value.getObject() instanceof Activity; + } + +} diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/StatementVerbValidator.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/StatementVerbValidator.java new file mode 100644 index 00000000..f755923b --- /dev/null +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/StatementVerbValidator.java @@ -0,0 +1,35 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.internal.validators; + +import dev.learning.xapi.model.StatementReference; +import dev.learning.xapi.model.ValidableStatement; +import dev.learning.xapi.model.validation.constraints.ValidStatementVerb; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; + +/** + * The Statement being validated must have a valid revision. + *

+ * if verb is 'voided' then object must be a {@link StatementReference}. + *

+ * + * @author Thomas Turrell-Croft + * @author István Rátkai (Selindek) + * @see + * Voiding Statement Requirements + */ +public class StatementVerbValidator implements + ConstraintValidator { + + @Override + public boolean isValid(ValidableStatement value, ConstraintValidatorContext context) { + + return value == null || value.getVerb() == null || !value.getVerb().isVoided() + || value.getObject() instanceof StatementReference; + + } + +} diff --git a/xapi-model/src/main/resources/META-INF/services/jakarta.validation.ConstraintValidator b/xapi-model/src/main/resources/META-INF/services/jakarta.validation.ConstraintValidator index 9e253511..0f706f9a 100644 --- a/xapi-model/src/main/resources/META-INF/services/jakarta.validation.ConstraintValidator +++ b/xapi-model/src/main/resources/META-INF/services/jakarta.validation.ConstraintValidator @@ -6,3 +6,6 @@ dev.learning.xapi.model.validation.internal.validators.MboxValidator dev.learning.xapi.model.validation.internal.validators.ScaledScoreValidator dev.learning.xapi.model.validation.internal.validators.ActivityDefinitionValidator dev.learning.xapi.model.validation.internal.validators.ActorValidator +dev.learning.xapi.model.validation.internal.validators.StatementRevisionValidator +dev.learning.xapi.model.validation.internal.validators.StatementPlatformValidator +dev.learning.xapi.model.validation.internal.validators.StatementVerbValidator diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/StatementPlatformValidatorTest.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/StatementPlatformValidatorTest.java new file mode 100644 index 00000000..58992706 --- /dev/null +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/StatementPlatformValidatorTest.java @@ -0,0 +1,105 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.internal.validators; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import dev.learning.xapi.model.Context; +import dev.learning.xapi.model.Statement; +import java.net.URI; +import java.util.UUID; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +/** + * StatementPlatformValidator Tests. + * + * @author István Rátkai (Selindek) + */ +@DisplayName("StatementPlatformValidator tests") +public class StatementPlatformValidatorTest { + + private static final StatementPlatformValidator validator = new StatementPlatformValidator(); + + @Test + void whenValueIsNullThenResultIsTrue() { + + // When Value Is Null + var result = validator.isValid(null, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenValueHasNoContextThenResultIsTrue() { + + // When Value has No Context + var value = Statement.builder().build(); + + var result = validator.isValid(value, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenValueHasNoContextPlatformThenResultIsTrue() { + + // When Value has No Context Platform + var value = Statement.builder().context(Context.builder().build()).build(); + + var result = validator.isValid(value, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenValueHasContextPlatformButNoObjectThenResultIsFalse() { + + // When Value has Context Platform But No Object + var value = Statement.builder().context(c->c.platform("platform")).build(); + + var result = validator.isValid(value, null); + + // Then Result Is False + assertFalse(result); + } + + @Test + void whenValueHasContextPlatformAndInvalidObjectThenResultIsFalse() { + + // When Value has Context Platform And Invalid Object + var value = Statement.builder().context(c->c.platform("platform")) + + .statementReferenceObject(sr->sr.id(UUID.randomUUID())) + + .build(); + + var result = validator.isValid(value, null); + + // Then Result Is False + assertFalse(result); + } + + @Test + void whenValueHasContextPlatformAndValidObjectThenResultIsTrue() { + + // When Value has Context Platform And Valid Object + var value = Statement.builder().context(c->c.platform("platform")) + + .activityObject(a->a.id(URI.create("http://example.com/activity"))) + + .build(); + + var result = validator.isValid(value, null); + + // Then Result Is True + assertTrue(result); + } + +} diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/StatementRevisionValidatorTest.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/StatementRevisionValidatorTest.java new file mode 100644 index 00000000..24598e42 --- /dev/null +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/StatementRevisionValidatorTest.java @@ -0,0 +1,106 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.internal.validators; + + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import dev.learning.xapi.model.Context; +import dev.learning.xapi.model.Statement; +import java.net.URI; +import java.util.UUID; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +/** + * StatementRevisionValidator Tests. + * + * @author István Rátkai (Selindek) + */ +@DisplayName("StatementRevisionValidator tests") +public class StatementRevisionValidatorTest { + + private static final StatementRevisionValidator validator = new StatementRevisionValidator(); + + @Test + void whenValueIsNullThenResultIsTrue() { + + // When Value Is Null + var result = validator.isValid(null, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenValueHasNoContextThenResultIsTrue() { + + // When Value has No Context + var value = Statement.builder().build(); + + var result = validator.isValid(value, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenValueHasNoContextRevisionThenResultIsTrue() { + + // When Value has No Context Revision + var value = Statement.builder().context(Context.builder().build()).build(); + + var result = validator.isValid(value, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenValueHasContextRevisionButNoObjectThenResultIsFalse() { + + // When Value has Context Revision But No Object + var value = Statement.builder().context(c->c.revision("revision")).build(); + + var result = validator.isValid(value, null); + + // Then Result Is False + assertFalse(result); + } + + @Test + void whenValueHasContextRevisionAndInvalidObjectThenResultIsFalse() { + + // When Value has Context Revision And Invalid Object + var value = Statement.builder().context(c->c.revision("revision")) + + .statementReferenceObject(sr->sr.id(UUID.randomUUID())) + + .build(); + + var result = validator.isValid(value, null); + + // Then Result Is False + assertFalse(result); + } + + @Test + void whenValueHasContextRevisionAndValidObjectThenResultIsTrue() { + + // When Value has Context Revision And Valid Object + var value = Statement.builder().context(c->c.revision("revision")) + + .activityObject(a->a.id(URI.create("http://example.com/activity"))) + + .build(); + + var result = validator.isValid(value, null); + + // Then Result Is True + assertTrue(result); + } + +} diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/StatementVerbValidatorTest.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/StatementVerbValidatorTest.java new file mode 100644 index 00000000..3566a85a --- /dev/null +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/StatementVerbValidatorTest.java @@ -0,0 +1,105 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.internal.validators; + + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import dev.learning.xapi.model.Statement; +import dev.learning.xapi.model.Verb; +import java.net.URI; +import java.util.UUID; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +/** + * StatementVerbValidator Tests. + * + * @author István Rátkai (Selindek) + */ +@DisplayName("StatementVerbValidator tests") +public class StatementVerbValidatorTest { + + private static final StatementVerbValidator validator = new StatementVerbValidator(); + + @Test + void whenValueIsNullThenResultIsTrue() { + + // When Value Is Null + var result = validator.isValid(null, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenValueHasNoVerbThenResultIsTrue() { + + // When Value has No Verb + var value = Statement.builder().build(); + + var result = validator.isValid(value, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenValueHasNonVoidedVerbThenResultIsTrue() { + + // When Value has Non-Voided Verb + var value = Statement.builder().verb(Verb.ANSWERED).build(); + + var result = validator.isValid(value, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenValueHasVoidedVerbButNoObjectThenResultIsFalse() { + + // When Value has Voided Verb But No Object + var value = Statement.builder().verb(Verb.VOIDED).build(); + + var result = validator.isValid(value, null); + + // Then Result Is False + assertFalse(result); + } + + @Test + void whenValueHasVoidedVerbAndInvalidObjectThenResultIsFalse() { + + // When Value has Voided Verb And Invalid Object + var value = Statement.builder().verb(Verb.VOIDED) + + .activityObject(a->a.id(URI.create("http://example.com/activity"))) + + .build(); + + var result = validator.isValid(value, null); + + // Then Result Is False + assertFalse(result); + } + + @Test + void whenValueHasVoidedVerbAndValidObjectThenResultIsTrue() { + + // When Value has Voided Verb And Valid Object + var value = Statement.builder().verb(Verb.VOIDED) + + .statementReferenceObject(sr->sr.id(UUID.randomUUID())) + + .build(); + + var result = validator.isValid(value, null); + + // Then Result Is True + assertTrue(result); + } +} From d5fd8e1eab842f308bcb3cd26e75bd1de5a06af6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20R=C3=A1tkai?= Date: Fri, 17 Mar 2023 13:47:02 +0000 Subject: [PATCH 20/34] pom --- xapi-model/pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/xapi-model/pom.xml b/xapi-model/pom.xml index 3e032a76..a124410d 100644 --- a/xapi-model/pom.xml +++ b/xapi-model/pom.xml @@ -27,6 +27,11 @@ jakarta.validation jakarta.validation-api + + org.hibernate.validator + hibernate-validator + provided + org.springframework.boot spring-boot-starter-test @@ -37,11 +42,6 @@ spring-boot-starter-validation test - - org.hibernate.validator - hibernate-validator - provided - From 10656e097b8d6c9f3cd865d407c9d19c72466869 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20R=C3=A1tkai?= Date: Fri, 17 Mar 2023 13:54:23 +0000 Subject: [PATCH 21/34] fsi --- .../ActivityDefinitionValidatorTest.java | 2 +- .../validators/ActorValidatorTest.java | 2 +- .../validators/MboxValidatorTest.java | 35 ++++++------------- .../NotUndeterminedValidatorTest.java | 2 +- .../validators/ScaledScoreValidatorTest.java | 2 +- .../StatementPlatformValidatorTest.java | 2 +- .../StatementRevisionValidatorTest.java | 2 +- .../StatementVerbValidatorTest.java | 2 +- 8 files changed, 17 insertions(+), 32 deletions(-) diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActivityDefinitionValidatorTest.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActivityDefinitionValidatorTest.java index 4d5946e3..17c027c1 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActivityDefinitionValidatorTest.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActivityDefinitionValidatorTest.java @@ -19,7 +19,7 @@ * @author István Rátkai (Selindek) */ @DisplayName("ActivityDefinitionValidator tests") -public class ActivityDefinitionValidatorTest { +class ActivityDefinitionValidatorTest { private static final ActivityDefinitionValidator validator = new ActivityDefinitionValidator(); diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActorValidatorTest.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActorValidatorTest.java index 5fdb29c5..bf148164 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActorValidatorTest.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActorValidatorTest.java @@ -25,7 +25,7 @@ * @author István Rátkai (Selindek) */ @DisplayName("ActorValidator tests") -public class ActorValidatorTest { +class ActorValidatorTest { private static final ActorValidator validator = new ActorValidator(); diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/MboxValidatorTest.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/MboxValidatorTest.java index 7f76a14a..226096fc 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/MboxValidatorTest.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/MboxValidatorTest.java @@ -12,6 +12,9 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + import dev.learning.xapi.model.validation.constraints.Mbox; /** @@ -20,7 +23,7 @@ * @author István Rátkai (Selindek) */ @DisplayName("MboxValidator tests") -public class MboxValidatorTest { +class MboxValidatorTest { private static final MboxValidator validator = new MboxValidator(); @@ -71,33 +74,15 @@ void whenValueIsValidMboxThenResultIsTrue() { } @Test - void whenValueHasInvalidPrefixThenResultIsFalse() { + @ParameterizedTest + @ValueSource(strings = {"email:fred@example.com", "fred@example.com", "mailto:fred@example@com"}) + void whenValueIsInvalidThenResultIsFalse(String value) { - // When Value Has Invalid Prefix - var result = validator.isValid("email:fred@example.com", null); + // When Value Is Invalid + var result = validator.isValid(value, null); // Then Result Is False assertFalse(result); } - - @Test - void whenValueHasNoPrefixThenResultIsFalse() { - - // When Value Has No Prefix - var result = validator.isValid("fred@example.com", null); - - // Then Result Is False - assertFalse(result); - } - - @Test - void whenValueHasInvalidEmailThenResultIsFalse() { - - // When Value Has Invalid Email - var result = validator.isValid("mailto:fred@example@com", null); - - // Then Result Is False - assertFalse(result); - } - + } diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/NotUndeterminedValidatorTest.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/NotUndeterminedValidatorTest.java index 18a843c2..d8fec9a4 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/NotUndeterminedValidatorTest.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/NotUndeterminedValidatorTest.java @@ -17,7 +17,7 @@ * @author István Rátkai (Selindek) */ @DisplayName("NotUndeterminedValidator tests") -public class NotUndeterminedValidatorTest { +class NotUndeterminedValidatorTest { private static final NotUndeterminedValidator validator = new NotUndeterminedValidator(); diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ScaledScoreValidatorTest.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ScaledScoreValidatorTest.java index de11ac9f..c1bc4c95 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ScaledScoreValidatorTest.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ScaledScoreValidatorTest.java @@ -16,7 +16,7 @@ * @author István Rátkai (Selindek) */ @DisplayName("ScaledSoreValidator tests") -public class ScaledScoreValidatorTest { +class ScaledScoreValidatorTest { private static final ScaledScoreValidator validator = new ScaledScoreValidator(); diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/StatementPlatformValidatorTest.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/StatementPlatformValidatorTest.java index 58992706..54e66cbd 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/StatementPlatformValidatorTest.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/StatementPlatformValidatorTest.java @@ -20,7 +20,7 @@ * @author István Rátkai (Selindek) */ @DisplayName("StatementPlatformValidator tests") -public class StatementPlatformValidatorTest { +class StatementPlatformValidatorTest { private static final StatementPlatformValidator validator = new StatementPlatformValidator(); diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/StatementRevisionValidatorTest.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/StatementRevisionValidatorTest.java index 24598e42..9db981d0 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/StatementRevisionValidatorTest.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/StatementRevisionValidatorTest.java @@ -21,7 +21,7 @@ * @author István Rátkai (Selindek) */ @DisplayName("StatementRevisionValidator tests") -public class StatementRevisionValidatorTest { +class StatementRevisionValidatorTest { private static final StatementRevisionValidator validator = new StatementRevisionValidator(); diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/StatementVerbValidatorTest.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/StatementVerbValidatorTest.java index 3566a85a..55dc3436 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/StatementVerbValidatorTest.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/StatementVerbValidatorTest.java @@ -21,7 +21,7 @@ * @author István Rátkai (Selindek) */ @DisplayName("StatementVerbValidator tests") -public class StatementVerbValidatorTest { +class StatementVerbValidatorTest { private static final StatementVerbValidator validator = new StatementVerbValidator(); From 2971ad3d7cf271930dfa5ff7efec6f0410ebf3d3 Mon Sep 17 00:00:00 2001 From: Thomas Turrell-Croft Date: Fri, 17 Mar 2023 14:07:56 +0000 Subject: [PATCH 22/34] top --- .../validators/VariantValidatorForUuid.java | 1 + ... => ActivityDefinitionValidatorTests.java} | 2 +- ...atorTest.java => ActorValidatorTests.java} | 2 +- .../HasSchemeValidatorForUriTests.java | 2 + ...datorTest.java => MboxValidatorTests.java} | 2 +- ...ava => NotUndeterminedValidatorTests.java} | 2 +- ...st.java => ScaledScoreValidatorTests.java} | 2 +- .../VariantValidatorForUuidTest.java | 51 ----------- .../VariantValidatorForUuidTests.java | 91 +++++++++++++++++++ 9 files changed, 99 insertions(+), 56 deletions(-) rename xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/{ActivityDefinitionValidatorTest.java => ActivityDefinitionValidatorTests.java} (98%) rename xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/{ActorValidatorTest.java => ActorValidatorTests.java} (99%) rename xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/{MboxValidatorTest.java => MboxValidatorTests.java} (98%) rename xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/{NotUndeterminedValidatorTest.java => NotUndeterminedValidatorTests.java} (96%) rename xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/{ScaledScoreValidatorTest.java => ScaledScoreValidatorTests.java} (98%) delete mode 100644 xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/VariantValidatorForUuidTest.java create mode 100644 xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/VariantValidatorForUuidTests.java diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/VariantValidatorForUuid.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/VariantValidatorForUuid.java index 71fa37b3..a300c911 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/VariantValidatorForUuid.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/VariantValidatorForUuid.java @@ -31,6 +31,7 @@ public boolean isValid(UUID value, ConstraintValidatorContext context) { return true; } + System.out.println("value " + value.variant()); return value.variant() == variant; } diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActivityDefinitionValidatorTest.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActivityDefinitionValidatorTests.java similarity index 98% rename from xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActivityDefinitionValidatorTest.java rename to xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActivityDefinitionValidatorTests.java index 4d5946e3..52e21723 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActivityDefinitionValidatorTest.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActivityDefinitionValidatorTests.java @@ -19,7 +19,7 @@ * @author István Rátkai (Selindek) */ @DisplayName("ActivityDefinitionValidator tests") -public class ActivityDefinitionValidatorTest { +public class ActivityDefinitionValidatorTests { private static final ActivityDefinitionValidator validator = new ActivityDefinitionValidator(); diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActorValidatorTest.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActorValidatorTests.java similarity index 99% rename from xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActorValidatorTest.java rename to xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActorValidatorTests.java index 5fdb29c5..bb440507 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActorValidatorTest.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActorValidatorTests.java @@ -25,7 +25,7 @@ * @author István Rátkai (Selindek) */ @DisplayName("ActorValidator tests") -public class ActorValidatorTest { +public class ActorValidatorTests { private static final ActorValidator validator = new ActorValidator(); diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/HasSchemeValidatorForUriTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/HasSchemeValidatorForUriTests.java index d54336ab..a41d1762 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/HasSchemeValidatorForUriTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/HasSchemeValidatorForUriTests.java @@ -8,8 +8,10 @@ import static org.hamcrest.core.Is.is; import java.net.URI; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +@DisplayName("HasSchemeValidatorForUri tests") class HasSchemeValidatorForUriTests { private HasSchemeValidatorForUri constraintValidator = new HasSchemeValidatorForUri(); diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/MboxValidatorTest.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/MboxValidatorTests.java similarity index 98% rename from xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/MboxValidatorTest.java rename to xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/MboxValidatorTests.java index 7f76a14a..8305c7fd 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/MboxValidatorTest.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/MboxValidatorTests.java @@ -20,7 +20,7 @@ * @author István Rátkai (Selindek) */ @DisplayName("MboxValidator tests") -public class MboxValidatorTest { +public class MboxValidatorTests { private static final MboxValidator validator = new MboxValidator(); diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/NotUndeterminedValidatorTest.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/NotUndeterminedValidatorTests.java similarity index 96% rename from xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/NotUndeterminedValidatorTest.java rename to xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/NotUndeterminedValidatorTests.java index 18a843c2..6869003f 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/NotUndeterminedValidatorTest.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/NotUndeterminedValidatorTests.java @@ -17,7 +17,7 @@ * @author István Rátkai (Selindek) */ @DisplayName("NotUndeterminedValidator tests") -public class NotUndeterminedValidatorTest { +public class NotUndeterminedValidatorTests { private static final NotUndeterminedValidator validator = new NotUndeterminedValidator(); diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ScaledScoreValidatorTest.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ScaledScoreValidatorTests.java similarity index 98% rename from xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ScaledScoreValidatorTest.java rename to xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ScaledScoreValidatorTests.java index de11ac9f..8516deed 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ScaledScoreValidatorTest.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ScaledScoreValidatorTests.java @@ -16,7 +16,7 @@ * @author István Rátkai (Selindek) */ @DisplayName("ScaledSoreValidator tests") -public class ScaledScoreValidatorTest { +public class ScaledScoreValidatorTests { private static final ScaledScoreValidator validator = new ScaledScoreValidator(); diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/VariantValidatorForUuidTest.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/VariantValidatorForUuidTest.java deleted file mode 100644 index 4a45b35f..00000000 --- a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/VariantValidatorForUuidTest.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. - */ - -package dev.learning.xapi.model.validation.internal.validators; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.util.UUID; -import org.junit.jupiter.api.Test; - -class VariantValidatorForUuidTest { - - private VariantValidatorForUuid constraintValidator = new VariantValidatorForUuid(); - - @Test - void whenIsValidIsCalledWithNullUUIDThenValidIsTrue() { - - // When IsValid Is Called With Null UUID - boolean valid = constraintValidator.isValid(null, null); - - // Then Valid Is True - assertTrue(valid); - } - - @Test - void whenIsValidIsCalledWithVersion4Variant2UUIDThenValidIsTrue() { - - // When IsValid Is Called With Version 4 Variant 2 UUID - boolean valid = - constraintValidator.isValid(UUID.fromString("3441c47b-c098-4245-b22e-db3d1242098f"), null); - - // Then Valid Is True - assertTrue(valid); - } - - @Test - void whenIsValidIsCalledWithVersion4VariantMicrosoftGUIDUUIDThenResultIsFalse() { - - // When IsValid Is Called With Version 4 Variant Microsoft GUID UUID - boolean valid = - constraintValidator.isValid(UUID.fromString("4c30eeb1-15ac-4833-cb0d-88613ba65267"), null); - - // Then Valid Is False - assertFalse(valid); - } - - - // -} diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/VariantValidatorForUuidTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/VariantValidatorForUuidTests.java new file mode 100644 index 00000000..27ad2b48 --- /dev/null +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/VariantValidatorForUuidTests.java @@ -0,0 +1,91 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model.validation.internal.validators; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import dev.learning.xapi.model.validation.constraints.Variant; +import java.util.UUID; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.platform.commons.util.AnnotationUtils; + +/** + * VariantValidatorForUuid Tests. + * + * @author Thomas Turrell-Croft + */ +@DisplayName("VariantValidatorForUuidTest tests") +class VariantValidatorForUuidTests { + + private VariantValidatorForUuid constraintValidator = new VariantValidatorForUuid(); + + @Variant(2) + private UUID variant2; + + @Variant(6) + private UUID variant6; + + @Test + void whenIsValidIsCalledWithNullUUIDThenValidIsTrue() { + + // When IsValid Is Called With Null UUID + boolean valid = constraintValidator.isValid(null, null); + + // Then Valid Is True + assertTrue(valid); + } + + @Test + void GivenValidatorIsInitializedWith2WhenIsValidIsCalledWithVersion4Variant2UUIDThenValidIsTrue() + throws Exception { + + // Given Validator Is Initialized With 2 + constraintValidator.initialize(AnnotationUtils + .findAnnotation(getClass().getDeclaredField("variant2"), Variant.class).get()); + + // When IsValid Is Called With Version 4 Variant 2 UUID + boolean valid = + constraintValidator.isValid(UUID.fromString("3441c47b-c098-4245-b22e-db3d1242098f"), null); + + // Then Valid Is True + assertTrue(valid); + } + + @Test + void GivenValidatorIsInitializedWith2whenIsValidIsCalledWithVersion4VariantMicrosoftGUIDUUIDThenResultIsFalse() + throws Exception { + + // Given Validator Is Initialized With 2 + constraintValidator.initialize(AnnotationUtils + .findAnnotation(getClass().getDeclaredField("variant2"), Variant.class).get()); + + // When IsValid Is Called With Version 4 Variant Microsoft GUID UUID + boolean valid = + constraintValidator.isValid(UUID.fromString("4c30eeb1-15ac-4833-cb0d-88613ba65267"), null); + + // Then Valid Is False + assertFalse(valid); + } + + + @Test + void GivenValidatorIsInitializedWith6whenIsValidIsCalledWithVersion4VariantMicrosoftGUIDUUIDThenResultIsTrue() + throws Exception { + + // Given Validator Is Initialized With 6 + constraintValidator.initialize(AnnotationUtils + .findAnnotation(getClass().getDeclaredField("variant6"), Variant.class).get()); + + // When IsValid Is Called With Version 4 Variant Microsoft GUID UUID + boolean valid = + constraintValidator.isValid(UUID.fromString("4c30eeb1-15ac-4833-cb0d-88613ba65267"), null); + + // Then Valid Is False + assertTrue(valid); + } + +} From 42c444f4f2d282f969dcc4071b388c59b71244e7 Mon Sep 17 00:00:00 2001 From: Thomas Turrell-Croft Date: Fri, 17 Mar 2023 14:25:24 +0000 Subject: [PATCH 23/34] tip --- .../validators/VariantValidatorForUuid.java | 1 - .../validators/MboxValidatorTests.java | 38 +++++++++---------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/VariantValidatorForUuid.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/VariantValidatorForUuid.java index a300c911..71fa37b3 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/VariantValidatorForUuid.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/VariantValidatorForUuid.java @@ -31,7 +31,6 @@ public boolean isValid(UUID value, ConstraintValidatorContext context) { return true; } - System.out.println("value " + value.variant()); return value.variant() == variant; } diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/MboxValidatorTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/MboxValidatorTests.java index c7d0511b..9d99b62d 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/MboxValidatorTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/MboxValidatorTests.java @@ -4,10 +4,11 @@ package dev.learning.xapi.model.validation.internal.validators; -import jakarta.validation.Payload; - import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; + +import dev.learning.xapi.model.validation.constraints.Mbox; +import jakarta.validation.Payload; import java.lang.annotation.Annotation; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; @@ -15,8 +16,6 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import dev.learning.xapi.model.validation.constraints.Mbox; - /** * MboxValidator Tests. * @@ -26,63 +25,62 @@ public class MboxValidatorTests { private static final MboxValidator validator = new MboxValidator(); - + @BeforeAll static void init() { validator.initialize(new Mbox() { - + @Override public Class annotationType() { return null; } - + @Override public Class[] payload() { return null; } - + @Override public String message() { return null; } - + @Override public Class[] groups() { return null; } }); } - + @Test void whenValueIsNullThenResultIsTrue() { - + // When Value Is Null var result = validator.isValid(null, null); - + // Then Result Is True assertTrue(result); } - + @Test void whenValueIsValidMboxThenResultIsTrue() { - + // When Value Is Valid Mbox var result = validator.isValid("mailto:fred@example.com", null); - + // Then Result Is True assertTrue(result); } - - @Test + @ParameterizedTest @ValueSource(strings = {"email:fred@example.com", "fred@example.com", "mailto:fred@example@com"}) void whenValueIsInvalidThenResultIsFalse(String value) { - + // When Value Is Invalid var result = validator.isValid(value, null); - + // Then Result Is False assertFalse(result); } - + } From c1ae8a21874773dfd4fa38af07ba16d6568abb2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20R=C3=A1tkai?= Date: Fri, 17 Mar 2023 15:11:59 +0000 Subject: [PATCH 24/34] fix test --- .../model/validation/internal/validators/MboxValidatorTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/MboxValidatorTest.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/MboxValidatorTest.java index 226096fc..4180c5bb 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/MboxValidatorTest.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/MboxValidatorTest.java @@ -73,7 +73,6 @@ void whenValueIsValidMboxThenResultIsTrue() { assertTrue(result); } - @Test @ParameterizedTest @ValueSource(strings = {"email:fred@example.com", "fred@example.com", "mailto:fred@example@com"}) void whenValueIsInvalidThenResultIsFalse(String value) { From b3af84992401902b37cc80453bd4b1f33d0e3d4e Mon Sep 17 00:00:00 2001 From: Thomas Turrell-Croft Date: Fri, 17 Mar 2023 15:53:07 +0000 Subject: [PATCH 25/34] tip --- .../ActivityDefinitionValidatorTests.java | 62 +++---- .../validators/ActorValidatorTests.java | 152 +++++++++--------- .../validators/MboxValidatorTests.java | 2 +- .../NotUndeterminedValidatorTests.java | 26 +-- .../validators/ScaledScoreValidatorTests.java | 32 ++-- 5 files changed, 140 insertions(+), 134 deletions(-) diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActivityDefinitionValidatorTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActivityDefinitionValidatorTests.java index 52e21723..0cd6484f 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActivityDefinitionValidatorTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActivityDefinitionValidatorTests.java @@ -7,11 +7,11 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import dev.learning.xapi.model.ActivityDefinition; +import dev.learning.xapi.model.InteractionType; import java.util.ArrayList; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import dev.learning.xapi.model.ActivityDefinition; -import dev.learning.xapi.model.InteractionType; /** * ActivityDefinitionValidator Tests. @@ -19,101 +19,101 @@ * @author István Rátkai (Selindek) */ @DisplayName("ActivityDefinitionValidator tests") -public class ActivityDefinitionValidatorTests { +class ActivityDefinitionValidatorTests { private static final ActivityDefinitionValidator validator = new ActivityDefinitionValidator(); - + @Test void whenValueIsNullThenResultIsTrue() { - + // When Value Is Null var result = validator.isValid(null, null); - + // Then Result Is True assertTrue(result); } - - + + @Test void whenValueHasInteractionTypeThenResultIsTrue() { - + // When Value Has InteractionType var value = ActivityDefinition.builder().interactionType(InteractionType.CHOICE).build(); - + var result = validator.isValid(value, null); - + // Then Result Is True assertTrue(result); } - + @Test void whenValueHasNoInteractionTypeButHasCorrectResponsepatternThenResultIsFalse() { - + // When Value Has No InteractionType But Has CorrectResponsePatters var value = ActivityDefinition.builder().correctResponsesPattern(new ArrayList<>()).build(); - + var result = validator.isValid(value, null); - + // Then Result Is False assertFalse(result); } - + @Test void whenValueHasNoInteractionTypeButHasChoicesThenResultIsFalse() { - + // When Value Has No InteractionType But Has Choices var value = ActivityDefinition.builder().choices(new ArrayList<>()).build(); - + var result = validator.isValid(value, null); - + // Then Result Is False assertFalse(result); } @Test void whenValueHasNoInteractionTypeButHasScaleThenResultIsFalse() { - + // When Value Has No InteractionType But Has Scale var value = ActivityDefinition.builder().scale(new ArrayList<>()).build(); - + var result = validator.isValid(value, null); - + // Then Result Is False assertFalse(result); } @Test void whenValueHasNoInteractionTypeButHasSourceThenResultIsFalse() { - + // When Value Has No InteractionType But Has Source var value = ActivityDefinition.builder().source(new ArrayList<>()).build(); - + var result = validator.isValid(value, null); - + // Then Result Is False assertFalse(result); } @Test void whenValueHasNoInteractionTypeButHasTargetThenResultIsFalse() { - + // When Value Has No InteractionType But Has Target var value = ActivityDefinition.builder().target(new ArrayList<>()).build(); - + var result = validator.isValid(value, null); - + // Then Result Is False assertFalse(result); } @Test void whenValueHasNoInteractionTypeButHasStepsThenResultIsFalse() { - + // When Value Has No InteractionType But Has Steps var value = ActivityDefinition.builder().steps(new ArrayList<>()).build(); - + var result = validator.isValid(value, null); - + // Then Result Is False assertFalse(result); } diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActorValidatorTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActorValidatorTests.java index bb440507..74b65525 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActorValidatorTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActorValidatorTests.java @@ -7,17 +7,15 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.net.URI; -import java.util.ArrayList; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - import dev.learning.xapi.model.Activity; import dev.learning.xapi.model.Agent; import dev.learning.xapi.model.Group; import dev.learning.xapi.model.StatementReference; import dev.learning.xapi.model.SubStatement; +import java.net.URI; +import java.util.ArrayList; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; /** * ActorValidator Tests. @@ -25,220 +23,226 @@ * @author István Rátkai (Selindek) */ @DisplayName("ActorValidator tests") -public class ActorValidatorTests { +class ActorValidatorTests { private static final ActorValidator validator = new ActorValidator(); - + @Test void whenValueIsNullThenResultIsTrue() { - + // When Value Is Null var result = validator.isValid(null, null); - + // Then Result Is True assertTrue(result); } - + @Test void whenValueIsActivityThenResultIsTrue() { - + // When Value Is Activity var value = Activity.builder().build(); var result = validator.isValid(value, null); - + // Then Result Is True assertTrue(result); } - + @Test void whenValueIsSubStatementThenResultIsTrue() { - + // When Value Is SubStatement var value = SubStatement.builder().build(); var result = validator.isValid(value, null); - + // Then Result Is True assertTrue(result); } - + @Test void whenValueIsStatementReferenceThenResultIsTrue() { - + // When Value Is StatementReference var value = StatementReference.builder().build(); var result = validator.isValid(value, null); - + // Then Result Is True assertTrue(result); } - + @Test void whenAgentValueHasNoIdentifierThenResultIsFalse() { - + // When Agent Value Has No Identifier var value = Agent.builder().build(); - + var result = validator.isValid(value, null); - + // Then Result Is False assertFalse(result); } - + @Test void whenAgentValueHasOnlyMboxThenResultIsTrue() { - + // When Agent Value Has Only Mbox var value = Agent.builder().mbox("mailto:fred@example.com").build(); - + var result = validator.isValid(value, null); - + // Then Result Is True assertTrue(result); } @Test void whenAgentValueHasOnlyMboxSha1sumThenResultIsTrue() { - + // When Agent Value Has Only MboxSha1sum var value = Agent.builder().mboxSha1sum("121212121212121212121212").build(); - + var result = validator.isValid(value, null); - + // Then Result Is True assertTrue(result); } @Test void whenAgentValueHasOnlyOpenIdThenResultIsTrue() { - + // When Agent Value Has Only MboxSha1sum - var value = Agent.builder().openid(URI.create("http://example.com/openid/121212121212121212")).build(); - + var value = + Agent.builder().openid(URI.create("http://example.com/openid/121212121212121212")).build(); + var result = validator.isValid(value, null); - + // Then Result Is True assertTrue(result); } @Test void whenAgentValueHasOnlyAccountThenResultIsTrue() { - + // When Agent Value Has Only Account - var value = Agent.builder().account(a->a.homePage(URI.create("http://example.com")).name("fred")).build(); - + var value = Agent.builder() + .account(a -> a.homePage(URI.create("http://example.com")).name("fred")).build(); + var result = validator.isValid(value, null); - + // Then Result Is True assertTrue(result); } @Test void whenAgentValueHasMoreThanOneIdentifierThenResultIsFalse() { - + // When Agent Value Has More Than One Identifier - var value = Agent.builder().mbox("mailto:fred@example.com").mboxSha1sum("121212121212121212121212").build(); - + var value = Agent.builder().mbox("mailto:fred@example.com") + .mboxSha1sum("121212121212121212121212").build(); + var result = validator.isValid(value, null); - + // Then Result Is False assertFalse(result); } - + @Test void whenGroupValueHasNoIdentifierThenResultIsFalse() { - + // When Group Value Has No Identifier var value = Group.builder().build(); - + var result = validator.isValid(value, null); - + // Then Result Is False assertFalse(result); } - + @Test void whenGroupValueHasNoIdentifierAndEmptyMembersThenResultIsFalse() { - + // When Group Value Has No Identifier And Empty Members var value = Group.builder().member(new ArrayList<>()).build(); - + var result = validator.isValid(value, null); - + // Then Result Is False assertFalse(result); } - + @Test void whenGroupValueHasNoIdentifierButHasMemberThenResultIsTrue() { - + // When Group Value Has No Identifier But Has Member - var value = Group.builder().addMember(a->a.mbox("mailto:fred@example.com")).build(); - + var value = Group.builder().addMember(a -> a.mbox("mailto:fred@example.com")).build(); + var result = validator.isValid(value, null); - + // Then Result Is True assertTrue(result); } - + @Test void whenGroupValueHasOnlyMboxThenResultIsTrue() { - + // When Group Value Has Only Mbox var value = Group.builder().mbox("mailto:fred@example.com").build(); - + var result = validator.isValid(value, null); - + // Then Result Is True assertTrue(result); } @Test void whenGroupValueHasOnlyMboxSha1sumThenResultIsTrue() { - + // When Group Value Has Only MboxSha1sum var value = Group.builder().mboxSha1sum("121212121212121212121212").build(); - + var result = validator.isValid(value, null); - + // Then Result Is True assertTrue(result); } @Test void whenGroupValueHasOnlyOpenIdThenResultIsTrue() { - + // When Group Value Has Only MboxSha1sum - var value = Group.builder().openid(URI.create("http://example.com/openid/121212121212121212")).build(); - + var value = + Group.builder().openid(URI.create("http://example.com/openid/121212121212121212")).build(); + var result = validator.isValid(value, null); - + // Then Result Is True assertTrue(result); } @Test void whenGroupValueHasOnlyAccountThenResultIsTrue() { - + // When Group Value Has Only Account - var value = Group.builder().account(a->a.homePage(URI.create("http://example.com")).name("fred")).build(); - + var value = Group.builder() + .account(a -> a.homePage(URI.create("http://example.com")).name("fred")).build(); + var result = validator.isValid(value, null); - + // Then Result Is True assertTrue(result); } @Test void whenGroupValueHasMoreThanOneIdentifierThenResultIsFalse() { - + // When Group Value Has More Than One Identifier - var value = Group.builder().mbox("mailto:fred@example.com").mboxSha1sum("121212121212121212121212").build(); - + var value = Group.builder().mbox("mailto:fred@example.com") + .mboxSha1sum("121212121212121212121212").build(); + var result = validator.isValid(value, null); - + // Then Result Is False assertFalse(result); } diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/MboxValidatorTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/MboxValidatorTests.java index 9d99b62d..7bb7071a 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/MboxValidatorTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/MboxValidatorTests.java @@ -22,7 +22,7 @@ * @author István Rátkai (Selindek) */ @DisplayName("MboxValidator tests") -public class MboxValidatorTests { +class MboxValidatorTests { private static final MboxValidator validator = new MboxValidator(); diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/NotUndeterminedValidatorTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/NotUndeterminedValidatorTests.java index 6869003f..862eb54f 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/NotUndeterminedValidatorTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/NotUndeterminedValidatorTests.java @@ -17,36 +17,38 @@ * @author István Rátkai (Selindek) */ @DisplayName("NotUndeterminedValidator tests") -public class NotUndeterminedValidatorTests { +class NotUndeterminedValidatorTests { private static final NotUndeterminedValidator validator = new NotUndeterminedValidator(); - + @Test void whenValueIsNullThenResultIsTrue() { - + // When Value Is Null var result = validator.isValid(null, null); - - //Then Result Is True + + // Then Result Is True assertTrue(result); } @Test - void whenValueIsUndeterminedLocaleThenResultIsFalse() throws NoSuchFieldException, SecurityException { - + void whenValueIsUndeterminedLocaleThenResultIsFalse() + throws NoSuchFieldException, SecurityException { + // When Value Is Undetermined Locale var result = validator.isValid(Locale.forLanguageTag("und"), null); - + // Then Result Is False assertFalse(result); } - + @Test - void whenValueIsNotUndeterminedLocaleThenResultIsTrue() throws NoSuchFieldException, SecurityException { - + void whenValueIsNotUndeterminedLocaleThenResultIsTrue() + throws NoSuchFieldException, SecurityException { + // When Value Is Not Undetermined Locale var result = validator.isValid(Locale.forLanguageTag("en-US"), null); - + // Then Result Is True assertTrue(result); } diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ScaledScoreValidatorTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ScaledScoreValidatorTests.java index 8516deed..44c1f6c5 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ScaledScoreValidatorTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ScaledScoreValidatorTests.java @@ -16,76 +16,76 @@ * @author István Rátkai (Selindek) */ @DisplayName("ScaledSoreValidator tests") -public class ScaledScoreValidatorTests { +class ScaledScoreValidatorTests { private static final ScaledScoreValidator validator = new ScaledScoreValidator(); @Test void whenValueIsNullThenResultIsTrue() { - + // When Value Is Null var result = validator.isValid(null, null); - + // Then Result Is True assertTrue(result); } - + @Test void whenValueIsValidScaledScoreThenResultIsTrue() { - + // When Value Is Valid Score var result = validator.isValid(0F, null); - + // Then Result Is True assertTrue(result); } @Test void whenValueIsValidMinimumScoreThenResultIsTrue() { - + // When Value Is Valid Minimum Score var result = validator.isValid(-1F, null); - + // Then Result Is True assertTrue(result); } @Test void whenValueIsValidMaximumScoreThenResultIsTrue() { - + // When Value Is Valid Maximum Score var result = validator.isValid(1F, null); - + // Then Result Is True assertTrue(result); } @Test void WhenValueIsOverMaximumScoreThenResultIsFalse() { - + // when Value Is Over Maximum Score var result = validator.isValid(1.001F, null); - + // Then Result Is False assertFalse(result); } @Test void whenValueIsBelowMinimumScoreThenResultIsFalse() { - + // When Value Is Below Minimum Score var result = validator.isValid(-1.001F, null); - + // Then Result Is False assertFalse(result); } @Test void whenValueIsNanThenResultIsFalse() { - + // When Value Is NaN var result = validator.isValid(Float.NaN, null); - + // Then Result Is False assertFalse(result); } From 98ab2fe128e39be018db848d23a92beff2ef3aa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20R=C3=A1tkai?= Date: Fri, 17 Mar 2023 16:06:12 +0000 Subject: [PATCH 26/34] add Locale Serializer/Deserializer --- .../xapi/jackson/LocaleDeserializer.java | 79 +++++++++++++++++++ .../xapi/jackson/LocaleSerializer.java | 55 +++++++++++++ .../java/dev/learning/xapi/model/Context.java | 7 ++ .../dev/learning/xapi/model/LanguageMap.java | 8 ++ .../xapi/model/ActivityDefinitionTests.java | 40 +++++----- .../learning/xapi/model/ActivityTests.java | 4 +- .../dev/learning/xapi/model/ContextTests.java | 6 +- .../xapi/model/InteractionComponentTests.java | 12 +-- .../learning/xapi/model/LanguageMapTests.java | 2 +- .../learning/xapi/model/StatementTests.java | 6 +- .../xapi/model/SubStatementTests.java | 10 +-- .../dev/learning/xapi/model/VerbTests.java | 14 ++-- .../src/test/resources/activity/activity.json | 4 +- .../activity/activity_with_object_type.json | 4 +- .../activity_definition.json | 14 ++-- .../activity_definition_without_type.json | 2 +- .../resources/activity_definition/choice.json | 12 +-- .../activity_definition/true_false.json | 4 +- .../src/test/resources/context/context.json | 2 +- .../context_with_empty_registration.json | 2 +- .../context/context_without_registration.json | 2 +- .../interaction_component.json | 2 +- .../test/resources/statement/statement.json | 6 +- .../sub_statement/sub_statement.json | 8 +- ...ent_with_object_of_type_sub_statement.json | 2 +- xapi-model/src/test/resources/verb/verb.json | 2 +- 26 files changed, 229 insertions(+), 80 deletions(-) create mode 100644 xapi-model/src/main/java/dev/learning/xapi/jackson/LocaleDeserializer.java create mode 100644 xapi-model/src/main/java/dev/learning/xapi/jackson/LocaleSerializer.java diff --git a/xapi-model/src/main/java/dev/learning/xapi/jackson/LocaleDeserializer.java b/xapi-model/src/main/java/dev/learning/xapi/jackson/LocaleDeserializer.java new file mode 100644 index 00000000..8136b638 --- /dev/null +++ b/xapi-model/src/main/java/dev/learning/xapi/jackson/LocaleDeserializer.java @@ -0,0 +1,79 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.jackson; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.KeyDeserializer; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; + +import java.io.IOException; +import java.util.Locale; +import java.util.Map; +import java.util.MissingResourceException; + +/** + * Specific Locale converter using {@link Locale#forLanguageTag(String)} instead of {@link Locale#Locale(String)}. + * + * @author István Rátkai (Selindek) + */ +public class LocaleDeserializer extends StdDeserializer { + + private static final long serialVersionUID = 7182941951585541965L; + + public LocaleDeserializer() { + super(String.class); + } + + @Override + public Locale deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { + if (jsonParser.getCurrentToken() != JsonToken.VALUE_STRING) { + throw deserializationContext.wrongTokenException(jsonParser, (JavaType) null, JsonToken.VALUE_STRING, + "Attempted to parse non-String value to Locale"); + } + + return validateLocale(jsonParser.getValueAsString(), deserializationContext); + } + + static Locale validateLocale(String localeString, DeserializationContext deserializationContext) + throws JsonMappingException { + + var locale = Locale.forLanguageTag(localeString); + + try { + // test validity of language and country codes (throws exception) + locale.getISO3Language(); + locale.getISO3Country(); + } catch (final MissingResourceException e) { + locale = null; + } + // test the validity of the whole key + if (locale == null || !locale.toLanguageTag().equalsIgnoreCase(localeString)) { + throw deserializationContext.weirdStringException(localeString, Locale.class, "Invalid locale"); + } + return locale; + } + + /** + *

+ * Locale Key Deserializer. + *

+ * For deserializing Locale keys in {@link Map}s + * + * @author István Rátkai (Selindek) + */ + public static class LocaleKeyDeserializer extends KeyDeserializer { + + @Override + public Object deserializeKey(String key, DeserializationContext deserializationContext) throws IOException { + + return validateLocale(key, deserializationContext); + } + + } +} diff --git a/xapi-model/src/main/java/dev/learning/xapi/jackson/LocaleSerializer.java b/xapi-model/src/main/java/dev/learning/xapi/jackson/LocaleSerializer.java new file mode 100644 index 00000000..db47198f --- /dev/null +++ b/xapi-model/src/main/java/dev/learning/xapi/jackson/LocaleSerializer.java @@ -0,0 +1,55 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.jackson; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import java.io.IOException; +import java.util.Locale; +import java.util.Map; + +/** + * Specific Locale serializer using {@link Locale#toLanguageTag()} instead of {@link Locale#toString()}. + * + * @author István Rátkai (Selindek) + */ +public class LocaleSerializer extends StdSerializer { + + private static final long serialVersionUID = 7182941951585541965L; + + public LocaleSerializer() { + super(Locale.class); + } + + @Override + public void serialize(Locale value, JsonGenerator gen, SerializerProvider provider) throws IOException { + gen.writeString(value.toLanguageTag()); + } + + /** + *

+ * Locale Key Serializer. + *

+ * For serializing Locale keys in {@link Map}s + * + * @author István Rátkai (Selindek) + */ + public static class LocaleKeySerializer extends StdSerializer { + + private static final long serialVersionUID = 7182941951585541965L; + + public LocaleKeySerializer() { + super(Locale.class); + } + + @Override + public void serialize(Locale value, JsonGenerator gen, SerializerProvider provider) throws IOException { + gen.writeFieldName(value.toLanguageTag()); + } + + } + +} diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Context.java b/xapi-model/src/main/java/dev/learning/xapi/model/Context.java index 8b4fd44d..ccdf4b43 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/Context.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/Context.java @@ -6,6 +6,11 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import dev.learning.xapi.jackson.LocaleDeserializer; +import dev.learning.xapi.jackson.LocaleSerializer; import dev.learning.xapi.model.validation.constraints.HasScheme; import dev.learning.xapi.model.validation.constraints.NotUndetermined; import dev.learning.xapi.model.validation.constraints.ValidActor; @@ -73,6 +78,8 @@ public class Context { * The language in which the experience being recorded in this Statement (mainly) occurred in. */ @NotUndetermined + @JsonSerialize(using = LocaleSerializer.class) + @JsonDeserialize(using = LocaleDeserializer.class) private Locale language; /** diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/LanguageMap.java b/xapi-model/src/main/java/dev/learning/xapi/model/LanguageMap.java index 9566f5ed..4b3e6e34 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/LanguageMap.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/LanguageMap.java @@ -11,6 +11,12 @@ import java.util.Map; import java.util.Optional; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import dev.learning.xapi.jackson.LocaleDeserializer; +import dev.learning.xapi.jackson.LocaleSerializer; + /** * A language map is a dictionary where the key is a RFC 5646 Language Tag, and the value is a * string in the language specified in the tag. @@ -20,6 +26,8 @@ * @see Language * Maps */ +@JsonSerialize(keyUsing = LocaleSerializer.LocaleKeySerializer.class) +@JsonDeserialize(keyUsing = LocaleDeserializer.LocaleKeyDeserializer.class) public class LanguageMap extends LinkedHashMap { private static final long serialVersionUID = 7375610804995032187L; diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/ActivityDefinitionTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/ActivityDefinitionTests.java index e8f848a1..c34d292a 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/ActivityDefinitionTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/ActivityDefinitionTests.java @@ -114,7 +114,7 @@ void whenDeserializingActivityDefinitionThenNameIsExpected() throws Exception { final ActivityDefinition result = objectMapper.readValue(file, ActivityDefinition.class); // Then Name Is Expected - assertThat(result.getName().get(Locale.ENGLISH), + assertThat(result.getName().get(Locale.US), is("Does the xAPI include the concept of statements?")); } @@ -129,7 +129,7 @@ void whenDeserializingActivityDefinitionThenDescriptionIsExpected() throws Excep final ActivityDefinition result = objectMapper.readValue(file, ActivityDefinition.class); // Then Description Is Expected - assertThat(result.getDescription().get(Locale.ENGLISH), is("pong[.]1:[,]dg[.]:10[,]lunch[.]")); + assertThat(result.getDescription().get(Locale.US), is("pong[.]1:[,]dg[.]:10[,]lunch[.]")); } @@ -188,7 +188,7 @@ void whenDeserializingActivityDefinitionWithChoicesThenChoicesDescriptionIsExpec final ActivityDefinition result = objectMapper.readValue(file, ActivityDefinition.class); // Then Choices Description Is Expected - assertThat(result.getChoices().get(0).getDescription().get(Locale.ENGLISH), + assertThat(result.getChoices().get(0).getDescription().get(Locale.US), is("Does the xAPI include the concept of statements?")); } @@ -218,7 +218,7 @@ void whenDeserializingActivityDefinitionWithScaledThenScaleDescriptionIsExpected final ActivityDefinition result = objectMapper.readValue(file, ActivityDefinition.class); // Then Scale Description Is Expected - assertThat(result.getScale().get(0).getDescription().get(Locale.ENGLISH), + assertThat(result.getScale().get(0).getDescription().get(Locale.US), is("Does the xAPI include the concept of statements?")); } @@ -248,7 +248,7 @@ void whenDeserializingActivityDefinitionWithSourceThenSourceDescriptionIsExpecte final ActivityDefinition result = objectMapper.readValue(file, ActivityDefinition.class); // Then Source Description Is Expected - assertThat(result.getSource().get(0).getDescription().get(Locale.ENGLISH), + assertThat(result.getSource().get(0).getDescription().get(Locale.US), is("Does the xAPI include the concept of statements?")); } @@ -278,7 +278,7 @@ void whenDeserializingActivityDefinitionWithTargetThenTargetDescriptionIsExpecte final ActivityDefinition result = objectMapper.readValue(file, ActivityDefinition.class); // Then Target Description Is Expected - assertThat(result.getTarget().get(0).getDescription().get(Locale.ENGLISH), + assertThat(result.getTarget().get(0).getDescription().get(Locale.US), is("Does the xAPI include the concept of statements?")); } @@ -308,7 +308,7 @@ void whenDeserializingActivityDefinitionWithStepsThenStepsDescriptionIsExpected( final ActivityDefinition result = objectMapper.readValue(file, ActivityDefinition.class); // Then Steps Description Is Expected - assertThat(result.getSteps().get(0).getDescription().get(Locale.ENGLISH), + assertThat(result.getSteps().get(0).getDescription().get(Locale.US), is("Does the xAPI include the concept of statements?")); } @@ -319,9 +319,9 @@ void whenSerializingActivityDefinitionOfInteractionTypeTrueFalseThenResultIsEqua final ActivityDefinition activityDefinition = ActivityDefinition.builder() - .addName(Locale.ENGLISH, "True false question") + .addName(Locale.US, "True false question") - .addDescription(Locale.ENGLISH, "Does the xAPI include the concept of statements?") + .addDescription(Locale.US, "Does the xAPI include the concept of statements?") .interactionType(InteractionType.TRUE_FALSE) @@ -347,21 +347,21 @@ void whenSerializingActivityDefinitionOfInteractionTypeChoiceThenResultIsEqualTo final ActivityDefinition activityDefinition = ActivityDefinition.builder() - .addName(Locale.ENGLISH, "Choice") + .addName(Locale.US, "Choice") - .addDescription(Locale.ENGLISH, "Which of these prototypes are available at the beta site?") + .addDescription(Locale.US, "Which of these prototypes are available at the beta site?") .interactionType(InteractionType.CHOICE) .correctResponsesPattern(Collections.singletonList("golf[,]tetris")) - .addChoice(c -> c.id("golf").addDescription(Locale.ENGLISH, "Golf Example")) + .addChoice(c -> c.id("golf").addDescription(Locale.US, "Golf Example")) - .addChoice(c -> c.id("facebook").addDescription(Locale.ENGLISH, "Facebook App")) + .addChoice(c -> c.id("facebook").addDescription(Locale.US, "Facebook App")) - .addChoice(c -> c.id("tetris").addDescription(Locale.ENGLISH, "Tetris Example")) + .addChoice(c -> c.id("tetris").addDescription(Locale.US, "Tetris Example")) - .addChoice(c -> c.id("scrabble").addDescription(Locale.ENGLISH, "Scrabble Example")) + .addChoice(c -> c.id("scrabble").addDescription(Locale.US, "Scrabble Example")) .type(URI.create("http://adlnet.gov/expapi/activities/cmi.interaction")) @@ -389,7 +389,7 @@ void whenCallingToStringThenResultIsExpected() throws Exception { // Then Result Is Expected assertThat(result, is( - "ActivityDefinition(name={en=Does the xAPI include the concept of statements?}, description={en=pong[.]1:[,]dg[.]:10[,]lunch[.]}, type=http://adlnet.gov/expapi/activities/cmi.interaction, moreInfo=http://example.com/more, interactionType=TRUE_FALSE, correctResponsesPattern=[{case_matters=false}{lang=en}To store and provide access to learning experiences.], choices=[InteractionComponent(id=1, description={en=Does the xAPI include the concept of statements?})], scale=[InteractionComponent(id=1, description={en=Does the xAPI include the concept of statements?})], source=[InteractionComponent(id=1, description={en=Does the xAPI include the concept of statements?})], target=[InteractionComponent(id=1, description={en=Does the xAPI include the concept of statements?})], steps=[InteractionComponent(id=1, description={en=Does the xAPI include the concept of statements?})], extensions={http://url=www.example.com})")); + "ActivityDefinition(name={en_US=Does the xAPI include the concept of statements?}, description={en_US=pong[.]1:[,]dg[.]:10[,]lunch[.]}, type=http://adlnet.gov/expapi/activities/cmi.interaction, moreInfo=http://example.com/more, interactionType=TRUE_FALSE, correctResponsesPattern=[{case_matters=false}{lang=en}To store and provide access to learning experiences.], choices=[InteractionComponent(id=1, description={en_US=Does the xAPI include the concept of statements?})], scale=[InteractionComponent(id=1, description={en_US=Does the xAPI include the concept of statements?})], source=[InteractionComponent(id=1, description={en_US=Does the xAPI include the concept of statements?})], target=[InteractionComponent(id=1, description={en_US=Does the xAPI include the concept of statements?})], steps=[InteractionComponent(id=1, description={en_US=Does the xAPI include the concept of statements?})], extensions={http://url=www.example.com})")); } @Test @@ -398,11 +398,11 @@ void whenBuildingActivityDefinitionWithTwoNameValuesThenNameLanguageMapHasTwoEnt // When Building ActivityDefinition With Two Name Values final ActivityDefinition activityDefinition = ActivityDefinition.builder() - .addName(Locale.ENGLISH, "True false question") + .addName(Locale.US, "True false question") .addName(Locale.GERMAN, "Richtig / Falsch-Frage") - .addDescription(Locale.ENGLISH, "Does the xAPI include the concept of statements?") + .addDescription(Locale.US, "Does the xAPI include the concept of statements?") .interactionType(InteractionType.TRUE_FALSE) @@ -423,9 +423,9 @@ void whenBuildingActivityDefinitionWithTwoDescriptionValuesThenDescriptionLangua // When Building ActivityDefinition With Two Description Values final ActivityDefinition activityDefinition = ActivityDefinition.builder() - .addName(Locale.ENGLISH, "True false question") + .addName(Locale.US, "True false question") - .addDescription(Locale.ENGLISH, "Does the xAPI include the concept of statements?") + .addDescription(Locale.US, "Does the xAPI include the concept of statements?") .addDescription(Locale.GERMAN, "Enthält die xAPI das Konzept von Anweisungen?") diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/ActivityTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/ActivityTests.java index f30d4f41..933dfb1e 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/ActivityTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/ActivityTests.java @@ -79,9 +79,9 @@ void whenSerializingActivityThenResultIsEqualToExpectedJson() throws IOException .definition(d -> d - .addName(Locale.ENGLISH, "simple statement") + .addName(Locale.US, "simple statement") - .addDescription(Locale.ENGLISH, + .addDescription(Locale.US, "A simple Experience API statement. Note that the LRS does not need to have any prior information about the Actor (learner), the verb, or the Activity/object.")) .build(); diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/ContextTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/ContextTests.java index 1e834553..c6902fe2 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/ContextTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/ContextTests.java @@ -93,7 +93,7 @@ void whenDeserializingContextThenLanguageIsExpected() throws Exception { final Context result = objectMapper.readValue(file, Context.class); // Then Language Is Expected - assertThat(result.getLanguage(), is(Locale.ENGLISH)); + assertThat(result.getLanguage(), is(Locale.US)); } @@ -230,7 +230,7 @@ void whenSerializingContextThenResultIsEqualToExpectedJson() throws IOException .platform("platform") - .language(Locale.ENGLISH) + .language(Locale.US) .statementReference(s -> s.id(UUID.fromString("e9b6b9ed-ef48-4986-9b86-2ef697578bf7"))) @@ -268,7 +268,7 @@ void whenCallingToStringThenResultIsExpected() throws IOException { // Then Result Is Expected assertThat(result, is( - "Context(registration=1d527164-ed0d-4b1d-9f9b-39aab0e4a089, instructor=Agent(super=Actor(name=Andrew Downes, mbox=mailto:andrew@example.co.uk, mboxSha1sum=null, openid=null, account=null)), team=Group(super=Actor(name=Example Group, mbox=null, mboxSha1sum=null, openid=null, account=null), member=null), contextActivities=ContextActivities(parent=[Activity(id=https://example.com/activity/1, definition=null)], grouping=[Activity(id=https://example.com/activity/2, definition=null)], category=[Activity(id=https://example.com/activity/3, definition=null)], other=[Activity(id=https://example.com/activity/4, definition=null)]), revision=revision, platform=platform, language=en, statement=StatementReference(id=e9b6b9ed-ef48-4986-9b86-2ef697578bf7), extensions={http://url=www.example.com})")); + "Context(registration=1d527164-ed0d-4b1d-9f9b-39aab0e4a089, instructor=Agent(super=Actor(name=Andrew Downes, mbox=mailto:andrew@example.co.uk, mboxSha1sum=null, openid=null, account=null)), team=Group(super=Actor(name=Example Group, mbox=null, mboxSha1sum=null, openid=null, account=null), member=null), contextActivities=ContextActivities(parent=[Activity(id=https://example.com/activity/1, definition=null)], grouping=[Activity(id=https://example.com/activity/2, definition=null)], category=[Activity(id=https://example.com/activity/3, definition=null)], other=[Activity(id=https://example.com/activity/4, definition=null)]), revision=revision, platform=platform, language=en_US, statement=StatementReference(id=e9b6b9ed-ef48-4986-9b86-2ef697578bf7), extensions={http://url=www.example.com})")); } diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/InteractionComponentTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/InteractionComponentTests.java index 05f986f0..6daa5363 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/InteractionComponentTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/InteractionComponentTests.java @@ -75,7 +75,7 @@ void whenDeserializingInteractionComponentThenDescriptionIsExpected() throws Exc final InteractionComponent result = objectMapper.readValue(file, InteractionComponent.class); // Then Description Is Expected - assertThat(result.getDescription().get(Locale.ENGLISH), is("value")); + assertThat(result.getDescription().get(Locale.US), is("value")); } @@ -86,7 +86,7 @@ void whenSerializingInteractionComponentThenResultIsEqualToExpectedJson() throws .id("1") - .addDescription(Locale.ENGLISH, "value") + .addDescription(Locale.US, "value") .build(); @@ -111,7 +111,7 @@ void whenCallingToStringThenResultIsExpected() throws IOException { final String result = interactionComponent.toString(); // Then Result Is Expected - assertThat(result, is("InteractionComponent(id=1, description={en=value})")); + assertThat(result, is("InteractionComponent(id=1, description={en_US=value})")); } @@ -120,7 +120,7 @@ void whenBuildingInteractionComponentWithTwoDescriptionValuesThenDisplayLanguage // When Building InteractionComponent With Two Description Values final InteractionComponent interactionComponent = InteractionComponent.builder().id("1") - .addDescription(Locale.ENGLISH, "value").addDescription(Locale.GERMAN, "Wert").build(); + .addDescription(Locale.US, "value").addDescription(Locale.GERMAN, "Wert").build(); // Then Description Language Map Has Two Entries assertThat(interactionComponent.getDescription(), aMapWithSize(2)); @@ -131,7 +131,7 @@ void whenBuildingInteractionComponentWithTwoDescriptionValuesThenDisplayLanguage void whenValidatingInteractionComponentWithAllRequiredPropertiesThenConstraintViolationsSizeIsZero() { final InteractionComponent interactionComponent = - InteractionComponent.builder().id("1").addDescription(Locale.ENGLISH, "value").build(); + InteractionComponent.builder().id("1").addDescription(Locale.US, "value").build(); // When Validating Interaction Component With All Required Properties final Set> constraintViolations = @@ -146,7 +146,7 @@ void whenValidatingInteractionComponentWithAllRequiredPropertiesThenConstraintVi void whenValidatingInteractionComponentWithoutIdThenConstraintViolationsSizeIsOne() { final InteractionComponent interactionComponent = - InteractionComponent.builder().addDescription(Locale.ENGLISH, "value").build(); + InteractionComponent.builder().addDescription(Locale.US, "value").build(); // When Validating Interaction Component Without Id final Set> constraintViolations = diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/LanguageMapTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/LanguageMapTests.java index 2373e136..6cb22782 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/LanguageMapTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/LanguageMapTests.java @@ -68,7 +68,7 @@ void givenFrenchAndEnglishKeyWhenGettingUSValueThenValueIsEnglish() throws Excep // Given French And English Key languageMap.put(Locale.FRENCH, "Couleur"); - languageMap.put(Locale.ENGLISH, "Color"); + languageMap.put(Locale.US, "Color"); // When Getting US Value final String value = languageMap.get(LanguageRange.parse("en-US")); diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/StatementTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/StatementTests.java index 0fc125f8..5ac03318 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/StatementTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/StatementTests.java @@ -240,9 +240,9 @@ void whenSerializingStatementThenResultIsEqualToExpectedJson() throws IOExceptio final Attachment attachment = Attachment.builder().usageType(URI.create("http://example.com")) .fileUrl(URI.create("http://example.com")) - .addDisplay(Locale.ENGLISH, "value") + .addDisplay(Locale.US, "value") - .addDescription(Locale.ENGLISH, "value") + .addDescription(Locale.US, "value") .length(123) @@ -282,7 +282,7 @@ void whenSerializingStatementThenResultIsEqualToExpectedJson() throws IOExceptio .platform("Example virtual meeting software") - .language(Locale.ENGLISH) + .language(Locale.US) .statementReference(s -> s.id(UUID.fromString("6690e6c9-3ef0-4ed3-8b37-7f3964730bee"))) diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/SubStatementTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/SubStatementTests.java index d9bdaf61..94c7b5bc 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/SubStatementTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/SubStatementTests.java @@ -280,7 +280,7 @@ void whenSerializingSubStatementThenResultIsEqualToExpectedJson() throws IOExcep .platform("platform") - .language(Locale.forLanguageTag("en")) + .language(Locale.US) .instructor(agent) @@ -297,9 +297,9 @@ void whenSerializingSubStatementThenResultIsEqualToExpectedJson() throws IOExcep final Attachment attachment = Attachment.builder().usageType(URI.create("http://example.com")) .fileUrl(URI.create("http://example.com")) - .addDisplay(Locale.ENGLISH, "value") + .addDisplay(Locale.US, "value") - .addDescription(Locale.ENGLISH, "value") + .addDescription(Locale.US, "value") .length(123) @@ -315,7 +315,7 @@ void whenSerializingSubStatementThenResultIsEqualToExpectedJson() throws IOExcep .actor(agent) - .verb(v -> v.id(URI.create("http://example.com/confirmed")).addDisplay(Locale.ENGLISH, + .verb(v -> v.id(URI.create("http://example.com/confirmed")).addDisplay(Locale.US, "confirmed")) .object(statementRef) @@ -351,7 +351,7 @@ void whenCallingToStringThenResultIsExpected() throws IOException { // Then Result Is Expected assertThat(result, is( - "SubStatement(actor=Agent(super=Actor(name=null, mbox=mailto:agent@example.com, mboxSha1sum=null, openid=null, account=null)), verb=Verb(id=http://example.com/confirmed, display={en=confirmed}), object=StatementReference(id=9e13cefd-53d3-4eac-b5ed-2cf6693903bb), result=Result(score=Score(scaled=1.0, raw=1.0, min=0.0, max=5.0), success=true, completion=true, response=test, duration=P1D, extensions=null), context=Context(registration=6d969975-8d7e-4506-ac19-877c57f2921a, instructor=Agent(super=Actor(name=null, mbox=mailto:agent@example.com, mboxSha1sum=null, openid=null, account=null)), team=Group(super=Actor(name=Example Group, mbox=null, mboxSha1sum=null, openid=null, account=null), member=null), contextActivities=ContextActivities(parent=[Activity(id=http://www.example.co.uk/exampleactivity, definition=null)], grouping=[Activity(id=http://www.example.co.uk/exampleactivity, definition=null)], category=[Activity(id=http://www.example.co.uk/exampleactivity, definition=null)], other=[Activity(id=http://www.example.co.uk/exampleactivity, definition=null)]), revision=revision, platform=platform, language=en, statement=StatementReference(id=9e13cefd-53d3-4eac-b5ed-2cf6693903bb), extensions={http://url=www.example.com}), timestamp=2015-11-18T11:17:00Z, attachments=[Attachment(usageType=http://example.com, display={en=value}, description={en=value}, contentType=file, length=123, sha2=123, fileUrl=http://example.com)])")); + "SubStatement(actor=Agent(super=Actor(name=null, mbox=mailto:agent@example.com, mboxSha1sum=null, openid=null, account=null)), verb=Verb(id=http://example.com/confirmed, display={en_US=confirmed}), object=StatementReference(id=9e13cefd-53d3-4eac-b5ed-2cf6693903bb), result=Result(score=Score(scaled=1.0, raw=1.0, min=0.0, max=5.0), success=true, completion=true, response=test, duration=P1D, extensions=null), context=Context(registration=6d969975-8d7e-4506-ac19-877c57f2921a, instructor=Agent(super=Actor(name=null, mbox=mailto:agent@example.com, mboxSha1sum=null, openid=null, account=null)), team=Group(super=Actor(name=Example Group, mbox=null, mboxSha1sum=null, openid=null, account=null), member=null), contextActivities=ContextActivities(parent=[Activity(id=http://www.example.co.uk/exampleactivity, definition=null)], grouping=[Activity(id=http://www.example.co.uk/exampleactivity, definition=null)], category=[Activity(id=http://www.example.co.uk/exampleactivity, definition=null)], other=[Activity(id=http://www.example.co.uk/exampleactivity, definition=null)]), revision=revision, platform=platform, language=en_US, statement=StatementReference(id=9e13cefd-53d3-4eac-b5ed-2cf6693903bb), extensions={http://url=www.example.com}), timestamp=2015-11-18T11:17:00Z, attachments=[Attachment(usageType=http://example.com, display={en_US=value}, description={en_US=value}, contentType=file, length=123, sha2=123, fileUrl=http://example.com)])")); } diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/VerbTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/VerbTests.java index 6b660629..5017dcda 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/VerbTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/VerbTests.java @@ -78,7 +78,7 @@ void whenSerializingVerbThenResultIsEqualToExpectedJson() throws IOException { .id(URI.create("http://adlnet.gov/expapi/verbs/answered")) - .addDisplay(Locale.ENGLISH, "answered") + .addDisplay(Locale.US, "answered") .build(); @@ -128,7 +128,7 @@ void whenCallingToStringThenResultIsExpected() throws IOException { // Then Result Is Expected assertThat(result, - is("Verb(id=http://adlnet.gov/expapi/verbs/answered, display={en=answered})")); + is("Verb(id=http://adlnet.gov/expapi/verbs/answered, display={en_US=answered})")); } @@ -187,7 +187,7 @@ void whenBuildingVerbWithTwoDisplayValuesThenDisplayLanguageMapHasTwoEntries() { .id("http://adlnet.gov/expapi/verbs/answered") - .addDisplay(Locale.ENGLISH, "answered") + .addDisplay(Locale.US, "answered") .addDisplay(Locale.GERMAN, "antwortete") @@ -217,7 +217,7 @@ void givenVerbWithoutDisplayPropertyWhenTestingEqualityWithVerbWithDisplayProper .id("http://adlnet.gov/expapi/verbs/answered") - .addDisplay(Locale.ENGLISH, "answered") + .addDisplay(Locale.US, "answered") .build()); @@ -243,7 +243,7 @@ void givenVerbWithGermanDisplayPropertyWhenTestingEqualityWithVerbWithEnglishDis .id("http://adlnet.gov/expapi/verbs/answered") - .addDisplay(Locale.ENGLISH, "answered") + .addDisplay(Locale.US, "answered") .build()); @@ -318,7 +318,7 @@ void givenVerbDoesNotHaveIdVoidedWhenCallingIsVoidedThenResultIsFalse() { void whenValidatingVerbWithAllRequiredPropertiesThenConstraintViolationsSizeIsZero() { final Verb verb = Verb.builder().id("http://adlnet.gov/expapi/verbs/answered") - .addDisplay(Locale.ENGLISH, "answered").build(); + .addDisplay(Locale.US, "answered").build(); // When Validating Interaction Component With All Required Properties final Set> constraintViolations = validator.validate(verb); @@ -331,7 +331,7 @@ void whenValidatingVerbWithAllRequiredPropertiesThenConstraintViolationsSizeIsZe @Test void whenValidatingVerbWithoutIdThenConstraintViolationsSizeIsOne() { - final Verb verb = Verb.builder().addDisplay(Locale.ENGLISH, "answered").build(); + final Verb verb = Verb.builder().addDisplay(Locale.US, "answered").build(); // When Validating Interaction Component Without Id final Set> constraintViolations = validator.validate(verb); diff --git a/xapi-model/src/test/resources/activity/activity.json b/xapi-model/src/test/resources/activity/activity.json index b4627ef1..138307a8 100644 --- a/xapi-model/src/test/resources/activity/activity.json +++ b/xapi-model/src/test/resources/activity/activity.json @@ -2,10 +2,10 @@ "id":"http://example.com/xapi/activity/simplestatement", "definition":{ "name":{ - "en":"simple statement" + "en-US":"simple statement" }, "description":{ - "en":"A simple Experience API statement. Note that the LRS does not need to have any prior information about the Actor (learner), the verb, or the Activity/object." + "en-US":"A simple Experience API statement. Note that the LRS does not need to have any prior information about the Actor (learner), the verb, or the Activity/object." } } } \ No newline at end of file diff --git a/xapi-model/src/test/resources/activity/activity_with_object_type.json b/xapi-model/src/test/resources/activity/activity_with_object_type.json index adfae696..b9825af2 100644 --- a/xapi-model/src/test/resources/activity/activity_with_object_type.json +++ b/xapi-model/src/test/resources/activity/activity_with_object_type.json @@ -3,10 +3,10 @@ "id":"http://example.com/xapi/activity/simplestatement", "definition":{ "name":{ - "en":"simple statement" + "en-US":"simple statement" }, "description":{ - "en":"A simple Experience API statement. Note that the LRS does not need to have any prior information about the Actor (learner), the verb, or the Activity/object." + "en-US":"A simple Experience API statement. Note that the LRS does not need to have any prior information about the Actor (learner), the verb, or the Activity/object." } } } \ No newline at end of file diff --git a/xapi-model/src/test/resources/activity_definition/activity_definition.json b/xapi-model/src/test/resources/activity_definition/activity_definition.json index b6cad8f5..31a19763 100644 --- a/xapi-model/src/test/resources/activity_definition/activity_definition.json +++ b/xapi-model/src/test/resources/activity_definition/activity_definition.json @@ -6,40 +6,40 @@ "http://url" : "www.example.com" }, "name" : { - "en" : "Does the xAPI include the concept of statements?" + "en-US" : "Does the xAPI include the concept of statements?" }, "description" : { - "en" : "pong[.]1:[,]dg[.]:10[,]lunch[.]" + "en-US" : "pong[.]1:[,]dg[.]:10[,]lunch[.]" }, "type" : "http://adlnet.gov/expapi/activities/cmi.interaction", "choices" : [ { "id" : "1", "description" : { - "en" : "Does the xAPI include the concept of statements?" + "en-US" : "Does the xAPI include the concept of statements?" } } ], "scale" : [ { "id" : "1", "description" : { - "en" : "Does the xAPI include the concept of statements?" + "en-US" : "Does the xAPI include the concept of statements?" } } ], "source" : [ { "id" : "1", "description" : { - "en" : "Does the xAPI include the concept of statements?" + "en-US" : "Does the xAPI include the concept of statements?" } } ], "target" : [ { "id" : "1", "description" : { - "en" : "Does the xAPI include the concept of statements?" + "en-US" : "Does the xAPI include the concept of statements?" } } ], "steps" : [ { "id" : "1", "description" : { - "en" : "Does the xAPI include the concept of statements?" + "en-US" : "Does the xAPI include the concept of statements?" } } ] } diff --git a/xapi-model/src/test/resources/activity_definition/activity_definition_without_type.json b/xapi-model/src/test/resources/activity_definition/activity_definition_without_type.json index 505fb28e..90c3470c 100644 --- a/xapi-model/src/test/resources/activity_definition/activity_definition_without_type.json +++ b/xapi-model/src/test/resources/activity_definition/activity_definition_without_type.json @@ -1,6 +1,6 @@ { "description": { - "en": "Does the xAPI include the concept of statements?" + "en-US": "Does the xAPI include the concept of statements?" }, "interactionType": "fill-in", "correctResponsesPattern": [ diff --git a/xapi-model/src/test/resources/activity_definition/choice.json b/xapi-model/src/test/resources/activity_definition/choice.json index 1d1bfbad..cf411f4a 100644 --- a/xapi-model/src/test/resources/activity_definition/choice.json +++ b/xapi-model/src/test/resources/activity_definition/choice.json @@ -1,9 +1,9 @@ { "name": { - "en": "Choice" + "en-US": "Choice" }, "description": { - "en": "Which of these prototypes are available at the beta site?" + "en-US": "Which of these prototypes are available at the beta site?" }, "type": "http://adlnet.gov/expapi/activities/cmi.interaction", "interactionType": "choice", @@ -14,25 +14,25 @@ { "id": "golf", "description": { - "en": "Golf Example" + "en-US": "Golf Example" } }, { "id": "facebook", "description": { - "en": "Facebook App" + "en-US": "Facebook App" } }, { "id": "tetris", "description": { - "en": "Tetris Example" + "en-US": "Tetris Example" } }, { "id": "scrabble", "description": { - "en": "Scrabble Example" + "en-US": "Scrabble Example" } } ] diff --git a/xapi-model/src/test/resources/activity_definition/true_false.json b/xapi-model/src/test/resources/activity_definition/true_false.json index 6523da45..b1a64e3a 100644 --- a/xapi-model/src/test/resources/activity_definition/true_false.json +++ b/xapi-model/src/test/resources/activity_definition/true_false.json @@ -1,9 +1,9 @@ { "name":{ - "en":"True false question" + "en-US":"True false question" }, "description":{ - "en":"Does the xAPI include the concept of statements?" + "en-US":"Does the xAPI include the concept of statements?" }, "type":"http://adlnet.gov/expapi/activities/cmi.interaction", "interactionType":"true-false", diff --git a/xapi-model/src/test/resources/context/context.json b/xapi-model/src/test/resources/context/context.json index 06d476a4..bb044dca 100644 --- a/xapi-model/src/test/resources/context/context.json +++ b/xapi-model/src/test/resources/context/context.json @@ -2,7 +2,7 @@ "registration": "1d527164-ed0d-4b1d-9f9b-39aab0e4a089", "revision": "revision", "platform": "platform", - "language": "en", + "language": "en-US", "instructor": { "objectType": "Agent", "name": "Andrew Downes", diff --git a/xapi-model/src/test/resources/context/context_with_empty_registration.json b/xapi-model/src/test/resources/context/context_with_empty_registration.json index 990ffb74..59e0f7af 100644 --- a/xapi-model/src/test/resources/context/context_with_empty_registration.json +++ b/xapi-model/src/test/resources/context/context_with_empty_registration.json @@ -2,7 +2,7 @@ "registration": "", "revision": "revision", "platform": "platform", - "language": "en", + "language": "en-US", "instructor": { "name": "Andrew Downes", "mbox": "mailto:andrew@example.co.uk" diff --git a/xapi-model/src/test/resources/context/context_without_registration.json b/xapi-model/src/test/resources/context/context_without_registration.json index cd324ee9..50f37e08 100644 --- a/xapi-model/src/test/resources/context/context_without_registration.json +++ b/xapi-model/src/test/resources/context/context_without_registration.json @@ -1,7 +1,7 @@ { "revision": "revision", "platform": "platform", - "language": "en", + "language": "en-US", "instructor": { "name": "Andrew Downes", "mbox": "mailto:andrew@example.co.uk" diff --git a/xapi-model/src/test/resources/interaction_component/interaction_component.json b/xapi-model/src/test/resources/interaction_component/interaction_component.json index b01f6834..546d196b 100644 --- a/xapi-model/src/test/resources/interaction_component/interaction_component.json +++ b/xapi-model/src/test/resources/interaction_component/interaction_component.json @@ -1,6 +1,6 @@ { "id": "1", "description": { - "en": "value" + "en-US": "value" } } diff --git a/xapi-model/src/test/resources/statement/statement.json b/xapi-model/src/test/resources/statement/statement.json index c0b6e5ee..0f1baf6f 100644 --- a/xapi-model/src/test/resources/statement/statement.json +++ b/xapi-model/src/test/resources/statement/statement.json @@ -32,7 +32,7 @@ "objectType": "Group" }, "platform": "Example virtual meeting software", - "language": "en", + "language": "en-US", "statement": { "objectType": "StatementRef", "id": "6690e6c9-3ef0-4ed3-8b37-7f3964730bee" @@ -69,10 +69,10 @@ { "usageType": "http://example.com", "display": { - "en": "value" + "en-US": "value" }, "description": { - "en": "value" + "en-US": "value" }, "contentType": "file", "length": 123, diff --git a/xapi-model/src/test/resources/sub_statement/sub_statement.json b/xapi-model/src/test/resources/sub_statement/sub_statement.json index 58955494..a4db3e02 100644 --- a/xapi-model/src/test/resources/sub_statement/sub_statement.json +++ b/xapi-model/src/test/resources/sub_statement/sub_statement.json @@ -4,7 +4,7 @@ "verb" : { "id" : "http://example.com/confirmed", "display" : { - "en" : "confirmed" + "en-US" : "confirmed" } }, "actor" : { @@ -31,7 +31,7 @@ "registration" : "6d969975-8d7e-4506-ac19-877c57f2921a", "revision" : "revision", "platform" : "platform", - "language" : "en", + "language" : "en-US", "instructor" : { "objectType" : "Agent", "mbox" : "mailto:agent@example.com" @@ -69,10 +69,10 @@ "attachments" : [ { "usageType" : "http://example.com", "display" : { - "en" : "value" + "en-US" : "value" }, "description" : { - "en" : "value" + "en-US" : "value" }, "contentType" : "file", "length" : 123, diff --git a/xapi-model/src/test/resources/sub_statement/sub_statement_with_object_of_type_sub_statement.json b/xapi-model/src/test/resources/sub_statement/sub_statement_with_object_of_type_sub_statement.json index 4b6cd007..e5c65d94 100644 --- a/xapi-model/src/test/resources/sub_statement/sub_statement_with_object_of_type_sub_statement.json +++ b/xapi-model/src/test/resources/sub_statement/sub_statement_with_object_of_type_sub_statement.json @@ -19,7 +19,7 @@ "verb":{ "id":"http://example.com/confirmed", "display":{ - "en":"confirmed" + "en-US":"confirmed" } }, "object":{ diff --git a/xapi-model/src/test/resources/verb/verb.json b/xapi-model/src/test/resources/verb/verb.json index 78c1c7c7..5d9236b9 100644 --- a/xapi-model/src/test/resources/verb/verb.json +++ b/xapi-model/src/test/resources/verb/verb.json @@ -1,6 +1,6 @@ { "id": "http://adlnet.gov/expapi/verbs/answered", "display": { - "en": "answered" + "en-US": "answered" } } From 08070f03bc393de9767a324def52ba950b4442cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20R=C3=A1tkai?= Date: Fri, 17 Mar 2023 16:11:37 +0000 Subject: [PATCH 27/34] fcs --- .../xapi/jackson/LocaleDeserializer.java | 19 +++++++++++-------- .../xapi/jackson/LocaleSerializer.java | 11 +++++++---- .../java/dev/learning/xapi/model/Context.java | 1 - .../dev/learning/xapi/model/LanguageMap.java | 10 ++++------ 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/xapi-model/src/main/java/dev/learning/xapi/jackson/LocaleDeserializer.java b/xapi-model/src/main/java/dev/learning/xapi/jackson/LocaleDeserializer.java index 8136b638..200286fb 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/jackson/LocaleDeserializer.java +++ b/xapi-model/src/main/java/dev/learning/xapi/jackson/LocaleDeserializer.java @@ -11,14 +11,14 @@ import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.KeyDeserializer; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; - import java.io.IOException; import java.util.Locale; import java.util.Map; import java.util.MissingResourceException; /** - * Specific Locale converter using {@link Locale#forLanguageTag(String)} instead of {@link Locale#Locale(String)}. + * Specific Locale converter using {@link Locale#forLanguageTag(String)} instead of + * {@link Locale#Locale(String)}. * * @author István Rátkai (Selindek) */ @@ -31,10 +31,11 @@ public LocaleDeserializer() { } @Override - public Locale deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { + public Locale deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) + throws IOException { if (jsonParser.getCurrentToken() != JsonToken.VALUE_STRING) { - throw deserializationContext.wrongTokenException(jsonParser, (JavaType) null, JsonToken.VALUE_STRING, - "Attempted to parse non-String value to Locale"); + throw deserializationContext.wrongTokenException(jsonParser, (JavaType) null, + JsonToken.VALUE_STRING, "Attempted to parse non-String value to Locale"); } return validateLocale(jsonParser.getValueAsString(), deserializationContext); @@ -54,7 +55,8 @@ static Locale validateLocale(String localeString, DeserializationContext deseria } // test the validity of the whole key if (locale == null || !locale.toLanguageTag().equalsIgnoreCase(localeString)) { - throw deserializationContext.weirdStringException(localeString, Locale.class, "Invalid locale"); + throw deserializationContext.weirdStringException(localeString, Locale.class, + "Invalid locale"); } return locale; } @@ -64,13 +66,14 @@ static Locale validateLocale(String localeString, DeserializationContext deseria * Locale Key Deserializer. *

* For deserializing Locale keys in {@link Map}s - * + * * @author István Rátkai (Selindek) */ public static class LocaleKeyDeserializer extends KeyDeserializer { @Override - public Object deserializeKey(String key, DeserializationContext deserializationContext) throws IOException { + public Object deserializeKey(String key, DeserializationContext deserializationContext) + throws IOException { return validateLocale(key, deserializationContext); } diff --git a/xapi-model/src/main/java/dev/learning/xapi/jackson/LocaleSerializer.java b/xapi-model/src/main/java/dev/learning/xapi/jackson/LocaleSerializer.java index db47198f..02253f2e 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/jackson/LocaleSerializer.java +++ b/xapi-model/src/main/java/dev/learning/xapi/jackson/LocaleSerializer.java @@ -12,8 +12,9 @@ import java.util.Map; /** - * Specific Locale serializer using {@link Locale#toLanguageTag()} instead of {@link Locale#toString()}. - * + * Specific Locale serializer using {@link Locale#toLanguageTag()} instead of + * {@link Locale#toString()}. + * * @author István Rátkai (Selindek) */ public class LocaleSerializer extends StdSerializer { @@ -25,7 +26,8 @@ public LocaleSerializer() { } @Override - public void serialize(Locale value, JsonGenerator gen, SerializerProvider provider) throws IOException { + public void serialize(Locale value, JsonGenerator gen, SerializerProvider provider) + throws IOException { gen.writeString(value.toLanguageTag()); } @@ -46,7 +48,8 @@ public LocaleKeySerializer() { } @Override - public void serialize(Locale value, JsonGenerator gen, SerializerProvider provider) throws IOException { + public void serialize(Locale value, JsonGenerator gen, SerializerProvider provider) + throws IOException { gen.writeFieldName(value.toLanguageTag()); } diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Context.java b/xapi-model/src/main/java/dev/learning/xapi/model/Context.java index ccdf4b43..eca82581 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/Context.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/Context.java @@ -8,7 +8,6 @@ import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; - import dev.learning.xapi.jackson.LocaleDeserializer; import dev.learning.xapi.jackson.LocaleSerializer; import dev.learning.xapi.model.validation.constraints.HasScheme; diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/LanguageMap.java b/xapi-model/src/main/java/dev/learning/xapi/model/LanguageMap.java index 4b3e6e34..5d18d88b 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/LanguageMap.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/LanguageMap.java @@ -4,6 +4,10 @@ package dev.learning.xapi.model; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import dev.learning.xapi.jackson.LocaleDeserializer; +import dev.learning.xapi.jackson.LocaleSerializer; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; @@ -11,12 +15,6 @@ import java.util.Map; import java.util.Optional; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; - -import dev.learning.xapi.jackson.LocaleDeserializer; -import dev.learning.xapi.jackson.LocaleSerializer; - /** * A language map is a dictionary where the key is a RFC 5646 Language Tag, and the value is a * string in the language specified in the tag. From 8d5a40488b83f16f4220b209a1a78c36a059d096 Mon Sep 17 00:00:00 2001 From: Thomas Turrell-Croft Date: Sat, 18 Mar 2023 07:00:46 +0000 Subject: [PATCH 28/34] Update xapi-model/pom.xml --- xapi-model/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xapi-model/pom.xml b/xapi-model/pom.xml index a124410d..8b21d1f5 100644 --- a/xapi-model/pom.xml +++ b/xapi-model/pom.xml @@ -30,7 +30,7 @@ org.hibernate.validator hibernate-validator - provided + true org.springframework.boot From 80fbdad9b3e2cbe5bfe22a9156677a325a0712e0 Mon Sep 17 00:00:00 2001 From: Thomas Turrell-Croft Date: Sat, 18 Mar 2023 15:56:31 +0000 Subject: [PATCH 29/34] tip --- .../{ValidableStatement.java => CoreStatement.java} | 10 ++++++---- .../main/java/dev/learning/xapi/model/Statement.java | 2 +- .../java/dev/learning/xapi/model/SubStatement.java | 2 +- .../validators/StatementPlatformValidator.java | 6 +++--- .../validators/StatementRevisionValidator.java | 6 +++--- .../internal/validators/StatementVerbValidator.java | 12 ++++++------ 6 files changed, 20 insertions(+), 18 deletions(-) rename xapi-model/src/main/java/dev/learning/xapi/model/{ValidableStatement.java => CoreStatement.java} (70%) diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/ValidableStatement.java b/xapi-model/src/main/java/dev/learning/xapi/model/CoreStatement.java similarity index 70% rename from xapi-model/src/main/java/dev/learning/xapi/model/ValidableStatement.java rename to xapi-model/src/main/java/dev/learning/xapi/model/CoreStatement.java index 7aaeeb2d..ed7f1e9c 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/ValidableStatement.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/CoreStatement.java @@ -5,15 +5,17 @@ package dev.learning.xapi.model; /** - * This is a helper interface for signing Statement-like objects which can be targets of - * class level Statement-validators. + * This is a helper interface for signing Statement-like objects which can be targets of class level + * Statement-validators. * * @author István Rátkai (Selindek) */ -public interface ValidableStatement { +public interface CoreStatement { + + Actor getActor(); Verb getVerb(); - + Object getObject(); Context getContext(); diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Statement.java b/xapi-model/src/main/java/dev/learning/xapi/model/Statement.java index 9b071c68..3f0b057e 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/Statement.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/Statement.java @@ -43,7 +43,7 @@ @Builder(toBuilder = true) @JsonInclude(Include.NON_EMPTY) @EqualsAndHashCode(of = {"actor", "verb", "object", "result", "context"}) -public class Statement implements ValidableStatement { +public class Statement implements CoreStatement { /** * UUID assigned by LRS if not set by the Learning Record Provider. diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/SubStatement.java b/xapi-model/src/main/java/dev/learning/xapi/model/SubStatement.java index a13ac425..c9dbb53c 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/SubStatement.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/SubStatement.java @@ -29,7 +29,7 @@ @ValidStatementPlatform @ValidStatementRevision @EqualsAndHashCode(exclude = {"timestamp", "attachments"}) -public class SubStatement implements StatementObject, ValidableStatement { +public class SubStatement implements StatementObject, CoreStatement { /** * Whom the Statement is about, as an Agent or Group Object. diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/StatementPlatformValidator.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/StatementPlatformValidator.java index 388c99cd..ab3a0090 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/StatementPlatformValidator.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/StatementPlatformValidator.java @@ -5,7 +5,7 @@ package dev.learning.xapi.model.validation.internal.validators; import dev.learning.xapi.model.Activity; -import dev.learning.xapi.model.ValidableStatement; +import dev.learning.xapi.model.CoreStatement; import dev.learning.xapi.model.validation.constraints.ValidStatementPlatform; import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; @@ -22,10 +22,10 @@ * Statement Context Requirements */ public class StatementPlatformValidator implements - ConstraintValidator { + ConstraintValidator { @Override - public boolean isValid(ValidableStatement value, ConstraintValidatorContext context) { + public boolean isValid(CoreStatement value, ConstraintValidatorContext context) { return value == null || value.getContext() == null || value.getContext().getPlatform() == null || value.getObject() instanceof Activity; diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/StatementRevisionValidator.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/StatementRevisionValidator.java index 417e39c6..cbfc33d2 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/StatementRevisionValidator.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/StatementRevisionValidator.java @@ -5,7 +5,7 @@ package dev.learning.xapi.model.validation.internal.validators; import dev.learning.xapi.model.Activity; -import dev.learning.xapi.model.ValidableStatement; +import dev.learning.xapi.model.CoreStatement; import dev.learning.xapi.model.validation.constraints.ValidStatementRevision; import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; @@ -22,10 +22,10 @@ * Statement Context Requirements */ public class StatementRevisionValidator implements - ConstraintValidator { + ConstraintValidator { @Override - public boolean isValid(ValidableStatement value, ConstraintValidatorContext context) { + public boolean isValid(CoreStatement value, ConstraintValidatorContext context) { return value == null || value.getContext() == null || value.getContext().getRevision() == null || value.getObject() instanceof Activity; diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/StatementVerbValidator.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/StatementVerbValidator.java index f755923b..71334043 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/StatementVerbValidator.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/StatementVerbValidator.java @@ -4,8 +4,8 @@ package dev.learning.xapi.model.validation.internal.validators; +import dev.learning.xapi.model.CoreStatement; import dev.learning.xapi.model.StatementReference; -import dev.learning.xapi.model.ValidableStatement; import dev.learning.xapi.model.validation.constraints.ValidStatementVerb; import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; @@ -21,15 +21,15 @@ * @see * Voiding Statement Requirements */ -public class StatementVerbValidator implements - ConstraintValidator { +public class StatementVerbValidator + implements ConstraintValidator { @Override - public boolean isValid(ValidableStatement value, ConstraintValidatorContext context) { + public boolean isValid(CoreStatement value, ConstraintValidatorContext context) { - return value == null || value.getVerb() == null || !value.getVerb().isVoided() + return value == null || value.getVerb() == null || !value.getVerb().isVoided() || value.getObject() instanceof StatementReference; - + } } From 751ffbf8269c2bc50c578116e6b73b6eba42c691 Mon Sep 17 00:00:00 2001 From: Thomas Turrell-Croft Date: Sat, 18 Mar 2023 17:19:44 +0000 Subject: [PATCH 30/34] tip --- .../validation/internal/validators/ActorValidator.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ActorValidator.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ActorValidator.java index bc45b1ed..4c057a33 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ActorValidator.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ActorValidator.java @@ -18,7 +18,7 @@ * @author István Rátkai (Selindek) * @see Actor */ -public class ActorValidator implements ConstraintValidator { +public class ActorValidator implements ConstraintValidator { /** * Checks if this {@link Actor} contains exactly one identifier. @@ -26,7 +26,7 @@ public class ActorValidator implements ConstraintValidator Date: Sat, 18 Mar 2023 17:52:23 +0000 Subject: [PATCH 31/34] tip --- .../dev/learning/xapi/model/SubStatement.java | 36 +++++++++++++++++++ .../learning/xapi/model/StatementTests.java | 34 ++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/SubStatement.java b/xapi-model/src/main/java/dev/learning/xapi/model/SubStatement.java index c9dbb53c..faa911e7 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/SubStatement.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/SubStatement.java @@ -86,6 +86,42 @@ public static class Builder { // This static class extends the lombok builder. + /** + * Consumer Builder for agent. + * + * @param agent The Consumer Builder for agent + * + * @return This builder + * + * @see SubStatement#actor + */ + public Builder actor(Consumer> agent) { + + // TODO Handle a Group Builder + + final Agent.Builder builder = Agent.builder(); + + agent.accept(builder); + + return actor(builder.build()); + } + + /** + * Sets the agent. + * + * @param actor The actor of the Statement + * + * @return This builder + * + * @see SubStatement#actor + */ + public Builder actor(Actor actor) { + + this.actor = actor; + + return this; + } + /** * Consumer Builder for verb. * diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/StatementTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/StatementTests.java index 5ac03318..c2fdcb1c 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/StatementTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/StatementTests.java @@ -449,5 +449,39 @@ void whenValidatingStatementWithoutAnActivityThenConstraintViolationsSizeIsOne() } + @Test + void whenValidatingStatementWithSubStatementWithStatementRefrenceThenConstraintViolationsSizeIsZero() { + + StatementReference statementRef = StatementReference.builder() + .id(UUID.fromString("9e13cefd-53d3-4eac-b5ed-2cf6693903bb")).build(); + + SubStatement subStatement = SubStatement.builder() + + .actor(a -> a.name("A N Other").mbox("mailto:another@example.com")) + + .verb(Verb.EXPERIENCED) + + .object(statementRef) + + .build(); + + final Statement statement = Statement.builder() + + .actor(a -> a.name("A N Other").mbox("mailto:another@example.com")) + + .verb(Verb.EXPERIENCED) + + .object(subStatement) + + .build(); + + // When Validating Statement With SubStatement With StatementRefrence + final Set> constraintViolations = validator.validate(statement); + + // Then ConstraintViolations Size Is Zero + assertThat(constraintViolations, hasSize(0)); + + } + } From 5c8832e0966bb5174a58c5e7b7b86d8c58a30d73 Mon Sep 17 00:00:00 2001 From: Thomas Turrell-Croft Date: Sun, 19 Mar 2023 13:13:22 +0000 Subject: [PATCH 32/34] tip --- .../learning/xapi/model/CoreStatement.java | 33 +++++++++++++++++-- .../dev/learning/xapi/model/SubStatement.java | 18 ++++++---- .../xapi/model/SubStatementTests.java | 1 + 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/CoreStatement.java b/xapi-model/src/main/java/dev/learning/xapi/model/CoreStatement.java index ed7f1e9c..374d5b3f 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/CoreStatement.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/CoreStatement.java @@ -4,20 +4,49 @@ package dev.learning.xapi.model; +import java.time.Instant; +import java.util.List; + /** - * This is a helper interface for signing Statement-like objects which can be targets of class level - * Statement-validators. + * Contains the methods that are in common between Statement and SubStatement. * * @author István Rátkai (Selindek) */ public interface CoreStatement { + /** + * Whom the Statement is about, as an Agent or Group Object. + */ Actor getActor(); + /** + * Action taken by the Actor. + */ Verb getVerb(); + /** + * Activity, Agent, or another Statement that is the Object of the Statement. + */ Object getObject(); + /** + * Result Object, further details representing a measured outcome. + */ + Result getResult(); + + /** + * Context that gives the Statement more meaning. + */ Context getContext(); + /** + * Timestamp of when the events described within this Statement occurred. + */ + Instant getTimestamp(); + + /** + * Headers for Attachments to the Statement. + */ + List getAttachments(); + } diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/SubStatement.java b/xapi-model/src/main/java/dev/learning/xapi/model/SubStatement.java index faa911e7..a7fdab11 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/SubStatement.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/SubStatement.java @@ -32,7 +32,7 @@ public class SubStatement implements StatementObject, CoreStatement { /** - * Whom the Statement is about, as an Agent or Group Object. + * {@inheritDoc} */ @NotNull @Valid @@ -40,14 +40,18 @@ public class SubStatement implements StatementObject, CoreStatement { private Actor actor; /** - * Action taken by the Actor. + * {@inheritDoc} */ @NotNull @Valid private Verb verb; /** - * Activity or Agent. + * {@inheritDoc} + * + *

+ * A SubStatement MUST NOT contain a SubStatement of its own, i.e., cannot be nested. + *

*/ @NotNull @Valid @@ -55,24 +59,24 @@ public class SubStatement implements StatementObject, CoreStatement { private SubStatementObject object; /** - * Result Object, further details representing a measured outcome. + * {@inheritDoc} */ @Valid private Result result; /** - * Context that gives the Statement more meaning. + * {@inheritDoc} */ @Valid private Context context; /** - * Timestamp of when the events described within this Statement occurred. + * {@inheritDoc} */ private Instant timestamp; /** - * Headers for Attachments to the Statement. + * {@inheritDoc} */ @Valid private List attachments; diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/SubStatementTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/SubStatementTests.java index 94c7b5bc..da8eb198 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/SubStatementTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/SubStatementTests.java @@ -8,6 +8,7 @@ import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertThrows; + import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.File; From c7587fa694d43ead45964520a3bcf2a82ca862fc Mon Sep 17 00:00:00 2001 From: Thomas Turrell-Croft Date: Sun, 19 Mar 2023 13:53:47 +0000 Subject: [PATCH 33/34] tip --- .../xapi/jackson/LocaleDeserializer.java | 82 ------------------- .../xapi/jackson/LocaleSerializer.java | 58 ------------- .../java/dev/learning/xapi/model/Context.java | 6 -- .../dev/learning/xapi/model/LanguageMap.java | 6 -- .../xapi/model/ActivityDefinitionTests.java | 40 ++++----- .../learning/xapi/model/ActivityTests.java | 4 +- .../dev/learning/xapi/model/ContextTests.java | 6 +- .../xapi/model/InteractionComponentTests.java | 12 +-- .../learning/xapi/model/LanguageMapTests.java | 2 +- .../learning/xapi/model/StatementTests.java | 10 +-- .../xapi/model/SubStatementTests.java | 10 +-- .../dev/learning/xapi/model/VerbTests.java | 14 ++-- .../src/test/resources/activity/activity.json | 4 +- .../activity/activity_with_object_type.json | 4 +- .../activity_definition.json | 14 ++-- .../activity_definition_without_type.json | 2 +- .../resources/activity_definition/choice.json | 12 +-- .../activity_definition/true_false.json | 4 +- .../src/test/resources/context/context.json | 2 +- .../context_with_empty_registration.json | 2 +- .../context/context_without_registration.json | 2 +- .../interaction_component.json | 2 +- .../test/resources/statement/statement.json | 6 +- .../sub_statement/sub_statement.json | 8 +- ...ent_with_object_of_type_sub_statement.json | 2 +- xapi-model/src/test/resources/verb/verb.json | 2 +- 26 files changed, 82 insertions(+), 234 deletions(-) delete mode 100644 xapi-model/src/main/java/dev/learning/xapi/jackson/LocaleDeserializer.java delete mode 100644 xapi-model/src/main/java/dev/learning/xapi/jackson/LocaleSerializer.java diff --git a/xapi-model/src/main/java/dev/learning/xapi/jackson/LocaleDeserializer.java b/xapi-model/src/main/java/dev/learning/xapi/jackson/LocaleDeserializer.java deleted file mode 100644 index 200286fb..00000000 --- a/xapi-model/src/main/java/dev/learning/xapi/jackson/LocaleDeserializer.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. - */ - -package dev.learning.xapi.jackson; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.KeyDeserializer; -import com.fasterxml.jackson.databind.deser.std.StdDeserializer; -import java.io.IOException; -import java.util.Locale; -import java.util.Map; -import java.util.MissingResourceException; - -/** - * Specific Locale converter using {@link Locale#forLanguageTag(String)} instead of - * {@link Locale#Locale(String)}. - * - * @author István Rátkai (Selindek) - */ -public class LocaleDeserializer extends StdDeserializer { - - private static final long serialVersionUID = 7182941951585541965L; - - public LocaleDeserializer() { - super(String.class); - } - - @Override - public Locale deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) - throws IOException { - if (jsonParser.getCurrentToken() != JsonToken.VALUE_STRING) { - throw deserializationContext.wrongTokenException(jsonParser, (JavaType) null, - JsonToken.VALUE_STRING, "Attempted to parse non-String value to Locale"); - } - - return validateLocale(jsonParser.getValueAsString(), deserializationContext); - } - - static Locale validateLocale(String localeString, DeserializationContext deserializationContext) - throws JsonMappingException { - - var locale = Locale.forLanguageTag(localeString); - - try { - // test validity of language and country codes (throws exception) - locale.getISO3Language(); - locale.getISO3Country(); - } catch (final MissingResourceException e) { - locale = null; - } - // test the validity of the whole key - if (locale == null || !locale.toLanguageTag().equalsIgnoreCase(localeString)) { - throw deserializationContext.weirdStringException(localeString, Locale.class, - "Invalid locale"); - } - return locale; - } - - /** - *

- * Locale Key Deserializer. - *

- * For deserializing Locale keys in {@link Map}s - * - * @author István Rátkai (Selindek) - */ - public static class LocaleKeyDeserializer extends KeyDeserializer { - - @Override - public Object deserializeKey(String key, DeserializationContext deserializationContext) - throws IOException { - - return validateLocale(key, deserializationContext); - } - - } -} diff --git a/xapi-model/src/main/java/dev/learning/xapi/jackson/LocaleSerializer.java b/xapi-model/src/main/java/dev/learning/xapi/jackson/LocaleSerializer.java deleted file mode 100644 index 02253f2e..00000000 --- a/xapi-model/src/main/java/dev/learning/xapi/jackson/LocaleSerializer.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. - */ - -package dev.learning.xapi.jackson; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; -import java.io.IOException; -import java.util.Locale; -import java.util.Map; - -/** - * Specific Locale serializer using {@link Locale#toLanguageTag()} instead of - * {@link Locale#toString()}. - * - * @author István Rátkai (Selindek) - */ -public class LocaleSerializer extends StdSerializer { - - private static final long serialVersionUID = 7182941951585541965L; - - public LocaleSerializer() { - super(Locale.class); - } - - @Override - public void serialize(Locale value, JsonGenerator gen, SerializerProvider provider) - throws IOException { - gen.writeString(value.toLanguageTag()); - } - - /** - *

- * Locale Key Serializer. - *

- * For serializing Locale keys in {@link Map}s - * - * @author István Rátkai (Selindek) - */ - public static class LocaleKeySerializer extends StdSerializer { - - private static final long serialVersionUID = 7182941951585541965L; - - public LocaleKeySerializer() { - super(Locale.class); - } - - @Override - public void serialize(Locale value, JsonGenerator gen, SerializerProvider provider) - throws IOException { - gen.writeFieldName(value.toLanguageTag()); - } - - } - -} diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Context.java b/xapi-model/src/main/java/dev/learning/xapi/model/Context.java index eca82581..8b4fd44d 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/Context.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/Context.java @@ -6,10 +6,6 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import dev.learning.xapi.jackson.LocaleDeserializer; -import dev.learning.xapi.jackson.LocaleSerializer; import dev.learning.xapi.model.validation.constraints.HasScheme; import dev.learning.xapi.model.validation.constraints.NotUndetermined; import dev.learning.xapi.model.validation.constraints.ValidActor; @@ -77,8 +73,6 @@ public class Context { * The language in which the experience being recorded in this Statement (mainly) occurred in. */ @NotUndetermined - @JsonSerialize(using = LocaleSerializer.class) - @JsonDeserialize(using = LocaleDeserializer.class) private Locale language; /** diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/LanguageMap.java b/xapi-model/src/main/java/dev/learning/xapi/model/LanguageMap.java index 5d18d88b..9566f5ed 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/LanguageMap.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/LanguageMap.java @@ -4,10 +4,6 @@ package dev.learning.xapi.model; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import dev.learning.xapi.jackson.LocaleDeserializer; -import dev.learning.xapi.jackson.LocaleSerializer; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; @@ -24,8 +20,6 @@ * @see Language * Maps */ -@JsonSerialize(keyUsing = LocaleSerializer.LocaleKeySerializer.class) -@JsonDeserialize(keyUsing = LocaleDeserializer.LocaleKeyDeserializer.class) public class LanguageMap extends LinkedHashMap { private static final long serialVersionUID = 7375610804995032187L; diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/ActivityDefinitionTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/ActivityDefinitionTests.java index c34d292a..e8f848a1 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/ActivityDefinitionTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/ActivityDefinitionTests.java @@ -114,7 +114,7 @@ void whenDeserializingActivityDefinitionThenNameIsExpected() throws Exception { final ActivityDefinition result = objectMapper.readValue(file, ActivityDefinition.class); // Then Name Is Expected - assertThat(result.getName().get(Locale.US), + assertThat(result.getName().get(Locale.ENGLISH), is("Does the xAPI include the concept of statements?")); } @@ -129,7 +129,7 @@ void whenDeserializingActivityDefinitionThenDescriptionIsExpected() throws Excep final ActivityDefinition result = objectMapper.readValue(file, ActivityDefinition.class); // Then Description Is Expected - assertThat(result.getDescription().get(Locale.US), is("pong[.]1:[,]dg[.]:10[,]lunch[.]")); + assertThat(result.getDescription().get(Locale.ENGLISH), is("pong[.]1:[,]dg[.]:10[,]lunch[.]")); } @@ -188,7 +188,7 @@ void whenDeserializingActivityDefinitionWithChoicesThenChoicesDescriptionIsExpec final ActivityDefinition result = objectMapper.readValue(file, ActivityDefinition.class); // Then Choices Description Is Expected - assertThat(result.getChoices().get(0).getDescription().get(Locale.US), + assertThat(result.getChoices().get(0).getDescription().get(Locale.ENGLISH), is("Does the xAPI include the concept of statements?")); } @@ -218,7 +218,7 @@ void whenDeserializingActivityDefinitionWithScaledThenScaleDescriptionIsExpected final ActivityDefinition result = objectMapper.readValue(file, ActivityDefinition.class); // Then Scale Description Is Expected - assertThat(result.getScale().get(0).getDescription().get(Locale.US), + assertThat(result.getScale().get(0).getDescription().get(Locale.ENGLISH), is("Does the xAPI include the concept of statements?")); } @@ -248,7 +248,7 @@ void whenDeserializingActivityDefinitionWithSourceThenSourceDescriptionIsExpecte final ActivityDefinition result = objectMapper.readValue(file, ActivityDefinition.class); // Then Source Description Is Expected - assertThat(result.getSource().get(0).getDescription().get(Locale.US), + assertThat(result.getSource().get(0).getDescription().get(Locale.ENGLISH), is("Does the xAPI include the concept of statements?")); } @@ -278,7 +278,7 @@ void whenDeserializingActivityDefinitionWithTargetThenTargetDescriptionIsExpecte final ActivityDefinition result = objectMapper.readValue(file, ActivityDefinition.class); // Then Target Description Is Expected - assertThat(result.getTarget().get(0).getDescription().get(Locale.US), + assertThat(result.getTarget().get(0).getDescription().get(Locale.ENGLISH), is("Does the xAPI include the concept of statements?")); } @@ -308,7 +308,7 @@ void whenDeserializingActivityDefinitionWithStepsThenStepsDescriptionIsExpected( final ActivityDefinition result = objectMapper.readValue(file, ActivityDefinition.class); // Then Steps Description Is Expected - assertThat(result.getSteps().get(0).getDescription().get(Locale.US), + assertThat(result.getSteps().get(0).getDescription().get(Locale.ENGLISH), is("Does the xAPI include the concept of statements?")); } @@ -319,9 +319,9 @@ void whenSerializingActivityDefinitionOfInteractionTypeTrueFalseThenResultIsEqua final ActivityDefinition activityDefinition = ActivityDefinition.builder() - .addName(Locale.US, "True false question") + .addName(Locale.ENGLISH, "True false question") - .addDescription(Locale.US, "Does the xAPI include the concept of statements?") + .addDescription(Locale.ENGLISH, "Does the xAPI include the concept of statements?") .interactionType(InteractionType.TRUE_FALSE) @@ -347,21 +347,21 @@ void whenSerializingActivityDefinitionOfInteractionTypeChoiceThenResultIsEqualTo final ActivityDefinition activityDefinition = ActivityDefinition.builder() - .addName(Locale.US, "Choice") + .addName(Locale.ENGLISH, "Choice") - .addDescription(Locale.US, "Which of these prototypes are available at the beta site?") + .addDescription(Locale.ENGLISH, "Which of these prototypes are available at the beta site?") .interactionType(InteractionType.CHOICE) .correctResponsesPattern(Collections.singletonList("golf[,]tetris")) - .addChoice(c -> c.id("golf").addDescription(Locale.US, "Golf Example")) + .addChoice(c -> c.id("golf").addDescription(Locale.ENGLISH, "Golf Example")) - .addChoice(c -> c.id("facebook").addDescription(Locale.US, "Facebook App")) + .addChoice(c -> c.id("facebook").addDescription(Locale.ENGLISH, "Facebook App")) - .addChoice(c -> c.id("tetris").addDescription(Locale.US, "Tetris Example")) + .addChoice(c -> c.id("tetris").addDescription(Locale.ENGLISH, "Tetris Example")) - .addChoice(c -> c.id("scrabble").addDescription(Locale.US, "Scrabble Example")) + .addChoice(c -> c.id("scrabble").addDescription(Locale.ENGLISH, "Scrabble Example")) .type(URI.create("http://adlnet.gov/expapi/activities/cmi.interaction")) @@ -389,7 +389,7 @@ void whenCallingToStringThenResultIsExpected() throws Exception { // Then Result Is Expected assertThat(result, is( - "ActivityDefinition(name={en_US=Does the xAPI include the concept of statements?}, description={en_US=pong[.]1:[,]dg[.]:10[,]lunch[.]}, type=http://adlnet.gov/expapi/activities/cmi.interaction, moreInfo=http://example.com/more, interactionType=TRUE_FALSE, correctResponsesPattern=[{case_matters=false}{lang=en}To store and provide access to learning experiences.], choices=[InteractionComponent(id=1, description={en_US=Does the xAPI include the concept of statements?})], scale=[InteractionComponent(id=1, description={en_US=Does the xAPI include the concept of statements?})], source=[InteractionComponent(id=1, description={en_US=Does the xAPI include the concept of statements?})], target=[InteractionComponent(id=1, description={en_US=Does the xAPI include the concept of statements?})], steps=[InteractionComponent(id=1, description={en_US=Does the xAPI include the concept of statements?})], extensions={http://url=www.example.com})")); + "ActivityDefinition(name={en=Does the xAPI include the concept of statements?}, description={en=pong[.]1:[,]dg[.]:10[,]lunch[.]}, type=http://adlnet.gov/expapi/activities/cmi.interaction, moreInfo=http://example.com/more, interactionType=TRUE_FALSE, correctResponsesPattern=[{case_matters=false}{lang=en}To store and provide access to learning experiences.], choices=[InteractionComponent(id=1, description={en=Does the xAPI include the concept of statements?})], scale=[InteractionComponent(id=1, description={en=Does the xAPI include the concept of statements?})], source=[InteractionComponent(id=1, description={en=Does the xAPI include the concept of statements?})], target=[InteractionComponent(id=1, description={en=Does the xAPI include the concept of statements?})], steps=[InteractionComponent(id=1, description={en=Does the xAPI include the concept of statements?})], extensions={http://url=www.example.com})")); } @Test @@ -398,11 +398,11 @@ void whenBuildingActivityDefinitionWithTwoNameValuesThenNameLanguageMapHasTwoEnt // When Building ActivityDefinition With Two Name Values final ActivityDefinition activityDefinition = ActivityDefinition.builder() - .addName(Locale.US, "True false question") + .addName(Locale.ENGLISH, "True false question") .addName(Locale.GERMAN, "Richtig / Falsch-Frage") - .addDescription(Locale.US, "Does the xAPI include the concept of statements?") + .addDescription(Locale.ENGLISH, "Does the xAPI include the concept of statements?") .interactionType(InteractionType.TRUE_FALSE) @@ -423,9 +423,9 @@ void whenBuildingActivityDefinitionWithTwoDescriptionValuesThenDescriptionLangua // When Building ActivityDefinition With Two Description Values final ActivityDefinition activityDefinition = ActivityDefinition.builder() - .addName(Locale.US, "True false question") + .addName(Locale.ENGLISH, "True false question") - .addDescription(Locale.US, "Does the xAPI include the concept of statements?") + .addDescription(Locale.ENGLISH, "Does the xAPI include the concept of statements?") .addDescription(Locale.GERMAN, "Enthält die xAPI das Konzept von Anweisungen?") diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/ActivityTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/ActivityTests.java index 933dfb1e..f30d4f41 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/ActivityTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/ActivityTests.java @@ -79,9 +79,9 @@ void whenSerializingActivityThenResultIsEqualToExpectedJson() throws IOException .definition(d -> d - .addName(Locale.US, "simple statement") + .addName(Locale.ENGLISH, "simple statement") - .addDescription(Locale.US, + .addDescription(Locale.ENGLISH, "A simple Experience API statement. Note that the LRS does not need to have any prior information about the Actor (learner), the verb, or the Activity/object.")) .build(); diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/ContextTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/ContextTests.java index c6902fe2..1e834553 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/ContextTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/ContextTests.java @@ -93,7 +93,7 @@ void whenDeserializingContextThenLanguageIsExpected() throws Exception { final Context result = objectMapper.readValue(file, Context.class); // Then Language Is Expected - assertThat(result.getLanguage(), is(Locale.US)); + assertThat(result.getLanguage(), is(Locale.ENGLISH)); } @@ -230,7 +230,7 @@ void whenSerializingContextThenResultIsEqualToExpectedJson() throws IOException .platform("platform") - .language(Locale.US) + .language(Locale.ENGLISH) .statementReference(s -> s.id(UUID.fromString("e9b6b9ed-ef48-4986-9b86-2ef697578bf7"))) @@ -268,7 +268,7 @@ void whenCallingToStringThenResultIsExpected() throws IOException { // Then Result Is Expected assertThat(result, is( - "Context(registration=1d527164-ed0d-4b1d-9f9b-39aab0e4a089, instructor=Agent(super=Actor(name=Andrew Downes, mbox=mailto:andrew@example.co.uk, mboxSha1sum=null, openid=null, account=null)), team=Group(super=Actor(name=Example Group, mbox=null, mboxSha1sum=null, openid=null, account=null), member=null), contextActivities=ContextActivities(parent=[Activity(id=https://example.com/activity/1, definition=null)], grouping=[Activity(id=https://example.com/activity/2, definition=null)], category=[Activity(id=https://example.com/activity/3, definition=null)], other=[Activity(id=https://example.com/activity/4, definition=null)]), revision=revision, platform=platform, language=en_US, statement=StatementReference(id=e9b6b9ed-ef48-4986-9b86-2ef697578bf7), extensions={http://url=www.example.com})")); + "Context(registration=1d527164-ed0d-4b1d-9f9b-39aab0e4a089, instructor=Agent(super=Actor(name=Andrew Downes, mbox=mailto:andrew@example.co.uk, mboxSha1sum=null, openid=null, account=null)), team=Group(super=Actor(name=Example Group, mbox=null, mboxSha1sum=null, openid=null, account=null), member=null), contextActivities=ContextActivities(parent=[Activity(id=https://example.com/activity/1, definition=null)], grouping=[Activity(id=https://example.com/activity/2, definition=null)], category=[Activity(id=https://example.com/activity/3, definition=null)], other=[Activity(id=https://example.com/activity/4, definition=null)]), revision=revision, platform=platform, language=en, statement=StatementReference(id=e9b6b9ed-ef48-4986-9b86-2ef697578bf7), extensions={http://url=www.example.com})")); } diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/InteractionComponentTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/InteractionComponentTests.java index 6daa5363..05f986f0 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/InteractionComponentTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/InteractionComponentTests.java @@ -75,7 +75,7 @@ void whenDeserializingInteractionComponentThenDescriptionIsExpected() throws Exc final InteractionComponent result = objectMapper.readValue(file, InteractionComponent.class); // Then Description Is Expected - assertThat(result.getDescription().get(Locale.US), is("value")); + assertThat(result.getDescription().get(Locale.ENGLISH), is("value")); } @@ -86,7 +86,7 @@ void whenSerializingInteractionComponentThenResultIsEqualToExpectedJson() throws .id("1") - .addDescription(Locale.US, "value") + .addDescription(Locale.ENGLISH, "value") .build(); @@ -111,7 +111,7 @@ void whenCallingToStringThenResultIsExpected() throws IOException { final String result = interactionComponent.toString(); // Then Result Is Expected - assertThat(result, is("InteractionComponent(id=1, description={en_US=value})")); + assertThat(result, is("InteractionComponent(id=1, description={en=value})")); } @@ -120,7 +120,7 @@ void whenBuildingInteractionComponentWithTwoDescriptionValuesThenDisplayLanguage // When Building InteractionComponent With Two Description Values final InteractionComponent interactionComponent = InteractionComponent.builder().id("1") - .addDescription(Locale.US, "value").addDescription(Locale.GERMAN, "Wert").build(); + .addDescription(Locale.ENGLISH, "value").addDescription(Locale.GERMAN, "Wert").build(); // Then Description Language Map Has Two Entries assertThat(interactionComponent.getDescription(), aMapWithSize(2)); @@ -131,7 +131,7 @@ void whenBuildingInteractionComponentWithTwoDescriptionValuesThenDisplayLanguage void whenValidatingInteractionComponentWithAllRequiredPropertiesThenConstraintViolationsSizeIsZero() { final InteractionComponent interactionComponent = - InteractionComponent.builder().id("1").addDescription(Locale.US, "value").build(); + InteractionComponent.builder().id("1").addDescription(Locale.ENGLISH, "value").build(); // When Validating Interaction Component With All Required Properties final Set> constraintViolations = @@ -146,7 +146,7 @@ void whenValidatingInteractionComponentWithAllRequiredPropertiesThenConstraintVi void whenValidatingInteractionComponentWithoutIdThenConstraintViolationsSizeIsOne() { final InteractionComponent interactionComponent = - InteractionComponent.builder().addDescription(Locale.US, "value").build(); + InteractionComponent.builder().addDescription(Locale.ENGLISH, "value").build(); // When Validating Interaction Component Without Id final Set> constraintViolations = diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/LanguageMapTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/LanguageMapTests.java index 6cb22782..2373e136 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/LanguageMapTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/LanguageMapTests.java @@ -68,7 +68,7 @@ void givenFrenchAndEnglishKeyWhenGettingUSValueThenValueIsEnglish() throws Excep // Given French And English Key languageMap.put(Locale.FRENCH, "Couleur"); - languageMap.put(Locale.US, "Color"); + languageMap.put(Locale.ENGLISH, "Color"); // When Getting US Value final String value = languageMap.get(LanguageRange.parse("en-US")); diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/StatementTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/StatementTests.java index c2fdcb1c..7a2c3a28 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/StatementTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/StatementTests.java @@ -240,9 +240,9 @@ void whenSerializingStatementThenResultIsEqualToExpectedJson() throws IOExceptio final Attachment attachment = Attachment.builder().usageType(URI.create("http://example.com")) .fileUrl(URI.create("http://example.com")) - .addDisplay(Locale.US, "value") + .addDisplay(Locale.ENGLISH, "value") - .addDescription(Locale.US, "value") + .addDescription(Locale.ENGLISH, "value") .length(123) @@ -282,7 +282,7 @@ void whenSerializingStatementThenResultIsEqualToExpectedJson() throws IOExceptio .platform("Example virtual meeting software") - .language(Locale.US) + .language(Locale.ENGLISH) .statementReference(s -> s.id(UUID.fromString("6690e6c9-3ef0-4ed3-8b37-7f3964730bee"))) @@ -450,7 +450,7 @@ void whenValidatingStatementWithoutAnActivityThenConstraintViolationsSizeIsOne() } @Test - void whenValidatingStatementWithSubStatementWithStatementRefrenceThenConstraintViolationsSizeIsZero() { + void whenValidatingStatementWithSubStatementWithStatementReferenceThenConstraintViolationsSizeIsZero() { StatementReference statementRef = StatementReference.builder() .id(UUID.fromString("9e13cefd-53d3-4eac-b5ed-2cf6693903bb")).build(); @@ -475,7 +475,7 @@ void whenValidatingStatementWithSubStatementWithStatementRefrenceThenConstraintV .build(); - // When Validating Statement With SubStatement With StatementRefrence + // When Validating Statement With SubStatement With StatementReference final Set> constraintViolations = validator.validate(statement); // Then ConstraintViolations Size Is Zero diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/SubStatementTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/SubStatementTests.java index da8eb198..4d5e903c 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/SubStatementTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/SubStatementTests.java @@ -281,7 +281,7 @@ void whenSerializingSubStatementThenResultIsEqualToExpectedJson() throws IOExcep .platform("platform") - .language(Locale.US) + .language(Locale.forLanguageTag("en")) .instructor(agent) @@ -298,9 +298,9 @@ void whenSerializingSubStatementThenResultIsEqualToExpectedJson() throws IOExcep final Attachment attachment = Attachment.builder().usageType(URI.create("http://example.com")) .fileUrl(URI.create("http://example.com")) - .addDisplay(Locale.US, "value") + .addDisplay(Locale.ENGLISH, "value") - .addDescription(Locale.US, "value") + .addDescription(Locale.ENGLISH, "value") .length(123) @@ -316,7 +316,7 @@ void whenSerializingSubStatementThenResultIsEqualToExpectedJson() throws IOExcep .actor(agent) - .verb(v -> v.id(URI.create("http://example.com/confirmed")).addDisplay(Locale.US, + .verb(v -> v.id(URI.create("http://example.com/confirmed")).addDisplay(Locale.ENGLISH, "confirmed")) .object(statementRef) @@ -352,7 +352,7 @@ void whenCallingToStringThenResultIsExpected() throws IOException { // Then Result Is Expected assertThat(result, is( - "SubStatement(actor=Agent(super=Actor(name=null, mbox=mailto:agent@example.com, mboxSha1sum=null, openid=null, account=null)), verb=Verb(id=http://example.com/confirmed, display={en_US=confirmed}), object=StatementReference(id=9e13cefd-53d3-4eac-b5ed-2cf6693903bb), result=Result(score=Score(scaled=1.0, raw=1.0, min=0.0, max=5.0), success=true, completion=true, response=test, duration=P1D, extensions=null), context=Context(registration=6d969975-8d7e-4506-ac19-877c57f2921a, instructor=Agent(super=Actor(name=null, mbox=mailto:agent@example.com, mboxSha1sum=null, openid=null, account=null)), team=Group(super=Actor(name=Example Group, mbox=null, mboxSha1sum=null, openid=null, account=null), member=null), contextActivities=ContextActivities(parent=[Activity(id=http://www.example.co.uk/exampleactivity, definition=null)], grouping=[Activity(id=http://www.example.co.uk/exampleactivity, definition=null)], category=[Activity(id=http://www.example.co.uk/exampleactivity, definition=null)], other=[Activity(id=http://www.example.co.uk/exampleactivity, definition=null)]), revision=revision, platform=platform, language=en_US, statement=StatementReference(id=9e13cefd-53d3-4eac-b5ed-2cf6693903bb), extensions={http://url=www.example.com}), timestamp=2015-11-18T11:17:00Z, attachments=[Attachment(usageType=http://example.com, display={en_US=value}, description={en_US=value}, contentType=file, length=123, sha2=123, fileUrl=http://example.com)])")); + "SubStatement(actor=Agent(super=Actor(name=null, mbox=mailto:agent@example.com, mboxSha1sum=null, openid=null, account=null)), verb=Verb(id=http://example.com/confirmed, display={en=confirmed}), object=StatementReference(id=9e13cefd-53d3-4eac-b5ed-2cf6693903bb), result=Result(score=Score(scaled=1.0, raw=1.0, min=0.0, max=5.0), success=true, completion=true, response=test, duration=P1D, extensions=null), context=Context(registration=6d969975-8d7e-4506-ac19-877c57f2921a, instructor=Agent(super=Actor(name=null, mbox=mailto:agent@example.com, mboxSha1sum=null, openid=null, account=null)), team=Group(super=Actor(name=Example Group, mbox=null, mboxSha1sum=null, openid=null, account=null), member=null), contextActivities=ContextActivities(parent=[Activity(id=http://www.example.co.uk/exampleactivity, definition=null)], grouping=[Activity(id=http://www.example.co.uk/exampleactivity, definition=null)], category=[Activity(id=http://www.example.co.uk/exampleactivity, definition=null)], other=[Activity(id=http://www.example.co.uk/exampleactivity, definition=null)]), revision=revision, platform=platform, language=en, statement=StatementReference(id=9e13cefd-53d3-4eac-b5ed-2cf6693903bb), extensions={http://url=www.example.com}), timestamp=2015-11-18T11:17:00Z, attachments=[Attachment(usageType=http://example.com, display={en=value}, description={en=value}, contentType=file, length=123, sha2=123, fileUrl=http://example.com)])")); } diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/VerbTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/VerbTests.java index 5017dcda..6b660629 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/VerbTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/VerbTests.java @@ -78,7 +78,7 @@ void whenSerializingVerbThenResultIsEqualToExpectedJson() throws IOException { .id(URI.create("http://adlnet.gov/expapi/verbs/answered")) - .addDisplay(Locale.US, "answered") + .addDisplay(Locale.ENGLISH, "answered") .build(); @@ -128,7 +128,7 @@ void whenCallingToStringThenResultIsExpected() throws IOException { // Then Result Is Expected assertThat(result, - is("Verb(id=http://adlnet.gov/expapi/verbs/answered, display={en_US=answered})")); + is("Verb(id=http://adlnet.gov/expapi/verbs/answered, display={en=answered})")); } @@ -187,7 +187,7 @@ void whenBuildingVerbWithTwoDisplayValuesThenDisplayLanguageMapHasTwoEntries() { .id("http://adlnet.gov/expapi/verbs/answered") - .addDisplay(Locale.US, "answered") + .addDisplay(Locale.ENGLISH, "answered") .addDisplay(Locale.GERMAN, "antwortete") @@ -217,7 +217,7 @@ void givenVerbWithoutDisplayPropertyWhenTestingEqualityWithVerbWithDisplayProper .id("http://adlnet.gov/expapi/verbs/answered") - .addDisplay(Locale.US, "answered") + .addDisplay(Locale.ENGLISH, "answered") .build()); @@ -243,7 +243,7 @@ void givenVerbWithGermanDisplayPropertyWhenTestingEqualityWithVerbWithEnglishDis .id("http://adlnet.gov/expapi/verbs/answered") - .addDisplay(Locale.US, "answered") + .addDisplay(Locale.ENGLISH, "answered") .build()); @@ -318,7 +318,7 @@ void givenVerbDoesNotHaveIdVoidedWhenCallingIsVoidedThenResultIsFalse() { void whenValidatingVerbWithAllRequiredPropertiesThenConstraintViolationsSizeIsZero() { final Verb verb = Verb.builder().id("http://adlnet.gov/expapi/verbs/answered") - .addDisplay(Locale.US, "answered").build(); + .addDisplay(Locale.ENGLISH, "answered").build(); // When Validating Interaction Component With All Required Properties final Set> constraintViolations = validator.validate(verb); @@ -331,7 +331,7 @@ void whenValidatingVerbWithAllRequiredPropertiesThenConstraintViolationsSizeIsZe @Test void whenValidatingVerbWithoutIdThenConstraintViolationsSizeIsOne() { - final Verb verb = Verb.builder().addDisplay(Locale.US, "answered").build(); + final Verb verb = Verb.builder().addDisplay(Locale.ENGLISH, "answered").build(); // When Validating Interaction Component Without Id final Set> constraintViolations = validator.validate(verb); diff --git a/xapi-model/src/test/resources/activity/activity.json b/xapi-model/src/test/resources/activity/activity.json index 138307a8..b4627ef1 100644 --- a/xapi-model/src/test/resources/activity/activity.json +++ b/xapi-model/src/test/resources/activity/activity.json @@ -2,10 +2,10 @@ "id":"http://example.com/xapi/activity/simplestatement", "definition":{ "name":{ - "en-US":"simple statement" + "en":"simple statement" }, "description":{ - "en-US":"A simple Experience API statement. Note that the LRS does not need to have any prior information about the Actor (learner), the verb, or the Activity/object." + "en":"A simple Experience API statement. Note that the LRS does not need to have any prior information about the Actor (learner), the verb, or the Activity/object." } } } \ No newline at end of file diff --git a/xapi-model/src/test/resources/activity/activity_with_object_type.json b/xapi-model/src/test/resources/activity/activity_with_object_type.json index b9825af2..adfae696 100644 --- a/xapi-model/src/test/resources/activity/activity_with_object_type.json +++ b/xapi-model/src/test/resources/activity/activity_with_object_type.json @@ -3,10 +3,10 @@ "id":"http://example.com/xapi/activity/simplestatement", "definition":{ "name":{ - "en-US":"simple statement" + "en":"simple statement" }, "description":{ - "en-US":"A simple Experience API statement. Note that the LRS does not need to have any prior information about the Actor (learner), the verb, or the Activity/object." + "en":"A simple Experience API statement. Note that the LRS does not need to have any prior information about the Actor (learner), the verb, or the Activity/object." } } } \ No newline at end of file diff --git a/xapi-model/src/test/resources/activity_definition/activity_definition.json b/xapi-model/src/test/resources/activity_definition/activity_definition.json index 31a19763..b6cad8f5 100644 --- a/xapi-model/src/test/resources/activity_definition/activity_definition.json +++ b/xapi-model/src/test/resources/activity_definition/activity_definition.json @@ -6,40 +6,40 @@ "http://url" : "www.example.com" }, "name" : { - "en-US" : "Does the xAPI include the concept of statements?" + "en" : "Does the xAPI include the concept of statements?" }, "description" : { - "en-US" : "pong[.]1:[,]dg[.]:10[,]lunch[.]" + "en" : "pong[.]1:[,]dg[.]:10[,]lunch[.]" }, "type" : "http://adlnet.gov/expapi/activities/cmi.interaction", "choices" : [ { "id" : "1", "description" : { - "en-US" : "Does the xAPI include the concept of statements?" + "en" : "Does the xAPI include the concept of statements?" } } ], "scale" : [ { "id" : "1", "description" : { - "en-US" : "Does the xAPI include the concept of statements?" + "en" : "Does the xAPI include the concept of statements?" } } ], "source" : [ { "id" : "1", "description" : { - "en-US" : "Does the xAPI include the concept of statements?" + "en" : "Does the xAPI include the concept of statements?" } } ], "target" : [ { "id" : "1", "description" : { - "en-US" : "Does the xAPI include the concept of statements?" + "en" : "Does the xAPI include the concept of statements?" } } ], "steps" : [ { "id" : "1", "description" : { - "en-US" : "Does the xAPI include the concept of statements?" + "en" : "Does the xAPI include the concept of statements?" } } ] } diff --git a/xapi-model/src/test/resources/activity_definition/activity_definition_without_type.json b/xapi-model/src/test/resources/activity_definition/activity_definition_without_type.json index 90c3470c..505fb28e 100644 --- a/xapi-model/src/test/resources/activity_definition/activity_definition_without_type.json +++ b/xapi-model/src/test/resources/activity_definition/activity_definition_without_type.json @@ -1,6 +1,6 @@ { "description": { - "en-US": "Does the xAPI include the concept of statements?" + "en": "Does the xAPI include the concept of statements?" }, "interactionType": "fill-in", "correctResponsesPattern": [ diff --git a/xapi-model/src/test/resources/activity_definition/choice.json b/xapi-model/src/test/resources/activity_definition/choice.json index cf411f4a..1d1bfbad 100644 --- a/xapi-model/src/test/resources/activity_definition/choice.json +++ b/xapi-model/src/test/resources/activity_definition/choice.json @@ -1,9 +1,9 @@ { "name": { - "en-US": "Choice" + "en": "Choice" }, "description": { - "en-US": "Which of these prototypes are available at the beta site?" + "en": "Which of these prototypes are available at the beta site?" }, "type": "http://adlnet.gov/expapi/activities/cmi.interaction", "interactionType": "choice", @@ -14,25 +14,25 @@ { "id": "golf", "description": { - "en-US": "Golf Example" + "en": "Golf Example" } }, { "id": "facebook", "description": { - "en-US": "Facebook App" + "en": "Facebook App" } }, { "id": "tetris", "description": { - "en-US": "Tetris Example" + "en": "Tetris Example" } }, { "id": "scrabble", "description": { - "en-US": "Scrabble Example" + "en": "Scrabble Example" } } ] diff --git a/xapi-model/src/test/resources/activity_definition/true_false.json b/xapi-model/src/test/resources/activity_definition/true_false.json index b1a64e3a..6523da45 100644 --- a/xapi-model/src/test/resources/activity_definition/true_false.json +++ b/xapi-model/src/test/resources/activity_definition/true_false.json @@ -1,9 +1,9 @@ { "name":{ - "en-US":"True false question" + "en":"True false question" }, "description":{ - "en-US":"Does the xAPI include the concept of statements?" + "en":"Does the xAPI include the concept of statements?" }, "type":"http://adlnet.gov/expapi/activities/cmi.interaction", "interactionType":"true-false", diff --git a/xapi-model/src/test/resources/context/context.json b/xapi-model/src/test/resources/context/context.json index bb044dca..06d476a4 100644 --- a/xapi-model/src/test/resources/context/context.json +++ b/xapi-model/src/test/resources/context/context.json @@ -2,7 +2,7 @@ "registration": "1d527164-ed0d-4b1d-9f9b-39aab0e4a089", "revision": "revision", "platform": "platform", - "language": "en-US", + "language": "en", "instructor": { "objectType": "Agent", "name": "Andrew Downes", diff --git a/xapi-model/src/test/resources/context/context_with_empty_registration.json b/xapi-model/src/test/resources/context/context_with_empty_registration.json index 59e0f7af..990ffb74 100644 --- a/xapi-model/src/test/resources/context/context_with_empty_registration.json +++ b/xapi-model/src/test/resources/context/context_with_empty_registration.json @@ -2,7 +2,7 @@ "registration": "", "revision": "revision", "platform": "platform", - "language": "en-US", + "language": "en", "instructor": { "name": "Andrew Downes", "mbox": "mailto:andrew@example.co.uk" diff --git a/xapi-model/src/test/resources/context/context_without_registration.json b/xapi-model/src/test/resources/context/context_without_registration.json index 50f37e08..cd324ee9 100644 --- a/xapi-model/src/test/resources/context/context_without_registration.json +++ b/xapi-model/src/test/resources/context/context_without_registration.json @@ -1,7 +1,7 @@ { "revision": "revision", "platform": "platform", - "language": "en-US", + "language": "en", "instructor": { "name": "Andrew Downes", "mbox": "mailto:andrew@example.co.uk" diff --git a/xapi-model/src/test/resources/interaction_component/interaction_component.json b/xapi-model/src/test/resources/interaction_component/interaction_component.json index 546d196b..b01f6834 100644 --- a/xapi-model/src/test/resources/interaction_component/interaction_component.json +++ b/xapi-model/src/test/resources/interaction_component/interaction_component.json @@ -1,6 +1,6 @@ { "id": "1", "description": { - "en-US": "value" + "en": "value" } } diff --git a/xapi-model/src/test/resources/statement/statement.json b/xapi-model/src/test/resources/statement/statement.json index 0f1baf6f..c0b6e5ee 100644 --- a/xapi-model/src/test/resources/statement/statement.json +++ b/xapi-model/src/test/resources/statement/statement.json @@ -32,7 +32,7 @@ "objectType": "Group" }, "platform": "Example virtual meeting software", - "language": "en-US", + "language": "en", "statement": { "objectType": "StatementRef", "id": "6690e6c9-3ef0-4ed3-8b37-7f3964730bee" @@ -69,10 +69,10 @@ { "usageType": "http://example.com", "display": { - "en-US": "value" + "en": "value" }, "description": { - "en-US": "value" + "en": "value" }, "contentType": "file", "length": 123, diff --git a/xapi-model/src/test/resources/sub_statement/sub_statement.json b/xapi-model/src/test/resources/sub_statement/sub_statement.json index a4db3e02..58955494 100644 --- a/xapi-model/src/test/resources/sub_statement/sub_statement.json +++ b/xapi-model/src/test/resources/sub_statement/sub_statement.json @@ -4,7 +4,7 @@ "verb" : { "id" : "http://example.com/confirmed", "display" : { - "en-US" : "confirmed" + "en" : "confirmed" } }, "actor" : { @@ -31,7 +31,7 @@ "registration" : "6d969975-8d7e-4506-ac19-877c57f2921a", "revision" : "revision", "platform" : "platform", - "language" : "en-US", + "language" : "en", "instructor" : { "objectType" : "Agent", "mbox" : "mailto:agent@example.com" @@ -69,10 +69,10 @@ "attachments" : [ { "usageType" : "http://example.com", "display" : { - "en-US" : "value" + "en" : "value" }, "description" : { - "en-US" : "value" + "en" : "value" }, "contentType" : "file", "length" : 123, diff --git a/xapi-model/src/test/resources/sub_statement/sub_statement_with_object_of_type_sub_statement.json b/xapi-model/src/test/resources/sub_statement/sub_statement_with_object_of_type_sub_statement.json index e5c65d94..4b6cd007 100644 --- a/xapi-model/src/test/resources/sub_statement/sub_statement_with_object_of_type_sub_statement.json +++ b/xapi-model/src/test/resources/sub_statement/sub_statement_with_object_of_type_sub_statement.json @@ -19,7 +19,7 @@ "verb":{ "id":"http://example.com/confirmed", "display":{ - "en-US":"confirmed" + "en":"confirmed" } }, "object":{ diff --git a/xapi-model/src/test/resources/verb/verb.json b/xapi-model/src/test/resources/verb/verb.json index 5d9236b9..78c1c7c7 100644 --- a/xapi-model/src/test/resources/verb/verb.json +++ b/xapi-model/src/test/resources/verb/verb.json @@ -1,6 +1,6 @@ { "id": "http://adlnet.gov/expapi/verbs/answered", "display": { - "en-US": "answered" + "en": "answered" } } From 52097e6035473a3b2ba6995541b86930c6e54fca Mon Sep 17 00:00:00 2001 From: Thomas Turrell-Croft Date: Sun, 19 Mar 2023 14:46:24 +0000 Subject: [PATCH 34/34] tip --- .../ActivityDefinitionValidator.java | 12 +- .../ActivityDefinitionValidatorTests.java | 123 +++++++++++++++--- 2 files changed, 113 insertions(+), 22 deletions(-) diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ActivityDefinitionValidator.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ActivityDefinitionValidator.java index 5e4546b4..8da803d9 100644 --- a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ActivityDefinitionValidator.java +++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ActivityDefinitionValidator.java @@ -13,10 +13,11 @@ * The {@link ActivityDefinition} being validated must be valid. * * @author István Rátkai (Selindek) - * @see Activity + * @see Activity * Definition */ -public class ActivityDefinitionValidator +public class ActivityDefinitionValidator implements ConstraintValidator { @Override @@ -26,10 +27,9 @@ public boolean isValid(ActivityDefinition value, ConstraintValidatorContext cont return true; } - return !(value.getInteractionType() == null - && (value.getCorrectResponsesPattern() != null || value.getChoices() != null - || value.getScale() != null || value.getSource() != null || value.getTarget() != null - || value.getSteps() != null)); + return !(value.getInteractionType() == null && (value.getCorrectResponsesPattern() != null + || value.getChoices() != null || value.getScale() != null || value.getSource() != null + || value.getTarget() != null || value.getSteps() != null)); } diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActivityDefinitionValidatorTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActivityDefinitionValidatorTests.java index 0cd6484f..15ca16c2 100644 --- a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActivityDefinitionValidatorTests.java +++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ActivityDefinitionValidatorTests.java @@ -24,22 +24,33 @@ class ActivityDefinitionValidatorTests { private static final ActivityDefinitionValidator validator = new ActivityDefinitionValidator(); @Test - void whenValueIsNullThenResultIsTrue() { + void whenIsValidIsCalledWithNullActivityDefinitionThenResultIsTrue() { - // When Value Is Null + // When IsValid Is Called With Null Activity Definition var result = validator.isValid(null, null); // Then Result Is True assertTrue(result); } + @Test + void whenIsValidIsCalledWithActivityDefinitionThenResultIsTrue() { + + var value = ActivityDefinition.builder().build(); + + // When IsValid Is Called With Activity Definition + var result = validator.isValid(value, null); + + // Then Result Is True + assertTrue(result); + } @Test - void whenValueHasInteractionTypeThenResultIsTrue() { + void whenIsValidIsCalledWithActivityDefinitionThatHasInteractionTypeThenResultIsTrue() { - // When Value Has InteractionType var value = ActivityDefinition.builder().interactionType(InteractionType.CHOICE).build(); + // When IsValid Is Called With Activity Definition That Has Interaction Type var result = validator.isValid(value, null); // Then Result Is True @@ -47,11 +58,11 @@ void whenValueHasInteractionTypeThenResultIsTrue() { } @Test - void whenValueHasNoInteractionTypeButHasCorrectResponsepatternThenResultIsFalse() { + void whenIsValidIsCalledWithActivityDefinitionThatHasCorrectResponsesPatternThenResultIsFalse() { - // When Value Has No InteractionType But Has CorrectResponsePatters var value = ActivityDefinition.builder().correctResponsesPattern(new ArrayList<>()).build(); + // When IsValid Is Called With Activity Definition That Has Correct Responses Pattern var result = validator.isValid(value, null); // Then Result Is False @@ -59,11 +70,25 @@ void whenValueHasNoInteractionTypeButHasCorrectResponsepatternThenResultIsFalse( } @Test - void whenValueHasNoInteractionTypeButHasChoicesThenResultIsFalse() { + void whenIsValidIsCalledWithActivityDefinitionThatHasCorrectResponsesPatternAndInteractionTypeThenResultIsTrue() { + + var value = ActivityDefinition.builder().correctResponsesPattern(new ArrayList<>()) + .interactionType(InteractionType.CHOICE).build(); + + // When IsValid Is Called With Activity Definition That Has Correct Responses Pattern And + // InteractionType + var result = validator.isValid(value, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenIsValidIsCalledWithActivityDefinitionThatHasChoicesThenResultIsFalse() { - // When Value Has No InteractionType But Has Choices var value = ActivityDefinition.builder().choices(new ArrayList<>()).build(); + // When IsValid Is Called With Activity Definition That Has Choices var result = validator.isValid(value, null); // Then Result Is False @@ -71,11 +96,24 @@ void whenValueHasNoInteractionTypeButHasChoicesThenResultIsFalse() { } @Test - void whenValueHasNoInteractionTypeButHasScaleThenResultIsFalse() { + void whenIsValidIsCalledWithActivityDefinitionThatHasChoicesAndInteractionTypeThenResultIsTrue() { + + var value = ActivityDefinition.builder().choices(new ArrayList<>()) + .interactionType(InteractionType.CHOICE).build(); + + // When IsValid Is Called With Activity Definition That Has Choices And InteractionType + var result = validator.isValid(value, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenIsValidIsCalledWithActivityDefinitionThatHasScaleThenResultIsFalse() { - // When Value Has No InteractionType But Has Scale var value = ActivityDefinition.builder().scale(new ArrayList<>()).build(); + // When IsValid Is Called With Activity Definition That Has Scale var result = validator.isValid(value, null); // Then Result Is False @@ -83,11 +121,24 @@ void whenValueHasNoInteractionTypeButHasScaleThenResultIsFalse() { } @Test - void whenValueHasNoInteractionTypeButHasSourceThenResultIsFalse() { + void whenIsValidIsCalledWithActivityDefinitionThatHasScaleAndInteractionTypeThenResultIsTrue() { + + var value = ActivityDefinition.builder().scale(new ArrayList<>()) + .interactionType(InteractionType.LIKERT).build(); + + // When IsValid Is Called With Activity Definition That Has Scale And InteractionType + var result = validator.isValid(value, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenIsValidIsCalledWithActivityDefinitionThatHasSourceThenResultIsFalse() { - // When Value Has No InteractionType But Has Source var value = ActivityDefinition.builder().source(new ArrayList<>()).build(); + // When IsValid Is Called With Activity Definition That Has Source var result = validator.isValid(value, null); // Then Result Is False @@ -95,9 +146,22 @@ void whenValueHasNoInteractionTypeButHasSourceThenResultIsFalse() { } @Test - void whenValueHasNoInteractionTypeButHasTargetThenResultIsFalse() { + void whenIsValidIsCalledWithActivityDefinitionAndInteractionTypeThatHasSourceThenResultIsTrue() { + + var value = ActivityDefinition.builder().source(new ArrayList<>()) + .interactionType(InteractionType.MATCHING).build(); + + // When IsValid Is Called With Activity Definition That Has Source And InteractionType + var result = validator.isValid(value, null); + + // Then Result Is True + assertTrue(result); + } + + @Test + void whenIsValidIsCalledWithActivityDefinitionThatHasTargetThenResultIsFalse() { - // When Value Has No InteractionType But Has Target + // When IsValid Is Called With Activity Definition That Has Target var value = ActivityDefinition.builder().target(new ArrayList<>()).build(); var result = validator.isValid(value, null); @@ -107,16 +171,43 @@ void whenValueHasNoInteractionTypeButHasTargetThenResultIsFalse() { } @Test - void whenValueHasNoInteractionTypeButHasStepsThenResultIsFalse() { + void whenIsValidIsCalledWithActivityDefinitionThatHasTargetAndInteractionTypeThenResultIsTrue() { + + // When IsValid Is Called With Activity Definition That Has Target And InteractionType + var value = ActivityDefinition.builder().target(new ArrayList<>()) + .interactionType(InteractionType.MATCHING).build(); + + var result = validator.isValid(value, null); + + // Then Result Is True + assertTrue(result); + } + + + @Test + void whenIsValidIsCalledWithActivityDefinitionThatHasStepsThenResultIsFalse() { - // When Value Has No InteractionType But Has Steps var value = ActivityDefinition.builder().steps(new ArrayList<>()).build(); + // When IsValid Is Called With Activity Definition That Has Steps var result = validator.isValid(value, null); // Then Result Is False assertFalse(result); } + @Test + void whenIsValidIsCalledWithActivityDefinitionThatHasStepsAndInteractionTypeThenResultIsTrue() { + + var value = ActivityDefinition.builder().steps(new ArrayList<>()) + .interactionType(InteractionType.PERFORMANCE).build(); + + // When IsValid Is Called With Activity Definition That Has Steps And InteractionType + var result = validator.isValid(value, null); + + // Then Result Is True + assertTrue(result); + } + }