Skip to content

Commit

Permalink
Fix simulations with shadows
Browse files Browse the repository at this point in the history
Added linkRef modification to the "simulateable" changes. This allows
running some scenarios that were not possible before. For example,
TestPreviewChangesCoD.test200CreateOnDemandWithProvisioning now works
as well.
  • Loading branch information
mederly committed Dec 6, 2022
1 parent ac6b074 commit 2bc0390
Show file tree
Hide file tree
Showing 18 changed files with 801 additions and 126 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,11 @@ public abstract class SchemaConstants {
.create(AbstractRoleType.F_AUTOASSIGN, AutoassignSpecificationType.F_ENABLED);
public static final ItemPath PATH_PARENT_ORG_REF = ItemPath.create(ObjectType.F_PARENT_ORG_REF);
public static final ItemPath PATH_METADATA_MODIFY_TIMESTAMP = ItemPath.create(ObjectType.F_METADATA, MetadataType.F_MODIFY_TIMESTAMP);
public static final ItemPath PATH_METADATA_MODIFY_CHANNEL = ItemPath.create(ObjectType.F_METADATA, MetadataType.F_MODIFY_CHANNEL);
public static final ItemPath PATH_METADATA_MODIFIER_REF = ItemPath.create(ObjectType.F_METADATA, MetadataType.F_MODIFIER_REF);
public static final ItemPath PATH_METADATA_MODIFY_TASK_REF = ItemPath.create(ObjectType.F_METADATA, MetadataType.F_MODIFY_TASK_REF);
public static final ItemPath PATH_METADATA_MODIFY_APPROVER_REF = ItemPath.create(ObjectType.F_METADATA, MetadataType.F_MODIFY_APPROVER_REF);
public static final ItemPath PATH_METADATA_MODIFY_APPROVAL_COMMENT = ItemPath.create(ObjectType.F_METADATA, MetadataType.F_MODIFY_APPROVAL_COMMENT);

public static final String NS_PROVISIONING = NS_MIDPOINT_PUBLIC + "/provisioning";
public static final String NS_PROVISIONING_LIVE_SYNC = NS_PROVISIONING + "/liveSync-3";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -543,8 +543,7 @@ private PrismObject<O> createOnDemand()
Collection<ObjectDeltaOperation<? extends ObjectType>> executedChanges;
try {
executedChanges = modelService.executeChanges(deltas, null, task, result);
} catch (CommunicationException | ConfigurationException
| PolicyViolationException | SecurityViolationException e) {
} catch (CommunicationException | ConfigurationException | PolicyViolationException | SecurityViolationException e) {
throw new ExpressionEvaluationException(e.getMessage(), e);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,111 +82,102 @@ Evaluation createEvaluation(
String contextDescription,
Task task,
OperationResult result) throws SchemaException {
return new AssociationSearchEvaluation(variables, valueDestination, useNew, context, contextDescription, task, result);
}
return new Evaluation(variables, valueDestination, useNew, context, contextDescription, task, result) {

@Override
public String shortDebugDump() {
return "associationExpression";
}
@Override
protected QName getDefaultTargetType() {
return ShadowType.COMPLEX_TYPE;
}

@Override
protected PrismContainerValue<ShadowAssociationType> createResultValue(
String oid,
PrismObject<ShadowType> object,
List<ItemDelta<PrismContainerValue<ShadowAssociationType>, PrismContainerDefinition<ShadowAssociationType>>> newValueDeltas)
throws SchemaException {
ShadowAssociationType association = new ShadowAssociationType()
.name(context.getMappingQName())
.shadowRef(oid, targetTypeQName);

association.getShadowRef().asReferenceValue().setObject(object);

//noinspection unchecked
PrismContainerValue<ShadowAssociationType> associationCVal = association.asPrismContainerValue();
if (newValueDeltas != null) {
ItemDeltaCollectionsUtil.applyTo(newValueDeltas, associationCVal);
}
prismContext.adopt(associationCVal, ShadowType.COMPLEX_TYPE, ShadowType.F_ASSOCIATION);
if (InternalsConfig.consistencyChecks) {
associationCVal.assertDefinitions(
() -> "associationCVal in assignment expression in " + context.getContextDescription());
}
return associationCVal;
}

@Override
protected ObjectQuery extendQuery(ObjectQuery query, ExpressionEvaluationContext params)
throws ExpressionEvaluationException {
@SuppressWarnings("unchecked")
TypedValue<ResourceObjectTypeDefinition> rAssocTargetDefTypedValue =
params.getVariables().get(ExpressionConstants.VAR_ASSOCIATION_TARGET_OBJECT_CLASS_DEFINITION);
if (rAssocTargetDefTypedValue == null || rAssocTargetDefTypedValue.getValue() == null) {
throw new ExpressionEvaluationException(
String.format("No association target object definition variable in %s; the expression may be used in"
+ " a wrong place. It is only supposed to create an association.",
params.getContextDescription()));
}
ResourceObjectTypeDefinition rAssocTargetDef = (ResourceObjectTypeDefinition) rAssocTargetDefTypedValue.getValue();
ObjectFilter coordinatesFilter = prismContext.queryFor(ShadowType.class)
.item(ShadowType.F_RESOURCE_REF).ref(rAssocTargetDef.getResourceOid())
.and().item(ShadowType.F_KIND).eq(rAssocTargetDef.getKind())
.and().item(ShadowType.F_INTENT).eq(rAssocTargetDef.getIntent())
.buildFilter();
query.setFilter(
prismContext.queryFactory()
.createAnd(coordinatesFilter, query.getFilter()));
return query;
}

private class AssociationSearchEvaluation extends Evaluation {

AssociationSearchEvaluation(
VariablesMap variables,
PlusMinusZero valueDestination,
boolean useNew,
ExpressionEvaluationContext context,
String contextDescription,
Task task,
OperationResult result) throws SchemaException {
super(variables, valueDestination, useNew, context, contextDescription, task, result);
}

@Override
protected QName getDefaultTargetType() {
return ShadowType.COMPLEX_TYPE;
}

@Override
protected PrismContainerValue<ShadowAssociationType> createResultValue(
String oid,
PrismObject<ShadowType> object,
List<ItemDelta<PrismContainerValue<ShadowAssociationType>, PrismContainerDefinition<ShadowAssociationType>>> newValueDeltas)
throws SchemaException {
ShadowAssociationType association = new ShadowAssociationType()
.name(context.getMappingQName())
.shadowRef(oid, targetTypeQName);

association.getShadowRef().asReferenceValue().setObject(object);

//noinspection unchecked
PrismContainerValue<ShadowAssociationType> associationCVal = association.asPrismContainerValue();
if (newValueDeltas != null) {
ItemDeltaCollectionsUtil.applyTo(newValueDeltas, associationCVal);
@Override
protected void extendOptions(
Collection<SelectorOptions<GetOperationOptions>> options, boolean searchOnResource) {
super.extendOptions(options, searchOnResource);
// We do not need to worry about associations of associations here
// (nested associations). Avoiding that will make the query faster.
options.add(
SelectorOptions.create(
prismContext.toUniformPath(ShadowType.F_ASSOCIATION),
GetOperationOptions.createDontRetrieve()));
}
prismContext.adopt(associationCVal, ShadowType.COMPLEX_TYPE, ShadowType.F_ASSOCIATION);
if (InternalsConfig.consistencyChecks) {
associationCVal.assertDefinitions(
() -> "associationCVal in assignment expression in " + context.getContextDescription());

@Override
protected boolean isAcceptable(@NotNull PrismObject<ShadowType> object) {
return ShadowUtil.isNotDead(object.asObjectable());
}
return associationCVal;
}

@Override
protected ObjectQuery extendQuery(ObjectQuery query, ExpressionEvaluationContext params) throws ExpressionEvaluationException {
@SuppressWarnings("unchecked")
TypedValue<ResourceObjectTypeDefinition> rAssocTargetDefTypedValue =
params.getVariables().get(ExpressionConstants.VAR_ASSOCIATION_TARGET_OBJECT_CLASS_DEFINITION);
if (rAssocTargetDefTypedValue == null || rAssocTargetDefTypedValue.getValue() == null) {
throw new ExpressionEvaluationException("No association target object definition variable in "+
params.getContextDescription()+"; the expression may be used in a wrong place. It is only supposed to create an association.");

/**
* Create on demand used in AssociationTargetSearch would fail
* @return false
*/
@Override
protected boolean isCreateOnDemandSafe() {
return false;
}
ResourceObjectTypeDefinition rAssocTargetDef = (ResourceObjectTypeDefinition) rAssocTargetDefTypedValue.getValue();
ObjectFilter coordinatesFilter = prismContext.queryFor(ShadowType.class)
.item(ShadowType.F_RESOURCE_REF).ref(rAssocTargetDef.getResourceOid())
.and().item(ShadowType.F_KIND).eq(rAssocTargetDef.getKind())
.and().item(ShadowType.F_INTENT).eq(rAssocTargetDef.getIntent())
.buildFilter();
query.setFilter(
prismContext.queryFactory()
.createAnd(coordinatesFilter, query.getFilter()));
return query;
}

@Override
protected void extendOptions(
Collection<SelectorOptions<GetOperationOptions>> options, boolean searchOnResource) {
super.extendOptions(options, searchOnResource);
// We do not need to worry about associations of associations here
// (nested associations). Avoiding that will make the query faster.
options.add(
SelectorOptions.create(
prismContext.toUniformPath(ShadowType.F_ASSOCIATION),
GetOperationOptions.createDontRetrieve()));
}

@Override
protected boolean isAcceptable(@NotNull PrismObject<ShadowType> object) {
return ShadowUtil.isNotDead(object.asObjectable());
}

/**
* Create on demand used in AssociationTargetSearch would fail
* @return false
*/
@Override
protected boolean isCreateOnDemandSafe() {
return false;
}

@Override
protected CacheInfo getCacheInfo() {
return new CacheInfo(
AssociationSearchExpressionEvaluatorCache.getCache(),
AssociationSearchExpressionEvaluatorCache.class,
CacheType.LOCAL_ASSOCIATION_TARGET_SEARCH_EVALUATOR_CACHE,
ShadowType.class);
}

@Override
protected CacheInfo getCacheInfo() {
return new CacheInfo(
AssociationSearchExpressionEvaluatorCache.getCache(),
AssociationSearchExpressionEvaluatorCache.class,
CacheType.LOCAL_ASSOCIATION_TARGET_SEARCH_EVALUATOR_CACHE,
ShadowType.class);
}

};
}

@Override
public String shortDebugDump() {
return "associationExpression";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ public class LensContext<F extends ObjectType> implements ModelContext<F>, Clone
*/
@NotNull private final List<LensProjectionContext> conflictingProjectionContexts = new ArrayList<>();

/** Denotes (legacy) "preview changes" mode. */
private transient boolean preview;

private transient Map<String, Collection<Containerable>> hookPreviewResultsMap;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,18 +226,22 @@ private void executeFocusDelta(ObjectDelta<F> delta, String opName, OperationRes
String channel = focusContext.getLensContext().getChannel();
OperationResult result = parentResult.createSubresult(opName);
try {
repositoryService.modifyObject(focusType, focus.getOid(), delta.getModifications(), result);
boolean real = task.isPersistentExecution();
if (real) {
repositoryService.modifyObject(focusType, focus.getOid(), delta.getModifications(), result);
}
task.onChangeExecuted(delta, real, result);
task.recordObjectActionExecuted(focus, focusType, focus.getOid(), ChangeType.MODIFY, channel, null);
} catch (ObjectAlreadyExistsException ex) {
task.recordObjectActionExecuted(focus, focusType, focus.getOid(), ChangeType.MODIFY, channel, ex);
result.recordFatalError(ex);
result.recordException(ex);
throw new SystemException(ex);
} catch (Throwable t) {
task.recordObjectActionExecuted(focus, focusType, focus.getOid(), ChangeType.MODIFY, channel, t);
result.recordFatalError(t);
result.recordException(t);
throw t;
} finally {
result.computeStatusIfUnknown();
result.close();
if (delta != null) {
focusContext.addToExecutedDeltas(LensUtil.createObjectDeltaOperation(delta, result, focusContext, projCtx));
}
Expand Down

0 comments on commit 2bc0390

Please sign in to comment.