Skip to content

Commit

Permalink
Align applicability checks with text generation
Browse files Browse the repository at this point in the history
Notifiers were applied to events based on criteria that were different
from those used to generate textual representation of the events.
This sometimes lead to events with empty list of modifications
(e.g. in MID-5849). This commit aligns those two aspects.

Also we have refactored some years-old code e.g. cleaned up Event class
hierarchy. Quick applicability check is now carried out before nested
event handlers are invoked, making their execution more intuitive
and robust.

(cherry picked from commit fce73ae)
  • Loading branch information
mederly committed Mar 14, 2020
1 parent e937ccd commit aac7b85
Show file tree
Hide file tree
Showing 70 changed files with 2,644 additions and 2,215 deletions.
Expand Up @@ -13,16 +13,14 @@
import com.evolveum.midpoint.model.api.PipelineItem;
import com.evolveum.midpoint.notifications.api.NotificationManager;
import com.evolveum.midpoint.notifications.api.events.CustomEvent;
import com.evolveum.midpoint.notifications.api.events.Event;
import com.evolveum.midpoint.notifications.api.events.factory.CustomEventFactory;
import com.evolveum.midpoint.prism.PrismValue;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.EventHandlerType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.EventOperationType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.EventStatusType;
import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

Expand All @@ -34,13 +32,11 @@
@Component
public class NotifyExecutor extends BaseActionExecutor {

@Autowired
private LightweightIdentifierGenerator lightweightIdentifierGenerator;

@Autowired(required = false) // During some tests this might be unavailable
private NotificationManager notificationManager;

private static final Trace LOGGER = TraceManager.getTrace(NotifyExecutor.class);
@Autowired(required = false)
private CustomEventFactory customEventFactory;

private static final String NAME = "notify";
private static final String PARAM_SUBTYPE = "subtype";
Expand Down Expand Up @@ -80,18 +76,21 @@ public PipelineData execute(ActionExpressionType expression, PipelineData input,
if (notificationManager == null) {
throw new IllegalStateException("Notification manager is unavailable");
}
if (customEventFactory == null) {
throw new IllegalStateException("Custom event factory is unavailable");
}

int eventCount = 0;
if (forWholeInput) {
Event event = new CustomEvent(lightweightIdentifierGenerator, subtype, handler, input.getData(), operation, status, context.getChannel());
CustomEvent event = customEventFactory.createEvent(subtype, handler, input.getData(), operation, status, context.getChannel());
notificationManager.processEvent(event, context.getTask(), globalResult);
eventCount++;
} else {
for (PipelineItem item: input.getData()) {
PrismValue value = item.getValue();
OperationResult result = operationsHelper.createActionResult(item, this, context, globalResult);
context.checkTaskStop();
Event event = new CustomEvent(lightweightIdentifierGenerator, subtype, handler, value, operation, status, context.getChannel());
CustomEvent event = customEventFactory.createEvent(subtype, handler, value, operation, status, context.getChannel());
notificationManager.processEvent(event, context.getTask(), result);
eventCount++;
operationsHelper.trimAndCloneResult(result, globalResult, context);
Expand Down
Expand Up @@ -6,9 +6,9 @@
*/
package com.evolveum.midpoint.model.intest;

import com.evolveum.midpoint.notifications.api.events.CustomEvent;
import com.evolveum.midpoint.notifications.api.events.Event;
import com.evolveum.midpoint.notifications.api.transports.Message;
import com.evolveum.midpoint.notifications.impl.events.CustomEventImpl;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.PrismReferenceValue;
import com.evolveum.midpoint.prism.crypto.EncryptionException;
Expand Down Expand Up @@ -138,7 +138,15 @@ public void test100ModifyUserAddAccount() throws Exception {

// WHEN
TestUtil.displayWhen(TEST_NAME);
modifyUserAddAccount(USER_JACK_OID, ACCOUNT_JACK_DUMMY_FILE, task, result);
ObjectDelta<UserType> userDelta = createAddAccountDelta(USER_JACK_OID, ACCOUNT_JACK_DUMMY_FILE);
// This is to test for MID-5849. The applicability checking was not correct, so it passed even if there we no items
// to show in the notification.
userDelta.addModification(
deltaFor(UserType.class)
.item(UserType.F_CREDENTIALS, CredentialsType.F_PASSWORD, PasswordType.F_METADATA, MetadataType.F_CREATE_CHANNEL)
.replace("dummy")
.asItemDelta());
executeChanges(userDelta, null, task, result);

// THEN
TestUtil.displayThen(TEST_NAME);
Expand All @@ -151,7 +159,7 @@ public void test100ModifyUserAddAccount() throws Exception {
PrismObject<UserType> userJack = modelService.getObject(UserType.class, USER_JACK_OID, null, task, result);
assertUserJack(userJack);
UserType userJackType = userJack.asObjectable();
assertEquals("Unexpected number of accountRefs", 1, userJackType.getLinkRef().size());
assertEquals("Unexpected number of linkRefs", 1, userJackType.getLinkRef().size());
ObjectReferenceType accountRefType = userJackType.getLinkRef().get(0);
accountJackOid = accountRefType.getOid();
assertFalse("No accountRef oid", StringUtils.isBlank(accountJackOid));
Expand Down Expand Up @@ -548,7 +556,7 @@ public void test200SendSmsUsingGet() {

// WHEN
TestUtil.displayWhen(TEST_NAME);
Event event = new CustomEvent(lightweightIdentifierGenerator, "get", null,
Event event = new CustomEventImpl(lightweightIdentifierGenerator, "get", null,
"hello world", EventOperationType.ADD, EventStatusType.SUCCESS, null);
notificationManager.processEvent(event, task, result);

Expand All @@ -573,7 +581,7 @@ public void test210SendSmsUsingPost() {

// WHEN
TestUtil.displayWhen(TEST_NAME);
Event event = new CustomEvent(lightweightIdentifierGenerator, "post", null,
Event event = new CustomEventImpl(lightweightIdentifierGenerator, "post", null,
"hello world", EventOperationType.ADD, EventStatusType.SUCCESS, null);
notificationManager.processEvent(event, task, result);

Expand Down Expand Up @@ -606,7 +614,7 @@ public void test215SendSmsUsingGeneralPost() {

// WHEN
TestUtil.displayWhen(TEST_NAME);
Event event = new CustomEvent(lightweightIdentifierGenerator, "general-post", null,
Event event = new CustomEventImpl(lightweightIdentifierGenerator, "general-post", null,
"hello world", EventOperationType.ADD, EventStatusType.SUCCESS, null);
notificationManager.processEvent(event, task, result);

Expand Down Expand Up @@ -639,7 +647,7 @@ public void test220SendSmsViaProxy() {

// WHEN
TestUtil.displayWhen(TEST_NAME);
Event event = new CustomEvent(lightweightIdentifierGenerator, "get-via-proxy", null,
Event event = new CustomEventImpl(lightweightIdentifierGenerator, "get-via-proxy", null,
"hello world via proxy", EventOperationType.ADD, EventStatusType.SUCCESS, null);
notificationManager.processEvent(event, task, result);

Expand Down Expand Up @@ -667,7 +675,7 @@ public void test300CheckVariables() {

// WHEN
TestUtil.displayWhen(TEST_NAME);
Event event = new CustomEvent(lightweightIdentifierGenerator, "check-variables", null,
Event event = new CustomEventImpl(lightweightIdentifierGenerator, "check-variables", null,
"hello world", EventOperationType.ADD, EventStatusType.SUCCESS, null);
notificationManager.processEvent(event, task, result);

Expand Down
Expand Up @@ -4960,17 +4960,22 @@ protected ModelExecuteOptions nullToEmpty(ModelExecuteOptions options) {
}

protected void modifyUserAddAccount(String userOid, File accountFile, Task task, OperationResult result) throws SchemaException, IOException, ObjectAlreadyExistsException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, PolicyViolationException, SecurityViolationException {
//noinspection unchecked
Collection<ObjectDelta<? extends ObjectType>> deltas = singleton(createAddAccountDelta(userOid, accountFile));
modelService.executeChanges(deltas, null, task, result);
}

@NotNull
protected ObjectDelta<UserType> createAddAccountDelta(String userOid, File accountFile)
throws SchemaException, IOException {
PrismObject<ShadowType> account = prismContext.parseObject(accountFile);

ObjectDelta<UserType> userDelta = prismContext.deltaFactory().object().createEmptyModifyDelta(UserType.class, userOid
);
ObjectDelta<UserType> userDelta = prismContext.deltaFactory().object().createEmptyModifyDelta(UserType.class, userOid);
PrismReferenceValue accountRefVal = itemFactory().createReferenceValue();
accountRefVal.setObject(account);
ReferenceDelta accountDelta = prismContext.deltaFactory().reference().createModificationAdd(UserType.F_LINK_REF, getUserDefinition(), accountRefVal);
userDelta.addModification(accountDelta);
Collection<ObjectDelta<? extends ObjectType>> deltas = (Collection)MiscUtil.createCollection(userDelta);

modelService.executeChanges(deltas, null, task, result);
return userDelta;
}

protected void assertAuthorized(MidPointPrincipal principal, String action) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException {
Expand Down
11 changes: 0 additions & 11 deletions model/notifications-api/pom.xml
Expand Up @@ -43,11 +43,6 @@
<artifactId>model-api</artifactId>
<version>4.0.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.evolveum.midpoint.model</groupId>
<artifactId>workflow-api</artifactId>
<version>4.0.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.evolveum.midpoint.provisioning</groupId>
<artifactId>provisioning-api</artifactId>
Expand All @@ -58,12 +53,6 @@
<artifactId>task-api</artifactId>
<version>4.0.3-SNAPSHOT</version>
</dependency>

<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
</dependency>

<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations-java5</artifactId>
Expand Down
Expand Up @@ -7,35 +7,10 @@

package com.evolveum.midpoint.notifications.api;

import com.evolveum.midpoint.notifications.api.events.Event;
import com.evolveum.midpoint.notifications.api.events.ModelEvent;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;

import java.util.List;

/**
* Useful functions to be used in notifications scripts (including velocity templates).
*
* @author mederly
* TODO Decide what to do with this interface. Many of its methods were moved to Event and TextFormatter.
*/
public interface NotificationFunctions {

String getShadowName(PrismObject<? extends ShadowType> shadow);

String getPlaintextPasswordFromDelta(ObjectDelta delta);

String getContentAsFormattedList(Event event, boolean showSynchronizationItems, boolean showAuxiliaryAttributes);

List<ItemPath> getSynchronizationPaths();

List<ItemPath> getAuxiliaryPaths();

// TODO: polish this method
// TODO indicate somehow if password was erased from the focus
// We should (probably) return only a value if it has been (successfully) written to the focus.
String getFocusPasswordFromEvent(ModelEvent modelEvent);

}
@@ -1,96 +1,53 @@
/*
* Copyright (c) 2010-2017 Evolveum and contributors
* Copyright (c) 2020 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
*/

package com.evolveum.midpoint.notifications.api.events;

import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.schema.result.OperationResultStatus;
import com.evolveum.midpoint.schema.util.CertCampaignTypeUtil;
import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator;
import com.evolveum.midpoint.util.DebugUtil;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCampaignType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationStageDefinitionType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.EventCategoryType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.EventOperationType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.EventStatusType;
import org.apache.commons.lang.Validate;

import static com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCampaignStateType.*;
import org.jetbrains.annotations.NotNull;

/**
* Any event that is related to access certification.
*
* @author mederly
* An event related to access certification.
*/
public abstract class AccessCertificationEvent extends BaseEvent {

// all these must not be null
protected AccessCertificationCampaignType campaign;
protected OperationResultStatus status;
protected EventOperationType operationType;


public AccessCertificationEvent(LightweightIdentifierGenerator lightweightIdentifierGenerator, AccessCertificationCampaignType campaign, EventOperationType opType) {
super(lightweightIdentifierGenerator);
Validate.notNull(campaign, "campaign");
Validate.notNull(opType, "opType");
this.campaign = campaign;
this.operationType = opType;
this.status = OperationResultStatus.SUCCESS; // TODO fix this temporary implementation
}

public AccessCertificationCampaignType getCampaign() {
return campaign;
}

@Override
public boolean isRelatedToItem(ItemPath itemPath) {
return false; // not supported for this kind of events
}

@Override
public boolean isStatusType(EventStatusType eventStatusType) {
return false;
}

@Override
public boolean isOperationType(EventOperationType eventOperationType) {
return this.operationType.equals(eventOperationType);
}

@Override
public boolean isCategoryType(EventCategoryType eventCategoryType) {
return EventCategoryType.ACCESS_CERTIFICATION_EVENT.equals(eventCategoryType);
}

public String getCampaignName() {
return campaign.getName().getOrig();
}

public OperationResultStatus getStatus() {
return status;
}

public EventOperationType getOperationType() {
return operationType;
}

public AccessCertificationStageDefinitionType getCurrentStageDefinition() {
if (campaign.getState() != IN_REVIEW_STAGE && campaign.getState() != REVIEW_STAGE_DONE) {
return null;
}
return CertCampaignTypeUtil.findStageDefinition(campaign, campaign.getStageNumber());
}

@Override
protected void debugDumpCommon(StringBuilder sb, int indent) {
super.debugDumpCommon(sb, indent);
DebugUtil.debugDumpWithLabelToStringLn(sb, "campaign", campaign, indent + 1);
DebugUtil.debugDumpWithLabelToStringLn(sb, "status", status, indent + 1);
DebugUtil.debugDumpWithLabelToStringLn(sb, "operationType", operationType, indent + 1);
}
public interface AccessCertificationEvent extends Event {

/**
* Type of the operation: usually
* - ADD (opening campaign/stage)
* - DELETE (closing campaign/stage).
*
* In special cases there can be MODIFY e.g. meaning "stage deadline approaching".
*/
EventOperationType getOperationType();

/**
* Status of the operation.
* (Currently always SUCCESS.)
*/
OperationResultStatus getStatus();

/**
* Related certification campaign.
*/
@NotNull AccessCertificationCampaignType getCampaign();

/**
* Name of the related certification campaign.
*/
default String getCampaignName() {
return getCampaign().getName().getOrig();
}

/**
* Definition of the current stage.
*/
AccessCertificationStageDefinitionType getCurrentStageDefinition();
}

0 comments on commit aac7b85

Please sign in to comment.