Handling Policy deploy/undeploy audit models 16/121416/9
authoradheli.tavares <adheli.tavares@est.tech>
Tue, 11 May 2021 13:35:20 +0000 (14:35 +0100)
committeradheli.tavares <adheli.tavares@est.tech>
Fri, 28 May 2021 14:45:52 +0000 (15:45 +0100)
Added a new Entity to store information when a Pdp Policy is deployed or
undeployed and its create/retrieve methods.

Change-Id: I35b3608c878bbfbbee0a99a124d100a48fe08131
Issue-ID: POLICY-2899
Signed-off-by: adheli.tavares <adheli.tavares@est.tech>
models-dao/src/main/java/org/onap/policy/models/dao/PfDao.java
models-dao/src/main/java/org/onap/policy/models/dao/impl/DefaultPfDao.java
models-dao/src/test/java/org/onap/policy/models/dao/EntityTest.java
models-pap/pom.xml
models-pap/src/main/java/org/onap/policy/models/pap/concepts/PolicyAudit.java [new file with mode: 0644]
models-pap/src/main/java/org/onap/policy/models/pap/persistence/concepts/JpaPolicyAudit.java [new file with mode: 0644]
models-pap/src/main/java/org/onap/policy/models/pap/persistence/provider/PolicyAuditProvider.java [new file with mode: 0644]
models-pap/src/test/java/org/onap/policy/models/pap/persistence/concepts/JpaPolicyAuditTest.java [new file with mode: 0644]
models-pap/src/test/java/org/onap/policy/models/pap/persistence/provider/PolicyAuditProviderTest.java [new file with mode: 0644]
models-pap/src/test/resources/META-INF/persistence.xml [new file with mode: 0644]

