diff --git a/repo/repo-sqale/sql/pgnew-repo.sql b/repo/repo-sqale/sql/pgnew-repo.sql index 1e11f1f6dff..8523c04f5f8 100644 --- a/repo/repo-sqale/sql/pgnew-repo.sql +++ b/repo/repo-sqale/sql/pgnew-repo.sql @@ -1198,19 +1198,17 @@ CREATE INDEX m_form_name_orig_idx ON m_form (name_orig); ALTER TABLE m_form ADD CONSTRAINT m_form_name_norm_key UNIQUE (name_norm); -- endregion --- region Assignment/Inducement tables +-- region Assignment/Inducement table -- Represents AssignmentType, see https://wiki.evolveum.com/display/midPoint/Assignment -- and also https://wiki.evolveum.com/display/midPoint/Assignment+vs+Inducement --- TODO: if we never need mix of inducements and assignments then let's have two separate tables --- consult with Rado/Katka/Palo --- TODO: partitioning, not by object type, it's not even... hash-something? +-- TODO: partitioning, probably not by object type, it's not even... hash-something? -- select assignmentowner, count(*) From m_assignment group by assignmentowner; --1 45 (inducements) --0 48756229 --- abstract common structure for m_assignment and m_inducement -CREATE TABLE m_assignment_type ( - -- owner_oid + containerType from m_container, final specification in sub-tables - -- new column may avoid join to object for some queries +CREATE TABLE m_assignment ( + owner_oid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, + -- this is different from other containers, this is not generated, app must provide it + containerType ContainerType NOT NULL CHECK (containerType IN ('ASSIGNMENT', 'INDUCEMENT')), owner_type ObjectType NOT NULL, lifecycleState TEXT/*VARCHAR(255)*/, orderValue INTEGER, @@ -1255,21 +1253,9 @@ CREATE TABLE m_assignment_type ( modifyChannel_id INTEGER, modifyTimestamp TIMESTAMPTZ, - -- create PRIMARY KEY (owner_oid, cid) on sub-tables - -- no need to index owner_oid, it's part of the PK index - - CHECK (FALSE) NO INHERIT -) - INHERITS(m_container); - -CREATE TABLE m_assignment ( - owner_oid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, - containerType ContainerType GENERATED ALWAYS AS ('ASSIGNMENT') STORED, - PRIMARY KEY (owner_oid, cid) - -- no need to index owner_oid, it's part of the PK index ) - INHERITS(m_assignment_type); + INHERITS(m_container); CREATE INDEX m_assignment_policySituation_idx ON m_assignment USING GIN(policysituations gin__int_ops); CREATE INDEX m_assignment_ext_idx ON m_assignment USING gin (ext); @@ -1313,25 +1299,6 @@ ALTER TABLE m_assignment_ref_modify_approver ADD CONSTRAINT m_assignment_ref_mod FOREIGN KEY (owner_oid, assignment_cid) REFERENCES m_assignment (owner_oid, cid); -- TODO index targetOid, relation_id? - -CREATE TABLE m_inducement ( - owner_oid UUID NOT NULL REFERENCES m_object_oid(oid) ON DELETE CASCADE, - containerType ContainerType GENERATED ALWAYS AS ('INDUCEMENT') STORED, - - PRIMARY KEY (owner_oid, cid) - -- no need to index owner_oid, it's part of the PK index -) - INHERITS(m_assignment_type); - -CREATE INDEX m_inducement_ext_idx ON m_inducement USING gin (ext); -CREATE INDEX m_inducement_validFrom_idx ON m_inducement (validFrom); -CREATE INDEX m_inducement_validTo_idx ON m_inducement (validTo); -CREATE INDEX m_inducement_targetRef_targetOid_idx ON m_inducement (targetRef_targetOid); -CREATE INDEX m_inducement_tenantRef_targetOid_idx ON m_inducement (tenantRef_targetOid); -CREATE INDEX m_inducement_orgRef_targetOid_idx ON m_inducement (orgRef_targetOid); -CREATE INDEX m_inducement_resourceRef_targetOid_idx ON m_inducement (resourceRef_targetOid); - --- TODO other tables like for assignments needed? policy situations, refs? -- endregion -- region Other object containers diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ObjectTemplateSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ObjectTemplateSqlTransformer.java index d63494567a3..b4f23b44494 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ObjectTemplateSqlTransformer.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/ObjectTemplateSqlTransformer.java @@ -18,12 +18,9 @@ public class ObjectTemplateSqlTransformer extends ObjectSqlTransformer { - private final QObjectTemplateMapping mapping; - public ObjectTemplateSqlTransformer( SqlTransformerSupport transformerSupport, QObjectTemplateMapping mapping) { super(transformerSupport, mapping); - this.mapping = mapping; } @Override diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignmentMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignmentMapping.java index 35efcf9ae88..100b9b37c3a 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignmentMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/assignment/QAssignmentMapping.java @@ -8,9 +8,11 @@ import static com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType.*; +import com.evolveum.midpoint.repo.sqale.qmodel.common.MContainerType; import com.evolveum.midpoint.repo.sqale.qmodel.common.QContainerMapping; import com.evolveum.midpoint.repo.sqale.qmodel.object.MObject; import com.evolveum.midpoint.repo.sqale.qmodel.ref.QReferenceMapping; +import com.evolveum.midpoint.repo.sqale.qmodel.role.MAbstractRole; import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType; @@ -19,6 +21,10 @@ /** * Mapping between {@link QAssignment} and {@link AssignmentType}. + * There are separate instances for assignments and inducements and the instance also knows + * the {@link MAssignment#containerType} it should set. + * Only the instance for assignments is registered for queries as there is no way to distinguish + * between assignments and inducements when searching containers in the Query API anyway. * * @param type of the owner row */ @@ -27,13 +33,22 @@ public class QAssignmentMapping public static final String DEFAULT_ALIAS_NAME = "a"; - public static final QAssignmentMapping INSTANCE = new QAssignmentMapping<>(); + /** Default assignment mapping instance, for queries it works for inducements too. */ + public static final QAssignmentMapping INSTANCE = + new QAssignmentMapping<>(MContainerType.ASSIGNMENT); + + /** Inducement mapping instance, this must be used for inserting inducements. */ + public static final QAssignmentMapping INSTANCE_INDUCEMENT = + new QAssignmentMapping<>(MContainerType.INDUCEMENT); + + private final MContainerType containerType; // We can't declare Class>.class, so we cheat a bit. @SuppressWarnings({ "unchecked", "rawtypes" }) - private QAssignmentMapping() { + private QAssignmentMapping(MContainerType containerType) { super(QAssignment.TABLE_NAME, DEFAULT_ALIAS_NAME, AssignmentType.class, (Class) QAssignment.class); + this.containerType = containerType; // TODO OWNER_TYPE is new thing and can help avoid join to concrete object table // But this will likely require special treatment/heuristic. @@ -121,7 +136,9 @@ public AssignmentSqlTransformer createTransformer(SqlTransformerSupport tran @Override public MAssignment newRowObject() { - return new MAssignment(); + MAssignment row = new MAssignment(); + row.containerType = this.containerType; + return row; } @Override diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/FocusSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/FocusSqlTransformer.java index 266b2e10789..b37ea75b6f5 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/FocusSqlTransformer.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/focus/FocusSqlTransformer.java @@ -17,12 +17,14 @@ public class FocusSqlTransformer, R extends MFocus> extends ObjectSqlTransformer { - private final QFocusMapping mapping; - public FocusSqlTransformer( SqlTransformerSupport transformerSupport, QFocusMapping mapping) { super(transformerSupport, mapping); - this.mapping = mapping; + } + + @Override + protected QFocusMapping mapping() { + return (QFocusMapping) super.mapping(); } @SuppressWarnings("DuplicatedCode") // activation code duplicated with assignment @@ -79,8 +81,8 @@ public void storeRelatedEntities( super.storeRelatedEntities(row, schemaObject, jdbcSession); storeRefs(row, schemaObject.getLinkRef(), - mapping.projectionReferenceMapping(), jdbcSession); + mapping().projectionReferenceMapping(), jdbcSession); storeRefs(row, schemaObject.getPersonaRef(), - mapping.personaReferenceMapping(), jdbcSession); + mapping().personaReferenceMapping(), jdbcSession); } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/AbstractRoleSqlTransformer.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/AbstractRoleSqlTransformer.java index 18ed811255f..6b7c83f22d0 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/AbstractRoleSqlTransformer.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/AbstractRoleSqlTransformer.java @@ -6,12 +6,16 @@ */ package com.evolveum.midpoint.repo.sqale.qmodel.role; +import java.util.List; + import org.jetbrains.annotations.NotNull; +import com.evolveum.midpoint.repo.sqale.qmodel.assignment.AssignmentSqlTransformer; import com.evolveum.midpoint.repo.sqale.qmodel.focus.FocusSqlTransformer; import com.evolveum.midpoint.repo.sqlbase.JdbcSession; import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; import com.evolveum.midpoint.xml.ns._public.common.common_3.AbstractRoleType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AutoassignSpecificationType; public class AbstractRoleSqlTransformer< @@ -38,4 +42,23 @@ public AbstractRoleSqlTransformer( row.riskLevel = abstractRole.getRiskLevel(); return row; } + + @Override + public void storeRelatedEntities( + @NotNull R row, @NotNull S schemaObject, @NotNull JdbcSession jdbcSession) { + super.storeRelatedEntities(row, schemaObject, jdbcSession); + + List inducement = schemaObject.getInducement(); + if (!inducement.isEmpty()) { + AssignmentSqlTransformer transformer = + mapping().inducementMapping().createTransformer(transformerSupport); + inducement.forEach(assignment -> + transformer.insert(assignment, row, jdbcSession)); + } + } + + @Override + protected QAbstractRoleMapping mapping() { + return (QAbstractRoleMapping) super.mapping(); + } } diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/MAbstractRole.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/MAbstractRole.java index 3eed513b4aa..402cfa6f2ee 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/MAbstractRole.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/MAbstractRole.java @@ -13,7 +13,7 @@ */ public class MAbstractRole extends MFocus { - public Boolean autoAssignEnabled; + public Boolean autoAssignEnabled; // autoassign/enabled public String displayNameOrig; public String displayNameNorm; public String identifier; diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/QAbstractRoleMapping.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/QAbstractRoleMapping.java index d85e5248d56..b0abeb426ef 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/QAbstractRoleMapping.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/qmodel/role/QAbstractRoleMapping.java @@ -10,6 +10,7 @@ import org.jetbrains.annotations.NotNull; +import com.evolveum.midpoint.repo.sqale.qmodel.assignment.QAssignmentMapping; import com.evolveum.midpoint.repo.sqale.qmodel.focus.QFocusMapping; import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport; import com.evolveum.midpoint.xml.ns._public.common.common_3.AbstractRoleType; @@ -48,6 +49,16 @@ protected QAbstractRoleMapping( addItemMapping(F_IDENTIFIER, stringMapper(q -> q.identifier)); addItemMapping(F_REQUESTABLE, booleanMapper(q -> q.requestable)); addItemMapping(F_RISK_LEVEL, stringMapper(q -> q.riskLevel)); + + addContainerTableMapping(F_INDUCEMENT, inducementMapping(), + joinOn((o, a) -> o.oid.eq(a.ownerOid))); + } + + /** Fixes rigid parametric types of static mapping instance to this instance. */ + @NotNull + public QAssignmentMapping inducementMapping() { + //noinspection unchecked + return (QAssignmentMapping) QAssignmentMapping.INSTANCE_INDUCEMENT; } @Override diff --git a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/RootUpdateContext.java b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/RootUpdateContext.java index 2e801abf9b8..e5bf09f6a98 100644 --- a/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/RootUpdateContext.java +++ b/repo/repo-sqale/src/main/java/com/evolveum/midpoint/repo/sqale/update/RootUpdateContext.java @@ -110,13 +110,13 @@ public QueryTableMapping mapping() { private void processModification(ItemDelta modification) throws RepositoryException, SchemaException { cidGenerator.processModification(modification); - resolveContainerIdsForDeletedValues(modification); + resolveContainerIdsForValuesToDelete(modification); modification.applyTo(getPrismObject()); new DelegatingItemDeltaProcessor(this).process(modification); } - private void resolveContainerIdsForDeletedValues(ItemDelta modification) { + private void resolveContainerIdsForValuesToDelete(ItemDelta modification) { if (!modification.isDelete()) { return; } diff --git a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java index 0a306423b86..5c4fcca0a18 100644 --- a/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java +++ b/repo/repo-sqale/src/test/java/com/evolveum/midpoint/repo/sqale/func/SqaleRepoAddDeleteObjectTest.java @@ -24,9 +24,7 @@ import com.evolveum.midpoint.repo.sqale.SqaleRepoBaseTest; import com.evolveum.midpoint.repo.sqale.qmodel.accesscert.MAccessCertificationDefinition; import com.evolveum.midpoint.repo.sqale.qmodel.accesscert.QAccessCertificationDefinition; -import com.evolveum.midpoint.repo.sqale.qmodel.assignment.MAssignmentReference; -import com.evolveum.midpoint.repo.sqale.qmodel.assignment.QAssignmentReference; -import com.evolveum.midpoint.repo.sqale.qmodel.assignment.QAssignmentReferenceMapping; +import com.evolveum.midpoint.repo.sqale.qmodel.assignment.*; import com.evolveum.midpoint.repo.sqale.qmodel.common.MContainer; import com.evolveum.midpoint.repo.sqale.qmodel.common.MContainerType; import com.evolveum.midpoint.repo.sqale.qmodel.common.QContainer; @@ -48,6 +46,8 @@ import com.evolveum.midpoint.repo.sqale.qmodel.report.QReportData; import com.evolveum.midpoint.repo.sqale.qmodel.resource.MResource; import com.evolveum.midpoint.repo.sqale.qmodel.resource.QResource; +import com.evolveum.midpoint.repo.sqale.qmodel.role.MArchetype; +import com.evolveum.midpoint.repo.sqale.qmodel.role.QArchetype; import com.evolveum.midpoint.repo.sqale.qmodel.shadow.MShadow; import com.evolveum.midpoint.repo.sqale.qmodel.shadow.QShadow; import com.evolveum.midpoint.repo.sqale.qmodel.system.QSystemConfiguration; @@ -75,14 +75,15 @@ public void test100AddNamedUserWithoutOidWorksOk() .name(userName); when("adding it to the repository"); - repositoryService.addObject(userType.asPrismObject(), null, result); + String returnedOid = repositoryService.addObject(userType.asPrismObject(), null, result); then("operation is successful and user row for it is created"); assertThatOperationResult(result).isSuccess(); + assertThat(returnedOid).isEqualTo(userType.getOid()); QUser u = aliasFor(QUser.class); MUser row = selectOne(u, u.nameOrig.eq(userName)); - assertThat(row.oid).isNotNull(); + assertThat(row.oid).isEqualTo(UUID.fromString(returnedOid)); assertThat(row.nameNorm).isNotNull(); // normalized name is stored assertThat(row.version).isEqualTo(1); // initial version is set // read-only column with value generated/stored in the database @@ -402,7 +403,7 @@ public void test800SystemConfigurationBasicObjectAttributes() throws Exception { .creatorRef(creatorRefOid.toString(), UserType.COMPLEX_TYPE, relation1) .createChannel("create-channel") .createTimestamp(MiscUtil.asXMLGregorianCalendar(1L)) - .modifierRef(modifierRefOid.toString(), ServiceType.COMPLEX_TYPE, relation2) + .modifierRef(modifierRefOid.toString(), UserType.COMPLEX_TYPE, relation2) .modifyChannel("modify-channel") .modifyTimestamp(MiscUtil.asXMLGregorianCalendar(2L))); @@ -433,7 +434,7 @@ public void test800SystemConfigurationBasicObjectAttributes() throws Exception { assertCachedUri(row.createChannelId, "create-channel"); assertThat(row.createTimestamp).isEqualTo(Instant.ofEpochMilli(1)); assertThat(row.modifierRefTargetOid).isEqualTo(modifierRefOid); - assertThat(row.modifierRefTargetType).isEqualTo(MObjectType.SERVICE); + assertThat(row.modifierRefTargetType).isEqualTo(MObjectType.USER); assertCachedUri(row.modifierRefRelationId, relation2); assertCachedUri(row.modifyChannelId, "modify-channel"); assertThat(row.modifyTimestamp).isEqualTo(Instant.ofEpochMilli(2)); @@ -536,6 +537,114 @@ public void test802ContainerOperationExecution() throws Exception { // this time we didn't test assigned CID or CID SEQ value on owner (see test801) } + @Test + public void test803ContainerAssignment() throws Exception { + OperationResult result = createOperationResult(); + + given("object with assignments"); + String objectName = "sc" + getTestNumber(); + UUID orgRefOid = UUID.randomUUID(); + UUID targetRefOid = UUID.randomUUID(); + UUID tenantRefOid = UUID.randomUUID(); + UUID resourceRefOid = UUID.randomUUID(); + UUID creatorRefOid = UUID.randomUUID(); + UUID modifierRefOid = UUID.randomUUID(); + QName relation1 = QName.valueOf("{https://random.org/ns}random-rel-1"); + QName relation2 = QName.valueOf("{https://random.org/ns}random-rel-2"); + SystemConfigurationType object = new SystemConfigurationType(prismContext) + .name(objectName) + .assignment(new AssignmentType(prismContext) + .lifecycleState("lifecycle-state") + .order(47) + .orgRef(orgRefOid.toString(), OrgType.COMPLEX_TYPE, relation1) + .targetRef(targetRefOid.toString(), RoleType.COMPLEX_TYPE, relation2) + .tenantRef(tenantRefOid.toString(), OrgType.COMPLEX_TYPE, relation2) + // TODO extId, extOid, ext? + .policySituation("policy-situation-1") + .policySituation("policy-situation-2") + .construction(new ConstructionType() + .resourceRef(resourceRefOid.toString(), + ResourceType.COMPLEX_TYPE, relation1)) + .activation(new ActivationType() + .administrativeStatus(ActivationStatusType.ENABLED) + .effectiveStatus(ActivationStatusType.DISABLED) + .enableTimestamp(MiscUtil.asXMLGregorianCalendar(3L)) + .disableTimestamp(MiscUtil.asXMLGregorianCalendar(4L)) + .disableReason("disable-reason") + .validityStatus(TimeIntervalStatusType.IN) + .validFrom(MiscUtil.asXMLGregorianCalendar(5L)) + .validTo(MiscUtil.asXMLGregorianCalendar(6L)) + .validityChangeTimestamp(MiscUtil.asXMLGregorianCalendar(7L)) + .archiveTimestamp(MiscUtil.asXMLGregorianCalendar(8L))) + .metadata(new MetadataType() + // multi-value approver refs are tested elsewhere + .creatorRef(creatorRefOid.toString(), UserType.COMPLEX_TYPE, relation1) + .createChannel("create-channel") + .createTimestamp(MiscUtil.asXMLGregorianCalendar(1L)) + .modifierRef(modifierRefOid.toString(), UserType.COMPLEX_TYPE, relation2) + .modifyChannel("modify-channel") + .modifyTimestamp(MiscUtil.asXMLGregorianCalendar(2L)))) + // one more just to see it stores multiple assignments + .assignment(new AssignmentType().order(1)); + + when("adding it to the repository"); + repositoryService.addObject(object.asPrismObject(), null, result); + + then("it is stored and rows to child tables are inserted"); + assertThatOperationResult(result).isSuccess(); + + QAssignment a = QAssignmentMapping.INSTANCE.defaultAlias(); + List aRows = select(a, a.ownerOid.eq(UUID.fromString(object.getOid()))); + assertThat(aRows).hasSize(2) + .allMatch(ar -> ar.orderValue != null); + + MAssignment row = aRows.stream() + .filter(ar -> ar.orderValue == 47) + .findFirst().orElseThrow(); + + assertThat(row.lifecycleState).isEqualTo("lifecycle-state"); + assertThat(row.orderValue).isEqualTo(47); + assertThat(row.orgRefTargetOid).isEqualTo(orgRefOid); + assertThat(row.orgRefTargetType).isEqualTo(MObjectType.ORG); + assertCachedUri(row.orgRefRelationId, relation1); + assertThat(row.targetRefTargetOid).isEqualTo(targetRefOid); + assertThat(row.targetRefTargetType).isEqualTo(MObjectType.ROLE); + assertCachedUri(row.targetRefRelationId, relation2); + assertThat(row.tenantRefTargetOid).isEqualTo(tenantRefOid); + assertThat(row.tenantRefTargetType).isEqualTo(MObjectType.ORG); + assertCachedUri(row.tenantRefRelationId, relation2); + // complex DB columns + // TODO EXT + assertThat(resolveCachedUriIds(row.policySituations)) + .containsExactlyInAnyOrder("policy-situation-1", "policy-situation-2"); + // construction + assertThat(row.resourceRefTargetOid).isEqualTo(resourceRefOid); + assertThat(row.resourceRefTargetType).isEqualTo(MObjectType.RESOURCE); + assertCachedUri(row.resourceRefRelationId, relation1); + // activation + assertThat(row.administrativeStatus).isEqualTo(ActivationStatusType.ENABLED); + assertThat(row.effectiveStatus).isEqualTo(ActivationStatusType.DISABLED); + assertThat(row.enableTimestamp).isEqualTo(Instant.ofEpochMilli(3)); + assertThat(row.disableTimestamp).isEqualTo(Instant.ofEpochMilli(4)); + assertThat(row.disableReason).isEqualTo("disable-reason"); + assertThat(row.validityStatus).isEqualTo(TimeIntervalStatusType.IN); + assertThat(row.validFrom).isEqualTo(Instant.ofEpochMilli(5)); + assertThat(row.validTo).isEqualTo(Instant.ofEpochMilli(6)); + assertThat(row.validityChangeTimestamp).isEqualTo(Instant.ofEpochMilli(7)); + assertThat(row.archiveTimestamp).isEqualTo(Instant.ofEpochMilli(8)); + // metadata + assertThat(row.creatorRefTargetOid).isEqualTo(creatorRefOid); + assertThat(row.creatorRefTargetType).isEqualTo(MObjectType.USER); + assertCachedUri(row.creatorRefRelationId, relation1); + assertCachedUri(row.createChannelId, "create-channel"); + assertThat(row.createTimestamp).isEqualTo(Instant.ofEpochMilli(1)); + assertThat(row.modifierRefTargetOid).isEqualTo(modifierRefOid); + assertThat(row.modifierRefTargetType).isEqualTo(MObjectType.USER); + assertCachedUri(row.modifierRefRelationId, relation2); + assertCachedUri(row.modifyChannelId, "modify-channel"); + assertThat(row.modifyTimestamp).isEqualTo(Instant.ofEpochMilli(2)); + } + @Test public void test808LookupTable() throws Exception { OperationResult result = createOperationResult(); @@ -587,10 +696,6 @@ public void test808LookupTable() throws Exception { assertThat(containerRow.key).isEqualTo("row3"); } - // TODO test for object's related entities? - // - trigger - // - operation execution - @Test public void test810ResourceAndItsBusinessApproverReferences() throws Exception { OperationResult result = createOperationResult(); @@ -795,7 +900,7 @@ public void test818Shadow() throws Exception { assertThat(row.synchronizationTimestamp).isEqualTo(Instant.ofEpochMilli(2)); } - // this covers mapping of attributes in FocusSqlTransformer + GenericObject + // This covers mapping of attributes in FocusSqlTransformer + GenericObject. @Test public void test820GenericObject() throws Exception { OperationResult result = createOperationResult(); @@ -869,6 +974,54 @@ public void test820GenericObject() throws Exception { assertCachedUri(row.genericObjectTypeId, "some-custom-object-type-uri"); } + // This covers mapping of attributes in AbstractRole + Archetype + inducement mapping. + // There is no focus on FocusSqlTransformer that is covered above. + @Test + public void test821ArchetypeAndInducement() throws Exception { + OperationResult result = createOperationResult(); + + given("archetype object"); + String objectName = "arch" + getTestNumber(); + ArchetypeType archetype = new ArchetypeType(prismContext) + .name(objectName) + .autoassign(new AutoassignSpecificationType().enabled(true)) + .displayName("display-name") + .identifier("identifier") + .requestable(false) + .riskLevel("extremely-high") + // we don't need all attributes here, this is tested in TODO + .inducement(new AssignmentType() + .order(2) + .targetRef(UUID.randomUUID().toString(), RoleType.COMPLEX_TYPE)) + .inducement(new AssignmentType() + .order(3) + .targetRef(UUID.randomUUID().toString(), RoleType.COMPLEX_TYPE)); + // this is no additional attribute specific for archetype + + when("adding it to the repository"); + repositoryService.addObject(archetype.asPrismObject(), null, result); + + then("it is stored and relevant attributes are in columns"); + assertThatOperationResult(result).isSuccess(); + + UUID archetypeOid = UUID.fromString(archetype.getOid()); + MArchetype row = selectObjectByOid(QArchetype.class, archetypeOid); + // all attributes from MAbstractRole + assertThat(row.autoAssignEnabled).isTrue(); + assertThat(row.displayNameOrig).isEqualTo("display-name"); + assertThat(row.displayNameNorm).isEqualTo("displayname"); + assertThat(row.identifier).isEqualTo("identifier"); + assertThat(row.requestable).isFalse(); + assertThat(row.riskLevel).isEqualTo("extremely-high"); + + QAssignment a = QAssignmentMapping.INSTANCE.defaultAlias(); + assertThat(select(a, a.ownerOid.eq(archetypeOid))).hasSize(2) + .anyMatch(ar -> ar.orderValue.equals(2)) + .anyMatch(ar -> ar.orderValue.equals(3)) + .allMatch(ar -> ar.targetRefTargetOid != null + && ar.targetRefTargetType == MObjectType.ROLE); + } + // TODO test for focus' related entities? @Test