Skip to content

Commit edfa546

Browse files
committed
Implement DynamoDB consent image mapper
Implement mapping logic for converting consent images from DynamoDB attribute value maps to the Consent domain data model.
1 parent f35f25f commit edfa546

4 files changed

Lines changed: 231 additions & 3 deletions

File tree

src/main/java/com/consentframework/consenthistory/api/infrastructure/mappers/DynamoDbConsentChangeEventMapper.java

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
package com.consentframework.consenthistory.api.infrastructure.mappers;
22

33
import com.consentframework.consenthistory.api.infrastructure.entities.DynamoDbServiceUserConsentHistoryRecord;
4+
import com.consentframework.consenthistory.api.models.Consent;
45
import com.consentframework.consenthistory.api.models.ConsentChangeEvent;
56
import com.consentframework.consenthistory.api.models.ConsentEventType;
7+
import com.consentframework.consenthistory.api.models.ConsentStatus;
8+
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
69

710
import java.time.OffsetDateTime;
811
import java.time.ZoneOffset;
12+
import java.util.Map;
13+
import java.util.stream.Collectors;
914

1015
/**
1116
* Mapper for converting between DynamoDB consent change event records and domain models.
@@ -18,12 +23,52 @@ public final class DynamoDbConsentChangeEventMapper {
1823
* @return the converted ConsentChangeEvent.
1924
*/
2025
public static ConsentChangeEvent toConsentChangeEvent(final DynamoDbServiceUserConsentHistoryRecord record) {
26+
if (record == null) {
27+
return null;
28+
}
2129
return new ConsentChangeEvent()
2230
.consentId(record.id())
2331
.eventId(record.eventId())
2432
.eventTime(OffsetDateTime.parse(record.eventTime()).withOffsetSameLocal(ZoneOffset.UTC))
2533
.eventType(ConsentEventType.fromValue(record.eventType()))
26-
.oldImage(null) // TODO: Implement oldImage conversion
27-
.newImage(null); // TODO: Implement newImage conversion
34+
.oldImage(toConsent(record.oldImage()))
35+
.newImage(toConsent(record.newImage()));
36+
}
37+
38+
private static Consent toConsent(final Map<String, AttributeValue> ddbConsent) {
39+
if (ddbConsent == null) {
40+
return null;
41+
}
42+
return new Consent()
43+
.serviceId(ddbConsent.get(Consent.JSON_PROPERTY_SERVICE_ID).s())
44+
.userId(ddbConsent.get(Consent.JSON_PROPERTY_USER_ID).s())
45+
.consentId(ddbConsent.get(Consent.JSON_PROPERTY_CONSENT_ID).s())
46+
.consentVersion(Integer.valueOf(ddbConsent.get(Consent.JSON_PROPERTY_CONSENT_VERSION).n()))
47+
.status(ConsentStatus.fromValue(ddbConsent.get(Consent.JSON_PROPERTY_STATUS).s()))
48+
.consentType(toString(ddbConsent.get(Consent.JSON_PROPERTY_CONSENT_TYPE)))
49+
.consentData(toStringMap(ddbConsent.get(Consent.JSON_PROPERTY_CONSENT_DATA)))
50+
.expiryTime(toOffsetDateTime(ddbConsent.get(Consent.JSON_PROPERTY_EXPIRY_TIME)));
51+
}
52+
53+
private static String toString(final AttributeValue optionalAttributeValue) {
54+
if (optionalAttributeValue == null) {
55+
return null;
56+
}
57+
return optionalAttributeValue.s();
58+
}
59+
60+
private static Map<String, String> toStringMap(final AttributeValue optionalAttributeValue) {
61+
if (optionalAttributeValue == null) {
62+
return null;
63+
}
64+
return optionalAttributeValue.m().entrySet().stream()
65+
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().s()));
66+
}
67+
68+
private static OffsetDateTime toOffsetDateTime(final AttributeValue optionalAttributeValue) {
69+
if (optionalAttributeValue == null) {
70+
return null;
71+
}
72+
return OffsetDateTime.parse(optionalAttributeValue.s()).withOffsetSameLocal(ZoneOffset.UTC);
2873
}
2974
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package com.consentframework.consenthistory.api.infrastructure.mappers;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertNotNull;
5+
import static org.junit.jupiter.api.Assertions.assertNull;
6+
7+
import com.consentframework.consenthistory.api.infrastructure.entities.DynamoDbServiceUserConsentHistoryRecord;
8+
import com.consentframework.consenthistory.api.models.Consent;
9+
import com.consentframework.consenthistory.api.models.ConsentChangeEvent;
10+
import com.consentframework.consenthistory.api.models.ConsentEventType;
11+
import com.consentframework.consenthistory.api.testcommon.constants.TestConstants;
12+
import com.consentframework.consenthistory.api.testcommon.utils.DynamoDbServiceUserConsentHistoryRecordGenerator;
13+
import org.junit.jupiter.api.Test;
14+
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
15+
16+
import java.util.Map;
17+
18+
class DynamoDbConsentChangeEventMapperTest {
19+
@Test
20+
void toConsentChangeEventWhenNull() {
21+
final ConsentChangeEvent result = DynamoDbConsentChangeEventMapper.toConsentChangeEvent(null);
22+
assertNull(result);
23+
}
24+
25+
@Test
26+
void toConsentWhenNoImages() {
27+
final DynamoDbServiceUserConsentHistoryRecord ddbRecord = DynamoDbServiceUserConsentHistoryRecordGenerator.generate(
28+
null, null, ConsentEventType.MODIFY);
29+
validateConvertedConsentChangeEvent(ddbRecord);
30+
}
31+
32+
@Test
33+
void toConsentForConsentInsertEvent() {
34+
final Map<String, AttributeValue> ddbOldImage = null;
35+
final Map<String, AttributeValue> ddbNewImage = DynamoDbServiceUserConsentHistoryRecordGenerator.generateDdbConsentImage("1");
36+
final DynamoDbServiceUserConsentHistoryRecord ddbRecord = DynamoDbServiceUserConsentHistoryRecordGenerator.generate(
37+
ddbOldImage, ddbNewImage, ConsentEventType.INSERT);
38+
validateConvertedConsentChangeEvent(ddbRecord);
39+
}
40+
41+
@Test
42+
void toConsentForConsentRemoveEvent() {
43+
final Map<String, AttributeValue> ddbOldImage = DynamoDbServiceUserConsentHistoryRecordGenerator.generateDdbConsentImage("1");
44+
final Map<String, AttributeValue> ddbNewImage = null;
45+
final DynamoDbServiceUserConsentHistoryRecord ddbRecord = DynamoDbServiceUserConsentHistoryRecordGenerator.generate(
46+
ddbOldImage, ddbNewImage, ConsentEventType.REMOVE);
47+
validateConvertedConsentChangeEvent(ddbRecord);
48+
}
49+
50+
@Test
51+
void toConsentForConsentModifyEvent() {
52+
final Map<String, AttributeValue> ddbOldImage = DynamoDbServiceUserConsentHistoryRecordGenerator.generateDdbConsentImage("1");
53+
final Map<String, AttributeValue> ddbNewImage = DynamoDbServiceUserConsentHistoryRecordGenerator.generateDdbConsentImage("2");
54+
final DynamoDbServiceUserConsentHistoryRecord ddbRecord = DynamoDbServiceUserConsentHistoryRecordGenerator.generate(
55+
ddbOldImage, ddbNewImage, ConsentEventType.MODIFY);
56+
validateConvertedConsentChangeEvent(ddbRecord);
57+
}
58+
59+
@Test
60+
void toConsentForConsentUpdateEventDroppingAttributes() {
61+
final Map<String, AttributeValue> ddbOldImage = DynamoDbServiceUserConsentHistoryRecordGenerator.generateDdbConsentImage("1");
62+
final Map<String, AttributeValue> ddbNewImage = DynamoDbServiceUserConsentHistoryRecordGenerator.generateDdbConsentImage(
63+
"2", null, null);
64+
final DynamoDbServiceUserConsentHistoryRecord ddbRecord = DynamoDbServiceUserConsentHistoryRecordGenerator.generate(
65+
ddbOldImage, ddbNewImage, ConsentEventType.MODIFY);
66+
67+
final ConsentChangeEvent result = DynamoDbConsentChangeEventMapper.toConsentChangeEvent(ddbRecord);
68+
assertNotNull(result);
69+
assertEquals(ddbRecord.id(), result.getConsentId());
70+
assertEquals(ddbRecord.eventId(), result.getEventId());
71+
assertEquals(ddbRecord.eventTime(), result.getEventTime().toString());
72+
assertEquals(ddbRecord.eventType(), result.getEventType().name());
73+
validateConsentImage(ddbRecord.oldImage(), result.getOldImage(), TestConstants.TEST_CONSENT_DATA, TestConstants.TEST_EVENT_TIME);
74+
validateConsentImage(ddbRecord.newImage(), result.getNewImage(), null, null);
75+
}
76+
77+
private void validateConvertedConsentChangeEvent(final DynamoDbServiceUserConsentHistoryRecord ddbRecord) {
78+
final ConsentChangeEvent result = DynamoDbConsentChangeEventMapper.toConsentChangeEvent(ddbRecord);
79+
assertNotNull(result);
80+
assertEquals(ddbRecord.id(), result.getConsentId());
81+
assertEquals(ddbRecord.eventId(), result.getEventId());
82+
assertEquals(ddbRecord.eventTime(), result.getEventTime().toString());
83+
assertEquals(ddbRecord.eventType(), result.getEventType().name());
84+
validateConsentImage(ddbRecord.oldImage(), result.getOldImage(), TestConstants.TEST_CONSENT_DATA, TestConstants.TEST_EVENT_TIME);
85+
validateConsentImage(ddbRecord.newImage(), result.getNewImage(), TestConstants.TEST_CONSENT_DATA, TestConstants.TEST_EVENT_TIME);
86+
}
87+
88+
private void validateConsentImage(final Map<String, AttributeValue> ddbConsentImage, final Consent consent,
89+
final Map<String, String> expectedConsentData, final String expectedExpiryTime) {
90+
if (ddbConsentImage == null) {
91+
assertNull(consent);
92+
return;
93+
}
94+
assertNotNull(consent);
95+
assertEquals(TestConstants.TEST_SERVICE_ID, consent.getServiceId());
96+
assertEquals(TestConstants.TEST_USER_ID, consent.getUserId());
97+
assertEquals(TestConstants.TEST_CONSENT_ID, consent.getConsentId());
98+
assertEquals(Integer.valueOf(ddbConsentImage.get(Consent.JSON_PROPERTY_CONSENT_VERSION).n()), consent.getConsentVersion());
99+
assertEquals(ddbConsentImage.get(Consent.JSON_PROPERTY_STATUS).s(), consent.getStatus().name());
100+
assertEquals(ddbConsentImage.get(Consent.JSON_PROPERTY_CONSENT_TYPE).s(), consent.getConsentType());
101+
assertEquals(expectedConsentData, consent.getConsentData());
102+
assertEquals(expectedExpiryTime, expectedExpiryTime == null ? null : consent.getExpiryTime().toString());
103+
}
104+
}

