You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 3 Next »

The design here is to not load all groups from a loader, but be able to selectively load groups as needed, and located anywhere in the registry.

An example of this use case is someone can mark an arbitrary group as "load people from a course", while also selecting if that means students, instructors, or both. 

selectiveLoadingOfCourses

Sample data

Add the groups, some memberships, and the attributes.

import edu.internet2.middleware.grouper.*;
import edu.internet2.middleware.grouper.attr.*;
import edu.internet2.middleware.grouper.attr.assign.*;
import edu.internet2.middleware.grouper.attr.finder.*;

GrouperSession grouperSession = GrouperSession.startRootSession();

AttributeDef attributeDef = new AttributeDefSave(grouperSession).assignName("attr:courseDef").assignCreateParentStemsIfNotExist(true).assignCreateParentStemsIfNotExist(true).assignToGroup(true).assignAttributeDefType(AttributeDefType.attr).assignMultiAssignable(false).assignMultiValued(false).assignValueType(AttributeDefValueType.marker).save();
AttributeDef attributeValueDef = new AttributeDefSave(grouperSession).assignName("attr:courseValueDef").assignCreateParentStemsIfNotExist(true).assignToGroupAssn(true).assignAttributeDefType(AttributeDefType.attr).assignMultiAssignable(false).assignMultiValued(false).assignValueType(AttributeDefValueType.string).save();

AttributeDef attributeValueMarkerDef = new AttributeDefSave(grouperSession).assignName("attr:courseValueMarkerDef").assignCreateParentStemsIfNotExist(true).assignToGroupAssn(true).assignAttributeDefType(AttributeDefType.attr).assignMultiAssignable(false).assignMultiValued(false).assignValueType(AttributeDefValueType.marker).save();

attributeDef.getAttributeDefActionDelegate().configureActionList("assign");
attributeValueDef.getAttributeDefActionDelegate().configureActionList("assign");
attributeValueMarkerDef.getAttributeDefActionDelegate().configureActionList("assign");

course = new AttributeDefNameSave(grouperSession, attributeDef).assignName("attr:course").assignCreateParentStemsIfNotExist(true).save();

attributeValueDef.getAttributeDefScopeDelegate().assignOwnerNameEquals("attr:course");
attributeValueMarkerDef.getAttributeDefScopeDelegate().assignOwnerNameEquals("attr:course");

courseId = new AttributeDefNameSave(grouperSession, attributeValueDef).assignName("attr:courseId").assignCreateParentStemsIfNotExist(true).save();
includeStudents = new AttributeDefNameSave(grouperSession, attributeValueMarkerDef).assignName("attr:includeStudents").assignCreateParentStemsIfNotExist(true).save();
includeInstructors = new AttributeDefNameSave(grouperSession, attributeValueMarkerDef).assignName("attr:includeInstructors").assignCreateParentStemsIfNotExist(true).save();

Group math101 = new GroupSave(grouperSession).assignName("org:artsAndSciences:courses:math101").assignCreateParentStemsIfNotExist(true).save();

attributeAssignSave = new AttributeAssignSave(grouperSession).assignAttributeAssignType(AttributeAssignType.group).assignAttributeDefName(course).assignOwnerGroup(math101);

attributeAssignOnAssignSave = new AttributeAssignSave(grouperSession).assignAttributeAssignType(AttributeAssignType.group_asgn).assignAttributeDefName(courseId).addValue("math101");

attributeAssignSave.addAttributeAssignOnThisAssignment(attributeAssignOnAssignSave);

attributeAssignOnAssignSave = new AttributeAssignSave(grouperSession).assignAttributeAssignType(AttributeAssignType.group_asgn).assignAttributeDefName(includeStudents);

attributeAssignSave.addAttributeAssignOnThisAssignment(attributeAssignOnAssignSave);

attributeAssignSave.save();

Group clinical101 = new GroupSave(grouperSession).assignName("org:nursing:classes:clinical101").assignCreateParentStemsIfNotExist(true).save();

attributeAssignSave = new AttributeAssignSave(grouperSession).assignAttributeAssignType(AttributeAssignType.group).assignAttributeDefName(course).assignOwnerGroup(clinical101);

attributeAssignOnAssignSave = new AttributeAssignSave(grouperSession).assignAttributeAssignType(AttributeAssignType.group_asgn).assignAttributeDefName(courseId).addValue("clinical101");

attributeAssignSave.addAttributeAssignOnThisAssignment(attributeAssignOnAssignSave);

attributeAssignOnAssignSave = new AttributeAssignSave(grouperSession).assignAttributeAssignType(AttributeAssignType.group_asgn).assignAttributeDefName(includeStudents);

attributeAssignSave.addAttributeAssignOnThisAssignment(attributeAssignOnAssignSave);

attributeAssignOnAssignSave = new AttributeAssignSave(grouperSession).assignAttributeAssignType(AttributeAssignType.group_asgn).assignAttributeDefName(includeInstructors);

attributeAssignSave.addAttributeAssignOnThisAssignment(attributeAssignOnAssignSave);

attributeAssignSave.save();


Attribute assignments on groups

Note, if you delegate who has access to the course attribute def names, then others can control their own groups

Two groups with attributes:




Control views

