Skip to content

Commit

Permalink
#2349 Automatically register audit-entity types
Browse files Browse the repository at this point in the history
  • Loading branch information
homedirectory committed Oct 30, 2024
1 parent eb641ad commit e1cb8ad
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import com.google.inject.Stage;
import com.google.inject.name.Names;
import org.apache.logging.log4j.Logger;
import ua.com.fielden.platform.audit.AbstractAuditEntity;
import ua.com.fielden.platform.audit.AbstractAuditProp;
import ua.com.fielden.platform.basic.config.ApplicationSettings;
import ua.com.fielden.platform.basic.config.IApplicationDomainProvider;
import ua.com.fielden.platform.basic.config.IApplicationSettings;
Expand All @@ -29,19 +31,22 @@
import java.util.List;
import java.util.Properties;

import static com.google.common.collect.ImmutableList.toImmutableList;
import static org.apache.logging.log4j.LogManager.getLogger;
import static ua.com.fielden.platform.audit.AuditUtils.*;
import static ua.com.fielden.platform.web_api.GraphQLService.DEFAULT_MAX_QUERY_DEPTH;
import static ua.com.fielden.platform.web_api.GraphQLService.WARN_INSUFFICIENT_MAX_QUERY_DEPTH;

/**
* Basic IoC module for server web applications, which should be extended by an application-specific IoC module.
*
* This IoC provides all the necessary bindings for:
* <p>
* This module is reponsible for:
* <ul>
* <li>Applications settings (refer {@link IApplicationSettings});
* <li>Serialisation mechanism;
* <li>Essential DAO interfaces {@link IUser}, {@link IAuthorisationModel}, and more;
* <li>Provides application main menu configuration related DAO bindings.
* <li> Binding of applications settings (refer {@link IApplicationSettings});
* <li> Serialisation mechanism;
* <li> Binding of essential DAO interfaces {@link IUser}, {@link IAuthorisationModel}, and more;
* <li> Providing application main menu configuration related DAO bindings;
* <li> Binding of audit-entity types (subtypes of {@link AbstractAuditEntity} and {@link AbstractAuditProp}).
* </ul>
* <p>
* Instantiation of singletons occurs in accordance with <a href="https://github.com/google/guice/wiki/Scopes#eager-singletons">Guice Eager Singletons</a>,
Expand All @@ -64,7 +69,7 @@ public BasicWebServerIocModule(
{
super(props, domainEntityTypes);
this.props = props;
this.applicationDomainProvider = applicationDomainProvider;
this.applicationDomainProvider = registerAuditTypes(applicationDomainProvider);
// Currently there is no good way of binding the default implementation of IAuthorisationModel other than having multiple constructors in this module.
// Good old @ImplementedBy cannot be used because the default implementation resides in platform-dao, while the interface is in platform-pojo-bl.
this.authorisationModelType = ServerAuthorisationModel.class;
Expand Down Expand Up @@ -141,4 +146,22 @@ public Properties getProps() {
return props;
}

/**
* Returns a new application domain provider that adds an audit-entity type and a corresponding audit-prop type
* to each audited entity type in the specified provider.
*/
private static IApplicationDomainProvider registerAuditTypes(final IApplicationDomainProvider applicationDomainProvider) {
final var newEntityTypes = applicationDomainProvider.entityTypes().stream()
.<Class<? extends AbstractEntity<?>>> mapMulti((type, sink) -> {
sink.accept(type);
if (isAudited(type)) {
sink.accept(getAuditType(type));
sink.accept(getAuditPropType(type));
}
})
.collect(toImmutableList());

return () -> newEntityTypes;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,48 @@ public static boolean isAudited(final Class<? extends AbstractEntity<?>> type) {
return isAnnotationPresentForClass(Audited.class, type);
}

/**
* Locates and returns the audit-entity type for the specified entity type.
* Throws an exception if the audit type cannot be located.
* <p>
* The return type of this method deliberately doesn't contain wildcards, as that would greatly reduce the ergonomics around its usage.
*/
public static <E extends AbstractEntity<?>> Class<AbstractAuditEntity<E>> getAuditType(final Class<E> entityType) {
// TODO Support multiple versions of audit-entity types
final var auditTypeName = getAuditTypeName(entityType, 1);
try {
// TODO verify type is really audit-type
return (Class<AbstractAuditEntity<E>>) entityType.getClassLoader().loadClass(auditTypeName);
} catch (final ClassNotFoundException e) {
throw new InvalidArgumentException("Audit-entity type doesn't exist for entity type [%s]".formatted(entityType.getTypeName()), e);
}
}

static String getAuditTypeName(final Class<? extends AbstractEntity<?>> type, final int version) {
final var simpleName = type.getSimpleName() + "_" + AbstractAuditEntity.A3T + "_" + version;
return type.getPackageName() + "." + simpleName;
}

/**
* Locates and returns the {@linkplain AbstractAuditProp audit-prop entity type} for the specified entity type.
* Throws an exception if the audit-prop type cannot be located.
* <p>
* The return type of this method deliberately doesn't contain wildcards, as that would greatly reduce the ergonomics around its usage.
*/
public static <E extends AbstractEntity<?>> Class<AbstractAuditProp<AbstractAuditEntity<E>>> getAuditPropType(final Class<E> entityType) {
// TODO Support multiple versions of audit-entity types
final var auditPropTypeName = getAuditPropTypeName(entityType, 1);
try {
return (Class<AbstractAuditProp<AbstractAuditEntity<E>>>) entityType.getClassLoader().loadClass(auditPropTypeName);
} catch (final ClassNotFoundException e) {
throw new InvalidArgumentException("Audit-prop entity type doesn't exist for entity type [%s]".formatted(entityType.getTypeName()), e);
}
}

static String getAuditPropTypeName(final Class<? extends AbstractEntity<?>> type, final int version) {
return getAuditTypeName(type, version) + "_Prop";
}

private AuditUtils() {}

}

0 comments on commit e1cb8ad

Please sign in to comment.