From fb2fe8a683082077dd624f3a1cc7e0942ec9de83 Mon Sep 17 00:00:00 2001 From: kate Date: Mon, 30 Mar 2020 12:26:35 +0200 Subject: [PATCH 1/5] shortDump for PolyString fix --- .../midpoint/prism/polystring/PolyString.java | 11 ++++++++++- .../midpoint/prism/impl/PrismPropertyValueImpl.java | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/polystring/PolyString.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/polystring/PolyString.java index 776a9e9483b..414545cea0f 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/polystring/PolyString.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/polystring/PolyString.java @@ -337,7 +337,16 @@ public String debugDump(int indent) { @Override public void shortDump(StringBuilder sb) { - sb.append(orig); + sb.append("orig=" + orig); + if (getTranslation() != null) { + sb.append("; translation.key=" + getTranslation().getKey()); + } + if (getLang() != null) { + sb.append("; lang:"); + getLang().keySet().forEach(langKey -> { + sb.append(" " + langKey + "=" + getLang().get(langKey) + ","); + }); + } } public static String getOrig(PolyString s) { diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismPropertyValueImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismPropertyValueImpl.java index e73bb5a86ee..2631607de1f 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismPropertyValueImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismPropertyValueImpl.java @@ -589,7 +589,7 @@ public String toHumanReadableString() { StringBuilder sb = new StringBuilder(); sb.append("orig=" + ps.getOrig()); if (ps.getTranslation() != null) { - sb.append(", translation="+ps.getTranslation().getKey()); + sb.append(", translation.key=" + ps.getTranslation().getKey()); } if (ps.getLang() != null) { sb.append("; lang:"); From d9f4f4c8036bf0d706fd2a0e1da19cfab9cce100 Mon Sep 17 00:00:00 2001 From: Pavol Mederly Date: Mon, 30 Mar 2020 12:31:07 +0200 Subject: [PATCH 2/5] Clean up database schema version computation The determination of database schema version from midPoint version is now (almost) definitely implemented, according to description in https://wiki.evolveum.com/display/midPoint/Database+schema+versioning. --- .../sql/schemacheck/SchemaActionComputer.java | 100 ++++++++++++------ 1 file changed, 67 insertions(+), 33 deletions(-) diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/schemacheck/SchemaActionComputer.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/schemacheck/SchemaActionComputer.java index 2b7d9de53f0..3d5a36ad528 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/schemacheck/SchemaActionComputer.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/schemacheck/SchemaActionComputer.java @@ -7,6 +7,16 @@ package com.evolveum.midpoint.repo.sql.schemacheck; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + import com.evolveum.midpoint.common.LocalizationService; import com.evolveum.midpoint.repo.sql.SqlRepositoryConfiguration; import com.evolveum.midpoint.repo.sql.SqlRepositoryConfiguration.MissingSchemaAction; @@ -17,16 +27,6 @@ import com.evolveum.midpoint.util.exception.SystemException; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.tuple.ImmutablePair; -import org.apache.commons.lang3.tuple.Pair; -import org.jetbrains.annotations.NotNull; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; /** * Determines the action that should be done against the database (none, stop, warn, create, upgrade) @@ -36,12 +36,8 @@ * - repository configuration (namely actions for missing/upgradeable/incompatible schemas) * * TODOs (issues to consider) - * 1) database schema version could differ from major midPoint version (e.g. 3.7.2 vs 3.7 - fortunately it's history) - * 2) check if db variant is applicable (e.g. upgrading "plain" mysql using "utf8mb4" variant and vice versa) - * - * @author mederly + * - check if db variant is applicable (e.g. upgrading "plain" mysql using "utf8mb4" variant and vice versa) */ - @Component class SchemaActionComputer { @@ -56,10 +52,29 @@ class SchemaActionComputer { @Autowired private BaseHelper baseHelper; private static final Set> AUTOMATICALLY_UPGRADEABLE = new HashSet<>( - Arrays.asList(new ImmutablePair<>("3.8", "3.9"), + Arrays.asList( + new ImmutablePair<>("3.8", "3.9"), new ImmutablePair<>("3.9", "4.0")) ); + /** + * Exceptions from the general "db version = mP minor version" rule, based on full mP version. + */ + private static final Map DATABASE_VERSION_FROM_MIDPOINT_VERSION; + static { + DATABASE_VERSION_FROM_MIDPOINT_VERSION = new HashMap<>(); + DATABASE_VERSION_FROM_MIDPOINT_VERSION.put("3.7.2", "3.7.2"); + } + + /** + * Exceptions from the general "db version = mP minor version" rule, based on mP minor version. + */ + private static final Map DATABASE_VERSION_FROM_MIDPOINT_MINOR_VERSION; + static { + DATABASE_VERSION_FROM_MIDPOINT_MINOR_VERSION = new HashMap<>(); + DATABASE_VERSION_FROM_MIDPOINT_MINOR_VERSION.put("4.1", "4.0"); + } + enum State { COMPATIBLE, NO_TABLES, AUTOMATICALLY_UPGRADEABLE, MANUALLY_UPGRADEABLE, INCOMPATIBLE } @@ -191,7 +206,12 @@ private String getCurrentAndRequiredVersionInformation(SchemaState state) { private State determineState(SchemaState schemaState) { @NotNull String requiredVersion = getRequiredDatabaseSchemaVersion(); DataStructureCompliance.State dataComplianceState = schemaState.dataStructureCompliance.state; - DeclaredVersion.State versionState = schemaState.declaredVersion.state; + Exception dataComplianceException = schemaState.dataStructureCompliance.validationException; + DeclaredVersion.State declaredVersionState = schemaState.declaredVersion.state; + String declaredVersionNumber = schemaState.declaredVersion.version; + LOGGER.info("Required database schema version: {}, declared version: {} ({}), data structure compliance: {}{}", + requiredVersion, declaredVersionNumber, declaredVersionState, dataComplianceState, + dataComplianceException != null ? " (" + dataComplianceException.getMessage() + ")": ""); if (requiredVersion.equals(schemaState.declaredVersion.version)) { switch (dataComplianceState) { case COMPLIANT: @@ -208,18 +228,18 @@ private State determineState(SchemaState schemaState) { case NO_TABLES: return State.NO_TABLES; case COMPLIANT: - if (versionState == DeclaredVersion.State.METADATA_TABLE_MISSING) { + if (declaredVersionState == DeclaredVersion.State.METADATA_TABLE_MISSING) { LOGGER.warn("Strange: Data structure is compliant but metadata table is missing or inaccessible. Please investigate this."); return State.INCOMPATIBLE; - } else if (versionState == DeclaredVersion.State.VERSION_VALUE_MISSING) { + } else if (declaredVersionState == DeclaredVersion.State.VERSION_VALUE_MISSING) { LOGGER.warn("Data structure is compliant but version information is missing from the global metadata table. Please investigate and fix this."); return State.COMPATIBLE; // let's continue } return determineUpgradeability(schemaState.declaredVersion, requiredVersion); case NOT_COMPLIANT: - if (versionState == DeclaredVersion.State.METADATA_TABLE_MISSING) { + if (declaredVersionState == DeclaredVersion.State.METADATA_TABLE_MISSING) { return State.MANUALLY_UPGRADEABLE; // this is currently true (any version can be upgraded to 3.9) - } else if (versionState == DeclaredVersion.State.VERSION_VALUE_MISSING) { + } else if (declaredVersionState == DeclaredVersion.State.VERSION_VALUE_MISSING) { return State.INCOMPATIBLE; // something strange happened; this does not seem to be an upgrade situation } return determineUpgradeability(schemaState.declaredVersion, requiredVersion); @@ -267,26 +287,35 @@ private MissingSchemaAction getMissingSchemaAction() { } /** - * Currently this is equal to midPoint major version. But, in general, there might be differences, e.g. - * - no database change between major mP versions: e.g. if 5.7 would have the same db as 5.6, then req.schema version for 5.7 would be "5.6" - * - database change between minor versions: e.g. 3.7 and 3.7.1 have version of "3.7", but 3.7.2 has version of "3.7.2" - * (fortunately, at that time we didn't have repository versions!) + * For database schema versioning please see https://wiki.evolveum.com/display/midPoint/Database+schema+versioning. + * + * Normally, database schema version is the same as midPoint minor version (e.g. 3.9). Exceptions are stored in + * DATABASE_VERSION_FROM_MIDPOINT_VERSION and DATABASE_VERSION_FROM_MIDPOINT_MINOR_VERSION tables. + * + * TODO Think out how we will deal with snapshots. Now they are ignored, so e.g. 4.2-SNAPSHOT converted to 4.2. */ @NotNull private String getRequiredDatabaseSchemaVersion() { - // TODO adapt if major mp version != database schema version - //return getMajorMidPointVersion(); - return "4.0"; // Temporary measure, until better mechanism is devised (MID-5884) + String fullMidPointVersion = getMidPointVersion(); + String exceptionFromFullVersion = DATABASE_VERSION_FROM_MIDPOINT_VERSION.get(fullMidPointVersion); + if (exceptionFromFullVersion != null) { + return exceptionFromFullVersion; + } + String minorMidPointVersion = getMinorMidPointVersion(fullMidPointVersion); + String exceptionFromMinorVersion = DATABASE_VERSION_FROM_MIDPOINT_MINOR_VERSION.get(minorMidPointVersion); + if (exceptionFromMinorVersion != null) { + return exceptionFromMinorVersion; + } else { + return minorMidPointVersion; + } } @NotNull - private String getMajorMidPointVersion() { - String version = localizationService - .translate(LocalizableMessageBuilder.buildKey("midPointVersion"), Locale.getDefault()); - String noSnapshot = removeSuffix(version); + private String getMinorMidPointVersion(String fullMidPointVersion) { + String noSnapshot = removeSuffix(fullMidPointVersion); int firstDot = noSnapshot.indexOf('.'); if (firstDot < 0) { - throw new SystemException("Couldn't determine midPoint version from '" + version + "'"); + throw new SystemException("Couldn't determine midPoint version from '" + fullMidPointVersion + "'"); } int secondDot = noSnapshot.indexOf('.', firstDot+1); if (secondDot < 0) { @@ -296,6 +325,11 @@ private String getMajorMidPointVersion() { } } + private String getMidPointVersion() { + return localizationService + .translate(LocalizableMessageBuilder.buildKey("midPointVersion"), Locale.getDefault()); + } + private String removeSuffix(String version) { Matcher matcher = VERSION_SUFFIX_PATTERN.matcher(version); if (matcher.find()) { From f28165ac226d5b87fafb762d35192381008dd1df Mon Sep 17 00:00:00 2001 From: kate Date: Mon, 30 Mar 2020 14:01:42 +0200 Subject: [PATCH 3/5] one more improvement for polyString tracing --- .../evolveum/midpoint/prism/polystring/PolyString.java | 10 ++++++++-- .../midpoint/prism/impl/PrismPropertyValueImpl.java | 10 ++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/polystring/PolyString.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/polystring/PolyString.java index 414545cea0f..70b6c9be6dc 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/polystring/PolyString.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/polystring/PolyString.java @@ -19,6 +19,8 @@ import com.evolveum.midpoint.util.annotation.Experimental; import com.evolveum.prism.xml.ns._public.types_3.PolyStringTranslationType; import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; + +import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang.StringUtils; import java.io.Serializable; @@ -337,11 +339,15 @@ public String debugDump(int indent) { @Override public void shortDump(StringBuilder sb) { - sb.append("orig=" + orig); + if (MapUtils.isNotEmpty(getLang()) || getTranslation() != null && StringUtils.isNotEmpty(getTranslation().getKey())){ + sb.append("orig=" + orig); + } else { + sb.append(orig); + } if (getTranslation() != null) { sb.append("; translation.key=" + getTranslation().getKey()); } - if (getLang() != null) { + if (MapUtils.isNotEmpty(getLang())) { sb.append("; lang:"); getLang().keySet().forEach(langKey -> { sb.append(" " + langKey + "=" + getLang().get(langKey) + ","); diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismPropertyValueImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismPropertyValueImpl.java index 2631607de1f..154cb24c0d7 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismPropertyValueImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/PrismPropertyValueImpl.java @@ -33,6 +33,8 @@ import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType; import com.evolveum.prism.xml.ns._public.types_3.RawType; import com.evolveum.prism.xml.ns._public.types_3.SchemaDefinitionType; + +import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -587,11 +589,15 @@ public String toHumanReadableString() { // (displaying the aux information in user-visible context). But for e.g. deltas we need this information. PolyString ps = (PolyString) this.value; StringBuilder sb = new StringBuilder(); - sb.append("orig=" + ps.getOrig()); + if (MapUtils.isNotEmpty(ps.getLang()) || ps.getTranslation() != null && StringUtils.isNotEmpty(ps.getTranslation().getKey())){ + sb.append("orig=" + ps.getOrig()); + } else { + sb.append(ps.getOrig()); + } if (ps.getTranslation() != null) { sb.append(", translation.key=" + ps.getTranslation().getKey()); } - if (ps.getLang() != null) { + if (MapUtils.isNotEmpty(ps.getLang())) { sb.append("; lang:"); ps.getLang().keySet().forEach(langKey -> { sb.append(" " + langKey + "=" + ps.getLang().get(langKey) + ","); From 05e0760e654f62acf1ae7aa985b2f2979ba4271e Mon Sep 17 00:00:00 2001 From: kate Date: Mon, 30 Mar 2020 14:26:43 +0200 Subject: [PATCH 4/5] fix for ObjectNameColumn translated value --- .../evolveum/midpoint/gui/api/util/WebComponentUtil.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/util/WebComponentUtil.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/util/WebComponentUtil.java index 05484d6e1f8..23969da6f1b 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/util/WebComponentUtil.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/util/WebComponentUtil.java @@ -1116,7 +1116,11 @@ public static String getTranslatedPolyString(PolyString value, LocalizationServi if (localizationService == null){ localizationService = MidPointApplication.get().getLocalizationService(); } - return localizationService.translate(value, getCurrentLocale(), true); + String translatedValue = localizationService.translate(value, getCurrentLocale(), true); + if (StringUtils.isNotEmpty(translatedValue)){ + return translatedValue; + } + return value.getOrig(); } public static String getName(ObjectReferenceType ref, PageBase pageBase, String operation) { From 67e5586538ce9bceabf7570bfcf16e3c2dc596e5 Mon Sep 17 00:00:00 2001 From: Pavol Mederly Date: Mon, 30 Mar 2020 16:26:26 +0200 Subject: [PATCH 5/5] Fix unqualified 'skip' auto-completion result When auto-completion expression returned 'skip' (as unqualified string or QName), the stage was not properly recognized as empty, and the approval process was started. Now the comparison is properly done with QNameUtil.matchUri. This resolves MID-5895. --- .../primary/PrimaryChangeProcessor.java | 6 ++-- .../assignments/TestAssignmentsAdvanced.java | 32 +++++++++++++++++++ .../assignments-advanced/role-skipped.xml | 31 ++++++++++++++++++ 3 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 model/workflow-impl/src/test/resources/assignments-advanced/role-skipped.xml diff --git a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/PrimaryChangeProcessor.java b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/PrimaryChangeProcessor.java index 8336aebc3e8..f041c4f697e 100644 --- a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/PrimaryChangeProcessor.java +++ b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/PrimaryChangeProcessor.java @@ -32,6 +32,7 @@ import com.evolveum.midpoint.task.api.TaskExecutionStatus; import com.evolveum.midpoint.task.api.TaskManager; import com.evolveum.midpoint.util.DebugUtil; +import com.evolveum.midpoint.util.QNameUtil; import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.util.logging.LoggingUtils; import com.evolveum.midpoint.util.logging.Trace; @@ -204,8 +205,9 @@ public boolean isEmpty(PcpStartInstruction instruction, } // second pass: check the conditions for (ApprovalStageDefinitionType stage : stages) { - if (!SchemaConstants.MODEL_APPROVAL_OUTCOME_SKIP.equals( - evaluateAutoCompleteExpression(instruction.getCase(), stage, instruction, stageComputeHelper, ctx, result))) { + String autoCompletionResult = evaluateAutoCompleteExpression(instruction.getCase(), stage, instruction, + stageComputeHelper, ctx, result); + if (!QNameUtil.matchUri(SchemaConstants.MODEL_APPROVAL_OUTCOME_SKIP, autoCompletionResult)) { return false; } } diff --git a/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/assignments/TestAssignmentsAdvanced.java b/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/assignments/TestAssignmentsAdvanced.java index 0d55bba59d4..8c10493ab98 100644 --- a/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/assignments/TestAssignmentsAdvanced.java +++ b/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/assignments/TestAssignmentsAdvanced.java @@ -73,6 +73,9 @@ public class TestAssignmentsAdvanced extends AbstractWfTestPolicy { private static final File ROLE_ROLE27_FILE = new File(TEST_RESOURCE_DIR, "role-role27-modifications-and.xml"); private static final File ROLE_ROLE28_FILE = new File(TEST_RESOURCE_DIR, "role-role28-modifications-or.xml"); private static final File ROLE_ROLE29_FILE = new File(TEST_RESOURCE_DIR, "role-role29-modifications-no-items.xml"); + + private static final TestResource ROLE_SKIPPED_FILE = new TestResource(TEST_RESOURCE_DIR, "role-skipped.xml", "66134203-f023-4986-bb5c-a350941909eb"); + private static final TestResource ROLE_IDEMPOTENT = new TestResource(TEST_RESOURCE_DIR, "role-idempotent.xml", "e2f2d977-887b-4ea1-99d8-a6a030a1a6c0"); private static final TestResource ROLE_WITH_IDEMPOTENT_METAROLE = new TestResource(TEST_RESOURCE_DIR, "role-with-idempotent-metarole.xml", "34855a80-3899-4ecf-bdb3-9fc008c4ff70"); @@ -127,6 +130,7 @@ public void initSystem(Task initTask, OperationResult initResult) throws Excepti roleRole29Oid = repoAddObjectFromFile(ROLE_ROLE29_FILE, initResult).getOid(); repoAdd(ROLE_IDEMPOTENT, initResult); repoAdd(ROLE_WITH_IDEMPOTENT_METAROLE, initResult); + repoAdd(ROLE_SKIPPED_FILE, initResult); orgLeads2122Oid = repoAddObjectFromFile(ORG_LEADS2122_FILE, initResult).getOid(); @@ -1071,6 +1075,34 @@ public void test910AssignRoleWithIdempotentMetarole() throws Exception { deleteCaseTree(rootCaseOid, result); } + /** + * MID-5895 + */ + @Test + public void test920AssignRoleWithComputedSkip() throws Exception { + given(); + login(userAdministrator); + Task task = getTestTask(); + OperationResult result = task.getResult(); + + when(); + unassignAllRoles(userJackOid); + + ObjectDelta delta = prismContext.deltaFor(UserType.class) + .item(UserType.F_ASSIGNMENT) + .add(new AssignmentType(prismContext).targetRef(ROLE_SKIPPED_FILE.oid, RoleType.COMPLEX_TYPE)) + .asObjectDeltaCast(userJackOid); + + executeChanges(delta, null, task, result); + + then(); + String ref = result.findAsynchronousOperationReference(); + assertNull("Present async operation reference", ref); + + assertUser(userJackOid, "after") + .assertAssignments(1); + } + private void executeAssignRoles123ToJack(boolean immediate, boolean approve1, boolean approve2, boolean approve3a, boolean approve3b, boolean securityDeputy) throws Exception { Task task = getTestTask(); diff --git a/model/workflow-impl/src/test/resources/assignments-advanced/role-skipped.xml b/model/workflow-impl/src/test/resources/assignments-advanced/role-skipped.xml new file mode 100644 index 00000000000..058d13d4b36 --- /dev/null +++ b/model/workflow-impl/src/test/resources/assignments-advanced/role-skipped.xml @@ -0,0 +1,31 @@ + + + + skipped + + + + + + + + + + + + + + + + + + + +