Merge branch 'dmp-refactoring' of https://code-repo.d4science.org/MaDgiK-CITE/argos into dmp-refactoring

# Conflicts:
#	dmp-frontend/src/app/ui/description/editor/description-form/description-form-annotation.service.ts
This commit is contained in:
Diamantis Tziotzios 2024-03-29 15:47:43 +02:00
commit ba26a1bd67
113 changed files with 2284 additions and 1017 deletions

View File

@ -0,0 +1,56 @@
package gr.cite.annotation.web.authorization;
import gr.cite.annotation.authorization.AffiliatedAuthorizationRequirement;
import gr.cite.annotation.authorization.AffiliatedResource;
import gr.cite.commons.web.authz.handler.AuthorizationHandler;
import gr.cite.commons.web.authz.handler.AuthorizationHandlerContext;
import gr.cite.commons.web.authz.policy.AuthorizationRequirement;
import gr.cite.commons.web.oidc.principal.MyPrincipal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component("affiliatedAuthorizationHandler")
public class AffiliatedAuthorizationHandler extends AuthorizationHandler<AffiliatedAuthorizationRequirement> {
private final CustomPermissionAttributesConfiguration myConfiguration;
@Autowired
public AffiliatedAuthorizationHandler(CustomPermissionAttributesConfiguration myConfiguration) {
this.myConfiguration = myConfiguration;
}
@Override
public int handleRequirement(AuthorizationHandlerContext context, Object resource, AuthorizationRequirement requirement) {
AffiliatedAuthorizationRequirement req = (AffiliatedAuthorizationRequirement) requirement;
if (req.getRequiredPermissions() == null)
return ACCESS_NOT_DETERMINED;
AffiliatedResource rs = (AffiliatedResource) resource;
boolean isAuthenticated = ((MyPrincipal) context.getPrincipal()).isAuthenticated();
if (!isAuthenticated)
return ACCESS_NOT_DETERMINED;
if (myConfiguration.getMyPolicies() == null)
return ACCESS_NOT_DETERMINED;
int hits = 0;
Boolean entityAffiliated = rs != null && rs.getAffiliated() != null ? rs.getAffiliated() : null;
for (String permission : req.getRequiredPermissions()) {
CustomPermissionAttributesProperties.MyPermission policy = myConfiguration.getMyPolicies().get(permission);
boolean hasPermission = policy != null && policy.getEntityAffiliated() != null && policy.getEntityAffiliated() && entityAffiliated != null && entityAffiliated;
if (hasPermission) hits += 1;
}
if ((req.getMatchAll() && req.getRequiredPermissions().size() == hits) || (!req.getMatchAll() && hits > 0))
return ACCESS_GRANTED;
return ACCESS_NOT_DETERMINED;
}
@Override
public Class<? extends AuthorizationRequirement> supporting() {
return AffiliatedAuthorizationRequirement.class;
}
}

View File

@ -0,0 +1,24 @@
package gr.cite.annotation.web.authorization;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
@Configuration
@EnableConfigurationProperties(CustomPermissionAttributesProperties.class)
public class CustomPermissionAttributesConfiguration {
private final CustomPermissionAttributesProperties properties;
@Autowired
public CustomPermissionAttributesConfiguration(CustomPermissionAttributesProperties properties) {
this.properties = properties;
}
public HashMap<String, CustomPermissionAttributesProperties.MyPermission> getMyPolicies() {
return properties.getPolicies();
}
}

View File

@ -0,0 +1,38 @@
package gr.cite.annotation.web.authorization;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.ConstructorBinding;
import java.util.HashMap;
@ConfigurationProperties(prefix = "permissions")
@ConditionalOnProperty(prefix = "permissions", name = "enabled", havingValue = "true")
public class CustomPermissionAttributesProperties {
private final HashMap<String, MyPermission> policies;
@ConstructorBinding
public CustomPermissionAttributesProperties(HashMap<String, MyPermission> policies) {
this.policies = policies;
}
public HashMap<String, MyPermission> getPolicies() {
return policies;
}
public static class MyPermission {
private final Boolean entityAffiliated;
@ConstructorBinding
public MyPermission(Boolean entityAffiliated) {
this.entityAffiliated = entityAffiliated;
}
public Boolean getEntityAffiliated() {
return entityAffiliated;
}
}
}

View File

