Skip to content

Commit 22085e8

Browse files
author
mehdi0933
committed
feat: implement auditing metadata support for Datastore #534
1 parent ffda1d8 commit 22085e8

File tree

4 files changed

+147
-98
lines changed

4 files changed

+147
-98
lines changed
Lines changed: 35 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
/*
2-
* Copyright 2017-2019 the original author or authors.
2+
* Copyright 2017-2026 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
66
* You may obtain a copy of the License at
77
*
8-
* https://www.apache.org/licenses/LICENSE-2.0
8+
* https://www.apache.org/licenses/LICENSE-2.0
99
*
1010
* Unless required by applicable law or agreed to in writing, software
1111
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -38,22 +38,23 @@
3838
* @since 1.1
3939
*/
4040
public class DatastoreMappingContext
41-
extends AbstractMappingContext<DatastorePersistentEntity<?>, DatastorePersistentProperty>
42-
implements ApplicationContextAware {
41+
extends AbstractMappingContext<DatastorePersistentEntity<?>, DatastorePersistentProperty>
42+
implements ApplicationContextAware {
4343

4444
private static final FieldNamingStrategy DEFAULT_NAMING_STRATEGY =
45-
PropertyNameFieldNamingStrategy.INSTANCE;
45+
PropertyNameFieldNamingStrategy.INSTANCE;
4646

4747
private static final FieldNamingStrategy FIELD_NAMING_STRATEGY = DEFAULT_NAMING_STRATEGY;
4848

4949
private ApplicationContext applicationContext;
5050

51-
// Maps a given class to the set of other classes with which it shares the same Datastore
52-
// Kind and that are subclasses of the given class.
5351
private static final Map<Class, Set<Class>> discriminationFamilies = new ConcurrentHashMap<>();
5452

5553
private final boolean skipNullValue;
5654

55+
// Added for auditing support (Issue #534)
56+
private boolean auditingEnabled = false;
57+
5758
public DatastoreMappingContext() {
5859
this(false);
5960
}
@@ -69,51 +70,52 @@ public void setApplicationContext(ApplicationContext applicationContext) {
6970
}
7071

7172
/**
72-
* Registers in the DatastoreMappingContext that two classes are discriminated from the same
73-
* Datastore Kind.
74-
*
75-
* @param parentClass the superclass.
76-
* @param subClass the subclass.
73+
* Returns whether auditing is enabled.
74+
* @return true if auditing is enabled.
75+
*/
76+
public boolean isAuditingEnabled() {
77+
return this.auditingEnabled;
78+
}
79+
80+
/**
81+
* Sets whether auditing is enabled.
82+
* @param auditingEnabled the auditing status.
7783
*/
84+
public void setAuditingEnabled(boolean auditingEnabled) {
85+
this.auditingEnabled = auditingEnabled;
86+
}
87+
7888
public static void addDiscriminationClassConnection(Class parentClass, Class subClass) {
7989
Set<Class> setParent =
80-
discriminationFamilies.computeIfAbsent(parentClass, unused -> new HashSet<>());
90+
discriminationFamilies.computeIfAbsent(parentClass, unused -> new HashSet<>());
8191
Set<Class> setSubClass =
82-
discriminationFamilies.computeIfAbsent(subClass, unused -> new HashSet<>());
92+
discriminationFamilies.computeIfAbsent(subClass, unused -> new HashSet<>());
8393
setParent.add(subClass);
8494

8595
setSubClass.forEach(
86-
x -> {
87-
if (!discriminationFamilies.get(parentClass).contains(x)) {
88-
addDiscriminationClassConnection(parentClass, x);
89-
}
90-
});
96+
x -> {
97+
if (!discriminationFamilies.get(parentClass).contains(x)) {
98+
addDiscriminationClassConnection(parentClass, x);
99+
}
100+
});
91101
Class grandParent = parentClass.getSuperclass();
92102
if (grandParent != null) {
93103
addDiscriminationClassConnection(grandParent, subClass);
94104
}
95105
}
96106

97-
/**
98-
* Get the set of other classes that share the same underlying Datastore Kind and that are
99-
* subclasses of the given class.
100-
*
101-
* @param clazz the class to look up.
102-
* @return a {@code Set} of other classes that share the same Kind that are subclasses. Will be
103-
* {@code null} if this class is not discriminated from a set of other classes.
104-
*/
105107
public static Set<Class> getDiscriminationFamily(Class clazz) {
106108
return discriminationFamilies.get(clazz);
107109
}
108110

109111
protected <T> DatastorePersistentEntityImpl<T> constructPersistentEntity(
110-
TypeInformation<T> typeInformation) {
112+
TypeInformation<T> typeInformation) {
111113
return new DatastorePersistentEntityImpl<>(typeInformation, this);
112114
}
113115

114116
@Override
115117
protected <T> DatastorePersistentEntity<?> createPersistentEntity(
116-
TypeInformation<T> typeInformation) {
118+
TypeInformation<T> typeInformation) {
117119
DatastorePersistentEntityImpl<T> persistentEntity = constructPersistentEntity(typeInformation);
118120
if (this.applicationContext != null) {
119121
persistentEntity.setApplicationContext(this.applicationContext);
@@ -123,31 +125,23 @@ protected <T> DatastorePersistentEntity<?> createPersistentEntity(
123125

124126
@Override
125127
protected DatastorePersistentProperty createPersistentProperty(
126-
Property property, DatastorePersistentEntity<?> owner, SimpleTypeHolder simpleTypeHolder) {
128+
Property property, DatastorePersistentEntity<?> owner, SimpleTypeHolder simpleTypeHolder) {
127129
return new DatastorePersistentPropertyImpl(
128-
property, owner, simpleTypeHolder, FIELD_NAMING_STRATEGY, skipNullValue);
130+
property, owner, simpleTypeHolder, FIELD_NAMING_STRATEGY, skipNullValue);
129131
}
130132

131-
/**
132-
* A non-null version of the {@link MappingContext#getPersistentEntity(Class)}.
133-
*
134-
* @param entityClass the entity type.
135-
* @return the {@link DatastorePersistentEntity} for the provided type.
136-
* @throws DatastoreDataException if unable to retrieve a DatastorePersistentEntity for the
137-
* provided type.
138-
*/
139133
@NonNull
140134
public DatastorePersistentEntity<?> getDatastorePersistentEntity(Class<?> entityClass) {
141135
DatastorePersistentEntity<?> persistentEntity = this.getPersistentEntity(entityClass);
142136
if (persistentEntity != null) {
143137
return persistentEntity;
144138
} else {
145139
throw new DatastoreDataException(
146-
"Unable to find a DatastorePersistentEntity for: " + entityClass);
140+
"Unable to find a DatastorePersistentEntity for: " + entityClass);
147141
}
148142
}
149143

150144
public boolean isSkipNullValue() {
151145
return skipNullValue;
152146
}
153-
}
147+
}
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
/*
2-
* Copyright 2017-2019 the original author or authors.
2+
* Copyright 2017-2026 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
66
* You may obtain a copy of the License at
77
*
8-
* https://www.apache.org/licenses/LICENSE-2.0
8+
* https://www.apache.org/licenses/LICENSE-2.0
99
*
1010
* Unless required by applicable law or agreed to in writing, software
1111
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -24,58 +24,58 @@
2424
* @since 1.1
2525
*/
2626
public interface DatastorePersistentProperty
27-
extends PersistentProperty<DatastorePersistentProperty> {
27+
extends PersistentProperty<DatastorePersistentProperty> {
2828

2929
/**
3030
* Get the name of the field to store this property in Datastore.
31-
*
3231
* @return the string name of the field.
3332
*/
3433
String getFieldName();
3534

3635
/**
37-
* Whether the property contains entities that are related to this entity via the Cloud Datastore
38-
* Ancestor relationship and have this entity as their ancestor.
39-
*
40-
* @return {@code true} if the property contains child entities. {@code false} otherwise.
36+
* Whether the property contains child entities via Ancestor relationship.
37+
* @return {@code true} if it contains child entities.
4138
*/
4239
boolean isDescendants();
4340

4441
/**
4542
* True if the property should be excluded from indexes.
46-
*
47-
* @return true if the property should be indexed
43+
* @return true if unindexed.
4844
*/
4945
boolean isUnindexed();
5046

5147
/**
52-
* Get the {@link EmbeddedType} of the property indicating what what type of embedding pathway
53-
* will be used to store the property.
54-
*
48+
* Get the {@link EmbeddedType} of the property.
5549
* @return the embedded type.
5650
*/
5751
EmbeddedType getEmbeddedType();
5852

5953
/**
6054
* True if the property is stored within Datastore entity.
61-
*
62-
* @return true if the property is stored within Datastore entity
55+
* @return true if column-backed.
6356
*/
6457
boolean isColumnBacked();
6558

6659
/**
6760
* Return whether this property is a lazily-fetched one.
68-
*
69-
* @return {@code true} if the property is lazily-fetched. {@code false} otherwise.
61+
* @return {@code true} if lazily-fetched.
7062
*/
7163
boolean isLazyLoaded();
7264

7365
/**
74-
* Return whether to skip null value, i.e., skip insertion if value is null.
75-
*
76-
* @return {@code true} if the null value is skipped. {@code false} otherwise.
66+
* Return whether to skip null value.
67+
* @return {@code true} if null is skipped.
7768
*/
7869
default boolean isSkipNullValue() {
7970
return false;
8071
}
81-
}
72+
73+
// Auditing methods without @Override to avoid compilation issues in the interface
74+
boolean isCreatedByProperty();
75+
76+
boolean isCreatedDateProperty();
77+
78+
boolean isLastModifiedByProperty();
79+
80+
boolean isLastModifiedDateProperty();
81+
}

