Skip to content

Commit f6105be

Browse files
committed
#281 - Add support for auditing.
We now provide auditing support that can be enabled through EnableR2dbcAuditing. @configuration @EnableR2dbcAuditing class Config { @bean public ReactiveAuditorAware<String> myAuditorProvider() { return new AuditorAwareImpl(); } }
1 parent ed63cef commit f6105be

File tree

10 files changed

+474
-2
lines changed

10 files changed

+474
-2
lines changed

Diff for: src/main/asciidoc/index.adoc

+5-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ ifdef::backend-epub3[:front-cover-image: image:epub-cover.png[Front Cover,1050,1
88
:spring-framework-ref: https://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference
99
:reactiveStreamsJavadoc: https://www.reactive-streams.org/reactive-streams-{reactiveStreamsVersion}-javadoc
1010

11-
(C) 2018-2019 The original authors.
11+
(C) 2018-2020 The original authors.
1212

1313
NOTE: Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.
1414

@@ -34,6 +34,10 @@ include::reference/r2dbc.adoc[leveloffset=+1]
3434

3535
include::reference/r2dbc-repositories.adoc[leveloffset=+1]
3636

37+
include::{spring-data-commons-docs}/auditing.adoc[leveloffset=+1]
38+
39+
include::reference/r2dbc-auditing.adoc[leveloffset=+1]
40+
3741
include::reference/r2dbc-connections.adoc[leveloffset=+1]
3842

3943
include::reference/r2dbc-initialization.adoc[leveloffset=+1]

Diff for: src/main/asciidoc/new-features.adoc

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
== What's New in Spring Data R2DBC 1.2.0
66

77
* Support for <<entity-callbacks>>.
8+
* <<r2dbc.auditing,Auditing>> through `@EnableR2dbcAuditing`.
89

910
[[new-features.1-1-0]]
1011
== What's New in Spring Data R2DBC 1.1.0

Diff for: src/main/asciidoc/reference/r2dbc-auditing.adoc

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
[[r2dbc.auditing]]
2+
== General Auditing Configuration for R2DBC
3+
4+
Since Spring Data R2DBC 1.2, auditing can be enabled by annotating a configuration class with the `@EnableR2dbcAuditing` annotation, as the following example shows:
5+
6+
.Activating auditing using JavaConfig
7+
====
8+
[source,java]
9+
----
10+
@Configuration
11+
@EnableR2dbcAuditing
12+
class Config {
13+
14+
@Bean
15+
public ReactiveAuditorAware<AuditableUser> myAuditorProvider() {
16+
return new AuditorAwareImpl();
17+
}
18+
}
19+
----
20+
====
21+
22+
If you expose a bean of type `ReactiveAuditorAware` to the `ApplicationContext`, the auditing infrastructure picks it up automatically and uses it to determine the current user to be set on domain types.
23+
If you have multiple implementations registered in the `ApplicationContext`, you can select the one to be used by explicitly setting the `auditorAwareRef` attribute of `@EnableR2dbcAuditing`.

Diff for: src/main/asciidoc/reference/r2dbc-entity-callbacks.adoc

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[[r2dbc.entity-callbacks]]
22
= Store specific EntityCallbacks
33

4-
Spring Data R2DBC uses the `EntityCallback` API and reacts on the following callbacks.
4+
Spring Data R2DBC uses the `EntityCallback` API for its auditing support and reacts on the following callbacks.
55

66
.Supported Entity Callbacks
77
[%header,cols="4"]
@@ -22,6 +22,11 @@ Spring Data R2DBC uses the `EntityCallback` API and reacts on the following call
2222
Can modify the domain object after reading it from a row.
2323
| `Ordered.LOWEST_PRECEDENCE`
2424

25+
| AuditingEntityCallback
26+
| `onBeforeConvert(T entity, SqlIdentifier table)`
27+
| Marks an auditable entity _created_ or _modified_
28+
| 100
29+
2530
| BeforeSaveCallback
2631
| `onBeforeSave(T entity, OutboundRow row, SqlIdentifier table)`
2732
| Invoked before a domain object is saved. +
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright 2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.r2dbc.config;
17+
18+
import java.lang.annotation.Documented;
19+
import java.lang.annotation.ElementType;
20+
import java.lang.annotation.Inherited;
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
import java.lang.annotation.Target;
24+
25+
import org.springframework.context.annotation.Import;
26+
import org.springframework.data.auditing.DateTimeProvider;
27+
import org.springframework.data.domain.ReactiveAuditorAware;
28+
29+
/**
30+
* Annotation to enable auditing in R2DBC via annotation configuration.
31+
*
32+
* @author Mark Paluch
33+
* @since 1.2
34+
*/
35+
@Inherited
36+
@Documented
37+
@Target(ElementType.TYPE)
38+
@Retention(RetentionPolicy.RUNTIME)
39+
@Import(R2dbcAuditingRegistrar.class)
40+
public @interface EnableR2dbcAuditing {
41+
42+
/**
43+
* Configures the {@link ReactiveAuditorAware} bean to be used to lookup the current principal.
44+
*
45+
* @return empty {@link String} by default.
46+
*/
47+
String auditorAwareRef() default "";
48+
49+
/**
50+
* Configures whether the creation and modification dates are set. Defaults to {@literal true}.
51+
*
52+
* @return {@literal true} by default.
53+
*/
54+
boolean setDates() default true;
55+
56+
/**
57+
* Configures whether the entity shall be marked as modified on creation. Defaults to {@literal true}.
58+
*
59+
* @return {@literal true} by default.
60+
*/
61+
boolean modifyOnCreate() default true;
62+
63+
/**
64+
* Configures a {@link DateTimeProvider} bean name that allows customizing the timestamp to be used for setting
65+
* creation and modification dates.
66+
*
67+
* @return empty {@link String} by default.
68+
*/
69+
String dateTimeProviderRef() default "";
70+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright 2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.r2dbc.config;
17+
18+
import org.springframework.beans.factory.FactoryBean;
19+
import org.springframework.data.mapping.context.PersistentEntities;
20+
import org.springframework.data.r2dbc.mapping.R2dbcMappingContext;
21+
22+
/**
23+
* Simple helper to be able to wire the {@link PersistentEntities} from a {@link R2dbcMappingContext} bean available in
24+
* the application context.
25+
*
26+
* @author Mark Paluch
27+
* @since 1.2
28+
*/
29+
class PersistentEntitiesFactoryBean implements FactoryBean<PersistentEntities> {
30+
31+
private final R2dbcMappingContext mappingContext;
32+
33+
/**
34+
* Creates a new {@link PersistentEntitiesFactoryBean} for the given {@link R2dbcMappingContext}.
35+
*
36+
* @param converter must not be {@literal null}.
37+
*/
38+
public PersistentEntitiesFactoryBean(R2dbcMappingContext mappingContext) {
39+
this.mappingContext = mappingContext;
40+
}
41+
42+
/*
43+
* (non-Javadoc)
44+
* @see org.springframework.beans.factory.FactoryBean#getObject()
45+
*/
46+
@Override
47+
public PersistentEntities getObject() {
48+
return PersistentEntities.of(mappingContext);
49+
}
50+
51+
/*
52+
* (non-Javadoc)
53+
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
54+
*/
55+
@Override
56+
public Class<?> getObjectType() {
57+
return PersistentEntities.class;
58+
}
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Copyright 2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.r2dbc.config;
17+
18+
import java.lang.annotation.Annotation;
19+
20+
import org.springframework.beans.factory.config.BeanDefinition;
21+
import org.springframework.beans.factory.support.AbstractBeanDefinition;
22+
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
23+
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
24+
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
25+
import org.springframework.data.auditing.ReactiveIsNewAwareAuditingHandler;
26+
import org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport;
27+
import org.springframework.data.auditing.config.AuditingConfiguration;
28+
import org.springframework.data.config.ParsingUtils;
29+
import org.springframework.data.r2dbc.mapping.event.ReactiveAuditingEntityCallback;
30+
import org.springframework.util.Assert;
31+
32+
/**
33+
* {@link ImportBeanDefinitionRegistrar} to enable {@link EnableR2dbcAuditing} annotation.
34+
*
35+
* @author Mark Paluch
36+
* @since 1.2
37+
*/
38+
class R2dbcAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
39+
40+
/*
41+
* (non-Javadoc)
42+
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAnnotation()
43+
*/
44+
@Override
45+
protected Class<? extends Annotation> getAnnotation() {
46+
return EnableR2dbcAuditing.class;
47+
}
48+
49+
/*
50+
* (non-Javadoc)
51+
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAuditingHandlerBeanName()
52+
*/
53+
@Override
54+
protected String getAuditingHandlerBeanName() {
55+
return "r2dbcAuditingHandler";
56+
}
57+
58+
/*
59+
* (non-Javadoc)
60+
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAuditHandlerBeanDefinitionBuilder(org.springframework.data.auditing.config.AuditingConfiguration)
61+
*/
62+
@Override
63+
protected BeanDefinitionBuilder getAuditHandlerBeanDefinitionBuilder(AuditingConfiguration configuration) {
64+
65+
Assert.notNull(configuration, "AuditingConfiguration must not be null!");
66+
67+
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ReactiveIsNewAwareAuditingHandler.class);
68+
69+
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(PersistentEntitiesFactoryBean.class);
70+
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
71+
72+
builder.addConstructorArgValue(definition.getBeanDefinition());
73+
return configureDefaultAuditHandlerAttributes(configuration, builder);
74+
}
75+
76+
/*
77+
* (non-Javadoc)
78+
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#registerAuditListener(org.springframework.beans.factory.config.BeanDefinition, org.springframework.beans.factory.support.BeanDefinitionRegistry)
79+
*/
80+
@Override
81+
protected void registerAuditListenerBeanDefinition(BeanDefinition auditingHandlerDefinition,
82+
BeanDefinitionRegistry registry) {
83+
84+
Assert.notNull(auditingHandlerDefinition, "BeanDefinition must not be null!");
85+
Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");
86+
87+
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ReactiveAuditingEntityCallback.class);
88+
89+
builder.addConstructorArgValue(ParsingUtils.getObjectFactoryBeanDefinition(getAuditingHandlerBeanName(), registry));
90+
builder.getRawBeanDefinition().setSource(auditingHandlerDefinition.getSource());
91+
92+
registerInfrastructureBeanWithId(builder.getBeanDefinition(), ReactiveAuditingEntityCallback.class.getName(),
93+
registry);
94+
}
95+
96+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright 2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.r2dbc.mapping.event;
17+
18+
import org.reactivestreams.Publisher;
19+
20+
import org.springframework.beans.factory.ObjectFactory;
21+
import org.springframework.core.Ordered;
22+
import org.springframework.data.auditing.AuditingHandler;
23+
import org.springframework.data.auditing.ReactiveIsNewAwareAuditingHandler;
24+
import org.springframework.data.mapping.callback.EntityCallback;
25+
import org.springframework.data.mapping.context.MappingContext;
26+
import org.springframework.data.relational.core.sql.SqlIdentifier;
27+
import org.springframework.util.Assert;
28+
29+
/**
30+
* Reactive {@link EntityCallback} to populate auditing related fields on an entity about to be saved.
31+
*
32+
* @author Mark Paluch
33+
* @since 1.2
34+
*/
35+
public class ReactiveAuditingEntityCallback implements BeforeConvertCallback<Object>, Ordered {
36+
37+
private final ObjectFactory<ReactiveIsNewAwareAuditingHandler> auditingHandlerFactory;
38+
39+
/**
40+
* Creates a new {@link BeforeConvertCallback} using the given {@link MappingContext} and {@link AuditingHandler}
41+
* provided by the given {@link ObjectFactory}.
42+
*
43+
* @param auditingHandlerFactory must not be {@literal null}.
44+
*/
45+
public ReactiveAuditingEntityCallback(ObjectFactory<ReactiveIsNewAwareAuditingHandler> auditingHandlerFactory) {
46+
47+
Assert.notNull(auditingHandlerFactory, "IsNewAwareAuditingHandler must not be null!");
48+
this.auditingHandlerFactory = auditingHandlerFactory;
49+
}
50+
51+
/*
52+
* (non-Javadoc)
53+
* @see org.springframework.data.r2dbc.mapping.event.ReactiveBeforeConvertCallback#onBeforeConvert(java.lang.Object, SqlIdentifier)
54+
*/
55+
@Override
56+
public Publisher<Object> onBeforeConvert(Object entity, SqlIdentifier table) {
57+
return auditingHandlerFactory.getObject().markAudited(entity);
58+
}
59+
60+
/*
61+
* (non-Javadoc)
62+
* @see org.springframework.core.Ordered#getOrder()
63+
*/
64+
@Override
65+
public int getOrder() {
66+
return 100;
67+
}
68+
}

0 commit comments

Comments
 (0)