index 8447593..378ae59 100644 (file)
@@ -251,6 +251,17 @@ public interface PfDao {
      */
     <T extends PfConcept> List<T> getAll(Class<T> someClass, PfConceptKey parentKey);
 
+    /**
+     * Get all the objects in the database of a given type.
+     *
+     * @param <T> the type of the objects to get, a subclass of {@link PfConcept}
+     * @param someClass the class of the objects to get, a subclass of {@link PfConcept}
+     * @param orderBy field from class to order results by
+     * @param numRecords number of records to be retrieved
+     * @return the objects or null if no objects were retrieved
+     */
+    <T extends PfConcept> List<T> getAll(Class<T> someClass, String orderBy, Integer numRecords);
+
     /**
      * Get all the objects in the database of a given type.
      *
index d1e3293..0095630 100644 (file)
@@ -32,6 +32,7 @@ import javax.persistence.EntityManagerFactory;
 import javax.persistence.Persistence;
 import javax.persistence.TypedQuery;
 import javax.ws.rs.core.Response;
+import org.apache.commons.lang3.StringUtils;
 import org.onap.policy.models.base.PfConcept;
 import org.onap.policy.models.base.PfConceptKey;
 import org.onap.policy.models.base.PfGeneratedIdKey;
@@ -72,6 +73,7 @@ public class DefaultPfDao implements PfDao {
 
     private static final String WHERE      = " WHERE ";
     private static final String AND        = " AND ";
+    private static final String ORDER_BY        = " ORDER BY c.";
 
     private static final String NAME_FILTER            = "c.key.name = :name";
     private static final String VERSION_FILTER         = "c.key.version = :version";
@@ -395,9 +397,8 @@ public class DefaultPfDao implements PfDao {
 
         try {
             PfFilter timeStampFilter = new PfFilterFactory().createFilter(someClass);
-            filterQueryString = timeStampFilter.addFilter(filterQueryString,
-                  name, startTime, endTime, filterMap, sortOrder, getRecordNum);
-
+            filterQueryString = timeStampFilter.addFilter(filterQueryString, name, startTime, endTime, filterMap,
+                    sortOrder, getRecordNum);
 
             TypedQuery<T> query = mg.createQuery(setQueryTable(filterQueryString, someClass), someClass);
 
@@ -427,7 +428,7 @@ public class DefaultPfDao implements PfDao {
 
             LOGGER.debug("filterQueryString is  \"{}\"", filterQueryString);
             return query.getResultList();
-        }  finally {
+        } finally {
             mg.close();
         }
     }
@@ -504,6 +505,27 @@ public class DefaultPfDao implements PfDao {
         }
     }
 
+    @Override
+    public <T extends PfConcept> List<T> getAll(Class<T> someClass, String orderBy, Integer numRecords) {
+
+        if (someClass == null) {
+            return Collections.emptyList();
+        }
+        final var mg = getEntityManager();
+        try {
+            String query = setQueryTable(SELECT_FROM_TABLE, someClass);
+
+            if (StringUtils.isNotBlank(orderBy)) {
+                query = query.concat(ORDER_BY).concat(orderBy);
+            }
+
+            return mg.createQuery(query, someClass).setMaxResults(numRecords)
+                    .getResultList();
+        } finally {
+            mg.close();
+        }
+    }
+
     @Override
     public <T extends PfConcept> List<T> getAllVersionsByParent(final Class<T> someClass, final String parentKeyName) {
         if (someClass == null || parentKeyName == null) {
@@ -539,9 +561,8 @@ public class DefaultPfDao implements PfDao {
     }
 
     @Override
-    public <T extends PfConcept> List<T> getByTimestamp(final Class<T> someClass,
-                                                        final PfGeneratedIdKey key,
-                                                        final Instant timeStamp) {
+    public <T extends PfConcept> List<T> getByTimestamp(final Class<T> someClass, final PfGeneratedIdKey key,
+            final Instant timeStamp) {
         if (someClass == null || key == null || timeStamp == null) {
             return Collections.emptyList();
         }
index 66fc1b5..59fc9b4 100644 (file)
@@ -183,6 +183,7 @@ public class EntityTest {
         pfDao.get(null, nullTimeKey);
         pfDao.getAll(null);
         pfDao.getAll(null, nullKey);
+        pfDao.getAll(null, null, null);
         pfDao.getConcept(null, nullKey);
         pfDao.getConcept(PfConceptKey.class, nullKey);
         pfDao.getConcept(null, nullRefKey);
index f2ac085..3950863 100644 (file)
@@ -1,6 +1,6 @@
 <!--
   ============LICENSE_START=======================================================
-   Copyright (C) 2019 Nordix Foundation.
+   Copyright (C) 2019-2021 Nordix Foundation.
    Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
   ================================================================================
   Licensed under the Apache License, Version 2.0 (the "License");
             <artifactId>assertj-core</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>com.h2database</groupId>
+            <artifactId>h2</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.awaitility</groupId>
+            <artifactId>awaitility</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
diff --git a/models-pap/src/main/java/org/onap/policy/models/pap/concepts/PolicyAudit.java b/models-pap/src/main/java/org/onap/policy/models/pap/concepts/PolicyAudit.java
new file mode 100644 (file)
index 0000000..765d246
--- /dev/null
@@ -0,0 +1,50 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.models.pap.concepts;
+
+import java.time.Instant;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
+
+/**
+ * Audit info on policy actions.
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class PolicyAudit {
+
+    public enum AuditAction {
+        DEPLOYMENT, UNDEPLOYMENT
+    }
+
+    private Long auditId;
+    private String pdpGroup;
+    private String pdpType;
+    private ToscaConceptIdentifier policy;
+    private AuditAction action;
+    private Instant timestamp;
+    private String user;
+}
diff --git a/models-pap/src/main/java/org/onap/policy/models/pap/persistence/concepts/JpaPolicyAudit.java b/models-pap/src/main/java/org/onap/policy/models/pap/persistence/concepts/JpaPolicyAudit.java
new file mode 100644 (file)
index 0000000..b31af8e
--- /dev/null
@@ -0,0 +1,191 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.models.pap.persistence.concepts;
+
+import java.time.Instant;
+import java.util.Date;
+import java.util.List;
+import javax.persistence.Column;
+import javax.persistence.EmbeddedId;
+import javax.persistence.Entity;
+import javax.persistence.Index;
+import javax.persistence.Inheritance;
+import javax.persistence.InheritanceType;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+import javax.validation.constraints.NotNull;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.apache.commons.lang3.builder.CompareToBuilder;
+import org.onap.policy.common.utils.validation.Assertions;
+import org.onap.policy.models.base.PfAuthorative;
+import org.onap.policy.models.base.PfConcept;
+import org.onap.policy.models.base.PfGeneratedIdKey;
+import org.onap.policy.models.base.PfKey;
+import org.onap.policy.models.base.PfReferenceKey;
+import org.onap.policy.models.base.validation.annotations.VerifyKey;
+import org.onap.policy.models.pap.concepts.PolicyAudit;
+import org.onap.policy.models.pap.concepts.PolicyAudit.AuditAction;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
+
+/**
+ * Entity to keep the records on policy actions for audit.
+ *
+ * @author Adheli Tavares (adheli.tavares@est.tech)
+ *
+ */
+@Entity
+@Table(name = "JpaPolicyAudit", indexes = {@Index(name = "JpaPolicyAuditIndex_timestamp", columnList = "timeStamp")})
+@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class JpaPolicyAudit extends PfConcept implements PfAuthorative<PolicyAudit> {
+    private static final long serialVersionUID = -2935734300607322191L;
+
+    @EmbeddedId
+    @Column
+    @NotNull
+    @VerifyKey(versionNotNull = true)
+    private PfGeneratedIdKey key;
+
+    @Column
+    private String pdpGroup;
+
+    @Column
+    private String pdpType;
+
+    @Column
+    @NotNull
+    private AuditAction action;
+
+    @Column
+    @Temporal(TemporalType.TIMESTAMP)
+    @NotNull
+    private Date timeStamp;
+
+    @Column
+    private String user;
+
+    /**
+     * Default constructor.
+     */
+    public JpaPolicyAudit() {
+        key = new PfGeneratedIdKey();
+    }
+
+    /**
+     * Constructor from an authorative.
+     *
+     * @param audit authorative model
+     */
+    public JpaPolicyAudit(PolicyAudit audit) {
+        fromAuthorative(audit);
+    }
+
+    /**
+     * Constructor as a copy.
+     *
+     * @param copyConcept original entity to be copied
+     */
+    public JpaPolicyAudit(JpaPolicyAudit copyConcept) {
+        this.key = new PfGeneratedIdKey(copyConcept.getKey());
+        this.pdpGroup = copyConcept.getPdpGroup();
+        this.pdpType = copyConcept.getPdpType();
+        this.action = copyConcept.getAction();
+        this.timeStamp = copyConcept.getTimeStamp();
+        this.user = copyConcept.getUser();
+    }
+
+    @Override
+    public int compareTo(PfConcept o) {
+        if (o == null) {
+            return -1;
+        }
+        if (this == o) {
+            return 0;
+        }
+        if (getClass() != o.getClass()) {
+            return getClass().getName().compareTo(o.getClass().getName());
+        }
+
+        final JpaPolicyAudit other = (JpaPolicyAudit) o;
+
+        // @formatter:off
+        return new CompareToBuilder()
+                        .append(key, other.key)
+                        .append(pdpGroup, other.pdpGroup)
+                        .append(pdpType, other.pdpType)
+                        .append(action, other.action)
+                        .append(timeStamp, other.timeStamp)
+                        .append(user, other.user)
+                        .toComparison();
+        // @formatter:on
+    }
+
+    @Override
+    public PolicyAudit toAuthorative() {
+        ToscaConceptIdentifier policyIdent = new ToscaConceptIdentifier(key.getName(), key.getVersion());
+
+        // @formatter:off
+        return PolicyAudit.builder()
+                        .auditId(key.getGeneratedId())
+                        .pdpGroup(pdpGroup)
+                        .pdpType(pdpType)
+                        .policy(policyIdent)
+                        .action(action)
+                        .timestamp(timeStamp == null ? null : timeStamp.toInstant())
+                        .user(user)
+                        .build();
+        // @formatter:on
+    }
+
+    @Override
+    public void fromAuthorative(PolicyAudit authorativeConcept) {
+        if (authorativeConcept.getPolicy() != null) {
+            final ToscaConceptIdentifier policy = authorativeConcept.getPolicy();
+            key = new PfGeneratedIdKey(policy.getName(), policy.getVersion(), authorativeConcept.getAuditId());
+        } else {
+            key = new PfGeneratedIdKey();
+        }
+
+        pdpGroup = authorativeConcept.getPdpGroup();
+        pdpType = authorativeConcept.getPdpType();
+        action = authorativeConcept.getAction();
+        timeStamp = authorativeConcept.getTimestamp() == null ? Date.from(Instant.now())
+                : Date.from(authorativeConcept.getTimestamp());
+        user = authorativeConcept.getUser();
+    }
+
+    @Override
+    public List<PfKey> getKeys() {
+        return getKey().getKeys();
+    }
+
+    @Override
+    public void clean() {
+        key.clean();
+
+        pdpGroup = Assertions.validateStringParameter("pdpGroup", pdpGroup, PfReferenceKey.LOCAL_NAME_REGEXP);
+        pdpType = Assertions.validateStringParameter("pdpType", pdpType, PfReferenceKey.LOCAL_NAME_REGEXP);
+        user = Assertions.validateStringParameter("user", user, PfReferenceKey.LOCAL_NAME_REGEXP);
+    }
+}
diff --git a/models-pap/src/main/java/org/onap/policy/models/pap/persistence/provider/PolicyAuditProvider.java b/models-pap/src/main/java/org/onap/policy/models/pap/persistence/provider/PolicyAuditProvider.java
new file mode 100644 (file)
index 0000000..c117bee
--- /dev/null
@@ -0,0 +1,137 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.models.pap.persistence.provider;
+
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import javax.ws.rs.core.Response;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NonNull;
+import org.apache.commons.lang3.StringUtils;
+import org.onap.policy.common.parameters.BeanValidationResult;
+import org.onap.policy.models.base.PfModelRuntimeException;
+import org.onap.policy.models.dao.PfDao;
+import org.onap.policy.models.pap.concepts.PolicyAudit;
+import org.onap.policy.models.pap.concepts.PolicyAudit.AuditAction;
+import org.onap.policy.models.pap.persistence.concepts.JpaPolicyAudit;
+
+/**
+ * Provider for Policy Audit.
+ *
+ * @author Adheli Tavares (adheli.tavares@est.tech)
+ *
+ */
+public class PolicyAuditProvider {
+
+    private static final Integer DEFAULT_MAX_RECORDS = 100;
+    private static final String DESCENDING_ORDER = "DESC";
+
+    /**
+     * Create audit records.
+     *
+     * @param audits list of policy audit
+     */
+    public void createAuditRecords(@NonNull PfDao dao, @NonNull final List<PolicyAudit> audits) {
+        List<JpaPolicyAudit> jpaAudits = audits.stream().map(JpaPolicyAudit::new).collect(Collectors.toList());
+
+        BeanValidationResult result = new BeanValidationResult("createAuditRecords", jpaAudits);
+
+        int count = 0;
+        for (JpaPolicyAudit jpaAudit: jpaAudits) {
+            result.addResult(jpaAudit.validate(String.valueOf(count++)));
+        }
+
+        if (!result.isValid()) {
+            throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, result.getResult());
+        }
+
+        dao.createCollection(jpaAudits);
+    }
+
+    /**
+     * Collect all audit records.
+     *
+     * @param numRecords number of records to be collected
+     * @return list of {@link PolicyAudit} records
+     */
+    public List<PolicyAudit> getAuditRecords(@NonNull PfDao dao, @NonNull Integer numRecords) {
+        numRecords = numRecords > DEFAULT_MAX_RECORDS ? DEFAULT_MAX_RECORDS : numRecords;
+
+        // @formatter:off
+        return dao.getAll(JpaPolicyAudit.class, "timeStamp DESC", numRecords)
+                .stream()
+                .map(JpaPolicyAudit::toAuthorative)
+                .collect(Collectors.toList());
+        // @formatter:on
+    }
+
+    /**
+     * Collect audit records based on filters at {@link AuditFilter}.
+     *
+     * @param auditFilter {@link AuditFilter} object with filters for search
+     * @param numRecords number of records to be collected
+     * @return list of {@link PolicyAudit} records
+     */
+    public List<PolicyAudit> getAuditRecords(@NonNull PfDao dao, @NonNull AuditFilter auditFilter,
+            @NonNull Integer numRecords) {
+        numRecords = numRecords > DEFAULT_MAX_RECORDS ? DEFAULT_MAX_RECORDS : numRecords;
+
+        Map<String, Object> filter = new HashMap<>();
+        if (StringUtils.isNotBlank(auditFilter.getPdpGroup())) {
+            filter.put("pdpGroup", auditFilter.getPdpGroup());
+        }
+
+        if (auditFilter.getAction() != null) {
+            filter.put("action", auditFilter.getAction());
+        }
+
+        // @formatter:off
+        return dao.getFiltered(JpaPolicyAudit.class,
+                auditFilter.getName(), auditFilter.getVersion(),
+                auditFilter.getFromDate(), auditFilter.getToDate(),
+                filter, DESCENDING_ORDER, numRecords)
+                .stream().map(JpaPolicyAudit::toAuthorative).collect(Collectors.toList());
+        // @formatter:on
+    }
+
+    /**
+     * Create a filter for looking for audit records.
+     * name - policy name
+     * version - policy version
+     * pdpGroup - PDP group that policy might be related
+     * action - type of action/operation realized on policy
+     * fromDate - start of period in case of time interval search
+     */
+    @Data
+    @Builder
+    protected static class AuditFilter {
+        private String name;
+        private String version;
+        private AuditAction action;
+        private String pdpGroup;
+        private Instant fromDate;
+        private Instant toDate;
+    }
+}
diff --git a/models-pap/src/test/java/org/onap/policy/models/pap/persistence/concepts/JpaPolicyAuditTest.java b/models-pap/src/test/java/org/onap/policy/models/pap/persistence/concepts/JpaPolicyAuditTest.java
new file mode 100644 (file)
index 0000000..4f86933
--- /dev/null
@@ -0,0 +1,126 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.models.pap.persistence.concepts;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatNoException;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.policy.common.parameters.BeanValidationResult;
+import org.onap.policy.models.pap.concepts.PolicyAudit;
+import org.onap.policy.models.pap.concepts.PolicyAudit.AuditAction;
+import org.onap.policy.models.pdp.concepts.PdpPolicyStatus;
+import org.onap.policy.models.pdp.concepts.PdpPolicyStatus.State;
+import org.onap.policy.models.pdp.persistence.concepts.JpaPdpPolicyStatus;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
+
+public class JpaPolicyAuditTest {
+
+    private static final ToscaConceptIdentifier MY_POLICY = new ToscaConceptIdentifier("MyPolicy", "1.2.3");
+    private static final String PDP_GROUP = "pdpGroupxyz";
+    private static final String USER = "user";
+
+    PolicyAudit audit;
+
+    /**
+     * Setup an audit for usage on unit tests.
+     */
+    @Before
+    public void setup() {
+        audit = PolicyAudit.builder().auditId(1L).pdpGroup(PDP_GROUP).pdpType("pdpType").policy(MY_POLICY)
+                .action(AuditAction.DEPLOYMENT).timestamp(Instant.now().truncatedTo(ChronoUnit.SECONDS)).build();
+    }
+
+    @Test
+    public void testCompareTo() {
+        JpaPolicyAudit jpaAudit = new JpaPolicyAudit(audit);
+        assertEquals(-1, jpaAudit.compareTo(null));
+        assertEquals(0, jpaAudit.compareTo(jpaAudit));
+        assertEquals(0, jpaAudit.compareTo(new JpaPolicyAudit(jpaAudit)));
+
+        PdpPolicyStatus idw = PdpPolicyStatus.builder().deploy(true).state(State.SUCCESS).pdpGroup(PDP_GROUP)
+                .pdpId("wId").policy(MY_POLICY).policyType(MY_POLICY).build();
+        JpaPdpPolicyStatus jpaStatus = new JpaPdpPolicyStatus(idw);
+        assertNotEquals(0, jpaAudit.compareTo(jpaStatus));
+    }
+
+    @Test
+    public void testKeys() {
+        JpaPolicyAudit jpaAudit = new JpaPolicyAudit();
+        assertThat(jpaAudit.getKeys()).isNotNull();
+        assertTrue(jpaAudit.getKey().isNullKey());
+
+        jpaAudit = new JpaPolicyAudit(audit);
+        assertFalse(jpaAudit.getKey().isNullKey());
+    }
+
+    @Test
+    public void testClean() {
+        audit.setUser("   user");
+        JpaPolicyAudit jpaAudit = new JpaPolicyAudit(audit);
+        assertThatNoException().isThrownBy(() -> jpaAudit.clean());
+        assertEquals(USER, jpaAudit.getUser());
+    }
+
+    @Test
+    public void testToAuthorative() {
+        audit.setUser(USER);
+        JpaPolicyAudit jpaAudit = new JpaPolicyAudit(audit);
+        PolicyAudit convertedAudit = jpaAudit.toAuthorative();
+
+        assertEquals(audit, convertedAudit);
+        assertEquals(USER, convertedAudit.getUser());
+
+        JpaPolicyAudit jpaAuditWithNullKey = new JpaPolicyAudit();
+        PolicyAudit convertedAudit2 = jpaAuditWithNullKey.toAuthorative();
+        assertTrue(convertedAudit2.getPolicy().asConceptKey().isNullKey());
+
+    }
+
+    @Test
+    public void testConversionsWithRequiredOnly() {
+        audit = PolicyAudit.builder().policy(MY_POLICY).action(AuditAction.DEPLOYMENT)
+                .timestamp(Instant.now().truncatedTo(ChronoUnit.SECONDS)).build();
+
+        JpaPolicyAudit jpaAudit = new JpaPolicyAudit(audit);
+        PolicyAudit convertedAudit = jpaAudit.toAuthorative();
+
+        assertEquals(audit, convertedAudit);
+        assertTrue(jpaAudit.validate("jpaAudit").isValid());
+    }
+
+    @Test
+    public void testValidation() {
+        PolicyAudit invalidAudit = PolicyAudit.builder().pdpGroup(PDP_GROUP).user(USER).build();
+
+        JpaPolicyAudit jpaAudit = new JpaPolicyAudit(invalidAudit);
+
+        BeanValidationResult result = jpaAudit.validate("jpaAudit");
+        assertFalse(result.isValid());
+    }
+}
diff --git a/models-pap/src/test/java/org/onap/policy/models/pap/persistence/provider/PolicyAuditProviderTest.java b/models-pap/src/test/java/org/onap/policy/models/pap/persistence/provider/PolicyAuditProviderTest.java
new file mode 100644 (file)
index 0000000..da231dc
--- /dev/null
@@ -0,0 +1,241 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.models.pap.persistence.provider;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+import org.awaitility.Awaitility;
+import org.eclipse.persistence.config.PersistenceUnitProperties;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.policy.models.base.PfModelRuntimeException;
+import org.onap.policy.models.dao.DaoParameters;
+import org.onap.policy.models.dao.PfDao;
+import org.onap.policy.models.dao.PfDaoFactory;
+import org.onap.policy.models.dao.impl.DefaultPfDao;
+import org.onap.policy.models.pap.concepts.PolicyAudit;
+import org.onap.policy.models.pap.concepts.PolicyAudit.AuditAction;
+import org.onap.policy.models.pap.persistence.provider.PolicyAuditProvider.AuditFilter;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
+
+/**
+ * Class for unit testing {@link PolicyAuditProvider}.
+ *
+ * @author Adheli Tavares (adheli.tavares@est.tech)
+ *
+ */
+public class PolicyAuditProviderTest {
+
+    private static final String FIELD_IS_NULL = "%s is marked .*ull but is null";
+    private static final String GROUP_A = "groupA";
+    private static final String GROUP_B = "groupB";
+    private static final ToscaConceptIdentifier MY_POLICY = new ToscaConceptIdentifier("MyPolicy", "1.2.3");
+    private static final ToscaConceptIdentifier MY_POLICY2 = new ToscaConceptIdentifier("MyPolicyB", "2.3.4");
+    private static final Integer NUMBER_RECORDS = 10;
+
+    private PfDao pfDao;
+
+    /**
+     * Set up the DAO towards the database.
+     *
+     * @throws Exception on database errors
+     */
+    @Before
+    public void setupDao() throws Exception {
+        final DaoParameters daoParameters = new DaoParameters();
+        daoParameters.setPluginClass(DefaultPfDao.class.getName());
+
+        daoParameters.setPersistenceUnit("ToscaConceptTest");
+
+        Properties jdbcProperties = new Properties();
+        jdbcProperties.setProperty(PersistenceUnitProperties.JDBC_USER, "policy");
+        jdbcProperties.setProperty(PersistenceUnitProperties.JDBC_PASSWORD, "P01icY");
+
+        if (System.getProperty("USE-MARIADB") != null) {
+            jdbcProperties.setProperty(PersistenceUnitProperties.JDBC_DRIVER, "org.mariadb.jdbc.Driver");
+            jdbcProperties.setProperty(PersistenceUnitProperties.JDBC_URL, "jdbc:mariadb://localhost:3306/policy");
+        } else {
+            jdbcProperties.setProperty(PersistenceUnitProperties.JDBC_DRIVER, "org.h2.Driver");
+            jdbcProperties.setProperty(PersistenceUnitProperties.JDBC_URL, "jdbc:h2:mem:PolicyAuditProviderTest");
+        }
+
+        daoParameters.setJdbcProperties(jdbcProperties);
+
+        pfDao = new PfDaoFactory().createPfDao(daoParameters);
+        pfDao.init(daoParameters);
+    }
+
+    @After
+    public void teardown() {
+        pfDao.close();
+    }
+
+    @Test
+    public void testCreatePolicyAudit() {
+        PolicyAuditProvider provider = new PolicyAuditProvider();
+
+        Instant date = Instant.now();
+        provider.createAuditRecords(pfDao, generatePolicyAudits(date, GROUP_A, MY_POLICY));
+
+        List<PolicyAudit> records = provider.getAuditRecords(pfDao, NUMBER_RECORDS);
+        assertThat(records).hasSize(2);
+
+        // as the start date is 10 min ahead of first record, shouldn't return any records
+        List<PolicyAudit> emptyList = provider.getAuditRecords(pfDao,
+                AuditFilter.builder().fromDate(Instant.now().plusSeconds(600)).build(), 600);
+        assertThat(emptyList).isEmpty();
+    }
+
+    @Test
+    public void testCreatePolicyAuditInvalid() {
+        PolicyAuditProvider provider = new PolicyAuditProvider();
+
+        List<PolicyAudit> audits = List.of(PolicyAudit.builder().pdpType("pdpType").action(AuditAction.DEPLOYMENT)
+                .timestamp(Instant.now()).build());
+
+        assertThrows(PfModelRuntimeException.class, () -> provider.createAuditRecords(pfDao, audits));
+
+        List<PolicyAudit> records = provider.getAuditRecords(pfDao, NUMBER_RECORDS);
+        assertThat(records).isEmpty();
+    }
+
+    @Test
+    public void testFilters() {
+        PolicyAuditProvider provider = new PolicyAuditProvider();
+
+        Instant date = Instant.now().truncatedTo(ChronoUnit.SECONDS);
+        System.out.println(date);
+        provider.createAuditRecords(pfDao, generatePolicyAudits(date, GROUP_A, MY_POLICY));
+        provider.createAuditRecords(pfDao, generatePolicyAudits(date, GROUP_B, MY_POLICY));
+        provider.createAuditRecords(pfDao, generatePolicyAudits(date, GROUP_B, MY_POLICY2));
+        Awaitility.await().pollDelay(3, TimeUnit.SECONDS).until(() -> {
+            return true;
+        });
+
+        List<PolicyAudit> records = provider.getAuditRecords(pfDao,
+                AuditFilter.builder().fromDate(date).toDate(Instant.now()).build(), NUMBER_RECORDS);
+        assertThat(records).hasSize(6);
+
+        List<PolicyAudit> recordsWithGroupB =
+                provider.getAuditRecords(pfDao, AuditFilter.builder().pdpGroup(GROUP_B).build(), NUMBER_RECORDS);
+        assertThat(recordsWithGroupB).hasSize(4);
+
+        List<PolicyAudit> recordsWithActionDeploy = provider.getAuditRecords(pfDao,
+                AuditFilter.builder().action(AuditAction.DEPLOYMENT).build(), NUMBER_RECORDS);
+        assertThat(recordsWithActionDeploy).hasSize(3);
+
+        List<PolicyAudit> recordsWithMyPolicy = provider.getAuditRecords(pfDao,
+                AuditFilter.builder().name(MY_POLICY.getName()).version(MY_POLICY.getVersion()).build(),
+                NUMBER_RECORDS);
+        assertThat(recordsWithMyPolicy).hasSize(4);
+    }
+
+    @Test
+    public void testLoadRecordsForLimit() {
+        PolicyAuditProvider provider = new PolicyAuditProvider();
+
+        List<PolicyAudit> loadAudits = new ArrayList<>();
+
+        // going to create 102 records.
+        for (int i = 0; i <= 50; i++) {
+            loadAudits.addAll(generatePolicyAudits(Instant.now().plusSeconds(i), GROUP_A, MY_POLICY));
+        }
+
+        provider.createAuditRecords(pfDao, loadAudits);
+
+        List<PolicyAudit> records = provider.getAuditRecords(pfDao, NUMBER_RECORDS);
+        assertThat(records).hasSize(10);
+
+        // check that is being ordered
+        assertTrue(records.get(0).getTimestamp().isAfter(records.get(9).getTimestamp()));
+        assertEquals(loadAudits.get(loadAudits.size() - 1).getTimestamp(), records.get(0).getTimestamp());
+
+        // try to get 102 records should return 100
+        records = provider.getAuditRecords(pfDao, 102);
+        assertThat(records).hasSize(100);
+    }
+
+    @Test
+    public void policyProviderExceptions() {
+        PolicyAuditProvider provider = new PolicyAuditProvider();
+
+        assertThatThrownBy(() -> {
+            provider.createAuditRecords(null, null);
+        }).hasMessageMatching(String.format(FIELD_IS_NULL, "dao"));
+
+        assertThatThrownBy(() -> {
+            provider.createAuditRecords(pfDao, null);
+        }).hasMessageMatching(String.format(FIELD_IS_NULL, "audits"));
+
+        assertThatThrownBy(() -> {
+            provider.getAuditRecords(null, NUMBER_RECORDS);
+        }).hasMessageMatching(String.format(FIELD_IS_NULL, "dao"));
+
+        assertThatThrownBy(() -> {
+            provider.getAuditRecords(pfDao, null);
+        }).hasMessageMatching(String.format(FIELD_IS_NULL, "numRecords"));
+
+        assertThatThrownBy(() -> {
+            provider.getAuditRecords(null, AuditFilter.builder().build(), NUMBER_RECORDS);
+        }).hasMessageMatching(String.format(FIELD_IS_NULL, "dao"));
+
+        assertThatThrownBy(() -> {
+            provider.getAuditRecords(pfDao, AuditFilter.builder().build(), null);
+        }).hasMessageMatching(String.format(FIELD_IS_NULL, "numRecords"));
+
+        assertThatThrownBy(() -> {
+            provider.getAuditRecords(pfDao, null, NUMBER_RECORDS);
+        }).hasMessageMatching(String.format(FIELD_IS_NULL, "auditFilter"));
+    }
+
+    private List<PolicyAudit> generatePolicyAudits(Instant date, String group, ToscaConceptIdentifier policy) {
+        // @formatter:off
+        PolicyAudit deploy = PolicyAudit.builder()
+                                        .pdpGroup(group)
+                                        .pdpType("pdpType")
+                                        .policy(policy)
+                                        .action(AuditAction.DEPLOYMENT)
+                                        .timestamp(date.truncatedTo(ChronoUnit.SECONDS))
+                                        .build();
+
+        PolicyAudit undeploy = PolicyAudit.builder()
+                                        .pdpGroup(group)
+                                        .pdpType("pdpType")
+                                        .policy(policy)
+                                        .action(AuditAction.UNDEPLOYMENT)
+                                        .timestamp(date.plusSeconds(1).truncatedTo(ChronoUnit.SECONDS))
+                                        .build();
+        // @formatter:on
+
+        return List.of(deploy, undeploy);
+    }
+}
diff --git a/models-pap/src/test/resources/META-INF/persistence.xml b/models-pap/src/test/resources/META-INF/persistence.xml
new file mode 100644 (file)
index 0000000..58e2dec
--- /dev/null
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ============LICENSE_START=======================================================
+   Copyright (C) 2021 Nordix Foundation.
+  ================================================================================
+  Licensed 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.
+
+  SPDX-License-Identifier: Apache-2.0
+  ============LICENSE_END=========================================================
+-->
+
+<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
+    <persistence-unit name="ToscaConceptTest" transaction-type="RESOURCE_LOCAL">
+        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
+
+        <class>org.onap.policy.models.pap.persistence.concepts.JpaPolicyAudit</class>
+
+        <properties>
+            <property name="eclipselink.target-database" value="MySQL" />
+            <property name="eclipselink.ddl-generation" value="drop-and-create-tables" />
+            <property name="eclipselink.ddl-generation.output-mode" value="database" />
+            <property name="eclipselink.logging.level" value="INFO" />
+
+            <property name="eclipselink.logging.level" value="ALL" />
+            <property name="eclipselink.logging.level.jpa" value="ALL" />
+            <property name="eclipselink.logging.level.ddl" value="ALL" />
+            <property name="eclipselink.logging.level.connection" value="ALL" />
+            <property name="eclipselink.logging.level.sql" value="ALL" />
+            <property name="eclipselink.logging.level.transaction" value="ALL" />
+            <property name="eclipselink.logging.level.sequencing" value="ALL" />
+            <property name="eclipselink.logging.level.server" value="ALL" />
+            <property name="eclipselink.logging.level.query" value="ALL" />
+            <property name="eclipselink.logging.level.properties" value="ALL" />
+        </properties>
+    </persistence-unit>
+</persistence>