Skip to content

Commit b8f8217

Browse files
adamsaghygalovics
authored andcommitted
Add CustomAuditHandler for supporting OffsetDateTime and store in UTC
1 parent da920b1 commit b8f8217

12 files changed

Lines changed: 626 additions & 0 deletions

File tree

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.fineract.infrastructure.core.auditing;
20+
21+
import java.time.LocalDateTime;
22+
import java.time.OffsetDateTime;
23+
import java.time.ZoneId;
24+
import java.time.temporal.TemporalAccessor;
25+
import java.util.Optional;
26+
import org.apache.fineract.infrastructure.core.service.DateUtils;
27+
import org.jetbrains.annotations.NotNull;
28+
import org.springframework.data.auditing.DateTimeProvider;
29+
30+
public enum CustomDateTimeProvider implements DateTimeProvider {
31+
32+
INSTANCE, TENANT;
33+
34+
/*
35+
* (non-Javadoc)
36+
*
37+
* @see org.springframework.data.auditing.DateTimeProvider#getNow()
38+
*/
39+
@NotNull
40+
@Override
41+
public Optional<TemporalAccessor> getNow() {
42+
43+
switch (this) {
44+
case INSTANCE -> {
45+
return Optional.of(LocalDateTime.now(ZoneId.systemDefault()));
46+
}
47+
case TENANT -> {
48+
return Optional.of(OffsetDateTime.now(DateUtils.getDateTimeZoneOfTenant()));
49+
}
50+
}
51+
throw new UnsupportedOperationException(this + " is not supported!");
52+
}
53+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.fineract.infrastructure.core.auditing;
20+
21+
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
22+
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
23+
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
24+
import org.springframework.core.type.AnnotationMetadata;
25+
import org.springframework.data.auditing.CustomAuditingHandler;
26+
27+
public class JpaAuditingHandlerRegistrar implements ImportBeanDefinitionRegistrar {
28+
29+
@Override
30+
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
31+
registry.registerBeanDefinition("jpaAuditingHandler", BeanDefinitionBuilder.rootBeanDefinition(CustomAuditingHandler.class)
32+
.addConstructorArgReference("jpaMappingContext").addConstructorArgReference("auditorAware").getBeanDefinition());
33+
}
34+
}

fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/JPAConfig.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
package org.apache.fineract.infrastructure.core.config;
2121