@ -1,5 +1,8 @@
package gr.cite.annotation.web.config;
import gr.cite.annotation.authorization.AffiliatedAuthorizationRequirement;
import gr.cite.annotation.authorization.AffiliatedResource;
import gr.cite.annotation.web.authorization.AffiliatedAuthorizationHandler;
import gr.cite.commons.web.authz.handler.AuthorizationHandler;
import gr.cite.commons.web.authz.handler.PermissionClientAuthorizationHandler;
import gr.cite.commons.web.authz.policy.AuthorizationRequirement;
@ -37,16 +40,19 @@ public class SecurityConfiguration {
private final AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;
private final Filter apiKeyFilter;
private final OwnedAuthorizationHandler ownedAuthorizationHandler;
private final AffiliatedAuthorizationHandler affiliatedAuthorizationHandler;
@Autowired
public SecurityConfiguration(WebSecurityProperties webSecurityProperties,
@Qualifier("tokenAuthenticationResolver") AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver,
@Qualifier("apiKeyFilter") Filter apiKeyFilter,
@Qualifier("ownedAuthorizationHandler") OwnedAuthorizationHandler ownedAuthorizationHandler) {
@Qualifier("ownedAuthorizationHandler") OwnedAuthorizationHandler ownedAuthorizationHandler,
@Qualifier("affiliatedAuthorizationHandler") AffiliatedAuthorizationHandler affiliatedAuthorizationHandler) {
this.webSecurityProperties = webSecurityProperties;
this.authenticationManagerResolver = authenticationManagerResolver;
this.apiKeyFilter = apiKeyFilter;
this.ownedAuthorizationHandler = ownedAuthorizationHandler;
this.affiliatedAuthorizationHandler = affiliatedAuthorizationHandler;
}
@Bean
@ -78,7 +84,7 @@ public class SecurityConfiguration {
//If not set / set to null, only the default authorization handlers will be used
@Override
public List<AuthorizationHandler<? extends AuthorizationRequirement>> addCustomHandlers() {
return List.of(ownedAuthorizationHandler);
return List.of(affiliatedAuthorizationHandler, ownedAuthorizationHandler);
}
//Here you can register your custom authorization requirements (if any)
@ -114,6 +120,9 @@ public class SecurityConfiguration {
if (OwnedResource.class.equals(type)) {
return new OwnedAuthorizationRequirement();
}
if (AffiliatedResource.class.equals(type)) {
return new AffiliatedAuthorizationRequirement(matchAll, permissions);
}
throw new IllegalArgumentException("resource");
}
};

View File

@ -2,6 +2,7 @@ package gr.cite.annotation.web.controllers;
import com.fasterxml.jackson.core.JsonProcessingException;
import gr.cite.annotation.audit.AuditableAction;
import gr.cite.annotation.authorization.AuthorizationFlags;
import gr.cite.annotation.data.AnnotationEntity;
import gr.cite.annotation.model.Annotation;
import gr.cite.annotation.model.builder.AnnotationBuilder;
@ -71,9 +72,9 @@ public class AnnotationController {
this.censorFactory.censor(AnnotationCensor.class).censor(lookup.getProject(), null);
AnnotationQuery query = lookup.enrich(this.queryFactory);
AnnotationQuery query = lookup.enrich(this.queryFactory).authorize(AuthorizationFlags.OwnerOrPermissionAssociated);
List<AnnotationEntity> data = query.collect();
List<Annotation> models = this.builderFactory.builder(AnnotationBuilder.class).build(lookup.getProject(), data);
List<Annotation> models = this.builderFactory.builder(AnnotationBuilder.class).authorize(AuthorizationFlags.OwnerOrPermissionAssociated).build(lookup.getProject(), data);
long count = (lookup.getMetadata() != null && lookup.getMetadata().getCountAll()) ? query.count() : models.size();
this.auditService.track(AuditableAction.Annotation_Query, "lookup", lookup);
@ -87,8 +88,8 @@ public class AnnotationController {
this.censorFactory.censor(AnnotationCensor.class).censor(fieldSet, null);
AnnotationQuery query = this.queryFactory.query(AnnotationQuery.class).ids(id);
Annotation model = this.builderFactory.builder(AnnotationBuilder.class).build(fieldSet, query.firstAs(fieldSet));
AnnotationQuery query = this.queryFactory.query(AnnotationQuery.class).authorize(AuthorizationFlags.OwnerOrPermissionAssociated).ids(id);
Annotation model = this.builderFactory.builder(AnnotationBuilder.class).authorize(AuthorizationFlags.OwnerOrPermissionAssociated).build(fieldSet, query.firstAs(fieldSet));
if (model == null)
throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{id, Annotation.class.getSimpleName()}, LocaleContextHolder.getLocale()));

View File

@ -67,7 +67,7 @@ public class TenantConfigurationController {
TenantConfigurationQuery query = lookup.enrich(this.queryFactory);
List<TenantConfigurationEntity> data = query.collectAs(lookup.getProject());
List<TenantConfiguration> models = this.builderFactory.builder(TenantConfigurationBuilder.class).authorize(AuthorizationFlags.OwnerOrPermission).build(lookup.getProject(), data);
List<TenantConfiguration> models = this.builderFactory.builder(TenantConfigurationBuilder.class).authorize(AuthorizationFlags.OwnerOrPermissionAssociated).build(lookup.getProject(), data);
long count = (lookup.getMetadata() != null && lookup.getMetadata().getCountAll()) ? query.count() : models.size();
this.auditService.track(AuditableAction.Tenant_Configuration_Query, "lookup", lookup);
@ -83,7 +83,7 @@ public class TenantConfigurationController {
this.censorFactory.censor(TenantConfigurationCensor.class).censor(fieldSet);
TenantConfigurationQuery query = this.queryFactory.query(TenantConfigurationQuery.class).ids(id);
TenantConfiguration model = this.builderFactory.builder(TenantConfigurationBuilder.class).authorize(AuthorizationFlags.OwnerOrPermission).build(fieldSet, query.firstAs(fieldSet));
TenantConfiguration model = this.builderFactory.builder(TenantConfigurationBuilder.class).authorize(AuthorizationFlags.OwnerOrPermissionAssociated).build(fieldSet, query.firstAs(fieldSet));
if (model == null)
throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{id, TenantConfiguration.class.getSimpleName()}, LocaleContextHolder.getLocale()));

View File

@ -32,6 +32,12 @@ cache:
maximumSize: 500
enableRecordStats: false
expireAfterWriteSeconds: 320
- names: [ "affiliation" ]
allowNullValues: true
initialCapacity: 100
maximumSize: 5000
enableRecordStats: false
expireAfterWriteSeconds: 20
mapCaches:
apiKey:
name: apikey
@ -48,3 +54,6 @@ cache:
userAllowedTenant:
name: userAccessTenant
keyPattern: user_access_tenant_$user_id$_$tenant_id$:v0
affiliation:
name: affiliation
keyPattern: affiliation_$entity$_$user$_$type$:v0

View File

@ -1,7 +1,14 @@
permissions:
extendedClaims: [ ]
policies:
DeferredAffiliation:
roles:
- Admin
- User
- Manager
- DescriptionTemplateEditor
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
# Tenants
BrowseTenant:
roles:
@ -49,30 +56,18 @@ permissions:
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
# UserContactInfo
BrowseUserContactInfo:
roles:
- Admin
clients: [ ]
allowAnonymous: true
allowAuthenticated: false
EditUserContactInfo:
roles:
- Admin
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
DeleteUserContactInfo:
roles:
- Admin
claims: [ ]
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
#Annotation
BrowseAnnotation:
roles:
- Admin
entityAffiliated: true
clients: [ ]
allowAnonymous: true
allowAuthenticated: false
NewAnnotation:
roles:
- Admin
entityAffiliated: true
clients: [ ]
allowAnonymous: true
allowAuthenticated: false
@ -85,6 +80,7 @@ permissions:
DeleteAnnotation:
roles:
- Admin
entityAffiliated: false
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
@ -101,5 +97,3 @@ permissions:
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
# ViewPage Permissions

View File

@ -45,7 +45,8 @@ queue:
tenant-touch-topic: tenant.touch
user-removal-topic: user.remove
user-touch-topic: user.touch
annotation-entity-touch-topic: annotation.entity.touch
annotation-entities-touch-topic: annotation.entities.touch
annotation-entities-removal-topic: annotation.entities.remove
rabbitmq:
enable: false
interval-seconds: 30

View File

@ -62,7 +62,7 @@
<dependency>
<groupId>gr.cite</groupId>
<artifactId>oidc-authn</artifactId>
<version>1.0.0</version>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>gr.cite</groupId>
@ -72,7 +72,7 @@
<dependency>
<groupId>gr.cite</groupId>
<artifactId>oidc-authz</artifactId>
<version>1.0.0</version>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>gr.cite</groupId>
@ -82,7 +82,7 @@
<dependency>
<groupId>gr.cite</groupId>
<artifactId>validation</artifactId>
<version>3.0.2</version>
<version>3.0.3</version>
</dependency>
<dependency>

View File

@ -0,0 +1,40 @@
package gr.cite.annotation.authorization;
import gr.cite.commons.web.authz.policy.AuthorizationRequirement;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class AffiliatedAuthorizationRequirement implements AuthorizationRequirement {
private final Set<String> requiredPermissions;
private final boolean matchAll;
public AffiliatedAuthorizationRequirement(Set<String> requiredPermissions) {
this(false, requiredPermissions);
}
public AffiliatedAuthorizationRequirement(String... requiredPermissions) {
this(false, requiredPermissions);
}
public AffiliatedAuthorizationRequirement(boolean matchAll, Set<String> requiredPermissions) {
this.matchAll = matchAll;
this.requiredPermissions = requiredPermissions;
}
public AffiliatedAuthorizationRequirement(boolean matchAll, String... requiredPermissions) {
this.requiredPermissions = new HashSet<>();
this.matchAll = matchAll;
this.requiredPermissions.addAll(Arrays.stream(requiredPermissions).distinct().toList());
}
public Set<String> getRequiredPermissions() {
return requiredPermissions;
}
public boolean getMatchAll() {
return matchAll;
}
}

View File

@ -0,0 +1,22 @@
package gr.cite.annotation.authorization;
import gr.cite.commons.web.authz.policy.AuthorizationResource;
public class AffiliatedResource extends AuthorizationResource {
private Boolean isAffiliated;
public AffiliatedResource() {
}
public AffiliatedResource(Boolean isAffiliated) {
this.isAffiliated = isAffiliated;
}
public Boolean getAffiliated() {
return isAffiliated;
}
public void setAffiliated(Boolean affiliated) {
isAffiliated = affiliated;
}
}

View File

@ -3,6 +3,6 @@ package gr.cite.annotation.authorization;
import java.util.EnumSet;
public enum AuthorizationFlags {
None, Permission, Owner;
public static final EnumSet<AuthorizationFlags> OwnerOrPermission = EnumSet.of(Owner, Permission);
None, Permission, Associated, Owner;
public static final EnumSet<AuthorizationFlags> OwnerOrPermissionAssociated = EnumSet.of(Owner, Permission, Associated);
}

View File

@ -1,15 +1,12 @@
package gr.cite.annotation.authorization;
public final class Permission {
public static String DeferredAffiliation = "DeferredAffiliation";
//User
public static String BrowseUser = "BrowseUser";
public static String EditUser = "EditUser";
public static String DeleteUser = "DeleteUser";
//UserContactInfo
public static String BrowseUserContactInfo = "BrowseUserContactInfo";
public static String EditUserContactInfo = "EditUserContactInfo";
public static String DeleteUserContactInfo = "DeleteUserContactInfo";
//Tenant
public static String BrowseTenant = "BrowseTenant";
@ -19,13 +16,12 @@ public final class Permission {
//Annotation
public static final String BrowseAnnotation = "BrowseAnnotation";
public static String NewAnnotation = "NewAnnotation";
public static String EditAnnotation = "EditAnnotation";
public static String DeleteAnnotation = "DeleteAnnotation";
public static final String BrowseTenantConfiguration = "BrowseTenantConfiguration";
public static final String EditTenantConfiguration = "EditTenantConfiguration";
// UI Pages
public static String ViewTenantConfigurationPage = "ViewTenantConfigurationPage";
}

View File

@ -0,0 +1,39 @@
package gr.cite.annotation.authorization;
import gr.cite.annotation.convention.ConventionService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
@Service
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public class PermissionNameProvider {
private static final Logger logger = LoggerFactory.getLogger(PermissionNameProvider.class);
private final List<String> permissions;
public PermissionNameProvider(ConventionService conventionService) {
this.permissions = new ArrayList<>();
Class<Permission> clazz = Permission.class;
for (Field f : clazz.getDeclaredFields()) {
if (Modifier.isStatic(f.getModifiers())) {
try {
Object value = f.get(null);
if (value != null && !conventionService.isNullOrEmpty((String)value)) this.permissions.add((String)value);
} catch (Exception e) {
logger.error("Can not load permission " + f.getName() + " " + e.getMessage());
}
}
}
}
public List<String> getPermissions() {
return permissions;
}
}

View File

@ -0,0 +1,10 @@
package gr.cite.annotation.authorization.authorizationcontentresolver;
import gr.cite.tools.cache.CacheOptions;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "cache.affiliation")
public class AffiliationCacheOptions extends CacheOptions {
}

View File

@ -0,0 +1,95 @@
package gr.cite.annotation.authorization.authorizationcontentresolver;
import gr.cite.annotation.authorization.AffiliatedResource;
import gr.cite.annotation.convention.ConventionService;
import gr.cite.tools.cache.CacheService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Locale;
import java.util.UUID;
@Service
public class AffiliationCacheService extends CacheService<AffiliationCacheService.AffiliationCacheValue> {
public static class AffiliationCacheValue {
public AffiliationCacheValue() {
}
public AffiliationCacheValue(UUID userId, UUID entityId, String entityType, AffiliatedResource affiliatedResource) {
this.userId = userId;
this.entityId = entityId;
this.entityType = entityType;
this.affiliatedResource = affiliatedResource;
}
private UUID userId;
private UUID entityId;
private String entityType;
private AffiliatedResource affiliatedResource;
public UUID getUserId() {
return userId;
}
public void setUserId(UUID userId) {
this.userId = userId;
}
public UUID getEntityId() {
return entityId;
}
public void setEntityId(UUID entityId) {
this.entityId = entityId;
}
public String getEntityType() {
return entityType;
}
public void setEntityType(String entityType) {
this.entityType = entityType;
}
public AffiliatedResource getAffiliatedResource() {
return affiliatedResource;
}
public void setAffiliatedResource(AffiliatedResource affiliatedResource) {
this.affiliatedResource = affiliatedResource;
}
}
private final ConventionService conventionService;
@Autowired
public AffiliationCacheService(AffiliationCacheOptions options, ConventionService conventionService) {
super(options);
this.conventionService = conventionService;
}
@Override
protected Class<AffiliationCacheValue> valueClass() {
return AffiliationCacheValue.class;
}
@Override
public String keyOf(AffiliationCacheValue value) {
return this.buildKey(value.getUserId(), value.getEntityId(), value.getEntityType());
}
public String buildKey(UUID userId, UUID entityId, String entityType) {
if (userId == null) throw new IllegalArgumentException("userId id is required");
if (entityId == null) throw new IllegalArgumentException("entityId id is required");
if (this.conventionService.isNullOrEmpty(entityType)) throw new IllegalArgumentException("entityType id is required");
HashMap<String, String> keyParts = new HashMap<>();
keyParts.put("$user$", userId.toString().replace("-", "").toLowerCase(Locale.ROOT));
keyParts.put("$entity$", entityId.toString().replace("-", "").toLowerCase(Locale.ROOT));
keyParts.put("$type$", entityType);
return this.generateKey(keyParts);
}
}

View File

@ -0,0 +1,20 @@
package gr.cite.annotation.authorization.authorizationcontentresolver;
import gr.cite.annotation.authorization.AffiliatedResource;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public interface AuthorizationContentResolver {
AffiliatedResource entityAffiliation(UUID id);
Map<UUID, AffiliatedResource> entitiesAffiliation(List<UUID> ids);
List<String> getPermissionNames();
AffiliatedResource annotationAffiliation(UUID id);
Map<UUID, AffiliatedResource> annotationsAffiliation(List<UUID> ids);
}

View File

@ -0,0 +1,118 @@
package gr.cite.annotation.authorization.authorizationcontentresolver;
import gr.cite.annotation.authorization.AffiliatedResource;
import gr.cite.annotation.authorization.PermissionNameProvider;
import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.common.scope.user.UserScope;
import gr.cite.annotation.data.AnnotationEntity;
import gr.cite.annotation.data.EntityUserEntity;
import gr.cite.annotation.model.Annotation;
import gr.cite.annotation.model.EntityUser;
import gr.cite.annotation.query.AnnotationQuery;
import gr.cite.annotation.query.EntityUserQuery;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.fieldset.BaseFieldSet;
import org.springframework.stereotype.Service;
import org.springframework.web.context.annotation.RequestScope;
import java.util.*;
import java.util.stream.Collectors;
@Service
@RequestScope
public class AuthorizationContentResolverImpl implements AuthorizationContentResolver {
private final QueryFactory queryFactory;
private final UserScope userScope;
private final AffiliationCacheService affiliationCacheService;
private final PermissionNameProvider permissionNameProvider;
public AuthorizationContentResolverImpl(QueryFactory queryFactory, UserScope userScope, AffiliationCacheService affiliationCacheService, PermissionNameProvider permissionNameProvider) {
this.queryFactory = queryFactory;
this.userScope = userScope;
this.affiliationCacheService = affiliationCacheService;
this.permissionNameProvider = permissionNameProvider;
}
@Override
public AffiliatedResource entityAffiliation(UUID id) {
return this.entitiesAffiliation(List.of(id)).getOrDefault(id, new AffiliatedResource());
}
@Override
public Map<UUID, AffiliatedResource> entitiesAffiliation(List<UUID> ids){
UUID userId = this.userScope.getUserIdSafe();
Map<UUID, AffiliatedResource> affiliatedResources = new HashMap<>();
for (UUID id : ids){
affiliatedResources.put(id, new AffiliatedResource());
}
if (userId == null || !userScope.isSet()) return affiliatedResources;
List<UUID> idsToResolve = this.getAffiliatedFromCache(ids, userId, affiliatedResources, AnnotationEntity._entityId);
if (idsToResolve.isEmpty()) return affiliatedResources;
List<EntityUserEntity> entityUsers = this.queryFactory.query(EntityUserQuery.class).entityIds(ids).userIds(userId).isActive(IsActive.Active).collectAs(new BaseFieldSet().ensure(EntityUser._id).ensure(EntityUser._entityId));
for (UUID entityId : entityUsers.stream().map(EntityUserEntity::getEntityId).distinct().toList()){
affiliatedResources.get(entityId).setAffiliated(true);
}
this.ensureAffiliatedInCache(idsToResolve, userId, affiliatedResources, AnnotationEntity._entityId);
return affiliatedResources;
}
@Override
public List<String> getPermissionNames() {
return permissionNameProvider.getPermissions();
}
@Override
public AffiliatedResource annotationAffiliation(UUID id) {
return this.annotationsAffiliation(List.of(id)).getOrDefault(id, new AffiliatedResource());
}
@Override
public Map<UUID, AffiliatedResource> annotationsAffiliation(List<UUID> ids){
UUID userId = this.userScope.getUserIdSafe();
Map<UUID, AffiliatedResource> affiliatedResources = new HashMap<>();
for (UUID id : ids){
affiliatedResources.put(id, new AffiliatedResource());
}
if (userId == null || !userScope.isSet()) return affiliatedResources;
List<UUID> idsToResolve = this.getAffiliatedFromCache(ids, userId, affiliatedResources, AnnotationEntity.class.getSimpleName());
if (idsToResolve.isEmpty()) return affiliatedResources;
List<AnnotationEntity> annotationEntities = this.queryFactory.query(AnnotationQuery.class).ids(ids).collectAs(new BaseFieldSet().ensure(Annotation._id).ensure(Annotation._entityId).ensure(Annotation._id));
List<EntityUserEntity> entityUsers = this.queryFactory.query(EntityUserQuery.class).entityIds(annotationEntities.stream().map(AnnotationEntity::getEntityId).distinct().toList()).userIds(userId).isActive(IsActive.Active).collectAs(new BaseFieldSet().ensure(EntityUser._id).ensure(EntityUser._entityId));
Map<UUID, List<EntityUserEntity>> dmpUsersMap = entityUsers.stream().collect(Collectors.groupingBy(EntityUserEntity::getEntityId));
for (AnnotationEntity annotation : annotationEntities){
List<EntityUserEntity> dmpDescriptionUsers = dmpUsersMap.getOrDefault(annotation.getEntityId(), new ArrayList<>());
if (!dmpDescriptionUsers.isEmpty()) {
affiliatedResources.get(annotation.getId()).setAffiliated(true);
}
}
this.ensureAffiliatedInCache(idsToResolve, userId, affiliatedResources, AnnotationEntity.class.getSimpleName());
return affiliatedResources;
}
private List<UUID> getAffiliatedFromCache(List<UUID> ids, UUID userId, Map<UUID, AffiliatedResource> affiliatedResources, String entityType){
List<UUID> idsToResolve = new ArrayList<>();
for (UUID id : ids){
AffiliationCacheService.AffiliationCacheValue cacheValue = this.affiliationCacheService.lookup(this.affiliationCacheService.buildKey(userId, id, entityType));
if (cacheValue != null) affiliatedResources.put(id, cacheValue.getAffiliatedResource());
else idsToResolve.add(id);
}
return idsToResolve;
}
private void ensureAffiliatedInCache(List<UUID> idsToResolve, UUID userId, Map<UUID, AffiliatedResource> affiliatedResources, String entityType){
for (UUID id : idsToResolve){
AffiliatedResource affiliatedResource = affiliatedResources.getOrDefault(id, null);
if (affiliatedResource != null) {
AffiliationCacheService.AffiliationCacheValue cacheValue = new AffiliationCacheService.AffiliationCacheValue(userId, id, entityType, affiliatedResource);
this.affiliationCacheService.put(cacheValue);
}
}
}
}

View File

@ -2,7 +2,6 @@ package gr.cite.annotation.common.validation;
import gr.cite.annotation.convention.ConventionService;
import gr.cite.annotation.errorcode.ErrorThesaurusProperties;
import gr.cite.annotation.integrationevent.inbox.annotationentitytouch.AnnotationEntityTouchedIntegrationEvent;
import gr.cite.tools.validation.specification.Specification;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.MessageSource;

View File

@ -2,6 +2,7 @@ package gr.cite.annotation.data;
import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.data.conventers.IsActiveConverter;
import gr.cite.annotation.data.conventers.QueueInboxStatusConverter;
import gr.cite.annotation.data.types.JsonSQLType;
import gr.cite.queueinbox.entity.QueueInbox;
import gr.cite.queueinbox.entity.QueueInboxStatus;
@ -21,17 +22,17 @@ public class QueueInboxEntity implements QueueInbox {
public static final String _id = "id";
@Column(name = "\"queue\"", nullable = false, length = 50)
@Column(name = "\"queue\"", nullable = false, length = 200)
private String queue;
public static final String _queue = "queue";
@Column(name = "\"exchange\"", nullable = false, length = 50)
@Column(name = "\"exchange\"", nullable = false, length = 200)
private String exchange;
public static final String _exchange = "exchange";
@Column(name = "\"route\"", nullable = false, length = 50)
@Column(name = "\"route\"", nullable = false, length = 200)
private String route;
public static final String _route = "route";
@ -62,11 +63,11 @@ public class QueueInboxEntity implements QueueInbox {
public static final String _tenantId = "tenantId";
@Column(name = "\"status\"", length = 50, nullable = false)
@Enumerated(EnumType.STRING)
@Column(name = "\"status\"", nullable = false)
@Convert(converter = QueueInboxStatusConverter.class)
private QueueInboxStatus status;
public final static String _status = "status";
public static final String _status = "status";
@Column(name = "\"created_at\"", nullable = false)
private Instant createdAt;
@ -79,7 +80,7 @@ public class QueueInboxEntity implements QueueInbox {
public static final String _updatedAt = "updatedAt";
@Column(name = "\"is_active\"", length = 20, nullable = false)
@Column(name = "\"is_active\"", nullable = false)
@Convert(converter = IsActiveConverter.class)
private IsActive isActive;

View File

@ -2,6 +2,7 @@ package gr.cite.annotation.data;
import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.data.conventers.IsActiveConverter;
import gr.cite.annotation.data.conventers.QueueOutboxNotifyStatusConverter;
import gr.cite.queueoutbox.entity.QueueOutbox;
import gr.cite.queueoutbox.entity.QueueOutboxNotifyStatus;
import jakarta.persistence.*;
@ -19,12 +20,12 @@ public class QueueOutboxEntity implements QueueOutbox {
public static final String _id = "id";
@Column(name = "\"exchange\"", nullable = false, length = 50)
@Column(name = "\"exchange\"", nullable = false, length = 200)
private String exchange;
public static final String _exchange = "exchange";
@Column(name = "\"route\"", length = 50)
@Column(name = "\"route\"", length = 200)
private String route;
public static final String _route = "route";
@ -39,11 +40,10 @@ public class QueueOutboxEntity implements QueueOutbox {
public static final String _message = "message";
@Column(name = "\"notify_status\"", length = 20, nullable = false)
@Enumerated(EnumType.STRING)
@Column(name = "\"notify_status\"", nullable = false)
@Convert(converter = QueueOutboxNotifyStatusConverter.class)
private QueueOutboxNotifyStatus notifyStatus;
public static final String _notifyStatus = "notifyStatus";
public final static String _notifyStatus = "notifyStatus";
@Column(name = "\"retry_count\"", nullable = false)
private Integer retryCount;

View File

@ -0,0 +1,19 @@
package gr.cite.annotation.data.conventers;
import gr.cite.queueinbox.entity.QueueInboxStatus;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
@Converter
public class QueueInboxStatusConverter implements AttributeConverter<QueueInboxStatus, Short> {
@Override
public Short convertToDatabaseColumn(QueueInboxStatus value) {
if (value == null) throw new IllegalArgumentException("Value could not be null for: " + this.getClass().getSimpleName());
return value.getValue();
}
@Override
public QueueInboxStatus convertToEntityAttribute(Short dbData) {
return QueueInboxStatus.of(dbData);
}
}

View File

@ -0,0 +1,19 @@
package gr.cite.annotation.data.conventers;
import gr.cite.queueoutbox.entity.QueueOutboxNotifyStatus;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
@Converter
public class QueueOutboxNotifyStatusConverter implements AttributeConverter<QueueOutboxNotifyStatus, Short> {
@Override
public Short convertToDatabaseColumn(QueueOutboxNotifyStatus value) {
if (value == null) throw new IllegalArgumentException("Value could not be null for: " + this.getClass().getSimpleName());
return value.getValue();
}
@Override
public QueueOutboxNotifyStatus convertToEntityAttribute(Short dbData) {
return QueueOutboxNotifyStatus.of(dbData);
}
}

View File

@ -36,7 +36,8 @@ public class AppRabbitConfigurer extends RabbitConfigurer {
bindingItems.addAll(this.inboxProperties.getTenantTouchTopic());
bindingItems.addAll(this.inboxProperties.getUserRemovalTopic());
bindingItems.addAll(this.inboxProperties.getUserTouchTopic());
bindingItems.addAll(this.inboxProperties.getAnnotationEntityTouchTopic());
bindingItems.addAll(this.inboxProperties.getAnnotationEntitiesTouchTopic());
bindingItems.addAll(this.inboxProperties.getAnnotationEntitiesRemovalTopic());
return new InboxBindings(bindingItems);
}

View File

@ -17,21 +17,23 @@ public class InboxProperties {
private final List<String> userTouchTopic;
private final List<String> annotationEntityTouchTopic;
private final List<String> annotationEntitiesTouchTopic;
private final List<String> annotationEntitiesRemovalTopic;
public InboxProperties(
String exchange,
List<String> tenantRemovalTopic,
List<String> tenantTouchTopic,
List<String> userRemovalTopic,
List<String> userTouchTopic,
List<String> annotationEntityTouchTopic) {
String exchange,
List<String> tenantRemovalTopic,
List<String> tenantTouchTopic,
List<String> userRemovalTopic,
List<String> userTouchTopic,
List<String> annotationEntitiesTouchTopic, List<String> annotationEntitiesRemovalTopic) {
this.exchange = exchange;
this.tenantRemovalTopic = tenantRemovalTopic;
this.tenantTouchTopic = tenantTouchTopic;
this.userRemovalTopic = userRemovalTopic;
this.userTouchTopic = userTouchTopic;
this.annotationEntityTouchTopic = annotationEntityTouchTopic;
this.annotationEntitiesTouchTopic = annotationEntitiesTouchTopic;
this.annotationEntitiesRemovalTopic = annotationEntitiesRemovalTopic;
}
public List<String> getTenantRemovalTopic() {
@ -50,8 +52,12 @@ public class InboxProperties {
return userTouchTopic;
}
public List<String> getAnnotationEntityTouchTopic() {
return annotationEntityTouchTopic;
public List<String> getAnnotationEntitiesTouchTopic() {
return annotationEntitiesTouchTopic;
}
public List<String> getAnnotationEntitiesRemovalTopic() {
return annotationEntitiesRemovalTopic;
}
public String getExchange() {

View File

@ -5,7 +5,8 @@ import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.common.scope.fake.FakeRequestScope;
import gr.cite.annotation.data.QueueInboxEntity;
import gr.cite.annotation.integrationevent.TrackedEvent;
import gr.cite.annotation.integrationevent.inbox.annotationentitytouch.AnnotationEntityTouchedIntegrationEventHandler;
import gr.cite.annotation.integrationevent.inbox.annotationentitiesremoval.AnnotationEntitiesRemovalIntegrationEventHandler;
import gr.cite.annotation.integrationevent.inbox.annotationentitiestouch.AnnotationEntitiesTouchedIntegrationEventHandler;
import gr.cite.annotation.integrationevent.inbox.tenantremoval.TenantRemovalIntegrationEventHandler;
import gr.cite.annotation.integrationevent.inbox.tenanttouch.TenantTouchedIntegrationEventHandler;
import gr.cite.annotation.integrationevent.inbox.userremoval.UserRemovalIntegrationEventHandler;
@ -332,8 +333,10 @@ public class InboxRepositoryImpl implements InboxRepository {
handler = this.applicationContext.getBean(UserRemovalIntegrationEventHandler.class);
else if (this.routingKeyMatched(routingKey, this.inboxProperties.getUserTouchTopic()))
handler = this.applicationContext.getBean(UserTouchedIntegrationEventHandler.class);
else if (this.routingKeyMatched(routingKey, this.inboxProperties.getAnnotationEntityTouchTopic()))
handler = this.applicationContext.getBean(AnnotationEntityTouchedIntegrationEventHandler.class);
else if (this.routingKeyMatched(routingKey, this.inboxProperties.getAnnotationEntitiesTouchTopic()))
handler = this.applicationContext.getBean(AnnotationEntitiesTouchedIntegrationEventHandler.class);
else if (this.routingKeyMatched(routingKey, this.inboxProperties.getAnnotationEntitiesRemovalTopic()))
handler = this.applicationContext.getBean(AnnotationEntitiesRemovalIntegrationEventHandler.class);
else {
logger.error("No handler found for message routing key '{}'. Discarding.", routingKey);
handler = null;

View File

@ -0,0 +1,70 @@
package gr.cite.annotation.integrationevent.inbox.annotationentitiesremoval;
import gr.cite.annotation.common.validation.BaseValidator;
import gr.cite.annotation.common.validation.UuidValidator;
import gr.cite.annotation.convention.ConventionService;
import gr.cite.annotation.errorcode.ErrorThesaurusProperties;
import gr.cite.annotation.integrationevent.TrackedEvent;
import gr.cite.tools.validation.ValidatorFactory;
import gr.cite.tools.validation.specification.Specification;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Scope;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
public class AnnotationEntitiesRemovalIntegrationEvent extends TrackedEvent {
private List<UUID> entityIds;
public static final String _entityIds = "entityIds";
public List<UUID> getEntityIds() {
return entityIds;
}
public void setEntityIds(List<UUID> entityIds) {
this.entityIds = entityIds;
}
@Component(AnnotationEntitiesRemovalIntegrationEventValidator.ValidatorName)
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public static class AnnotationEntitiesRemovalIntegrationEventValidator extends BaseValidator<AnnotationEntitiesRemovalIntegrationEvent> {
public static final String ValidatorName = "AnnotationEntitiesRemovalIntegrationEventValidator";
private final MessageSource messageSource;
private final ValidatorFactory validatorFactory;
protected AnnotationEntitiesRemovalIntegrationEventValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource, ValidatorFactory validatorFactory) {
super(conventionService, errors);
this.messageSource = messageSource;
this.validatorFactory = validatorFactory;
}
@Override
protected Class<AnnotationEntitiesRemovalIntegrationEvent> modelClass() {
return AnnotationEntitiesRemovalIntegrationEvent.class;
}
@Override
protected List<Specification> specifications(AnnotationEntitiesRemovalIntegrationEvent item) {
return Arrays.asList(
this.spec()
.must(() -> !this.isListNullOrEmpty(item.getEntityIds()))
.failOn(AnnotationEntitiesRemovalIntegrationEvent._entityIds).failWith(messageSource.getMessage("Validation_Required", new Object[]{AnnotationEntitiesRemovalIntegrationEvent._entityIds}, LocaleContextHolder.getLocale())),
this.navSpec()
.iff(() -> !this.isListNullOrEmpty(item.getEntityIds()))
.on(AnnotationEntitiesRemovalIntegrationEvent._entityIds)
.over(item.getEntityIds())
.using((i) -> this.validatorFactory.validator(UuidValidator.class))
);
}
}
}

View File

@ -0,0 +1,7 @@
package gr.cite.annotation.integrationevent.inbox.annotationentitiesremoval;
import gr.cite.annotation.integrationevent.inbox.IntegrationEventHandler;
public interface AnnotationEntitiesRemovalIntegrationEventHandler extends IntegrationEventHandler {
}

View File

@ -1,4 +1,4 @@
package gr.cite.annotation.integrationevent.inbox.annotationentitytouch;
package gr.cite.annotation.integrationevent.inbox.annotationentitiesremoval;
import gr.cite.annotation.audit.AuditableAction;
import gr.cite.annotation.common.JsonHandlingService;
@ -11,6 +11,7 @@ import gr.cite.annotation.integrationevent.inbox.IntegrationEventProperties;
import gr.cite.annotation.query.EntityUserQuery;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
import gr.cite.tools.auditing.AuditService;
import gr.cite.tools.data.deleter.DeleterFactory;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.validation.ValidatorFactory;
@ -24,17 +25,13 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.AbstractMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.*;
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class AnnotationEntityTouchedIntegrationEventHandlerImpl implements AnnotationEntityTouchedIntegrationEventHandler {
public class AnnotationEntitiesRemovalIntegrationEventHandlerImpl implements AnnotationEntitiesRemovalIntegrationEventHandler {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(AnnotationEntityTouchedIntegrationEventHandlerImpl.class));
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(AnnotationEntitiesRemovalIntegrationEventHandlerImpl.class));
private final JsonHandlingService jsonHandlingService;
@ -44,7 +41,7 @@ public class AnnotationEntityTouchedIntegrationEventHandlerImpl implements Annot
private final QueryFactory queryFactory;
public AnnotationEntityTouchedIntegrationEventHandlerImpl(JsonHandlingService jsonHandlingService, ValidatorFactory validatorFactory, ApplicationContext applicationContext, QueryFactory queryFactory) {
public AnnotationEntitiesRemovalIntegrationEventHandlerImpl(JsonHandlingService jsonHandlingService, ValidatorFactory validatorFactory, ApplicationContext applicationContext, QueryFactory queryFactory) {
this.jsonHandlingService = jsonHandlingService;
this.validatorFactory = validatorFactory;
this.applicationContext = applicationContext;
@ -53,66 +50,41 @@ public class AnnotationEntityTouchedIntegrationEventHandlerImpl implements Annot
@Override
public EventProcessingStatus handle(IntegrationEventProperties properties, String message) {
AnnotationEntityTouchedIntegrationEvent event = this.jsonHandlingService.fromJsonSafe(AnnotationEntityTouchedIntegrationEvent.class, message);
AnnotationEntitiesRemovalIntegrationEvent event = this.jsonHandlingService.fromJsonSafe(AnnotationEntitiesRemovalIntegrationEvent.class, message);
if (event == null)
return EventProcessingStatus.Error;
logger.debug("Handling {}", AnnotationEntityTouchedIntegrationEvent.class.getSimpleName());
logger.debug("Handling {}", AnnotationEntitiesRemovalIntegrationEvent.class.getSimpleName());
this.validatorFactory.validator(AnnotationEntityTouchedIntegrationEvent.AnnotationEntityTouchedIntegrationEventValidator.class).validateForce(event);
this.validatorFactory.validator(AnnotationEntitiesRemovalIntegrationEvent.AnnotationEntitiesRemovalIntegrationEventValidator.class).validateForce(event);
EntityManager entityManager = null;
EntityTransaction transaction = null;
try (FakeRequestScope ignored = new FakeRequestScope()) {
try {
// QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class);
// TenantScope scope = this.applicationContext.getBean(TenantScope.class);
// if (scope.isMultitenant() && event.getTenant() != null) {
// TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(event.getTenant()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code));
// if (tenant == null) {
// logger.error("missing tenant from event message");
// return EventProcessingStatus.Error;
// }
// scope.setTenant(event.getTenant(), tenant.getCode());
// } else if (scope.isMultitenant()) {
// logger.error("missing tenant from event message");
// return EventProcessingStatus.Error;
// }
//
// ValidationService validator = this.applicationContext.getBean(ValidationService.class);
// validator.validateForce(model);
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class);
currentPrincipalResolver.push(InboxPrincipal.build(properties));
EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class);
entityManager = entityManagerFactory.createEntityManager();
DeleterFactory deleterFactory = this.applicationContext.getBean(DeleterFactory.class);
transaction = entityManager.getTransaction();
transaction.begin();
try {
EntityUserQuery entityUserQuery = this.queryFactory.query(EntityUserQuery.class);
List<EntityUserEntity> associatedUsersEntities = entityUserQuery
.entityIds(event.getEntityId())
List<EntityUserEntity> items = entityUserQuery
.entityIds(event.getEntityIds())
.isActive(IsActive.Active)
.collect();
List<UUID> associatedUsers = associatedUsersEntities.stream()
.map(EntityUserEntity::getUserId)
.toList();
for (UUID user : event.getUserIds()) {
if (!associatedUsers.contains(user)) {
EntityUserEntity entityUserEntity = new EntityUserEntity();
entityUserEntity.setId(UUID.randomUUID());
entityUserEntity.setEntityId(event.getEntityId());
entityUserEntity.setUserId(user);
entityUserEntity.setCreatedAt(Instant.now());
entityUserEntity.setUpdatedAt(Instant.now());
entityUserEntity.setIsActive(IsActive.Active);
entityManager.persist(entityUserEntity);
}
}
deleterFactory.deleter(gr.cite.EntityUser.model.deleter.EntityUserDeleter.class).delete(items);
entityManager.flush();
AuditService auditService = this.applicationContext.getBean(AuditService.class);

View File

@ -0,0 +1,138 @@
package gr.cite.annotation.integrationevent.inbox.annotationentitiestouch;
import gr.cite.annotation.common.validation.BaseValidator;
import gr.cite.annotation.common.validation.UuidValidator;
import gr.cite.annotation.convention.ConventionService;
import gr.cite.annotation.errorcode.ErrorThesaurusProperties;
import gr.cite.annotation.integrationevent.TrackedEvent;
import gr.cite.tools.validation.ValidatorFactory;
import gr.cite.tools.validation.specification.Specification;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Scope;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
public class AnnotationEntitiesTouchedIntegrationEvent extends TrackedEvent {
private List<AnnotationEntityTouchedIntegrationEvent> events;
public static final String _events = "events";
public List<AnnotationEntityTouchedIntegrationEvent> getEvents() {
return events;
}
public void setEvents(List<AnnotationEntityTouchedIntegrationEvent> events) {
this.events = events;
}
@Component(AnnotationEntitiesTouchedIntegrationEventValidator.ValidatorName)
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public static class AnnotationEntitiesTouchedIntegrationEventValidator extends BaseValidator<AnnotationEntitiesTouchedIntegrationEvent> {
public static final String ValidatorName = "AnnotationEntitiesTouchedIntegrationEventValidator";
private final MessageSource messageSource;
private final ValidatorFactory validatorFactory;
protected AnnotationEntitiesTouchedIntegrationEventValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource, ValidatorFactory validatorFactory) {
super(conventionService, errors);
this.messageSource = messageSource;
this.validatorFactory = validatorFactory;
}
@Override
protected Class<AnnotationEntitiesTouchedIntegrationEvent> modelClass() {
return AnnotationEntitiesTouchedIntegrationEvent.class;
}
@Override
protected List<Specification> specifications(AnnotationEntitiesTouchedIntegrationEvent item) {
return Arrays.asList(
this.spec()
.must(() -> !this.isListNullOrEmpty(item.getEvents()))
.failOn(AnnotationEntitiesTouchedIntegrationEvent._events).failWith(messageSource.getMessage("Validation_Required", new Object[]{AnnotationEntitiesTouchedIntegrationEvent._events}, LocaleContextHolder.getLocale())),
this.navSpec()
.iff(() -> !this.isListNullOrEmpty(item.getEvents()))
.on(AnnotationEntitiesTouchedIntegrationEvent._events)
.over(item.getEvents())
.using((i) -> this.validatorFactory.validator(AnnotationEntityTouchedIntegrationEvent.AnnotationEntityTouchedIntegrationEventValidator.class))
);
}
}
public static class AnnotationEntityTouchedIntegrationEvent {
private UUID entityId;
public static final String _entityId = "entityId";
private List<UUID> userIds;
public static final String _userIds = "userIds";
public UUID getEntityId() {
return entityId;
}
public void setEntityId(UUID entityId) {
this.entityId = entityId;
}
public List<UUID> getUserIds() {
return userIds;
}
public void setUserIds(List<UUID> userIds) {
this.userIds = userIds;
}
@Component(AnnotationEntityTouchedIntegrationEvent.AnnotationEntityTouchedIntegrationEventValidator.ValidatorName)
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public static class AnnotationEntityTouchedIntegrationEventValidator extends BaseValidator<AnnotationEntityTouchedIntegrationEvent> {
public static final String ValidatorName = "AnnotationEntityTouchedIntegrationEventValidator";
private final MessageSource messageSource;
private final ValidatorFactory validatorFactory;
protected AnnotationEntityTouchedIntegrationEventValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource, ValidatorFactory validatorFactory) {
super(conventionService, errors);
this.messageSource = messageSource;
this.validatorFactory = validatorFactory;
}
@Override
protected Class<AnnotationEntityTouchedIntegrationEvent> modelClass() {
return AnnotationEntityTouchedIntegrationEvent.class;
}
@Override
protected List<Specification> specifications(AnnotationEntityTouchedIntegrationEvent item) {
return Arrays.asList(
this.spec()
.iff(() -> !this.isNull(item.getEntityId()))
.must(() -> this.isValidGuid(item.getEntityId()))
.failOn(AnnotationEntityTouchedIntegrationEvent._entityId).failWith(messageSource.getMessage("Validation_Required", new Object[]{AnnotationEntityTouchedIntegrationEvent._entityId}, LocaleContextHolder.getLocale())),
this.spec()
.must(() -> !this.isListNullOrEmpty(item.getUserIds()))
.failOn(AnnotationEntityTouchedIntegrationEvent._userIds).failWith(messageSource.getMessage("Validation_Required", new Object[]{AnnotationEntityTouchedIntegrationEvent._userIds}, LocaleContextHolder.getLocale())),
this.navSpec()
.iff(() -> !this.isListNullOrEmpty(item.getUserIds()))
.on(AnnotationEntityTouchedIntegrationEvent._userIds)
.over(item.getUserIds())
.using((i) -> this.validatorFactory.validator(UuidValidator.class))
);
}
}
}
}

View File

@ -1,7 +1,7 @@
package gr.cite.annotation.integrationevent.inbox.annotationentitytouch;
package gr.cite.annotation.integrationevent.inbox.annotationentitiestouch;
import gr.cite.annotation.integrationevent.inbox.IntegrationEventHandler;
public interface AnnotationEntityTouchedIntegrationEventHandler extends IntegrationEventHandler {
public interface AnnotationEntitiesTouchedIntegrationEventHandler extends IntegrationEventHandler {
}

View File

@ -0,0 +1,141 @@
package gr.cite.annotation.integrationevent.inbox.annotationentitiestouch;
import gr.cite.annotation.audit.AuditableAction;
import gr.cite.annotation.common.JsonHandlingService;
import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.common.scope.fake.FakeRequestScope;
import gr.cite.annotation.data.EntityUserEntity;
import gr.cite.annotation.integrationevent.inbox.EventProcessingStatus;
import gr.cite.annotation.integrationevent.inbox.InboxPrincipal;
import gr.cite.annotation.integrationevent.inbox.IntegrationEventProperties;
import gr.cite.annotation.query.EntityUserQuery;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
import gr.cite.tools.auditing.AuditService;
import gr.cite.tools.data.deleter.DeleterFactory;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.validation.ValidatorFactory;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.EntityTransaction;
import jakarta.persistence.OptimisticLockException;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class AnnotationEntitiesTouchedIntegrationEventHandlerImpl implements AnnotationEntitiesTouchedIntegrationEventHandler {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(AnnotationEntitiesTouchedIntegrationEventHandlerImpl.class));
private final JsonHandlingService jsonHandlingService;
private final ValidatorFactory validatorFactory;
private final ApplicationContext applicationContext;
private final QueryFactory queryFactory;
public AnnotationEntitiesTouchedIntegrationEventHandlerImpl(JsonHandlingService jsonHandlingService, ValidatorFactory validatorFactory, ApplicationContext applicationContext, QueryFactory queryFactory) {
this.jsonHandlingService = jsonHandlingService;
this.validatorFactory = validatorFactory;
this.applicationContext = applicationContext;
this.queryFactory = queryFactory;
}
@Override
public EventProcessingStatus handle(IntegrationEventProperties properties, String message) {
AnnotationEntitiesTouchedIntegrationEvent event = this.jsonHandlingService.fromJsonSafe(AnnotationEntitiesTouchedIntegrationEvent.class, message);
if (event == null)
return EventProcessingStatus.Error;
logger.debug("Handling {}", AnnotationEntitiesTouchedIntegrationEvent.class.getSimpleName());
this.validatorFactory.validator(AnnotationEntitiesTouchedIntegrationEvent.AnnotationEntitiesTouchedIntegrationEventValidator.class).validateForce(event);
EntityManager entityManager = null;
EntityTransaction transaction = null;
try (FakeRequestScope ignored = new FakeRequestScope()) {
try {
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class);
currentPrincipalResolver.push(InboxPrincipal.build(properties));
EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class);
entityManager = entityManagerFactory.createEntityManager();
DeleterFactory deleterFactory = this.applicationContext.getBean(DeleterFactory.class);
transaction = entityManager.getTransaction();
transaction.begin();
try {
for (AnnotationEntitiesTouchedIntegrationEvent.AnnotationEntityTouchedIntegrationEvent entityEvent : event.getEvents()) {
EntityUserQuery entityUserQuery = this.queryFactory.query(EntityUserQuery.class);
List<EntityUserEntity> items = entityUserQuery
.entityIds(entityEvent.getEntityId())
.isActive(IsActive.Active)
.collect();
List<UUID> updatedCreatedIds = new ArrayList<>();
for (UUID user : entityEvent.getUserIds()) {
EntityUserEntity data = items.stream().filter(x -> x.getUserId().equals(user)).findFirst().orElse(null);
if (data == null) {
data = new EntityUserEntity();
data.setId(UUID.randomUUID());
data.setEntityId(entityEvent.getEntityId());
data.setUserId(user);
data.setCreatedAt(Instant.now());
data.setUpdatedAt(Instant.now());
data.setIsActive(IsActive.Active);
entityManager.persist(data);
}
updatedCreatedIds.add(data.getId());
}
List<EntityUserEntity> toDelete = items.stream().filter(x -> updatedCreatedIds.stream().noneMatch(y -> y.equals(x.getId()))).collect(Collectors.toList());
deleterFactory.deleter(gr.cite.EntityUser.model.deleter.EntityUserDeleter.class).delete(toDelete);
entityManager.flush();
}
AuditService auditService = this.applicationContext.getBean(AuditService.class);
auditService.track(AuditableAction.User_Persist, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("model", event)
));
transaction.commit();
} catch (Exception e) {
transaction.rollback();
throw e;
} finally {
currentPrincipalResolver.pop();
}
} catch (OptimisticLockException ex) {
// we get this if/when someone else already modified the notifications. We want to essentially ignore this, and keep working
logger.debug("Concurrency exception getting queue outbox. Skipping: {} ", ex.getMessage());
if (transaction != null)
transaction.rollback();
} catch (Exception ex) {
logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
if (transaction != null)
transaction.rollback();
} finally {
if (entityManager != null)
entityManager.close();
}
} catch (Exception ex) {
logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
}
return EventProcessingStatus.Success;
}
}

View File

@ -1,86 +0,0 @@
package gr.cite.annotation.integrationevent.inbox.annotationentitytouch;
import gr.cite.annotation.common.validation.BaseValidator;
import gr.cite.annotation.common.validation.UuidValidator;
import gr.cite.annotation.convention.ConventionService;
import gr.cite.annotation.errorcode.ErrorThesaurusProperties;
import gr.cite.annotation.integrationevent.TrackedEvent;
import gr.cite.tools.validation.ValidatorFactory;
import gr.cite.tools.validation.specification.Specification;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Scope;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
public class AnnotationEntityTouchedIntegrationEvent extends TrackedEvent {
private UUID entityId;
public static final String _entityId = "entityId";
private List<UUID> userIds;
public static final String _userIds = "userIds";
public UUID getEntityId() {
return entityId;
}
public void setEntityId(UUID entityId) {
this.entityId = entityId;
}
public List<UUID> getUserIds() {
return userIds;
}
public void setUserIds(List<UUID> userIds) {
this.userIds = userIds;
}
@Component(AnnotationEntityTouchedIntegrationEvent.AnnotationEntityTouchedIntegrationEventValidator.ValidatorName)
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public static class AnnotationEntityTouchedIntegrationEventValidator extends BaseValidator<AnnotationEntityTouchedIntegrationEvent> {
public static final String ValidatorName = "AnnotationEntityTouchedIntegrationEventValidator";
private final MessageSource messageSource;
private final ValidatorFactory validatorFactory;
protected AnnotationEntityTouchedIntegrationEventValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource, ValidatorFactory validatorFactory) {
super(conventionService, errors);
this.messageSource = messageSource;
this.validatorFactory = validatorFactory;
}
@Override
protected Class<AnnotationEntityTouchedIntegrationEvent> modelClass() {
return AnnotationEntityTouchedIntegrationEvent.class;
}
@Override
protected List<Specification> specifications(AnnotationEntityTouchedIntegrationEvent item) {
return Arrays.asList(
this.spec()
.iff(() -> !this.isNull(item.getEntityId()))
.must(() -> this.isValidGuid(item.getEntityId()))
.failOn(AnnotationEntityTouchedIntegrationEvent._entityId).failWith(messageSource.getMessage("Validation_Required", new Object[]{AnnotationEntityTouchedIntegrationEvent._entityId}, LocaleContextHolder.getLocale())),
this.spec()
.must(() -> !this.isListNullOrEmpty(item.getUserIds()))
.failOn(AnnotationEntityTouchedIntegrationEvent._userIds).failWith(messageSource.getMessage("Validation_Required", new Object[]{AnnotationEntityTouchedIntegrationEvent._userIds}, LocaleContextHolder.getLocale())),
this.navSpec()
.iff(() -> !this.isListNullOrEmpty(item.getUserIds()))
.on(AnnotationEntityTouchedIntegrationEvent._userIds)
.over(item.getUserIds())
.using((i) -> this.validatorFactory.validator(UuidValidator.class))
);
}
}
}

View File

@ -7,6 +7,7 @@ import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import java.time.Instant;
import java.util.List;
import java.util.UUID;
public class Annotation {
@ -67,6 +68,13 @@ public class Annotation {
public static final String _isActive = "isActive";
private String hash;
public static final String _hash = "hash";
private List<String> authorizationFlags;
public static final String _authorizationFlags = "authorizationFlags";
public UUID getId() {
return id;
}
@ -179,4 +187,19 @@ public class Annotation {
this.isActive = isActive;
}
public String getHash() {
return hash;
}
public void setHash(String hash) {
this.hash = hash;
}
public List<String> getAuthorizationFlags() {
return authorizationFlags;
}
public void setAuthorizationFlags(List<String> authorizationFlags) {
this.authorizationFlags = authorizationFlags;
}
}

View File

@ -0,0 +1,94 @@
package gr.cite.annotation.model;
import gr.cite.annotation.common.enums.AnnotationProtectionType;
import gr.cite.annotation.common.enums.IsActive;
import java.time.Instant;
import java.util.UUID;
public class EntityUser {
private UUID id;
public static final String _id = "id";
private UUID entityId;
public static final String _entityId = "entityId";
private User user;
public static final String _user = "user";
private Instant createdAt;
public static final String _createdAt = "createdAt";
private Instant updatedAt;
public static final String _updatedAt = "updatedAt";
private IsActive isActive;
public static final String _isActive = "isActive";
private String hash;
public static final String _hash = "hash";
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public UUID getEntityId() {
return entityId;
}
public void setEntityId(UUID entityId) {
this.entityId = entityId;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Instant getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Instant createdAt) {
this.createdAt = createdAt;
}
public Instant getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Instant updatedAt) {
this.updatedAt = updatedAt;
}
public IsActive getIsActive() {
return isActive;
}
public void setIsActive(IsActive isActive) {
this.isActive = isActive;
}
public String getHash() {
return hash;
}
public void setHash(String hash) {
this.hash = hash;
}
}

View File

@ -1,12 +1,15 @@
package gr.cite.annotation.model.builder;
import gr.cite.annotation.authorization.AffiliatedResource;
import gr.cite.annotation.authorization.AuthorizationFlags;
import gr.cite.annotation.authorization.authorizationcontentresolver.AuthorizationContentResolver;
import gr.cite.annotation.convention.ConventionService;
import gr.cite.annotation.data.AnnotationEntity;
import gr.cite.annotation.model.Annotation;
import gr.cite.annotation.model.AnnotationAuthor;
import gr.cite.annotation.model.User;
import gr.cite.annotation.query.UserQuery;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.builder.BuilderFactory;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyApplicationException;
@ -29,13 +32,17 @@ public class AnnotationBuilder extends BaseBuilder<Annotation, AnnotationEntity>
private final QueryFactory queryFactory;
private final BuilderFactory builderFactory;
private final AuthorizationContentResolver authorizationContentResolver;
private final AuthorizationService authorizationService;
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
public AnnotationBuilder(ConventionService conventionService, QueryFactory queryFactory, BuilderFactory builderFactory) {
public AnnotationBuilder(ConventionService conventionService, QueryFactory queryFactory, BuilderFactory builderFactory, AuthorizationContentResolver authorizationContentResolver, AuthorizationService authorizationService) {
super(conventionService, new LoggerService(LoggerFactory.getLogger(AnnotationBuilder.class)));
this.queryFactory = queryFactory;
this.builderFactory = builderFactory;
this.authorizationContentResolver = authorizationContentResolver;
this.authorizationService = authorizationService;
}
public AnnotationBuilder authorize(EnumSet<AuthorizationFlags> values) {
@ -51,42 +58,32 @@ public class AnnotationBuilder extends BaseBuilder<Annotation, AnnotationEntity>
return new ArrayList<>();
List<Annotation> models = new ArrayList<>();
if (data == null) return models;
FieldSet authorFields = fields.extractPrefixed(this.asPrefix(Annotation._author));
Map<UUID, AnnotationAuthor> authorsMap = this.collectAuthors(authorFields, data);
Set<String> authorizationFlags = this.extractAuthorizationFlags(fields, Annotation._authorizationFlags, this.authorizationContentResolver.getPermissionNames());
Map<UUID, AffiliatedResource> affiliatedResourceMap = authorizationFlags == null || authorizationFlags.isEmpty() ? null : this.authorizationContentResolver.annotationsAffiliation(data.stream().map(AnnotationEntity::getId).toList());
if (data == null)
return models;
for (AnnotationEntity d : data) {
Annotation m = new Annotation();
if (fields.hasField(this.asIndexer(Annotation._id)))
m.setId(d.getId());
if (fields.hasField(this.asIndexer(Annotation._entityId)))
m.setEntityId(d.getEntityId());
if (fields.hasField(this.asIndexer(Annotation._entityType)))
m.setEntityType(d.getEntityType());
if (fields.hasField(this.asIndexer(Annotation._anchor)))
m.setAnchor(d.getAnchor());
if (fields.hasField(this.asIndexer(Annotation._payload)))
m.setPayload(d.getPayload());
if (fields.hasField(this.asIndexer(Annotation._subjectId)))
m.setSubjectId(d.getSubjectId());
if (authorsMap != null && authorsMap.containsKey(d.getSubjectId()))
m.setAuthor(authorsMap.get(d.getSubjectId()));
if (fields.hasField(this.asIndexer(Annotation._threadId)))
m.setThreadId(d.getThreadId());
if (fields.hasField(this.asIndexer(Annotation._parentId)))
m.setParentId(d.getParentId());
if (fields.hasField(this.asIndexer(Annotation._protectionType)))
m.setProtectionType(d.getProtectionType());
if (fields.hasField(this.asIndexer(Annotation._timeStamp)))
m.setTimeStamp(d.getTimeStamp());
if (fields.hasField(this.asIndexer(Annotation._createdAt)))
m.setCreatedAt(d.getCreatedAt());
if (fields.hasField(this.asIndexer(Annotation._updatedAt)))
m.setUpdatedAt(d.getUpdatedAt());
if (fields.hasField(this.asIndexer(Annotation._isActive)))
m.setIsActive(d.getIsActive());
if (fields.hasField(this.asIndexer(Annotation._id))) m.setId(d.getId());
if (fields.hasField(this.asIndexer(Annotation._entityId))) m.setEntityId(d.getEntityId());
if (fields.hasField(this.asIndexer(Annotation._entityType))) m.setEntityType(d.getEntityType());
if (fields.hasField(this.asIndexer(Annotation._anchor))) m.setAnchor(d.getAnchor());
if (fields.hasField(this.asIndexer(Annotation._payload))) m.setPayload(d.getPayload());
if (fields.hasField(this.asIndexer(Annotation._subjectId))) m.setSubjectId(d.getSubjectId());
if (fields.hasField(this.asIndexer(Annotation._threadId))) m.setThreadId(d.getThreadId());
if (fields.hasField(this.asIndexer(Annotation._parentId))) m.setParentId(d.getParentId());
if (fields.hasField(this.asIndexer(Annotation._protectionType))) m.setProtectionType(d.getProtectionType());
if (fields.hasField(this.asIndexer(Annotation._timeStamp))) m.setTimeStamp(d.getTimeStamp());
if (fields.hasField(this.asIndexer(Annotation._createdAt))) m.setCreatedAt(d.getCreatedAt());
if (fields.hasField(this.asIndexer(Annotation._updatedAt))) m.setUpdatedAt(d.getUpdatedAt());
if (fields.hasField(this.asIndexer(Annotation._isActive))) m.setIsActive(d.getIsActive());
if (fields.hasField(this.asIndexer(Annotation._hash))) m.setHash(this.hashValue(d.getUpdatedAt()));
if (!authorFields.isEmpty() && authorsMap != null && authorsMap.containsKey(d.getSubjectId())) m.setAuthor(authorsMap.get(d.getSubjectId()));
if (affiliatedResourceMap != null && !authorizationFlags.isEmpty()) m.setAuthorizationFlags(this.evaluateAuthorizationFlags(this.authorizationService, authorizationFlags, affiliatedResourceMap.getOrDefault(d.getId(), null)));
models.add(m);
}
this.logger.debug("build {} items", Optional.of(models).map(List::size).orElse(0));
@ -103,7 +100,7 @@ public class AnnotationBuilder extends BaseBuilder<Annotation, AnnotationEntity>
.map(AnnotationEntity::getSubjectId)
.distinct()
.collect(Collectors.toList());
UserQuery query = this.queryFactory.query(UserQuery.class).authorize(this.authorize).ids(userIds);
UserQuery query = this.queryFactory.query(UserQuery.class).ids(userIds);
Map<UUID, List<User>> users = this.builderFactory.builder(UserBuilder.class).authorize(this.authorize).asMasterKey(query, clone, User::getId);
users.forEach((key, val) -> {

View File

@ -1,6 +1,8 @@
package gr.cite.annotation.model.builder;
import gr.cite.annotation.authorization.AffiliatedResource;
import gr.cite.annotation.convention.ConventionService;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.builder.Builder;
import gr.cite.tools.data.query.QueryBase;
import gr.cite.tools.exception.MyApplicationException;
@ -92,4 +94,25 @@ public abstract class BaseBuilder<M, D> implements Builder {
return this.conventionService.asIndexer(names);
}
protected Set<String> extractAuthorizationFlags(FieldSet fields, String propertyName, List<String> permissionNames){
if (fields == null) return new HashSet<>();
if (permissionNames == null) return new HashSet<>();
FieldSet authorizationFlags = fields.extractPrefixed(this.asPrefix(propertyName));
List<String> permissions = new ArrayList<>();
for (String fieldValue : authorizationFlags.getFields()) permissions.addAll(permissionNames.stream().filter(x-> x.equalsIgnoreCase(fieldValue)).toList());
return new HashSet<>(permissions);
}
protected List<String> evaluateAuthorizationFlags(AuthorizationService authorizationService, Set<String> authorizationFlags, AffiliatedResource affiliatedResource) {
List<String> allowed = new ArrayList<>();
if (authorizationFlags == null) return allowed;
if (authorizationService == null) return allowed;
for (String permission : authorizationFlags) {
Boolean isAllowed = affiliatedResource == null ? authorizationService.authorize(permission) : authorizationService.authorizeAtLeastOne(List.of(affiliatedResource), permission);
if (isAllowed) allowed.add(permission);
}
return allowed;
}
}

View File

@ -0,0 +1,38 @@
package gr.cite.annotation.model.censorship;
import gr.cite.annotation.authorization.Permission;
import gr.cite.annotation.convention.ConventionService;
import gr.cite.annotation.model.Annotation;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.fieldset.FieldSet;
import gr.cite.tools.logging.DataLogEntry;
import gr.cite.tools.logging.LoggerService;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.util.UUID;
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class AnnotationAuthorCensor extends BaseCensor{
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(AnnotationAuthorCensor.class));
protected final AuthorizationService authService;
public AnnotationAuthorCensor(ConventionService conventionService, AuthorizationService authService) {
super(conventionService);
this.authService = authService;
}
public void censor(FieldSet fields, UUID userId) {
logger.debug(new DataLogEntry("censoring fields", fields));
if (fields == null || fields.isEmpty())
return;
this.authService.authorizeForce(Permission.BrowseAnnotation, Permission.DeferredAffiliation);
}
}

View File

@ -2,7 +2,9 @@ package gr.cite.annotation.model.censorship;
import gr.cite.annotation.authorization.Permission;
import gr.cite.annotation.convention.ConventionService;
import gr.cite.annotation.model.Annotation;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.censor.CensorFactory;
import gr.cite.tools.fieldset.FieldSet;
import gr.cite.tools.logging.DataLogEntry;
import gr.cite.tools.logging.LoggerService;
@ -21,9 +23,12 @@ public class AnnotationCensor extends BaseCensor{
protected final AuthorizationService authService;
public AnnotationCensor(ConventionService conventionService, AuthorizationService authService) {
protected final CensorFactory censorFactory;
public AnnotationCensor(ConventionService conventionService, AuthorizationService authService, CensorFactory censorFactory) {
super(conventionService);
this.authService = authService;
this.censorFactory = censorFactory;
}
public void censor(FieldSet fields, UUID userId) {
@ -31,7 +36,10 @@ public class AnnotationCensor extends BaseCensor{
if (fields == null || fields.isEmpty())
return;
this.authService.authorizeForce(Permission.BrowseAnnotation);
this.authService.authorizeForce(Permission.BrowseAnnotation, Permission.DeferredAffiliation);
FieldSet authorFields = fields.extractPrefixed(this.asIndexerPrefix(Annotation._author));
this.censorFactory.censor(AnnotationAuthorCensor.class).censor(authorFields, userId);
}
}

View File

@ -0,0 +1,74 @@
package gr.cite.EntityUser.model.deleter;
import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.data.EntityUserEntity;
import gr.cite.annotation.data.TenantScopedEntityManager;
import gr.cite.annotation.query.EntityUserQuery;
import gr.cite.tools.data.deleter.Deleter;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.logging.MapLogEntry;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.management.InvalidApplicationException;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class EntityUserDeleter implements Deleter {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(EntityUserDeleter.class));
private final TenantScopedEntityManager entityManager;
private final QueryFactory queryFactory;
@Autowired
public EntityUserDeleter(
TenantScopedEntityManager entityManager,
QueryFactory queryFactory
) {
this.entityManager = entityManager;
this.queryFactory = queryFactory;
}
public void deleteAndSaveByIds(List<UUID> ids) throws InvalidApplicationException {
logger.debug(new MapLogEntry("collecting to delete").And("count", Optional.ofNullable(ids).map(List::size).orElse(0)).And("ids", ids));
List<EntityUserEntity> data = this.queryFactory.query(EntityUserQuery.class).ids(ids).collect();
logger.trace("retrieved {} items", Optional.ofNullable(data).map(List::size).orElse(0));
this.deleteAndSave(data);
}
public void deleteAndSave(List<EntityUserEntity> data) throws InvalidApplicationException {
logger.debug("will delete {} items", Optional.ofNullable(data).map(List::size).orElse(0));
this.delete(data);
logger.trace("saving changes");
this.entityManager.flush();
logger.trace("changes saved");
}
public void delete(List<EntityUserEntity> data) throws InvalidApplicationException {
logger.debug("will delete {} items", Optional.ofNullable(data).map(List::size).orElse(0));
if (data == null || data.isEmpty())
return;
Instant now = Instant.now();
for (EntityUserEntity item : data) {
logger.trace("deleting item {}", item);
item.setIsActive(IsActive.Inactive);
item.setUpdatedAt(now);
logger.trace("updating item");
this.entityManager.merge(item);
logger.trace("updated item");
}
}
}

View File

@ -1,11 +1,16 @@
package gr.cite.annotation.query;
import gr.cite.annotation.authorization.AuthorizationFlags;
import gr.cite.annotation.authorization.Permission;
import gr.cite.annotation.common.enums.AnnotationProtectionType;
import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.common.scope.user.UserScope;
import gr.cite.annotation.data.AnnotationEntity;
import gr.cite.annotation.data.EntityUserEntity;
import gr.cite.annotation.model.Annotation;
import gr.cite.annotation.model.EntityUser;
import gr.cite.annotation.query.utils.BuildSubQueryInput;
import gr.cite.annotation.query.utils.QueryUtilsService;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.query.FieldResolver;
import gr.cite.tools.data.query.QueryBase;
@ -13,6 +18,7 @@ import gr.cite.tools.data.query.QueryContext;
import jakarta.persistence.Tuple;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Subquery;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@ -44,11 +50,14 @@ public class AnnotationQuery extends QueryBase<AnnotationEntity> {
private final UserScope userScope;
private final QueryUtilsService queryUtilsService;
private final AuthorizationService authService;
public AnnotationQuery(UserScope userScope, AuthorizationService authService) {
public AnnotationQuery(UserScope userScope, QueryUtilsService queryUtilsService, AuthorizationService authService) {
this.userScope = userScope;
this.authService = authService;
this.queryUtilsService = queryUtilsService;
this.authService = authService;
}
public AnnotationQuery like(String value) {
@ -161,6 +170,11 @@ public class AnnotationQuery extends QueryBase<AnnotationEntity> {
return this;
}
public AnnotationQuery authorize(EnumSet<AuthorizationFlags> values) {
this.authorize = values;
return this;
}
@Override
protected Boolean isFalseQuery() {
return this.isEmpty(this.ids) || this.isEmpty(this.excludedIds) || this.isEmpty(this.isActives) || this.isEmpty(this.entityIds);
@ -171,6 +185,43 @@ public class AnnotationQuery extends QueryBase<AnnotationEntity> {
return AnnotationEntity.class;
}
@Override
protected <X, Y> Predicate applyAuthZ(QueryContext<X, Y> queryContext) {
if (this.authorize.contains(AuthorizationFlags.None)) return null;
if (this.authorize.contains(AuthorizationFlags.Permission) && this.authService.authorize(Permission.BrowseAnnotation)) return null;
UUID userId = null;
if (this.authorize.contains(AuthorizationFlags.Associated)) userId = this.userScope.getUserIdSafe();
if (this.authorize.contains(AuthorizationFlags.Owner)) userId = this.userScope.getUserIdSafe();
List<Predicate> predicates = new ArrayList<>();
if (userId != null ) {
UUID finalUserId = userId;
Subquery<UUID> subquery = this.queryUtilsService.buildSubQuery(new BuildSubQueryInput<>(new BuildSubQueryInput.Builder<>(EntityUserEntity.class, UUID.class)
.query(queryContext.Query)
.criteriaBuilder(queryContext.CriteriaBuilder)
.keyPathFunc((subQueryRoot) -> subQueryRoot.get(EntityUserEntity._entityId))
.filterFunc((subQueryRoot, cb) ->
cb.and(
cb.equal(subQueryRoot.get(EntityUserEntity._userId), finalUserId),
cb.equal(subQueryRoot.get(EntityUserEntity._isActive), IsActive.Active)
)
)
));
predicates.add(queryContext.CriteriaBuilder.or(
queryContext.CriteriaBuilder.in(queryContext.Root.get(AnnotationEntity._subjectId)).value(userId),
queryContext.CriteriaBuilder.and(
queryContext.CriteriaBuilder.equal(queryContext.Root.get(AnnotationEntity._protectionType), AnnotationProtectionType.EntityAccessors),
queryContext.CriteriaBuilder.in(queryContext.Root.get(AnnotationEntity._entityId)).value(subquery)
)));
}
if (!predicates.isEmpty()) {
Predicate[] predicatesArray = predicates.toArray(new Predicate[0]);
return queryContext.CriteriaBuilder.and(predicatesArray);
} else {
return queryContext.CriteriaBuilder.or(); //Creates a false query
}
}
@Override
protected <X, Y> Predicate applyFilters(QueryContext<X, Y> queryContext) {
List<Predicate> predicates = new ArrayList<>();

View File

@ -1,8 +1,16 @@
package gr.cite.annotation.query;
import gr.cite.annotation.authorization.AuthorizationFlags;
import gr.cite.annotation.authorization.Permission;
import gr.cite.annotation.common.enums.AnnotationProtectionType;
import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.common.scope.user.UserScope;
import gr.cite.annotation.data.AnnotationEntity;
import gr.cite.annotation.data.EntityUserEntity;
import gr.cite.annotation.model.EntityUser;
import gr.cite.annotation.query.utils.BuildSubQueryInput;
import gr.cite.annotation.query.utils.QueryUtilsService;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.query.FieldResolver;
import gr.cite.tools.data.query.QueryBase;
import gr.cite.tools.data.query.QueryContext;
@ -12,6 +20,7 @@ import jakarta.persistence.criteria.Predicate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope;
import java.time.Instant;
import java.util.*;
@Component
@ -20,11 +29,20 @@ public class EntityUserQuery extends QueryBase<EntityUserEntity> {
private Collection<UUID> ids, entityIds, userIds;
private Collection<IsActive> isActives;
private Collection<IsActive> isActives;;
private final AuthorizationService authService;
private final UserScope userScope;
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
public EntityUserQuery ids(UUID value) {
public EntityUserQuery(AuthorizationService authService, UserScope userScope) {
this.authService = authService;
this.userScope = userScope;
}
public EntityUserQuery ids(UUID value) {
this.ids = List.of(value);
return this;
}
@ -98,6 +116,26 @@ public class EntityUserQuery extends QueryBase<EntityUserEntity> {
protected Class<EntityUserEntity> entityClass() {
return EntityUserEntity.class;
}
@Override
protected <X, Y> Predicate applyAuthZ(QueryContext<X, Y> queryContext) {
if (this.authorize.contains(AuthorizationFlags.None)) return null;
if (this.authorize.contains(AuthorizationFlags.Permission) && this.authService.authorize(Permission.BrowseAnnotation)) return null;
UUID userId = null;
if (this.authorize.contains(AuthorizationFlags.Associated)) userId = this.userScope.getUserIdSafe();
if (this.authorize.contains(AuthorizationFlags.Owner)) userId = this.userScope.getUserIdSafe();
List<Predicate> predicates = new ArrayList<>();
if (userId != null ) {
predicates.add( queryContext.CriteriaBuilder.in(queryContext.Root.get(EntityUserEntity._userId)).value(userId));
}
if (!predicates.isEmpty()) {
Predicate[] predicatesArray = predicates.toArray(new Predicate[0]);
return queryContext.CriteriaBuilder.and(predicatesArray);
} else {
return queryContext.CriteriaBuilder.or(); //Creates a false query
}
}
@Override
protected <X, Y> Predicate applyFilters(QueryContext<X, Y> queryContext) {
@ -136,11 +174,35 @@ public class EntityUserQuery extends QueryBase<EntityUserEntity> {
@Override
protected String fieldNameOf(FieldResolver item) {
return null;
if (item.match(EntityUser._id))
return EntityUserEntity._id;
else if (item.match(EntityUser._entityId))
return EntityUserEntity._entityId;
else if (item.match(EntityUser._user))
return EntityUserEntity._userId;
else if (item.prefix(EntityUser._user))
return EntityUserEntity._userId;
else if (item.match(EntityUser._createdAt))
return EntityUserEntity._createdAt;
else if (item.match(EntityUser._updatedAt))
return EntityUserEntity._updatedAt;
else if (item.match(EntityUser._hash))
return EntityUserEntity._updatedAt;
else if (item.match(EntityUser._isActive))
return EntityUserEntity._isActive;
else
return null;
}
@Override
protected EntityUserEntity convert(Tuple tuple, Set<String> columns) {
return null;
EntityUserEntity item = new EntityUserEntity();
item.setId(QueryBase.convertSafe(tuple, columns, EntityUserEntity._id, UUID.class));
item.setEntityId(QueryBase.convertSafe(tuple, columns, EntityUserEntity._entityId, UUID.class));
item.setUserId(QueryBase.convertSafe(tuple, columns, EntityUserEntity._userId, UUID.class));
item.setCreatedAt(QueryBase.convertSafe(tuple, columns, EntityUserEntity._createdAt, Instant.class));
item.setUpdatedAt(QueryBase.convertSafe(tuple, columns, EntityUserEntity._updatedAt, Instant.class));
item.setIsActive(QueryBase.convertSafe(tuple, columns, EntityUserEntity._isActive, IsActive.class));
return item;
}
}

View File

@ -0,0 +1,90 @@
package gr.cite.annotation.query.utils;
import gr.cite.tools.data.query.QueryContext;
import jakarta.persistence.criteria.*;
import java.util.function.BiFunction;
import java.util.function.Function;
public class BuildSubQueryInput<Entity, Key> {
private final AbstractQuery<?> query;
private final CriteriaBuilder criteriaBuilder;
private final Class<Entity> entityType;
private final Class<Key> keyType;
private final Function<Root<Entity>, Expression<Key>> keyPathFunc;
private final BiFunction<Root<Entity>, CriteriaBuilder, Predicate> filterFunc;
public BuildSubQueryInput(Builder<Entity, Key> builder) {
query = builder.query;
criteriaBuilder = builder.criteriaBuilder;
entityType = builder.entityType;
keyType = builder.keyType;
keyPathFunc = builder.keyPathFunc;
filterFunc = builder.filterFunc;
}
public AbstractQuery<?> getQuery() {
return query;
}
public CriteriaBuilder getCriteriaBuilder() {
return criteriaBuilder;
}
public Class<Entity> getEntityType() {
return entityType;
}
public Class<Key> getKeyType() {
return keyType;
}
public Function<Root<Entity>, Expression<Key>> getKeyPathFunc() {
return keyPathFunc;
}
public BiFunction<Root<Entity>, CriteriaBuilder, Predicate> getFilterFunc() {
return filterFunc;
}
public static class Builder<Entity, Key> {
private final Class<Entity> entityType;
private final Class<Key> keyType;
private AbstractQuery<?> query;
private CriteriaBuilder criteriaBuilder;
private Function<Root<Entity>, Expression<Key>> keyPathFunc;
private BiFunction<Root<Entity>, CriteriaBuilder, Predicate> filterFunc;
public Builder(Class<Entity> entityType, Class<Key> keyType) {
this.entityType = entityType;
this.keyType = keyType;
}
public Builder(Class<Entity> entityType, Class<Key> keyType, QueryContext<?, ?> queryContext) {
this.entityType = entityType;
this.keyType = keyType;
this.query = queryContext.Query;
this.criteriaBuilder = queryContext.CriteriaBuilder;
}
public Builder<Entity, Key> query(AbstractQuery<?> query) {
this.query = query;
return this;
}
public Builder<Entity, Key> criteriaBuilder(CriteriaBuilder criteriaBuilder) {
this.criteriaBuilder = criteriaBuilder;
return this;
}
public Builder<Entity, Key> keyPathFunc(Function<Root<Entity>, Expression<Key>> keyPathFunc) {
this.keyPathFunc = keyPathFunc;
return this;
}
public Builder<Entity, Key> filterFunc(BiFunction<Root<Entity>, CriteriaBuilder, Predicate> filterFunc) {
this.filterFunc = filterFunc;
return this;
}
}
}

View File

@ -0,0 +1,11 @@
package gr.cite.annotation.query.utils;
import jakarta.persistence.criteria.AbstractQuery;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.Subquery;
import java.util.UUID;
public interface QueryUtilsService {
<Key, D> Subquery<Key> buildSubQuery(BuildSubQueryInput<D, Key> parameters);
}

View File

@ -0,0 +1,20 @@
package gr.cite.annotation.query.utils;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.criteria.Subquery;
import org.springframework.stereotype.Component;
import java.util.UUID;
@Component
public class QueryUtilsServiceImpl implements QueryUtilsService {
@Override
public <Key, D> Subquery<Key> buildSubQuery(BuildSubQueryInput<D, Key> parameters){
Subquery<Key> subQuery = parameters.getQuery().subquery(parameters.getKeyType());
Root<D> subQueryRoot = subQuery.from(parameters.getEntityType());
subQuery.select(parameters.getKeyPathFunc().apply(subQueryRoot)).distinct(true);
subQuery.where(parameters.getFilterFunc().apply(subQueryRoot, parameters.getCriteriaBuilder()));
return subQuery;
}
}

View File

@ -1,7 +1,9 @@
package gr.cite.annotation.service.annotation;
import gr.cite.annotation.authorization.AuthorizationFlags;
import gr.cite.annotation.authorization.OwnedResource;
import gr.cite.annotation.authorization.Permission;
import gr.cite.annotation.authorization.authorizationcontentresolver.AuthorizationContentResolver;
import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.common.scope.user.UserScope;
import gr.cite.annotation.data.AnnotationEntity;
@ -23,6 +25,8 @@ import gr.cite.tools.logging.MapLogEntry;
import jakarta.persistence.EntityManager;
import jakarta.transaction.Transactional;
import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service;
import javax.management.InvalidApplicationException;
@ -45,17 +49,23 @@ public class AnnotationServiceImpl implements AnnotationService {
private final BuilderFactory builderFactory;
private final UserScope userScope;
private final AuthorizationContentResolver authorizationContentResolver;
private final MessageSource messageSource;
public AnnotationServiceImpl(
AuthorizationService authorizationService,
DeleterFactory deleterFactory,
EntityManager entityManager,
BuilderFactory builderFactory, UserScope userScope) {
AuthorizationService authorizationService,
DeleterFactory deleterFactory,
EntityManager entityManager,
BuilderFactory builderFactory, UserScope userScope, AuthorizationContentResolver authorizationContentResolver, MessageSource messageSource) {
this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory;
this.entityManager = entityManager;
this.builderFactory = builderFactory;
this.userScope = userScope;
this.authorizationContentResolver = authorizationContentResolver;
this.messageSource = messageSource;
}
@Override
@ -63,7 +73,7 @@ public class AnnotationServiceImpl implements AnnotationService {
public Annotation persist(AnnotationPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException {
logger.debug(new MapLogEntry("persisting annotation").And("model", model).And("fields", fields));
this.authorizationService.authorizeForce(Permission.EditAnnotation);
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.entityAffiliation(model.getEntityId())), Permission.NewAnnotation);
AnnotationEntity data = new AnnotationEntity();
data.setId(UUID.randomUUID());
@ -91,7 +101,10 @@ public class AnnotationServiceImpl implements AnnotationService {
public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException {
logger.debug("deleting Annotation: {}", id);
this.authorizationService.authorizeForce(Permission.DeleteAnnotation);
AnnotationEntity annotation = this.entityManager.find(AnnotationEntity.class, id);
if (annotation == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{id, Annotation.class.getSimpleName()}, LocaleContextHolder.getLocale()));
this.authorizationService.authorizeAtLeastOneForce(annotation.getSubjectId() != null ? List.of(new OwnedResource(annotation.getSubjectId())) : null, Permission.DeleteAnnotation);
this.deleterFactory.deleter(AnnotationDeleter.class).deleteAndSaveByIds(List.of(id));
}

View File

@ -13,7 +13,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.2</version>
<version>3.2.1</version>
</parent>
<modules>
@ -136,18 +136,18 @@
<dependency>
<groupId>gr.cite</groupId>
<artifactId>rabbitmq-core</artifactId>
<version>2.1.1</version>
<version>2.1.2</version>
</dependency>
<dependency>
<groupId>gr.cite</groupId>
<artifactId>queue-inbox</artifactId>
<version>1.0.0</version>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>gr.cite</groupId>
<artifactId>queue-outbox</artifactId>
<version>1.0.0</version>
<version>2.1.1</version>
</dependency>
</dependencies>

View File

@ -65,12 +65,12 @@
<dependency>
<groupId>gr.cite</groupId>
<artifactId>queue-inbox</artifactId>
<version>1.0.0</version>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>gr.cite</groupId>
<artifactId>queue-outbox</artifactId>
<version>1.0.0</version>
<version>2.1.1</version>
</dependency>
<dependency>

View File

@ -16,7 +16,7 @@ import java.util.List;
@Service
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public class PermissionNameProvider {
private static final Logger logger = LoggerFactory.getLogger(DepositServiceImpl.class);
private static final Logger logger = LoggerFactory.getLogger(PermissionNameProvider.class);
private final List<String> permissions;
public PermissionNameProvider(ConventionService conventionService) {

View File

@ -0,0 +1,20 @@
package eu.eudat.configurations.db;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.ConstructorBinding;
@ConfigurationProperties(prefix = "naming-strategy")
public class NamingStrategyProperties {
private final String prefix;
@ConstructorBinding
public NamingStrategyProperties(String prefix) {
this.prefix = prefix;
}
public String getPrefix() {
return prefix;
}
}

View File

@ -0,0 +1,30 @@
package eu.eudat.configurations.db;
import eu.eudat.convention.ConventionService;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@EnableConfigurationProperties({NamingStrategyProperties.class})
public class PrefixPhysicalNamingStrategy extends PhysicalNamingStrategyStandardImpl {
private final NamingStrategyProperties namingStrategyProperties;
private final ConventionService conventionService;
public PrefixPhysicalNamingStrategy(NamingStrategyProperties namingStrategyProperties, ConventionService conventionService) {
this.namingStrategyProperties = namingStrategyProperties;
this.conventionService = conventionService;
}
@Override
public Identifier toPhysicalTableName(Identifier logicalName, JdbcEnvironment context) {
if (conventionService.isNullOrEmpty(namingStrategyProperties.getPrefix()))
return super.toPhysicalTableName(logicalName, context);
Identifier identifier = new Identifier(namingStrategyProperties.getPrefix() + logicalName.getText(), logicalName.isQuoted());
return super.toPhysicalTableName(identifier, context);
}
}

View File

@ -2,6 +2,7 @@ package eu.eudat.data;
import eu.eudat.commons.enums.IsActive;
import eu.eudat.data.converters.enums.IsActiveConverter;
import eu.eudat.data.converters.enums.QueueInboxStatusConverter;
import eu.eudat.data.types.JsonSQLType;
import gr.cite.queueinbox.entity.QueueInbox;
import gr.cite.queueinbox.entity.QueueInboxStatus;
@ -19,15 +20,15 @@ public class QueueInboxEntity implements QueueInbox {
private UUID id;
public final static String _id = "id";
@Column(name = "\"queue\"", nullable = false, length = 50)
@Column(name = "\"queue\"", nullable = false, length = 200)
private String queue;
public final static String _queue = "queue";
@Column(name = "\"exchange\"", nullable = false, length = 50)
@Column(name = "\"exchange\"", nullable = false, length = 200)
private String exchange;
public final static String _exchange = "exchange";
@Column(name = "\"route\"", nullable = false, length = 50)
@Column(name = "\"route\"", nullable = false, length = 200)
private String route;
public final static String _route = "route";
@ -52,14 +53,13 @@ public class QueueInboxEntity implements QueueInbox {
private UUID tenantId;
public final static String _tenantId = "tenantId";
@Column(name = "\"is_active\"", length = 20, nullable = false)
@Column(name = "\"is_active\"", nullable = false)
@Convert(converter = IsActiveConverter.class)
private IsActive isActive;
public final static String _isActive = "isActive";
//TODO: as integer
@Column(name = "\"status\"", length = 50, nullable = false)
@Enumerated(EnumType.STRING)
@Column(name = "\"status\"", nullable = false)
@Convert(converter = QueueInboxStatusConverter.class)
private QueueInboxStatus status;
public final static String _status = "status";

View File

@ -2,6 +2,7 @@ package eu.eudat.data;
import eu.eudat.commons.enums.IsActive;
import eu.eudat.data.converters.enums.IsActiveConverter;
import eu.eudat.data.converters.enums.QueueOutboxNotifyStatusConverter;
import gr.cite.queueoutbox.entity.QueueOutbox;
import gr.cite.queueoutbox.entity.QueueOutboxNotifyStatus;
@ -17,11 +18,11 @@ public class QueueOutboxEntity implements QueueOutbox {
private UUID id;
public final static String _id = "id";
@Column(name = "\"exchange\"", nullable = false, length = 50)
@Column(name = "\"exchange\"", nullable = false, length = 200)
private String exchange;
public final static String _exchange = "exchange";
@Column(name = "\"route\"", length = 50)
@Column(name = "\"route\"", length = 200)
private String route;
public final static String _route = "route";
@ -33,9 +34,8 @@ public class QueueOutboxEntity implements QueueOutbox {
private String message;
public final static String _message = "message";
//TODO: as integer
@Column(name = "\"notify_status\"", length = 20, nullable = false)
@Enumerated(EnumType.STRING)
@Column(name = "\"notify_status\"", nullable = false)
@Convert(converter = QueueOutboxNotifyStatusConverter.class)
private QueueOutboxNotifyStatus notifyStatus;
public final static String _notifyStatus = "notifyStatus";
@ -55,7 +55,7 @@ public class QueueOutboxEntity implements QueueOutbox {
private UUID tenantId;
public final static String _tenantId = "tenantId";
@Column(name = "\"is_active\"", length = 20, nullable = false)
@Column(name = "\"is_active\"", nullable = false)
@Convert(converter = IsActiveConverter.class)
private IsActive isActive;
public final static String _isActive = "isActive";

View File

@ -0,0 +1,19 @@
package eu.eudat.data.converters.enums;
import gr.cite.queueinbox.entity.QueueInboxStatus;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
@Converter
public class QueueInboxStatusConverter implements AttributeConverter<QueueInboxStatus, Short> {
@Override
public Short convertToDatabaseColumn(QueueInboxStatus value) {
if (value == null) throw new IllegalArgumentException("Value could not be null for: " + this.getClass().getSimpleName());
return value.getValue();
}
@Override
public QueueInboxStatus convertToEntityAttribute(Short dbData) {
return QueueInboxStatus.of(dbData);
}
}

View File

@ -0,0 +1,19 @@
package eu.eudat.data.converters.enums;
import gr.cite.queueoutbox.entity.QueueOutboxNotifyStatus;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
@Converter
public class QueueOutboxNotifyStatusConverter implements AttributeConverter<QueueOutboxNotifyStatus, Short> {
@Override
public Short convertToDatabaseColumn(QueueOutboxNotifyStatus value) {
if (value == null) throw new IllegalArgumentException("Value could not be null for: " + this.getClass().getSimpleName());
return value.getValue();
}
@Override
public QueueOutboxNotifyStatus convertToEntityAttribute(Short dbData) {
return QueueOutboxNotifyStatus.of(dbData);
}
}

View File

@ -28,6 +28,7 @@ public class OutboxIntegrationEvent extends IntegrationEvent {
public static final String DESCRIPTION_TOUCH = "DESCRIPTION_TOUCH";
public static final String ANNOTATION_ENTITY_TOUCH = "ANNOTATION_ENTITY_TOUCH";
public static final String ANNOTATION_ENTITY_REMOVE = "ANNOTATION_ENTITY_REMOVE";
public static final String WHAT_YOU_KNOW_ABOUT_ME_COMPLETED = "WHAT_YOU_KNOW_ABOUT_ME_COMPLETED";

View File

@ -23,7 +23,8 @@ public class OutboxProperties {
private final String descriptionTouchTopic;
private final String annotationEntityTouchTopic;
private final String annotationEntitiesTouchTopic;
private final String annotationEntitiesRemovalTopic;
private final String notifyTopic;
@ -34,19 +35,20 @@ public class OutboxProperties {
private final String generateFileTopic;
public OutboxProperties(String exchange,
String tenantTouchTopic,
String tenantRemovalTopic,
String tenantReactivationTopic,
String tenantUserInviteTopic,
String userRemovalTopic,
String userTouchTopic,
String dmpTouchTopic,
String descriptionTouchTopic,
String annotationEntityTouchTopic,
String notifyTopic,
String forgetMeCompletedTopic,
String whatYouKnowAboutMeCompletedTopic,
String generateFileTopic
String tenantTouchTopic,
String tenantRemovalTopic,
String tenantReactivationTopic,
String tenantUserInviteTopic,
String userRemovalTopic,
String userTouchTopic,
String dmpTouchTopic,
String descriptionTouchTopic,
String annotationEntitiesTouchTopic,
String annotationEntitiesRemovalTopic,
String notifyTopic,
String forgetMeCompletedTopic,
String whatYouKnowAboutMeCompletedTopic,
String generateFileTopic
) {
this.exchange = exchange;
this.tenantTouchTopic = tenantTouchTopic;
@ -57,8 +59,9 @@ public class OutboxProperties {
this.userTouchTopic = userTouchTopic;
this.dmpTouchTopic = dmpTouchTopic;
this.descriptionTouchTopic = descriptionTouchTopic;
this.annotationEntityTouchTopic = annotationEntityTouchTopic;
this.notifyTopic = notifyTopic;
this.annotationEntitiesTouchTopic = annotationEntitiesTouchTopic;
this.annotationEntitiesRemovalTopic = annotationEntitiesRemovalTopic;
this.notifyTopic = notifyTopic;
this.forgetMeCompletedTopic = forgetMeCompletedTopic;
this.whatYouKnowAboutMeCompletedTopic = whatYouKnowAboutMeCompletedTopic;
this.generateFileTopic = generateFileTopic;
@ -100,8 +103,12 @@ public class OutboxProperties {
return descriptionTouchTopic;
}
public String getAnnotationEntityTouchTopic() {
return annotationEntityTouchTopic;
public String getAnnotationEntitiesTouchTopic() {
return annotationEntitiesTouchTopic;
}
public String getAnnotationEntitiesRemovalTopic() {
return annotationEntitiesRemovalTopic;
}
public String getNotifyTopic() {

View File

@ -416,7 +416,11 @@ public class OutboxRepositoryImpl implements OutboxRepository {
break;
}
case OutboxIntegrationEvent.ANNOTATION_ENTITY_TOUCH: {
routingKey = this.outboxProperties.getAnnotationEntityTouchTopic();
routingKey = this.outboxProperties.getAnnotationEntitiesTouchTopic();
break;
}
case OutboxIntegrationEvent.ANNOTATION_ENTITY_REMOVE: {
routingKey = this.outboxProperties.getAnnotationEntitiesRemovalTopic();
break;
}
case OutboxIntegrationEvent.FORGET_ME_COMPLETED: {

View File

@ -0,0 +1,19 @@
package eu.eudat.integrationevent.outbox.annotationentityremoval;
import eu.eudat.integrationevent.TrackedEvent;
import java.util.List;
import java.util.UUID;
public class AnnotationEntitiesRemovalIntegrationEvent extends TrackedEvent {
private List<UUID> entityIds;
public List<UUID> getEntityIds() {
return entityIds;
}
public void setEntityIds(List<UUID> entityIds) {
this.entityIds = entityIds;
}
}

View File

@ -0,0 +1,9 @@
package eu.eudat.integrationevent.outbox.annotationentityremoval;
import java.util.UUID;
public interface AnnotationEntityRemovalIntegrationEventHandler {
void handleDescription(UUID descriptionId);
void handleDmp(UUID dmpId);
}

View File

@ -0,0 +1,67 @@
package eu.eudat.integrationevent.outbox.annotationentityremoval;
import eu.eudat.commons.enums.IsActive;
import eu.eudat.data.DescriptionEntity;
import eu.eudat.data.DmpUserEntity;
import eu.eudat.integrationevent.outbox.OutboxIntegrationEvent;
import eu.eudat.integrationevent.outbox.OutboxService;
import eu.eudat.model.Description;
import eu.eudat.model.DmpUser;
import eu.eudat.query.DescriptionQuery;
import eu.eudat.query.DmpUserQuery;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.logging.LoggerService;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class AnnotationEntityRemovalIntegrationEventHandlerImpl implements AnnotationEntityRemovalIntegrationEventHandler {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(AnnotationEntityRemovalIntegrationEventHandlerImpl.class));
private final OutboxService outboxService;
private final QueryFactory queryFactory;
public AnnotationEntityRemovalIntegrationEventHandlerImpl(OutboxService outboxService, QueryFactory queryFactory) {
this.outboxService = outboxService;
this.queryFactory = queryFactory;
}
private void handle(AnnotationEntitiesRemovalIntegrationEvent event) {
OutboxIntegrationEvent message = new OutboxIntegrationEvent();
message.setMessageId(UUID.randomUUID());
message.setType(OutboxIntegrationEvent.ANNOTATION_ENTITY_REMOVE);
message.setEvent(event);
this.outboxService.publish(message);
}
@Override
public void handleDescription(UUID descriptionId) {
AnnotationEntitiesRemovalIntegrationEvent event = new AnnotationEntitiesRemovalIntegrationEvent();
event.setEntityIds(List.of(descriptionId));
this.handle(event);
}
@Override
public void handleDmp(UUID dmpId) {
List<DescriptionEntity> descriptionEntities = this.queryFactory.query(DescriptionQuery.class).dmpIds(dmpId).collectAs(new BaseFieldSet().ensure(Description._id));
AnnotationEntitiesRemovalIntegrationEvent event = new AnnotationEntitiesRemovalIntegrationEvent();
event.setEntityIds(new ArrayList<>());
event.getEntityIds().add(dmpId);
for (DescriptionEntity description : descriptionEntities) event.getEntityIds().add(description.getId());
this.handle(event);
}
}

View File

@ -0,0 +1,43 @@
package eu.eudat.integrationevent.outbox.annotationentitytouch;
import eu.eudat.integrationevent.TrackedEvent;
import java.util.List;
import java.util.UUID;
public class AnnotationEntitiesTouchedIntegrationEvent extends TrackedEvent {
private List<AnnotationEntityTouchedIntegrationEvent> events;
public List<AnnotationEntityTouchedIntegrationEvent> getEvents() {
return events;
}
public void setEvents(List<AnnotationEntityTouchedIntegrationEvent> events) {
this.events = events;
}
public static class AnnotationEntityTouchedIntegrationEvent {
private UUID entityId;
private List<UUID> userIds;
public UUID getEntityId() {
return entityId;
}
public void setEntityId(UUID entityId) {
this.entityId = entityId;
}
public List<UUID> getUserIds() {
return userIds;
}
public void setUserIds(List<UUID> userIds) {
this.userIds = userIds;
}
}
}

View File

@ -1,30 +0,0 @@
package eu.eudat.integrationevent.outbox.annotationentitytouch;
import eu.eudat.integrationevent.TrackedEvent;
import java.util.List;
import java.util.UUID;
public class AnnotationEntityTouchedIntegrationEvent extends TrackedEvent {
private UUID entityId;
private List<UUID> userIds;
public UUID getEntityId() {
return entityId;
}
public void setEntityId(UUID entityId) {
this.entityId = entityId;
}
public List<UUID> getUserIds() {
return userIds;
}
public void setUserIds(List<UUID> userIds) {
this.userIds = userIds;
}
}

View File

@ -9,17 +9,6 @@ import java.util.UUID;
public interface AnnotationEntityTouchedIntegrationEventHandler {
void handle(AnnotationEntityTouchedIntegrationEvent event);
static AnnotationEntityTouchedIntegrationEvent buildEventFromPersistModel(DmpPersist persist) {
AnnotationEntityTouchedIntegrationEvent event = new AnnotationEntityTouchedIntegrationEvent();
event.setEntityId(persist.getId());
List<UUID> users = new ArrayList<>();
persist.getUsers().forEach(dmpUserPersist -> {
users.add(dmpUserPersist.getUser());
});
event.setUserIds(users);
return event;
}
void handleDescription(UUID descriptionId);
void handleDmp(UUID dmpId);
}

View File

@ -1,13 +1,24 @@
package eu.eudat.integrationevent.outbox.annotationentitytouch;
import eu.eudat.commons.enums.IsActive;
import eu.eudat.data.DescriptionEntity;
import eu.eudat.data.DmpUserEntity;
import eu.eudat.integrationevent.outbox.OutboxIntegrationEvent;
import eu.eudat.integrationevent.outbox.OutboxService;
import eu.eudat.model.Description;
import eu.eudat.model.DmpUser;
import eu.eudat.query.DescriptionQuery;
import eu.eudat.query.DmpUserQuery;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.logging.LoggerService;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@Component
@ -18,12 +29,14 @@ public class AnnotationEntityTouchedIntegrationEventHandlerImpl implements Annot
private final OutboxService outboxService;
public AnnotationEntityTouchedIntegrationEventHandlerImpl(OutboxService outboxService) {
private final QueryFactory queryFactory;
public AnnotationEntityTouchedIntegrationEventHandlerImpl(OutboxService outboxService, QueryFactory queryFactory) {
this.outboxService = outboxService;
this.queryFactory = queryFactory;
}
@Override
public void handle(AnnotationEntityTouchedIntegrationEvent event) {
private void handle(AnnotationEntitiesTouchedIntegrationEvent event) {
OutboxIntegrationEvent message = new OutboxIntegrationEvent();
message.setMessageId(UUID.randomUUID());
message.setType(OutboxIntegrationEvent.ANNOTATION_ENTITY_TOUCH);
@ -31,4 +44,40 @@ public class AnnotationEntityTouchedIntegrationEventHandlerImpl implements Annot
this.outboxService.publish(message);
}
@Override
public void handleDescription(UUID descriptionId) {
DescriptionEntity entity = this.queryFactory.query(DescriptionQuery.class).ids(descriptionId).firstAs(new BaseFieldSet().ensure(Description._dmp));
if (entity == null) return;
List<DmpUserEntity> dmpUsers = this.queryFactory.query(DmpUserQuery.class).dmpIds(entity.getDmpId()).isActives(IsActive.Active).collectAs(new BaseFieldSet().ensure(DmpUser._user));
AnnotationEntitiesTouchedIntegrationEvent event = new AnnotationEntitiesTouchedIntegrationEvent();
event.setEvents(List.of(this.buildEventItem(descriptionId, dmpUsers)));
this.handle(event);
}
@Override
public void handleDmp(UUID dmpId) {
List<DescriptionEntity> descriptionEntities = this.queryFactory.query(DescriptionQuery.class).dmpIds(dmpId).collectAs(new BaseFieldSet().ensure(Description._id));
List<DmpUserEntity> dmpUsers = this.queryFactory.query(DmpUserQuery.class).dmpIds(dmpId).isActives(IsActive.Active).collectAs(new BaseFieldSet().ensure(DmpUser._user));
AnnotationEntitiesTouchedIntegrationEvent event = new AnnotationEntitiesTouchedIntegrationEvent();
event.setEvents(new ArrayList<>());
event.getEvents().add(this.buildEventItem(dmpId, dmpUsers));
for (DescriptionEntity description : descriptionEntities) event.getEvents().add(this.buildEventItem(description.getId(), dmpUsers));
this.handle(event);
}
private AnnotationEntitiesTouchedIntegrationEvent.AnnotationEntityTouchedIntegrationEvent buildEventItem(UUID entityId, List<DmpUserEntity> dmpUsers){
AnnotationEntitiesTouchedIntegrationEvent.AnnotationEntityTouchedIntegrationEvent eventItem = new AnnotationEntitiesTouchedIntegrationEvent.AnnotationEntityTouchedIntegrationEvent();
eventItem.setEntityId(entityId);
List<UUID> users = new ArrayList<>();
for (DmpUserEntity dmpUser : dmpUsers){
users.add(dmpUser.getUserId());
}
eventItem.setUserIds(users);
return eventItem;
}
}

View File

@ -57,6 +57,9 @@ public class PublicDmp {
private List<PublicDescription> descriptions;
public static final String _descriptions = "descriptions";
private List<PublicEntityDoi> entityDois;
public static final String _entityDois = "entityDois";
public UUID getId() {
return id;
}
@ -160,4 +163,12 @@ public class PublicDmp {
public void setDescriptions(List<PublicDescription> descriptions) {
this.descriptions = descriptions;
}
public List<PublicEntityDoi> getEntityDois() {
return entityDois;
}
public void setEntityDois(List<PublicEntityDoi> entityDois) {
this.entityDois = entityDois;
}
}

View File

@ -0,0 +1,69 @@
package eu.eudat.model;
import eu.eudat.commons.enums.EntityType;
import java.util.UUID;
public class PublicEntityDoi {
private UUID id;
public static final String _id = "id";
private EntityType entityType;
public static final String _entityType = "entityType";
private String repositoryId;
public static final String _repositoryId = "repositoryId";
private String doi;
public static final String _doi = "doi";
private UUID entityId;
public static final String _entityId = "entityId";
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public EntityType getEntityType() {
return entityType;
}
public void setEntityType(EntityType entityType) {
this.entityType = entityType;
}
public String getRepositoryId() {
return repositoryId;
}
public void setRepositoryId(String repositoryId) {
this.repositoryId = repositoryId;
}
public String getDoi() {
return doi;
}
public void setDoi(String doi) {
this.doi = doi;
}
public UUID getEntityId() {
return entityId;
}
public void setEntityId(UUID entityId) {
this.entityId = entityId;
}
}

View File

@ -1,13 +1,14 @@
package eu.eudat.model.builder;
import eu.eudat.authorization.AuthorizationFlags;
import eu.eudat.commons.enums.IsActive;
import eu.eudat.commons.enums.EntityType;
import eu.eudat.convention.ConventionService;
import eu.eudat.data.DmpEntity;
import eu.eudat.model.*;
import eu.eudat.query.DescriptionQuery;
import eu.eudat.query.DmpReferenceQuery;
import eu.eudat.query.DmpUserQuery;
import eu.eudat.query.EntityDoiQuery;
import gr.cite.tools.data.builder.BuilderFactory;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyApplicationException;
@ -66,6 +67,9 @@ public class PublicDmpBuilder extends BaseBuilder<PublicDmp, DmpEntity> {
FieldSet descriptionsFields = fields.extractPrefixed(this.asPrefix(PublicDmp._descriptions));
Map<UUID, List<PublicDescription>> descriptionsMap = this.collectDmpDescriptions(descriptionsFields, data);
FieldSet entityDoisFields = fields.extractPrefixed(this.asPrefix(PublicDmp._entityDois));
Map<UUID, List<PublicEntityDoi>> entityDoisMap = this.collectEntityDois(entityDoisFields, data);
for (DmpEntity d : data) {
PublicDmp m = new PublicDmp();
if (fields.hasField(this.asIndexer(PublicDmp._id))) m.setId(d.getId());
@ -82,6 +86,7 @@ public class PublicDmpBuilder extends BaseBuilder<PublicDmp, DmpEntity> {
if (dmpReferenceMap != null && !dmpReferenceMap.isEmpty() && dmpReferenceMap.containsKey(d.getId())) m.setDmpReferences(dmpReferenceMap.get(d.getId()));
if (dmpUsersMap != null && !dmpUsersMap.isEmpty() && dmpUsersMap.containsKey(d.getId())) m.setDmpUsers(dmpUsersMap.get(d.getId()));
if (descriptionsMap != null && !descriptionsMap.isEmpty() && descriptionsMap.containsKey(d.getId())) m.setDescriptions(descriptionsMap.get(d.getId()));
if (entityDoisMap != null && !entityDoisMap.isEmpty() && entityDoisMap.containsKey(d.getId())) m.setEntityDois(entityDoisMap.get(d.getId()));
models.add(m);
}
@ -144,4 +149,22 @@ public class PublicDmpBuilder extends BaseBuilder<PublicDmp, DmpEntity> {
return itemMap;
}
private Map<UUID, List<PublicEntityDoi>> collectEntityDois(FieldSet fields, List<DmpEntity> data) throws MyApplicationException {
if (fields.isEmpty() || data.isEmpty()) return null;
this.logger.debug("checking related - {}", PublicEntityDoi.class.getSimpleName());
Map<UUID, List<PublicEntityDoi>> itemMap;
FieldSet clone = new BaseFieldSet(fields.getFields()).ensure(this.asIndexer(PublicEntityDoi._entityId));
EntityDoiQuery query = this.queryFactory.query(EntityDoiQuery.class).authorize(this.authorize).types(EntityType.DMP).entityIds(data.stream().map(DmpEntity::getId).distinct().collect(Collectors.toList()));
itemMap = this.builderFactory.builder(PublicEntityDoiBuilder.class).authorize(this.authorize).asMasterKey(query, clone, PublicEntityDoi::getEntityId);
if (!fields.hasField(this.asIndexer(PublicEntityDoi._entityId))) {
itemMap.values().stream().flatMap(List::stream).filter(x -> x != null && x.getEntityId() != null).peek(x -> {
x.setEntityId(null);
});
}
return itemMap;
}
}

View File

@ -0,0 +1,69 @@
package eu.eudat.model.builder;
import eu.eudat.authorization.AuthorizationFlags;
import eu.eudat.convention.ConventionService;
import eu.eudat.data.EntityDoiEntity;
import eu.eudat.model.*;
import gr.cite.tools.data.builder.BuilderFactory;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyApplicationException;
import gr.cite.tools.fieldset.FieldSet;
import gr.cite.tools.logging.DataLogEntry;
import gr.cite.tools.logging.LoggerService;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.util.*;
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PublicEntityDoiBuilder extends BaseBuilder<PublicEntityDoi, EntityDoiEntity> {
private final QueryFactory queryFactory;
private final BuilderFactory builderFactory;
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
@Autowired
public PublicEntityDoiBuilder(
ConventionService conventionService,
QueryFactory queryFactory,
BuilderFactory builderFactory) {
super(conventionService, new LoggerService(LoggerFactory.getLogger(PublicEntityDoiBuilder.class)));
this.queryFactory = queryFactory;
this.builderFactory = builderFactory;
}
public PublicEntityDoiBuilder authorize(EnumSet<AuthorizationFlags> values) {
this.authorize = values;
return this;
}
@Override
public List<PublicEntityDoi> build(FieldSet fields, List<EntityDoiEntity> data) throws MyApplicationException {
this.logger.debug("building for {} items requesting {} fields", Optional.ofNullable(data).map(List::size).orElse(0), Optional.ofNullable(fields).map(FieldSet::getFields).map(Set::size).orElse(0));
this.logger.trace(new DataLogEntry("requested fields", fields));
if (fields == null || data == null || fields.isEmpty())
return new ArrayList<>();
List<PublicEntityDoi> models = new ArrayList<>();
for (EntityDoiEntity d : data) {
PublicEntityDoi m = new PublicEntityDoi();
if (fields.hasField(this.asIndexer(PublicEntityDoi._id))) m.setId(d.getId());
if (fields.hasField(this.asIndexer(PublicEntityDoi._entityType))) m.setEntityType(d.getEntityType());
if (fields.hasField(this.asIndexer(PublicEntityDoi._repositoryId))) m.setRepositoryId(d.getRepositoryId());
if (fields.hasField(this.asIndexer(PublicEntityDoi._doi))) m.setDoi(d.getDoi());
if (fields.hasField(this.asIndexer(PublicEntityDoi._entityId))) m.setEntityId(d.getEntityId());
models.add(m);
}
this.logger.debug("build {} items", Optional.of(models).map(List::size).orElse(0));
return models;
}
}

View File

@ -4,13 +4,13 @@ import eu.eudat.commons.validation.BaseValidator;
import gr.cite.tools.validation.specification.Specification;
import eu.eudat.convention.ConventionService;
import eu.eudat.errorcode.ErrorThesaurusProperties;
import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Scope;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
@ -29,7 +29,7 @@ public class CloneDmpPersist {
public static final String _description = "description";
private List<UUID> descriptions = Lists.newArrayList();
private List<UUID> descriptions = new ArrayList<>();
public static final String _descriptions = "descriptions";

View File

@ -5,13 +5,13 @@ import gr.cite.tools.validation.specification.Specification;
import eu.eudat.convention.ConventionService;
import eu.eudat.data.DmpEntity;
import eu.eudat.errorcode.ErrorThesaurusProperties;
import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Scope;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
@ -33,7 +33,7 @@ public class NewVersionDmpPersist {
public static final String _blueprintId = "blueprintId";
private List<UUID> descriptions = Lists.newArrayList();
private List<UUID> descriptions = new ArrayList<>();
public static final String _descriptions = "descriptions";

View File

@ -22,6 +22,8 @@ import eu.eudat.data.*;
import eu.eudat.errorcode.ErrorThesaurusProperties;
import eu.eudat.event.DescriptionTouchedEvent;
import eu.eudat.event.EventBroker;
import eu.eudat.integrationevent.outbox.annotationentityremoval.AnnotationEntityRemovalIntegrationEventHandler;
import eu.eudat.integrationevent.outbox.annotationentitytouch.AnnotationEntityTouchedIntegrationEventHandler;
import eu.eudat.integrationevent.outbox.descriptiontouched.DescriptionTouchedIntegrationEventHandler;
import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEvent;
import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEventHandler;
@ -99,6 +101,8 @@ public class DescriptionServiceImpl implements DescriptionService {
private final StorageFileService storageFileService;
private final DescriptionTouchedIntegrationEventHandler descriptionTouchedIntegrationEventHandler;
private final AuthorizationContentResolver authorizationContentResolver;
private final AnnotationEntityTouchedIntegrationEventHandler annotationEntityTouchedIntegrationEventHandler;
private final AnnotationEntityRemovalIntegrationEventHandler annotationEntityRemovalIntegrationEventHandler;
@Autowired
public DescriptionServiceImpl(
@ -113,7 +117,7 @@ public class DescriptionServiceImpl implements DescriptionService {
QueryFactory queryFactory,
JsonHandlingService jsonHandlingService,
UserScope userScope,
XmlHandlingService xmlHandlingService, NotifyIntegrationEventHandler eventHandler, NotificationProperties notificationProperties, FileTransformerService fileTransformerService, ElasticService elasticService, ValidatorFactory validatorFactory, StorageFileProperties storageFileConfig, StorageFileService storageFileService, DescriptionTouchedIntegrationEventHandler descriptionTouchedIntegrationEventHandler, AuthorizationContentResolver authorizationContentResolver) {
XmlHandlingService xmlHandlingService, NotifyIntegrationEventHandler eventHandler, NotificationProperties notificationProperties, FileTransformerService fileTransformerService, ElasticService elasticService, ValidatorFactory validatorFactory, StorageFileProperties storageFileConfig, StorageFileService storageFileService, DescriptionTouchedIntegrationEventHandler descriptionTouchedIntegrationEventHandler, AuthorizationContentResolver authorizationContentResolver, AnnotationEntityTouchedIntegrationEventHandler annotationEntityTouchedIntegrationEventHandler, AnnotationEntityRemovalIntegrationEventHandler annotationEntityRemovalIntegrationEventHandler) {
this.entityManager = entityManager;
this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory;
@ -135,6 +139,8 @@ public class DescriptionServiceImpl implements DescriptionService {
this.storageFileService = storageFileService;
this.descriptionTouchedIntegrationEventHandler = descriptionTouchedIntegrationEventHandler;
this.authorizationContentResolver = authorizationContentResolver;
this.annotationEntityTouchedIntegrationEventHandler = annotationEntityTouchedIntegrationEventHandler;
this.annotationEntityRemovalIntegrationEventHandler = annotationEntityRemovalIntegrationEventHandler;
}
@Override
@ -230,6 +236,8 @@ public class DescriptionServiceImpl implements DescriptionService {
this.descriptionTouchedIntegrationEventHandler.handle(DescriptionTouchedIntegrationEventHandler.buildEventFromPersistModel(model));
this.annotationEntityTouchedIntegrationEventHandler.handleDescription(data.getId());
this.elasticService.persistDescription(data);
return this.builderFactory.builder(DescriptionBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fields, Description._id), data);
}
@ -294,71 +302,6 @@ public class DescriptionServiceImpl implements DescriptionService {
return event;
}
// public List<eu.eudat.commons.types.descriptiontemplate.FieldEntity> getFieldById(String id){
// List<eu.eudat.commons.types.descriptiontemplate.FieldEntity> fieldEntities = new ArrayList<>();
// if (id == null || id.isBlank()) return fieldEntities;
// if (this.getFieldSets() != null){
// for (FieldSetEntity fieldSetEntity: this.getFieldSets()) {
// fieldEntities.addAll(fieldSetEntity.getFieldById(id));
// }
// }
// if (this.getSections() != null){
// for (SectionEntity sectionEntity: this.getSections()) {
// fieldEntities.addAll(sectionEntity.getFieldById(id));
// }
// }
// return fieldEntities;
// }
private void descriptionForce(DescriptionEntity description) throws Exception {
List<String> datasetProfileValidators = new LinkedList<>();
DescriptionTemplateEntity descriptionTemplateEntity = this.entityManager.find(DescriptionTemplateEntity.class, description.getDescriptionTemplateId());
if (descriptionTemplateEntity == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{description.getDescriptionTemplateId(), DescriptionTemplate.class.getSimpleName()}, LocaleContextHolder.getLocale()));
eu.eudat.commons.types.descriptiontemplate.DefinitionEntity descriptionTemplateDefinition = this.xmlHandlingService.fromXml(eu.eudat.commons.types.descriptiontemplate.DefinitionEntity.class, descriptionTemplateEntity.getDefinition());
// DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
// DocumentBuilder builder = builderFactory.newDocumentBuilder();
// Document xmlDocument = builder.parse(new ByteArrayInputStream(profile.getDefinition().getBytes()));
//
// XPath xPath = XPathFactory.newInstance().newXPath();
// String expression = "//validation/@type[.=1]/ancestor::field/@id";
// NodeList nodeList = (NodeList) xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODESET);
//
// for (int i = 0; i < nodeList.getLength(); i++) {
// Node node = nodeList.item(i);
// datasetProfileValidators.add(node.getNodeValue());
// }
//
// expression = "//validation/@type[.=1]/ancestor::fieldSet";
// nodeList = (NodeList) xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODESET);
//
//
// JSONObject obj = new JSONObject(description.getProperties());
// VisibilityRuleService visibilityRuleService = new VisibilityRuleServiceImpl();
// visibilityRuleService.setProperties(obj.toMap());
//
// description.setProfile(profile);
// PagedDatasetProfile pagedDatasetProfile = this.getPagedProfile(new DatasetWizardModel(), description);
// visibilityRuleService.buildVisibilityContext(pagedDatasetProfile.getRules());
//
//
// String failedField = null;
//
// for (String validator : datasetProfileValidators) {
// if (obj.has(validator) && isNullOrEmpty(obj.getString(validator)) && isElementVisible(nodeList, validator, visibilityRuleService)) {
// //throw new Exception("Field value of " + validator + " must be filled.");
// failedField = validator;
// break;
// }
// }
// return failedField;
}
@Override
public Description persistStatus(DescriptionStatusPersist model, FieldSet fields) throws IOException {
logger.debug(new MapLogEntry("persisting data dmp").And("model", model).And("fields", fields));
@ -385,6 +328,8 @@ public class DescriptionServiceImpl implements DescriptionService {
this.elasticService.persistDescription(data);
this.eventBroker.emit(new DescriptionTouchedEvent(data.getId()));
this.annotationEntityTouchedIntegrationEventHandler.handleDescription(data.getId());
}
return this.builderFactory.builder(DescriptionBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fields, Description._id), data);
}
@ -706,6 +651,8 @@ public class DescriptionServiceImpl implements DescriptionService {
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.descriptionAffiliation(id)), Permission.DeleteDescription);
this.deleterFactory.deleter(DescriptionDeleter.class).deleteAndSaveByIds(List.of(id), false);
this.annotationEntityRemovalIntegrationEventHandler.handleDescription(id);
}
//endregion
@ -774,6 +721,8 @@ public class DescriptionServiceImpl implements DescriptionService {
this.elasticService.persistDescription(newDescription);
this.annotationEntityTouchedIntegrationEventHandler.handleDescription(newDescription.getId());
this.annotationEntityTouchedIntegrationEventHandler.handleDescription(existing.getId());
}
//endregion

View File

@ -24,6 +24,7 @@ import eu.eudat.data.*;
import eu.eudat.errorcode.ErrorThesaurusProperties;
import eu.eudat.event.DmpTouchedEvent;
import eu.eudat.event.EventBroker;
import eu.eudat.integrationevent.outbox.annotationentityremoval.AnnotationEntityRemovalIntegrationEventHandler;
import eu.eudat.integrationevent.outbox.annotationentitytouch.AnnotationEntityTouchedIntegrationEventHandler;
import eu.eudat.integrationevent.outbox.dmptouched.DmpTouchedIntegrationEventHandler;
import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEvent;
@ -124,6 +125,7 @@ public class DmpServiceImpl implements DmpService {
private final DmpTouchedIntegrationEventHandler dmpTouchedIntegrationEventHandler;
private final AnnotationEntityTouchedIntegrationEventHandler annotationEntityTouchedIntegrationEventHandler;
private final AnnotationEntityRemovalIntegrationEventHandler annotationEntityRemovalIntegrationEventHandler;
private final AuthorizationContentResolver authorizationContentResolver;
@Autowired
@ -148,7 +150,7 @@ public class DmpServiceImpl implements DmpService {
ValidatorFactory validatorFactory,
ElasticService elasticService,
DmpTouchedIntegrationEventHandler dmpTouchedIntegrationEventHandler,
AnnotationEntityTouchedIntegrationEventHandler annotationEntityTouchedIntegrationEventHandler, AuthorizationContentResolver authorizationContentResolver) {
AnnotationEntityTouchedIntegrationEventHandler annotationEntityTouchedIntegrationEventHandler, AnnotationEntityRemovalIntegrationEventHandler annotationEntityRemovalIntegrationEventHandler, AuthorizationContentResolver authorizationContentResolver) {
this.entityManager = entityManager;
this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory;
@ -170,6 +172,7 @@ public class DmpServiceImpl implements DmpService {
this.elasticService = elasticService;
this.dmpTouchedIntegrationEventHandler = dmpTouchedIntegrationEventHandler;
this.annotationEntityTouchedIntegrationEventHandler = annotationEntityTouchedIntegrationEventHandler;
this.annotationEntityRemovalIntegrationEventHandler = annotationEntityRemovalIntegrationEventHandler;
this.authorizationContentResolver = authorizationContentResolver;
}
@ -202,13 +205,13 @@ public class DmpServiceImpl implements DmpService {
this.dmpTouchedIntegrationEventHandler.handle(DmpTouchedIntegrationEventHandler.buildEventFromPersistModel(model));
this.annotationEntityTouchedIntegrationEventHandler.handle(AnnotationEntityTouchedIntegrationEventHandler.buildEventFromPersistModel(model));
this.sendNotification(data);
this.assignUsers(data.getId(), this.inviteUserOrAssignUsers(data.getId(), model.getUsers(), false), null, false);
this.elasticService.persistDmp(data);
this.annotationEntityTouchedIntegrationEventHandler.handleDmp(data.getId());
return this.builderFactory.builder(DmpBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fields, Dmp._id, Dmp._hash), data);
}
@ -334,6 +337,8 @@ public class DmpServiceImpl implements DmpService {
this.deleterFactory.deleter(DmpDeleter.class).deleteAndSaveByIds(List.of(id), false);
if (previousFinalized != null) this.elasticService.persistDmp(previousFinalized);
this.annotationEntityRemovalIntegrationEventHandler.handleDmp(data.getId());
}
@Override
@ -445,6 +450,9 @@ public class DmpServiceImpl implements DmpService {
this.elasticService.persistDmp(oldDmpEntity);
this.elasticService.persistDmp(newDmp);
this.annotationEntityTouchedIntegrationEventHandler.handleDmp(newDmp.getId());
this.annotationEntityTouchedIntegrationEventHandler.handleDmp(oldDmpEntity.getId());
return this.builderFactory.builder(DmpBuilder.class).build(BaseFieldSet.build(fields, Dmp._id), newDmp);
}
@ -565,9 +573,10 @@ public class DmpServiceImpl implements DmpService {
this.entityManager.flush();
this.elasticService.persistDmp(newDmp);
this.annotationEntityTouchedIntegrationEventHandler.handleDmp(newDmp.getId());
DmpEntity resultingDmpEntity = this.queryFactory.query(DmpQuery.class).ids(newDmp.getId()).firstAs(fields);
return this.builderFactory.builder(DmpBuilder.class).build(fields, resultingDmpEntity);
}
@ -615,6 +624,9 @@ public class DmpServiceImpl implements DmpService {
.collect();
this.elasticService.persistDmp(dmpEntity);
this.annotationEntityTouchedIntegrationEventHandler.handleDmp(dmpEntity.getId());
return this.builderFactory.builder(DmpUserBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fieldSet, DmpUser._id, DmpUser._hash), persisted);
}
@ -636,6 +648,8 @@ public class DmpServiceImpl implements DmpService {
if (dmpEntity == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getDmpId(), Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale()));
this.elasticService.persistDmp(dmpEntity);
this.annotationEntityTouchedIntegrationEventHandler.handleDmp(dmpEntity.getId());
return this.builderFactory.builder(DmpBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fields, Dmp._id, Dmp._hash), data);
}
@ -727,8 +741,6 @@ public class DmpServiceImpl implements DmpService {
return data;
}
private @NotNull DmpBlueprintValueEntity buildDmpBlueprintValueEntity(DmpBlueprintValuePersist persist){
DmpBlueprintValueEntity data = new DmpBlueprintValueEntity();
if (persist == null) return data;
@ -738,8 +750,6 @@ public class DmpServiceImpl implements DmpService {
return data;
}
private @NotNull List<DmpReferencePersist> buildDmpReferencePersists(DmpPropertiesPersist persist){
List<DmpReferencePersist> dmpReferencePersists = new ArrayList<>();
if (persist.getDmpBlueprintValues() != null && !persist.getDmpBlueprintValues().isEmpty()){
@ -839,7 +849,6 @@ public class DmpServiceImpl implements DmpService {
this.entityManager.flush();
}
private void patchAndSaveTemplates(UUID id, List<DmpDescriptionTemplatePersist> models) throws InvalidApplicationException {
if (models == null) models = new ArrayList<>();
List<DmpDescriptionTemplateEntity> items = this.queryFactory.query(DmpDescriptionTemplateQuery.class).isActive(IsActive.Active).dmpIds(id).collect();
@ -942,6 +951,8 @@ public class DmpServiceImpl implements DmpService {
this.entityManager.flush();
this.elasticService.persistDmp(dmp);
this.annotationEntityTouchedIntegrationEventHandler.handleDmp(dmp.getId());
this.sendNotification(dmp);
}
@ -964,12 +975,16 @@ public class DmpServiceImpl implements DmpService {
this.updateVersionStatusAndSave(dmp, DmpStatus.Finalized, dmp.getStatus());
this.entityManager.flush();
this.annotationEntityTouchedIntegrationEventHandler.handleDmp(dmp.getId());
this.sendNotification(dmp);
}
// invites
public void inviteUserOrAssignUsers(UUID id, List<DmpUserPersist> users) throws InvalidApplicationException, JAXBException, IOException {
this.inviteUserOrAssignUsers(id, users, true);
this.annotationEntityTouchedIntegrationEventHandler.handleDmp(id);
}
@ -1074,7 +1089,6 @@ public class DmpServiceImpl implements DmpService {
return persist.getToken();
}
public void dmpInvitationAccept(String token) {
ActionConfirmationEntity action = this.queryFactory.query(ActionConfirmationQuery.class).tokens(token).types(ActionConfirmationType.DmpInvitation).isActive(IsActive.Active).first();
@ -1102,6 +1116,8 @@ public class DmpServiceImpl implements DmpService {
action.setStatus(ActionConfirmationStatus.Accepted);
this.entityManager.merge(action);
this.annotationEntityTouchedIntegrationEventHandler.handleDmp(dmpInvitation.getDmpId());
}
}

View File

@ -10,7 +10,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.2</version>
<version>3.2.1</version>
</parent>
<modules>
@ -41,7 +41,7 @@
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20230227</version>
<version>20240303</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
@ -92,7 +92,7 @@
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.6.0</version>
<version>7.17.19</version>
</dependency>
<dependency>
<groupId>org.hibernate.orm</groupId>
@ -109,7 +109,7 @@
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.4.0</version>
<version>2.9.0</version>
</dependency>
@ -153,24 +153,8 @@
<version>3.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.xmlgraphics/fop -->
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>fop</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
@ -190,11 +174,6 @@
<version>1.0.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/fr.opensagres.xdocreport/fr.opensagres.xdocreport.itext.extension -->
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.itext.extension</artifactId>
<version>2.0.4</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
@ -356,18 +335,18 @@
<dependency>
<groupId>gr.cite</groupId>
<artifactId>queue-inbox</artifactId>
<version>1.0.0</version>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>gr.cite</groupId>
<artifactId>queue-outbox</artifactId>
<version>1.0.0</version>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>gr.cite</groupId>
<artifactId>rabbitmq-core</artifactId>
<version>1.0.0</version>
<version>2.1.2</version>
</dependency>
</dependencies>

View File

@ -16,7 +16,7 @@ spring:
dialect: org.hibernate.dialect.PostgreSQLDialect
hibernate:
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
physical-strategy: eu.eudat.configurations.db.PrefixPhysicalNamingStrategy
implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
datasource:
url: ${DB_URL:}
@ -29,3 +29,6 @@ spring:
maximum-pool-size: 10
idle-timeout: 600000
max-lifetime: 1800000
naming-strategy:
prefix:

View File

@ -35,7 +35,8 @@ queue:
user-removal-topic: user.remove
dmp-touch-topic: dmp.touch
description-touch-topic: description.touch
annotation-entity-touch-topic: annotation.entity.touch
annotation-entities-touch-topic: annotation.entities.touch
annotation-entities-removal-topic: annotation.entities.remove
what-you-know-about-me-completed-topic: whatyouknowaboutme.completed
generate-file-topic: generate.file
rabbitmq:

View File

@ -10,7 +10,7 @@ BEGIN
exchange character varying(200) COLLATE pg_catalog."default" NOT NULL,
route character varying(200) COLLATE pg_catalog."default" NOT NULL,
message_id uuid NOT NULL,
notify_status character varying(100) COLLATE pg_catalog."default" NOT NULL,
notify_status smallint NOT NULL,
retry_count integer NOT NULL,
published_at timestamp without time zone,
confirmed_at timestamp without time zone,

View File

@ -7,14 +7,14 @@ BEGIN
CREATE TABLE public."QueueInbox"
(
id uuid NOT NULL,
queue character varying(50) COLLATE pg_catalog."default" NOT NULL,
exchange character varying(50) COLLATE pg_catalog."default" NOT NULL,
route character varying(50) COLLATE pg_catalog."default" NOT NULL,
queue character varying(200) COLLATE pg_catalog."default" NOT NULL,
exchange character varying(200) COLLATE pg_catalog."default" NOT NULL,
route character varying(200) COLLATE pg_catalog."default" NOT NULL,
application_id character varying(100) COLLATE pg_catalog."default" NOT NULL,
message_id uuid NOT NULL,
message json NOT NULL,
retry_count integer,
status character varying(50) COLLATE pg_catalog."default" NOT NULL,
status smallint NOT NULL,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL,
tenant uuid,

View File

@ -7,14 +7,14 @@ BEGIN
CREATE TABLE public."ntf_QueueInbox"
(
id uuid NOT NULL,
queue character varying(50) COLLATE pg_catalog."default" NOT NULL,
exchange character varying(50) COLLATE pg_catalog."default" NOT NULL,
route character varying(50) COLLATE pg_catalog."default" NOT NULL,
queue character varying(200) COLLATE pg_catalog."default" NOT NULL,
exchange character varying(200) COLLATE pg_catalog."default" NOT NULL,
route character varying(200) COLLATE pg_catalog."default" NOT NULL,
application_id character varying(100) COLLATE pg_catalog."default" NOT NULL,
message_id uuid NOT NULL,
message json NOT NULL,
retry_count integer,
status character varying(50) COLLATE pg_catalog."default" NOT NULL,
status smallint NOT NULL,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL,
tenant uuid,

View File

@ -10,7 +10,7 @@ BEGIN
exchange character varying(200) COLLATE pg_catalog."default" NOT NULL,
route character varying(200) COLLATE pg_catalog."default" NOT NULL,
message_id uuid NOT NULL,
notify_status character varying(100) COLLATE pg_catalog."default" NOT NULL,
notify_status smallint NOT NULL,
retry_count integer NOT NULL,
published_at timestamp without time zone,
confirmed_at timestamp without time zone,

View File

@ -7,14 +7,14 @@ BEGIN
CREATE TABLE public."ant_QueueInbox"
(
id uuid NOT NULL,
queue character varying(50) COLLATE pg_catalog."default" NOT NULL,
exchange character varying(50) COLLATE pg_catalog."default" NOT NULL,
route character varying(50) COLLATE pg_catalog."default" NOT NULL,
queue character varying(200) COLLATE pg_catalog."default" NOT NULL,
exchange character varying(200) COLLATE pg_catalog."default" NOT NULL,
route character varying(200) COLLATE pg_catalog."default" NOT NULL,
application_id character varying(100) COLLATE pg_catalog."default" NOT NULL,
message_id uuid NOT NULL,
message json NOT NULL,
retry_count integer,
status character varying(50) COLLATE pg_catalog."default" NOT NULL,
status smallint NOT NULL,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL,
tenant uuid,

View File

@ -10,7 +10,7 @@ BEGIN
exchange character varying(200) COLLATE pg_catalog."default" NOT NULL,
route character varying(200) COLLATE pg_catalog."default" NOT NULL,
message_id uuid NOT NULL,
notify_status character varying(100) COLLATE pg_catalog."default" NOT NULL,
notify_status smallint NOT NULL,
retry_count integer NOT NULL,
published_at timestamp without time zone,
confirmed_at timestamp without time zone,

View File

@ -47,6 +47,7 @@ import { InAppNotificationService } from './services/inapp-notification/inapp-no
import { NotificationService } from './services/notification/notification-service';
import { SemanticsService } from './services/semantic/semantics.service';
import { PrefillingSourceService } from './services/prefilling-source/prefilling-source.service';
import { VisibilityRulesService } from '@app/ui/description/editor/description-form/visibility-rules/visibility-rules.service';
//
//
// This is shared module that provides all the services. Its imported only once on the AppModule.
@ -112,7 +113,8 @@ export class CoreServiceModule {
InAppNotificationService,
NotificationService,
SemanticsService,
PrefillingSourceService
PrefillingSourceService,
VisibilityRulesService
],
};
}

View File

@ -13,6 +13,7 @@ import { DmpAssociatedUser, User } from "../user/user";
import { DmpReference } from './dmp-reference';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { AppPermission } from '@app/core/common/enum/permission.enum';
import { EntityType } from '@app/core/common/enum/entity-type';
export interface Dmp extends BaseEntity {
label?: string;
@ -166,6 +167,7 @@ export interface PublicDmp extends BaseEntity {
dmpReferences: PublicDmpReference[];
dmpUsers: PublicDmpUser[];
descriptions: PublicDescription[];
entityDois: PublicEntityDoi[];
}
export interface PublicDmpReference {
@ -201,3 +203,10 @@ export interface PublicUser {
name: string;
}
export interface PublicEntityDoi {
id: Guid;
entityType: EntityType;
entityId: Guid;
repositoryId: string;
doi: string;
}

View File

@ -1,7 +1,8 @@
<div class="row" *ngFor="let ruleFormGroup of form['controls'] let i=index;" [formGroup]="ruleFormGroup">
<span class="col-auto align-self-center">{{i + 1}}</span>
<app-description-template-editor-default-value-component class="col align-self-center" [fieldType]="fieldTypeForCheck" [form]="ruleFormGroup.get('value')" [formArrayOptions]="formArrayOptionsForCheck" placeHolder="{{'DESCRIPTION-TEMPLATE-EDITOR.STEPS.FORM.RULE.FIELDS.RULE-IF'| translate}}" required="true"></app-description-template-editor-default-value-component>
<app-description-template-editor-default-value-component *ngIf="isTextType(fieldTypeForCheck)" class="col align-self-center" [fieldType]="fieldTypeForCheck" [form]="ruleFormGroup.get('textValue')" [formArrayOptions]="formArrayOptionsForCheck" placeHolder="{{'DESCRIPTION-TEMPLATE-EDITOR.STEPS.FORM.RULE.FIELDS.RULE-IF'| translate}}" required="true"></app-description-template-editor-default-value-component>
<app-description-template-editor-default-value-component *ngIf="isDateType(fieldTypeForCheck)" class="col align-self-center" [fieldType]="fieldTypeForCheck" [form]="ruleFormGroup.get('dateValue')" [formArrayOptions]="formArrayOptionsForCheck" placeHolder="{{'DESCRIPTION-TEMPLATE-EDITOR.STEPS.FORM.RULE.FIELDS.RULE-IF'| translate}}" required="true"></app-description-template-editor-default-value-component>
<!-- SELECTION -->
<mat-form-field class="col align-self-center">

View File

@ -39,6 +39,30 @@ export class DescriptionTemplateEditorRuleComponent implements OnInit {
}
isTextType(type: DescriptionTemplateFieldType){
return type == DescriptionTemplateFieldType.FREE_TEXT || type == DescriptionTemplateFieldType.CHECK_BOX ||
type == DescriptionTemplateFieldType.TEXT_AREA || type == DescriptionTemplateFieldType.RICH_TEXT_AREA ||
type == DescriptionTemplateFieldType.BOOLEAN_DECISION || type == DescriptionTemplateFieldType.RADIO_BOX ||
type == DescriptionTemplateFieldType.CURRENCY || type == DescriptionTemplateFieldType.SELECT;
}
isTextListType(type: DescriptionTemplateFieldType){
return type == DescriptionTemplateFieldType.TAGS || type == DescriptionTemplateFieldType.INTERNAL_ENTRIES_DMPS ||
type == DescriptionTemplateFieldType.INTERNAL_ENTRIES_DESCRIPTIONS;
}
isDateType(type: DescriptionTemplateFieldType){
return type == DescriptionTemplateFieldType.DATE_PICKER;
}
isReferenceType(type: DescriptionTemplateFieldType){
return type == DescriptionTemplateFieldType.REFERENCE_TYPES;
}
isExternalIdentifierType(type: DescriptionTemplateFieldType){
return type == DescriptionTemplateFieldType.VALIDATION || type == DescriptionTemplateFieldType.DATASET_IDENTIFIER;;
}
targetValidation() {
//TODO
}

View File

@ -139,9 +139,9 @@
<!-- <mat-form-field>
<input matInput #email placeholder="{{'DESCRIPTION-TEMPLATE-EDITOR.STEPS.GENERAL-INFO.DATASET-TEMPLATE-USERS'| translate}}" (focus)="onUserFieldFocus()" (blur)="onUserFieldBlur()" (keyup.enter)="addUser(email)">
</mat-form-field> -->
<mat-form-field class="full-width basic-info-input">
<mat-form-field class="full-width basic-info-input" *ngIf="!formGroup.disabled">
<mat-label>{{'DESCRIPTION-TEMPLATE-EDITOR.STEPS.GENERAL-INFO.DATASET-TEMPLATE-USERS' | translate}}</mat-label>
<app-single-auto-complete [disabled]="formGroup.disabled" [required]="false" [formControl]="userFormControl" (optionSelected)="addUser($event)" placeholder="{{'DESCRIPTION-TEMPLATE-EDITOR.STEPS.GENERAL-INFO.DATASET-TEMPLATE-USERS' | translate}}" [configuration]="userService.singleAutocompleteConfiguration">
<app-single-auto-complete [required]="false" [formControl]="userFormControl" (optionSelected)="addUser($event)" placeholder="{{'DESCRIPTION-TEMPLATE-EDITOR.STEPS.GENERAL-INFO.DATASET-TEMPLATE-USERS' | translate}}" [configuration]="userService.singleAutocompleteConfiguration">
</app-single-auto-complete>
<mat-error *ngIf="formGroup.get('type').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>

View File

@ -951,7 +951,7 @@ export class DescriptionTemplateRuleEditorModel implements DescriptionTemplateRu
validationErrorModel
});
['target', 'value'].forEach(keyField => {
['target', 'textValue', 'textListValue', 'dateValue'].forEach(keyField => {
const control = formGroup?.get(keyField);
control?.clearValidators();
control?.addValidators(context.getValidation(keyField).validators);

View File

@ -13,10 +13,10 @@
</div>
<div *ngIf="!fieldSet?.multiplicity?.tableView" class="col-12">
<div class="row" *ngFor="let fieldSetItemPropertiesControl of propertiesFormGroup?.get('items')?.controls">
<div class="col" *ngIf="visibilityRulesService.isVisibleMap[fieldSet.id + '_' + fieldSetItemPropertiesControl.get('ordinal').value]">
<div class="col" *ngIf="visibilityRulesService && visibilityRulesService.isVisibleMap[fieldSet.id + '_' + fieldSetItemPropertiesControl.get('ordinal').value]">
<div class="row">
<div *ngFor="let field of fieldSet.fields; let i = index;" class="col-12 compositeField">
<ng-container *ngIf="visibilityRulesService.isVisibleMap[field.id + '_' + fieldSetItemPropertiesControl.get('ordinal').value]">
<ng-container *ngIf="visibilityRulesService && visibilityRulesService.isVisibleMap[field.id + '_' + fieldSetItemPropertiesControl.get('ordinal').value]">
<div class="row">
<h5 *ngIf="placeholderTitle" class="col-auto font-weight-bold">{{field.label}}</h5>
</div>
@ -48,9 +48,9 @@
<th class="actions"></th>
</tr>
<ng-container *ngFor="let fieldSetItemPropertiesControl of propertiesFormGroup?.get('items')?.controls; let j = index">
<tr *ngIf="visibilityRulesService.isVisibleMap[fieldSet.id + '_' + fieldSetItemPropertiesControl.get('ordinal').value]">
<tr *ngIf="visibilityRulesService && visibilityRulesService.isVisibleMap[fieldSet.id + '_' + fieldSetItemPropertiesControl.get('ordinal').value]">
<td *ngFor="let field of fieldSet.fields;" class="text-wrap">
<ng-container *ngIf="visibilityRulesService.isVisibleMap[field.id + '_' + fieldSetItemPropertiesControl.get('ordinal').value]">
<ng-container *ngIf="visibilityRulesService && visibilityRulesService.isVisibleMap[field.id + '_' + fieldSetItemPropertiesControl.get('ordinal').value]">
{{fieldSetItemPropertiesControl.get('fields').get(field.id).get('value').getRawValue()}}
</ng-container>
</td>

View File

@ -1,456 +0,0 @@
// import { ApplicationRef, Injectable, NgZone } from '@angular/core';
// import { AbstractControl, UntypedFormArray, UntypedFormGroup } from '@angular/forms';
// import { isNumeric } from '@app/utilities/enhancers/utils';
// import { Observable, Subject } from 'rxjs';
// import { VisibilityRule } from './models/visibility-rule';
// import { VisibilityRuleSource } from './models/visibility-rule-source';
// import { VisibilityRulesContext } from './models/visibility-rules-context';
// import { DescriptionTemplate, DescriptionTemplatePage, DescriptionTemplateSection } from '@app/core/model/description-template/description-template';
// import { Rule } from './models/rule';
// @Injectable()
// export class VisibilityRulesService {
// private readonly VISIBILITY_RULE_LOGIC: 'OR' | 'AND' = 'OR';
// private readonly DEFAULTVISIBILITY = false;
// private visibilityRuleContext: VisibilityRulesContext;
// private form: AbstractControl;
// public isVisibleMap: { [key: string]: boolean } = {};
// private elementVisibilityMapSubject = new Subject<{ [key: string]: boolean }>();
// private elementComputationalMap = new Map<String, Map<String, boolean>>(); /// keep saved the values of each form control validity value
// private _changeMade$ = new Subject<void>();
// // get isVisibleMap(): MapWithDefault {
// // // console.log('isVisibleMap');
// // return this.elementVisibilityMap;
// // }; /// keep saved the values of each form control validity value
// constructor(
// public applicationReference: ApplicationRef,
// public ngZone: NgZone
// ) {
// }
// getElementVisibilityMapObservable(): Observable<{ [key: string]: boolean }> {
// // this.isVisibleMap
// // console.log('getElementVisibilityMapObservable: ');
// return this.elementVisibilityMapSubject.asObservable();
// }
// public checkElementVisibility(id: string): boolean {
// //console.log('checkElementVisibility: ' + id);
// return true;
// // if (this.visibilityRuleContext.rules.filter(item => item.targetControlId === id).length === 0) { return true; }
// // console.log(this.elementVisibilityMap.has(id) ? this.elementVisibilityMap.get(id) : false);
// // return this.elementVisibilityMap.has(id) ? this.elementVisibilityMap.get(id) : false;
// }
// public buildVisibilityRules(item: Array<Rule>, form: AbstractControl) {
// this.visibilityRuleContext = new VisibilityRulesContext();
// this.visibilityRuleContext.buildVisibilityRuleContext(item || []);
// this.form = form;
// this.resetVisibilityRules();
// }
// public updateValueAndVisibility(id: string, value: any) {
// //console.log('updateValueAndVisibility: ' + id + ' value: ' + value);
// const visibilityRules = this.visibilityRuleContext.rules.filter(item => item.sourceVisibilityRules.filter(source => source.sourceControlId === id).length > 0);
// if (visibilityRules.length > 0) {
// visibilityRules.forEach(item => this.evaluateVisibility(item, value, id));
// this.elementVisibilityMapSubject.next(this.isVisibleMap);
// }
// }
// private evaluateVisibility(visibilityRule: VisibilityRule, value: any, sourceId: string) {// source controlId is the same
// //console.log('evaluateVisibility: ' + visibilityRule + ' value: ' + value + ' sourceId: ' + sourceId);
// const targetId = visibilityRule.targetControlId;
// const visibilityMap = this.elementComputationalMap.get(targetId) ? this.elementComputationalMap.get(targetId) : new Map<String, boolean>();
// if (value instanceof Array) {
// const parsedSourceControlValues = visibilityRule.sourceVisibilityRules.map(e => this.parseValue(e.sourceControlValue));
// const parsedValues = value.map(e => this.parseValue(e));
// const isVisible = parsedValues.map(v => parsedSourceControlValues.includes(v)).reduce((acc, current) => acc || current, false);
// // if(isVisible){
// // this._emitChangesIfNeeded(visibilityRule.targetControlId, true);
// // this.elementVisibilityMap.set(visibilityRule.targetControlId, true);
// // return;
// // }
// visibilityMap.set(sourceId, isVisible);
// } else {
// const visibilityDependencySource = visibilityRule.sourceVisibilityRules.filter(x => x.sourceControlId === sourceId);
// const shouldBeVisible = visibilityDependencySource.reduce((isVisible, x) => {
// const shouldBeHidden = value !== null && (this.parseValue(value) !== this.parseValue(x.sourceControlValue));
// return this.VISIBILITY_RULE_LOGIC === 'OR' ? (isVisible || !shouldBeHidden) : (isVisible && !shouldBeHidden);
// // if(value !== null && )
// }, this.VISIBILITY_RULE_LOGIC === 'AND');
// visibilityMap.set(sourceId, shouldBeVisible);
// }
// this.elementComputationalMap.set(targetId, visibilityMap);// unnessecary
// const isVisible = this._computeVisibility(targetId);
// this._emitChangesIfNeeded(targetId, isVisible);
// const previousVisibility = this.isVisibleMap[targetId];
// this.isVisibleMap[targetId] = isVisible;
// if (!isVisible && previousVisibility !== isVisible) {
// this.resetControlWithId(this.form, targetId);
// }
// // for (let i = 0; i < visibilityRule.sourceVisibilityRules.length; i++) {
// // if (value != null && (this.parseValue(value) !== this.parseValue(visibilityRule.sourceVisibilityRules[i].sourceControlValue))) {
// // this._emitChangesIfNeeded(visibilityRule.targetControlId, false);
// // this.elementVisibilityMap.set(visibilityRule.targetControlId, false);
// // this.resetControlWithId(this.form, visibilityRule.targetControlId);
// // //this.updateValueAndVisibility(visibilityRule.targetControlId, null);
// // // this.clearValues(targetPathKey);
// // return;
// // }
// // }
// // this._emitChangesIfNeeded(visibilityRule.targetControlId, true);
// // this.elementVisibilityMap.set(visibilityRule.targetControlId, true);
// // this.updateValueAndVisibility(visibilityRule.targetControlId, null);
// }
// private _computeVisibility(targetId: string): boolean {
// //console.log('_computeVisibility: ' + targetId);
// const visibilityMap = this.elementComputationalMap.get(targetId);
// const values = visibilityMap.values();
// let currentVal = values.next();
// let visibilityValues: boolean[] = [];
// while (!currentVal.done) {
// visibilityValues.push(currentVal.value);
// currentVal = values.next();
// }
// if (visibilityValues.length) {
// return visibilityValues.reduce((r, c) => {
// if (this.VISIBILITY_RULE_LOGIC === 'OR') {
// return r || c;
// } else {
// return r && c;
// }
// }, visibilityValues[0]);
// }
// return this.DEFAULTVISIBILITY;
// }
// private resetVisibilityRules() {
// //console.log('resetVisibilityRules: ');
// this.isVisibleMap = {};
// this.elementComputationalMap.clear();
// this.elementComputationalMap = new Map<String, Map<String, boolean>>();
// this._populateComputationMap(); /// !IMPORTANT FOR THE AND LOGIC
// this._changeMade$.next();
// }
// private _populateComputationMap(): void {
// //console.log('_populateComputationMap: ');
// // this.visibilityRuleContext.rules.forEach(rule => {
// // const targetId = rule.targetControlId;
// // const visibilityMap = this.elementComputationalMap.get(targetId) ? this.elementComputationalMap.get(targetId) : new Map<String, boolean>();
// // rule.sourceVisibilityRules.forEach(vr => {
// // visibilityMap.set(vr.sourceControlId, this.DEFAULTVISIBILITY);
// // });
// // this.elementComputationalMap.set(targetId, visibilityMap);
// // });
// }
// parseValue(value: any) {
// if (typeof value === 'string') {
// if (isNumeric(value)) { return value; }
// else if (value === 'true') {
// return true;
// }
// else if (value === 'false') {
// return false;
// }
// else { return this.translate(value); }
// } else { return value; }
// }
// search(path, obj, target) {
// for (const k in obj) {
// if (obj.hasOwnProperty(k)) {
// if (obj[k] === target) {
// return path + '.' + k;
// } else if (typeof obj[k] === 'object') {
// const result = this.search(path + '.' + k, obj[k], target);
// if (result) {
// return result;
// }
// }
// }
// }
// return false;
// }
// scanIfChildsOfCompositeFieldHasVisibleItems(compositeFieldParent: UntypedFormGroup): boolean {
// // console.log('scanIfChildsOfCompositeFieldHasVisibleItems: ' + compositeFieldParent);
// //TODO: implement this
// return true;
// // let isVisible = false;
// // (<UntypedFormArray>(compositeFieldParent.get('fields'))).controls.forEach(element => {
// // if (this.checkElementVisibility(element.get('id').value)) {
// // isVisible = true;
// // }
// // });
// // return isVisible;
// }
// private translate(item: any) {
// try {
// return JSON.parse(item).value;
// } catch (error) {
// return item;
// }
// }
// private resetControlWithId(formControl: AbstractControl, id: string) {
// //'resetControlWithId: ' + id);
// //TODO: implement this
// // if (formControl instanceof UntypedFormGroup) {
// // if ((formControl as UntypedFormGroup).contains('id') && (formControl as UntypedFormGroup).contains('value') && (formControl as UntypedFormGroup).get('id').value === id) {
// // this.resetFieldFormGroup(formControl);
// // } if ((formControl as UntypedFormGroup).contains('id') && (formControl as UntypedFormGroup).contains('fields') && (formControl as UntypedFormGroup).get('id').value === id) {
// // this.resetCompositeFieldFormGroup(formControl);
// // } else {
// // Object.keys(formControl.controls).forEach(item => {
// // const control = formControl.get(item);
// // this.resetControlWithId(control, id);
// // });
// // }
// // } else if (formControl instanceof UntypedFormArray) {
// // formControl.controls.forEach(item => {
// // this.resetControlWithId(item, id);
// // });
// // }
// }
// private resetFieldFormGroup(formGroup: UntypedFormGroup) {
// //console.log('resetFieldFormGroup: ' + formGroup);
// //TODO: implement this
// // const renderStyle = formGroup.getRawValue().viewStyle.renderStyle;
// // if (renderStyle === DatasetProfileFieldViewStyle.Validation || renderStyle === DatasetProfileFieldViewStyle.DatasetIdentifier) {
// // formGroup.get('value').setValue({ identifier: '', type: '' });
// // } else {
// // formGroup.get('value').setValue(formGroup.get('defaultValue').value ? this.parseValue(formGroup.get('defaultValue').value.value) : undefined);
// // }
// }
// private resetCompositeFieldFormGroup(formGroup: UntypedFormGroup) {
// //console.log('resetCompositeFieldFormGroup: ' + formGroup);
// //TODO: implement this
// // (formGroup.get('fields') as UntypedFormArray).controls.forEach((element: UntypedFormGroup) => {
// // this.resetFieldFormGroup(element);
// // });
// // (formGroup.get('multiplicityItems') as UntypedFormArray).controls.splice(0);
// }
// private _emitChangesIfNeeded(id: string, valueToBeSet: boolean) {
// if (this.isVisibleMap[id]) {
// if (this.isVisibleMap[id] != valueToBeSet) {
// this._changeMade$.next();
// }
// } else {
// this._changeMade$.next();
// }
// }
// public get visibilityChange() {
// return this._changeMade$.asObservable();
// }
// public getVisibilityDependency(targetId: string): VisibilityRuleSource[] | null {
// //console.log('getVisibilityDependency: ' + targetId);
// return this.visibilityRuleContext.rules.reduce((hasDependency, rule) => {
// if (hasDependency) return hasDependency;
// if (rule.targetControlId === targetId) {
// return rule.sourceVisibilityRules;
// }
// return null;
// }, null) as VisibilityRuleSource[];
// }
// public getVisibilityTargets(sourceId: string): string[] {
// console.log('getVisibilityTargets: ' + sourceId);
// return this.visibilityRuleContext.rules.filter(x => {
// const result = x.sourceVisibilityRules.filter(y => y.sourceControlId === sourceId);
// return result.length;
// }).map(x => x.targetControlId);
// }
// // public appendVisibilityRule(rule: VisibilityRule): void{
// // const existingTargetRule = this.visibilityRuleContext.rules.find( r => r.targetControlId === rule.targetControlId);
// // if(existingTargetRule){
// // rule.sourceVisibilityRules.forEach(svr =>{
// // existingTargetRule.sourceVisibilityRules.push(svr);
// // });
// // }else{
// // this.visibilityRuleContext.rules.push(rule);
// // }
// // }
// //removes rule that has the specific id either as a source either as a target
// public removeAllIdReferences(id: string): void {
// //console.log('removeAllIdReferences: ' + id);
// // * Remove from visibility rues and visibility rules context
// //remove as a target
// const temp = this.visibilityRuleContext.rules.map((x, i) => (x.targetControlId === id) ? i : null);
// const indexes = temp.filter(x => x !== null);
// indexes.reverse().forEach(index => this.visibilityRuleContext.rules.splice(index, 1));
// this.isVisibleMap[id] = undefined;
// //remove as a source
// const tbd = this.visibilityRuleContext.rules.reduce((to_be_deleted, rule, ruleIdx) => {
// const idxs = rule.sourceVisibilityRules.map((x, i) => (x.sourceControlId === id) ? i : null).filter(x => x !== null);
// idxs.reverse().forEach(index => rule.sourceVisibilityRules.splice(index, 1));
// if (!rule.sourceVisibilityRules.length) {
// to_be_deleted.push(ruleIdx);
// }
// return to_be_deleted
// }, []);
// //clean up empty
// tbd.reverse().forEach(index => {
// this.visibilityRuleContext.rules.splice(index, 1);
// });
// // * Remove from computational map
// // as a target
// if (this.elementComputationalMap.get(id)) {
// this.elementComputationalMap.delete(id);
// }
// // as a source
// const keyIterator = this.elementComputationalMap.keys();
// let currentKey = keyIterator.next();
// while (!currentKey.done) {
// const currentVals = this.elementComputationalMap.get(currentKey.value);
// currentVals.delete(id);
// currentKey = keyIterator.next();
// }
// }
// public addNewRule(rule: Rule, currentVisibility = this.DEFAULTVISIBILITY): void {
// //console.log('addNewRule: ' + rule + ' currentVisibility: ' + currentVisibility);
// const targetId = rule.targetField;
// const sourceId = rule.sourceField;
// this.visibilityRuleContext.addToVisibilityRulesContext(rule);
// let visibilityMap = this.elementComputationalMap.get(targetId);
// if (!visibilityMap) {
// visibilityMap = new Map<String, boolean>();
// this.elementComputationalMap.set(targetId, visibilityMap);
// }
// visibilityMap.set(sourceId, currentVisibility);
// const isVisible = this._computeVisibility(targetId);
// this._emitChangesIfNeeded(targetId, isVisible);
// this.isVisibleMap[targetId] = isVisible;
// this.elementVisibilityMapSubject.next(this.isVisibleMap);
// }
// /**
// * Check what sourceId hides or shows the target field
// * return true if no rule found
// */
// public checkTargetVisibilityProvidedBySource(sourceId: string, targetId: string): boolean {
// //console.log('checkTargetVisibilityProvidedBySource: ' + sourceId + ' targetId: ' + targetId);
// const computationalMap = this.elementComputationalMap.get(targetId);
// if (computationalMap) {
// return !!computationalMap.get(sourceId);
// }
// return true;
// }
// public getVisibilityRulesFromDescriptionTempalte(descriptionTemplate: DescriptionTemplate): Rule[] {
// //console.log('getVisibilityRulesFromDescriptionTempalte: ' + descriptionTemplate);
// const result: Rule[] = this.getVisibilityRulesFromDescriptionTempalteSections(descriptionTemplate?.definition?.pages);
// return result;
// }
// public getVisibilityRulesFromDescriptionTempalteSections(pages: DescriptionTemplatePage[]): Rule[] {
// //console.log('getVisibilityRulesFromDescriptionTempalteSections: ' + sections);
// const result: Rule[] = [];
// pages.forEach(page => {
// page?.sections?.forEach(section => {
// if (section.sections != null) { result.push(...this.getVisibilityRulesFromDescriptionTempalteSections(section.sections)); };
// section?.fieldSets?.forEach(fieldSet => {
// fieldSet?.fields?.forEach(field => {
// field.visibilityRules?.forEach(visibilityRule => {
// result.push({
// sourceField: field.id.toString(),
// targetField: visibilityRule.target,
// dateValue: visibilityRule.dateValue,
// textValue: visibilityRule.textValue,
// textListValue: visibilityRule.textListValue
// })
// });
// });
// });
// });
// });
// return result;
// }
// }
// class MapWithDefault extends Map<string, boolean> {
// get(key) {
// //console.log('MapWithDefault');
// if (!this.has(key)) {
// this.set(key, true);
// }
// return super.get(key);
// }
// }

View File

@ -263,7 +263,7 @@ export class VisibilityRulesService {
const fieldType: DescriptionTemplateFieldType = rule.field != null && rule.field.data != null ? rule.field.data.fieldType : DescriptionTemplateFieldType.FREE_TEXT;
if ([DescriptionTemplateFieldType.FREE_TEXT, DescriptionTemplateFieldType.CHECK_BOX, DescriptionTemplateFieldType.TEXT_AREA,
DescriptionTemplateFieldType.RICH_TEXT_AREA, DescriptionTemplateFieldType.UPLOAD, DescriptionTemplateFieldType.BOOLEAN_DECISION,
DescriptionTemplateFieldType.RADIO_BOX, DescriptionTemplateFieldType.CURRENCY, DescriptionTemplateFieldType.SELECT].includes(fieldType) && field.textValue != null && field.textValue.length > 0) {
DescriptionTemplateFieldType.RADIO_BOX, DescriptionTemplateFieldType.CURRENCY].includes(fieldType) && field.textValue != null && field.textValue.length > 0) {
if (DescriptionTemplateFieldType.UPLOAD == fieldType){
return false; //not apply visibility logic
} else {

View File

@ -71,10 +71,10 @@ public class InAppNotificationController {
@PostMapping("query")
public QueryResult<InAppNotification> Query(@RequestBody InAppNotificationLookup lookup) throws MyApplicationException, MyForbiddenException, InvalidApplicationException {
logger.debug("querying {}", InAppNotification.class.getSimpleName());
this.censorFactory.censor(InAppNotificationCensor.class).censor(lookup.getProject());
UUID userId = this.userScope.getUserId();
this.censorFactory.censor(InAppNotificationCensor.class).censor(lookup.getProject(), userId);
if (userId == null) throw new MyForbiddenException(this.errors.getNonPersonPrincipal().getCode(), this.errors.getNonPersonPrincipal().getMessage());
InAppNotificationQuery query = lookup.enrich(this.queryFactory).userId(userId);
List<InAppNotificationEntity> data = query.collectAs(lookup.getProject());
@ -88,12 +88,13 @@ public class InAppNotificationController {
@GetMapping("{id}")
@Transactional
public InAppNotification Get(@PathVariable UUID id, FieldSet fieldSet, Locale locale) throws MyApplicationException, MyForbiddenException, MyNotFoundException {
public InAppNotification Get(@PathVariable UUID id, FieldSet fieldSet, Locale locale) throws MyApplicationException, MyForbiddenException, MyNotFoundException, InvalidApplicationException {
logger.debug(new MapLogEntry("retrieving" + InAppNotification.class.getSimpleName()).And("id", id).And("fields", fieldSet));
this.censorFactory.censor(InAppNotificationCensor.class).censor(fieldSet);
UUID userId = this.userScope.getUserId();
this.censorFactory.censor(InAppNotificationCensor.class).censor(fieldSet, userId);
InAppNotificationQuery query = this.queryFactory.query(InAppNotificationQuery.class).authorize(AuthorizationFlags.OwnerOrPermission).ids(id);
InAppNotificationQuery query = this.queryFactory.query(InAppNotificationQuery.class).authorize(AuthorizationFlags.OwnerOrPermission).userId(userId).ids(id);
InAppNotification model = this.builderFactory.builder(InAppNotificationBuilder.class).authorize(AuthorizationFlags.OwnerOrPermission).build(fieldSet, query.firstAs(fieldSet));
if (model == null)
throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{id, Notification.class.getSimpleName()}, LocaleContextHolder.getLocale()));
@ -142,7 +143,7 @@ public class InAppNotificationController {
UUID userId = this.userScope.getUserId();
if (userId == null) throw new MyForbiddenException(this.errors.getNonPersonPrincipal().getCode(), this.errors.getNonPersonPrincipal().getMessage());
this.censorFactory.censor(InAppNotificationCensor.class).censor(new BaseFieldSet(InAppNotification.Field.ID));
this.censorFactory.censor(InAppNotificationCensor.class).censor(new BaseFieldSet(InAppNotification.Field.ID), userId);
InAppNotificationQuery query = this.queryFactory.query(InAppNotificationQuery.class).isActive(IsActive.Active).trackingState(NotificationInAppTracking.STORED).userId(userId);
int count = Math.toIntExact(query.count());

View File

@ -66,7 +66,7 @@ public class UserNotificationPreferenceController {
public QueryResult<UserNotificationPreference> query(@RequestBody UserNotificationPreferenceLookup lookup) throws MyApplicationException, MyForbiddenException {
logger.debug("querying {}", UserNotificationPreference.class.getSimpleName());
this.censorFactory.censor(UserNotificationPreferenceCensor.class).censor(lookup.getProject());
this.censorFactory.censor(UserNotificationPreferenceCensor.class).censor(lookup.getProject(), null);
UserNotificationPreferenceQuery query = lookup.enrich(this.queryFactory);
List<UserNotificationPreferenceEntity> data = query.collectAs(lookup.getProject());
@ -83,7 +83,7 @@ public class UserNotificationPreferenceController {
public UserNotificationPreference current(@PathVariable UUID userId, FieldSet fieldSet, Locale locale) throws MyApplicationException, MyForbiddenException, MyNotFoundException {
logger.debug(new MapLogEntry("retrieving" + UserNotificationPreference.class.getSimpleName()).And("userId", userId).And("fields", fieldSet));
this.censorFactory.censor(UserNotificationPreferenceCensor.class).censor(fieldSet);
this.censorFactory.censor(UserNotificationPreferenceCensor.class).censor(fieldSet, userId);
UserNotificationPreferenceQuery query = this.queryFactory.query(UserNotificationPreferenceQuery.class).userId(userId);
UserNotificationPreference model = this.builderFactory.builder(UserNotificationPreferenceBuilder.class).authorize(AuthorizationFlags.OwnerOrPermission).build(fieldSet, query.firstAs(fieldSet));

View File

@ -39,9 +39,6 @@ cache:
enableRecordStats: false
expireAfterWriteSeconds: 60
mapCaches:
- names: [ cacheB ]
allowNullValues: true
storeByValue: true
apiKey:
name: apikey
keyPattern: resolve_$keyhash$:v0

View File

@ -1,7 +1,11 @@
spring:
jpa:
properties:
org:
hibernate:
flushMode: MANUAL
hibernate:
globally_quoted_identifiers: true
ddl-auto: validate
dialect: org.hibernate.dialect.PostgreSQLDialect
hibernate:
@ -16,9 +20,10 @@ spring:
hikari:
connection-timeout: 30000
minimum-idle: 3
maximum-pool-size: 5
maximum-pool-size: 10
idle-timeout: 600000
max-lifetime: 1800000
naming-strategy:
prefix: ntf_

View File

@ -1,14 +1,14 @@
spring:
mail:
host: ${MAIL_HOST:}
port: ${MAIL_PORT:}
username: ${MAIL_USERNAME:}
password: ${MAIL_PASSWORD:}
host: ${MAIL_HOST}
port: ${MAIL_PORT}
username: ${MAIL_USERNAME}
password: ${MAIL_PASSWORD}
properties:
mail:
smtp:
auth: ${MAIL_AUTH:}
auth: ${MAIL_AUTH}
starttls:
enable: ${MAIL_TLS:}
enable: ${MAIL_TLS}
email:
address: ${MAIL_ADDRESS:}
address: ${MAIL_ADDRESS}

View File

@ -37,3 +37,5 @@ idpclient:
- type: azp
Authorities:
- type: authorities
ExternalProviderName:
- type: identity_provider

View File

@ -5,26 +5,26 @@ permissions:
# Tenants
BrowseTenant:
roles:
- ic-sti-superuser
- Admin
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
EditTenant:
roles:
- ic-sti-superuser
- Admin
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
DeleteTenant:
roles:
- ic-sti-superuser
- Admin
claims: [ ]
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
AllowNoTenant:
roles:
- ic-sti-superuser
- Admin
claims: [ ]
clients: [ ]
allowAnonymous: false
@ -32,21 +32,19 @@ permissions:
# Users
BrowseUser:
roles:
- ic-sti-superuser
- tenantadmin
- Admin
clients: [ ]
allowAnonymous: true
allowAuthenticated: false
EditUser:
roles:
- admin
- ic-sti-superuser
- Admin
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
DeleteUser:
roles:
- ic-sti-superuser
- Admin
claims: [ ]
clients: [ ]
allowAnonymous: false
@ -54,20 +52,19 @@ permissions:
# UserContactInfo
BrowseUserContactInfo:
roles:
- ic-sti-superuser
- Admin
clients: [ ]
allowAnonymous: true
allowAuthenticated: false
EditUserContactInfo:
roles:
- ic-sti-superuser
- user
- Admin
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
DeleteUserContactInfo:
roles:
- ic-sti-superuser
- Admin
claims: [ ]
clients: [ ]
allowAnonymous: false
@ -94,27 +91,26 @@ permissions:
#Tenant Configuration
BrowseTenantConfiguration:
roles:
- ic-sti-superuser
- Admin
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
EditTenantConfiguration:
roles:
- ic-sti-superuser
- Admin
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
#User Notification Preference
BrowseUserNotificationPreference:
roles:
- ic-sti-superuser
- Admin
clients: [ ]
allowAnonymous: true
allowAuthenticated: false
EditUserNotificationPreference:
roles:
- ic-sti-superuser
- user
- Admin
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
@ -122,26 +118,25 @@ permissions:
# ViewPage Permissions
ViewNotificationPage:
roles:
- ic-sti-superuser
- Admin
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
ViewNotificationEventRulePage:
roles:
- ic-sti-superuser
- Admin
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
ViewInAppNotificationPage:
roles:
- ic-sti-superuser
- tenantadmin
- Admin
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
ViewNotificationTemplatePage:
roles:
- ic-sti-superuser
- Admin
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
@ -170,7 +165,6 @@ permissions:
BrowseInAppNotification:
roles:
- Admin
- User
clients: [ ]
allowAnonymous: false
allowAuthenticated: false

View File

@ -0,0 +1,6 @@
web:
security:
idp:
resource:
jwt:
audiences: [ "dmp_notification" ]

Some files were not shown because too many files have changed in this diff Show More