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; 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; import java.time.Instant; import java.util.*; @Component @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class AnnotationQuery extends QueryBase { private String like; private Collection ids; private Collection excludedIds; private Collection isActives; private Collection entityIds; private Collection entityTypes; private Collection anchors; private Collection threadIds; private EnumSet authorize = EnumSet.of(AuthorizationFlags.None); private final UserScope userScope; private final QueryUtilsService queryUtilsService; private final AuthorizationService authService; public AnnotationQuery(UserScope userScope, QueryUtilsService queryUtilsService, AuthorizationService authService) { this.userScope = userScope; this.queryUtilsService = queryUtilsService; this.authService = authService; } public AnnotationQuery like(String value) { this.like = like; return this; } public AnnotationQuery ids(UUID value) { this.ids = List.of(value); return this; } public AnnotationQuery ids(UUID... value) { this.ids = Arrays.asList(value); return this; } public AnnotationQuery ids(Collection values) { this.ids = values; return this; } public AnnotationQuery excludedIds(Collection values) { this.excludedIds = values; return this; } public AnnotationQuery excludedIds(UUID value) { this.excludedIds = List.of(value); return this; } public AnnotationQuery excludedIds(UUID... value) { this.excludedIds = Arrays.asList(value); return this; } public AnnotationQuery isActive(IsActive value) { this.isActives = List.of(value); return this; } public AnnotationQuery isActive(IsActive... value) { this.isActives = Arrays.asList(value); return this; } public AnnotationQuery isActive(Collection values) { this.isActives = values; return this; } public AnnotationQuery entityIds(UUID value) { this.entityIds = List.of(value); return this; } public AnnotationQuery entityIds(UUID... value) { this.entityIds = Arrays.asList(value); return this; } public AnnotationQuery entityIds(Collection values) { this.entityIds = values; return this; } public AnnotationQuery entityTypes(String value) { this.entityTypes = List.of(value); return this; } public AnnotationQuery entityTypes(String... value) { this.entityTypes = Arrays.asList(value); return this; } public AnnotationQuery entityTypes(Collection values) { this.entityTypes = values; return this; } public AnnotationQuery anchors(String value) { this.anchors = List.of(value); return this; } public AnnotationQuery anchors(String... value) { this.anchors = Arrays.asList(value); return this; } public AnnotationQuery anchors(Collection values) { this.anchors = values; return this; } public AnnotationQuery threadIds(UUID value) { this.threadIds = List.of(value); return this; } public AnnotationQuery threadIds(UUID... value) { this.threadIds = Arrays.asList(value); return this; } public AnnotationQuery threadIds(Collection values) { this.threadIds = values; return this; } public AnnotationQuery authorize(EnumSet 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); } @Override protected Class entityClass() { return AnnotationEntity.class; } @Override protected Predicate applyAuthZ(QueryContext 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 predicates = new ArrayList<>(); if (userId != null ) { UUID finalUserId = userId; Subquery 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 Predicate applyFilters(QueryContext queryContext) { List predicates = new ArrayList<>(); if (this.like != null && !this.like.isBlank()) { Predicate likePredicate = queryContext.CriteriaBuilder.or( queryContext.CriteriaBuilder.like(queryContext.Root.get(AnnotationEntity._payload), this.like) ); predicates.add(likePredicate); } if (this.ids != null) { CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(AnnotationEntity._id)); for (UUID item : this.ids) inClause.value(item); predicates.add(inClause); } if (this.excludedIds != null) { CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(AnnotationEntity._id)); for (UUID item : this.ids) inClause.value(item); predicates.add(inClause.not()); } if (this.isActives != null) { CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(AnnotationEntity._isActive)); for (IsActive item : this.isActives) inClause.value(item); predicates.add(inClause); } if (this.entityIds != null) { CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(AnnotationEntity._entityId)); for (UUID item : this.entityIds) inClause.value(item); predicates.add(inClause); } if (this.entityTypes != null) { CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(AnnotationEntity._entityType)); for (String item : this.entityTypes) inClause.value(item); predicates.add(inClause); } if (this.anchors != null) { CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(AnnotationEntity._anchor)); for (String item : this.anchors) inClause.value(item); predicates.add(inClause); } if (this.threadIds != null) { CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(AnnotationEntity._threadId)); for (UUID item : this.threadIds) inClause.value(item); predicates.add(inClause); } if (!predicates.isEmpty()) { Predicate[] predicatesArray = predicates.toArray(new Predicate[0]); return queryContext.CriteriaBuilder.and(predicatesArray); } else { return null; } } @Override protected String fieldNameOf(FieldResolver item) { if (item.match(Annotation._id)) return AnnotationEntity._id; else if (item.match(Annotation._entityId)) return AnnotationEntity._entityId; else if (item.match(Annotation._entityType)) return AnnotationEntity._entityType; else if (item.match(Annotation._anchor)) return AnnotationEntity._anchor; else if (item.match(Annotation._payload)) return AnnotationEntity._payload; else if (item.match(Annotation._subjectId)) return AnnotationEntity._subjectId; else if (item.match(Annotation._threadId)) return AnnotationEntity._threadId; else if (item.match(Annotation._parentId)) return AnnotationEntity._parentId; else if (item.match(Annotation._protectionType)) return AnnotationEntity._protectionType; else if (item.match(Annotation._timeStamp)) return AnnotationEntity._timeStamp; else if (item.match(Annotation._createdAt)) return AnnotationEntity._createdAt; else if (item.match(Annotation._updatedAt)) return AnnotationEntity._updatedAt; // else if (item.match(Annotation._hash)) return AnnotationEntity._updatedAt; else if (item.match(Annotation._isActive)) return AnnotationEntity._isActive; else return null; } @Override protected AnnotationEntity convert(Tuple tuple, Set columns) { AnnotationEntity item = new AnnotationEntity(); item.setId(QueryBase.convertSafe(tuple, columns, AnnotationEntity._id, UUID.class)); item.setEntityId(QueryBase.convertSafe(tuple, columns, AnnotationEntity._entityId, UUID.class)); item.setEntityType(QueryBase.convertSafe(tuple, columns, AnnotationEntity._entityType, String.class)); item.setAnchor(QueryBase.convertSafe(tuple, columns, AnnotationEntity._anchor, String.class)); item.setPayload(QueryBase.convertSafe(tuple, columns, AnnotationEntity._payload, String.class)); item.setSubjectId(QueryBase.convertSafe(tuple, columns, AnnotationEntity._subjectId, UUID.class)); item.setThreadId(QueryBase.convertSafe(tuple, columns, AnnotationEntity._threadId, UUID.class)); item.setParentId(QueryBase.convertSafe(tuple, columns, AnnotationEntity._parentId, UUID.class)); item.setProtectionType(QueryBase.convertSafe(tuple, columns, AnnotationEntity._protectionType, AnnotationProtectionType.class)); item.setTimeStamp(QueryBase.convertSafe(tuple, columns, AnnotationEntity._timeStamp, Instant.class)); item.setCreatedAt(QueryBase.convertSafe(tuple, columns, AnnotationEntity._createdAt, Instant.class)); item.setUpdatedAt(QueryBase.convertSafe(tuple, columns, AnnotationEntity._updatedAt, Instant.class)); item.setIsActive(QueryBase.convertSafe(tuple, columns, AnnotationEntity._isActive, IsActive.class)); return item; } }