2222
import java.util.Map;
23+
import org.apache.fineract.infrastructure.core.auditing.JpaAuditingHandlerRegistrar;
2324
import org.apache.fineract.infrastructure.core.domain.AuditorAwareImpl;
2425
import org.apache.fineract.infrastructure.core.persistence.DatabaseSelectingPersistenceUnitPostProcessor;
2526
import org.apache.fineract.infrastructure.core.persistence.ExtendedJpaTransactionManager;
@@ -36,6 +37,7 @@
3637
import org.springframework.context.annotation.Bean;
3738
import org.springframework.context.annotation.Configuration;
3839
import org.springframework.context.annotation.DependsOn;
40+
import org.springframework.context.annotation.Import;
3941
import org.springframework.context.annotation.Primary;
4042
import org.springframework.data.domain.AuditorAware;
4143
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
@@ -53,6 +55,7 @@
5355
@EnableJpaAuditing
5456
@EnableJpaRepositories(basePackages = "org.apache.fineract.**.domain")
5557
@EnableConfigurationProperties(JpaProperties.class)
58+
@Import(JpaAuditingHandlerRegistrar.class)
5659
public class JPAConfig extends JpaBaseConfiguration {
5760

5861
private final DatabaseTypeResolver databaseTypeResolver;
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.fineract.infrastructure.core.domain;
20+
21+
import static org.apache.fineract.infrastructure.core.domain.AuditableFieldsConstants.CREATED_BY_DB_FIELD;
22+
import static org.apache.fineract.infrastructure.core.domain.AuditableFieldsConstants.CREATED_DATE_DB_FIELD;
23+
import static org.apache.fineract.infrastructure.core.domain.AuditableFieldsConstants.LAST_MODIFIED_BY_DB_FIELD;
24+
import static org.apache.fineract.infrastructure.core.domain.AuditableFieldsConstants.LAST_MODIFIED_DATE_DB_FIELD;
25+
26+
import java.time.OffsetDateTime;
27+
import java.util.Optional;
28+
import javax.persistence.Column;
29+
import javax.persistence.MappedSuperclass;
30+
import org.apache.fineract.infrastructure.core.service.DateUtils;
31+
import org.springframework.data.domain.Auditable;
32+
import org.springframework.data.jpa.domain.AbstractAuditable;
33+
34+
/**
35+
* A custom copy of {@link AbstractAuditable} to override the column names used on database. It also uses OffsetDateTime
36+
* for created and modified. The datetimes will be converted from tenant TZ to UTC before storing (automatically
37+
* happens) and converted from System TZ to tenant TZ after fetching from DB
38+
*
39+
* Abstract base class for auditable entities. Stores the audit values in persistent fields.
40+
*/
41+
@MappedSuperclass
42+
public abstract class AbstractAuditableWithUTCDateTimeCustom extends AbstractPersistableCustom
43+
implements Auditable<Long, Long, OffsetDateTime> {
44+
45+
private static final long serialVersionUID = 141481953116476081L;
46+
47+
@Column(name = CREATED_BY_DB_FIELD, nullable = false)
48+
private Long createdBy;
49+
50+
@Column(name = CREATED_DATE_DB_FIELD, nullable = false)
51+
private OffsetDateTime createdDate;
52+
53+
@Column(name = LAST_MODIFIED_BY_DB_FIELD, nullable = false)
54+
private Long lastModifiedBy;
55+
56+
@Column(name = LAST_MODIFIED_DATE_DB_FIELD, nullable = false)
57+
private OffsetDateTime lastModifiedDate;
58+
59+
@Override
60+
public Optional<Long> getCreatedBy() {
61+
return Optional.ofNullable(this.createdBy);
62+
}
63+
64+
@Override
65+
public void setCreatedBy(final Long createdBy) {
66+
this.createdBy = createdBy;
67+
}
68+
69+
@Override
70+
public Optional<OffsetDateTime> getCreatedDate() {
71+
if (this.createdDate == null) {
72+
return Optional.empty();
73+
} else {
74+
return Optional.of(this.createdDate
75+
.withOffsetSameInstant(DateUtils.getDateTimeZoneOfTenant().getRules().getOffset(this.createdDate.toInstant())));
76+
}
77+
}
78+
79+
@Override
80+
public void setCreatedDate(final OffsetDateTime createdDate) {
81+
this.createdDate = createdDate;
82+
}
83+
84+
@Override
85+
public Optional<Long> getLastModifiedBy() {
86+
return Optional.ofNullable(this.lastModifiedBy);
87+
}
88+
89+
@Override
90+
public void setLastModifiedBy(final Long lastModifiedBy) {
91+
this.lastModifiedBy = lastModifiedBy;
92+
}
93+
94+
@Override
95+
public Optional<OffsetDateTime> getLastModifiedDate() {
96+
if (this.lastModifiedDate == null) {
97+
return Optional.empty();
98+
} else {
99+
return Optional.of(this.lastModifiedDate
100+
.withOffsetSameInstant(DateUtils.getDateTimeZoneOfTenant().getRules().getOffset(this.lastModifiedDate.toInstant())));
101+
}
102+
}
103+
104+
@Override
105+
public void setLastModifiedDate(final OffsetDateTime lastModifiedDate) {
106+
this.lastModifiedDate = lastModifiedDate;
107+
}
108+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.fineract.infrastructure.core.domain;
20+
21+
public final class AuditableFieldsConstants {
22+
23+
public static final String CREATED_BY = "createdBy";
24+
public static final String CREATED_DATE = "createdDate";
25+
public static final String LAST_MODIFIED_BY = "lastModifiedBy";
26+
public static final String LAST_MODIFIED_DATE = "lastModifiedDate";
27+
28+
public static final String CREATED_BY_DB_FIELD = "created_by";
29+
public static final String CREATED_DATE_DB_FIELD = "created_on_utc";
30+
public static final String LAST_MODIFIED_BY_DB_FIELD = "last_modified_by";
31+
public static final String LAST_MODIFIED_DATE_DB_FIELD = "last_modified_on_utc";
32+
33+
private AuditableFieldsConstants() {}
34+
}

fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/GoogleGsonSerializerHelper.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.time.LocalDateTime;
2626
import java.time.LocalTime;
2727
import java.time.MonthDay;
28+
import java.time.OffsetDateTime;
2829
import java.time.ZonedDateTime;
2930
import java.util.ArrayList;
3031
import java.util.HashSet;
@@ -35,6 +36,7 @@
3536
import org.apache.fineract.infrastructure.core.api.LocalDateAdapter;
3637
import org.apache.fineract.infrastructure.core.api.LocalDateTimeAdapter;
3738
import org.apache.fineract.infrastructure.core.api.LocalTimeAdapter;
39+
import org.apache.fineract.infrastructure.core.api.OffsetDateTimeAdapter;
3840
import org.apache.fineract.infrastructure.core.api.ParameterListExclusionStrategy;
3941
import org.apache.fineract.infrastructure.core.api.ParameterListInclusionStrategy;
4042
import org.apache.fineract.infrastructure.core.exception.UnsupportedParameterException;
@@ -115,5 +117,6 @@ public static void registerTypeAdapters(final GsonBuilder builder) {
115117
builder.registerTypeAdapter(ZonedDateTime.class, new JodaDateTimeAdapter());
116118
builder.registerTypeAdapter(MonthDay.class, new JodaMonthDayAdapter());
117119
builder.registerTypeAdapter(LocalDateTime.class, new LocalDateTimeAdapter());
120+
builder.registerTypeAdapter(OffsetDateTime.class, new OffsetDateTimeAdapter());
118121
}
119122
}

0 commit comments

Comments
 (0)