-
Notifications
You must be signed in to change notification settings - Fork 188
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
repo-sqale: first successful extension modifyObject test
Added ExtensionUpdateContext and ExtensionItemDeltaProcessor. Extension support stuff moved mostly to new ExtensionProcessor. Still WIP, no JSONB key delete, and it now overwrites the whole stuff.
- Loading branch information
Showing
23 changed files
with
424 additions
and
198 deletions.
There are no files selected for viewing
178 changes: 178 additions & 0 deletions
178
repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/ExtensionProcessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
/* | ||
* Copyright (C) 2010-2021 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.repo.sqale; | ||
|
||
import static com.evolveum.midpoint.repo.sqale.ExtUtils.*; | ||
|
||
import java.io.IOException; | ||
import java.util.*; | ||
import javax.xml.datatype.XMLGregorianCalendar; | ||
import javax.xml.namespace.QName; | ||
|
||
import org.jetbrains.annotations.NotNull; | ||
import org.jetbrains.annotations.Nullable; | ||
|
||
import com.evolveum.midpoint.prism.*; | ||
import com.evolveum.midpoint.prism.polystring.PolyString; | ||
import com.evolveum.midpoint.repo.sqale.jsonb.Jsonb; | ||
import com.evolveum.midpoint.repo.sqale.qmodel.ext.MExtItem; | ||
import com.evolveum.midpoint.repo.sqale.qmodel.ext.MExtItemCardinality; | ||
import com.evolveum.midpoint.repo.sqale.qmodel.ext.MExtItemHolderType; | ||
import com.evolveum.midpoint.repo.sqale.qmodel.object.MObjectType; | ||
import com.evolveum.midpoint.util.exception.SystemException; | ||
|
||
public class ExtensionProcessor { | ||
|
||
private final SqaleRepoContext repositoryContext; | ||
|
||
public ExtensionProcessor(SqaleRepoContext repositoryContext) { | ||
this.repositoryContext = repositoryContext; | ||
} | ||
|
||
public Jsonb processExtensions( | ||
@NotNull Containerable extContainer, MExtItemHolderType holderType) { | ||
Map<String, Object> extMap = new LinkedHashMap<>(); | ||
PrismContainerValue<?> prismContainerValue = extContainer.asPrismContainerValue(); | ||
for (Item<?, ?> item : prismContainerValue.getItems()) { | ||
try { | ||
Objects.requireNonNull(item, "Object for converting must not be null."); | ||
ExtItemInfo extItemInfo = findExtensionItem(item.getDefinition(), holderType); | ||
if (extItemInfo == null) { | ||
continue; // not-indexed, skipping this item | ||
} | ||
|
||
Object value = extItemValue(item, extItemInfo); | ||
extMap.put(extItemInfo.getId(), value); | ||
} catch (RuntimeException e) { | ||
// If anything happens (like NPE in Map.of) we want to capture the "bad" item. | ||
throw new SystemException( | ||
"Unexpected exception while processing extension item " + item, e); | ||
} | ||
} | ||
|
||
try { | ||
return Jsonb.from(extMap); | ||
} catch (IOException e) { | ||
throw new SystemException(e); | ||
} | ||
} | ||
|
||
/** Returns ext item definition or null if the item is not indexed and should be skipped. */ | ||
@Nullable | ||
public ExtensionProcessor.ExtItemInfo findExtensionItem( | ||
ItemDefinition<?> definition, MExtItemHolderType holderType) { | ||
MExtItem extItem = resolveExtensionItem(definition, holderType); | ||
if (extItem == null) { | ||
return null; // not-indexed, returning null | ||
} | ||
|
||
// TODO review any need for shadow attributes, now they are stored fine, but the code here | ||
// is way too simple compared to the old repo. | ||
|
||
ExtItemInfo info = new ExtItemInfo(); | ||
info.item = extItem; | ||
if (definition instanceof PrismReferenceDefinition) { | ||
info.defaultRefTargetType = ((PrismReferenceDefinition) definition).getTargetTypeName(); | ||
} | ||
|
||
return info; | ||
} | ||
|
||
public Object extItemValue(Item<?, ?> item, ExtItemInfo extItemInfo) { | ||
MExtItem extItem = extItemInfo.item; | ||
if (extItem.cardinality == MExtItemCardinality.ARRAY) { | ||
List<Object> vals = new ArrayList<>(); | ||
for (Object realValue : item.getRealValues()) { | ||
vals.add(convertExtItemValue(realValue, extItemInfo)); | ||
} | ||
return vals; | ||
} else { | ||
return convertExtItemValue(item.getRealValue(), extItemInfo); | ||
} | ||
} | ||
|
||
private Object convertExtItemValue(Object realValue, ExtItemInfo extItemInfo) { | ||
if (realValue instanceof String | ||
|| realValue instanceof Number | ||
|| realValue instanceof Boolean) { | ||
return realValue; | ||
} | ||
|
||
if (realValue instanceof PolyString) { | ||
PolyString poly = (PolyString) realValue; | ||
return Map.of(EXT_POLY_ORIG_KEY, poly.getOrig(), | ||
EXT_POLY_NORM_KEY, poly.getNorm()); | ||
} | ||
|
||
if (realValue instanceof Referencable) { | ||
Referencable ref = (Referencable) realValue; | ||
// we always want to store the type for consistent search results | ||
QName targetType = ref.getType(); | ||
if (targetType == null) { | ||
targetType = extItemInfo.defaultRefTargetType; | ||
} | ||
if (targetType == null) { | ||
throw new IllegalArgumentException( | ||
"Reference without target type can't be stored: " + ref); | ||
} | ||
return Map.of(EXT_REF_TARGET_OID_KEY, ref.getOid(), | ||
EXT_REF_TARGET_TYPE_KEY, MObjectType.fromTypeQName(targetType), | ||
EXT_REF_RELATION_KEY, repositoryContext.processCacheableRelation(ref.getRelation())); | ||
} | ||
|
||
if (realValue instanceof Enum) { | ||
return realValue.toString(); | ||
} | ||
|
||
if (realValue instanceof XMLGregorianCalendar) { | ||
// XMLGregorianCalendar stores only millis, but we cut it to 3 fraction digits | ||
// to make the behavior explicit and consistent. | ||
return ExtUtils.extensionDateTime((XMLGregorianCalendar) realValue); | ||
} | ||
|
||
throw new IllegalArgumentException( | ||
"Unsupported type '" + realValue.getClass() + "' for value '" + realValue + "'."); | ||
} | ||
|
||
/** | ||
* Finds extension item for the provided definition and holder type. | ||
* Returns null if the item is not indexed. | ||
*/ | ||
public MExtItem resolveExtensionItem( | ||
ItemDefinition<?> definition, MExtItemHolderType holderType) { | ||
Objects.requireNonNull(definition, | ||
"Item '" + definition.getItemName() + "' without definition can't be saved."); | ||
|
||
if (definition instanceof PrismPropertyDefinition) { | ||
Boolean indexed = ((PrismPropertyDefinition<?>) definition).isIndexed(); | ||
// null is default which is "indexed" | ||
if (indexed != null && !indexed) { | ||
return null; | ||
} | ||
// enum is recognized by having allowed values | ||
if (!ExtUtils.SUPPORTED_INDEXED_EXTENSION_TYPES.contains(definition.getTypeName()) | ||
&& !ExtUtils.isEnumDefinition(((PrismPropertyDefinition<?>) definition))) { | ||
return null; | ||
} | ||
} else if (!(definition instanceof PrismReferenceDefinition)) { | ||
throw new UnsupportedOperationException("Unknown definition type '" + definition | ||
+ "', can't say if '" + definition.getItemName() + "' is indexed or not."); | ||
} // else it's reference which is indexed implicitly | ||
|
||
return repositoryContext.resolveExtensionItem(MExtItem.keyFrom(definition, holderType)); | ||
} | ||
|
||
/** Contains ext item from catalog and additional info needed for processing. */ | ||
public static class ExtItemInfo { | ||
public MExtItem item; | ||
public QName defaultRefTargetType; | ||
|
||
public String getId() { | ||
return item.id.toString(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
61 changes: 61 additions & 0 deletions
61
...rc/main/java/com/evolveum/midpoint/repo/sqale/delta/item/ExtensionItemDeltaProcessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
/* | ||
* Copyright (C) 2010-2021 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.repo.sqale.delta.item; | ||
|
||
import java.util.Collection; | ||
|
||
import com.evolveum.midpoint.prism.Item; | ||
import com.evolveum.midpoint.prism.ItemDefinition; | ||
import com.evolveum.midpoint.prism.PrismValue; | ||
import com.evolveum.midpoint.prism.delta.ItemDelta; | ||
import com.evolveum.midpoint.prism.path.ItemPath; | ||
import com.evolveum.midpoint.repo.sqale.ExtensionProcessor; | ||
import com.evolveum.midpoint.repo.sqale.delta.ItemDeltaProcessor; | ||
import com.evolveum.midpoint.repo.sqale.qmodel.ext.MExtItemHolderType; | ||
import com.evolveum.midpoint.repo.sqale.update.ExtensionUpdateContext; | ||
import com.evolveum.midpoint.repo.sqale.update.SqaleUpdateContext; | ||
import com.evolveum.midpoint.repo.sqlbase.RepositoryException; | ||
import com.evolveum.midpoint.util.exception.SchemaException; | ||
|
||
public class ExtensionItemDeltaProcessor implements ItemDeltaProcessor { | ||
|
||
private final ExtensionUpdateContext<?, ?, ?> context; | ||
private final MExtItemHolderType holderType; | ||
|
||
/** | ||
* Constructs delta processor for extension item inside JSONB column. | ||
* Takes more general context type for caller's sake, but it is {@link ExtensionUpdateContext}. | ||
*/ | ||
public ExtensionItemDeltaProcessor( | ||
SqaleUpdateContext<?, ?, ?> context, MExtItemHolderType holderType) { | ||
this.context = (ExtensionUpdateContext<?, ?, ?>) context; | ||
this.holderType = holderType; | ||
} | ||
|
||
@Override | ||
public void process(ItemDelta<?, ?> modification) throws RepositoryException, SchemaException { | ||
ItemPath itemPath = modification.getPath(); | ||
Item<PrismValue, ?> item = context.findItem(itemPath); | ||
Collection<?> realValues = item != null ? item.getRealValues() : null; | ||
ItemDefinition<?> definition = modification.getDefinition(); | ||
|
||
ExtensionProcessor extProcessor = new ExtensionProcessor(context.repositoryContext()); | ||
ExtensionProcessor.ExtItemInfo extItemInfo = | ||
extProcessor.findExtensionItem(definition, holderType); | ||
if (extItemInfo == null) { | ||
return; // not-indexed, no action | ||
} | ||
|
||
if (realValues == null || realValues.isEmpty()) { | ||
context.deleteItem(extItemInfo.getId()); | ||
return; | ||
} | ||
|
||
// changed value | ||
context.setChangedItem(extItemInfo.getId(), extProcessor.extItemValue(item, extItemInfo)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.