Skip to content
Open
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
1 change: 1 addition & 0 deletions api-docs/aggregated.swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,7 @@ definitions:
- FixedList
- MoneyGBP
- TextArea
- RichTextArea
- ComplexType
- Collection
- MultiSelectList
Expand Down
8 changes: 8 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,14 @@ task integration(type: Test) {
failFast = true
}

task aatTest(type: Test) {
description = "Runs JUnit AAT tests"
group = "Verification"
testClassesDirs = sourceSets.aat.output.classesDirs
classpath = sourceSets.aat.runtimeClasspath
failFast = true
}

ext.libraries = [
junit5: [
"org.junit.jupiter:junit-jupiter-api:${junit}",
Expand Down
11 changes: 10 additions & 1 deletion docs/api/case-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,16 @@ A `TextArea` value is a `string`. It contain `\n` to represent line breaks.
}
```

### RichTextArea

A `RichTextArea` value is a `string`. CCD stores the rich text content as an opaque string and does not interpret the markup.

```json
{
"FieldId": "<p><strong>Order</strong></p>"
}
```

### FixedList

A `FixedList` value is a `string` exactly matching the code of one the pre-defined options for that list.
Expand Down Expand Up @@ -349,4 +359,3 @@ Also shows the optional required OrganisationPolicyField field.
}
}
```

Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package uk.gov.hmcts.ccd.datastore.tests.v2.external;

import io.restassured.response.Response;
import io.restassured.specification.RequestSpecification;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import uk.gov.hmcts.ccd.datastore.tests.AATHelper;
import uk.gov.hmcts.ccd.datastore.tests.BaseTest;
import uk.gov.hmcts.ccd.v2.V2;

import java.util.function.Supplier;

import static java.lang.Boolean.FALSE;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.notNullValue;

@DisplayName("RichTextArea case data")
class RichTextAreaCaseTest extends BaseTest {

private static final String CASE_TYPE = "AllDataTypes2";
private static final String CREATE_EVENT = "createCase";
private static final String UPDATE_EVENT = "updateRichTextArea";
private static final String RICH_TEXT_AREA_FIELD = "RichTextAreaField";
private static final String RICH_TEXT_AREA_COMPLEX_FIELD = "RichTextAreaComplexField";
private static final String RICH_TEXT_AREA_COMPLEX_ELEMENT = "RichTextAreaElement";
private static final String CREATED_RICH_TEXT = "<p><strong>Order</strong> created for CCD-7988</p>";
private static final String CREATED_COMPLEX_RICH_TEXT =
"<p><em>Complex order text created for CCD-7988</em></p>";
private static final String UPDATED_RICH_TEXT = "<p><strong>Order</strong> updated for CCD-7988</p>";
private static final String UPDATED_COMPLEX_RICH_TEXT =
"<p><em>Complex order text updated for CCD-7988</em></p>";

protected RichTextAreaCaseTest(AATHelper aat) {
super(aat);
}

@Test
@DisplayName("should create, update and retrieve RichTextArea fields")
void shouldCreateUpdateAndRetrieveRichTextAreaFields() {
String createToken = getStartCaseToken(CREATE_EVENT);

String caseReference = createCase(createToken)
.then()
.statusCode(201)
.body("id", notNullValue())
.body("data." + RICH_TEXT_AREA_FIELD, equalTo(CREATED_RICH_TEXT))
.body("data." + RICH_TEXT_AREA_COMPLEX_FIELD + "." + RICH_TEXT_AREA_COMPLEX_ELEMENT,
equalTo(CREATED_COMPLEX_RICH_TEXT))
.extract()
.path("id");

String updateToken = getStartEventToken(caseReference, UPDATE_EVENT);

updateCase(caseReference, updateToken)
.then()
.statusCode(201)
.body("id", equalTo(caseReference))
.body("data." + RICH_TEXT_AREA_FIELD, equalTo(UPDATED_RICH_TEXT))
.body("data." + RICH_TEXT_AREA_COMPLEX_FIELD + "." + RICH_TEXT_AREA_COMPLEX_ELEMENT,
equalTo(UPDATED_COMPLEX_RICH_TEXT));

getCase(caseReference)
.then()
.statusCode(200)
.body("id", equalTo(caseReference))
.body("data." + RICH_TEXT_AREA_FIELD, equalTo(UPDATED_RICH_TEXT))
.body("data." + RICH_TEXT_AREA_COMPLEX_FIELD + "." + RICH_TEXT_AREA_COMPLEX_ELEMENT,
equalTo(UPDATED_COMPLEX_RICH_TEXT));
}

private String getStartCaseToken(String eventId) {
return asV2AutoTestCaseworker()
.get()
.given()
.pathParam("caseTypeId", CASE_TYPE)
.pathParam("triggerId", eventId)
.accept(V2.MediaType.START_CASE_EVENT)
.when()
.get("/case-types/{caseTypeId}/event-triggers/{triggerId}?ignore-warning=true")
.then()
.statusCode(200)
.extract()
.path("token");
}

private String getStartEventToken(String caseReference, String eventId) {
return asV2AutoTestCaseworker()
.get()
.given()
.pathParam("caseId", caseReference)
.pathParam("triggerId", eventId)
.accept(V2.MediaType.START_EVENT)
.when()
.get("/cases/{caseId}/event-triggers/{triggerId}?ignore-warning=true")
.then()
.statusCode(200)
.extract()
.path("token");
}

private Response createCase(String token) {
return asV2AutoTestCaseworker()
.get()
.given()
.pathParam("caseTypeId", CASE_TYPE)
.contentType(V2.MediaType.CREATE_CASE)
.accept(V2.MediaType.CREATE_CASE)
.body(caseDataContent(CREATE_EVENT, token, CREATED_RICH_TEXT, CREATED_COMPLEX_RICH_TEXT))
.when()
.post("/case-types/{caseTypeId}/cases?ignore-warning=true");
}

private Response updateCase(String caseReference, String token) {
return asV2AutoTestCaseworker()
.get()
.given()
.pathParam("caseId", caseReference)
.contentType(V2.MediaType.CREATE_EVENT)
.accept(V2.MediaType.CREATE_EVENT)
.body(caseDataContent(UPDATE_EVENT, token, UPDATED_RICH_TEXT, UPDATED_COMPLEX_RICH_TEXT))
.when()
.post("/cases/{caseId}/events");
}

private Response getCase(String caseReference) {
return asV2AutoTestCaseworker()
.get()
.given()
.pathParam("caseId", caseReference)
.accept(V2.MediaType.CASE)
.when()
.get("/cases/{caseId}");
}

private Supplier<RequestSpecification> asV2AutoTestCaseworker() {
return () -> asAutoTestCaseworker(FALSE)
.get()
.given()
.header("experimental", "true");
}

private String caseDataContent(String eventId, String token, String richText, String complexRichText) {
return """
{
"event_token": "%s",
"event": {
"id": "%s",
"summary": "CCD-7988 RichTextArea",
"description": "RichTextArea AAT"
},
"data": {
"%s": "%s",
"%s": {
"%s": "%s"
}
}
}
""".formatted(
token,
eventId,
RICH_TEXT_AREA_FIELD,
richText,
RICH_TEXT_AREA_COMPLEX_FIELD,
RICH_TEXT_AREA_COMPLEX_ELEMENT,
complexRichText
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"_guid_": "BaseRichTextAreaFieldType",
"_extends_": "CommonFieldType",

"id": "RichTextArea",
"type": "RichTextArea"
}
3 changes: 2 additions & 1 deletion src/contractTest/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ management.health.elasticsearch.enabled=false
management.health.status.order=DOWN, OUT_OF_SERVICE, UNKNOWN, UP, OUT_OF_SYNC, PROBLEM, COULD_NOT_CONNECT

# Elasticsearch mappings
elasticsearch.type-mappings.defaultText=Text, TextArea, FixedList, FixedListEdit, MultiSelectList, FixedRadioList, DynamicList, DynamicRadioList, DynamicMultiSelectList, Region, BaseLocation, PhoneUK
elasticsearch.type-mappings.defaultText=Text, TextArea, RichTextArea, FixedList, FixedListEdit, MultiSelectList, FixedRadioList, DynamicList, DynamicRadioList, DynamicMultiSelectList, Region, BaseLocation, PhoneUK
elasticsearch.case-predefined-mappings.defaultText=reference, jurisdiction, state, case_type_id

# Remote Case Audit
Expand Down Expand Up @@ -271,6 +271,7 @@ ccd.messaging.type-mappings.Text=SimpleText
ccd.messaging.type-mappings.PhoneUK=SimpleText
ccd.messaging.type-mappings.Email=SimpleText
ccd.messaging.type-mappings.TextArea=SimpleText
ccd.messaging.type-mappings.RichTextArea=SimpleText
ccd.messaging.type-mappings.BaseLocation=SimpleText
ccd.messaging.type-mappings.Region=SimpleText
ccd.messaging.type-mappings.Date=SimpleDate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public class FieldTypeDefinition implements Serializable, Copyable<FieldTypeDefi
public static final String DATE = "Date";
public static final String DOCUMENT = "Document";
public static final String TEXT = "Text";
public static final String RICH_TEXT_AREA = "RichTextArea";
public static final String WAYS_TO_PAY = "WaysToPay";
public static final String FLAG_LAUNCHER = "FlagLauncher";
public static final String COMPONENT_LAUNCHER = "ComponentLauncher";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package uk.gov.hmcts.ccd.domain.types;

import com.fasterxml.jackson.databind.JsonNode;
import jakarta.inject.Named;
import jakarta.inject.Singleton;
import uk.gov.hmcts.ccd.domain.model.definition.CaseFieldDefinition;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import java.util.Locale;

@Named
@Singleton
public class RichTextAreaValidator implements BaseTypeValidator {
static final String TYPE_ID = "RichTextArea";

@Override
public BaseType getType() {
return BaseType.get(TYPE_ID);
}

@Override
public List<ValidationResult> validate(final String dataFieldId,
final JsonNode dataValue,
final CaseFieldDefinition caseFieldDefinition) {
if (isNullOrEmpty(dataValue)) {
return Collections.emptyList();
}

if (!dataValue.isTextual()) {
final String nodeType = dataValue.getNodeType().toString().toLowerCase(Locale.ROOT);
return Collections.singletonList(new ValidationResult(nodeType + " is not a string", dataFieldId));
}

final String value = dataValue.textValue();

final BigDecimal minLength = caseFieldDefinition.getFieldTypeDefinition().getMin();

if (!TextValidator.checkMin(minLength, value)) {
return Collections.singletonList(
new ValidationResult("requires a minimum length of " + minLength, dataFieldId)
);
}

return Collections.emptyList();
}
}
3 changes: 2 additions & 1 deletion src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ management.health.elasticsearch.enabled=false
management.health.status.order=DOWN, OUT_OF_SERVICE, UNKNOWN, UP, OUT_OF_SYNC, PROBLEM, COULD_NOT_CONNECT

# Elasticsearch mappings
elasticsearch.type-mappings.defaultText=Text, TextArea, FixedList, FixedListEdit, MultiSelectList, FixedRadioList, DynamicList, DynamicRadioList, DynamicMultiSelectList, Region, BaseLocation, PhoneUK
elasticsearch.type-mappings.defaultText=Text, TextArea, RichTextArea, FixedList, FixedListEdit, MultiSelectList, FixedRadioList, DynamicList, DynamicRadioList, DynamicMultiSelectList, Region, BaseLocation, PhoneUK
elasticsearch.case-predefined-mappings.defaultText=reference, jurisdiction, state, case_type_id

# Remote Case Audit
Expand Down Expand Up @@ -295,6 +295,7 @@ ccd.messaging.type-mappings.Text=SimpleText
ccd.messaging.type-mappings.PhoneUK=SimpleText
ccd.messaging.type-mappings.Email=SimpleText
ccd.messaging.type-mappings.TextArea=SimpleText
ccd.messaging.type-mappings.RichTextArea=SimpleText
ccd.messaging.type-mappings.BaseLocation=SimpleText
ccd.messaging.type-mappings.Region=SimpleText
ccd.messaging.type-mappings.Date=SimpleDate
Expand Down
3 changes: 3 additions & 0 deletions src/test/java/uk/gov/hmcts/ccd/TestConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ class TestConfiguration extends ContextCleanupListener {
+ " \"type\": \"TextArea\"\n"
+ " },\n"
+ " {\n"
+ " \"type\": \"RichTextArea\"\n"
+ " },\n"
+ " {\n"
+ " \"type\": \"Complex\"\n"
+ " },\n"
+ " {\n"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,19 @@ private void referenceDataNotFoundUpstream() {

private void verifyWiremockInvocation(final String path, final int count) {
final RequestPattern requestPattern = getRequestedFor(urlPathEqualTo(path)).build();
final VerificationResult verificationResult = wireMockServer.countRequestsMatching(requestPattern);
assertThat(verificationResult)
.isNotNull()
.satisfies(result -> assertThat(result.getCount()).isGreaterThanOrEqualTo(count));

await()
.atMost(Durations.FIVE_SECONDS)
.untilAsserted(() -> {
final VerificationResult verificationResult = wireMockServer.countRequestsMatching(requestPattern);

assertThat(verificationResult)
.isNotNull();

assertThat(verificationResult.getCount())
.as("Expected WireMock path %s to be called at least %s times", path, count)
.isGreaterThanOrEqualTo(count);
});
}

private void cacheContainsInitialReferenceData() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public void shouldGetBaseTypes() {

assertAll(
"Assert All of these",
() -> assertThat(baseTypes, IsCollectionWithSize.hasSize(18)),
() -> assertThat(baseTypes, IsCollectionWithSize.hasSize(19)),
() -> assertThat(baseTypes, hasItem(hasProperty("type", is("Text")))),
() -> assertThat(baseTypes, hasItem(hasProperty("type", is("Number")))),
() -> assertThat(baseTypes, hasItem(hasProperty("type", is("Email")))),
Expand All @@ -68,6 +68,7 @@ public void shouldGetBaseTypes() {
() -> assertThat(baseTypes, hasItem(hasProperty("type", is("MoneyGBP")))),
() -> assertThat(baseTypes, hasItem(hasProperty("type", is("PhoneUK")))),
() -> assertThat(baseTypes, hasItem(hasProperty("type", is("TextArea")))),
() -> assertThat(baseTypes, hasItem(hasProperty("type", is("RichTextArea")))),
() -> assertThat(baseTypes, hasItem(hasProperty("type", is(COLLECTION)))),
() -> assertThat(baseTypes, hasItem(hasProperty("type", is("MultiSelectList")))),
() -> assertThat(baseTypes, hasItem(hasProperty("type", is("DynamicRadioList")))),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ class ElasticsearchMappingsTest {
@BeforeEach
void setUp() {
ElasticsearchMappings.TypeMappings typeMappings = new ElasticsearchMappings.TypeMappings(
Arrays.asList("Text", "TextArea", "FixedList", "FixedListEdit", "MultiSelectList", "FixedRadioList",
"DynamicList", "DynamicRadioList", "DynamicMultiSelectList")
Arrays.asList("Text", "TextArea", "RichTextArea", "FixedList", "FixedListEdit", "MultiSelectList",
"FixedRadioList", "DynamicList", "DynamicRadioList", "DynamicMultiSelectList")
);

ElasticsearchMappings.CasePredefinedMappings casePredefinedMappings =
Expand Down
Loading