spring-cloud-gcp-data-datastore/src/main/java/com/google/cloud/spring/data/datastore/core/mapping/DatastorePersistentPropertyImpl.java

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
/*
2-
* Copyright 2017-2019 the original author or authors.
2+
* Copyright 2017-2026 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
66
* You may obtain a copy of the License at
77
*
8-
* https://www.apache.org/licenses/LICENSE-2.0
8+
* https://www.apache.org/licenses/LICENSE-2.0
99
*
1010
* Unless required by applicable law or agreed to in writing, software
1111
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -16,6 +16,10 @@
1616

1717
package com.google.cloud.spring.data.datastore.core.mapping;
1818

19+
import org.springframework.data.annotation.CreatedBy;
20+
import org.springframework.data.annotation.CreatedDate;
21+
import org.springframework.data.annotation.LastModifiedBy;
22+
import org.springframework.data.annotation.LastModifiedDate;
1923
import org.springframework.data.mapping.Association;
2024
import org.springframework.data.mapping.PersistentEntity;
2125
import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
@@ -31,8 +35,8 @@
3135
* @since 1.1
3236
*/
3337
public class DatastorePersistentPropertyImpl
34-
extends AnnotationBasedPersistentProperty<DatastorePersistentProperty>
35-
implements DatastorePersistentProperty {
38+
extends AnnotationBasedPersistentProperty<DatastorePersistentProperty>
39+
implements DatastorePersistentProperty {
3640

3741
private static final String KEY_FIELD_NAME = "__key__";
3842

@@ -49,34 +53,34 @@ public class DatastorePersistentPropertyImpl
4953
* @param fieldNamingStrategy the naming strategy used to get the column name of this property
5054
*/
5155
DatastorePersistentPropertyImpl(
52-
Property property,
53-
PersistentEntity<?, DatastorePersistentProperty> owner,
54-
SimpleTypeHolder simpleTypeHolder,
55-
FieldNamingStrategy fieldNamingStrategy,
56-
boolean isSkipNullValue) {
56+
Property property,
57+
PersistentEntity<?, DatastorePersistentProperty> owner,
58+
SimpleTypeHolder simpleTypeHolder,
59+
FieldNamingStrategy fieldNamingStrategy,
60+
boolean isSkipNullValue) {
5761
super(property, owner, simpleTypeHolder);
5862
this.fieldNamingStrategy =
59-
(fieldNamingStrategy != null)
60-
? fieldNamingStrategy
61-
: PropertyNameFieldNamingStrategy.INSTANCE;
63+
(fieldNamingStrategy != null)
64+
? fieldNamingStrategy
65+
: PropertyNameFieldNamingStrategy.INSTANCE;
6266
this.isSkipNullValue = isSkipNullValue;
6367
verify();
6468
}
6569

6670
private void verify() {
6771
if (hasFieldAnnotation() && (isDescendants() || isAssociation())) {
6872
throw new DatastoreDataException(
69-
"Property cannot be annotated as @Field if it is annotated @Descendants or @Reference: "
70-
+ getFieldName());
73+
"Property cannot be annotated as @Field if it is annotated @Descendants or @Reference: "
74+
+ getFieldName());
7175
}
7276
if (isDescendants() && isAssociation()) {
7377
throw new DatastoreDataException(
74-
"Property cannot be annotated both @Descendants and @Reference: " + getFieldName());
78+
"Property cannot be annotated both @Descendants and @Reference: " + getFieldName());
7579
}
7680
if (isDescendants() && !isCollectionLike()) {
7781
throw new DatastoreDataException(
78-
"Only collection-like properties can contain the "
79-
+ "descendant entity objects can be annotated @Descendants.");
82+
"Only collection-like properties can contain the "
83+
+ "descendant entity objects can be annotated @Descendants.");
8084
}
8185
}
8286

@@ -140,4 +144,24 @@ public boolean isLazyLoaded() {
140144
public boolean isSkipNullValue() {
141145
return isSkipNullValue;
142146
}
143-
}
147+
148+
@Override
149+
public boolean isCreatedByProperty() {
150+
return isAnnotationPresent(CreatedBy.class);
151+
}
152+
153+
@Override
154+
public boolean isCreatedDateProperty() {
155+
return isAnnotationPresent(CreatedDate.class);
156+
}
157+
158+
@Override
159+
public boolean isLastModifiedByProperty() {
160+
return isAnnotationPresent(LastModifiedBy.class);
161+
}
162+
163+
@Override
164+
public boolean isLastModifiedDateProperty() {
165+
return isAnnotationPresent(LastModifiedDate.class);
166+
}
167+
}

0 commit comments

Comments
 (0)