Skip to content

Commit

Permalink
#337 - Classification Schema validation circumvented by PUT classific…
Browse files Browse the repository at this point in the history
…ation as stringValues
  • Loading branch information
mfriesen committed Feb 1, 2025
1 parent 6eef696 commit 6e3c02d
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import com.formkiq.aws.dynamodb.ApiAuthorization;
import com.formkiq.aws.services.lambda.ApiGatewayRequestEvent;
Expand All @@ -49,6 +50,7 @@
import com.formkiq.stacks.dynamodb.attributes.AttributeValidationType;
import com.formkiq.stacks.dynamodb.attributes.AttributeValidationAccess;
import com.formkiq.stacks.dynamodb.attributes.DocumentAttributeRecord;
import com.formkiq.stacks.dynamodb.attributes.DocumentAttributeValueType;
import com.formkiq.validation.ValidationErrorImpl;
import com.formkiq.validation.ValidationException;

Expand Down Expand Up @@ -141,12 +143,10 @@ private Collection<DocumentAttributeRecord> getDocumentAttributesFromRequest(
Collections.singletonList(new ValidationErrorImpl().error("no attribute values found")));
}

Collection<DocumentAttributeRecord> documentAttributes =
new DocumentAttributeToDocumentAttributeRecord(documentId, authorization.getUsername())
.apply(request.getAttribute());
documentAttributes.forEach(a -> a.setKey(attributeKey));
request.getAttribute().key(attributeKey);

return documentAttributes;
return new DocumentAttributeToDocumentAttributeRecord(documentId, authorization.getUsername())
.apply(request.getAttribute());
}

@Override
Expand All @@ -172,14 +172,22 @@ public ApiRequestHandlerResponse put(final ApiGatewayRequestEvent event,
AttributeValidationAccess validationAccess =
getAttributeValidationAccess(authorization, siteId);

documentService.saveDocumentAttributes(siteId, documentId, documentAttributes,
AttributeValidationType.PARTIAL, validationAccess);
AttributeValidationType type = getValidationType(documentAttributes);
documentService.saveDocumentAttributes(siteId, documentId, documentAttributes, type,
validationAccess);

ApiResponse resp = new ApiMessageResponse(
"Updated attribute '" + attributeKey + "' on document '" + documentId + "'");
return new ApiRequestHandlerResponse(SC_OK, resp);
}

private static AttributeValidationType getValidationType(
final Collection<DocumentAttributeRecord> documentAttributes) {
Optional<DocumentAttributeRecord> o = documentAttributes.stream()
.filter(a -> DocumentAttributeValueType.CLASSIFICATION.equals(a.getValueType())).findAny();
return o.isPresent() ? AttributeValidationType.FULL : AttributeValidationType.PARTIAL;
}

