From 505867352e4737fde338aa0dbbe7874cd39c1212 Mon Sep 17 00:00:00 2001 From: Pavol Mederly Date: Tue, 31 May 2016 13:45:40 +0200 Subject: [PATCH 1/2] Data model visualizer - interim commit. --- .../model/api/ModelDiagnosticService.java | 7 +++++++ .../impl/controller/ModelDiagController.java | 18 +++++++++++++++++- .../impl/dataModel/DataModelVisualizer.java | 18 ++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/DataModelVisualizer.java diff --git a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelDiagnosticService.java b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelDiagnosticService.java index 179d818f041..7b6c59b7c0d 100644 --- a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelDiagnosticService.java +++ b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelDiagnosticService.java @@ -96,4 +96,11 @@ public interface ModelDiagnosticService { * EXPERIMENTAL. */ public String executeRepositoryQuery(String query, Task task, OperationResult parentResult) throws SchemaException, SecurityViolationException; + + /** + * Exports data model + * + * EXPERIMENTAL. (TODO find a better place) + */ + String exportDataModel(Task task, OperationResult parentResult) throws SchemaException; } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelDiagController.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelDiagController.java index 3a5ceaf9a3e..62c8cdef76b 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelDiagController.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelDiagController.java @@ -20,6 +20,7 @@ import javax.xml.namespace.QName; +import com.evolveum.midpoint.model.impl.dataModel.DataModelVisualizer; import com.evolveum.midpoint.schema.ProvisioningDiag; import com.evolveum.midpoint.security.api.AuthorizationConstants; import com.evolveum.midpoint.security.api.SecurityEnforcer; @@ -63,7 +64,8 @@ public class ModelDiagController implements ModelDiagnosticService { public static final String CLASS_NAME_WITH_DOT = ModelDiagController.class.getName() + "."; private static final String REPOSITORY_SELF_TEST_USER = CLASS_NAME_WITH_DOT + "repositorySelfTest.user"; - + private static final String EXPORT_DATA_MODEL = CLASS_NAME_WITH_DOT + "exportDataModel"; + private static final String NAME_PREFIX = "selftest"; private static final int NAME_RANDOM_LENGTH = 5; @@ -77,6 +79,8 @@ public class ModelDiagController implements ModelDiagnosticService { private static final Trace LOGGER = TraceManager.getTrace(ModelDiagController.class); + @Autowired + private DataModelVisualizer dataModelVisualizer; @Autowired(required = true) private PrismContext prismContext; @@ -461,4 +465,16 @@ private PrismObjectDefinition getObjectDefinition(Clas return prismContext.getSchemaRegistry().findObjectDefinitionByCompileTimeClass(type); } + @Override + public String exportDataModel(Task task, OperationResult parentResult) throws SchemaException { + OperationResult result = parentResult.createSubresult(EXPORT_DATA_MODEL); + try { + String rv = dataModelVisualizer.visualize(task, result); + result.computeStatusIfUnknown(); + return rv; + } catch(Throwable t) { + result.recordFatalError(t.getMessage(), t); + throw t; + } + } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/DataModelVisualizer.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/DataModelVisualizer.java new file mode 100644 index 00000000000..1786effa835 --- /dev/null +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/DataModelVisualizer.java @@ -0,0 +1,18 @@ +package com.evolveum.midpoint.model.impl.dataModel; + +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType; +import org.springframework.stereotype.Component; + +/** + * @author pmederly + */ + +@Component +public class DataModelVisualizer { + + public String visualize(Task task, OperationResult result) { + return null; + } +} From 1035e00976ae409d72d2b16a6f9ffd1421b80a79 Mon Sep 17 00:00:00 2001 From: Pavol Mederly Date: Thu, 2 Jun 2016 11:41:13 +0200 Subject: [PATCH 2/2] Interim commit. --- .../model/api/ModelDiagnosticService.java | 8 +- .../impl/controller/ModelDiagController.java | 10 +- .../model/impl/dataModel/AdHocDataItem.java | 21 ++ .../model/impl/dataModel/DataItem.java | 12 + .../impl/dataModel/DataModelVisualizer.java | 320 +++++++++++++++++- .../model/impl/dataModel/MappingRelation.java | 83 +++++ .../model/impl/dataModel/Relation.java | 46 +++ .../impl/dataModel/RepositoryDataItem.java | 93 +++++ .../impl/dataModel/ResourceDataItem.java | 129 +++++++ .../impl/dataModel/VisualizationContext.java | 247 ++++++++++++++ .../model/intest/TestModelVisualization.java | 96 ++++++ 11 files changed, 1050 insertions(+), 15 deletions(-) create mode 100644 model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/AdHocDataItem.java create mode 100644 model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/DataItem.java create mode 100644 model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/MappingRelation.java create mode 100644 model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/Relation.java create mode 100644 model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/RepositoryDataItem.java create mode 100644 model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/ResourceDataItem.java create mode 100644 model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/VisualizationContext.java create mode 100644 model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestModelVisualization.java diff --git a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelDiagnosticService.java b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelDiagnosticService.java index 7b6c59b7c0d..4f61d5d7aae 100644 --- a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelDiagnosticService.java +++ b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelDiagnosticService.java @@ -17,20 +17,15 @@ import java.util.Collection; -import com.evolveum.midpoint.model.api.context.ModelContext; -import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.schema.ProvisioningDiag; import com.evolveum.midpoint.schema.RepositoryDiag; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.exception.CommunicationException; import com.evolveum.midpoint.util.exception.ConfigurationException; -import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; -import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.exception.SecurityViolationException; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; /** * A service provided by the IDM Model focused on system diagnostic. It allows to retrieve diagnostic data @@ -102,5 +97,6 @@ public interface ModelDiagnosticService { * * EXPERIMENTAL. (TODO find a better place) */ - String exportDataModel(Task task, OperationResult parentResult) throws SchemaException; + String exportDataModel(Collection resourceOids, Task task, OperationResult parentResult) + throws SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException, SecurityViolationException; } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelDiagController.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelDiagController.java index 62c8cdef76b..1456e53a1a2 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelDiagController.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelDiagController.java @@ -24,7 +24,7 @@ import com.evolveum.midpoint.schema.ProvisioningDiag; import com.evolveum.midpoint.security.api.AuthorizationConstants; import com.evolveum.midpoint.security.api.SecurityEnforcer; -import com.evolveum.midpoint.util.exception.SecurityViolationException; +import com.evolveum.midpoint.util.exception.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @@ -46,9 +46,6 @@ import com.evolveum.midpoint.util.DebugUtil; import com.evolveum.midpoint.util.MiscUtil; import com.evolveum.midpoint.util.RandomString; -import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException; -import com.evolveum.midpoint.util.exception.ObjectNotFoundException; -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.ObjectType; @@ -466,10 +463,11 @@ private PrismObjectDefinition getObjectDefinition(Clas } @Override - public String exportDataModel(Task task, OperationResult parentResult) throws SchemaException { + public String exportDataModel(Collection resourceOids, Task task, OperationResult parentResult) + throws SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException, SecurityViolationException { OperationResult result = parentResult.createSubresult(EXPORT_DATA_MODEL); try { - String rv = dataModelVisualizer.visualize(task, result); + String rv = dataModelVisualizer.visualize(resourceOids, task, result); result.computeStatusIfUnknown(); return rv; } catch(Throwable t) { diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/AdHocDataItem.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/AdHocDataItem.java new file mode 100644 index 00000000000..38a4cf4301f --- /dev/null +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/AdHocDataItem.java @@ -0,0 +1,21 @@ +package com.evolveum.midpoint.model.impl.dataModel; + +import com.evolveum.midpoint.prism.path.ItemPath; +import org.jetbrains.annotations.NotNull; + +/** + * @author mederly + */ +public class AdHocDataItem extends DataItem { + + @NotNull private final ItemPath itemPath; + + public AdHocDataItem(@NotNull ItemPath itemPath) { + this.itemPath = itemPath; + } + + @Override + public String getNodeName() { + return "Unresolved: " + itemPath; + } +} diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/DataItem.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/DataItem.java new file mode 100644 index 00000000000..aff00104101 --- /dev/null +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/DataItem.java @@ -0,0 +1,12 @@ +package com.evolveum.midpoint.model.impl.dataModel; + +/** + * @author mederly + */ +public abstract class DataItem { + + public abstract String getNodeName(); + public abstract String getNodeLabel(); + public abstract String getNodeStyleAttributes(); + +} diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/DataModelVisualizer.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/DataModelVisualizer.java index 1786effa835..e0190a45c21 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/DataModelVisualizer.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/DataModelVisualizer.java @@ -1,10 +1,36 @@ package com.evolveum.midpoint.model.impl.dataModel; +import com.evolveum.midpoint.common.refinery.RefinedAssociationDefinition; +import com.evolveum.midpoint.common.refinery.RefinedAttributeDefinition; +import com.evolveum.midpoint.common.refinery.RefinedObjectClassDefinition; +import com.evolveum.midpoint.common.refinery.RefinedResourceSchema; +import com.evolveum.midpoint.model.impl.controller.ModelController; +import com.evolveum.midpoint.prism.PrismContext; +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.prism.path.NameItemPathSegment; +import com.evolveum.midpoint.prism.query.ObjectQuery; +import com.evolveum.midpoint.prism.query.builder.QueryBuilder; +import com.evolveum.midpoint.schema.constants.ExpressionConstants; +import com.evolveum.midpoint.schema.processor.ResourceSchema; import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.schema.util.ObjectTypeUtil; import com.evolveum.midpoint.task.api.Task; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType; +import com.evolveum.midpoint.util.QNameUtil; +import com.evolveum.midpoint.util.exception.*; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import javax.xml.namespace.QName; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + /** * @author pmederly */ @@ -12,7 +38,295 @@ @Component public class DataModelVisualizer { - public String visualize(Task task, OperationResult result) { - return null; + private static final Trace LOGGER = TraceManager.getTrace(DataModelVisualizer.class); + + @Autowired + private ModelController modelController; + + @Autowired + private PrismContext prismContext; + + public String visualize(Collection resourceOids, Task task, OperationResult result) + throws SchemaException, SecurityViolationException, ObjectNotFoundException, CommunicationException, ConfigurationException { + + LOGGER.info("Starting data model visualization"); + + VisualizationContext ctx = new VisualizationContext(prismContext); + + ObjectQuery resourceQuery; + if (resourceOids != null) { + resourceQuery = QueryBuilder.queryFor(ResourceType.class, prismContext) + .id(resourceOids.toArray(new String[0])) + .build(); + } else { + resourceQuery = null; + } + List> resources = modelController.searchObjects(ResourceType.class, resourceQuery, null, task, result); + + createDataItems(ctx, resources); + processResourceMappings(ctx, resources); + + return ctx.exportDot(); } + + private void processResourceMappings(VisualizationContext ctx, List> resources) throws SchemaException { + for (PrismObject resource : resources) { + LOGGER.info("Processing {}", ObjectTypeUtil.toShortString(resource)); + RefinedResourceSchema refinedResourceSchema = RefinedResourceSchema.getRefinedSchema(resource); + if (refinedResourceSchema == null) { + LOGGER.info("Refined resource schema is null, skipping the resource."); + continue; + } + List refinedDefinitions = refinedResourceSchema.getRefinedDefinitions(); + for (RefinedObjectClassDefinition refinedDefinition : refinedDefinitions) { + LOGGER.debug("Processing refined definition {}", refinedDefinition); + Collection> attributeDefinitions = refinedDefinition.getAttributeDefinitions(); + for (RefinedAttributeDefinition attributeDefinition : attributeDefinitions) { + if (attributeDefinition.isIgnored()) { + continue; + } + LOGGER.debug("Processing refined attribute definition for {}", attributeDefinition.getName()); + ResourceDataItem attrItem = ctx.findResourceItem(resource.getOid(), def(refinedDefinition.getKind()), def(refinedDefinition.getIntent()), new ItemPath(attributeDefinition.getName())); + if (attributeDefinition.getOutboundMappingType() != null) { + processOutboundMapping(ctx, attrItem, attributeDefinition.getOutboundMappingType()); + } + processInboundMappings(ctx, attrItem, attributeDefinition.getInboundMappingTypes()); + } + Collection associationDefinitions = refinedDefinition.getAssociations(); + for (RefinedAssociationDefinition associationDefinition : associationDefinitions) { + if (associationDefinition.isIgnored()) { + continue; + } + LOGGER.debug("Processing refined association definition for {}", associationDefinition.getName()); + ResourceDataItem assocItem = ctx.findResourceItem(resource.getOid(), def(refinedDefinition.getKind()), def(refinedDefinition.getIntent()), new ItemPath(associationDefinition.getName())); + if (associationDefinition.getOutboundMappingType() != null) { + processOutboundMapping(ctx, assocItem, associationDefinition.getOutboundMappingType()); + } +// if (associationDefinition.getAssociationTarget() != null) { +// RefinedObjectClassDefinition target = associationDefinition.getAssociationTarget(); +// boolean objectToSubject = associationDefinition.getResourceObjectAssociationType().getDirection() == ResourceObjectAssociationDirectionType.OBJECT_TO_SUBJECT; +// associationDefinition.getResourceObjectAssociationType().getAssociationAttribute() +// } + } + } + } + } + + private void createDataItems(VisualizationContext ctx, List> resources) throws SchemaException { + LOGGER.debug("createDataItems starting"); + for (PrismObject resource : resources) { + final ResourceSchema resourceSchema = RefinedResourceSchema.getResourceSchema(resource, prismContext); + if (resourceSchema == null) { + LOGGER.info("Resource schema is null, skipping the resource."); + continue; + } + RefinedResourceSchema refinedResourceSchema = RefinedResourceSchema.getRefinedSchema(resource); + if (refinedResourceSchema == null) { + LOGGER.info("Refined resource schema is null, skipping the resource."); // actually shouldn't be null if resource schema exists + continue; + } + + ctx.registerResource(resource); + + List refinedDefinitions = refinedResourceSchema.getRefinedDefinitions(); + for (RefinedObjectClassDefinition refinedDefinition : refinedDefinitions) { + LOGGER.debug("Processing refined definition {} in {}", refinedDefinition, resource); + Collection> attributeDefinitions = refinedDefinition.getAttributeDefinitions(); + //Collection rawAttributeDefinitions = refinedDefinition.getObjectClassDefinition().getAttributeDefinitions(); + for (RefinedAttributeDefinition attributeDefinition : attributeDefinitions) { + if (attributeDefinition.isIgnored()) { + continue; + } + LOGGER.debug("Registering refined attribute definition for {}", attributeDefinition.getName()); + ResourceDataItem attrItem = new ResourceDataItem(ctx, resource.getOid(), def(refinedDefinition.getKind()), def(refinedDefinition.getIntent()), attributeDefinition.getName()); + attrItem.setRefinedResourceSchema(refinedResourceSchema); + attrItem.setRefinedObjectClassDefinition(refinedDefinition); + attrItem.setRefinedAttributeDefinition(attributeDefinition); + // TODO check the name + ctx.registerDataItem(attrItem); + } + // TODO check attributes not mentioned in schema handling + Collection associationDefinitions = refinedDefinition.getAssociations(); + for (RefinedAssociationDefinition associationDefinition : associationDefinitions) { + if (associationDefinition.isIgnored()) { + continue; + } + LOGGER.debug("Registering refined association definition for {}", associationDefinition.getName()); + ResourceDataItem assocItem = new ResourceDataItem(ctx, resource.getOid(), def(refinedDefinition.getKind()), def(refinedDefinition.getIntent()), associationDefinition.getName()); + ctx.registerDataItem(assocItem); + } + } + } +// createRepoDataItems(UserType.class); +// createRepoDataItems(RoleType.class); +// createRepoDataItems(OrgType.class); +// createRepoDataItems(ServiceType.class); + + LOGGER.debug("createDataItems finished"); + } + + static ShadowKindType def(ShadowKindType kind) { + return kind != null ? kind : ShadowKindType.ACCOUNT; + } + + static String def(String intent) { + return intent != null ? intent : "default"; + } + + private void processInboundMappings(VisualizationContext ctx, ResourceDataItem item, List mappings) { + if (mappings == null) { + return; + } + for (MappingType mapping : mappings) { + processInboundMapping(ctx, item, mapping); + } + } + + private void processInboundMapping(@NotNull VisualizationContext ctx, @NotNull ResourceDataItem sourceItem, @NotNull MappingType mapping) { + LOGGER.info("Processing inbound mapping: {} for {}", mapping, sourceItem); + List sources = new ArrayList<>(); + for (MappingSourceDeclarationType sourceDecl : mapping.getSource()) { + LOGGER.debug(" - src: {}", sourceDecl.getPath()); + DataItem explicitSourceItem = resolveSourceItem(ctx, sourceItem, mapping, sourceDecl, null); + sources.add(explicitSourceItem); + } + if (!sources.contains(sourceItem)) { + sources.add(sourceItem); + } + DataItem targetItem = null; + MappingTargetDeclarationType targetDecl = mapping.getTarget(); + if (mapping.getTarget() != null) { + LOGGER.debug(" - target: {}", targetDecl.getPath()); + targetItem = resolveTargetItem(ctx, sourceItem, mapping, targetDecl, ExpressionConstants.VAR_FOCUS); + } + ctx.registerMappingRelation(sources, targetItem, mapping); + } + + // for outbound (but sometimes also inbound) mappings + @NotNull + private DataItem resolveSourceItem(@NotNull VisualizationContext ctx, @NotNull ResourceDataItem currentItem, + @NotNull MappingType mapping, @NotNull MappingSourceDeclarationType sourceDecl, @Nullable QName defaultVariable) { + // todo from the description + ItemPath path = sourceDecl.getPath().getItemPath(); + if (!(path.first() instanceof NameItemPathSegment)) { + LOGGER.warn("Probably incorrect path ({}) - does not start with a name - skipping", path); + return createAdHocDataItem(ctx, path); + } + QName varName; + ItemPath itemPath; + NameItemPathSegment firstNameSegment = (NameItemPathSegment) path.first(); + if (firstNameSegment.isVariable()) { + varName = firstNameSegment.getName(); + itemPath = path.tail(); + } else { + if (defaultVariable == null) { + LOGGER.warn("No default variable for mapping source"); + return createAdHocDataItem(ctx, path); + } + varName = defaultVariable; + itemPath = path; + } + + if (QNameUtil.match(ExpressionConstants.VAR_ACCOUNT, varName)) { + return resolveResourceItem(ctx, currentItem, itemPath); + } else if (QNameUtil.match(ExpressionConstants.VAR_USER, varName)) { + return ctx.resolveRepositoryItem(UserType.class, itemPath); + } else if (QNameUtil.match(ExpressionConstants.VAR_ACTOR, varName)) { + return ctx.resolveRepositoryItem(UserType.class, itemPath); // TODO + } else if (QNameUtil.match(ExpressionConstants.VAR_FOCUS, varName)) { + Class guessedClass = guessFocusClass(currentItem.getResourceOid(), currentItem.getKind(), currentItem.getIntent()); + DataItem item = ctx.resolveRepositoryItem(guessedClass, itemPath); + if (item != null) { + return item; + } + // TODO guess e.g. by item existence in schema + LOGGER.warn("Couldn't resolve {} in $focus", path); + } else if (QNameUtil.match(ExpressionConstants.VAR_INPUT, varName)) { + return currentItem; + } else { + LOGGER.warn("Unsupported variable {} in {}", varName, path); + } + return createAdHocDataItem(ctx, path); + } + + private DataItem createAdHocDataItem(VisualizationContext ctx, ItemPath path) { + return new AdHocDataItem(path); + } + + // currently for inbounds only + @NotNull + private DataItem resolveTargetItem(@NotNull VisualizationContext ctx, @NotNull ResourceDataItem currentItem, + @NotNull MappingType mapping, @NotNull MappingTargetDeclarationType targetDecl, @Nullable QName defaultVariable) { + // todo from the description + ItemPath path = targetDecl.getPath().getItemPath(); + if (!(path.first() instanceof NameItemPathSegment)) { + LOGGER.warn("Probably incorrect path ({}) - does not start with a name - skipping", path); + return createAdHocDataItem(ctx, path); + } + QName varName; + ItemPath itemPath; + NameItemPathSegment firstNameSegment = (NameItemPathSegment) path.first(); + if (firstNameSegment.isVariable()) { + varName = firstNameSegment.getName(); + itemPath = path.tail(); + } else { + if (defaultVariable == null) { + LOGGER.warn("No default variable for mapping target"); + return createAdHocDataItem(ctx, path); + } + varName = defaultVariable; + itemPath = path; + } + + if (QNameUtil.match(ExpressionConstants.VAR_ACCOUNT, varName)) { + return resolveResourceItem(ctx, currentItem, itemPath); // does make sense? + } else if (QNameUtil.match(ExpressionConstants.VAR_USER, varName)) { + return ctx.resolveRepositoryItem(UserType.class, itemPath); + } else if (QNameUtil.match(ExpressionConstants.VAR_ACTOR, varName)) { + return ctx.resolveRepositoryItem(UserType.class, itemPath); // TODO + } else if (QNameUtil.match(ExpressionConstants.VAR_FOCUS, varName)) { + Class guessedClass = guessFocusClass(currentItem.getResourceOid(), currentItem.getKind(), currentItem.getIntent()); + DataItem item = ctx.resolveRepositoryItem(guessedClass, itemPath); + if (item != null) { + return item; + } + // TODO guess e.g. by item existence in schema + LOGGER.warn("Couldn't resolve {} in $focus", path); + } else if (QNameUtil.match(ExpressionConstants.VAR_INPUT, varName)) { + return currentItem; // does make sense? + } else { + LOGGER.warn("Unsupported variable {} in {}", varName, path); + } + return createAdHocDataItem(ctx, path); + } + + private Class guessFocusClass(@NotNull String resourceOid, @NotNull ShadowKindType kind, @NotNull String intent) { + // TODO use synchronization as well + switch (kind) { + case ACCOUNT: return UserType.class; + case ENTITLEMENT: return RoleType.class; + case GENERIC: return OrgType.class; + } + throw new IllegalStateException(); + } + + private ResourceDataItem resolveResourceItem(VisualizationContext ctx, ResourceDataItem currentItem, ItemPath path) { + return ctx.findResourceItem(currentItem.getResourceOid(), currentItem.getKind(), currentItem.getIntent(), path); + } + + private void processOutboundMapping(@NotNull VisualizationContext ctx, @NotNull ResourceDataItem targetItem, @NotNull MappingType mapping) { + LOGGER.info("Processing outbound mapping: {} for {}", mapping, targetItem); + List sources = new ArrayList<>(); + for (MappingSourceDeclarationType sourceDecl : mapping.getSource()) { + LOGGER.info(" - src: {}", sourceDecl.getPath()); + DataItem sourceItem = resolveSourceItem(ctx, targetItem, mapping, sourceDecl, ExpressionConstants.VAR_FOCUS); + sources.add(sourceItem); + } + MappingTargetDeclarationType targetDecl = mapping.getTarget(); + if (targetDecl != null) { + LOGGER.warn(" - ignoring target (mapping is outbound): {}; using {} instead", targetDecl.getPath(), targetItem); + } + ctx.registerMappingRelation(sources, targetItem, mapping); + } + } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/MappingRelation.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/MappingRelation.java new file mode 100644 index 00000000000..8bd710a9a77 --- /dev/null +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/MappingRelation.java @@ -0,0 +1,83 @@ +package com.evolveum.midpoint.model.impl.dataModel; + +import com.evolveum.midpoint.prism.xnode.PrimitiveXNode; +import com.evolveum.midpoint.prism.xnode.XNode; +import com.evolveum.midpoint.schema.constants.SchemaConstants; +import com.evolveum.midpoint.util.QNameUtil; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import com.evolveum.prism.xml.ns._public.types_3.RawType; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.xml.bind.JAXBElement; +import java.util.List; + +/** + * @author mederly + */ +public class MappingRelation extends Relation { + + public static final int MAX_CONSTANT_WIDTH = 50; + @NotNull private final MappingType mapping; + + public MappingRelation(@NotNull List sources, @Nullable DataItem target, @NotNull MappingType mapping) { + super(sources, target); + this.mapping = mapping; + } + + @NotNull + public MappingType getMapping() { + return mapping; + } + + @Override + public String getEdgeLabel() { + return getLabel("", false); + } + + @Nullable + private String getLabel(String defaultLabel, boolean showConstant) { + ExpressionType expression = mapping.getExpression(); + if (expression == null || expression.getExpressionEvaluator().isEmpty()) { + return defaultLabel; + } + if (expression.getExpressionEvaluator().size() > 1) { + return "> 1 evaluator"; + } + JAXBElement evalElement = expression.getExpressionEvaluator().get(0); + Object eval = evalElement.getValue(); + if (QNameUtil.match(evalElement.getName(), SchemaConstants.C_VALUE)) { + if (showConstant) { + if (eval instanceof RawType) { + XNode xnode = ((RawType) eval).getXnode(); + if (xnode instanceof PrimitiveXNode) { + eval = ((PrimitiveXNode) xnode).getStringValue(); + } else { + eval = xnode.toString(); + } + } + return "\'" + StringUtils.abbreviate(String.valueOf(eval), MAX_CONSTANT_WIDTH) + "\'"; + } else { + return "constant"; + } + } else if (eval instanceof AsIsExpressionEvaluatorType) { + return defaultLabel; + } else if (eval instanceof ScriptExpressionEvaluatorType) { + ScriptExpressionEvaluatorType script = (ScriptExpressionEvaluatorType) eval; + if (script.getLanguage() == null) { + return "groovy"; + } else { + return StringUtils.substringAfter(script.getLanguage(), "#"); + } + } else { + return evalElement.getName().getLocalPart(); + } + } + + @Override + public String getNodeLabel(String defaultLabel) { + return getLabel(defaultLabel, true); + } +} diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/Relation.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/Relation.java new file mode 100644 index 00000000000..e588f20fb87 --- /dev/null +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/Relation.java @@ -0,0 +1,46 @@ +package com.evolveum.midpoint.model.impl.dataModel; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * @author mederly + */ +public class Relation { + + @NotNull final private List sources; + @Nullable final private DataItem target; + + public Relation(@NotNull List sources, @Nullable DataItem target) { + this.sources = sources; + this.target = target; + } + + @NotNull + public List getSources() { + return sources; + } + + @Nullable + public DataItem getTarget() { + return target; + } + + @Override + public String toString() { + return "Relation{" + + "sources=" + sources + + ", target=" + target + + '}'; + } + + public String getEdgeLabel() { + return ""; + } + + public String getNodeLabel(String defaultLabel) { + return null; + } +} diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/RepositoryDataItem.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/RepositoryDataItem.java new file mode 100644 index 00000000000..7f645c6d44a --- /dev/null +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/RepositoryDataItem.java @@ -0,0 +1,93 @@ +package com.evolveum.midpoint.model.impl.dataModel; + +import com.evolveum.midpoint.common.refinery.RefinedAttributeDefinition; +import com.evolveum.midpoint.common.refinery.RefinedResourceSchema; +import com.evolveum.midpoint.prism.PrismObjectDefinition; +import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.util.QNameUtil; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import org.apache.commons.lang.StringUtils; +import org.jetbrains.annotations.NotNull; + +import javax.xml.namespace.QName; + +/** + * @author mederly + */ +public class RepositoryDataItem extends DataItem { + + private static final String COLOR_USER = "darkred"; + private static final String COLOR_ROLE = "darkgreen"; + private static final String COLOR_ORG = "darkgreen"; + private static final String COLOR_DEFAULT = "black"; + private static final String COLOR_FILL = "grey92"; + + @NotNull private final QName typeName; + @NotNull private final ItemPath itemPath; + + private PrismObjectDefinition objectDefinition; + + public RepositoryDataItem(@NotNull QName typeName, @NotNull ItemPath itemPath) { + this.typeName = typeName; + if (itemPath.isEmpty()) { + throw new IllegalArgumentException("Empty item path"); + } + this.itemPath = itemPath; + } + + @NotNull + public QName getTypeName() { + return typeName; + } + + @NotNull + public ItemPath getItemPath() { + return itemPath; + } + + public PrismObjectDefinition getObjectDefinition() { + return objectDefinition; + } + + public void setObjectDefinition(PrismObjectDefinition objectDefinition) { + this.objectDefinition = objectDefinition; + } + + public boolean matches(@NotNull QName typeName, @NotNull ItemPath path) { + return QNameUtil.match(this.typeName, typeName) && itemPath.equivalent(path); + } + + @Override + public String getNodeName() { + return "\"" + typeName.getLocalPart() + "." + itemPath + "\""; + } + + @Override + public String getNodeLabel() { + String entity = StringUtils.removeEnd(typeName.getLocalPart(), "Type"); + String pathString = itemPath.toString(); + final String EXT = "extension/"; + if (pathString.startsWith(EXT)) { + entity += " extension"; + pathString = pathString.substring(EXT.length()); + } + return entity + " " + pathString; + } + + @Override + public String getNodeStyleAttributes() { + return "style=filled, fillcolor=" + COLOR_FILL + ", color=" + getBorderColor(); + } + + private String getBorderColor() { + if (QNameUtil.match(UserType.COMPLEX_TYPE, typeName)) { + return COLOR_USER; + } else if (QNameUtil.match(RoleType.COMPLEX_TYPE, typeName)) { + return COLOR_ROLE; + } else if (QNameUtil.match(OrgType.COMPLEX_TYPE, typeName)) { + return COLOR_ORG; + } else { + return COLOR_DEFAULT; + } + } +} diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/ResourceDataItem.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/ResourceDataItem.java new file mode 100644 index 00000000000..6bd0d6bedc4 --- /dev/null +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/ResourceDataItem.java @@ -0,0 +1,129 @@ +package com.evolveum.midpoint.model.impl.dataModel; + +import com.evolveum.midpoint.common.refinery.RefinedAttributeDefinition; +import com.evolveum.midpoint.common.refinery.RefinedObjectClassDefinition; +import com.evolveum.midpoint.common.refinery.RefinedResourceSchema; +import com.evolveum.midpoint.prism.polystring.PolyString; +import com.evolveum.midpoint.util.QNameUtil; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowKindType; +import net.sf.jasperreports.engine.util.ObjectUtils; +import org.jetbrains.annotations.NotNull; + +import javax.xml.namespace.QName; + +/** + * @author mederly + */ +public class ResourceDataItem extends DataItem { + + @NotNull private final VisualizationContext ctx; + @NotNull private final String resourceOid; + @NotNull private final ShadowKindType kind; + @NotNull private final String intent; // TODO or more intents? + @NotNull private final QName itemName; + + private RefinedResourceSchema refinedResourceSchema; + private RefinedObjectClassDefinition refinedObjectClassDefinition; + private RefinedAttributeDefinition refinedAttributeDefinition; + + public ResourceDataItem(@NotNull VisualizationContext ctx, @NotNull String resourceOid, @NotNull ShadowKindType kind, @NotNull String intent, @NotNull QName itemName) { + this.ctx = ctx; + this.resourceOid = resourceOid; + this.kind = kind; + this.intent = intent; + this.itemName = itemName; + } + + @NotNull + public String getResourceOid() { + return resourceOid; + } + + @NotNull + public ShadowKindType getKind() { + return kind; + } + + @NotNull + public String getIntent() { + return intent; + } + + @NotNull + public QName getItemName() { + return itemName; + } + + public RefinedResourceSchema getRefinedResourceSchema() { + if (refinedResourceSchema == null) { + refinedResourceSchema = ctx.getRefinedResourceSchema(resourceOid); + } + return refinedResourceSchema; + } + + public void setRefinedResourceSchema(RefinedResourceSchema refinedResourceSchema) { + this.refinedResourceSchema = refinedResourceSchema; + } + + public void setRefinedObjectClassDefinition(RefinedObjectClassDefinition refinedObjectClassDefinition) { + this.refinedObjectClassDefinition = refinedObjectClassDefinition; + } + + public RefinedObjectClassDefinition getRefinedObjectClassDefinition() { + if (refinedObjectClassDefinition == null) { + RefinedResourceSchema schema = getRefinedResourceSchema(); + if (schema != null) { + refinedObjectClassDefinition = schema.getRefinedDefinition(kind, intent); + } + } + return refinedObjectClassDefinition; + } + + public RefinedAttributeDefinition getRefinedAttributeDefinition() { + if (refinedAttributeDefinition == null) { + RefinedObjectClassDefinition def = getRefinedObjectClassDefinition(); + if (def != null) { + refinedAttributeDefinition = def.findAttributeDefinition(itemName); + } + } + return refinedAttributeDefinition; + } + + public void setRefinedAttributeDefinition(RefinedAttributeDefinition refinedAttributeDefinition) { + this.refinedAttributeDefinition = refinedAttributeDefinition; + } + + @Override + public String toString() { + return "ResourceDataItem{" + + "resourceOid='" + resourceOid + '\'' + + ", kind=" + kind + + ", intent='" + intent + '\'' + + ", name=" + itemName + + '}'; + } + + public boolean matches(String resourceOid, ShadowKindType kind, String intent, QName name) { + if (!this.resourceOid.equals(resourceOid)) { + return false; + } + if (this.kind != kind) { + return false; + } + if (!ObjectUtils.equals(this.intent, intent)) { + return false; + } + return QNameUtil.match(this.itemName, name); + } + + @Override + public String getNodeName() { + return "\"" + getResourceName() + ":" + ctx.getObjectTypeName(getRefinedObjectClassDefinition()) + ":" + itemName.getLocalPart() + "\""; + } + + @NotNull + public String getResourceName() { + PolyString name = ctx.getResource(resourceOid).getName(); + return name != null ? name.getOrig() : resourceOid; + } +} diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/VisualizationContext.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/VisualizationContext.java new file mode 100644 index 00000000000..ec99a2ae850 --- /dev/null +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/dataModel/VisualizationContext.java @@ -0,0 +1,247 @@ +package com.evolveum.midpoint.model.impl.dataModel; + +import com.evolveum.midpoint.common.refinery.RefinedAssociationDefinition; +import com.evolveum.midpoint.common.refinery.RefinedObjectClassDefinition; +import com.evolveum.midpoint.common.refinery.RefinedResourceSchema; +import com.evolveum.midpoint.prism.PrismContext; +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.prism.polystring.PolyString; +import com.evolveum.midpoint.schema.processor.ResourceAttributeDefinition; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.exception.SystemException; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import org.apache.commons.lang3.Validate; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.xml.namespace.QName; +import java.util.*; + +/** + * @author mederly + */ +public class VisualizationContext { + + private static final Trace LOGGER = TraceManager.getTrace(VisualizationContext.class); + + @NotNull private final PrismContext prismContext; + @NotNull private final Set dataItems = new HashSet<>(); + @NotNull private final Map> resources = new HashMap<>(); + @NotNull private final List relations = new ArrayList<>(); + + private boolean subgraphsForResources = false; + private boolean showUnusedItems = false; + + public VisualizationContext(@NotNull PrismContext prismContext) { + this.prismContext = prismContext; + } + + @NotNull + public List getRelations() { + return relations; + } + + @NotNull + public Set getDataItems() { + return dataItems; + } + + public void registerResource(PrismObject resource) { + Validate.notNull(resource.getOid()); + resources.put(resource.getOid(), resource); + } + + public void registerDataItem(ResourceDataItem item) { + dataItems.add(item); + } + + public RefinedResourceSchema getRefinedResourceSchema(String resourceOid) { + PrismObject resource = resources.get(resourceOid); + if (resource == null) { + return null; + } + try { + return RefinedResourceSchema.getRefinedSchema(resource, prismContext); + } catch (SchemaException e) { + throw new SystemException("Unexpected exception: " + e.getMessage(), e); + } + } + + public ResourceDataItem findResourceItem(@NotNull String resourceOid, @Nullable ShadowKindType kind, @Nullable String intent, @NotNull ItemPath path) { + kind = DataModelVisualizer.def(kind); + intent = DataModelVisualizer.def(intent); + QName name = path.asSingleName(); + if (name == null) { + LOGGER.warn("Unexpected path to an attribute: {}", path); + return null; + } + for (ResourceDataItem item : getResourceDataItems()) { + if (item.matches(resourceOid, kind, intent, name)) { + return item; + } + } + LOGGER.warn("Unknown resource data item: resource={}, kind={}, intent={}, name={}", resourceOid, kind, intent, name); + return null; + } + + private List getResourceDataItems() { + List rv = new ArrayList<>(); + for (DataItem item : dataItems) { + if (item instanceof ResourceDataItem) { + rv.add((ResourceDataItem) item); + } + } + return rv; + } + + private List getRepositoryDataItems() { + List rv = new ArrayList<>(); + for (DataItem item : dataItems) { + if (item instanceof RepositoryDataItem) { + rv.add((RepositoryDataItem) item); + } + } + return rv; + } + + public RepositoryDataItem resolveRepositoryItem(Class aClass, ItemPath path) { + QName typeName = prismContext.getSchemaRegistry().findObjectDefinitionByCompileTimeClass(aClass).getTypeName(); + for (RepositoryDataItem item : getRepositoryDataItems()) { + if (item.matches(typeName, path)) { + return item; + } + } + RepositoryDataItem item = new RepositoryDataItem(typeName, path); + dataItems.add(item); + return item; + } + + public void registerMappingRelation(@NotNull List sources, @Nullable DataItem target, @NotNull MappingType mapping) { + LOGGER.info("Adding relation: {} -> {}", sources, target); + MappingRelation relation = new MappingRelation(sources, target, mapping); + relations.add(relation); + } + + public String exportDot() { + StringBuilder sb = new StringBuilder(); + sb.append("digraph G {\n"); + + int clusterNumber = 1; + for (PrismObject resource : resources.values()) { + if (subgraphsForResources) { + sb.append(" subgraph cluster_").append(clusterNumber++).append(" {"); + sb.append(" label=\"").append(resource.getName()).append("\";\n"); + } + RefinedResourceSchema schema = getRefinedResourceSchema(resource.getOid()); + for (RefinedObjectClassDefinition def : schema.getRefinedDefinitions()) { + StringBuilder sb1 = new StringBuilder(); + sb1.append(" subgraph cluster_").append(clusterNumber++).append(" {"); + String typeName = ""; + if (!subgraphsForResources && resources.size() > 1) { + typeName = PolyString.getOrig(resource.getName()) + ": "; + } + typeName += getObjectTypeName(def); + sb1.append(" label=\"").append(typeName).append("\";\n"); + String previousNodeName = null; + for (ResourceAttributeDefinition attrDef : def.getAttributeDefinitions()) { + if (attrDef.isIgnored()) { + continue; + } + ResourceDataItem item = findResourceItem(resource.getOid(), def.getKind(), def.getIntent(), new ItemPath(attrDef.getName())); + if (showUnusedItems || isUsed(item)) { + String nodeName = addLabelForItem(sb1, item); + addHiddenEdge(sb1, previousNodeName, nodeName); + previousNodeName = nodeName; + } + } + for (RefinedAssociationDefinition assocDef : def.getAssociations()) { + if (assocDef.isIgnored()) { + continue; + } + ResourceDataItem item = findResourceItem(resource.getOid(), def.getKind(), def.getIntent(), new ItemPath(assocDef.getName())); + if (showUnusedItems || isUsed(item)) { + String nodeName = addLabelForItem(sb1, item); + addHiddenEdge(sb1, previousNodeName, nodeName); + previousNodeName = nodeName; + } + } + sb1.append(" }\n"); + if (previousNodeName != null) { + sb.append(sb1.toString()); + } + } + if (subgraphsForResources) { + sb.append(" }\n"); + } + } + sb.append("\n"); + + int mappingNode = 1; + for (Relation relation : relations) { + if (relation.getSources().size() == 1 && relation.getTarget() != null) { + sb.append(" ").append(relation.getSources().get(0).getNodeName()); + sb.append(" -> ").append(relation.getTarget().getNodeName()); + sb.append(" [label=\"").append(relation.getEdgeLabel()).append("\"];").append("\n"); + } else { + String mappingName = "m" + (mappingNode++); + String nodeLabel = relation.getNodeLabel(mappingName); + if (nodeLabel != null) { + sb.append(" ").append(mappingName).append(" [label=\"").append(nodeLabel).append("\"];\n"); + } + for (DataItem src : relation.getSources()) { + sb.append(" ").append(src.getNodeName()).append(" -> ").append(mappingName).append("\n"); + } + if (relation.getTarget() != null) { + sb.append(" ").append(mappingName).append(" -> ").append(relation.getTarget().getNodeName()).append("\n"); + } + } + } + + sb.append("}"); + + return sb.toString(); + } + + private boolean isUsed(DataItem item) { + for (Relation relation : relations) { + if (relation.getSources().contains(item) || relation.getTarget() == item) { + return true; + } + } + return false; + } + + private void addHiddenEdge(StringBuilder sb, String previousNodeName, String nodeName) { + if (previousNodeName == null) { + return; + } + sb.append(previousNodeName).append(" -> ").append(nodeName).append(" [style=invis];\n"); + } + + private String addLabelForItem(StringBuilder sb, ResourceDataItem item) { + String nodeName = item.getNodeName(); + sb.append(" ").append(nodeName).append(" [label=\"").append(item.getItemName().getLocalPart()).append("\"];\n"); + return nodeName; + } + + @NotNull + public PrismObject getResource(@NotNull String resourceOid) { + return resources.get(resourceOid); + } + + @NotNull + String getObjectTypeName(RefinedObjectClassDefinition refinedObjectClassDefinition) { + if (refinedObjectClassDefinition == null) { + return "?"; + } + if (refinedObjectClassDefinition.getDisplayName() != null) { + return refinedObjectClassDefinition.getDisplayName(); + } + return refinedObjectClassDefinition.getObjectClassDefinition().getTypeName().getLocalPart() + "/" + refinedObjectClassDefinition.getKind() + "/" + refinedObjectClassDefinition.getIntent(); + } + + +} diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestModelVisualization.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestModelVisualization.java new file mode 100644 index 00000000000..d7ba9cc9405 --- /dev/null +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestModelVisualization.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2010-2016 Evolveum + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.evolveum.midpoint.model.intest; + +import com.evolveum.midpoint.common.refinery.RefinedObjectClassDefinition; +import com.evolveum.midpoint.model.api.ModelExecuteOptions; +import com.evolveum.midpoint.model.api.PolicyViolationException; +import com.evolveum.midpoint.model.api.context.ModelContext; +import com.evolveum.midpoint.model.api.context.ModelElementContext; +import com.evolveum.midpoint.model.api.context.ModelProjectionContext; +import com.evolveum.midpoint.model.api.context.SynchronizationPolicyDecision; +import com.evolveum.midpoint.prism.*; +import com.evolveum.midpoint.prism.delta.ChangeType; +import com.evolveum.midpoint.prism.delta.ObjectDelta; +import com.evolveum.midpoint.prism.delta.PropertyDelta; +import com.evolveum.midpoint.prism.delta.ReferenceDelta; +import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.prism.util.PrismAsserts; +import com.evolveum.midpoint.prism.util.PrismTestUtil; +import com.evolveum.midpoint.schema.ResourceShadowDiscriminator; +import com.evolveum.midpoint.schema.constants.MidPointConstants; +import com.evolveum.midpoint.schema.constants.SchemaConstants; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.schema.util.MiscSchemaUtil; +import com.evolveum.midpoint.schema.util.ResourceTypeUtil; +import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.test.DummyResourceContoller; +import com.evolveum.midpoint.test.IntegrationTestTools; +import com.evolveum.midpoint.test.ObjectChecker; +import com.evolveum.midpoint.test.ObjectSource; +import com.evolveum.midpoint.test.util.TestUtil; +import com.evolveum.midpoint.util.exception.*; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.annotation.DirtiesContext.ClassMode; +import org.springframework.test.context.ContextConfiguration; +import org.testng.AssertJUnit; +import org.testng.annotations.Test; + +import javax.xml.namespace.QName; +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; + +import static com.evolveum.midpoint.test.IntegrationTestTools.display; +import static org.testng.AssertJUnit.*; + +/** + * @author mederly + * + */ +@ContextConfiguration(locations = {"classpath:ctx-model-intest-test-main.xml"}) +@DirtiesContext(classMode = ClassMode.AFTER_CLASS) +public class TestModelVisualization extends AbstractInitializedModelIntegrationTest { + + public static final File TEST_DIR = new File("src/test/resources/modelVisualization"); + + @Override + public void initSystem(Task initTask, OperationResult initResult) throws Exception { + super.initSystem(initTask, initResult); + } + + @Test + public void test100VisualizeOneResource() throws Exception { + final String TEST_NAME = "test100VisualizeOneResource"; + + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(TestModelVisualization.class.getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + // WHEN + String output = modelDiagnosticService.exportDataModel(Collections.singleton(RESOURCE_DUMMY_OID), task, result); + + // THEN + display("Visualization output", output); + result.computeStatus(); + TestUtil.assertSuccess(result); + } + +}