The attribute assignment queries are complicated and have performance implications.  So to simplify things, we will make a view, and sync that into a table, so when we do the loader query, it is simpler and performs better.

View for which groups are courses

Note, this is for mysql but will either work or will work with minor edits in other databases

create view course_config_v as
select distinct gaagv.group_id, gaagv.group_name,
(select gaaagv.value_string from grouper_aval_asn_asn_group_v gaaagv where gaaagv.attribute_def_name_name2 = 'attr:courseId' and gaaagv.group_name = gaagv.group_name and gaaagv.enabled2 = 'T' limit 1)
  as course_id,
case when exists 
(select 1 from grouper_attr_asn_asn_group_v gaaagv where gaaagv.attribute_def_name_name2 = 'attr:includeStudents' and gaaagv.group_name = gaagv.group_name and gaaagv.enabled2 = 'T')
then 'T'
else 'F'
end as include_students,
case when exists 
(select 1 from grouper_attr_asn_asn_group_v gaaagv where gaaagv.attribute_def_name_name2 = 'attr:includeInstructors' and gaaagv.group_name = gaagv.group_name and gaaagv.enabled2 = 'T')
then 'T'
else 'F'
end as include_instructors
from  grouper_attr_asn_group_v gaagv
where gaagv.attribute_def_name_name = 'attr:course'
and gaagv.enabled = 'T';


Control table

To simplify things and improve performance, we will sync this view into a table

Convert to table:

create table course_config as select * from course_config_v;
ALTER TABLE course_config MODIFY group_id varchar(40);
ALTER TABLE course_config ADD PRIMARY KEY(group_id);
CREATE INDEX course_config_name_idx ON course_config (group_name);

Sync the view to table. 

grouper.client.properties

grouperClient.syncTable.courseConfig.databaseFrom = grouper
grouperClient.syncTable.courseConfig.tableFrom = course_config_v
grouperClient.syncTable.courseConfig.databaseTo = grouper
grouperClient.syncTable.courseConfig.tableTo = course_config
grouperClient.syncTable.courseConfig.columns = *
grouperClient.syncTable.courseConfig.primaryKeyColumns = group_id

Lets schedule this hourly at 50 minutes passed the hour

grouper-loader.properties

otherJob.courseConfigFull.class = edu.internet2.middleware.grouper.app.tableSync.TableSyncOtherJob
otherJob.courseConfigFull.quartzCron = 0 50 * * * ?
otherJob.courseConfigFull.grouperClientTableSyncConfigKey = courseConfig
otherJob.courseConfigFull.syncType = fullSyncFull

Run the job and make sure it is ok



Membership view

This view has the loader results to load the groups with attributes.  Note, since all the groups used are loaded basis groups (not in this example, but yes in practice) with immediate memberships, we can join to the grouper_memberships table.  This is done for performance reasons.  If there were effective members, then the grouper_memberships_lw_v would need to be used, and the grouper_groups table wouldnt be needed since that is in the view.

Note also the field_id of the "members" field from grouper_fields is hard-coded here.  If you use grouper_memberships_lw_v you dont need to do that.

create view employee_intersect_mships_v as
select eio.group_name as group_name,
       gm.subject_id as subject_id,
       gm.subject_source as subject_source_id
from employee_intersect_owners eio,
     grouper_members gm
     where
       (eio.has_job_class = 'T' or eio.has_job_code = 'T' or eio.has_org_unit_code = 'T')
       and  (eio.has_job_class = 'F' 
            or exists (select 1 from grouper_memberships gms, employee_intersect_unions eiu, grouper_groups gg
                       where eiu.group_id_owner = eio.group_id 
                         and gm.id = gms.member_id and gms.field_id = '58796e2b91fc4756a894af4d0d8d46b5' and gms.enabled = 'T'
                         and eiu.union_attribute = 'jobClass' and gg.name = eiu.group_name_union and gms.owner_id = gg.id ))
        and (eio.has_job_code = 'F' 
            or exists (select 1 from grouper_memberships gms, employee_intersect_unions eiu, grouper_groups gg
                       where eiu.group_id_owner = eio.group_id 
                         and gm.id = gms.member_id and gms.field_id = '58796e2b91fc4756a894af4d0d8d46b5' and gms.enabled = 'T'
                         and eiu.union_attribute = 'jobCode' and gg.name = eiu.group_name_union and gms.owner_id = gg.id ))
       and (eio.has_org_unit_code = 'F' 
            or exists (select 1 from grouper_memberships gms, employee_intersect_unions eiu, grouper_groups gg
                       where eiu.group_id_owner = eio.group_id 
                         and gm.id = gms.member_id and gms.field_id = '58796e2b91fc4756a894af4d0d8d46b5' and gms.enabled = 'T'
                         and eiu.union_attribute = 'orgUnitCode' and gg.name = eiu.group_name_union and gms.owner_id = gg.id ))



Loader job



See memberships on any groups with employee intersect attributes

Real time updates

This is not implemented yet but you could easily have a change log consumer that looks for membership changes to any of the union groups, and looks for attribute changes for the applicable attributes, and runs a full or incremental loader job based on events.  Ask the Grouper team for an example if this is a requirement.  Otherwise the above will run periodically (this example is hourly)




  • No labels