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) { 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..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,7 +339,20 @@ public String debugDump(int indent) { @Override public void shortDump(StringBuilder sb) { - sb.append(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 (MapUtils.isNotEmpty(getLang())) { + 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..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="+ps.getTranslation().getKey()); + 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) + ","); 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 + + + + + + + + + + + + + + + + + + + + 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()) {