Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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.ValidLocale;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
Expand All @@ -33,11 +34,13 @@ public class ActivityDefinition {
/**
* The human readable/visual name of the Activity.
*/
@ValidLocale
private LanguageMap name;

/**
* A description of the Activity.
*/
@ValidLocale
private LanguageMap description;

/**
Expand Down Expand Up @@ -92,7 +95,8 @@ public class ActivityDefinition {
*/
private Map<@HasScheme URI, Object> extensions;

// **Warning** do not add fields that are not required by the xAPI specification.
// **Warning** do not add fields that are not required by the xAPI
// specification.

/**
* Builder for ActivityDefinition.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
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.ValidLocale;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.valueextraction.Unwrapping;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
Expand Down Expand Up @@ -40,12 +42,14 @@ public class Attachment {
/**
* Display name of this Attachment.
*/
@NotNull
@NotNull(payload = Unwrapping.Skip.class)
@ValidLocale
private LanguageMap display;

/**
* A description of the Attachment.
*/
@ValidLocale
private LanguageMap description;

/**
Expand Down
2 changes: 2 additions & 0 deletions xapi-model/src/main/java/dev/learning/xapi/model/Context.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
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.ValidLocale;
import dev.learning.xapi.model.validation.constraints.Variant;
import jakarta.validation.Valid;
import java.net.URI;
Expand Down Expand Up @@ -76,6 +77,7 @@ public class Context {
*/
@NotUndetermined
@JsonSerialize(using = LocaleSerializer.class)
@ValidLocale
private Locale language;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import dev.learning.xapi.model.validation.constraints.ValidLocale;
import jakarta.validation.constraints.NotNull;
import java.util.Locale;
import lombok.Builder;
Expand Down Expand Up @@ -36,6 +37,7 @@ public class InteractionComponent {
/**
* A description of the interaction component.
*/
@ValidLocale
private LanguageMap description;

// **Warning** do not add fields that are not required by the xAPI specification.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ public class Statement implements CoreStatement {
/**
* Agent or Group who is asserting this Statement is true.
*/
@Valid
@ValidActor
@ValidAuthority
private Actor authority;
Expand All @@ -113,6 +114,7 @@ public class Statement implements CoreStatement {
/**
* Headers for Attachments to the Statement.
*/
@Valid
@JsonFormat(without = {JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY})
private List<Attachment> attachments;

Expand Down Expand Up @@ -349,7 +351,7 @@ public Builder addAttachment(Attachment attachment) {
*/
public Builder addAttachment(Consumer<Attachment.Builder> attachment) {

final Attachment.Builder builder = Attachment.builder();
final var builder = Attachment.builder();

attachment.accept(builder);

Expand Down
2 changes: 2 additions & 0 deletions xapi-model/src/main/java/dev/learning/xapi/model/Verb.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,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.ValidLocale;
import jakarta.validation.constraints.NotNull;
import java.net.URI;
import java.util.Locale;
Expand Down Expand Up @@ -351,6 +352,7 @@ public class Verb {
* impact on the meaning of the Statement, but serves to give a human-readable display of the
* meaning already determined by the chosen Verb.
*/
@ValidLocale
private LanguageMap display;

// **Warning** do not add fields that are not required by the xAPI specification.
Expand Down
Original file line number Diff line number Diff line change
@@ -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 Locale must have a ISO3 Language and Country.
*
* @author István Rátkai (Selindek)
*/
@Documented
@Constraint(validatedBy = {})
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
public @interface ValidLocale {

/**
* Error Message.
*/
String message() default "all keys must have a ISO3 Language and Country";

/**
* Groups.
*/
Class<?>[] groups() default {};

/**
* Payload.
*/
Class<? extends Payload>[] payload() default {};

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright 2016-2023 Berry Cloud Ltd. All rights reserved.
*/

package dev.learning.xapi.model.validation.internal.validators;

import dev.learning.xapi.model.LanguageMap;
import jakarta.validation.valueextraction.ExtractedValue;
import jakarta.validation.valueextraction.UnwrapByDefault;
import jakarta.validation.valueextraction.ValueExtractor;
import java.util.Locale;

/**
* ValueExtractor for {@link LanguageMap}.
*
* @author István Rátkai (Selindek)
*/
@UnwrapByDefault
public class LanguageMapValueExtractor
implements ValueExtractor<@ExtractedValue(type = Locale.class) LanguageMap> {

@Override
public void extractValues(LanguageMap originalValue, ValueReceiver receiver) {
originalValue.keySet().forEach(k -> receiver.iterableValue(k.toLanguageTag(), k));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright 2016-2023 Berry Cloud Ltd. All rights reserved.
*/

package dev.learning.xapi.model.validation.internal.validators;

import dev.learning.xapi.model.validation.constraints.ValidLocale;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import java.util.Locale;
import java.util.MissingResourceException;

/**
* The Locale being validated must have a ISO3 Language and Country.
*
* @author István Rátkai (Selindek)
* @author Thomas Turrell-Croft
*/
public class LocaleValidator implements ConstraintValidator<ValidLocale, Locale> {

@Override
public boolean isValid(Locale locale, ConstraintValidatorContext context) {

if (locale == null) {
return true;
}

try {
locale.getISO3Language();
locale.getISO3Country();

return true;
} catch (final MissingResourceException e1) {

// Handle locale instantiated with Locale#Locale(String)
final var blar = Locale.forLanguageTag(locale.toString());

try {
blar.getISO3Language();
blar.getISO3Country();

return true;
} catch (final MissingResourceException e2) {

return false;
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ dev.learning.xapi.model.validation.internal.validators.StatementRevisionValidato
dev.learning.xapi.model.validation.internal.validators.StatementPlatformValidator
dev.learning.xapi.model.validation.internal.validators.StatementVerbValidator
dev.learning.xapi.model.validation.internal.validators.StatementsValidator
dev.learning.xapi.model.validation.internal.validators.LocaleValidator
dev.learning.xapi.model.validation.internal.validators.ScoreValidator
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dev.learning.xapi.model.validation.internal.validators.LanguageMapValueExtractor
18 changes: 16 additions & 2 deletions xapi-model/src/test/java/dev/learning/xapi/model/VerbTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ void whenValidatingVerbWithAllRequiredPropertiesThenConstraintViolationsSizeIsZe
final var verb = Verb.builder().id("http://adlnet.gov/expapi/verbs/answered")
.addDisplay(Locale.US, "answered").build();

// When Validating Interaction Component With All Required Properties
// When Validating Verb with All Required Properties
final Set<ConstraintViolation<Verb>> constraintViolations = validator.validate(verb);

// Then ConstraintViolations Size Is Zero
Expand All @@ -332,7 +332,21 @@ void whenValidatingVerbWithoutIdThenConstraintViolationsSizeIsOne() {

final var verb = Verb.builder().addDisplay(Locale.US, "answered").build();

// When Validating Interaction Component Without Id
// When Validating Verb Component Without Id
final Set<ConstraintViolation<Verb>> constraintViolations = validator.validate(verb);

// Then ConstraintViolations Size Is One
assertThat(constraintViolations, hasSize(1));

}

@Test
void whenValidatingVerbWithInvalidDisplayIdThenConstraintViolationsSizeIsOne() {

final var verb = Verb.builder().id("http://adlnet.gov/expapi/verbs/asked")
.addDisplay(new Locale("unknown"), "answered").build();

// When Validating Verb With invalid Display
final Set<ConstraintViolation<Verb>> constraintViolations = validator.validate(verb);

// Then ConstraintViolations Size Is One
Expand Down
Loading