diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/GuiStyleConstants.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/GuiStyleConstants.java index 5e7482d5bbd..05321174f40 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/GuiStyleConstants.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/GuiStyleConstants.java @@ -111,6 +111,8 @@ public class GuiStyleConstants { public static final String CLASS_ICON_NO_OBJECTS = "fa fa-times"; public static final String CLASS_ICON_ACTIVATION_ACTIVE = "fa fa-check"; public static final String CLASS_ICON_ACTIVATION_INACTIVE = "fa fa-times"; + public static final String CLASS_ICON_RESOURCE_BROKEN = "fa fa-exclamation"; + public static final String CLASS_ICON_RESOURCE_UNKNOWN = "fa fa-question"; public static final String CLASS_ICON_ASSIGNMENTS = "fa fa-bank"; public static final String CLASS_SHADOW_ICON_REQUEST = "fa fa-pencil-square-o"; public static final String CLASS_ICON_TACHOMETER = "fa fa-tachometer"; diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/wizard/resource/SchemaStep.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/wizard/resource/SchemaStep.java index 724333a2569..4a33a2777d8 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/wizard/resource/SchemaStep.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/wizard/resource/SchemaStep.java @@ -151,12 +151,16 @@ private ITab createSchemaEditor() { @Override public WebMarkupContainer getPanel(String panelId) { - XmlEditorPanel xmlEditorPanel = new XmlEditorPanel(panelId, createXmlEditorModel()); - // quick fix: now changes from XmlEditorPanel are not saved anyhow - //(e.g. by clicking Finish button in wizard). For now, - //panel is made disabled for editing - AceEditor aceEditor = (AceEditor) xmlEditorPanel.get(ID_ACE_EDITOR); - aceEditor.setReadonly(true); + XmlEditorPanel xmlEditorPanel = new XmlEditorPanel(panelId, createXmlEditorModel()) { + + // quick fix: now changes from XmlEditorPanel are not saved anyhow + //(e.g. by clicking Finish button in wizard). For now, + @Override + protected boolean isEditEnabled() { + return false; + } + }; + return xmlEditorPanel; } }; diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/wizard/resource/component/XmlEditorPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/wizard/resource/component/XmlEditorPanel.java index 0f270820c4d..10fb7fa7c5f 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/wizard/resource/component/XmlEditorPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/wizard/resource/component/XmlEditorPanel.java @@ -7,6 +7,7 @@ package com.evolveum.midpoint.web.component.wizard.resource.component; +import com.evolveum.midpoint.web.component.util.EnableBehaviour; import org.apache.wicket.model.IModel; import com.evolveum.midpoint.gui.api.component.BasePanel; @@ -32,6 +33,11 @@ protected void onInitialize() { protected void initLayout() { AceEditor editor = new AceEditor(ID_ACE_EDITOR, getModel()); + editor.setReadonly(!isEditEnabled()); add(editor); } + + protected boolean isEditEnabled() { + return true; + } } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/resources/ResourceSummaryPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/resources/ResourceSummaryPanel.java index cecc4f1afb9..168fb425a96 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/resources/ResourceSummaryPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/resources/ResourceSummaryPanel.java @@ -9,6 +9,7 @@ import java.util.ArrayList; import java.util.List; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AvailabilityStatusType; import org.apache.wicket.model.IModel; import com.evolveum.midpoint.gui.api.GuiStyleConstants; @@ -21,21 +22,13 @@ public class ResourceSummaryPanel extends ObjectSummaryPanel { private static final long serialVersionUID = 1L; - private static final String ID_UP_DOWN_TAG = "upDownTag"; - private IModel model; - public ResourceSummaryPanel(String id, IModel model, ModelServiceLocator serviceLocator) { super(id, ResourceType.class, model, serviceLocator); } -// @Override -// protected void onBeforeRender() { -// super.onBeforeRender(); -// } - @Override protected List> getSummaryTagComponentList(){ - boolean down = ResourceTypeUtil.isDown(getModelObject()); + AvailabilityStatusType availability = ResourceTypeUtil.getLastAvailabilityStatus(getModelObject()); List> summaryTagList = new ArrayList<>(); @@ -44,12 +37,24 @@ protected List> getSummaryTagComponentList(){ @Override protected void initialize(ResourceType object) { - if (!down) { - setIconCssClass(GuiStyleConstants.CLASS_ICON_ACTIVATION_ACTIVE); - setLabel(getString("ResourceSummaryPanel.UP")); - } else { - setIconCssClass(GuiStyleConstants.CLASS_ICON_ACTIVATION_INACTIVE); - setLabel(getString("ResourceSummaryPanel.DOWN")); + if (availability== null) { + setIconCssClass(GuiStyleConstants.CLASS_ICON_RESOURCE_UNKNOWN); + setLabel(getString("ResourceSummaryPanel.UNKNOWN")); + return; + } + setLabel(getString(ResourceSummaryPanel.this.getString(availability))); + switch(availability) { + case UP: + setIconCssClass(GuiStyleConstants.CLASS_ICON_ACTIVATION_ACTIVE); + break; + case DOWN: + setIconCssClass(GuiStyleConstants.CLASS_ICON_ACTIVATION_INACTIVE); + break; + case BROKEN: + setIconCssClass(GuiStyleConstants.CLASS_ICON_RESOURCE_BROKEN); + break; + + } } }; diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismSerializer.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismSerializer.java index cdbfc8a7d56..6d4d234ab42 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismSerializer.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismSerializer.java @@ -67,7 +67,8 @@ public interface PrismSerializer { PrismSerializer options(@Nullable SerializationOptions options); /** - * These items will be skipped during serialization. + * These items will be skipped during serialization. If present, their original presence is indicated + * by the "incomplete" flag. * * @param itemNames Names of items to be skipped. * @return Serializer with the items to be skipped set. diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/SerializationOptions.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/SerializationOptions.java index cddda082c3d..3842bed905c 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/SerializationOptions.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/SerializationOptions.java @@ -15,7 +15,12 @@ public class SerializationOptions implements Cloneable { private boolean serializeCompositeObjects; private boolean serializeReferenceNames; private boolean serializeReferenceNamesForNullOids; + + /** + * Should we skip index-only items? Their values will be omitted and they will be marked as incomplete. + */ private boolean skipIndexOnly; + private ItemNameQualificationStrategy itemNameQualificationStrategy; /** diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/util/PrismAsserts.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/util/PrismAsserts.java index a8dd148ead8..2248e6585ab 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/util/PrismAsserts.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/util/PrismAsserts.java @@ -1016,7 +1016,7 @@ public static void assertRefFilter(ObjectFilter objectFilter, QName expectedFilt assertPathEquivalent("Wrong filter path", path, filter.getFullPath()); } - // Local version of JUnit assers to avoid pulling JUnit dependecy to main + // Local version of JUnit asserts to avoid pulling JUnit dependency to main static void assertNotNull(String string, Object object) { assert object != null : string; @@ -1241,4 +1241,9 @@ public static void assertEquivalent(String message, ItemP } } + public static void assertIncomplete(PrismObject object, ItemPath itemPath) { + Item item = object.findItem(itemPath); + assertNotNull("No " + itemPath + " in " + object, item); + assertTrue(itemPath + " is not incomplete in " + object, item.isIncomplete()); + } } diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/xnode/IncompleteMarkerXNode.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/xnode/IncompleteMarkerXNode.java new file mode 100644 index 00000000000..640673fb846 --- /dev/null +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/xnode/IncompleteMarkerXNode.java @@ -0,0 +1,16 @@ +/* + * 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.prism.xnode; + +/** + * This is a marker that a given prism item is incomplete. + * + * EXPERIMENTAL + */ +public interface IncompleteMarkerXNode extends XNode { +} diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismContainerValueImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismContainerValueImpl.java index c383e6e1c9b..baee568b561 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismContainerValueImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismContainerValueImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2018 Evolveum and contributors + * Copyright (c) 2010-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. @@ -369,6 +369,9 @@ public boolean merge(Item roots, @Nullable QName aggregateElementName, @Nullable SerializationContext context) throws SchemaException; diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalProcessor.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalProcessor.java index 1d0dae71fbe..fc2cc8cf823 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalProcessor.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalProcessor.java @@ -233,6 +233,10 @@ private XNodeImpl parseElementContent(@NotNull Element element, QName knownEleme } else { node = parseElementContentToMap(element); } + } else if (DOMUtil.isMarkedAsIncomplete(element)) { + // Note that it is of no use to check for "incomplete" on non-leaf elements. In XML the incomplete attribute + // must be attached to an empty element. + node = new IncompleteMarkerXNodeImpl(); } else { node = parsePrimitiveElement(element); } @@ -278,7 +282,7 @@ private MapXNodeImpl parseElementContentToMap(Element element) throws SchemaExce } private boolean isList(@NotNull Element element, @NotNull QName elementName, @Nullable QName xsiType) { - String isListAttribute = DOMUtil.getAttribute(element, new QName(DOMUtil.IS_LIST_ATTRIBUTE_NAME)); + String isListAttribute = DOMUtil.getAttribute(element, DOMUtil.IS_LIST_ATTRIBUTE_NAME); if (StringUtils.isNotEmpty(isListAttribute)) { return Boolean.parseBoolean(isListAttribute); } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalWriter.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalWriter.java index 2ffb58d7045..4d5d9aff59f 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalWriter.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2017 Evolveum and contributors + * Copyright (c) 2010-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. @@ -17,6 +17,7 @@ import com.evolveum.midpoint.prism.impl.xnode.RootXNodeImpl; import com.evolveum.midpoint.prism.impl.xnode.SchemaXNodeImpl; import com.evolveum.midpoint.prism.impl.xnode.XNodeImpl; +import com.evolveum.midpoint.prism.xnode.IncompleteMarkerXNode; import com.evolveum.midpoint.util.DOMUtil; import com.evolveum.midpoint.util.QNameUtil; import com.evolveum.midpoint.util.exception.SchemaException; @@ -120,23 +121,25 @@ private Element serializeInternal(@NotNull RootXNodeImpl rootxnode, Element pare DOMUtil.setXsiType(topElement, setQNamePrefixExplicitIfNeeded(typeQName)); } XNodeImpl subnode = rootxnode.getSubnode(); - if (subnode instanceof PrimitiveXNodeImpl){ + if (subnode instanceof PrimitiveXNodeImpl) { serializePrimitiveElementOrAttribute((PrimitiveXNodeImpl) subnode, topElement, rootElementName, false); return DOMUtil.getFirstChildElement(topElement); - } - if (subnode instanceof MapXNodeImpl) { - // at this point we can put frequently used namespaces (e.g. c, t, q, ri) into the document to eliminate their use - // on many places inside the doc (MID-2198) - DOMUtil.setNamespaceDeclarations(topElement, getNamespacePrefixMapper().getNamespacesDeclaredByDefault()); - serializeMap((MapXNodeImpl) subnode, topElement); - } else if (subnode.isHeterogeneousList()) { - DOMUtil.setNamespaceDeclarations(topElement, getNamespacePrefixMapper().getNamespacesDeclaredByDefault()); - serializeExplicitList((ListXNodeImpl) subnode, topElement); } else { - throw new SchemaException("Sub-root xnode is not a map nor an explicit list, cannot serialize to XML (it is "+subnode+")"); + if (subnode instanceof MapXNodeImpl) { + // at this point we can put frequently used namespaces (e.g. c, t, q, ri) into the document to eliminate their use + // on many places inside the doc (MID-2198) + DOMUtil.setNamespaceDeclarations(topElement, getNamespacePrefixMapper().getNamespacesDeclaredByDefault()); + serializeMap((MapXNodeImpl) subnode, topElement); + } else if (subnode.isHeterogeneousList()) { + DOMUtil.setNamespaceDeclarations(topElement, getNamespacePrefixMapper().getNamespacesDeclaredByDefault()); + serializeExplicitList((ListXNodeImpl) subnode, topElement); + } else { + throw new SchemaException( + "Sub-root xnode is not a map nor an explicit list, cannot serialize to XML (it is " + subnode + ")"); + } + addDefaultNamespaceDeclaration(topElement); + return topElement; } - addDefaultNamespaceDeclaration(topElement); - return topElement; } /** @@ -176,7 +179,11 @@ private void serializeSubnode(XNodeImpl xsubnode, Element parentElement, QName e if (xsubnode == null) { return; } - if (xsubnode instanceof RootXNodeImpl) { + if (xsubnode instanceof IncompleteMarkerXNode) { + Element element = createElement(elementName, parentElement); + DOMUtil.setAttributeValue(element, DOMUtil.IS_INCOMPLETE_ATTRIBUTE_NAME, "true"); + parentElement.appendChild(element); + } else if (xsubnode instanceof RootXNodeImpl) { Element element = createElement(elementName, parentElement); appendCommentIfPresent(element, xsubnode); parentElement.appendChild(element); @@ -184,7 +191,7 @@ private void serializeSubnode(XNodeImpl xsubnode, Element parentElement, QName e } else if (xsubnode instanceof MapXNodeImpl) { Element element = createElement(elementName, parentElement); appendCommentIfPresent(element, xsubnode); - if (xsubnode.isExplicitTypeDeclaration() && xsubnode.getTypeQName() != null){ + if (xsubnode.isExplicitTypeDeclaration() && xsubnode.getTypeQName() != null) { DOMUtil.setXsiType(element, setQNamePrefixExplicitIfNeeded(xsubnode.getTypeQName())); } parentElement.appendChild(element); diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/AbstractJsonLexicalProcessor.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/AbstractJsonLexicalProcessor.java index 311064a5833..e5600382ee2 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/AbstractJsonLexicalProcessor.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/json/AbstractJsonLexicalProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2017 Evolveum and contributors + * Copyright (c) 2010-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. @@ -14,6 +14,7 @@ import javax.xml.namespace.QName; import com.evolveum.midpoint.prism.*; +import com.evolveum.midpoint.prism.impl.ParsingContextImpl; import com.evolveum.midpoint.prism.impl.lex.LexicalProcessor; import com.evolveum.midpoint.prism.impl.lex.LexicalUtils; import com.evolveum.midpoint.prism.impl.lex.json.yaml.MidpointYAMLGenerator; @@ -21,10 +22,10 @@ import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.schema.SchemaRegistry; import com.evolveum.midpoint.prism.xnode.*; +import com.evolveum.midpoint.util.logging.LoggingUtils; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.fasterxml.jackson.core.*; -import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.Validate; import org.jetbrains.annotations.NotNull; @@ -45,9 +46,15 @@ public abstract class AbstractJsonLexicalProcessor implements LexicalProcessor readInternal(@NotNull ParserSource source, @NotNull return parseFromStart(parser, parsingContext, null); } finally { if (source.closeStreamAfterParsing()) { - IOUtils.closeQuietly(is); + closeQuietly(is); + } + } + } + + private void closeQuietly(InputStream is) { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + LoggingUtils.logExceptionAsWarning(LOGGER, "Couldn't close the input stream", e); } } } private static final class IterativeParsingContext { - final RootXNodeHandler handler; - boolean dataSent; // true if we really found the list of objects and sent it out - String defaultNamespace; // default namespace, if present - boolean abortProcessing; // used when handler returns 'do not continue' + private final RootXNodeHandler handler; + private boolean dataSent; // true if we really found the list of objects and sent it out + private String defaultNamespace; // default namespace, if present + private boolean abortProcessing; // used when handler returns 'do not continue' private IterativeParsingContext(RootXNodeHandler handler) { this.handler = handler; @@ -114,7 +131,7 @@ public void readObjectsIteratively(@NotNull ParserSource source, @NotNull Parsin parseFromStart(parser, parsingContext, handler); } finally { if (source.closeStreamAfterParsing()) { - IOUtils.closeQuietly(is); + closeQuietly(is); } } } @@ -123,7 +140,7 @@ public void readObjectsIteratively(@NotNull ParserSource source, @NotNull Parsin static class JsonParsingContext { @NotNull final JsonParser parser; - @NotNull private final ParsingContext prismParsingContext; + @NotNull private final ParsingContextImpl prismParsingContext; // TODO consider getting rid of these IdentityHashMaps by support default namespace marking and resolution // directly in XNode structures (like it was done for Map XNode keys recently). @@ -133,7 +150,7 @@ static class JsonParsingContext { // Elements that should be skipped when filling-in default namespaces - those that are explicitly set with no-NS ('#name'). // (Values for these entries are not important. Only key presence is relevant.) @NotNull private final IdentityHashMap noNamespaceElementNames = new IdentityHashMap<>(); - private JsonParsingContext(@NotNull JsonParser parser, @NotNull ParsingContext prismParsingContext) { + private JsonParsingContext(@NotNull JsonParser parser, @NotNull ParsingContextImpl prismParsingContext) { this.parser = parser; this.prismParsingContext = prismParsingContext; } @@ -155,7 +172,7 @@ private List parseFromStart(JsonParser unconfiguredParser, Parsin throw new SchemaException("Nothing to parse: the input is empty."); } do { - ctx = new JsonParsingContext(parser, parsingContext); + ctx = new JsonParsingContext(parser, (ParsingContextImpl) parsingContext); IterativeParsingContext ipc = handler != null ? new IterativeParsingContext(handler) : null; XNodeImpl xnode = parseValue(ctx, ipc); if (ipc != null && ipc.dataSent) { @@ -313,7 +330,9 @@ private XNodeImpl parseValue(JsonParsingContext ctx, IterativeParsingContext ipc } /** - * Normally returns a MapXNode. However, JSON primitives/lists can be simulated by two-member object (@type + @value); in these cases we return respective XNode. + * Normally returns a MapXNode. However, there are exceptions: + * - JSON primitives/lists can be simulated by two-member object (@type + @value); in these cases we return respective XNode. + * - Incomplete marker i.e. { "@incomplete" : "true" } should be interpreted as IncompleteMarkerXNode. */ @NotNull private XNodeImpl parseJsonObject(JsonParsingContext ctx, IterativeParsingContext ipc) throws SchemaException, IOException { @@ -331,7 +350,7 @@ private XNodeImpl parseJsonObject(JsonParsingContext ctx, IterativeParsingContex XNodeImpl wrappedValue = null; boolean defaultNamespaceDefined = false; QNameUtil.QNameInfo currentFieldNameInfo = null; - + Boolean incomplete = null; for (;;) { if (ipc != null && ipc.abortProcessing) { break; @@ -398,6 +417,17 @@ private XNodeImpl parseJsonObject(JsonParsingContext ctx, IterativeParsingContex ctx.prismParsingContext.warnOrThrow(LOGGER, "Value ('" + PROP_VALUE + "') defined more than once at " + getPositionSuffix(ctx)); } wrappedValue = valueXNode; + } else if (isIncompleteDeclaration(currentFieldNameInfo.name)) { + if (incomplete != null) { + ctx.prismParsingContext.warnOrThrow(LOGGER, "Duplicate @incomplete marker found with the value: " + valueXNode); + } else if (valueXNode instanceof PrimitiveXNodeImpl) { + //noinspection unchecked + Boolean value = ((PrimitiveXNodeImpl) valueXNode) + .getParsedValue(DOMUtil.XSD_BOOLEAN, Boolean.class, ctx.prismParsingContext.getEvaluationMode()); + incomplete = Boolean.TRUE.equals(value); + } else { + ctx.prismParsingContext.warnOrThrow(LOGGER, "@incomplete marker found with incompatible value: " + valueXNode); + } } } else { // Beware of potential unqualified value conflict (see MID-5326). @@ -414,17 +444,23 @@ private XNodeImpl parseJsonObject(JsonParsingContext ctx, IterativeParsingContex currentFieldNameInfo = null; } } - // Return either map or primitive value (in case of @type/@value) + // Return either map or primitive value (in case of @type/@value) or incomplete xnode + int haveRegular = !map.isEmpty() ? 1 : 0; + int haveWrapped = wrappedValue != null ? 1 : 0; + int haveIncomplete = Boolean.TRUE.equals(incomplete) ? 1 : 0; XNodeImpl rv; - if (wrappedValue != null) { - if (!map.isEmpty()) { - ctx.prismParsingContext.warnOrThrow(LOGGER, "Both '" + PROP_VALUE + "' and regular content present at " + getPositionSuffix(ctx)); - rv = map; - } else { + if (haveRegular + haveWrapped + haveIncomplete > 1) { + ctx.prismParsingContext.warnOrThrow(LOGGER, "More than one of '" + PROP_VALUE + "', '" + PROP_INCOMPLETE + + "' and regular content present at " + getPositionSuffix(ctx)); + rv = map; + } else { + if (haveIncomplete > 0) { + rv = new IncompleteMarkerXNodeImpl(); + } else if (haveWrapped > 0) { rv = wrappedValue; + } else { + rv = map; // map can be empty here } - } else { - rv = map; } if (typeName != null) { if (wrappedValue != null && wrappedValue.getTypeQName() != null && !wrappedValue.getTypeQName().equals(typeName)) { @@ -469,25 +505,30 @@ private String getStringValue(XNodeImpl valueXNode, QNameUtil.QNameInfo currentF private boolean isSpecial(QName fieldName) { return isTypeDeclaration(fieldName) + || isIncompleteDeclaration(fieldName) || isElementDeclaration(fieldName) || isNamespaceDeclaration(fieldName) || isValue(fieldName); } private boolean isTypeDeclaration(QName fieldName) { - return new QName(PROP_TYPE).equals(fieldName); + return PROP_TYPE_QNAME.equals(fieldName); + } + + private boolean isIncompleteDeclaration(QName fieldName) { + return PROP_INCOMPLETE_QNAME.equals(fieldName); } private boolean isElementDeclaration(QName fieldName) { - return new QName(PROP_ELEMENT).equals(fieldName); + return PROP_ELEMENT_QNAME.equals(fieldName); } private boolean isNamespaceDeclaration(QName fieldName) { - return new QName(PROP_NAMESPACE).equals(fieldName); + return PROP_NAMESPACE_QNAME.equals(fieldName); } private boolean isValue(QName fieldName) { - return new QName(PROP_VALUE).equals(fieldName); + return PROP_VALUE_QNAME.equals(fieldName); } private String getPositionSuffix(JsonParsingContext ctx) { @@ -552,7 +593,7 @@ private PrimitiveXNodeImpl parseToPrimitive(JsonParsingContext ctx) throw return primitive; } - private PrimitiveXNodeImpl parseToEmptyPrimitive() throws IOException, SchemaException { + private PrimitiveXNodeImpl parseToEmptyPrimitive() { PrimitiveXNodeImpl primitive = new PrimitiveXNodeImpl<>(); primitive.setValueParser(new JsonNullValueParser<>()); return primitive; @@ -704,6 +745,11 @@ private void serialize(XNodeImpl xnode, JsonSerializationContext ctx, boolean in serializeFromPrimitive((PrimitiveXNodeImpl) xnode, ctx); } else if (xnode instanceof SchemaXNodeImpl) { serializeFromSchema((SchemaXNodeImpl) xnode, ctx); + } else if (xnode instanceof IncompleteMarkerXNodeImpl) { + ctx.generator.writeStartObject(); + ctx.generator.writeFieldName(PROP_INCOMPLETE); + ctx.generator.writeBoolean(true); + ctx.generator.writeEndObject(); } else { throw new UnsupportedOperationException("Cannot serialize from " + xnode); } @@ -862,7 +908,7 @@ private void serializeFromPrimitive(PrimitiveXNodeImpl primitive, JsonSer } } - protected QName getExplicitType(XNodeImpl xnode) { + private QName getExplicitType(XNodeImpl xnode) { return xnode.isExplicitTypeDeclaration() ? xnode.getTypeQName() : null; } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/PrismMarshaller.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/PrismMarshaller.java index f82d20313c4..eee8f082a94 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/PrismMarshaller.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/PrismMarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2018 Evolveum and contributors + * Copyright (c) 2010-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. @@ -172,13 +172,18 @@ public boolean canSerialize(Object object) { private XNodeImpl marshalItemContent(@NotNull Item item, ItemDefinition itemDefinition, SerializationContext context, Collection itemsToSkip) throws SchemaException { - if (item.size() == 1) { + if (item.isEmpty() && item.isIncomplete()) { + return new IncompleteMarkerXNodeImpl(); + } else if (item.size() == 1 && !item.isIncomplete()) { return marshalItemValue(item.getAnyValue(), itemDefinition, null, context, itemsToSkip); } else { ListXNodeImpl xlist = new ListXNodeImpl(); for (PrismValue val : item.getValues()) { xlist.add(marshalItemValue(val, itemDefinition, null, context, itemsToSkip)); } + if (item.isIncomplete()) { + xlist.add(new IncompleteMarkerXNodeImpl()); + } return xlist; } } @@ -198,13 +203,13 @@ private XNodeImpl marshalItemValue(@NotNull PrismValue itemValue, @Nullable Item Collection itemsToSkip) throws SchemaException { XNodeImpl xnode; if (definition == null && typeName == null && itemValue instanceof PrismPropertyValue) { - checkItemsToSkip(itemValue, itemsToSkip); + warnIfItemsToSkip(itemValue, itemsToSkip); return serializePropertyRawValue((PrismPropertyValue) itemValue); } else if (itemValue instanceof PrismReferenceValue) { - checkItemsToSkip(itemValue, itemsToSkip); + warnIfItemsToSkip(itemValue, itemsToSkip); xnode = serializeReferenceValue((PrismReferenceValue)itemValue, (PrismReferenceDefinition) definition, ctx); } else if (itemValue instanceof PrismPropertyValue) { - checkItemsToSkip(itemValue, itemsToSkip); + warnIfItemsToSkip(itemValue, itemsToSkip); xnode = serializePropertyValue((PrismPropertyValue)itemValue, (PrismPropertyDefinition) definition, typeName); } else if (itemValue instanceof PrismContainerValue) { xnode = marshalContainerValue((PrismContainerValue)itemValue, (PrismContainerDefinition) definition, ctx, itemsToSkip); @@ -220,7 +225,7 @@ private XNodeImpl marshalItemValue(@NotNull PrismValue itemValue, @Nullable Item return xnode; } - private void checkItemsToSkip(@NotNull PrismValue itemValue, Collection itemsToSkip) { + private void warnIfItemsToSkip(@NotNull PrismValue itemValue, Collection itemsToSkip) { if (CollectionUtils.isNotEmpty(itemsToSkip)) { LOGGER.warn("Trying to skip marshalling items {} where not applicable: {}", itemsToSkip, itemValue); } @@ -306,11 +311,14 @@ private void marshalContainerValue(MapXNodeImpl xmap, ItemName elementName = itemDef.getItemName(); Item item = containerVal.findItem(elementName); if (item != null) { - if (!shouldSkipItem(itemsToSkip, elementName, itemDef, ctx)) { - XNodeImpl xsubnode = marshalItemContent(item, getItemDefinition(containerVal, item), ctx, null); - xmap.put(elementName, xsubnode); - marshaledItems.add(elementName); + XNodeImpl xsubnode; + if (shouldSkipItem(itemsToSkip, elementName, itemDef, ctx) && !item.hasNoValues()) { + xsubnode = new IncompleteMarkerXNodeImpl(); + } else { + xsubnode = marshalItemContent(item, getItemDefinition(containerVal, item), ctx, null); } + xmap.put(elementName, xsubnode); + marshaledItems.add(elementName); } } } @@ -318,11 +326,15 @@ private void marshalContainerValue(MapXNodeImpl xmap, // E.g. in run-time schema. Therefore we must also iterate over items and not just item definitions. for (Item item : containerVal.getItems()) { QName elementName = item.getElementName(); - if (marshaledItems.contains(elementName) || shouldSkipItem(itemsToSkip, elementName, item.getDefinition(), ctx)) { - continue; + if (!marshaledItems.contains(elementName)) { + XNodeImpl xsubnode; + if (shouldSkipItem(itemsToSkip, elementName, item.getDefinition(), ctx) && !item.hasNoValues()) { + xsubnode = new IncompleteMarkerXNodeImpl(); + } else { + xsubnode = marshalItemContent(item, getItemDefinition(containerVal, item), ctx, null); + } + xmap.put(elementName, xsubnode); } - XNodeImpl xsubnode = marshalItemContent(item, getItemDefinition(containerVal, item), ctx, null); - xmap.put(elementName, xsubnode); } } @@ -522,7 +534,6 @@ private SchemaRegistry getSchemaRegistry() { return beanMarshaller.getPrismContext().getSchemaRegistry(); } - @SuppressWarnings("deprecation") private void addTypeDefinitionIfNeeded(@NotNull QName itemName, QName typeName, @NotNull XNodeImpl valueNode) { if (valueNode.getTypeQName() != null && valueNode.isExplicitTypeDeclaration()) { return; // already set diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/PrismUnmarshaller.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/PrismUnmarshaller.java index 9f426e22be4..5cc5f7ecc5f 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/PrismUnmarshaller.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/marshaller/PrismUnmarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2018 Evolveum and contributors + * Copyright (c) 2010-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. @@ -202,12 +202,16 @@ private PrismContainer parseContainer(@NotNull XNod private void parseContainerValueToContainer(PrismContainer container, XNodeImpl node, @NotNull ParsingContext pc) throws SchemaException { - container.add(parseContainerValue(node, container.getDefinition(), pc)); - if (node instanceof MapXNodeImpl && container instanceof PrismObject) { - MapXNodeImpl map = (MapXNodeImpl) node; - PrismObject object = (PrismObject) container; - object.setOid(getOid(map)); - object.setVersion(getVersion(map)); + if (node instanceof IncompleteMarkerXNodeImpl) { + container.setIncomplete(true); + } else { + container.add(parseContainerValue(node, container.getDefinition(), pc)); + if (node instanceof MapXNodeImpl && container instanceof PrismObject) { + MapXNodeImpl map = (MapXNodeImpl) node; + PrismObject object = (PrismObject) container; + object.setOid(getOid(map)); + object.setVersion(getVersion(map)); + } } } @@ -367,8 +371,12 @@ private PrismProperty parseProperty(@NotNull XNodeImpl node, @NotNull QNa throw new SchemaException("Attempt to store multiple values in single-valued property " + itemName); } for (XNodeImpl subNode : listNode) { - PrismPropertyValue pval = parsePropertyValue(subNode, itemDefinition, pc); - addItemValueIfPossible(property, pval, pc); + if (subNode instanceof IncompleteMarkerXNodeImpl) { + property.setIncomplete(true); + } else { + PrismPropertyValue pval = parsePropertyValue(subNode, itemDefinition, pc); + addItemValueIfPossible(property, pval, pc); + } } } else if (node instanceof MapXNodeImpl || node instanceof PrimitiveXNodeImpl || node.isHeterogeneousList()) { PrismPropertyValue pval = parsePropertyValue(node, itemDefinition, pc); @@ -380,6 +388,8 @@ private PrismProperty parseProperty(@NotNull XNodeImpl node, @NotNull QNa @SuppressWarnings("unchecked") PrismPropertyValue val = new PrismPropertyValueImpl(schemaDefType); addItemValueIfPossible(property, val, pc); + } else if (node instanceof IncompleteMarkerXNodeImpl) { + property.setIncomplete(true); } else { throw new IllegalArgumentException("Cannot parse property from " + node); } @@ -463,17 +473,19 @@ private PrismReference parseReference(@NotNull XNodeImpl node, @NotNull QName it @NotNull PrismReferenceDefinition definition, @NotNull ParsingContext pc) throws SchemaException { PrismReference ref = definition.instantiate(); if (node instanceof ListXNodeImpl) { - ListXNodeImpl listNode = (ListXNodeImpl) node; - if (!definition.isMultiValue() && listNode.size() > 1) { - throw new SchemaException("Attempt to store multiple values in single-valued reference " + itemName); - } - for (XNodeImpl subNode : listNode) { - ref.add(parseReferenceValueFromXNode(subNode, definition, itemName, pc)); + for (XNodeImpl subNode : (ListXNodeImpl) node) { + if (subNode instanceof IncompleteMarkerXNodeImpl) { + ref.setIncomplete(true); + } else { + ref.add(parseReferenceValueFromXNode(subNode, definition, itemName, pc)); + } } } else if (node instanceof MapXNodeImpl) { ref.add(parseReferenceValueFromXNode(node, definition, itemName, pc)); } else if (node instanceof PrimitiveXNodeImpl) { // empty + } else if (node instanceof IncompleteMarkerXNodeImpl) { + ref.setIncomplete(true); } else { throw new IllegalArgumentException("Cannot parse reference from " + node); } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/xnode/IncompleteMarkerXNodeImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/xnode/IncompleteMarkerXNodeImpl.java new file mode 100644 index 00000000000..50f7ec2d9d0 --- /dev/null +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/xnode/IncompleteMarkerXNodeImpl.java @@ -0,0 +1,42 @@ +/* + * 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.prism.impl.xnode; + +import com.evolveum.midpoint.prism.Visitor; +import com.evolveum.midpoint.prism.xnode.IncompleteMarkerXNode; +import com.evolveum.midpoint.prism.xnode.XNode; +import com.evolveum.midpoint.util.DebugUtil; + +/** + * + */ +public class IncompleteMarkerXNodeImpl extends XNodeImpl implements IncompleteMarkerXNode { + + @Override + public boolean isEmpty() { + return true; + } + + @Override + public String getDesc() { + return "incomplete"; + } + + @Override + public void accept(Visitor visitor) { + visitor.visit(this); + } + + @Override + public String debugDump(int indent) { + StringBuilder sb = new StringBuilder(); + DebugUtil.indentDebugDump(sb, indent); + sb.append("Incomplete"); + return sb.toString(); + } +} diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/xnode/XNodeImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/xnode/XNodeImpl.java index 62ee2c1568e..768fde2cef0 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/xnode/XNodeImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/xnode/XNodeImpl.java @@ -38,7 +38,7 @@ public abstract class XNodeImpl implements XNode { public static final QName DUMMY_NAME = new QName(null, "dummy"); // Common fields - protected XNodeImpl parent; + protected XNodeImpl parent; // currently unused /** * If set to true that the element came from the explicit type definition diff --git a/infra/prism-impl/src/main/resources/xml/ns/test/foo-1.xsd b/infra/prism-impl/src/main/resources/xml/ns/test/foo-1.xsd index a04ccd0ae4f..738d772e247 100644 --- a/infra/prism-impl/src/main/resources/xml/ns/test/foo-1.xsd +++ b/infra/prism-impl/src/main/resources/xml/ns/test/foo-1.xsd @@ -1,7 +1,7 @@ diff --git a/infra/prism-impl/src/test/resources/common/xml/user-will.xml b/infra/prism-impl/src/test/resources/common/xml/user-will.xml index 8253e89858b..66faeff2166 100644 --- a/infra/prism-impl/src/test/resources/common/xml/user-will.xml +++ b/infra/prism-impl/src/test/resources/common/xml/user-will.xml @@ -1,6 +1,6 @@ + + strong + + + + + $focus/jpegPhoto + + + + + ri:proof diff --git a/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java b/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java index de46867fc39..9187f0acf45 100644 --- a/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java +++ b/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java @@ -3172,6 +3172,24 @@ protected void displayTaskWithOperationStats(String message, Task task) throws S display(message + ": Operational stats", stats); } + protected void assertJpegPhoto(Class clazz, String oid, byte[] expectedValue, OperationResult result) + throws SchemaException, ObjectNotFoundException { + PrismObject object = repositoryService + .getObject(clazz, oid, schemaHelper.getOperationOptionsBuilder().retrieve().build(), result); + byte[] actualValue = object.asObjectable().getJpegPhoto(); + if (expectedValue == null) { + if (actualValue != null) { + fail("Photo present even if it should not be: " + Arrays.toString(actualValue)); + } + } else { + assertNotNull("No photo", actualValue); + if (!Arrays.equals(actualValue, expectedValue)) { + fail("Photo is different than expected.\nExpected = " + Arrays.toString(expectedValue) + + "\nActual value = " + Arrays.toString(actualValue)); + } + } + } + private class TaskFinishChecker implements Checker { private final String taskOid; private final OperationResult waitResult; diff --git a/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/AbstractPhotoTest.java b/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/AbstractPhotoTest.java new file mode 100644 index 00000000000..762be95ec9d --- /dev/null +++ b/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/AbstractPhotoTest.java @@ -0,0 +1,104 @@ +/* + * 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.repo.sql; + +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.PrismProperty; +import com.evolveum.midpoint.prism.delta.ObjectDelta; +import com.evolveum.midpoint.prism.util.PrismTestUtil; +import com.evolveum.midpoint.schema.*; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.util.exception.ObjectNotFoundException; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.xml.ns._public.common.api_types_3.ObjectModificationType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType; +import org.testng.annotations.BeforeClass; + +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; + +import static org.testng.AssertJUnit.*; + +/** + * + */ +public abstract class AbstractPhotoTest extends BaseSQLRepoTest { + + static final File TEST_DIR = new File("src/test/resources/photo"); + + @BeforeClass + public void beforeClass() throws Exception { + super.beforeClass(); + + PrismTestUtil.resetPrismContext(MidPointPrismContextFactory.FACTORY); + } + + protected abstract Class getObjectType(); + + void checkObject(String oid, File file, OperationResult result) throws SchemaException, IOException, + ObjectNotFoundException { + PrismObject expected = PrismTestUtil.parseObject(file); + checkObject(oid, expected, true, result); + } + + void checkObjectNoPhoto(String oid, File file, OperationResult result) throws SchemaException, IOException, ObjectNotFoundException { + PrismObject expected = PrismTestUtil.parseObject(file); + checkObject(oid, expected, false, result); + } + + // if !loadPhoto this MODIFIES 'expected' object + void checkObject(String oid, PrismObject expected, boolean loadPhoto, OperationResult result) throws ObjectNotFoundException, SchemaException { + boolean shouldPhotoExist = expected.asObjectable().getJpegPhoto() != null; + Collection> options; + if (loadPhoto) { + options = Collections.singletonList( + SelectorOptions.create(prismContext.toUniformPath(FocusType.F_JPEG_PHOTO), GetOperationOptions.createRetrieve( + RetrieveOption.INCLUDE))); + } else { + expected.asObjectable().setJpegPhoto(null); + options = null; + } + PrismObject real = repositoryService.getObject(getObjectType(), oid, options, result); + ObjectDelta delta = expected.diff(real); + System.out.println("Expected object = \n" + expected.debugDump()); + System.out.println("Real object in repo = \n" + real.debugDump()); + System.out.println("Difference = \n" + delta.debugDump()); + if (!delta.isEmpty()) { + fail("Objects are not equal.\n*** Expected:\n" + expected.debugDump() + "\n*** Got:\n" + real.debugDump() + "\n*** Delta:\n" + delta.debugDump()); + } + if (shouldPhotoExist) { + PrismProperty photoProperty = real.findProperty(FocusType.F_JPEG_PHOTO); + assertNotNull("No photo", photoProperty); + if (loadPhoto) { + assertFalse("Photo is marked as incomplete even when loaded", photoProperty.isIncomplete()); + assertEquals("Wrong # of photo values", 1, photoProperty.size()); + } else { + assertTrue("Photo is marked as complete even when not loaded", photoProperty.isIncomplete()); + assertEquals("Wrong # of photo values", 0, photoProperty.size()); + } + } + } + + @SafeVarargs + final void checkObject(String oid, File file, OperationResult result, ObjectDelta... appliedDeltas) throws SchemaException, IOException, ObjectNotFoundException { + PrismObject expected = PrismTestUtil.parseObject(file); + for (ObjectDelta delta : appliedDeltas) { + delta.applyTo(expected); + } + checkObject(oid, expected, true, result); + } + + ObjectDelta parseDelta(String oid, File file) throws SchemaException, IOException { + ObjectModificationType modification = PrismTestUtil.parseAtomicValue(file, ObjectModificationType.COMPLEX_TYPE); + ObjectDelta delta = DeltaConvertor.createObjectDelta(modification, getObjectType(), prismContext); + delta.setOid(oid); + return delta; + } +} diff --git a/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/CertificationTest.java b/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/CertificationTest.java index c67380859af..82ca1fcc4c9 100644 --- a/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/CertificationTest.java +++ b/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/CertificationTest.java @@ -54,8 +54,7 @@ import static com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationWorkItemType.F_CLOSE_TIMESTAMP; import static com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationWorkItemType.F_OUTPUT; import static com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType.F_NAME; -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.AssertJUnit.*; /** * @author mederly @@ -684,13 +683,26 @@ private void checkCampaign(String campaignOid, OperationResult result, PrismObje LOGGER.trace("Expected object = \n{}", expectedObject.debugDump()); - PrismObject campaign = getFullCampaign(campaignOid, result); + boolean casesExpected = !expectedObject.asObjectable().getCase().isEmpty(); - LOGGER.trace("Actual object from repo = \n{}", campaign.debugDump()); + PrismObject campaignFull = getFullCampaign(campaignOid, result); + PrismContainer caseContainerFull = campaignFull.findContainer(F_CASE); + if (caseContainerFull != null) { + assertFalse("campaign.case is marked as incomplete", caseContainerFull.isIncomplete()); + } + + LOGGER.trace("Actual object from repo = \n{}", campaignFull.debugDump()); - PrismAsserts.assertEquivalent("Campaign is not as expected", expectedObject, campaign); + PrismAsserts.assertEquivalent("Campaign is not as expected", expectedObject, campaignFull); if (expectedVersion != null) { - AssertJUnit.assertEquals("Incorrect version", (int) expectedVersion, Integer.parseInt(campaign.getVersion())); + AssertJUnit.assertEquals("Incorrect version", (int) expectedVersion, Integer.parseInt(campaignFull.getVersion())); + } + + PrismObject campaignPlain = getCampaignPlain(campaignOid, result); + if (casesExpected) { + PrismContainer caseContainerPlain = campaignPlain.findContainer(F_CASE); + assertNotNull("campaign.case is not present", caseContainerPlain); + assertTrue("campaign.case is NOT marked as incomplete", caseContainerPlain.isIncomplete()); } } @@ -701,6 +713,10 @@ private PrismObject getFullCampaign(String camp return repositoryService.getObject(AccessCertificationCampaignType.class, campaignOid, options, result); } + private PrismObject getCampaignPlain(String campaignOid, OperationResult result) throws ObjectNotFoundException, SchemaException { + return repositoryService.getObject(AccessCertificationCampaignType.class, campaignOid, null, result); + } + private PrismObject getFullCampaign(String oid) throws ObjectNotFoundException, SchemaException { OperationResult result = new OperationResult("getFullCampaign"); PrismObject object = getFullCampaign(oid, result); diff --git a/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/LookupTableTest.java b/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/LookupTableTest.java index 7a824b0de4c..d56e2cc532f 100644 --- a/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/LookupTableTest.java +++ b/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/LookupTableTest.java @@ -7,6 +7,8 @@ package com.evolveum.midpoint.repo.sql; +import com.evolveum.midpoint.prism.Containerable; +import com.evolveum.midpoint.prism.PrismContainer; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.delta.ItemDelta; import com.evolveum.midpoint.prism.delta.ItemDeltaCollectionsUtil; @@ -39,8 +41,7 @@ import static com.evolveum.midpoint.xml.ns._public.common.common_3.LookupTableRowType.*; import static com.evolveum.midpoint.xml.ns._public.common.common_3.LookupTableType.F_ROW; import static com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType.F_NAME; -import static org.testng.AssertJUnit.assertNotNull; -import static org.testng.AssertJUnit.assertTrue; +import static org.testng.AssertJUnit.*; /** * @author mederly @@ -339,11 +340,40 @@ public void test900DeleteTable() throws Exception { assertTrue(result.isSuccess()); } - private void checkTable(String tableOid, PrismObject expectedObject, OperationResult result) throws SchemaException, ObjectNotFoundException { + private void checkTable(String tableOid, PrismObject expectedTableFull, OperationResult result) throws SchemaException, ObjectNotFoundException { + PrismObject actualTableFull = getTableFull(tableOid, result); + expectedTableFull.setOid(tableOid); + PrismAsserts.assertEquivalent("Table is not as expected", expectedTableFull, actualTableFull); + + checkIncompleteFlag(tableOid, expectedTableFull, actualTableFull, result); + } + + private void checkIncompleteFlag(String tableOid, PrismObject expectedTableFull, + PrismObject actualTableFull, OperationResult result) throws ObjectNotFoundException, SchemaException { + boolean expectsRows = expectedTableFull.asObjectable().getRow().isEmpty(); + PrismContainer rowContainerFull = actualTableFull.findContainer(F_ROW); + if (rowContainerFull != null) { + assertFalse("table.row is marked as incomplete", rowContainerFull.isIncomplete()); + } + + PrismObject tablePlain = getTablePlain(tableOid, result); + if (expectsRows) { + PrismContainer rowContainerPlain = tablePlain.findContainer(F_ROW); + assertNotNull("No table.row", rowContainerPlain); + assertTrue("table.row is NOT marked as incomplete", rowContainerPlain.isIncomplete()); + } + } + + private PrismObject getTableFull(String tableOid, OperationResult result) + throws ObjectNotFoundException, SchemaException { SelectorOptions retrieve = SelectorOptions.create(prismContext.toUniformPath(F_ROW), GetOperationOptions.createRetrieve(INCLUDE)); - PrismObject table = repositoryService.getObject(LookupTableType.class, tableOid, Arrays.asList(retrieve), result); - expectedObject.setOid(tableOid); - PrismAsserts.assertEquivalent("Table is not as expected", expectedObject, table); + return repositoryService.getObject(LookupTableType.class, tableOid, + Collections.singletonList(retrieve), result); + } + + private PrismObject getTablePlain(String tableOid, OperationResult result) + throws ObjectNotFoundException, SchemaException { + return repositoryService.getObject(LookupTableType.class, tableOid,null, result); } protected void executeAndCheckModification(List> modifications, OperationResult result, int versionDelta, @@ -375,12 +405,8 @@ private void checkTable(String oid, OperationResult result, PrismObject iterator = expectedObject.asObjectable().getRow().iterator(); - while (iterator.hasNext()) { - if (replacedKeys.contains(iterator.next().getKey())) { - iterator.remove(); - } - } + expectedObject.asObjectable().getRow() + .removeIf(row -> replacedKeys.contains(row.getKey())); } if (modifications != null) { @@ -406,6 +432,8 @@ private void checkTable(String oid, OperationResult result, PrismObject { - private static final File TEST_DIR = new File("src/test/resources/photo"); private static final File ORG_FILE = new File(TEST_DIR, "org.xml"); private static final File T101_ADD_ORG_TYPE = new File(TEST_DIR, "t101-add-org-type.xml"); private static final File T102_REMOVE_PHOTO = new File(TEST_DIR, "t102-remove-photo.xml"); @@ -45,16 +34,14 @@ public class OrgPhotoTest extends BaseSQLRepoTest { private static final File T104_CHANGE_PHOTO = new File(TEST_DIR, "t104-change-photo.xml"); private static final File T105_ADD_PHOTO_BY_ADD = new File(TEST_DIR, "t105-add-photo-by-add.xml"); private static final File T106_ADD_PHOTO_BY_ADD_OTHER = new File(TEST_DIR, "t106-add-photo-by-add-other.xml"); - private static final File T107_REMOVE_PHOTO_BY_DELETE = new File(TEST_DIR, "t107-remove-photo-by-delete.xml"); - private static final File T108_REMOVE_OTHER_PHOTO_BY_DELETE = new File(TEST_DIR, "t108-remove-other-photo-by-delete.xml"); + private static final File T107_REMOVE_NON_EXISTING_PHOTO_BY_DELETE = new File(TEST_DIR, "t107-remove-non-existing-photo-by-delete.xml"); + private static final File T108_REMOVE_PHOTO_BY_DELETE = new File(TEST_DIR, "t108-remove-photo-by-delete.xml"); private String orgOid; - @BeforeClass - public void beforeClass() throws Exception { - super.beforeClass(); - - PrismTestUtil.resetPrismContext(MidPointPrismContextFactory.FACTORY); + @Override + protected Class getObjectType() { + return OrgType.class; } @Test @@ -72,7 +59,7 @@ public void test010AddOrg() throws Exception { public void test020ModifyOrg() throws Exception { OperationResult result = new OperationResult(OrgPhotoTest.class.getName() + ".test020ModifyOrg"); - ObjectDelta delta = parseDelta(orgOid, T101_ADD_ORG_TYPE); + ObjectDelta delta = parseDelta(orgOid, T101_ADD_ORG_TYPE); repositoryService.modifyObject(OrgType.class, orgOid, delta.getModifications(), result); checkObject(orgOid, ORG_FILE, result, delta); @@ -82,8 +69,8 @@ public void test020ModifyOrg() throws Exception { public void test030RemovePhotoByReplace() throws Exception { OperationResult result = new OperationResult(OrgPhotoTest.class.getName() + ".test030RemovePhotoByReplace"); - ObjectDelta delta1 = parseDelta(orgOid, T101_ADD_ORG_TYPE); - ObjectDelta delta2 = parseDelta(orgOid, T102_REMOVE_PHOTO); + ObjectDelta delta1 = parseDelta(orgOid, T101_ADD_ORG_TYPE); + ObjectDelta delta2 = parseDelta(orgOid, T102_REMOVE_PHOTO); repositoryService.modifyObject(OrgType.class, orgOid, delta2.getModifications(), result); checkObject(orgOid, ORG_FILE, result, delta1, delta2); @@ -93,9 +80,9 @@ public void test030RemovePhotoByReplace() throws Exception { public void test040ReAddPhoto() throws Exception { OperationResult result = new OperationResult(OrgPhotoTest.class.getName() + ".test040ReAddPhoto"); - ObjectDelta delta1 = parseDelta(orgOid, T101_ADD_ORG_TYPE); - ObjectDelta delta2 = parseDelta(orgOid, T102_REMOVE_PHOTO); - ObjectDelta delta3 = parseDelta(orgOid, T103_RE_ADD_PHOTO); + ObjectDelta delta1 = parseDelta(orgOid, T101_ADD_ORG_TYPE); + ObjectDelta delta2 = parseDelta(orgOid, T102_REMOVE_PHOTO); + ObjectDelta delta3 = parseDelta(orgOid, T103_RE_ADD_PHOTO); repositoryService.modifyObject(OrgType.class, orgOid, delta3.getModifications(), result); checkObject(orgOid, ORG_FILE, result, delta1, delta2, delta3); @@ -105,10 +92,10 @@ public void test040ReAddPhoto() throws Exception { public void test050ChangePhoto() throws Exception { OperationResult result = new OperationResult(OrgPhotoTest.class.getName() + ".test050ReplacePhoto"); - ObjectDelta delta1 = parseDelta(orgOid, T101_ADD_ORG_TYPE); - ObjectDelta delta2 = parseDelta(orgOid, T102_REMOVE_PHOTO); - ObjectDelta delta3 = parseDelta(orgOid, T103_RE_ADD_PHOTO); - ObjectDelta delta4 = parseDelta(orgOid, T104_CHANGE_PHOTO); + ObjectDelta delta1 = parseDelta(orgOid, T101_ADD_ORG_TYPE); + ObjectDelta delta2 = parseDelta(orgOid, T102_REMOVE_PHOTO); + ObjectDelta delta3 = parseDelta(orgOid, T103_RE_ADD_PHOTO); + ObjectDelta delta4 = parseDelta(orgOid, T104_CHANGE_PHOTO); repositoryService.modifyObject(OrgType.class, orgOid, delta4.getModifications(), result); checkObject(orgOid, ORG_FILE, result, delta1, delta2, delta3, delta4); @@ -135,7 +122,7 @@ public void test099DeleteOrg() throws Exception { public void test100AddPhotoByAdd() throws Exception { OperationResult result = new OperationResult(OrgPhotoTest.class.getName() + ".test100AddPhotoByAdd"); - ObjectDelta delta = parseDelta(orgOid, T105_ADD_PHOTO_BY_ADD); + ObjectDelta delta = parseDelta(orgOid, T105_ADD_PHOTO_BY_ADD); repositoryService.modifyObject(OrgType.class, orgOid, delta.getModifications(), result); checkObject(orgOid, ORG_FILE, result); // no need to mention delta here, because object now should be equal to ORG_FILE @@ -146,93 +133,45 @@ public void test100AddPhotoByAdd() throws Exception { public void test110DuplicatePhotoAddSame() throws Exception { OperationResult result = new OperationResult(OrgPhotoTest.class.getName() + ".test110DuplicatePhotoAddSame"); - ObjectDelta delta = parseDelta(orgOid, T105_ADD_PHOTO_BY_ADD); // adding the same value again + ObjectDelta delta = parseDelta(orgOid, T105_ADD_PHOTO_BY_ADD); // adding the same value again repositoryService.modifyObject(OrgType.class, orgOid, delta.getModifications(), result); checkObject(orgOid, ORG_FILE, result); // no need to mention delta here, because object now should be equal to ORG_FILE checkObjectNoPhoto(orgOid, ORG_FILE, result); } - @Test + @Test(expectedExceptions = SchemaException.class) // there is already a photo public void test120DuplicatePhotoAddOther() throws Exception { OperationResult result = new OperationResult(OrgPhotoTest.class.getName() + ".test120DuplicatePhotoAddOther"); - // because photo is single-value, the ADD operation will simply replace the old value - ObjectDelta delta = parseDelta(orgOid, T106_ADD_PHOTO_BY_ADD_OTHER); + ObjectDelta delta = parseDelta(orgOid, T106_ADD_PHOTO_BY_ADD_OTHER); repositoryService.modifyObject(OrgType.class, orgOid, delta.getModifications(), result); - - checkObject(orgOid, ORG_FILE, result, delta); } @Test public void test130RemoveNonExistingPhotoByDelete() throws Exception { OperationResult result = new OperationResult(OrgPhotoTest.class.getName() + ".test130RemoveNonExistingPhotoByDelete"); - ObjectDelta delta1 = parseDelta(orgOid, T106_ADD_PHOTO_BY_ADD_OTHER); - ObjectDelta delta2 = parseDelta(orgOid, T107_REMOVE_PHOTO_BY_DELETE); + ObjectDelta delta2 = parseDelta(orgOid, T107_REMOVE_NON_EXISTING_PHOTO_BY_DELETE); repositoryService.modifyObject(OrgType.class, orgOid, delta2.getModifications(), result); // should not remove the photo because the value is different - checkObject(orgOid, ORG_FILE, result, delta1); - checkObject(orgOid, ORG_FILE, result, delta1, delta2); // should be equivalent + checkObject(orgOid, ORG_FILE, result); + checkObject(orgOid, ORG_FILE, result, delta2); // should be equivalent } @Test public void test140RemoveExistingPhotoByDelete() throws Exception { OperationResult result = new OperationResult(OrgPhotoTest.class.getName() + ".test140RemoveExistingPhotoByDelete"); - ObjectDelta delta1 = parseDelta(orgOid, T106_ADD_PHOTO_BY_ADD_OTHER); - ObjectDelta delta2 = parseDelta(orgOid, T107_REMOVE_PHOTO_BY_DELETE); - ObjectDelta delta3 = parseDelta(orgOid, T108_REMOVE_OTHER_PHOTO_BY_DELETE); + ObjectDelta delta2 = parseDelta(orgOid, T107_REMOVE_NON_EXISTING_PHOTO_BY_DELETE); + ObjectDelta delta3 = parseDelta(orgOid, T108_REMOVE_PHOTO_BY_DELETE); repositoryService.modifyObject(OrgType.class, orgOid, delta3.getModifications(), result); // this one should remove the photo - checkObject(orgOid, ORG_FILE, result, delta1, delta2, delta3); + checkObject(orgOid, ORG_FILE, result, delta2, delta3); // just to be 100% sure ;) - ObjectDelta deltaRemoveByReplace = parseDelta(orgOid, T102_REMOVE_PHOTO); // this deletes photo by setting jpegPhoto:=null + ObjectDelta deltaRemoveByReplace = parseDelta(orgOid, T102_REMOVE_PHOTO); // this deletes photo by setting jpegPhoto:=null checkObject(orgOid, ORG_FILE, result, deltaRemoveByReplace); } - protected ObjectDelta parseDelta(String oid, File file) throws SchemaException, IOException { - ObjectModificationType modification = PrismTestUtil.parseAtomicValue(file, ObjectModificationType.COMPLEX_TYPE); - ObjectDelta delta = DeltaConvertor.createObjectDelta(modification, OrgType.class, prismContext); - delta.setOid(oid); - return delta; - } - - private void checkObject(String oid, File file, OperationResult result) throws SchemaException, IOException, ObjectNotFoundException { - PrismObject expected = PrismTestUtil.parseObject(file); - checkObject(oid, expected, true, result); - } - - private void checkObjectNoPhoto(String oid, File file, OperationResult result) throws SchemaException, IOException, ObjectNotFoundException { - PrismObject expected = PrismTestUtil.parseObject(file); - expected.asObjectable().setJpegPhoto(null); - checkObject(oid, expected, false, result); - } - - private void checkObject(String oid, PrismObject expected, boolean loadPhoto, OperationResult result) throws ObjectNotFoundException, SchemaException { - Collection> options; - if (loadPhoto) { - options = Collections.singletonList( - SelectorOptions.create(prismContext.toUniformPath(FocusType.F_JPEG_PHOTO), GetOperationOptions.createRetrieve(RetrieveOption.INCLUDE))); - } else { - options = null; - } - PrismObject real = repositoryService.getObject(OrgType.class, oid, options, result); - ObjectDelta delta = expected.diff(real); - System.out.println("Expected object = \n" + expected.debugDump()); - System.out.println("Real object in repo = \n" + real.debugDump()); - System.out.println("Difference = \n" + delta.debugDump()); - if (!delta.isEmpty()) { - fail("Objects are not equal.\n*** Expected:\n" + expected.debugDump() + "\n*** Got:\n" + real.debugDump() + "\n*** Delta:\n" + delta.debugDump()); - } - } - - private void checkObject(String oid, File file, OperationResult result, ObjectDelta... appliedDeltas) throws SchemaException, IOException, ObjectNotFoundException { - PrismObject expected = PrismTestUtil.parseObject(file); - for (ObjectDelta delta : appliedDeltas) { - delta.applyTo(expected); - } - checkObject(oid, expected, true, result); - } } diff --git a/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/UserPhotoTest.java b/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/UserPhotoTest.java index f2c84a76b54..78d6f9443b1 100644 --- a/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/UserPhotoTest.java +++ b/repo/repo-sql-impl-test/src/test/java/com/evolveum/midpoint/repo/sql/UserPhotoTest.java @@ -9,36 +9,23 @@ import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.util.PrismTestUtil; -import com.evolveum.midpoint.schema.DeltaConvertor; -import com.evolveum.midpoint.schema.GetOperationOptions; -import com.evolveum.midpoint.schema.MidPointPrismContextFactory; -import com.evolveum.midpoint.schema.RetrieveOption; -import com.evolveum.midpoint.schema.SelectorOptions; import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.util.exception.ObjectNotFoundException; import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.xml.ns._public.common.api_types_3.ObjectModificationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; -import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.io.File; -import java.io.IOException; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.fail; /** * @author mederly */ @ContextConfiguration(locations = {"../../../../../ctx-test.xml"}) @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) -public class UserPhotoTest extends BaseSQLRepoTest { +public class UserPhotoTest extends AbstractPhotoTest { private static final File TEST_DIR = new File("src/test/resources/photo"); private static final File USER_FILE = new File(TEST_DIR, "user.xml"); @@ -48,16 +35,14 @@ public class UserPhotoTest extends BaseSQLRepoTest { private static final File T004_CHANGE_PHOTO = new File(TEST_DIR, "t004-change-photo.xml"); private static final File T005_ADD_PHOTO_BY_ADD = new File(TEST_DIR, "t005-add-photo-by-add.xml"); private static final File T006_ADD_PHOTO_BY_ADD_OTHER = new File(TEST_DIR, "t006-add-photo-by-add-other.xml"); - private static final File T007_REMOVE_PHOTO_BY_DELETE = new File(TEST_DIR, "t007-remove-photo-by-delete.xml"); - private static final File T008_REMOVE_OTHER_PHOTO_BY_DELETE = new File(TEST_DIR, "t008-remove-other-photo-by-delete.xml"); + private static final File T007_REMOVE_NON_EXISTING_PHOTO_BY_DELETE = new File(TEST_DIR, "t007-remove-non-existing-photo-by-delete.xml"); + private static final File T008_REMOVE_PHOTO_BY_DELETE = new File(TEST_DIR, "t008-remove-photo-by-delete.xml"); private String userOid; - @BeforeClass - public void beforeClass() throws Exception { - super.beforeClass(); - - PrismTestUtil.resetPrismContext(MidPointPrismContextFactory.FACTORY); + @Override + protected Class getObjectType() { + return UserType.class; } @Test @@ -75,7 +60,7 @@ public void test010AddUser() throws Exception { public void test020ModifyUser() throws Exception { OperationResult result = new OperationResult(UserPhotoTest.class.getName() + ".test020ModifyUser"); - ObjectDelta delta = parseDelta(userOid, T001_ADD_EMPLOYEE_TYPE); + ObjectDelta delta = parseDelta(userOid, T001_ADD_EMPLOYEE_TYPE); repositoryService.modifyObject(UserType.class, userOid, delta.getModifications(), result); checkObject(userOid, USER_FILE, result, delta); @@ -85,8 +70,8 @@ public void test020ModifyUser() throws Exception { public void test030RemovePhotoByReplace() throws Exception { OperationResult result = new OperationResult(UserPhotoTest.class.getName() + ".test030RemovePhotoByReplace"); - ObjectDelta delta1 = parseDelta(userOid, T001_ADD_EMPLOYEE_TYPE); - ObjectDelta delta2 = parseDelta(userOid, T002_REMOVE_PHOTO); + ObjectDelta delta1 = parseDelta(userOid, T001_ADD_EMPLOYEE_TYPE); + ObjectDelta delta2 = parseDelta(userOid, T002_REMOVE_PHOTO); repositoryService.modifyObject(UserType.class, userOid, delta2.getModifications(), result); checkObject(userOid, USER_FILE, result, delta1, delta2); @@ -96,9 +81,9 @@ public void test030RemovePhotoByReplace() throws Exception { public void test040ReAddPhoto() throws Exception { OperationResult result = new OperationResult(UserPhotoTest.class.getName() + ".test040ReAddPhoto"); - ObjectDelta delta1 = parseDelta(userOid, T001_ADD_EMPLOYEE_TYPE); - ObjectDelta delta2 = parseDelta(userOid, T002_REMOVE_PHOTO); - ObjectDelta delta3 = parseDelta(userOid, T003_RE_ADD_PHOTO); + ObjectDelta delta1 = parseDelta(userOid, T001_ADD_EMPLOYEE_TYPE); + ObjectDelta delta2 = parseDelta(userOid, T002_REMOVE_PHOTO); + ObjectDelta delta3 = parseDelta(userOid, T003_RE_ADD_PHOTO); repositoryService.modifyObject(UserType.class, userOid, delta3.getModifications(), result); checkObject(userOid, USER_FILE, result, delta1, delta2, delta3); @@ -108,10 +93,10 @@ public void test040ReAddPhoto() throws Exception { public void test050ChangePhoto() throws Exception { OperationResult result = new OperationResult(UserPhotoTest.class.getName() + ".test050ReplacePhoto"); - ObjectDelta delta1 = parseDelta(userOid, T001_ADD_EMPLOYEE_TYPE); - ObjectDelta delta2 = parseDelta(userOid, T002_REMOVE_PHOTO); - ObjectDelta delta3 = parseDelta(userOid, T003_RE_ADD_PHOTO); - ObjectDelta delta4 = parseDelta(userOid, T004_CHANGE_PHOTO); + ObjectDelta delta1 = parseDelta(userOid, T001_ADD_EMPLOYEE_TYPE); + ObjectDelta delta2 = parseDelta(userOid, T002_REMOVE_PHOTO); + ObjectDelta delta3 = parseDelta(userOid, T003_RE_ADD_PHOTO); + ObjectDelta delta4 = parseDelta(userOid, T004_CHANGE_PHOTO); repositoryService.modifyObject(UserType.class, userOid, delta4.getModifications(), result); checkObject(userOid, USER_FILE, result, delta1, delta2, delta3, delta4); @@ -138,7 +123,7 @@ public void test099DeleteUser() throws Exception { public void test100AddPhotoByAdd() throws Exception { OperationResult result = new OperationResult(UserPhotoTest.class.getName() + ".test100AddPhotoByAdd"); - ObjectDelta delta = parseDelta(userOid, T005_ADD_PHOTO_BY_ADD); + ObjectDelta delta = parseDelta(userOid, T005_ADD_PHOTO_BY_ADD); repositoryService.modifyObject(UserType.class, userOid, delta.getModifications(), result); checkObject(userOid, USER_FILE, result); // no need to mention delta here, because object now should be equal to USER_FILE @@ -149,93 +134,44 @@ public void test100AddPhotoByAdd() throws Exception { public void test110DuplicatePhotoAddSame() throws Exception { OperationResult result = new OperationResult(UserPhotoTest.class.getName() + ".test110DuplicatePhotoAddSame"); - ObjectDelta delta = parseDelta(userOid, T005_ADD_PHOTO_BY_ADD); // adding the same value again + ObjectDelta delta = parseDelta(userOid, T005_ADD_PHOTO_BY_ADD); // adding the same value again repositoryService.modifyObject(UserType.class, userOid, delta.getModifications(), result); checkObject(userOid, USER_FILE, result); // no need to mention delta here, because object now should be equal to USER_FILE checkObjectNoPhoto(userOid, USER_FILE, result); } - @Test + @Test(expectedExceptions = SchemaException.class) // photo already exists public void test120DuplicatePhotoAddOther() throws Exception { OperationResult result = new OperationResult(UserPhotoTest.class.getName() + ".test120DuplicatePhotoAddOther"); - // because photo is single-value, the ADD operation will simply replace the old value - ObjectDelta delta = parseDelta(userOid, T006_ADD_PHOTO_BY_ADD_OTHER); + ObjectDelta delta = parseDelta(userOid, T006_ADD_PHOTO_BY_ADD_OTHER); repositoryService.modifyObject(UserType.class, userOid, delta.getModifications(), result); - - checkObject(userOid, USER_FILE, result, delta); } @Test public void test130RemoveNonExistingPhotoByDelete() throws Exception { OperationResult result = new OperationResult(UserPhotoTest.class.getName() + ".test130RemoveNonExistingPhotoByDelete"); - ObjectDelta delta1 = parseDelta(userOid, T006_ADD_PHOTO_BY_ADD_OTHER); - ObjectDelta delta2 = parseDelta(userOid, T007_REMOVE_PHOTO_BY_DELETE); + ObjectDelta delta2 = parseDelta(userOid, T007_REMOVE_NON_EXISTING_PHOTO_BY_DELETE); repositoryService.modifyObject(UserType.class, userOid, delta2.getModifications(), result); // should not remove the photo because the value is different - checkObject(userOid, USER_FILE, result, delta1); - checkObject(userOid, USER_FILE, result, delta1, delta2); // should be equivalent + checkObject(userOid, USER_FILE, result); + checkObject(userOid, USER_FILE, result, delta2); // should be equivalent } @Test public void test140RemoveExistingPhotoByDelete() throws Exception { OperationResult result = new OperationResult(UserPhotoTest.class.getName() + ".test140RemoveExistingPhotoByDelete"); - ObjectDelta delta1 = parseDelta(userOid, T006_ADD_PHOTO_BY_ADD_OTHER); - ObjectDelta delta2 = parseDelta(userOid, T007_REMOVE_PHOTO_BY_DELETE); - ObjectDelta delta3 = parseDelta(userOid, T008_REMOVE_OTHER_PHOTO_BY_DELETE); + ObjectDelta delta2 = parseDelta(userOid, T007_REMOVE_NON_EXISTING_PHOTO_BY_DELETE); + ObjectDelta delta3 = parseDelta(userOid, T008_REMOVE_PHOTO_BY_DELETE); repositoryService.modifyObject(UserType.class, userOid, delta3.getModifications(), result); // this one should remove the photo - checkObject(userOid, USER_FILE, result, delta1, delta2, delta3); + checkObject(userOid, USER_FILE, result, delta2, delta3); // just to be 100% sure ;) - ObjectDelta deltaRemoveByReplace = parseDelta(userOid, T002_REMOVE_PHOTO); // this deletes photo by setting jpegPhoto:=null + ObjectDelta deltaRemoveByReplace = parseDelta(userOid, T002_REMOVE_PHOTO); // this deletes photo by setting jpegPhoto:=null checkObject(userOid, USER_FILE, result, deltaRemoveByReplace); } - - protected ObjectDelta parseDelta(String oid, File file) throws SchemaException, IOException { - ObjectModificationType modification = PrismTestUtil.parseAtomicValue(file, ObjectModificationType.COMPLEX_TYPE); - ObjectDelta delta = DeltaConvertor.createObjectDelta(modification, UserType.class, prismContext); - delta.setOid(oid); - return delta; - } - - private void checkObject(String oid, File file, OperationResult result) throws SchemaException, IOException, ObjectNotFoundException { - PrismObject expected = PrismTestUtil.parseObject(file); - checkObject(oid, expected, true, result); - } - - private void checkObjectNoPhoto(String oid, File file, OperationResult result) throws SchemaException, IOException, ObjectNotFoundException { - PrismObject expected = PrismTestUtil.parseObject(file); - expected.asObjectable().setJpegPhoto(null); - checkObject(oid, expected, false, result); - } - - private void checkObject(String oid, PrismObject expected, boolean loadPhoto, OperationResult result) throws ObjectNotFoundException, SchemaException { - Collection> options; - if (loadPhoto) { - options = Collections.singletonList( - SelectorOptions.create(prismContext.toUniformPath(UserType.F_JPEG_PHOTO), GetOperationOptions.createRetrieve(RetrieveOption.INCLUDE))); - } else { - options = null; - } - PrismObject real = repositoryService.getObject(UserType.class, oid, options, result); - ObjectDelta delta = expected.diff(real); - System.out.println("Expected object = \n" + expected.debugDump()); - System.out.println("Real object in repo = \n" + real.debugDump()); - System.out.println("Difference = \n" + delta.debugDump()); - if (!delta.isEmpty()) { - fail("Objects are not equal.\n*** Expected:\n" + expected.debugDump() + "\n*** Got:\n" + real.debugDump() + "\n*** Delta:\n" + delta.debugDump()); - } - } - - private void checkObject(String oid, File file, OperationResult result, ObjectDelta... appliedDeltas) throws SchemaException, IOException, ObjectNotFoundException { - PrismObject expected = PrismTestUtil.parseObject(file); - for (ObjectDelta delta : appliedDeltas) { - delta.applyTo(expected); - } - checkObject(oid, expected, true, result); - } } diff --git a/repo/repo-sql-impl-test/src/test/resources/photo/t008-remove-other-photo-by-delete.xml b/repo/repo-sql-impl-test/src/test/resources/photo/t007-remove-non-existing-photo-by-delete.xml similarity index 100% rename from repo/repo-sql-impl-test/src/test/resources/photo/t008-remove-other-photo-by-delete.xml rename to repo/repo-sql-impl-test/src/test/resources/photo/t007-remove-non-existing-photo-by-delete.xml diff --git a/repo/repo-sql-impl-test/src/test/resources/photo/t007-remove-photo-by-delete.xml b/repo/repo-sql-impl-test/src/test/resources/photo/t008-remove-photo-by-delete.xml similarity index 100% rename from repo/repo-sql-impl-test/src/test/resources/photo/t007-remove-photo-by-delete.xml rename to repo/repo-sql-impl-test/src/test/resources/photo/t008-remove-photo-by-delete.xml diff --git a/repo/repo-sql-impl-test/src/test/resources/photo/t108-remove-other-photo-by-delete.xml b/repo/repo-sql-impl-test/src/test/resources/photo/t107-remove-non-existing-photo-by-delete.xml similarity index 100% rename from repo/repo-sql-impl-test/src/test/resources/photo/t108-remove-other-photo-by-delete.xml rename to repo/repo-sql-impl-test/src/test/resources/photo/t107-remove-non-existing-photo-by-delete.xml diff --git a/repo/repo-sql-impl-test/src/test/resources/photo/t107-remove-photo-by-delete.xml b/repo/repo-sql-impl-test/src/test/resources/photo/t108-remove-photo-by-delete.xml similarity index 100% rename from repo/repo-sql-impl-test/src/test/resources/photo/t107-remove-photo-by-delete.xml rename to repo/repo-sql-impl-test/src/test/resources/photo/t108-remove-photo-by-delete.xml diff --git a/repo/repo-sql-impl-test/testng-integration.xml b/repo/repo-sql-impl-test/testng-integration.xml index 59eadde6ce4..458d7c7e2be 100644 --- a/repo/repo-sql-impl-test/testng-integration.xml +++ b/repo/repo-sql-impl-test/testng-integration.xml @@ -46,6 +46,8 @@ + + diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/CertificationCaseHelper.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/CertificationCaseHelper.java index 5598caee53b..b569d90b845 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/CertificationCaseHelper.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/CertificationCaseHelper.java @@ -7,6 +7,7 @@ package com.evolveum.midpoint.repo.sql.helpers; +import com.evolveum.midpoint.prism.PrismContainer; import com.evolveum.midpoint.prism.PrismContainerValue; import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.PrismObject; @@ -39,6 +40,7 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCaseType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationWorkItemType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import org.apache.commons.collections4.CollectionUtils; import org.hibernate.query.Query; import org.hibernate.Session; import org.hibernate.query.NativeQuery; @@ -68,13 +70,12 @@ public class CertificationCaseHelper { @Autowired private PrismContext prismContext; @Autowired private RelationRegistry relationRegistry; @Autowired private GeneralHelper generalHelper; - @Autowired private NameResolutionHelper nameResolutionHelper; @Autowired private ObjectRetriever objectRetriever; @Autowired private RepositoryService repositoryService; @Autowired private ExtItemDictionary extItemDictionary; @Autowired private BaseHelper baseHelper; - public void addCertificationCampaignCases(Session session, RObject object, boolean deleteBeforeAdd) { + void addCertificationCampaignCases(Session session, RObject object, boolean deleteBeforeAdd) { if (!(object instanceof RAccessCertificationCampaign)) { return; } @@ -91,7 +92,8 @@ public void addCertificationCampaignCases(Session session, RObject object, boole } } - public void addCertificationCampaignCases(Session session, String campaignOid, Collection values, int currentId, List affectedIds) throws DtoTranslationException { + private void addCertificationCampaignCases(Session session, String campaignOid, Collection values, + int currentId, List affectedIds) throws DtoTranslationException { for (PrismContainerValue value : values) { AccessCertificationCaseType caseType = new AccessCertificationCaseType(); caseType.setupContainerValue(value); @@ -116,7 +118,7 @@ private RepositoryContext createRepositoryContext() { return new RepositoryContext(repositoryService, prismContext, relationRegistry, extItemDictionary, baseHelper.getConfiguration()); } - public void deleteCertificationCampaignCases(Session session, String oid) { + void deleteCertificationCampaignCases(Session session, String oid) { // TODO couldn't this cascading be done by hibernate itself? // Query deleteReferences = session.getNamedQuery("delete.campaignCasesReferences"); // deleteReferences.setParameter("oid", oid); @@ -135,8 +137,8 @@ public void deleteCertificationCampaignCases(Session session, String oid) { deleteCases.executeUpdate(); } - public Collection filterCampaignCaseModifications(Class type, - Collection modifications) { + Collection filterCampaignCaseModifications(Class type, + Collection modifications) { Collection caseDelta = new ArrayList<>(); if (!AccessCertificationCampaignType.class.equals(type)) { return caseDelta; @@ -159,7 +161,7 @@ public Collection filterCampaignCase return caseDelta; } - public void updateCampaignCases(Session session, String campaignOid, + void updateCampaignCases(Session session, String campaignOid, Collection modifications, RepoModifyOptions modifyOptions) throws SchemaException, ObjectNotFoundException, DtoTranslationException { if (modifications.isEmpty() && !RepoModifyOptions.isExecuteIfNoChanges(modifyOptions)) { return; @@ -171,7 +173,7 @@ public void updateCampaignCases(Session session, String c updateCasesContent(session, campaignOid, modifications, casesAddedOrDeleted, modifyOptions); } - protected List addOrDeleteCases(Session session, String campaignOid, Collection modifications) throws SchemaException, DtoTranslationException { + private List addOrDeleteCases(Session session, String campaignOid, Collection modifications) throws SchemaException, DtoTranslationException { boolean replacePresent = false; List affectedIds = new ArrayList<>(); for (ItemDelta delta : modifications) { @@ -384,9 +386,8 @@ private PrismObject resolveCampaign(String camp } // adds cases to campaign if requested by options - public void updateLoadedCampaign(PrismObject object, - Collection> options, - Session session) throws SchemaException { + void updateLoadedCampaign(PrismObject object, Collection> options, + Session session) throws SchemaException { if (!SelectorOptions.hasToLoadPath(AccessCertificationCampaignType.F_CASE, options)) { return; } @@ -402,16 +403,21 @@ public void updateLoadedCampaign(PrismObject object, // TODO fetch only XML representation @SuppressWarnings({"raw", "unchecked"}) List cases = query.list(); - if (cases == null || cases.isEmpty()) { - return; - } - - AccessCertificationCampaignType campaign = (AccessCertificationCampaignType) object.asObjectable(); - List jaxbCases = campaign.getCase(); - for (RAccessCertificationCase rCase : cases) { - AccessCertificationCaseType jaxbCase = rCase.toJAXB(prismContext); - jaxbCases.add(jaxbCase); + if (CollectionUtils.isNotEmpty(cases)) { + AccessCertificationCampaignType campaign = (AccessCertificationCampaignType) object.asObjectable(); + List jaxbCases = campaign.getCase(); + for (RAccessCertificationCase rCase : cases) { + AccessCertificationCaseType jaxbCase = rCase.toJAXB(prismContext); + jaxbCases.add(jaxbCase); + } + PrismContainer caseContainer = object.findContainer(AccessCertificationCampaignType.F_CASE); + caseContainer.setIncomplete(false); + } else { + PrismContainer caseContainer = object.findContainer(AccessCertificationCampaignType.F_CASE); + if (caseContainer != null) { + caseContainer.clear(); // just in case + caseContainer.setIncomplete(false); + } } } - } diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/LookupTableHelper.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/LookupTableHelper.java index 497165b4297..563dc7cf8ce 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/LookupTableHelper.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/LookupTableHelper.java @@ -7,6 +7,7 @@ package com.evolveum.midpoint.repo.sql.helpers; +import com.evolveum.midpoint.prism.PrismContainer; import com.evolveum.midpoint.prism.PrismContainerValue; import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.PrismObject; @@ -29,9 +30,8 @@ import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; -import com.evolveum.midpoint.xml.ns._public.common.common_3.LookupTableRowType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.LookupTableType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.hibernate.Session; import org.hibernate.query.Query; @@ -188,9 +188,9 @@ public GetOperationOptions findLookupTableGetOption(Collection void updateLoadedLookupTable(PrismObject object, - Collection> options, - Session session) throws SchemaException { + void updateLoadedLookupTable(PrismObject object, + Collection> options, + Session session) throws SchemaException { if (!SelectorOptions.hasToLoadPath(LookupTableType.F_ROW, options)) { return; } @@ -211,16 +211,23 @@ public void updateLoadedLookupTable(PrismObject object } } + //noinspection unchecked List rows = query.list(); - if (rows == null || rows.isEmpty()) { - return; - } - - LookupTableType lookup = (LookupTableType) object.asObjectable(); - List jaxbRows = lookup.getRow(); - for (RLookupTableRow row : rows) { - LookupTableRowType jaxbRow = row.toJAXB(prismContext); - jaxbRows.add(jaxbRow); + if (CollectionUtils.isNotEmpty(rows)) { + LookupTableType lookup = (LookupTableType) object.asObjectable(); + List jaxbRows = lookup.getRow(); + for (RLookupTableRow row : rows) { + LookupTableRowType jaxbRow = row.toJAXB(prismContext); + jaxbRows.add(jaxbRow); + } + PrismContainer rowContainer = object.findContainer(LookupTableType.F_ROW); + rowContainer.setIncomplete(false); + } else { + PrismContainer rowContainer = object.findContainer(LookupTableType.F_ROW); + if (rowContainer != null) { + rowContainer.clear(); // just in case + rowContainer.setIncomplete(false); + } } } diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/ObjectRetriever.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/ObjectRetriever.java index fa098f7c923..63355be0884 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/ObjectRetriever.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/ObjectRetriever.java @@ -54,6 +54,7 @@ import javax.persistence.criteria.CriteriaQuery; import javax.xml.namespace.QName; import java.util.*; +import java.util.stream.Collectors; import static org.apache.commons.lang3.ArrayUtils.getLength; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; @@ -540,16 +541,17 @@ private PrismObject updateLoadedObject(GetObjectResult throw e; } attachDiagDataIfRequested(prismObject, fullObject, options); - if (FocusType.class.isAssignableFrom(prismObject.getCompileTimeClass())) { + if (prismObject.getCompileTimeClass() != null && FocusType.class.isAssignableFrom(prismObject.getCompileTimeClass())) { if (SelectorOptions.hasToLoadPath(FocusType.F_JPEG_PHOTO, options)) { - //todo improve, use user.hasPhoto flag and take options into account [lazyman] - //this is called only when options contains INCLUDE user/jpegPhoto Query query = session.getNamedQuery("get.focusPhoto"); query.setParameter("oid", prismObject.getOid()); byte[] photo = (byte[]) query.uniqueResult(); if (photo != null) { - PrismProperty property = prismObject.findOrCreateProperty(FocusType.F_JPEG_PHOTO); - property.setRealValue(photo); + PrismProperty photoProperty = prismObject.findOrCreateProperty(FocusType.F_JPEG_PHOTO); + photoProperty.setRealValue(photo); + photoProperty.setIncomplete(false); + } else { + setPropertyNullAndComplete(prismObject, FocusType.F_JPEG_PHOTO); } } } else if (ShadowType.class.equals(prismObject.getCompileTimeClass())) { @@ -580,19 +582,15 @@ private PrismObject updateLoadedObject(GetObjectResult String xmlResult = RUtil.getXmlFromByteArray(opResult, true); OperationResultType resultType = prismContext.parserFor(xmlResult).parseRealValue(OperationResultType.class); - PrismProperty property = prismObject.findOrCreateProperty(TaskType.F_RESULT); - property.setRealValue(resultType); //OperationResult.createOperationResult(resultType) + PrismProperty resultProperty = prismObject.findOrCreateProperty(TaskType.F_RESULT); + resultProperty.setRealValue(resultType); + resultProperty.setIncomplete(false); prismObject.setPropertyRealValue(TaskType.F_RESULT_STATUS, resultType.getStatus()); + } else { + setPropertyNullAndComplete(prismObject, TaskType.F_RESULT); } } -// else { -// Query query = session.getNamedQuery("get.taskStatus"); -// query.setParameter("oid", prismObject.getOid()); -// -// ROperationResultStatus status = query.uniqueResult(); -// prismObject.setPropertyRealValue(TaskType.F_RESULT_STATUS, (status != null ? status.getSchemaValue() : null)); -// } } if (getConfiguration().isEnableIndexOnlyItems()) { loadIndexOnlyItemsIfNeeded(prismObject, options, raw, session); @@ -608,7 +606,14 @@ private PrismObject updateLoadedObject(GetObjectResult return prismObject; } - // TODO add support for index-only attributes here + private void setPropertyNullAndComplete(PrismObject prismObject, ItemPath path) { + PrismProperty photoProperty = prismObject.findProperty(path); + if (photoProperty != null) { + photoProperty.setRealValue(null); + photoProperty.setIncomplete(false); + } + } + private void loadIndexOnlyItemsIfNeeded(PrismObject prismObject, Collection> options, boolean raw, Session session) throws SchemaException { List> retrieveOptions = SelectorOptions.filterRetrieveOptions(options); @@ -641,16 +646,25 @@ private void loadIndexOnlyItemsIfNeeded(PrismObject pr item.addRealValue(value.getValue()); } } - prismObject.getOrCreateExtension().getValue().add(item); + PrismContainerValue extensionPcv = prismObject.getOrCreateExtension().getValue(); + extensionPcv.removeProperty(itemDefinition.getItemName()); // temporary hack + extensionPcv.add(item); } else { throw new UnsupportedOperationException( "Non-property index-only items are not supported: " + itemDefinition); } + } else { + // this is to remove "incomplete" flag and/or any obsolete information + PrismContainerValue extensionPcv = prismObject.getExtensionContainerValue(); + if (extensionPcv != null) { + extensionPcv.removeProperty(itemDefinition.getItemName()); + } } } } } // todo what about extension items that are not part of object extension definition? + // we should utilize 'incomplete' flag // attributes Class compileTimeClass = prismObject.getCompileTimeClass(); @@ -681,8 +695,11 @@ private void retrieveAllAttributeValues(PrismObject shadowObject, Collection> dbCollection, boolean raw) throws SchemaException { PrismContainer attributeContainer = shadowObject.findOrCreateContainer(ShadowType.F_ATTRIBUTES); // Hack: let's ignore values of attributes that already exist in this container - Collection existingAttributeNames = attributeContainer.getValue().getItemNames(); - LOGGER.trace("existingAttributeNames = {}", existingAttributeNames); + Set existingCompleteAttributeNames = attributeContainer.getValue().getItems().stream() + .filter(item -> !item.isIncomplete()) + .map(Item::getElementName) + .collect(Collectors.toSet()); + LOGGER.trace("existingAttributeNames = {}", existingCompleteAttributeNames); for (ROExtValue rValue : dbCollection) { if (rValue.getOwnerType() == RObjectExtensionType.ATTRIBUTES) { LOGGER.trace("- processing {}", rValue); @@ -692,7 +709,7 @@ private void retrieveAllAttributeValues(PrismObject shadowObject, rValue.getItemId(), rValue, shadowObject); } else { ItemName extItemName = RUtil.stringToQName(extItem.getName()); - if (!QNameUtil.matchAny(extItemName, existingAttributeNames)) { + if (!QNameUtil.matchAny(extItemName, existingCompleteAttributeNames)) { PrismProperty attribute = attributeContainer.findProperty(extItemName); if (attribute == null) { if (raw) { @@ -705,6 +722,7 @@ private void retrieveAllAttributeValues(PrismObject shadowObject, } //noinspection unchecked attribute.addRealValue(rValue.getValue()); // no polystring nor reference is expected here + attribute.setIncomplete(false); } } } diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/ObjectUpdater.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/ObjectUpdater.java index 2fe0966eef4..c3c4ec9724f 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/ObjectUpdater.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/helpers/ObjectUpdater.java @@ -95,8 +95,8 @@ public String addObjectAttempt(PrismObject object, Rep boolean noFetchExtensionValueInsertionForbidden, OperationResult result) throws ObjectAlreadyExistsException, SchemaException { - LOGGER_PERFORMANCE.debug("> add object {}, oid={}, overwrite={}", - object.getCompileTimeClass().getSimpleName(), object.getOid(), options.isOverwrite()); + String classSimpleName = object.getCompileTimeClass() != null ? object.getCompileTimeClass().getSimpleName() : "(unknown class)"; + LOGGER_PERFORMANCE.debug("> add object {}, oid={}, overwrite={}", classSimpleName, object.getOid(), options.isOverwrite()); String oid = null; Session session = null; @@ -106,9 +106,7 @@ public String addObjectAttempt(PrismObject object, Rep // or it is org. and by the import we do not know it so it will be trying to delete non-existing object String originalOid = object.getOid(); try { - if (LOGGER.isTraceEnabled()) { - LOGGER.trace("Object\n{}", object.debugDump()); - } + LOGGER.trace("Object\n{}", object.debugDumpLazily()); ObjectTypeUtil.normalizeAllRelations(object, relationRegistry); LOGGER.trace("Translating JAXB to data type."); @@ -130,7 +128,7 @@ public String addObjectAttempt(PrismObject object, Rep } session.getTransaction().commit(); - LOGGER.trace("Saved object '{}' with oid '{}'", object.getCompileTimeClass().getSimpleName(), oid); + LOGGER.trace("Saved object '{}' with oid '{}'", classSimpleName, oid); object.setOid(oid); } catch (PersistenceException ex) { @@ -236,8 +234,7 @@ private List createAddParentRefDelta(Pris return Collections.singletonList(delta); } - public void updateFullObject(RObject object, PrismObject savedObject) - throws DtoTranslationException, SchemaException { + void updateFullObject(RObject object, PrismObject savedObject) throws SchemaException { LOGGER.trace("Updating full object xml column start."); savedObject.setVersion(Integer.toString(object.getVersion()));