blob: 1a12103a42c474c3237a09edc9a04f2bbb05ae19 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.syncope.core.persistence.jpa.dao;
import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.apache.syncope.common.lib.to.AuditEventTO;
import org.apache.syncope.common.lib.types.OpEvent;
import org.apache.syncope.core.persistence.api.dao.AuditEventDAO;
import org.apache.syncope.core.persistence.api.entity.AuditEvent;
import org.apache.syncope.core.persistence.jpa.entity.JPAAuditEvent;
import org.springframework.data.domain.Pageable;
import org.springframework.transaction.annotation.Transactional;
public class JPAAuditEventDAO implements AuditEventDAO {
protected static class AuditEventCriteriaBuilder {
protected final StringBuilder query = new StringBuilder();
protected String andIfNeeded() {
return query.length() == 0 ? " " : " AND ";
}
protected int setParameter(final List<Object> parameters, final Object parameter) {
parameters.add(parameter);
return parameters.size();
}
protected AuditEventCriteriaBuilder entityKey(final String entityKey) {
if (entityKey != null) {
query.append(andIfNeeded()).
append("(before_value LIKE '%key%").append(entityKey).append("%' OR ").
append("inputs LIKE '%key%").append(entityKey).append("%' OR ").
append("output LIKE '%key%").append(entityKey).append("%' OR ").
append("throwable LIKE '%key%").append(entityKey).append("%')");
}
return this;
}
public AuditEventCriteriaBuilder opEvent(
final OpEvent.CategoryType type,
final String category,
final String subcategory,
final String op,
final OpEvent.Outcome outcome) {
query.append(andIfNeeded()).
append("opEvent LIKE '").
append(OpEvent.toString(type, category, subcategory, op, outcome).replace("[]", "[%]")).
append("'");
return this;
}
public AuditEventCriteriaBuilder before(final OffsetDateTime before, final List<Object> parameters) {
if (before != null) {
query.append(andIfNeeded()).
append("event_date").append(" <= ?").append(setParameter(parameters, before));
}
return this;
}
public AuditEventCriteriaBuilder after(final OffsetDateTime after, final List<Object> parameters) {
if (after != null) {
query.append(andIfNeeded()).
append("event_date").append(" >= ?").append(setParameter(parameters, after));
}
return this;
}
public String build() {
return query.toString();
}
}
protected final EntityManager entityManager;
public JPAAuditEventDAO(final EntityManager entityManager) {
this.entityManager = entityManager;
}
@Transactional
@Override
public AuditEvent save(final AuditEvent auditEvent) {
return entityManager.merge(auditEvent);
}
protected AuditEventCriteriaBuilder criteriaBuilder(final String entityKey) {
return new AuditEventCriteriaBuilder().entityKey(entityKey);
}
protected void fillWithParameters(final Query query, final List<Object> parameters) {
for (int i = 0; i < parameters.size(); i++) {
if (parameters.get(i) instanceof Boolean aBoolean) {
query.setParameter(i + 1, aBoolean ? 1 : 0);
} else {
query.setParameter(i + 1, parameters.get(i));
}
}
}
@Transactional(readOnly = true)
@Override
public long count(
final String entityKey,
final OpEvent.CategoryType type,
final String category,
final String subcategory,
final String op,
final OpEvent.Outcome outcome,
final OffsetDateTime before,
final OffsetDateTime after) {
List<Object> parameters = new ArrayList<>();
String queryString = "SELECT COUNT(0)"
+ " FROM " + JPAAuditEvent.TABLE
+ " WHERE" + criteriaBuilder(entityKey).
opEvent(type, category, subcategory, op, outcome).
before(before, parameters).
after(after, parameters).
build();
Query query = entityManager.createNativeQuery(queryString);
fillWithParameters(query, parameters);
return ((Number) query.getSingleResult()).longValue();
}
@Transactional(readOnly = true)
@Override
public List<AuditEventTO> search(
final String entityKey,
final OpEvent.CategoryType type,
final String category,
final String subcategory,
final String op,
final OpEvent.Outcome outcome,
final OffsetDateTime before,
final OffsetDateTime after,
final Pageable pageable) {
List<Object> parameters = new ArrayList<>();
String queryString = "SELECT id"
+ " FROM " + JPAAuditEvent.TABLE
+ " WHERE" + criteriaBuilder(entityKey).
opEvent(type, category, subcategory, op, outcome).
before(before, parameters).
after(after, parameters).
build();
if (!pageable.getSort().isEmpty()) {
queryString += " ORDER BY " + pageable.getSort().stream().
map(clause -> ("when".equals(clause.getProperty()) ? "event_date" : clause.getProperty())
+ ' ' + clause.getDirection().name()).
collect(Collectors.joining(","));
}
Query query = entityManager.createNativeQuery(queryString);
fillWithParameters(query, parameters);
if (pageable.isPaged()) {
query.setFirstResult(pageable.getPageSize() * pageable.getPageNumber());
query.setMaxResults(pageable.getPageSize());
}
@SuppressWarnings("unchecked")
List<Object> entries = query.getResultList();
return entries.stream().
map(row -> entityManager.find(JPAAuditEvent.class, row.toString())).
filter(Objects::nonNull).map(this::toAuditEventTO).toList();
}
}