src/test/java/com/consentframework/consenthistory/api/testcommon/constants/TestConstants.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
package com.consentframework.consenthistory.api.testcommon.constants;
22

3+
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
4+
5+
import java.time.OffsetDateTime;
6+
import java.time.ZoneOffset;
7+
import java.util.Map;
8+
import java.util.stream.Collectors;
9+
310
/**
411
* Utility class defining common test constants.
512
*/
@@ -9,6 +16,19 @@ public final class TestConstants {
916
public static final String TEST_USER_ID = "TestUserId";
1017
public static final String TEST_PARTITION_KEY = String.format("%s|%s|%s", TEST_SERVICE_ID, TEST_USER_ID, TEST_CONSENT_ID);
1118

19+
public static final String TEST_CONSENT_TYPE = "TestConsentType";
20+
public static final Map<String, String> TEST_CONSENT_DATA = Map.of(
21+
"testKey1", "testValue1",
22+
"testKey2", "testValue2"
23+
);
24+
public static final Map<String, AttributeValue> TEST_DDB_CONSENT_DATA = TEST_CONSENT_DATA.entrySet()
25+
.stream()
26+
.collect(
27+
Collectors.toMap(Map.Entry::getKey, e -> AttributeValue.builder().s(e.getValue()).build())
28+
);
29+
30+
public static final String TEST_EVENT_TIME = OffsetDateTime.now().withOffsetSameInstant(ZoneOffset.UTC).toString();
31+
1232
public static final String TEST_CONSENT_HISTORY_PATH = String.format(
1333
"/v1/consent-history/services/%s/users/%s/consents/%s",
1434
TEST_SERVICE_ID, TEST_USER_ID, TEST_CONSENT_ID

src/test/java/com/consentframework/consenthistory/api/testcommon/utils/DynamoDbServiceUserConsentHistoryRecordGenerator.java

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
package com.consentframework.consenthistory.api.testcommon.utils;
22

33
import com.consentframework.consenthistory.api.infrastructure.entities.DynamoDbServiceUserConsentHistoryRecord;
4+
import com.consentframework.consenthistory.api.models.Consent;
45
import com.consentframework.consenthistory.api.models.ConsentEventType;
6+
import com.consentframework.consenthistory.api.models.ConsentStatus;
57
import com.consentframework.consenthistory.api.testcommon.constants.TestConstants;
8+
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
69

710
import java.time.OffsetDateTime;
811
import java.time.ZoneOffset;
12+
import java.util.HashMap;
13+
import java.util.Map;
914
import java.util.UUID;
1015

1116
/**
@@ -16,11 +21,65 @@ public final class DynamoDbServiceUserConsentHistoryRecordGenerator {
1621
* Generates a DynamoDbServiceUserConsentHistoryRecord with default values.
1722
*/
1823
public static DynamoDbServiceUserConsentHistoryRecord generate() {
24+
return generate(
25+
generateDdbConsentImage("1"),
26+
generateDdbConsentImage("2"),
27+
ConsentEventType.MODIFY
28+
);
29+
}
30+
31+
/**
32+
* Generates a DynamoDbServiceUserConsentHistoryRecord with default values.
33+
*
34+
* @param oldImage old consent before change event
35+
* @param newImage new consent after change event
36+
* @param eventType type of change
37+
*/
38+
public static DynamoDbServiceUserConsentHistoryRecord generate(
39+
final Map<String, AttributeValue> oldImage,
40+
final Map<String, AttributeValue> newImage,
41+
final ConsentEventType eventType) {
1942
return DynamoDbServiceUserConsentHistoryRecord.builder()
2043
.id(TestConstants.TEST_PARTITION_KEY)
2144
.eventId(UUID.randomUUID().toString())
22-
.eventType(ConsentEventType.MODIFY.name())
45+
.eventType(eventType.name())
2346
.eventTime(OffsetDateTime.now().withOffsetSameInstant(ZoneOffset.UTC).toString())
47+
.oldImage(oldImage)
48+
.newImage(newImage)
2449
.build();
2550
}
51+
52+
/**
53+
* Generates a consent image with default values.
54+
*
55+
* @param consentVersion version
56+
*/
57+
public static Map<String, AttributeValue> generateDdbConsentImage(final String consentVersion) {
58+
return generateDdbConsentImage(consentVersion, TestConstants.TEST_DDB_CONSENT_DATA, TestConstants.TEST_EVENT_TIME);
59+
}
60+
61+
/**
62+
* Generates a consent image with default values.
63+
*
64+
* @param consentVersion version
65+
* @param consentData nested consent data
66+
*/
67+
public static Map<String, AttributeValue> generateDdbConsentImage(final String consentVersion,
68+
final Map<String, AttributeValue> consentData, final String expiryTime) {
69+
final Map<String, AttributeValue> ddbConsentImage = new HashMap<>(Map.of(
70+
Consent.JSON_PROPERTY_SERVICE_ID, AttributeValue.builder().s(TestConstants.TEST_SERVICE_ID).build(),
71+
Consent.JSON_PROPERTY_USER_ID, AttributeValue.builder().s(TestConstants.TEST_USER_ID).build(),
72+
Consent.JSON_PROPERTY_CONSENT_ID, AttributeValue.builder().s(TestConstants.TEST_CONSENT_ID).build(),
73+
Consent.JSON_PROPERTY_CONSENT_VERSION, AttributeValue.builder().n(consentVersion).build(),
74+
Consent.JSON_PROPERTY_STATUS, AttributeValue.builder().s(ConsentStatus.ACTIVE.name()).build(),
75+
Consent.JSON_PROPERTY_CONSENT_TYPE, AttributeValue.builder().s(TestConstants.TEST_CONSENT_TYPE).build()
76+
));
77+
if (consentData != null) {
78+
ddbConsentImage.put(Consent.JSON_PROPERTY_CONSENT_DATA, AttributeValue.builder().m(consentData).build());
79+
}
80+
if (expiryTime != null) {
81+
ddbConsentImage.put(Consent.JSON_PROPERTY_EXPIRY_TIME, AttributeValue.builder().s(expiryTime).build());
82+
}
83+
return ddbConsentImage;
84+
}
2685
}

0 commit comments

Comments
 (0)