Skip to content

Commit

Permalink
Merge branch 'wip-MSFTMPP-629-m30' into MOODLE_30_RC
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesmcq committed May 11, 2017
2 parents cbfb146 + 9a6dcaa commit 503bc6f
Show file tree
Hide file tree
Showing 2 changed files with 241 additions and 22 deletions.
49 changes: 40 additions & 9 deletions classes/feature/sds/task/sync.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,19 +78,39 @@ public static function runsync($apiclient) {
try {
$schooldata = $apiclient->get_school($school);
} catch (\Exception $e) {
mtrace('... Skipped SDS school '.$school.' because we received an error from the API');
static::mtrace('... Skipped SDS school '.$school.' because we received an error from the API');
continue;
}
$coursecat = static::get_or_create_school_coursecategory($schooldata['objectId'], $schooldata['displayName']);
mtrace('... Processing '.$schooldata['displayName']);
static::mtrace('... Processing '.$schooldata['displayName']);
$schoolnumber = $schooldata[$apiclient::PREFIX.'_SchoolNumber'];
$sections = $apiclient->get_school_sections($schoolnumber);
foreach ($sections['value'] as $section) {
mtrace('...... Processing '.$section['displayName']);
static::mtrace('...... Processing '.$section['displayName']);
// Create the course.
$fullname = $section[$apiclient::PREFIX.'_CourseName'];
$fullname .= ' '.$section[$apiclient::PREFIX.'_SectionNumber'];
$course = static::get_or_create_section_course($section['objectId'], $section['displayName'], $fullname, $coursecat->id);
$coursecontext = \context_course::instance($course->id);

// Associate the section group with the course.
$groupobjectparams = [
'type' => 'group',
'subtype' => 'course',
'objectid' => $section['objectId'],
'moodleid' => $course->id,
];
$groupobjectrec = $DB->get_record('local_o365_objects', $groupobjectparams);
if (empty($groupobjectrec)) {
$now = time();
$groupobjectrec = $groupobjectparams;
$groupobjectrec['o365name'] = $section['displayName'];
$groupobjectrec['timecreated'] = $now;
$groupobjectrec['timemodified'] = $now;
$groupobjectrec['id'] = $DB->insert_record('local_o365_objects', (object)$groupobjectrec);
}

// Sync membership.
if (!empty($enrolenabled) || !empty($profilesyncenabled)) {
$members = $apiclient->get_section_members($section['objectId']);
foreach ($members['value'] as $member) {
Expand Down Expand Up @@ -118,9 +138,9 @@ public static function runsync($apiclient) {
];
$ra = $DB->get_record('role_assignments', $roleparams, 'id');
if (empty($ra)) {
mtrace('......... Assigning role for user '.$objectrec->moodleid.' in course '.$course->id);
static::mtrace('......... Assigning role for user '.$objectrec->moodleid.' in course '.$course->id);
role_assign($role->id, $objectrec->moodleid, $coursecontext->id);
mtrace('......... Enrolling user '.$objectrec->moodleid.' into course '.$course->id);
static::mtrace('......... Enrolling user '.$objectrec->moodleid.' into course '.$course->id);
enrol_try_internal_enrol($course->id, $objectrec->moodleid);
}
}
Expand All @@ -130,7 +150,7 @@ public static function runsync($apiclient) {
}

if (!empty($profilesyncenabled)) {
mtrace('...... Running profile sync');
static::mtrace('...... Running profile sync');
$skiptoken = get_config('local_o365', 'sdsprofilesync_skiptoken');
$students = $apiclient->get_school_users($schooldata['objectId'], $skiptoken);
foreach ($students['value'] as $student) {
Expand All @@ -148,17 +168,28 @@ public static function runsync($apiclient) {
$query = [];
parse_str($nextlink['query'], $query);
if (isset($query['$skiptoken'])) {
mtrace('...... Skip token saved for next run');
static::mtrace('...... Skip token saved for next run');
set_config('sdsprofilesync_skiptoken', $query['$skiptoken'], 'local_o365');
}
}
} else {
mtrace('...... Full run complete.');
static::mtrace('...... Full run complete.');
}
}
}
}

/**
* Run mtrace if not in a unit test.
*
* @param string $str The trace string.
*/
public static function mtrace($str) {
if (!PHPUNIT_TEST) {
mtrace($str);
}
}

/**
* Update a user's information based on configured field map.
*
Expand All @@ -171,7 +202,7 @@ public static function update_profiledata($muser, $sdsdata, $fieldmaps) {
global $DB, $CFG;
require_once($CFG->dirroot.'/user/profile/lib.php');

mtrace("......... Updating user {$muser->id}");
static::mtrace("......... Updating user {$muser->id}");
foreach ($fieldmaps as $fieldmap) {
$fieldmap = explode('/', $fieldmap);
list($remotefield, $localfield) = $fieldmap;
Expand Down
214 changes: 201 additions & 13 deletions tests/sdssync_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,18 @@ protected function get_mock_token() {
return $token;
}

/**
* Get a mock apiclient
*
* @return \local_o365\rest\sds A mock SDS api client.
*/
protected function get_mock_apiclient() {
$mocktoken = $this->get_mock_token();
$mockhttpclient = new \local_o365\tests\mockhttpclient();
$mockclient = new \local_o365\rest\sds($mocktoken, $mockhttpclient);
return $mockclient;
}

/**
* Test get_or_create_school_coursecategory.
*/
Expand Down Expand Up @@ -173,23 +185,199 @@ public function test_get_or_section_course() {
$this->assertEquals($courserec->id, $courserec2->id);
}

/**
* Test the main runsync function.
*/
public function test_runsync() {
global $DB;
$mocktoken = $this->get_mock_token();
$mockhttpclient = new \local_o365\tests\mockhttpclient();
$schoolresponse = $this->get_school_response();
$schoolsectionsresponse = $this->get_school_sections_response();
$responses = [
json_encode($schoolresponse),
json_encode($schoolsectionsresponse),
];
$mockhttpclient->set_responses($responses);
$mockclient = new \local_o365\rest\sds($mocktoken, $mockhttpclient);

set_config('sdsschools', 'fd1bdc2b-a59c-444e-af75-e250c546410e', 'local_o365');
set_config('sdsprofilesyncenabled', '0', 'local_o365');
set_config('sdsfieldmap', serialize([]), 'local_o365');
set_config('sdsenrolmentenabled', '0', 'local_o365');

\local_o365\feature\sds\task\sync::runsync($mockclient);

// Verify course is created.
$sectiondata = $schoolsectionsresponse['value'][0];
$expectedcoursename = $sectiondata[$mockclient::PREFIX.'_CourseName'];
$expectedcoursename .= ' '.$sectiondata[$mockclient::PREFIX.'_SectionNumber'];
$expectedcourseshortname = $sectiondata['displayName'];
$params = [
'fullname' => $expectedcoursename,
'shortname' => $expectedcourseshortname
];
$course = $DB->get_record('course', $params);
$this->assertNotEmpty($course);

// Verify objects records.
$params = [
'type' => 'sdsschool',
'subtype' => 'coursecat',
];
$sdscoursecats = $DB->get_records('local_o365_objects', $params);
$this->assertNotEmpty($sdscoursecats, 'No sdsschool/coursecat object record for the school found.');
$this->assertEquals(1, count($sdscoursecats), 'Only 1 sdsschool/coursecat object record for the school should exist.');
$objectrec = reset($sdscoursecats);
$this->assertEquals($schoolresponse['displayName'], $objectrec->o365name, 'o365name incorrect');
$this->assertEquals($schoolresponse['objectId'], $objectrec->objectid, 'Object ID should match');

$params = [
'type' => 'sdssection',
'subtype' => 'course',
];
$sdssections = $DB->get_records('local_o365_objects', $params);
$this->assertNotEmpty($sdssections, 'No sdssection/course object record for the section found.');
$this->assertEquals(1, count($sdssections), 'Only 1 sdssection/course object record for the school should exist.');
$objectrec = reset($sdssections);
$this->assertEquals($expectedcourseshortname, $objectrec->o365name, 'o365name incorrect');
$this->assertEquals($sectiondata['objectId'], $objectrec->objectid, 'Object ID should match');

$params = [
'type' => 'group',
'subtype' => 'course',
];
$groups = $DB->get_records('local_o365_objects', $params);
$this->assertNotEmpty($groups, 'No group/course object record for the section found.');
$this->assertEquals(1, count($groups), 'Only 1 group/course object record for the school should exist.');
$objectrec = reset($groups);
$this->assertEquals($expectedcourseshortname, $objectrec->o365name, 'o365name incorrect');
$this->assertEquals($sectiondata['objectId'], $objectrec->objectid, 'Object ID should match');

// Run sync again to make sure we don't create duplicates.
$mockhttpclient->set_responses($responses);
\local_o365\feature\sds\task\sync::runsync($mockclient);

$params = [
'fullname' => $expectedcoursename,
'shortname' => $expectedcourseshortname
];
$course = $DB->get_record('course', $params);
$this->assertNotEmpty($course);

// Verify objects records.
$params = [
'type' => 'sdsschool',
'subtype' => 'coursecat',
];
$sdscoursecats = $DB->get_records('local_o365_objects', $params);
$this->assertNotEmpty($sdscoursecats, 'No sdsschool/coursecat object record for the school found.');
$this->assertEquals(1, count($sdscoursecats), 'Only 1 sdsschool/coursecat object record for the school should exist.');
$objectrec = reset($sdscoursecats);
$this->assertEquals($schoolresponse['displayName'], $objectrec->o365name, 'o365name incorrect');
$this->assertEquals($schoolresponse['objectId'], $objectrec->objectid, 'Object ID should match');

$params = [
'type' => 'sdssection',
'subtype' => 'course',
];
$sdssections = $DB->get_records('local_o365_objects', $params);
$this->assertNotEmpty($sdssections, 'No sdssection/course object record for the section found.');
$this->assertEquals(1, count($sdssections), 'Only 1 sdssection/course object record for the school should exist.');
$objectrec = reset($sdssections);
$this->assertEquals($expectedcourseshortname, $objectrec->o365name, 'o365name incorrect');
$this->assertEquals($sectiondata['objectId'], $objectrec->objectid, 'Object ID should match');

$params = [
'type' => 'group',
'subtype' => 'course',
];
$groups = $DB->get_records('local_o365_objects', $params);
$this->assertNotEmpty($groups, 'No group/course object record for the section found.');
$this->assertEquals(1, count($groups), 'Only 1 group/course object record for the school should exist.');
$objectrec = reset($groups);
$this->assertEquals($expectedcourseshortname, $objectrec->o365name, 'o365name incorrect');
$this->assertEquals($sectiondata['objectId'], $objectrec->objectid, 'Object ID should match');
}

/**
* Get a response for a school query.
*
* @param string $objectid The school objectid.
* @param string $name The school name.
* @param string $schoolnumber The school number.
* @return string API response JSON.
* @return array Array of response data.
*/
protected function get_school_response() {
$response = [
"odata.metadata" => 'https://graph.windows.net/contososd.com/$metadata#directoryObjects/Microsoft.DirectoryServices.AdministrativeUnit/@Element',
"odata.type" => "Microsoft.DirectoryServices.AdministrativeUnit",
"objectType" => "AdministrativeUnit",
"objectId" => "fd1bdc2b-a59c-444e-af75-e250c546410e",
"deletionTimestamp" => null,
"displayName" => "Palo Alto High School",
"description" => null,
\local_o365\rest\sds::PREFIX."_SchoolPrincipalEmail" => "[email protected]",
\local_o365\rest\sds::PREFIX."_SchoolPrincipalName" => "John Doe",
\local_o365\rest\sds::PREFIX."_HighestGrade" => "12",
\local_o365\rest\sds::PREFIX."_LowestGrade" => "9",
\local_o365\rest\sds::PREFIX."_SchoolNumber" => "425",
\local_o365\rest\sds::PREFIX."_SyncSource_SchoolId" => "425",
\local_o365\rest\sds::PREFIX."_SyncSource" => "SIS",
\local_o365\rest\sds::PREFIX."_Phone" => "(916) 555-1200",
\local_o365\rest\sds::PREFIX."_Zip" => "94003",
\local_o365\rest\sds::PREFIX."_State" => "CA",
\local_o365\rest\sds::PREFIX."_City" => "Palo Alto",
\local_o365\rest\sds::PREFIX."_Address" => "123 Fake St",
\local_o365\rest\sds::PREFIX."_AnchorId" => "School_425",
\local_o365\rest\sds::PREFIX."_ObjectType" => "School"
];
return $response;
}

/**
* Get a mock response for a school sections query.
*
* @return array Array of response data.
*/
protected function get_school_response($objectid, $name, $schoolnumber) {
$prefix = \local_o365\rest\sds::PREFIX;
$data = [
'objectType' => 'AdministrativeUnit',
'objectId' => $objectid,
'displayName' => $name,
'description' => '',
$prefix.'_SchoolNumber' => $schoolnumber,
protected function get_school_sections_response() {
$response = [
'odata.metadata' => 'https://graph.windows.net/95b43ae0-0554-4cc5-8c22-fe219dc31156/$metadata#directoryObjects/Microsoft.DirectoryServices.Group',
'value' => [
[
"odata.type" => "Microsoft.DirectoryServices.Group",
"objectType" => "Group",
"objectId" => "d016567a-3d56-4162-aa16-1d938dbd7b8e",
"deletionTimestamp" => null,
"description" => null,
"dirSyncEnabled" => null,
"displayName" => "425_JZ100 Section 20",
"lastDirSyncTime" => null,
"mail" => "[email protected]",
"mailNickname" => "Section_1140",
"mailEnabled" => true,
"onPremisesSecurityIdentifier" => null,
"provisioningErrors" => [],
"proxyAddresses" => [
"SMTP:[email protected]"
],
"securityEnabled" => true,
\local_o365\rest\sds::PREFIX."_Period" => "2",
\local_o365\rest\sds::PREFIX."_CourseNumber" => "JZ100",
\local_o365\rest\sds::PREFIX."_CourseDescription" => "JZ100 - JAZZ ENSEMBLE 1",
\local_o365\rest\sds::PREFIX."_CourseName" => "JAZZ ENSEMBLE 1",
\local_o365\rest\sds::PREFIX."_SyncSource_CourseId" => "15057",
\local_o365\rest\sds::PREFIX."_TermEndDate" => "12/21/2015",
\local_o365\rest\sds::PREFIX."_TermStartDate" => "8/30/2015",
\local_o365\rest\sds::PREFIX."_TermName" => "2015 Term 1",
\local_o365\rest\sds::PREFIX."_SyncSource_TermId" => "2015",
\local_o365\rest\sds::PREFIX."_SectionNumber" => "425_JZ100_20",
\local_o365\rest\sds::PREFIX."_SectionName" => "425_JZ100 Section 20",
\local_o365\rest\sds::PREFIX."_SyncSource_SectionId" => "1140",
\local_o365\rest\sds::PREFIX."_SyncSource_SchoolId" => "425",
\local_o365\rest\sds::PREFIX."_SyncSource" => "SIS",
\local_o365\rest\sds::PREFIX."_AnchorId" => "Section_1140",
\local_o365\rest\sds::PREFIX."_ObjectType" => "Section",
]
]
];
return json_encode($data);
return $response;
}
}

0 comments on commit 503bc6f

Please sign in to comment.