diff options
author | Jim Hahn <jrh3@att.com> | 2019-04-12 10:00:05 -0400 |
---|---|---|
committer | Jim Hahn <jrh3@att.com> | 2019-04-13 20:21:36 -0400 |
commit | d641bd5f3c4f6333e56ea85d95abb3dc124d4f4b (patch) | |
tree | b62566d613d90db4d03a87431c300f741dccfd81 /main/src | |
parent | fb057952d747a91139bce6e259b775bf57fc158f (diff) |
Support group-oriented REST API
Added code to support PDP group create/update & delete REST API calls.
The create/update request can also be used to deploy/undeploy policies.
It is assumed that the create/update request specifies a valid
list of supported policy types for each subgroup (i.e., PDP type).
Updated due to elimination of "version" from group in policy/models.
Added/updated junits.
Change-Id: I1916d9b17dfd5f12129c6f6a2fcf54e706662c10
Issue-ID: POLICY-1542
Signed-off-by: Jim Hahn <jrh3@att.com>
Diffstat (limited to 'main/src')
22 files changed, 1418 insertions, 170 deletions
diff --git a/main/src/main/java/org/onap/policy/pap/main/rest/depundep/GroupData.java b/main/src/main/java/org/onap/policy/pap/main/rest/depundep/GroupData.java index 25a886bd..f97107f7 100644 --- a/main/src/main/java/org/onap/policy/pap/main/rest/depundep/GroupData.java +++ b/main/src/main/java/org/onap/policy/pap/main/rest/depundep/GroupData.java @@ -24,26 +24,58 @@ import lombok.Getter; import org.onap.policy.models.pdp.concepts.PdpGroup; /** - * PdpGroup data, which includes the old group, that's in the DB, and possibly a new - * group, that must be added to the DB. + * PdpGroup data, only the latest copy of the group is retained. */ -@Getter public class GroupData { + @Getter private PdpGroup group; + private enum State { + UNCHANGED, CREATED, UPDATED + } + /** - * {@code True} if the group has been updated, {@code false} otherwise. + * State of the group. */ - private boolean updated = false; + private State state; /** - * Constructs the object. + * Constructs the object, for an existing group. * * @param group the group that is in the DB */ public GroupData(PdpGroup group) { + this(group, false); + } + + /** + * Constructs the object. + * + * @param group the group that is in, or to be added to, the DB + * @param isNew {@code true} if this is a new group, {@code false} otherwise + */ + public GroupData(PdpGroup group, boolean isNew) { this.group = group; + this.state = (isNew ? State.CREATED : State.UNCHANGED); + } + + /** + * Determines if the group is new. + * + * @return {@code true} if the group is new, {@code false} otherwise + */ + public boolean isNew() { + return (state == State.CREATED); + } + + /** + * Determines if the group has been updated. + * + * @return {@code true} if the group has been updated, {@code false} otherwise + */ + public boolean isUpdated() { + return (state == State.UPDATED); } /** @@ -57,7 +89,10 @@ public class GroupData { "expected group " + this.group.getName() + ", but received " + newGroup.getName()); } - this.updated = true; this.group = newGroup; + + if (state == State.UNCHANGED) { + state = State.UPDATED; + } } } diff --git a/main/src/main/java/org/onap/policy/pap/main/rest/depundep/PdpGroupDeleteControllerV1.java b/main/src/main/java/org/onap/policy/pap/main/rest/depundep/PdpGroupDeleteControllerV1.java index ca638f3e..08781857 100644 --- a/main/src/main/java/org/onap/policy/pap/main/rest/depundep/PdpGroupDeleteControllerV1.java +++ b/main/src/main/java/org/onap/policy/pap/main/rest/depundep/PdpGroupDeleteControllerV1.java @@ -82,51 +82,7 @@ public class PdpGroupDeleteControllerV1 extends PapRestControllerV1 { public Response deleteGroup(@HeaderParam(REQUEST_ID_NAME) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId, @ApiParam(value = "PDP Group Name", required = true) @PathParam("name") String groupName) { - Pair<Status, PdpGroupDeleteResponse> pair = provider.deleteGroup(groupName, null); - - return addLoggingHeaders(addVersionControlHeaders(Response.status(pair.getLeft())), requestId) - .entity(pair.getRight()).build(); - } - - /** - * Deletes a particular version of a PDP group. - * - * @param requestId request ID used in ONAP logging - * @param groupName name of the PDP group to be deleted - * @param version version to be deleted - * @return a response - */ - // @formatter:off - @DELETE - @Path("pdps/groups/{name}/versions/{version}") - @ApiOperation(value = "Delete version of a PDP Group", - notes = "Deletes a version of PDP Group, returning optional error details", - response = PdpGroupDeleteResponse.class, - tags = {"Policy Administration (PAP) API"}, - authorizations = @Authorization(value = AUTHORIZATION_TYPE), - responseHeaders = { - @ResponseHeader(name = VERSION_MINOR_NAME, description = VERSION_MINOR_DESCRIPTION, - response = String.class), - @ResponseHeader(name = VERSION_PATCH_NAME, description = VERSION_PATCH_DESCRIPTION, - response = String.class), - @ResponseHeader(name = VERSION_LATEST_NAME, description = VERSION_LATEST_DESCRIPTION, - response = String.class), - @ResponseHeader(name = REQUEST_ID_NAME, description = REQUEST_ID_HDR_DESCRIPTION, - response = UUID.class)}, - extensions = {@Extension(name = EXTENSION_NAME, - properties = {@ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION), - @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE)})}) - @ApiResponses(value = {@ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE), - @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE), - @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)}) - // @formatter:on - - public Response deleteGroupVersion( - @HeaderParam(REQUEST_ID_NAME) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId, - @ApiParam(value = "PDP Group Name", required = true) @PathParam("name") String groupName, - @ApiParam(value = "PDP Group Version", required = true) @PathParam("version") String version) { - - Pair<Status, PdpGroupDeleteResponse> pair = provider.deleteGroup(groupName, version); + Pair<Status, PdpGroupDeleteResponse> pair = provider.deleteGroup(groupName); return addLoggingHeaders(addVersionControlHeaders(Response.status(pair.getLeft())), requestId) .entity(pair.getRight()).build(); diff --git a/main/src/main/java/org/onap/policy/pap/main/rest/depundep/PdpGroupDeleteProvider.java b/main/src/main/java/org/onap/policy/pap/main/rest/depundep/PdpGroupDeleteProvider.java index 6457efae..7d47b3e2 100644 --- a/main/src/main/java/org/onap/policy/pap/main/rest/depundep/PdpGroupDeleteProvider.java +++ b/main/src/main/java/org/onap/policy/pap/main/rest/depundep/PdpGroupDeleteProvider.java @@ -27,6 +27,7 @@ import org.onap.policy.models.base.PfModelException; import org.onap.policy.models.pap.concepts.PdpGroupDeleteResponse; import org.onap.policy.models.pdp.concepts.PdpGroup; import org.onap.policy.models.pdp.concepts.PdpSubGroup; +import org.onap.policy.models.pdp.enums.PdpState; import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy; import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyIdentifier; import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyIdentifierOptVersion; @@ -52,47 +53,73 @@ public class PdpGroupDeleteProvider extends ProviderBase<PdpGroupDeleteResponse> * Deletes a PDP group. * * @param groupName name of the PDP group to be deleted - * @param version group version to delete; may be {@code null} if the group has only - * one version * @return a pair containing the status and the response */ - public Pair<Response.Status, PdpGroupDeleteResponse> deleteGroup(String groupName, String version) { + public Pair<Response.Status, PdpGroupDeleteResponse> deleteGroup(String groupName) { - PdpGroupDeleteResponse resp = new PdpGroupDeleteResponse(); - resp.setErrorDetails("not implemented yet"); + return process(groupName, this::deleteGroup); + } + + /** + * Deletes a PDP group. + * + * @param data session data + * @param groupName name of the PDP group to be deleted + */ + private void deleteGroup(SessionData data, String groupName) { + try { + PdpGroup group = data.getGroup(groupName); + if (group == null) { + throw new PolicyPapRuntimeException("group not found"); + } + + if (group.getPdpGroupState() == PdpState.ACTIVE) { + throw new PolicyPapRuntimeException("group is still " + PdpState.ACTIVE); + } - return Pair.of(Response.Status.INTERNAL_SERVER_ERROR, resp); + data.deleteGroupFromDb(group); + + } catch (PfModelException e) { + // no need to log the error here, as it will be logged by the invoker + logger.warn("failed to delete group: {}", groupName); + throw new PolicyPapRuntimeException(DB_ERROR_MSG, e); + + } catch (RuntimeException e) { + // no need to log the error here, as it will be logged by the invoker + logger.warn("failed to delete group: {}", groupName); + throw e; + } } /** - * Deletes a PDP policy. + * Undeploys a policy. * * @param policyIdent identifier of the policy to be undeployed * @return a pair containing the status and the response */ public Pair<Response.Status, PdpGroupDeleteResponse> undeploy(ToscaPolicyIdentifierOptVersion policyIdent) { - return process(policyIdent, this::deletePolicy); + return process(policyIdent, this::undeployPolicy); } /** - * Deletes a policy from its groups. + * Undeploys a policy from its groups. * * @param data session data * @param ident identifier of the policy to be deleted */ - private void deletePolicy(SessionData data, ToscaPolicyIdentifierOptVersion ident) { + private void undeployPolicy(SessionData data, ToscaPolicyIdentifierOptVersion ident) { try { processPolicy(data, ident); } catch (PfModelException e) { // no need to log the error here, as it will be logged by the invoker - logger.warn("failed to deploy policy: {}", ident); + logger.warn("failed to undeploy policy: {}", ident); throw new PolicyPapRuntimeException(DB_ERROR_MSG, e); } catch (RuntimeException e) { // no need to log the error here, as it will be logged by the invoker - logger.warn("failed to deploy policy: {}", ident); + logger.warn("failed to undeploy policy: {}", ident); throw e; } } @@ -104,6 +131,9 @@ public class PdpGroupDeleteProvider extends ProviderBase<PdpGroupDeleteResponse> return resp; } + /** + * Returns a function that will remove the specified policy from a subgroup. + */ @Override protected BiFunction<PdpGroup, PdpSubGroup, Boolean> makeUpdater(ToscaPolicy policy) { ToscaPolicyIdentifier desiredIdent = policy.getIdentifier(); diff --git a/main/src/main/java/org/onap/policy/pap/main/rest/depundep/PdpGroupDeployControllerV1.java b/main/src/main/java/org/onap/policy/pap/main/rest/depundep/PdpGroupDeployControllerV1.java index 6d4dc7ed..bdec3562 100644 --- a/main/src/main/java/org/onap/policy/pap/main/rest/depundep/PdpGroupDeployControllerV1.java +++ b/main/src/main/java/org/onap/policy/pap/main/rest/depundep/PdpGroupDeployControllerV1.java @@ -82,7 +82,7 @@ public class PdpGroupDeployControllerV1 extends PapRestControllerV1 { public Response deployGroup(@HeaderParam(REQUEST_ID_NAME) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId, @ApiParam(value = "List of PDP Group Configuration", required = true) PdpGroups groups) { - Pair<Status, PdpGroupDeployResponse> pair = provider.deployGroup(groups); + Pair<Status, PdpGroupDeployResponse> pair = provider.createOrUpdateGroups(groups); return addLoggingHeaders(addVersionControlHeaders(Response.status(pair.getLeft())), requestId) .entity(pair.getRight()).build(); diff --git a/main/src/main/java/org/onap/policy/pap/main/rest/depundep/PdpGroupDeployProvider.java b/main/src/main/java/org/onap/policy/pap/main/rest/depundep/PdpGroupDeployProvider.java index eef14388..cb2d1e34 100644 --- a/main/src/main/java/org/onap/policy/pap/main/rest/depundep/PdpGroupDeployProvider.java +++ b/main/src/main/java/org/onap/policy/pap/main/rest/depundep/PdpGroupDeployProvider.java @@ -20,10 +20,22 @@ package org.onap.policy.pap.main.rest.depundep; +import com.att.aft.dme2.internal.apache.commons.lang.ObjectUtils; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.function.BiFunction; +import java.util.function.Consumer; import javax.ws.rs.core.Response; import org.apache.commons.lang3.tuple.Pair; +import org.onap.policy.common.parameters.BeanValidationResult; +import org.onap.policy.common.parameters.ObjectValidationResult; +import org.onap.policy.common.parameters.ValidationResult; +import org.onap.policy.common.parameters.ValidationStatus; import org.onap.policy.common.utils.services.Registry; import org.onap.policy.models.base.PfModelException; import org.onap.policy.models.pap.concepts.PdpDeployPolicies; @@ -31,7 +43,7 @@ import org.onap.policy.models.pap.concepts.PdpGroupDeployResponse; import org.onap.policy.models.pdp.concepts.PdpGroup; import org.onap.policy.models.pdp.concepts.PdpGroups; import org.onap.policy.models.pdp.concepts.PdpSubGroup; -import org.onap.policy.models.pdp.concepts.PdpUpdate; +import org.onap.policy.models.pdp.enums.PdpState; import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy; import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyIdentifier; import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyIdentifierOptVersion; @@ -61,28 +73,297 @@ public class PdpGroupDeployProvider extends ProviderBase<PdpGroupDeployResponse> } /** - * Deploys or updates PDP groups. + * Creates or updates PDP groups. * - * @param groups PDP group configurations + * @param groups PDP group configurations to be created or updated * @return a pair containing the status and the response */ - public Pair<Response.Status, PdpGroupDeployResponse> deployGroup(PdpGroups groups) { - return process(groups, this::deployGroups); + public Pair<Response.Status, PdpGroupDeployResponse> createOrUpdateGroups(PdpGroups groups) { + ValidationResult result = groups.validatePapRest(); + + if (!result.isValid()) { + String msg = result.getResult().trim(); + logger.warn(msg); + return Pair.of(Response.Status.INTERNAL_SERVER_ERROR, makeResponse(msg)); + } + + return process(groups, this::createOrUpdate); } /** - * Deploys or updates PDP groups. + * Creates or updates PDP groups. This is the method that does the actual work. * * @param data session data * @param groups PDP group configurations - * @return a list of requests that should be sent to configure the PDPs */ - private List<PdpUpdate> deployGroups(SessionData data, PdpGroups groups) { - throw new PolicyPapRuntimeException("not implemented yet"); + private void createOrUpdate(SessionData data, PdpGroups groups) { + BeanValidationResult result = new BeanValidationResult("groups", groups); + + for (PdpGroup group : groups.getGroups()) { + PdpGroup dbgroup = data.getGroup(group.getName()); + + if (dbgroup == null) { + result.addResult(addGroup(data, group)); + + } else { + result.addResult(updateGroup(data, dbgroup, group)); + } + } + + if (!result.isValid()) { + throw new PolicyPapRuntimeException(result.getResult().trim()); + } + } + + /** + * Adds a new group. + * + * @param data session data + * @param group the group to be added + * @return the validation result + */ + private ValidationResult addGroup(SessionData data, PdpGroup group) { + BeanValidationResult result = new BeanValidationResult(group.getName(), group); + + validateGroupOnly(group, result); + if (!result.isValid()) { + return result; + } + + // default to active + if (group.getPdpGroupState() == null) { + group.setPdpGroupState(PdpState.ACTIVE); + } + + for (PdpSubGroup subgrp : group.getPdpSubgroups()) { + result.addResult(addSubGroup(data, subgrp)); + } + + if (result.isValid()) { + data.create(group); + } + + return result; + } + + /** + * Performs additional validations of a group, but does not examine the subgroups. + * + * @param group the group to be validated + * @param result the validation result + */ + private void validateGroupOnly(PdpGroup group, BeanValidationResult result) { + if (group.getPdpGroupState() == null) { + return; + } + + switch (group.getPdpGroupState()) { + case ACTIVE: + case PASSIVE: + break; + + default: + result.addResult(new ObjectValidationResult("pdpGroupState", group.getPdpGroupState(), + ValidationStatus.INVALID, "must be null, ACTIVE, or PASSIVE")); + break; + } + } + + /** + * Updates an existing group. + * + * @param data session data + * @param dbgroup the group, as it appears within the DB + * @param group the group to be added + * @return the validation result + */ + private ValidationResult updateGroup(SessionData data, PdpGroup dbgroup, PdpGroup group) { + BeanValidationResult result = new BeanValidationResult(group.getName(), group); + + if (!ObjectUtils.equals(dbgroup.getProperties(), group.getProperties())) { + result.addResult(new ObjectValidationResult("properties", "", ValidationStatus.INVALID, + "cannot change properties")); + } + + // create a map of existing subgroups + Map<String, PdpSubGroup> type2sub = new HashMap<>(); + dbgroup.getPdpSubgroups().forEach(subgrp -> type2sub.put(subgrp.getPdpType(), subgrp)); + + boolean updated = updateField(dbgroup.getDescription(), group.getDescription(), dbgroup::setDescription); + + for (PdpSubGroup subgrp : group.getPdpSubgroups()) { + PdpSubGroup dbsub = type2sub.get(subgrp.getPdpType()); + BeanValidationResult subResult = new BeanValidationResult(subgrp.getPdpType(), subgrp); + + if (dbsub == null) { + updated = true; + subResult.addResult(addSubGroup(data, subgrp)); + dbgroup.getPdpSubgroups().add(subgrp); + + } else { + updated = updateSubGroup(data, group, dbsub, subgrp, subResult) || updated; + } + + result.addResult(subResult); + } + + if (result.isValid() && updated) { + data.update(group); + } + + return result; + } + + /** + * Updates a field, if the new value is different than the old value. + * + * @param oldValue old value + * @param newValue new value + * @param setter function to set the field to the new value + * @return {@code true} if the field was updated, {@code false} if it already matched + * the new value + */ + private <T> boolean updateField(T oldValue, T newValue, Consumer<T> setter) { + if (oldValue == newValue) { + return false; + } + + if (oldValue != null && oldValue.equals(newValue)) { + return false; + } + + setter.accept(newValue); + return true; + } + + /** + * Adds a new subgroup. + * + * @param data session data + * @param subgrp the subgroup to be added + * @return the validation result + */ + private ValidationResult addSubGroup(SessionData data, PdpSubGroup subgrp) { + subgrp.setCurrentInstanceCount(0); + subgrp.setPdpInstances(Collections.emptyList()); + + BeanValidationResult result = new BeanValidationResult(subgrp.getPdpType(), subgrp); + + result.addResult(validatePolicies(data, subgrp)); + + return result; } /** - * Deploys or updates PDP policies. + * Updates an existing subgroup. + * + * @param data session data + * @param dbgroup the group, from the DB, containing the subgroup + * @param dbsub the subgroup, from the DB + * @param subgrp the subgroup to be updated + * @param container container for additional validation results + * @return {@code true} if the subgroup content was changed, {@code false} if there + * were no changes + */ + private boolean updateSubGroup(SessionData data, PdpGroup dbgroup, PdpSubGroup dbsub, PdpSubGroup subgrp, + BeanValidationResult container) { + + // perform additional validations first + if (!validateSubGroup(data, dbsub, subgrp, container)) { + return false; + } + + /* + * first, apply the changes about which the PDPs care + */ + boolean updated = updateList(dbsub.getPolicies(), subgrp.getPolicies(), dbsub::setPolicies); + + // publish any changes to the PDPs + if (updated) { + makeUpdates(data, dbgroup, dbsub); + } + + /* + * now make any remaining changes + */ + updated = updateList(dbsub.getSupportedPolicyTypes(), subgrp.getSupportedPolicyTypes(), + dbsub::setSupportedPolicyTypes) || updated; + + return updateField(dbsub.getDesiredInstanceCount(), subgrp.getDesiredInstanceCount(), + dbsub::setDesiredInstanceCount) || updated; + } + + /** + * Performs additional validations of a subgroup. + * + * @param data session data + * @param dbsub the subgroup, from the DB + * @param subgrp the subgroup to be validated + * @param container container for additional validation results + * @return {@code true} if the subgroup is valid, {@code false} otherwise + */ + private boolean validateSubGroup(SessionData data, PdpSubGroup dbsub, PdpSubGroup subgrp, + BeanValidationResult container) { + + BeanValidationResult result = new BeanValidationResult(subgrp.getPdpType(), subgrp); + + if (!ObjectUtils.equals(dbsub.getProperties(), subgrp.getProperties())) { + result.addResult(new ObjectValidationResult("properties", "", ValidationStatus.INVALID, + "cannot change properties")); + } + + result.addResult(validatePolicies(data, subgrp)); + container.addResult(result); + + return result.isValid(); + } + + /** + * Updates a DB list with items from a new list. + * + * @param dblist the list from the DB + * @param newList the new list + * @param setter function to set the new list + * @return {@code true} if the list changed, {@code false} if the lists were the same + */ + private <T> boolean updateList(List<T> dblist, List<T> newList, Consumer<List<T>> setter) { + + Set<T> dbTypes = new HashSet<T>(dblist); + Set<T> newTypes = new HashSet<T>(newList); + + if (dbTypes.equals(newTypes)) { + return false; + } + + setter.accept(new ArrayList<>(newTypes)); + + return true; + } + + /** + * Performs additional validations of the policies within a subgroup. + * + * @param data session data + * @param subgrp the subgroup to be validated + * @param result the validation result + */ + private ValidationResult validatePolicies(SessionData data, PdpSubGroup subgrp) { + BeanValidationResult result = new BeanValidationResult(subgrp.getPdpType(), subgrp); + + for (ToscaPolicyIdentifier ident : subgrp.getPolicies()) { + ToscaPolicy policy = data.getPolicy(ident); + + if (!subgrp.getSupportedPolicyTypes().contains(policy.getTypeIdentifier())) { + result.addResult(new ObjectValidationResult("policy", ident, ValidationStatus.INVALID, + "not a supported policy for the subgroup")); + } + } + + return result; + } + + /** + * Deploys or updates PDP policies using the simple API. * * @param policies PDP policies * @return a pair containing the status and the response @@ -92,7 +373,8 @@ public class PdpGroupDeployProvider extends ProviderBase<PdpGroupDeployResponse> } /** - * Deploys or updates PDP policies using the simple API. + * Deploys or updates PDP policies using the simple API. This is the method that does + * the actual work. * * @param data session data * @param extPolicies external PDP policies @@ -118,6 +400,9 @@ public class PdpGroupDeployProvider extends ProviderBase<PdpGroupDeployResponse> } } + /** + * Adds a policy to a subgroup, if it isn't there already. + */ @Override protected BiFunction<PdpGroup, PdpSubGroup, Boolean> makeUpdater(ToscaPolicy policy) { ToscaPolicyIdentifier desiredIdent = policy.getIdentifier(); diff --git a/main/src/main/java/org/onap/policy/pap/main/rest/depundep/ProviderBase.java b/main/src/main/java/org/onap/policy/pap/main/rest/depundep/ProviderBase.java index d3519197..5e0fb71a 100644 --- a/main/src/main/java/org/onap/policy/pap/main/rest/depundep/ProviderBase.java +++ b/main/src/main/java/org/onap/policy/pap/main/rest/depundep/ProviderBase.java @@ -32,6 +32,7 @@ import org.onap.policy.models.base.PfModelException; import org.onap.policy.models.pap.concepts.SimpleResponse; import org.onap.policy.models.pdp.concepts.Pdp; import org.onap.policy.models.pdp.concepts.PdpGroup; +import org.onap.policy.models.pdp.concepts.PdpStateChange; import org.onap.policy.models.pdp.concepts.PdpSubGroup; import org.onap.policy.models.pdp.concepts.PdpUpdate; import org.onap.policy.models.provider.PolicyModelsProvider; @@ -59,7 +60,6 @@ import org.slf4j.LoggerFactory; */ public abstract class ProviderBase<R extends SimpleResponse> { private static final String DEPLOY_FAILED = "failed to deploy/undeploy policies"; - public static final String DB_ERROR_MSG = "DB error"; private static final Logger logger = LoggerFactory.getLogger(ProviderBase.class); @@ -100,7 +100,7 @@ public abstract class ProviderBase<R extends SimpleResponse> { synchronized (updateLock) { // list of requests to be published to the PDPs - Collection<PdpUpdate> requests = Collections.emptyList(); + Collection<Pair<PdpUpdate, PdpStateChange>> requests = Collections.emptyList(); try (PolicyModelsProvider dao = daoFactory.create()) { @@ -110,7 +110,7 @@ public abstract class ProviderBase<R extends SimpleResponse> { // make all of the DB updates data.updateDb(); - requests = data.getPdpUpdates(); + requests = data.getPdpRequests(); } catch (PfModelException e) { logger.warn(DEPLOY_FAILED, e); @@ -127,7 +127,7 @@ public abstract class ProviderBase<R extends SimpleResponse> { // publish the requests - requests.forEach(requestMap::addRequest); + requests.forEach(pair -> requestMap.addRequest(pair.getLeft(), pair.getRight())); } return Pair.of(Response.Status.OK, makeResponse(null)); @@ -236,13 +236,7 @@ public abstract class ProviderBase<R extends SimpleResponse> { updated = true; - /* - * generate an UPDATE for each PDP instance. Since the group is active, we - * assume that the PDP is, too, thus no need for a STATE-CHANGE. - */ - for (Pdp pdpInstance : subgroup.getPdpInstances()) { - data.addUpdate(makeUpdate(data, group, subgroup, pdpInstance)); - } + makeUpdates(data, group, subgroup); } @@ -253,19 +247,32 @@ public abstract class ProviderBase<R extends SimpleResponse> { } /** + * Makes UPDATE messages for each PDP in a subgroup. + * + * @param data session data + * @param group group containing the subgroup + * @param subgroup subgroup whose PDPs should receive messages + */ + protected void makeUpdates(SessionData data, PdpGroup group, PdpSubGroup subgroup) { + for (Pdp pdp : subgroup.getPdpInstances()) { + data.addUpdate(makeUpdate(data, group, subgroup, pdp)); + } + } + + /** * Makes an UPDATE message for a particular PDP. * * @param data session data * @param group group to which the PDP should belong * @param subgroup subgroup to which the PDP should belong - * @param pdpInstance identifies the PDP of interest + * @param pdp the PDP of interest * @return a new UPDATE message */ - private PdpUpdate makeUpdate(SessionData data, PdpGroup group, PdpSubGroup subgroup, Pdp pdpInstance) { + private PdpUpdate makeUpdate(SessionData data, PdpGroup group, PdpSubGroup subgroup, Pdp pdp) { PdpUpdate update = new PdpUpdate(); - update.setName(pdpInstance.getInstanceId()); + update.setName(pdp.getInstanceId()); update.setDescription(group.getDescription()); update.setPdpGroup(group.getName()); update.setPdpSubgroup(subgroup.getPdpType()); diff --git a/main/src/main/java/org/onap/policy/pap/main/rest/depundep/SessionData.java b/main/src/main/java/org/onap/policy/pap/main/rest/depundep/SessionData.java index 6b42b19b..0537c80b 100644 --- a/main/src/main/java/org/onap/policy/pap/main/rest/depundep/SessionData.java +++ b/main/src/main/java/org/onap/policy/pap/main/rest/depundep/SessionData.java @@ -26,9 +26,11 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import org.apache.commons.lang3.tuple.Pair; import org.onap.policy.models.base.PfModelException; import org.onap.policy.models.pdp.concepts.PdpGroup; import org.onap.policy.models.pdp.concepts.PdpGroupFilter; +import org.onap.policy.models.pdp.concepts.PdpStateChange; import org.onap.policy.models.pdp.concepts.PdpUpdate; import org.onap.policy.models.pdp.enums.PdpState; import org.onap.policy.models.provider.PolicyModelsProvider; @@ -57,9 +59,9 @@ public class SessionData { private final Map<ToscaPolicyTypeIdentifier, List<GroupData>> type2groups = new HashMap<>(); /** - * Maps a PDP name to its most recently generated update request. + * Maps a PDP name to its most recently generated update and state-change requests. */ - private final Map<String, PdpUpdate> pdpUpdates = new HashMap<>(); + private final Map<String, Pair<PdpUpdate, PdpStateChange>> pdpRequests = new HashMap<>(); /** * Maps a policy's identifier to the policy. @@ -96,17 +98,11 @@ public class SessionData { try { List<ToscaPolicy> lst = dao.getPolicyList(ident.getName(), ident.getVersion()); - if (lst.isEmpty()) { throw new PolicyPapRuntimeException( "cannot find policy: " + ident.getName() + " " + ident.getVersion()); } - if (lst.size() > 1) { - throw new PolicyPapRuntimeException( - "too many policies match: " + ident.getName() + " " + ident.getVersion()); - } - return lst.get(0); } catch (PfModelException e) { @@ -117,22 +113,67 @@ public class SessionData { } /** + * Adds an update and state-change to the sets, replacing any previous entries for the + * given PDP. + * + * @param update the update to be added + * @param change the state-change to be added + */ + public void addRequests(PdpUpdate update, PdpStateChange change) { + if (!update.getName().equals(change.getName())) { + throw new IllegalArgumentException("PDP name mismatch " + update.getName() + ", " + change.getName()); + } + + pdpRequests.put(update.getName(), Pair.of(update, change)); + } + + /** * Adds an update to the set of updates, replacing any previous entry for the given * PDP. * * @param update the update to be added */ public void addUpdate(PdpUpdate update) { - pdpUpdates.put(update.getName(), update); + pdpRequests.compute(update.getName(), (name, data) -> Pair.of(update, (data == null ? null : data.getRight()))); } /** - * Gets the accumulated UPDATE requests. + * Adds a state-change to the set of state-change requests, replacing any previous entry for the given + * PDP. * - * @return the UPDATE requests + * @param change the state-change to be added */ - public Collection<PdpUpdate> getPdpUpdates() { - return pdpUpdates.values(); + public void addStateChange(PdpStateChange change) { + pdpRequests.compute(change.getName(), (name, data) -> Pair.of((data == null ? null : data.getLeft()), change)); + } + + /** + * Gets the accumulated PDP requests. + * + * @return the PDP requests + */ + public Collection<Pair<PdpUpdate, PdpStateChange>> getPdpRequests() { + return pdpRequests.values(); + } + + /** + * Gets the accumulated PDP update requests. + * + * @return the PDP requests + */ + public List<PdpUpdate> getPdpUpdates() { + return pdpRequests.values().stream().filter(req -> req.getLeft() != null).map(Pair::getLeft) + .collect(Collectors.toList()); + } + + /** + * Gets the accumulated PDP state-change requests. + * + * @return the PDP requests + */ + public List<PdpStateChange> getPdpStateChanges() { + return pdpRequests.values().stream().filter(req -> req.getRight() != null).map(Pair::getRight) + .collect(Collectors.toList()); } /** @@ -163,6 +204,19 @@ public class SessionData { } /** + * Creates a group. + * + * @param newGroup the new group + */ + public void create(PdpGroup newGroup) { + String name = newGroup.getName(); + + if (groupCache.put(name, new GroupData(newGroup, true)) != null) { + throw new IllegalStateException("group already cached: " + name); + } + } + + /** * Updates a group. * * @param newGroup the updated group @@ -178,6 +232,34 @@ public class SessionData { } /** + * Gets the group by the given name. + * + * @param name name of the group to get + * @return the group, or {@code null} if it does not exist + * @throws PolicyPapRuntimeException if an error occurs + */ + public PdpGroup getGroup(String name) { + + GroupData data = groupCache.computeIfAbsent(name, key -> { + + try { + List<PdpGroup> lst = dao.getPdpGroups(key); + if (lst.isEmpty()) { + return null; + } + + return new GroupData(lst.get(0)); + + } catch (PfModelException e) { + throw new PolicyPapRuntimeException("cannot get group: " + name, e); + } + + }); + + return (data == null ? null : data.getGroup()); + } + + /** * Gets the active groups supporting the given policy. * * @param type desired policy type @@ -225,13 +307,28 @@ public class SessionData { * @throws PfModelException if an error occurs */ public void updateDb() throws PfModelException { - List<GroupData> updatedGroups = + // create new groups + List<GroupData> created = groupCache.values().stream().filter(GroupData::isNew).collect(Collectors.toList()); + if (!created.isEmpty()) { + dao.createPdpGroups(created.stream().map(GroupData::getGroup).collect(Collectors.toList())); + } + + // update existing groups + List<GroupData> updated = groupCache.values().stream().filter(GroupData::isUpdated).collect(Collectors.toList()); - if (updatedGroups.isEmpty()) { - return; + if (!updated.isEmpty()) { + dao.updatePdpGroups(updated.stream().map(GroupData::getGroup).collect(Collectors.toList())); } + } - // update the groups - dao.updatePdpGroups(updatedGroups.stream().map(GroupData::getGroup).collect(Collectors.toList())); + /** + * Deletes a group from the DB, immediately (i.e., without caching the request to be + * executed later). + * + * @param group the group to be deleted + * @throws PfModelException if an error occurs + */ + public void deleteGroupFromDb(PdpGroup group) throws PfModelException { + dao.deletePdpGroup(group.getName()); } } diff --git a/main/src/test/java/org/onap/policy/pap/main/rest/depundep/ProviderSuper.java b/main/src/test/java/org/onap/policy/pap/main/rest/depundep/ProviderSuper.java index 65b1234f..256d3af0 100644 --- a/main/src/test/java/org/onap/policy/pap/main/rest/depundep/ProviderSuper.java +++ b/main/src/test/java/org/onap/policy/pap/main/rest/depundep/ProviderSuper.java @@ -1,4 +1,4 @@ -/* +/*- * ============LICENSE_START======================================================= * ONAP PAP * ================================================================================ @@ -31,6 +31,7 @@ import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; import org.junit.Before; import org.mockito.ArgumentCaptor; import org.mockito.Captor; @@ -43,6 +44,7 @@ import org.onap.policy.common.utils.resources.ResourceUtils; import org.onap.policy.common.utils.services.Registry; import org.onap.policy.models.pdp.concepts.PdpGroup; import org.onap.policy.models.pdp.concepts.PdpGroups; +import org.onap.policy.models.pdp.concepts.PdpStateChange; import org.onap.policy.models.pdp.concepts.PdpUpdate; import org.onap.policy.models.provider.PolicyModelsProvider; import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy; @@ -61,7 +63,7 @@ public class ProviderSuper { /** - * Used to capture input to dao.updatePdpGroups(). + * Used to capture input to dao.updatePdpGroups() and dao.createPdpGroups(). */ @Captor private ArgumentCaptor<List<PdpGroup>> updateCaptor; @@ -118,7 +120,19 @@ public class ProviderSuper { } /** - * Gets the input to the method. + * Gets the input to the create() method. + * + * @return the input that was passed to the dao.updatePdpGroups() method + * @throws Exception if an error occurred + */ + protected List<PdpGroup> getGroupCreates() throws Exception { + verify(dao).createPdpGroups(updateCaptor.capture()); + + return copyList(updateCaptor.getValue()); + } + + /** + * Gets the input to the update() method. * * @return the input that was passed to the dao.updatePdpGroups() method * @throws Exception if an error occurred @@ -130,6 +144,20 @@ public class ProviderSuper { } /** + * Gets the state-changes that were added to the request map. + * + * @param count the number of times the method is expected to have been called + * @return the state-changes that were added to the request map + */ + protected List<PdpStateChange> getStateChangeRequests(int count) { + ArgumentCaptor<PdpStateChange> captor = ArgumentCaptor.forClass(PdpStateChange.class); + + verify(reqmap, times(count)).addRequest(any(), captor.capture()); + + return captor.getAllValues().stream().filter(req -> req != null).collect(Collectors.toList()); + } + + /** * Gets the updates that were added to the request map. * * @param count the number of times the method is expected to have been called @@ -138,9 +166,9 @@ public class ProviderSuper { protected List<PdpUpdate> getUpdateRequests(int count) { ArgumentCaptor<PdpUpdate> captor = ArgumentCaptor.forClass(PdpUpdate.class); - verify(reqmap, times(count)).addRequest(captor.capture()); + verify(reqmap, times(count)).addRequest(captor.capture(), any()); - return new ArrayList<>(captor.getAllValues()); + return captor.getAllValues().stream().filter(req -> req != null).collect(Collectors.toList()); } /** @@ -162,7 +190,17 @@ public class ProviderSuper { * @return a list of groups */ protected List<PdpGroup> loadGroups(String fileName) { - return loadFile(fileName, PdpGroups.class).getGroups(); + return loadPdpGroups(fileName).getGroups(); + } + + /** + * Loads a PdpGroups. + * + * @param fileName name of the file from which to load + * @return a PdpGroups + */ + protected PdpGroups loadPdpGroups(String fileName) { + return loadFile(fileName, PdpGroups.class); } /** diff --git a/main/src/test/java/org/onap/policy/pap/main/rest/depundep/TestGroupData.java b/main/src/test/java/org/onap/policy/pap/main/rest/depundep/TestGroupData.java index 8313d193..0c14aa2f 100644 --- a/main/src/test/java/org/onap/policy/pap/main/rest/depundep/TestGroupData.java +++ b/main/src/test/java/org/onap/policy/pap/main/rest/depundep/TestGroupData.java @@ -50,19 +50,42 @@ public class TestGroupData { } @Test - public void test() { + public void testNew() { + data = new GroupData(oldGroup, true); + assertSame(oldGroup, data.getGroup()); + + assertTrue(data.isNew()); + assertFalse(data.isUpdated()); + + data.update(newGroup); + assertTrue(data.isNew()); + assertFalse(data.isUpdated()); + assertSame(newGroup, data.getGroup()); + + // repeat with a new group + newGroup = new PdpGroup(oldGroup); + data.update(newGroup); + assertTrue(data.isNew()); + assertFalse(data.isUpdated()); + assertSame(newGroup, data.getGroup()); + } + + @Test + public void testUpdateOnly() { assertFalse(data.isUpdated()); assertSame(oldGroup, data.getGroup()); data.update(newGroup); assertTrue(data.isUpdated()); + assertFalse(data.isNew()); assertSame(newGroup, data.getGroup()); // repeat newGroup = new PdpGroup(oldGroup); data.update(newGroup); assertTrue(data.isUpdated()); + assertFalse(data.isNew()); assertSame(newGroup, data.getGroup()); // incorrect name diff --git a/main/src/test/java/org/onap/policy/pap/main/rest/depundep/TestPdpGroupDeleteControllerV1.java b/main/src/test/java/org/onap/policy/pap/main/rest/depundep/TestPdpGroupDeleteControllerV1.java index c3928789..458ca9e7 100644 --- a/main/src/test/java/org/onap/policy/pap/main/rest/depundep/TestPdpGroupDeleteControllerV1.java +++ b/main/src/test/java/org/onap/policy/pap/main/rest/depundep/TestPdpGroupDeleteControllerV1.java @@ -31,14 +31,13 @@ import org.onap.policy.pap.main.rest.CommonPapRestServer; public class TestPdpGroupDeleteControllerV1 extends CommonPapRestServer { - private static final String NOT_IMPLEMENTED_YET = "not implemented yet"; + private static final String GROUP_NOT_FOUND = "group not found"; private static final String DELETE_GROUP_ENDPOINT = "pdps/groups"; private static final String DELETE_POLICIES_ENDPOINT = "pdps/policies"; @Test public void testSwagger() throws Exception { super.testSwagger(DELETE_GROUP_ENDPOINT + "/{name}"); - super.testSwagger(DELETE_GROUP_ENDPOINT + "/{name}/versions/{version}"); super.testSwagger(DELETE_POLICIES_ENDPOINT + "/{name}"); super.testSwagger(DELETE_POLICIES_ENDPOINT + "/{name}/versions/{version}"); @@ -52,31 +51,12 @@ public class TestPdpGroupDeleteControllerV1 extends CommonPapRestServer { Response rawresp = invocationBuilder.delete(); PdpGroupDeleteResponse resp = rawresp.readEntity(PdpGroupDeleteResponse.class); assertEquals(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), rawresp.getStatus()); - assertEquals(NOT_IMPLEMENTED_YET, resp.getErrorDetails()); + assertEquals(GROUP_NOT_FOUND, resp.getErrorDetails()); rawresp = invocationBuilder.delete(); resp = rawresp.readEntity(PdpGroupDeleteResponse.class); assertEquals(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), rawresp.getStatus()); - assertEquals(NOT_IMPLEMENTED_YET, resp.getErrorDetails()); - - // verify it fails when no authorization info is included - checkUnauthRequest(uri, req -> req.delete()); - } - - @Test - public void testDeleteGroupVersion() throws Exception { - String uri = DELETE_GROUP_ENDPOINT + "/my-name/versions/1.2.3"; - - Invocation.Builder invocationBuilder = sendRequest(uri); - Response rawresp = invocationBuilder.delete(); - PdpGroupDeleteResponse resp = rawresp.readEntity(PdpGroupDeleteResponse.class); - assertEquals(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), rawresp.getStatus()); - assertEquals(NOT_IMPLEMENTED_YET, resp.getErrorDetails()); - - rawresp = invocationBuilder.delete(); - resp = rawresp.readEntity(PdpGroupDeleteResponse.class); - assertEquals(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), rawresp.getStatus()); - assertEquals(NOT_IMPLEMENTED_YET, resp.getErrorDetails()); + assertEquals(GROUP_NOT_FOUND, resp.getErrorDetails()); // verify it fails when no authorization info is included checkUnauthRequest(uri, req -> req.delete()); diff --git a/main/src/test/java/org/onap/policy/pap/main/rest/depundep/TestPdpGroupDeleteProvider.java b/main/src/test/java/org/onap/policy/pap/main/rest/depundep/TestPdpGroupDeleteProvider.java index dbb7951e..c0b08644 100644 --- a/main/src/test/java/org/onap/policy/pap/main/rest/depundep/TestPdpGroupDeleteProvider.java +++ b/main/src/test/java/org/onap/policy/pap/main/rest/depundep/TestPdpGroupDeleteProvider.java @@ -1,4 +1,4 @@ -/* +/*- * ============LICENSE_START======================================================= * ONAP PAP * ================================================================================ @@ -29,7 +29,9 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.Arrays; @@ -57,7 +59,6 @@ public class TestPdpGroupDeleteProvider extends ProviderSuper { private static final String POLICY1_NAME = "policyA"; private static final String POLICY1_VERSION = "1.2.3"; private static final String GROUP1_NAME = "groupA"; - private static final String GROUP1_VERSION = "200.2.3"; private MyProvider prov; private SessionData session; @@ -91,10 +92,62 @@ public class TestPdpGroupDeleteProvider extends ProviderSuper { } @Test - public void testDeleteGroup() { - Pair<Status, PdpGroupDeleteResponse> pair = prov.deleteGroup(GROUP1_NAME, GROUP1_VERSION); - assertEquals(Status.INTERNAL_SERVER_ERROR, pair.getLeft()); - assertEquals("not implemented yet", pair.getRight().getErrorDetails()); + public void testDeleteGroup_Inctive() throws Exception { + PdpGroup group = loadGroup("deleteGroup.json"); + + when(session.getGroup(GROUP1_NAME)).thenReturn(group); + + prov.deleteGroup(GROUP1_NAME); + + verify(session).deleteGroupFromDb(group); + + // should be no PDP requests + verify(session, never()).addRequests(any(), any()); + } + + @Test + public void testDeleteGroup_Active() throws Exception { + PdpGroup group = loadGroup("deleteGroup.json"); + + group.setPdpGroupState(PdpState.ACTIVE); + + when(session.getGroup(GROUP1_NAME)).thenReturn(group); + + assertThatThrownBy(() -> prov.deleteGroup(GROUP1_NAME)).isInstanceOf(PolicyPapRuntimeException.class) + .hasMessage("group is still ACTIVE"); + } + + @Test + public void testDeleteGroup_NotFound() throws Exception { + assertThatThrownBy(() -> prov.deleteGroup(GROUP1_NAME)).isInstanceOf(PolicyPapRuntimeException.class) + .hasMessage("group not found"); + } + + @Test + public void testDeleteGroup_Inactive() throws Exception { + PdpGroup group = loadGroup("deleteGroup.json"); + + when(session.getGroup(GROUP1_NAME)).thenReturn(group); + + prov.deleteGroup(GROUP1_NAME); + + verify(session).deleteGroupFromDb(group); + + // should done no requests for the PDPs + verify(session, never()).addRequests(any(), any()); + } + + @Test + public void testDeleteGroup_DaoEx() throws Exception { + PdpGroup group = loadGroup("deleteGroup.json"); + + when(session.getGroup(GROUP1_NAME)).thenReturn(group); + + PfModelException ex = new PfModelException(Status.BAD_REQUEST, EXPECTED_EXCEPTION); + doThrow(ex).when(session).deleteGroupFromDb(group); + + assertThatThrownBy(() -> prov.deleteGroup(GROUP1_NAME)).isInstanceOf(PolicyPapRuntimeException.class) + .hasMessage(ProviderBase.DB_ERROR_MSG); } @Test diff --git a/main/src/test/java/org/onap/policy/pap/main/rest/depundep/TestPdpGroupDeployProvider.java b/main/src/test/java/org/onap/policy/pap/main/rest/depundep/TestPdpGroupDeployProvider.java index 8ca205cd..cad73d92 100644 --- a/main/src/test/java/org/onap/policy/pap/main/rest/depundep/TestPdpGroupDeployProvider.java +++ b/main/src/test/java/org/onap/policy/pap/main/rest/depundep/TestPdpGroupDeployProvider.java @@ -22,6 +22,7 @@ package org.onap.policy.pap.main.rest.depundep; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Mockito.never; @@ -29,7 +30,12 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.onap.policy.pap.main.rest.depundep.ProviderBase.DB_ERROR_MSG; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.TreeMap; +import java.util.stream.Collectors; import javax.ws.rs.core.Response.Status; import org.apache.commons.lang3.tuple.Pair; import org.junit.AfterClass; @@ -39,14 +45,21 @@ import org.onap.policy.common.utils.services.Registry; import org.onap.policy.models.base.PfModelException; import org.onap.policy.models.pap.concepts.PdpDeployPolicies; import org.onap.policy.models.pap.concepts.PdpGroupDeployResponse; +import org.onap.policy.models.pdp.concepts.PdpGroup; import org.onap.policy.models.pdp.concepts.PdpGroups; +import org.onap.policy.models.pdp.concepts.PdpSubGroup; import org.onap.policy.models.pdp.concepts.PdpUpdate; +import org.onap.policy.models.pdp.enums.PdpState; +import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy; +import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyIdentifier; +import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyTypeIdentifier; public class TestPdpGroupDeployProvider extends ProviderSuper { private static final String EXPECTED_EXCEPTION = "expected exception"; private static final Object REQUEST_FAILED_MSG = "request failed"; private static final String POLICY1_NAME = "policyA"; + private static final String POLICY2_NAME = "policyB"; private static final String POLICY1_VERSION = "1.2.3"; private static final String GROUP1_NAME = "groupA"; private static final String PDP1_TYPE = "pdpTypeA"; @@ -79,10 +92,365 @@ public class TestPdpGroupDeployProvider extends ProviderSuper { } @Test - public void testDeployGroup() { - Pair<Status, PdpGroupDeployResponse> pair = prov.deployGroup(new PdpGroups()); + public void testCreateOrUpdateGroups() throws Exception { + Pair<Status, PdpGroupDeployResponse> pair = prov.createOrUpdateGroups(loadPdpGroups("emptyGroups.json")); + assertEquals(Status.OK, pair.getLeft()); + assertNull(pair.getRight().getErrorDetails()); + + // no groups, so no action should have been taken + assertNoGroupAction(); + } + + @Test + public void testCreateOrUpdateGroups_InvalidRequest() throws Exception { + Pair<Status, PdpGroupDeployResponse> pair = prov.createOrUpdateGroups(new PdpGroups()); + assertEquals(Status.INTERNAL_SERVER_ERROR, pair.getLeft()); + assertTrue(pair.getRight().getErrorDetails().contains("is null")); + + assertNoGroupAction(); + } + + @Test + public void testCreateOrUpdate_Invalid() throws Exception { + PdpGroups groups = loadPdpGroups("createGroups.json"); + groups.getGroups().get(0).setPdpGroupState(PdpState.TERMINATED); + + Pair<Status, PdpGroupDeployResponse> pair = prov.createOrUpdateGroups(groups); assertEquals(Status.INTERNAL_SERVER_ERROR, pair.getLeft()); - assertEquals("not implemented yet", pair.getRight().getErrorDetails()); + assertTrue(pair.getRight().getErrorDetails().contains("pdpGroupState")); + + assertNoGroupAction(); + } + + @Test + public void testAddGroup() throws Exception { + PdpGroups groups = loadPdpGroups("createGroups.json"); + PdpGroup group = groups.getGroups().get(0); + group.setPdpGroupState(PdpState.PASSIVE); + + assertEquals(Status.OK, prov.createOrUpdateGroups(groups).getLeft()); + + // should not have updated the state + assertEquals(PdpState.PASSIVE, group.getPdpGroupState()); + + assertSame(group, getGroupCreates().get(0)); + } + + @Test + public void testAddGroup_Invalid() throws Exception { + PdpGroups groups = loadPdpGroups("createGroups.json"); + groups.getGroups().get(0).setPdpGroupState(PdpState.TERMINATED); + + Pair<Status, PdpGroupDeployResponse> pair = prov.createOrUpdateGroups(groups); + assertEquals(Status.INTERNAL_SERVER_ERROR, pair.getLeft()); + assertTrue(pair.getRight().getErrorDetails().contains("pdpGroupState")); + + assertNoGroupAction(); + } + + @Test + public void testAddGroup_InvalidSubGroup() throws Exception { + PdpGroups groups = loadPdpGroups("createGroups.json"); + + // policy won't match supported type + groups.getGroups().get(0).getPdpSubgroups().get(0).getSupportedPolicyTypes().get(0).setVersion("99.99.99"); + + Pair<Status, PdpGroupDeployResponse> pair = prov.createOrUpdateGroups(groups); + assertEquals(Status.INTERNAL_SERVER_ERROR, pair.getLeft()); + assertTrue(pair.getRight().getErrorDetails().contains("supported policy")); + + assertNoGroupAction(); + } + + @Test + public void testValidateGroupOnly_NullState() { + PdpGroups groups = loadPdpGroups("createGroups.json"); + groups.getGroups().get(0).setPdpGroupState(null); + assertEquals(Status.OK, prov.createOrUpdateGroups(groups).getLeft()); + } + + @Test + public void testValidateGroupOnly_Active() { + PdpGroups groups = loadPdpGroups("createGroups.json"); + groups.getGroups().get(0).setPdpGroupState(PdpState.ACTIVE); + assertEquals(Status.OK, prov.createOrUpdateGroups(groups).getLeft()); + } + + @Test + public void testValidateGroupOnly_Passive() { + PdpGroups groups = loadPdpGroups("createGroups.json"); + groups.getGroups().get(0).setPdpGroupState(PdpState.PASSIVE); + assertEquals(Status.OK, prov.createOrUpdateGroups(groups).getLeft()); + } + + @Test + public void testValidateGroupOnly_Invalid() { + PdpGroups groups = loadPdpGroups("createGroups.json"); + groups.getGroups().get(0).setPdpGroupState(PdpState.TERMINATED); + + Pair<Status, PdpGroupDeployResponse> pair = prov.createOrUpdateGroups(groups); + assertEquals(Status.INTERNAL_SERVER_ERROR, pair.getLeft()); + assertTrue(pair.getRight().getErrorDetails().contains("pdpGroupState")); + } + + @Test + public void testUpdateGroup() throws Exception { + PdpGroups groups = loadPdpGroups("createGroups.json"); + + // DB group = new group + PdpGroup group = new PdpGroup(groups.getGroups().get(0)); + when(dao.getPdpGroups(group.getName())).thenReturn(Arrays.asList(group)); + + assertEquals(Status.OK, prov.createOrUpdateGroups(groups).getLeft()); + + assertNoGroupAction(); + } + + @Test + public void testUpdateGroup_PropertiesChanged() throws Exception { + PdpGroups groups = loadPdpGroups("createGroups.json"); + + PdpGroup group = new PdpGroup(groups.getGroups().get(0)); + group.setProperties(new TreeMap<>()); + + when(dao.getPdpGroups(group.getName())).thenReturn(Arrays.asList(group)); + + Pair<Status, PdpGroupDeployResponse> pair = prov.createOrUpdateGroups(groups); + assertEquals(Status.INTERNAL_SERVER_ERROR, pair.getLeft()); + assertTrue(pair.getRight().getErrorDetails().contains("properties")); + + assertNoGroupAction(); + } + + @Test + public void testUpdateGroup_NewDescription() throws Exception { + PdpGroups groups = loadPdpGroups("createGroups.json"); + PdpGroup newgrp = groups.getGroups().get(0); + PdpGroup group = new PdpGroup(newgrp); + group.setDescription("old description"); + when(dao.getPdpGroups(group.getName())).thenReturn(Arrays.asList(group)); + + assertEquals(Status.OK, prov.createOrUpdateGroups(groups).getLeft()); + + assertGroupUpdateOnly(group); + + assertEquals(group.getDescription(), "my description"); + assertEquals(newgrp.toString(), group.toString()); + } + + @Test + public void testUpdateGroup_NewSubGroup() throws Exception { + PdpGroups groups = loadPdpGroups("createGroupsNewSub.json"); + PdpGroup group = loadPdpGroups("createGroups.json").getGroups().get(0); + when(dao.getPdpGroups(group.getName())).thenReturn(Arrays.asList(group)); + + assertEquals(Status.OK, prov.createOrUpdateGroups(groups).getLeft()); + + PdpGroup newgrp = groups.getGroups().get(0); + assertEquals(newgrp.toString(), group.toString()); + assertGroupUpdateOnly(group); + } + + @Test + public void testUpdateGroup_UpdatedSubGroup() throws Exception { + PdpGroups groups = loadPdpGroups("createGroups.json"); + PdpGroup newgrp = groups.getGroups().get(0); + PdpGroup group = new PdpGroup(newgrp); + when(dao.getPdpGroups(group.getName())).thenReturn(Arrays.asList(group)); + + // something different in this subgroup + group.getPdpSubgroups().get(0).setDesiredInstanceCount(10); + + assertEquals(Status.OK, prov.createOrUpdateGroups(groups).getLeft()); + + assertEquals(newgrp.toString(), group.toString()); + assertGroupUpdateOnly(group); + } + + @Test + public void testUpdateGroup_MultipleChanges() throws Exception { + PdpGroups groups = loadPdpGroups("createGroups.json"); + PdpGroup newgrp = groups.getGroups().get(0); + PdpGroup group = new PdpGroup(newgrp); + when(dao.getPdpGroups(group.getName())).thenReturn(Arrays.asList(group)); + + PdpSubGroup subgrp = newgrp.getPdpSubgroups().get(0); + subgrp.setDesiredInstanceCount(30); + subgrp.getPolicies().add(new ToscaPolicyIdentifier(POLICY2_NAME, POLICY1_VERSION)); + subgrp.getSupportedPolicyTypes().add(new ToscaPolicyTypeIdentifier("typeX", "9.8.7")); + + when(dao.getPolicyList(POLICY2_NAME, POLICY1_VERSION)).thenReturn(loadPolicies("createGroupNewPolicy.json")); + + assertEquals(Status.OK, prov.createOrUpdateGroups(groups).getLeft()); + + Collections.sort(newgrp.getPdpSubgroups().get(0).getPolicies()); + Collections.sort(group.getPdpSubgroups().get(0).getPolicies()); + + assertEquals(newgrp.toString(), group.toString()); + + // this requires a PDP UPDATE message + assertGroupUpdate(group, subgrp); + } + + @Test + public void testUpdateField_Unchanged() throws Exception { + PdpGroups groups = loadPdpGroups("createGroups.json"); + PdpGroup newgrp = groups.getGroups().get(0); + PdpGroup group = new PdpGroup(newgrp); + when(dao.getPdpGroups(group.getName())).thenReturn(Arrays.asList(group)); + + assertEquals(Status.OK, prov.createOrUpdateGroups(groups).getLeft()); + + assertNoGroupAction(); + } + + @Test + public void testUpdateField_WasNull() throws Exception { + PdpGroups groups = loadPdpGroups("createGroups.json"); + PdpGroup newgrp = groups.getGroups().get(0); + PdpGroup group = new PdpGroup(newgrp); + when(dao.getPdpGroups(group.getName())).thenReturn(Arrays.asList(group)); + + group.setDescription(null); + + assertEquals(Status.OK, prov.createOrUpdateGroups(groups).getLeft()); + + assertEquals(newgrp.toString(), group.toString()); + assertGroupUpdateOnly(group); + } + + @Test + public void testUpdateField_NowNull() throws Exception { + PdpGroups groups = loadPdpGroups("createGroups.json"); + PdpGroup newgrp = groups.getGroups().get(0); + PdpGroup group = new PdpGroup(newgrp); + when(dao.getPdpGroups(group.getName())).thenReturn(Arrays.asList(group)); + + newgrp.setDescription(null); + + assertEquals(Status.OK, prov.createOrUpdateGroups(groups).getLeft()); + + assertEquals(newgrp.toString(), group.toString()); + assertGroupUpdateOnly(group); + } + + @Test + public void testUpdateField_Changed() throws Exception { + PdpGroups groups = loadPdpGroups("createGroups.json"); + PdpGroup newgrp = groups.getGroups().get(0); + PdpGroup group = new PdpGroup(newgrp); + when(dao.getPdpGroups(group.getName())).thenReturn(Arrays.asList(group)); + + newgrp.setDescription(group.getDescription() + "-changed"); + + assertEquals(Status.OK, prov.createOrUpdateGroups(groups).getLeft()); + + assertEquals(newgrp.toString(), group.toString()); + assertGroupUpdateOnly(group); + } + + @Test + public void testAddSubGroup() throws Exception { + PdpGroups groups = loadPdpGroups("createGroupsNewSub.json"); + PdpGroup group = loadPdpGroups("createGroups.json").getGroups().get(0); + when(dao.getPdpGroups(group.getName())).thenReturn(Arrays.asList(group)); + + assertEquals(Status.OK, prov.createOrUpdateGroups(groups).getLeft()); + + PdpGroup newgrp = groups.getGroups().get(0); + + PdpSubGroup newsub = newgrp.getPdpSubgroups().get(1); + newsub.setCurrentInstanceCount(0); + newsub.setPdpInstances(new ArrayList<>(0)); + + assertEquals(newgrp.toString(), group.toString()); + assertGroupUpdateOnly(group); + } + + @Test + public void testUpdateSubGroup_Invalid() throws Exception { + PdpGroups groups = loadPdpGroups("createGroups.json"); + PdpGroup newgrp = groups.getGroups().get(0); + PdpGroup group = new PdpGroup(newgrp); + when(dao.getPdpGroups(group.getName())).thenReturn(Arrays.asList(group)); + + // change properties + newgrp.getPdpSubgroups().get(0).setProperties(new TreeMap<>()); + + Pair<Status, PdpGroupDeployResponse> pair = prov.createOrUpdateGroups(groups); + assertEquals(Status.INTERNAL_SERVER_ERROR, pair.getLeft()); + assertTrue(pair.getRight().getErrorDetails().contains("properties")); + + assertNoGroupAction(); + } + + @Test + public void testUpdateSubGroup_SupportedPolicies() throws Exception { + PdpGroups groups = loadPdpGroups("createGroups.json"); + PdpGroup newgrp = groups.getGroups().get(0); + PdpGroup group = new PdpGroup(newgrp); + when(dao.getPdpGroups(group.getName())).thenReturn(Arrays.asList(group)); + + newgrp.getPdpSubgroups().get(0).getSupportedPolicyTypes().add(new ToscaPolicyTypeIdentifier("typeX", "9.8.7")); + + assertEquals(Status.OK, prov.createOrUpdateGroups(groups).getLeft()); + + assertEquals(newgrp.toString(), group.toString()); + assertGroupUpdateOnly(group); + } + + @Test + public void testUpdateSubGroup_DesiredCount() throws Exception { + PdpGroups groups = loadPdpGroups("createGroups.json"); + PdpGroup newgrp = groups.getGroups().get(0); + PdpGroup group = new PdpGroup(newgrp); + when(dao.getPdpGroups(group.getName())).thenReturn(Arrays.asList(group)); + + newgrp.getPdpSubgroups().get(0).setDesiredInstanceCount(20); + + assertEquals(Status.OK, prov.createOrUpdateGroups(groups).getLeft()); + + assertEquals(newgrp.toString(), group.toString()); + assertGroupUpdateOnly(group); + } + + @Test + public void testUpdateSubGroup_Policies() throws Exception { + PdpGroups groups = loadPdpGroups("createGroups.json"); + PdpGroup newgrp = groups.getGroups().get(0); + PdpGroup group = new PdpGroup(newgrp); + when(dao.getPdpGroups(group.getName())).thenReturn(Arrays.asList(group)); + + PdpSubGroup subgrp = newgrp.getPdpSubgroups().get(0); + subgrp.getPolicies().add(new ToscaPolicyIdentifier(POLICY2_NAME, POLICY1_VERSION)); + + when(dao.getPolicyList(POLICY2_NAME, POLICY1_VERSION)).thenReturn(loadPolicies("createGroupNewPolicy.json")); + + assertEquals(Status.OK, prov.createOrUpdateGroups(groups).getLeft()); + + Collections.sort(newgrp.getPdpSubgroups().get(0).getPolicies()); + Collections.sort(group.getPdpSubgroups().get(0).getPolicies()); + + assertEquals(newgrp.toString(), group.toString()); + + // this requires a PDP UPDATE message + assertGroupUpdate(group, subgrp); + } + + @Test + public void testValidateSubGroup_PropertiesMismatch() throws Exception { + PdpGroups groups = loadPdpGroups("createGroups.json"); + PdpGroup newgrp = groups.getGroups().get(0); + PdpGroup group = new PdpGroup(newgrp); + when(dao.getPdpGroups(group.getName())).thenReturn(Arrays.asList(group)); + + newgrp.setProperties(new TreeMap<>()); + + Pair<Status, PdpGroupDeployResponse> pair = prov.createOrUpdateGroups(groups); + assertEquals(Status.INTERNAL_SERVER_ERROR, pair.getLeft()); + assertTrue(pair.getRight().getErrorDetails().contains("properties")); + + assertNoGroupAction(); } @Test @@ -180,6 +548,44 @@ public class TestPdpGroupDeployProvider extends ProviderSuper { assertTrue(update.getPolicies().contains(policy1)); } + private void assertNoGroupAction() throws Exception { + verify(dao, never()).createPdpGroups(any()); + verify(dao, never()).updatePdpGroups(any()); + verify(reqmap, never()).addRequest(any(), any()); + } + + private void assertGroupUpdate(PdpGroup group, PdpSubGroup subgrp) throws Exception { + verify(dao, never()).createPdpGroups(any()); + + assertEquals(0, getStateChangeRequests(1).size()); + + List<PdpUpdate> pdpUpdates = getUpdateRequests(1); + assertEquals(1, pdpUpdates.size()); + + PdpUpdate pdpUpdate = pdpUpdates.get(0); + assertEquals("pdpA", pdpUpdate.getName()); + assertEquals(group.getName(), pdpUpdate.getPdpGroup()); + + assertEquals(subgrp.getPdpType(), pdpUpdate.getPdpSubgroup()); + + List<ToscaPolicyIdentifier> pdpPolicies = + pdpUpdate.getPolicies().stream().map(ToscaPolicy::getIdentifier).collect(Collectors.toList()); + Collections.sort(pdpPolicies); + + assertEquals(subgrp.getPolicies().toString(), pdpPolicies.toString()); + + List<PdpGroup> updates = getGroupUpdates(); + assertEquals(Arrays.asList(group), updates); + } + + private void assertGroupUpdateOnly(PdpGroup group) throws Exception { + verify(dao, never()).createPdpGroups(any()); + verify(reqmap, never()).addRequest(any(), any()); + + List<PdpGroup> updates = getGroupUpdates(); + assertEquals(Arrays.asList(group), updates); + } + /** * Loads a standard request. * diff --git a/main/src/test/java/org/onap/policy/pap/main/rest/depundep/TestSessionData.java b/main/src/test/java/org/onap/policy/pap/main/rest/depundep/TestSessionData.java index fd351c89..2eac4324 100644 --- a/main/src/test/java/org/onap/policy/pap/main/rest/depundep/TestSessionData.java +++ b/main/src/test/java/org/onap/policy/pap/main/rest/depundep/TestSessionData.java @@ -20,9 +20,11 @@ package org.onap.policy.pap.main.rest.depundep; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.mockito.Matchers.any; import static org.mockito.Mockito.never; @@ -35,20 +37,23 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.Iterator; import java.util.List; import javax.ws.rs.core.Response.Status; +import org.apache.commons.lang3.tuple.Pair; import org.junit.Before; import org.junit.Test; import org.onap.policy.models.base.PfModelException; import org.onap.policy.models.pdp.concepts.PdpGroup; +import org.onap.policy.models.pdp.concepts.PdpStateChange; import org.onap.policy.models.pdp.concepts.PdpUpdate; import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy; import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyIdentifier; import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyTypeIdentifier; +import org.onap.policy.pap.main.PolicyPapRuntimeException; public class TestSessionData extends ProviderSuper { private static final String GROUP_NAME = "groupA"; - private static final String GROUP_NAME2 = "groupB"; private static final String PDP1 = "pdp_1"; private static final String PDP2 = "pdp_2"; private static final String PDP3 = "pdp_3"; @@ -58,6 +63,7 @@ public class TestSessionData extends ProviderSuper { private static final String POLICY_VERSION2 = POLICY_VERSION_PREFIX + "4"; private static final String POLICY_TYPE = "myType"; private static final String POLICY_TYPE_VERSION = "10.20.30"; + private static final String EXPECTED_EXCEPTION = "expected exception"; private SessionData session; private ToscaPolicyIdentifier ident; @@ -78,8 +84,8 @@ public class TestSessionData extends ProviderSuper { ident = new ToscaPolicyIdentifier(POLICY_NAME, POLICY_VERSION); type = new ToscaPolicyTypeIdentifier(POLICY_TYPE, POLICY_TYPE_VERSION); type2 = new ToscaPolicyTypeIdentifier(POLICY_TYPE, POLICY_TYPE_VERSION + "0"); - group1 = makeGroup(GROUP_NAME); - group2 = makeGroup(GROUP_NAME2); + group1 = loadGroup("group1.json"); + group2 = loadGroup("group2.json"); session = new SessionData(dao); } @@ -116,14 +122,6 @@ public class TestSessionData extends ProviderSuper { } @Test - public void testGetPolicy_TooMany() throws Exception { - ToscaPolicy policy = new ToscaPolicy(); - when(dao.getPolicyList(any(), any())).thenReturn(Arrays.asList(policy, policy)); - - assertThatThrownBy(() -> session.getPolicy(ident)).hasMessage("too many policies match: myPolicy 1.2.3"); - } - - @Test public void testGetPolicy_DaoEx() throws Exception { PfModelException ex = new PfModelException(Status.INTERNAL_SERVER_ERROR, "expected exception"); when(dao.getPolicyList(any(), any())).thenThrow(ex); @@ -132,6 +130,71 @@ public class TestSessionData extends ProviderSuper { } @Test + public void testAddRequests_testGetPdpStateChanges_testGetPdpUpdates() { + // pre-load with a update and state-change for other PDPs + PdpUpdate update2 = makeUpdate(PDP2); + session.addUpdate(update2); + + PdpStateChange change3 = makeStateChange(PDP3); + session.addStateChange(change3); + + // add requests + PdpUpdate update = makeUpdate(PDP1); + PdpStateChange change = makeStateChange(PDP1); + session.addRequests(update, change); + verifyRequests(update, update2, change, change3); + + /* + * repeat with a new pair + */ + update = makeUpdate(PDP1); + change = makeStateChange(PDP1); + session.addRequests(update, change); + verifyRequests(update, update2, change, change3); + + // just make an update this time + update = makeUpdate(PDP1); + session.addUpdate(update); + verifyRequests(update, update2, change, change3); + } + + private void verifyRequests(PdpUpdate update, PdpUpdate update2, PdpStateChange change, PdpStateChange change3) { + List<Pair<PdpUpdate, PdpStateChange>> requests = sort(session.getPdpRequests(), this::compare); + assertEquals(3, requests.size()); + + System.out.println(requests); + System.out.println(update); + + Iterator<Pair<PdpUpdate, PdpStateChange>> reqiter = requests.iterator(); + Pair<PdpUpdate, PdpStateChange> pair = reqiter.next(); + assertSame(update, pair.getLeft()); + assertSame(change, pair.getRight()); + + pair = reqiter.next(); + assertSame(update2, pair.getLeft()); + assertSame(null, pair.getRight()); + + pair = reqiter.next(); + assertSame(null, pair.getLeft()); + assertSame(change3, pair.getRight()); + + // verify individual lists + List<PdpUpdate> updates = Arrays.asList(update, update2); + assertEquals(sort(updates, this::compare), sort(session.getPdpUpdates(), this::compare)); + + List<PdpStateChange> changes = Arrays.asList(change, change3); + assertEquals(sort(changes, this::compare), sort(session.getPdpStateChanges(), this::compare)); + } + + @Test + public void testAddRequests_MismatchedNames() { + PdpUpdate update = makeUpdate(PDP1); + PdpStateChange change = makeStateChange(PDP2); + assertThatIllegalArgumentException().isThrownBy(() -> session.addRequests(update, change)) + .withMessage("PDP name mismatch pdp_1, pdp_2"); + } + + @Test public void testAddUpdate_testGetPdpUpdates() { // several different updates, but one duplicate PdpUpdate update1 = makeUpdate(PDP1); @@ -143,17 +206,40 @@ public class TestSessionData extends ProviderSuper { PdpUpdate update3 = makeUpdate(PDP3); session.addUpdate(update3); - List<PdpUpdate> lst = sort(session.getPdpUpdates(), this::compare); + List<PdpUpdate> lst = sort(getUpdateRequests(), this::compare); assertEquals(Arrays.asList(update1, update2, update3).toString(), lst.toString()); // overwrite one update2 = makeUpdate(PDP2); session.addUpdate(update2); - lst = sort(session.getPdpUpdates(), this::compare); + lst = sort(getUpdateRequests(), this::compare); assertEquals(Arrays.asList(update1, update2, update3).toString(), lst.toString()); } + @Test + public void testAddStateChange_testGetPdpStateChanges() { + // several different changes, but one duplicate + PdpStateChange change1 = makeStateChange(PDP1); + session.addStateChange(change1); + + PdpStateChange change2 = makeStateChange(PDP2); + session.addStateChange(change2); + + PdpStateChange change3 = makeStateChange(PDP3); + session.addStateChange(change3); + + List<PdpStateChange> lst = sort(getStateChangeRequests(), this::compare); + assertEquals(Arrays.asList(change1, change2, change3).toString(), lst.toString()); + + // overwrite one + change2 = makeStateChange(PDP2); + session.addStateChange(change2); + + lst = sort(getStateChangeRequests(), this::compare); + assertEquals(Arrays.asList(change1, change2, change3).toString(), lst.toString()); + } + private ToscaPolicy makePolicy(String name, String version) { ToscaPolicy policy = new ToscaPolicy(); @@ -183,12 +269,19 @@ public class TestSessionData extends ProviderSuper { assertThatThrownBy(() -> session.getPolicyMaxVersion(POLICY_NAME)).hasMessage("cannot find policy: myPolicy"); } - private PdpGroup makeGroup(String name) { - PdpGroup group = new PdpGroup(); - - group.setName(name); - - return group; + @Test + public void testCreate() throws Exception { + session.create(group1); + assertSame(group1, session.getGroup(group1.getName())); + + // can add another + session.create(group2); + assertSame(group1, session.getGroup(group1.getName())); + assertSame(group2, session.getGroup(group2.getName())); + + // cannot overwrite + assertThatIllegalStateException().isThrownBy(() -> session.create(group1)) + .withMessage("group already cached: groupA"); } @Test @@ -229,6 +322,48 @@ public class TestSessionData extends ProviderSuper { } @Test + public void testGetGroup() throws Exception { + when(dao.getPdpGroups(GROUP_NAME)).thenReturn(Arrays.asList(group1)); + + assertSame(group1, session.getGroup(GROUP_NAME)); + verify(dao).getPdpGroups(any()); + + // repeat + assertSame(group1, session.getGroup(GROUP_NAME)); + + // should not access dao again + verify(dao, times(1)).getPdpGroups(any()); + } + + @Test + public void testGetGroup_NotFound() throws Exception { + when(dao.getPdpGroups(GROUP_NAME)).thenReturn(Collections.emptyList()); + + assertNull(session.getGroup(GROUP_NAME)); + verify(dao).getPdpGroups(any()); + + // repeat + assertNull(session.getGroup(GROUP_NAME)); + + // SHOULD access dao again + verify(dao, times(2)).getPdpGroups(GROUP_NAME); + + // find it this time + when(dao.getPdpGroups(GROUP_NAME)).thenReturn(Arrays.asList(group1)); + assertSame(group1, session.getGroup(GROUP_NAME)); + verify(dao, times(3)).getPdpGroups(GROUP_NAME); + } + + @Test + public void testGetGroup_DaoEx() throws Exception { + PfModelException ex = new PfModelException(Status.BAD_REQUEST, EXPECTED_EXCEPTION); + when(dao.getPdpGroups(GROUP_NAME)).thenThrow(ex); + + assertThatThrownBy(() -> session.getGroup(GROUP_NAME)).isInstanceOf(PolicyPapRuntimeException.class) + .hasCause(ex); + } + + @Test public void testGetActivePdpGroupsByPolicyType() throws Exception { List<PdpGroup> groups = Arrays.asList(group1, group2); when(dao.getFilteredPdpGroups(any())).thenReturn(groups); @@ -263,10 +398,17 @@ public class TestSessionData extends ProviderSuper { @Test public void testUpdateDb() throws Exception { // force the groups into the cache - PdpGroup group3 = makeGroup("groupC"); + PdpGroup group3 = loadGroup("group3.json"); when(dao.getFilteredPdpGroups(any())).thenReturn(Arrays.asList(group1, group2, group3)); session.getActivePdpGroupsByPolicyType(type); + // create groups 4 & 5 + PdpGroup group4 = loadGroup("group4.json"); + session.create(group4); + + PdpGroup group5 = loadGroup("group5.json"); + session.create(group5); + // update group 1 when(dao.getFilteredPdpGroups(any())).thenReturn(Arrays.asList(group1)); PdpGroup newgrp1 = new PdpGroup(group1); @@ -281,13 +423,25 @@ public class TestSessionData extends ProviderSuper { PdpGroup newgrp3 = new PdpGroup(group3); session.update(newgrp3); + // update group 5 + when(dao.getFilteredPdpGroups(any())).thenReturn(Arrays.asList(group5)); + PdpGroup newgrp5 = new PdpGroup(group5); + session.update(newgrp5); + // push the changes to the DB session.updateDb(); + // expect one create for groups 4 & 5 (group5 replaced by newgrp5) + List<PdpGroup> creates = getGroupCreates(); + assertEquals(2, creates.size()); + assertSame(group4, creates.get(0)); + assertSame(newgrp5, creates.get(1)); + // expect one update for groups 1 & 3 - List<PdpGroup> changes = getGroupUpdates(); - assertSame(newgrp1, changes.get(0)); - assertSame(newgrp3, changes.get(1)); + List<PdpGroup> updates = getGroupUpdates(); + assertEquals(2, updates.size()); + assertSame(newgrp1, updates.get(0)); + assertSame(newgrp3, updates.get(1)); } @Test @@ -301,6 +455,13 @@ public class TestSessionData extends ProviderSuper { verify(dao, never()).updatePdpGroups(any()); } + @Test + public void testDeleteGroupFromDb() throws Exception { + session.deleteGroupFromDb(group1); + + verify(dao).deletePdpGroup(group1.getName()); + } + private PdpUpdate makeUpdate(String pdpName) { PdpUpdate update = new PdpUpdate(); @@ -309,6 +470,22 @@ public class TestSessionData extends ProviderSuper { return update; } + private PdpStateChange makeStateChange(String pdpName) { + PdpStateChange change = new PdpStateChange(); + + change.setName(pdpName); + + return change; + } + + private List<PdpUpdate> getUpdateRequests() { + return session.getPdpUpdates(); + } + + private List<PdpStateChange> getStateChangeRequests() { + return session.getPdpStateChanges(); + } + private <T> List<T> sort(Collection<T> collection, Comparator<T> comparator) { List<T> lst = new ArrayList<>(collection); Collections.sort(lst, comparator); @@ -316,7 +493,19 @@ public class TestSessionData extends ProviderSuper { return lst; } + private int compare(Pair<PdpUpdate, PdpStateChange> left, Pair<PdpUpdate, PdpStateChange> right) { + return getName(left).compareTo(getName(right)); + } + private int compare(PdpUpdate left, PdpUpdate right) { return left.getName().compareTo(right.getName()); } + + private int compare(PdpStateChange left, PdpStateChange right) { + return left.getName().compareTo(right.getName()); + } + + private String getName(Pair<PdpUpdate, PdpStateChange> pair) { + return (pair.getKey() != null ? pair.getKey().getName() : pair.getValue().getName()); + } } diff --git a/main/src/test/resources/simpleDeploy/createGroupNewPolicy.json b/main/src/test/resources/simpleDeploy/createGroupNewPolicy.json new file mode 100644 index 00000000..285db315 --- /dev/null +++ b/main/src/test/resources/simpleDeploy/createGroupNewPolicy.json @@ -0,0 +1,10 @@ +{ + "policies": [ + { + "name": "policyB", + "version": "1.2.3", + "type": "typeA", + "typeVersion": "100.2.3" + } + ] +} diff --git a/main/src/test/resources/simpleDeploy/createGroups.json b/main/src/test/resources/simpleDeploy/createGroups.json new file mode 100644 index 00000000..59c4eb87 --- /dev/null +++ b/main/src/test/resources/simpleDeploy/createGroups.json @@ -0,0 +1,39 @@ +{ + "groups": [ + { + "name": "groupA", + "version": "200.2.3", + "description": "my description", + "pdpGroupState": "ACTIVE", + "properties": { + "hello": "world" + }, + "pdpSubgroups": [ + { + "pdpType": "pdpTypeA", + "desiredInstanceCount": 1, + "properties": { + "abc": "def" + }, + "supportedPolicyTypes": [ + { + "name": "typeA", + "version": "100.2.3" + } + ], + "pdpInstances": [ + { + "instanceId": "pdpA" + } + ], + "policies": [ + { + "name": "policyA", + "version": "1.2.3" + } + ] + } + ] + } + ] +} diff --git a/main/src/test/resources/simpleDeploy/createGroupsNewSub.json b/main/src/test/resources/simpleDeploy/createGroupsNewSub.json new file mode 100644 index 00000000..033ced8c --- /dev/null +++ b/main/src/test/resources/simpleDeploy/createGroupsNewSub.json @@ -0,0 +1,56 @@ +{ + "groups": [ + { + "name": "groupA", + "version": "200.2.3", + "description": "my description", + "pdpGroupState": "ACTIVE", + "properties": { + "hello": "world" + }, + "pdpSubgroups": [ + { + "pdpType": "pdpTypeA", + "desiredInstanceCount": 1, + "properties": { + "abc": "def" + }, + "supportedPolicyTypes": [ + { + "name": "typeA", + "version": "100.2.3" + } + ], + "pdpInstances": [ + { + "instanceId": "pdpA" + } + ], + "policies": [ + { + "name": "policyA", + "version": "1.2.3" + } + ] + }, + { + "pdpType": "pdpTypeB", + "desiredInstanceCount": 1, + "currentInstanceCount": 22, + "supportedPolicyTypes": [ + { + "name": "typeA", + "version": "100.2.3" + } + ], + "pdpInstances": [ + { + "instanceId": "pdpB" + } + ], + "policies": [] + } + ] + } + ] +} diff --git a/main/src/test/resources/simpleDeploy/deleteGroup.json b/main/src/test/resources/simpleDeploy/deleteGroup.json new file mode 100644 index 00000000..6def3510 --- /dev/null +++ b/main/src/test/resources/simpleDeploy/deleteGroup.json @@ -0,0 +1,29 @@ +{ + "name": "groupA", + "pdpGroupState": "PASSIVE", + "pdpSubgroups": [ + { + "pdpType": "pdpTypeA", + "pdpInstances": [ + { + "instanceId": "pdpA_1" + }, + { + "instanceId": "pdpA_2" + } + ] + }, + { + "pdpType": "pdpTypeB", + "pdpInstances": [] + }, + { + "pdpType": "pdpTypeC", + "pdpInstances": [ + { + "instanceId": "pdpC_1" + } + ] + } + ] +} diff --git a/main/src/test/resources/simpleDeploy/group1.json b/main/src/test/resources/simpleDeploy/group1.json new file mode 100644 index 00000000..8df853c1 --- /dev/null +++ b/main/src/test/resources/simpleDeploy/group1.json @@ -0,0 +1,3 @@ +{ + "name": "groupA" +} diff --git a/main/src/test/resources/simpleDeploy/group2.json b/main/src/test/resources/simpleDeploy/group2.json new file mode 100644 index 00000000..fca12ad0 --- /dev/null +++ b/main/src/test/resources/simpleDeploy/group2.json @@ -0,0 +1,3 @@ +{ + "name": "groupB" +} diff --git a/main/src/test/resources/simpleDeploy/group3.json b/main/src/test/resources/simpleDeploy/group3.json new file mode 100644 index 00000000..4a6ac2d6 --- /dev/null +++ b/main/src/test/resources/simpleDeploy/group3.json @@ -0,0 +1,3 @@ +{ + "name": "groupC" +} diff --git a/main/src/test/resources/simpleDeploy/group4.json b/main/src/test/resources/simpleDeploy/group4.json new file mode 100644 index 00000000..c73ab0ac --- /dev/null +++ b/main/src/test/resources/simpleDeploy/group4.json @@ -0,0 +1,3 @@ +{ + "name": "groupD" +} diff --git a/main/src/test/resources/simpleDeploy/group5.json b/main/src/test/resources/simpleDeploy/group5.json new file mode 100644 index 00000000..4bd56272 --- /dev/null +++ b/main/src/test/resources/simpleDeploy/group5.json @@ -0,0 +1,3 @@ +{ + "name": "groupE" +} |