Skip to content

Commit a513e7e

Browse files
committed
CEL: Creating midPoint "object" extension, implementing shadow identifier methods, adapting model expression tests
1 parent 64f054e commit a513e7e

File tree

44 files changed

+601
-77
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+601
-77
lines changed

infra/schema/src/main/java/com/evolveum/midpoint/schema/util/ShadowUtil.java

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,39 +58,43 @@ public class ShadowUtil {
5858

5959
private static final Trace LOGGER = TraceManager.getTrace(ShadowUtil.class);
6060

61-
public static Collection<ShadowSimpleAttribute<?>> getPrimaryIdentifiers(ShadowType shadowType) {
61+
@Nullable
62+
public static Collection<ShadowSimpleAttribute<?>> getPrimaryIdentifiers(@NotNull ShadowType shadowType) {
6263
return getPrimaryIdentifiers(shadowType.asPrismObject());
6364
}
6465

65-
public static Collection<ShadowSimpleAttribute<?>> getPrimaryIdentifiers(PrismObject<? extends ShadowType> shadow) {
66+
@Nullable
67+
public static Collection<ShadowSimpleAttribute<?>> getPrimaryIdentifiers(@NotNull PrismObject<? extends ShadowType> shadow) {
6668
ShadowAttributesContainer attributesContainer = getAttributesContainer(shadow);
6769
if (attributesContainer == null) {
6870
return null;
6971
}
7072
return attributesContainer.getPrimaryIdentifiers();
7173
}
7274

73-
public static Collection<ShadowSimpleAttribute<?>> getSecondaryIdentifiers(ShadowType shadowType) {
75+
@Nullable
76+
public static Collection<ShadowSimpleAttribute<?>> getSecondaryIdentifiers(@NotNull ShadowType shadowType) {
7477
return getSecondaryIdentifiers(shadowType.asPrismObject());
7578
}
7679

77-
public static Collection<ShadowSimpleAttribute<?>> getSecondaryIdentifiers(PrismObject<? extends ShadowType> shadow) {
80+
@Nullable
81+
public static Collection<ShadowSimpleAttribute<?>> getSecondaryIdentifiers(@NotNull PrismObject<? extends ShadowType> shadow) {
7882
ShadowAttributesContainer attributesContainer = getAttributesContainer(shadow);
7983
if (attributesContainer == null) {
8084
return null;
8185
}
8286
return attributesContainer.getSecondaryIdentifiers();
8387
}
8488

85-
public static @NotNull Collection<ShadowSimpleAttribute<?>> getAllIdentifiers(PrismObject<? extends ShadowType> shadow) {
89+
public static @NotNull Collection<ShadowSimpleAttribute<?>> getAllIdentifiers(@NotNull PrismObject<? extends ShadowType> shadow) {
8690
ShadowAttributesContainer attributesContainer = getAttributesContainer(shadow);
8791
if (attributesContainer == null) {
8892
return List.of();
8993
}
9094
return attributesContainer.getAllIdentifiers();
9195
}
9296

93-
public static @NotNull Collection<ShadowSimpleAttribute<?>> getAllIdentifiers(ShadowType shadow) {
97+
public static @NotNull Collection<ShadowSimpleAttribute<?>> getAllIdentifiers(@NotNull ShadowType shadow) {
9498
return getAllIdentifiers(shadow.asPrismObject());
9599
}
96100

@@ -165,7 +169,7 @@ public static ShadowAttributesContainer getAttributesContainer(ShadowType shadow
165169
"No attributes container in %s", shadow);
166170
}
167171

168-
public static @Nullable ShadowAttributesContainer getAttributesContainer(PrismObject<? extends ShadowType> shadow) {
172+
public static @Nullable ShadowAttributesContainer getAttributesContainer(@NotNull PrismObject<? extends ShadowType> shadow) {
169173
return castShadowContainer(shadow.getValue(), ShadowType.F_ATTRIBUTES, ShadowAttributesContainer.class);
170174
}
171175

model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/functions/BasicExpressionFunctions.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -688,7 +688,12 @@ public Collection<?> getMetadataExtensionValues(PrismValue value, String itemLoc
688688
return getMetadataValues(value, ValueMetadataType.F_EXTENSION, itemLocalPart);
689689
}
690690

691+
@Deprecated // Use getPrimaryIdentifierValue instead
691692
public <T> T getIdentifierValue(ShadowType shadow) throws SchemaException {
693+
return getPrimaryIdentifierValue(shadow);
694+
}
695+
696+
public <T> T getPrimaryIdentifierValue(ShadowType shadow) throws SchemaException {
692697
if (shadow == null) {
693698
return null;
694699
}

model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/script/mel/extension/CelMelExtensions.java

Lines changed: 46 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ protected ImmutableSet<Function> initializeFunctions() {
231231

232232
),
233233

234-
// secret.resolveBinary(provider, key)
234+
// secret.resolveBinary(provider, key)
235235
new Function(
236236
CelFunctionDecl.newFunctionDeclaration(
237237
"secret.resolveBinary",
@@ -356,6 +356,51 @@ protected ImmutableSet<Function> initializeFunctions() {
356356
);
357357
}
358358

359+
private static final class Library implements CelExtensionLibrary<CelMelExtensions> {
360+
private final CelMelExtensions version0;
361+
362+
private Library(Protector protector, BasicExpressionFunctions basicExpressionFunctions) {
363+
version0 = new CelMelExtensions(protector, basicExpressionFunctions);
364+
}
365+
366+
@Override
367+
public String name() {
368+
return "mel";
369+
}
370+
371+
@Override
372+
public ImmutableSet<CelMelExtensions> versions() {
373+
return ImmutableSet.of(version0);
374+
}
375+
}
376+
377+
public static CelExtensionLibrary<CelMelExtensions> library(Protector protector, BasicExpressionFunctions basicExpressionFunctions) {
378+
return new Library(protector, basicExpressionFunctions);
379+
}
380+
381+
@Override
382+
public int version() {
383+
return 0;
384+
}
385+
386+
private String ascii(Object o) {
387+
return basicExpressionFunctions.ascii(toJava(o));
388+
}
389+
390+
public static boolean stringIsEmpty(String str) {
391+
if (isCellNull(str)) {
392+
return true;
393+
}
394+
return str.isEmpty();
395+
}
396+
397+
public static boolean stringIsBlank(String str) {
398+
if (isCellNull(str)) {
399+
return true;
400+
}
401+
return str.isBlank();
402+
}
403+
359404
private QNameCelValue qname(String namespace, String localPart) {
360405
return QNameCelValue.create(namespace, localPart);
361406
}
@@ -415,54 +460,8 @@ private Timestamp atEndOfDay(Timestamp timestamp, ZoneId zoneId) {
415460
.build();
416461
}
417462

418-
private static final class Library implements CelExtensionLibrary<CelMelExtensions> {
419-
private final CelMelExtensions version0;
420-
421-
private Library(Protector protector, BasicExpressionFunctions basicExpressionFunctions) {
422-
version0 = new CelMelExtensions(protector, basicExpressionFunctions);
423-
}
424-
425-
@Override
426-
public String name() {
427-
return "mel";
428-
}
429-
430-
@Override
431-
public ImmutableSet<CelMelExtensions> versions() {
432-
return ImmutableSet.of(version0);
433-
}
434-
}
435-
436-
public static CelExtensionLibrary<CelMelExtensions> library(Protector protector, BasicExpressionFunctions basicExpressionFunctions) {
437-
return new Library(protector, basicExpressionFunctions);
438-
}
439-
440-
@Override
441-
public int version() {
442-
return 0;
443-
}
444-
445-
private String ascii(Object o) {
446-
return basicExpressionFunctions.ascii(toJava(o));
447-
}
448-
449-
public static boolean stringIsEmpty(String str) {
450-
if (isCellNull(str)) {
451-
return true;
452-
}
453-
return str.isEmpty();
454-
}
455-
456-
public static boolean stringIsBlank(String str) {
457-
if (isCellNull(str)) {
458-
return true;
459-
}
460-
return str.isBlank();
461-
}
462-
463463
// TODO: do we need this?
464464
public static List<Object> melList(Object input) {
465-
LOGGER.info("MMMMMMMMMMMM: melList({})", input);
466465
if (input instanceof List) {
467466
return (List)input;
468467
} else {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
/*
2+
* Copyright (c) 2026 Evolveum and contributors
3+
*
4+
* Licensed under the EUPL-1.2 or later.
5+
*/
6+
package com.evolveum.midpoint.model.common.expression.script.mel.extension;
7+
8+
import java.util.Collection;
9+
import java.util.List;
10+
11+
import com.evolveum.midpoint.model.common.expression.script.mel.value.ObjectCelValue;
12+
13+
import com.evolveum.midpoint.prism.PrismObject;
14+
import com.evolveum.midpoint.schema.processor.ShadowSimpleAttribute;
15+
import com.evolveum.midpoint.schema.util.ShadowUtil;
16+
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;
17+
18+
import com.google.common.collect.ImmutableList;
19+
import com.google.common.collect.ImmutableSet;
20+
import dev.cel.common.CelFunctionDecl;
21+
import dev.cel.common.CelOverloadDecl;
22+
import dev.cel.common.types.ListType;
23+
import dev.cel.common.types.SimpleType;
24+
import dev.cel.extensions.CelExtensionLibrary;
25+
import dev.cel.runtime.CelFunctionBinding;
26+
import org.jetbrains.annotations.NotNull;
27+
import org.jetbrains.annotations.Nullable;
28+
29+
import com.evolveum.midpoint.model.common.expression.functions.BasicExpressionFunctions;
30+
import com.evolveum.midpoint.model.common.expression.script.mel.CelTypeMapper;
31+
import com.evolveum.midpoint.util.logging.Trace;
32+
import com.evolveum.midpoint.util.logging.TraceManager;
33+
34+
/**
35+
* Extensions for CEL compiler and runtime implementing functions executed on MidPoint objects.
36+
* E.g. shadow.primaryIdentifier(), resource.configurationProperty()
37+
*
38+
* @author Radovan Semancik
39+
*/
40+
public class CelObjectExtensions extends AbstractMidPointCelExtensions {
41+
42+
private static final Trace LOGGER = TraceManager.getTrace(CelObjectExtensions.class);
43+
44+
private final BasicExpressionFunctions basicExpressionFunctions;
45+
46+
public CelObjectExtensions(BasicExpressionFunctions basicExpressionFunctions) {
47+
this.basicExpressionFunctions = basicExpressionFunctions;
48+
initialize();
49+
}
50+
51+
@Override
52+
protected ImmutableSet<Function> initializeFunctions() {
53+
return ImmutableSet.of(
54+
55+
// shadow.primaryIdentifiers()
56+
new Function(
57+
CelFunctionDecl.newFunctionDeclaration(
58+
"primaryIdentifiers",
59+
CelOverloadDecl.newMemberOverload(
60+
"mp-shadow-primaryIdentifiers",
61+
"TODO",
62+
ListType.create(SimpleType.DYN),
63+
ObjectCelValue.CEL_TYPE)),
64+
CelFunctionBinding.from("mp-shadow-primaryIdentifiers", Object.class,
65+
this::primaryIdentifiers)),
66+
67+
// shadow.secondaryIdentifiers()
68+
new Function(
69+
CelFunctionDecl.newFunctionDeclaration(
70+
"secondaryIdentifiers",
71+
CelOverloadDecl.newMemberOverload(
72+
"mp-shadow-secondaryIdentifiers",
73+
"TODO",
74+
ListType.create(SimpleType.DYN),
75+
ObjectCelValue.CEL_TYPE)),
76+
CelFunctionBinding.from("mp-shadow-secondaryIdentifiers", Object.class,
77+
this::secondaryIdentifiers))
78+
);
79+
}
80+
81+
@NotNull
82+
private List<?> primaryIdentifiers(@Nullable Object o) {
83+
if (CelTypeMapper.isCellNull(o)) {
84+
return ImmutableList.of();
85+
}
86+
if (o instanceof ObjectCelValue<?> mpCelObject) {
87+
if (mpCelObject.getObject().isOfType(ShadowType.class)) {
88+
//noinspection unchecked
89+
return processShadowIdentifiers(
90+
ShadowUtil.getPrimaryIdentifiers((PrismObject<ShadowType>)mpCelObject.getObject()));
91+
}
92+
throw createException("Function primaryIdentifier() invoked on non-shadow object " + mpCelObject.getObject());
93+
}
94+
throw createException("Function primaryIdentifier() invoked on unknown object " + o);
95+
}
96+
97+
@NotNull
98+
private List<?> secondaryIdentifiers(@Nullable Object o) {
99+
if (CelTypeMapper.isCellNull(o)) {
100+
return ImmutableList.of();
101+
}
102+
if (o instanceof ObjectCelValue<?> mpCelObject) {
103+
if (mpCelObject.getObject().isOfType(ShadowType.class)) {
104+
//noinspection unchecked
105+
return processShadowIdentifiers(
106+
ShadowUtil.getSecondaryIdentifiers((PrismObject<ShadowType>)mpCelObject.getObject()));
107+
}
108+
throw createException("Function secondaryIdentifiers() invoked on non-shadow object " + mpCelObject.getObject());
109+
}
110+
throw createException("Function secondaryIdentifiers() invoked on unknown object " + o);
111+
}
112+
113+
@NotNull
114+
private List<?> processShadowIdentifiers(@Nullable Collection<ShadowSimpleAttribute<?>> identifiers) {
115+
if (identifiers == null || identifiers.isEmpty()) {
116+
return ImmutableList.of();
117+
}
118+
return identifiers.stream()
119+
.map(ssa -> ssa.getRealValue())
120+
.toList();
121+
}
122+
123+
private static final class Library implements CelExtensionLibrary<CelObjectExtensions> {
124+
private final CelObjectExtensions version0;
125+
126+
private Library(BasicExpressionFunctions basicExpressionFunctions) {
127+
version0 = new CelObjectExtensions(basicExpressionFunctions);
128+
}
129+
130+
@Override
131+
public String name() {
132+
return "mpObject";
133+
}
134+
135+
@Override
136+
public ImmutableSet<CelObjectExtensions> versions() {
137+
return ImmutableSet.of(version0);
138+
}
139+
}
140+
141+
public static CelExtensionLibrary<CelObjectExtensions> library(BasicExpressionFunctions basicExpressionFunctions) {
142+
return new Library(basicExpressionFunctions);
143+
}
144+
145+
@Override
146+
public int version() {
147+
return 0;
148+
}
149+
150+
}

model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/script/mel/extension/MidPointCelExtensionManager.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public class MidPointCelExtensionManager {
2626
private CelFormatExtensions extFormat;
2727
private CelPrismItemsExtensions extPrismItems;
2828
private CelLdapExtensions extLdap;
29+
private CelObjectExtensions extObject;
2930

3031
private ImmutableList<? extends CelCompilerLibrary> allCompilerLibraries;
3132
private ImmutableList<? extends CelRuntimeLibrary> allRuntimeLibraries;
@@ -43,6 +44,7 @@ private void initializeExtensions() {
4344
extFormat = CelFormatExtensions.library(basicExpressionFunctions).latest();
4445
extPrismItems = CelPrismItemsExtensions.library().latest();
4546
extLdap = CelLdapExtensions.library(basicExpressionFunctions).latest();
47+
extObject = CelObjectExtensions.library(basicExpressionFunctions).latest();
4648

4749
allCompilerLibraries = ImmutableList.of(
4850
CelExtensions.strings(),
@@ -58,7 +60,8 @@ private void initializeExtensions() {
5860
extPolyString,
5961
extFormat,
6062
extPrismItems,
61-
extLdap
63+
extLdap,
64+
extObject
6265
);
6366

6467
allRuntimeLibraries = ImmutableList.of(
@@ -74,7 +77,8 @@ private void initializeExtensions() {
7477
extPolyString,
7578
extFormat,
7679
extPrismItems,
77-
extLdap
80+
extLdap,
81+
extObject
7882
);
7983
}
8084

model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/TestMelExpressions.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -816,7 +816,6 @@ public void testUserAssignmentTargetRefOids() throws Exception {
816816
"c0c010c0-d34d-b33f-f00d-001111111112", "c0c010c0-d34d-b33f-f00d-001111111111");
817817
}
818818

819-
820819
@Test
821820
public void testUserLinkRefFirstOid() throws Exception {
822821
evaluateAndAssertStringScalarExpression(

0 commit comments

Comments
 (0)