private void verifyDocument(final AwsServiceCache awsservice, final String siteId,
final String documentId) throws Exception {
DocumentService ds = awsservice.getExtension(DocumentService.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,47 +77,60 @@ private Collection<DocumentAttributeRecord> buildAttributeRecords(final Document
String key = a.getKey();

if (isRelationship(a)) {
used = true;
addRelationship(a, c);
} else if (isClassification(a)) {
addClassification(a, c);
} else {

if (!isEmpty(a.getStringValue())) {
used = true;
addToList(c, DocumentAttributeValueType.STRING, key, a.getStringValue(), null, null);
}

if (a.getNumberValue() != null) {
used = true;
addToList(c, DocumentAttributeValueType.NUMBER, key, null, null, a.getNumberValue());
}

if (a.getBooleanValue() != null) {
used = true;
addToList(c, DocumentAttributeValueType.BOOLEAN, key, null, a.getBooleanValue(), null);
}

for (String stringValue : notNull(a.getStringValues())) {
used = true;
addToList(c, DocumentAttributeValueType.STRING, key, stringValue, null, null);
}

for (Double numberValue : notNull(a.getNumberValues())) {
used = true;
addToList(c, DocumentAttributeValueType.NUMBER, key, null, null, numberValue);
}

if (!used) {
addToList(c, DocumentAttributeValueType.KEY_ONLY, key, null, null, null);
}
}
}

if (!isEmpty(a.getClassificationId())) {
used = true;
addToList(c, DocumentAttributeValueType.CLASSIFICATION,
AttributeKeyReserved.CLASSIFICATION.getKey(), a.getClassificationId(), null, null);
}

if (!isEmpty(a.getStringValue())) {
used = true;
addToList(c, DocumentAttributeValueType.STRING, key, a.getStringValue(), null, null);
}

if (a.getNumberValue() != null) {
used = true;
addToList(c, DocumentAttributeValueType.NUMBER, key, null, null, a.getNumberValue());
}

if (a.getBooleanValue() != null) {
used = true;
addToList(c, DocumentAttributeValueType.BOOLEAN, key, null, a.getBooleanValue(), null);
}
return c;
}

for (String stringValue : notNull(a.getStringValues())) {
used = true;
addToList(c, DocumentAttributeValueType.STRING, key, stringValue, null, null);
}
private void addClassification(final DocumentAttribute a,
final Collection<DocumentAttributeRecord> c) {
String k = AttributeKeyReserved.CLASSIFICATION.getKey();

for (Double numberValue : notNull(a.getNumberValues())) {
used = true;
addToList(c, DocumentAttributeValueType.NUMBER, key, null, null, numberValue);
}
if (!isEmpty(a.getClassificationId())) {
addToList(c, DocumentAttributeValueType.CLASSIFICATION, k, a.getClassificationId(), null,
null);
}

if (!used) {
addToList(c, DocumentAttributeValueType.KEY_ONLY, key, null, null, null);
}
if (!isEmpty(a.getStringValue())) {
addToList(c, DocumentAttributeValueType.CLASSIFICATION, k, a.getStringValue(), null, null);
}

return c;
notNull(a.getStringValues())
.forEach(v -> addToList(c, DocumentAttributeValueType.CLASSIFICATION, k, v, null, null));
}

private static boolean isRelationship(final DocumentAttribute a) {
Expand Down Expand Up @@ -153,4 +166,9 @@ private void addToList(final Collection<DocumentAttributeRecord> list, final Str

list.add(a);
}

private boolean isClassification(final DocumentAttribute a) {
String k = AttributeKeyReserved.CLASSIFICATION.getKey();
return k.equalsIgnoreCase(a.getKey()) || !isEmpty(a.getClassificationId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import com.formkiq.client.model.AddDocumentAttribute;
import com.formkiq.client.model.AddDocumentAttributeClassification;
import com.formkiq.client.model.AddDocumentAttributeStandard;
import com.formkiq.client.model.AddDocumentAttributeValue;
import com.formkiq.client.model.AddDocumentAttributesRequest;
import com.formkiq.client.model.AddDocumentRequest;
import com.formkiq.client.model.AttributeDataType;
Expand All @@ -51,7 +52,10 @@
import com.formkiq.client.model.SchemaAttributes;
import com.formkiq.client.model.SearchResultDocument;
import com.formkiq.client.model.SetClassificationRequest;
import com.formkiq.client.model.SetDocumentAttributeRequest;
import com.formkiq.client.model.SetDocumentAttributesRequest;
import com.formkiq.client.model.SetSitesSchemaRequest;
import com.formkiq.stacks.dynamodb.attributes.AttributeKeyReserved;
import com.formkiq.testutils.aws.DynamoDbExtension;
import com.formkiq.testutils.aws.LocalStackExtension;
import org.jetbrains.annotations.NotNull;
Expand Down Expand Up @@ -1045,6 +1049,74 @@ void testAddDocument10() throws ApiException {
}
}

/**
* Set Document Classification, missing required attributes.
*/
@Test
void testSetDocumentClassification01() throws ApiException {
// given
for (String siteId : Arrays.asList(DEFAULT_SITE_ID, ID.uuid())) {

// given
setBearerToken(siteId);

String documentId = addDocument(siteId, null);
addAttribute(siteId, "TestBoolean", AttributeDataType.BOOLEAN);
addAttribute(siteId, "TestBoolean2", AttributeDataType.BOOLEAN);

SchemaAttributes sattr0 = createSchemaAttributes(List.of("TestBoolean"), null);
String classificationId0 = addClassification(siteId, sattr0);

AddDocumentAttributeClassification attr0 =
new AddDocumentAttributeClassification().classificationId(classificationId0);
SetDocumentAttributesRequest setReq0 =
new SetDocumentAttributesRequest().addAttributesItem(new AddDocumentAttribute(attr0));

AddDocumentAttributeStandard attr1 = new AddDocumentAttributeStandard()
.key(AttributeKeyReserved.CLASSIFICATION.getKey()).stringValue(classificationId0);
SetDocumentAttributesRequest setReq1 =
new SetDocumentAttributesRequest().addAttributesItem(new AddDocumentAttribute(attr1));

for (SetDocumentAttributesRequest setReq : List.of(setReq0, setReq1)) {
// when
try {
this.documentAttributesApi.setDocumentAttributes(documentId, setReq, siteId);
fail();
} catch (ApiException e) {
// then
assertEquals(ApiResponseStatus.SC_BAD_REQUEST.getStatusCode(), e.getCode());
assertEquals(
"{\"errors\":[{\"key\":\"TestBoolean\","
+ "\"error\":\"missing required attribute 'TestBoolean'\"}]}",
e.getResponseBody());
}
}

// given
SchemaAttributes sattr1 = createSchemaAttributes(List.of("TestBoolean2"), null);
String classificationId1 = addClassification(siteId, "doc1", sattr1);
SetDocumentAttributeRequest setReqValue =
new SetDocumentAttributeRequest().attribute(new AddDocumentAttributeValue()
.stringValues(List.of(classificationId0, classificationId1)));

// when
try {
this.documentAttributesApi.setDocumentAttributeValue(documentId,
AttributeKeyReserved.CLASSIFICATION.getKey(), setReqValue, siteId);
fail();
} catch (ApiException e) {
// then
assertEquals(ApiResponseStatus.SC_BAD_REQUEST.getStatusCode(), e.getCode());
assertEquals(
"{\"errors\":[{\"key\":\"TestBoolean\","
+ "\"error\":\"missing required attribute 'TestBoolean'\"},"
+ "{\"key\":\"TestBoolean2\","
+ "\"error\":\"missing required attribute 'TestBoolean2'\"}]}",
e.getResponseBody());
}
}
}

private void addDocumentAttributes(final String siteId, final String documentId,
final List<AddDocumentAttribute> attributes) throws ApiException {
AddDocumentAttributesRequest req = new AddDocumentAttributesRequest().attributes(attributes);
Expand Down

0 comments on commit 6e3c02d

Please sign in to comment.