diff --git a/.editorconfig b/.editorconfig index 1cb779bf7..bb87ae7b5 100644 --- a/.editorconfig +++ b/.editorconfig @@ -13,3 +13,4 @@ charset = utf-8 [*.{php,json}] indent_size = 4 +trim_trailing_whitespace=true diff --git a/classes/log/store.php b/classes/log/store.php index 48eb0c7eb..0e301c1aa 100755 --- a/classes/log/store.php +++ b/classes/log/store.php @@ -109,7 +109,7 @@ public function process_events(array $events) { 'app_url' => $CFG->wwwroot, ], 'loader' => [ - 'loader' => 'lrs', + 'loader' => 'moodle_curl_lrs', 'lrs_endpoint' => $this->get_config('endpoint', ''), 'lrs_username' => $this->get_config('username', ''), 'lrs_password' => $this->get_config('password', ''), diff --git a/src/loader/lrs.php b/src/loader/lrs.php index af36eafc7..781be00b5 100644 --- a/src/loader/lrs.php +++ b/src/loader/lrs.php @@ -19,82 +19,35 @@ use src\loader\utils as utils; -function correct_endpoint($endpoint) { - $endswithstatements = substr($endpoint, -11) === "/statements"; - if ($endswithstatements) { - return substr($endpoint, 0, -11); - } - return rtrim($endpoint, '/'); -} - -function send_http_statements(array $config, array $statements) { - $endpoint = $config['lrs_endpoint']; - $username = $config['lrs_username']; - $password = $config['lrs_password']; - - $url = correct_endpoint($endpoint).'/statements'; - $auth = base64_encode($username.':'.$password); - $postdata = json_encode($statements); - - $request = curl_init(); - curl_setopt($request, CURLOPT_URL, $url); - curl_setopt($request, CURLOPT_POSTFIELDS, $postdata); - curl_setopt($request, CURLOPT_RETURNTRANSFER, true); - curl_setopt($request, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($request, CURLOPT_HTTPHEADER, [ - 'Authorization: Basic '.$auth, - 'X-Experience-API-Version: 1.0.0', - 'Content-Type: application/json', - ]); - - $responsetext = curl_exec($request); - $responsecode = curl_getinfo($request, CURLINFO_RESPONSE_CODE); - curl_close($request); - - if ($responsecode !== 200) { - throw new \Exception($responsetext); - } -} - -function load_transormed_events_to_lrs(array $config, array $transformedevents) { - try { - $statements = array_reduce($transformedevents, function ($result, $transformedevent) { - $eventstatements = $transformedevent['statements']; - return array_merge($result, $eventstatements); - }, []); - send_http_statements($config, $statements); - $loadedevents = utils\construct_loaded_events($transformedevents, true); - return $loadedevents; - } catch (\Exception $e) { - $logerror = $config['log_error']; - $logerror("Failed load for event id #" . $eventobj->id . ": " . $e->getMessage()); - $logerror($e->getTraceAsString()); - $loadedevents = utils\construct_loaded_events($transformedevents, false); - return $loadedevents; - } -} - -function get_event_batches(array $config, array $transformedevents) { - $maxbatchsize = $config['lrs_max_batch_size']; - if (!empty($maxbatchsize) && $maxbatchsize < count($transformedevents)) { - return array_chunk($transformedevents, $maxbatchsize); - } - return [$transformedevents]; -} - function load(array $config, array $events) { - // Attempts to load events that were transformed successfully in batches. - $successfultransformevents = utils\filter_transformed_events($events, true); - $batches = get_event_batches($config, $successfultransformevents); - $loadedevents = array_reduce($batches, function ($result, $batch) use ($config) { - $loadedbatchevents = load_transormed_events_to_lrs($config, $batch); - return array_merge($result, $loadedbatchevents); - }, []); - - // Flags events that weren't transformed successfully as events that didn't load. - $failedtransformevents = utils\filter_transformed_events($events, false); - $nonloadedevents = utils\construct_loaded_events($failedtransformevents, false); - - // Returns loaded and non-loaded events to avoid re-processing. - return array_merge($loadedevents, $nonloadedevents); + $sendhttpstatements = function (array $config, array $statements) { + $endpoint = $config['lrs_endpoint']; + $username = $config['lrs_username']; + $password = $config['lrs_password']; + $proxyendpoint = $config['lrs_proxy_endpoint']; + + $url = utils\correct_endpoint($endpoint).'/statements'; + $auth = base64_encode($username.':'.$password); + $postdata = json_encode($statements); + + $request = curl_init(); + curl_setopt($request, CURLOPT_URL, $url); + curl_setopt($request, CURLOPT_POSTFIELDS, $postdata); + curl_setopt($request, CURLOPT_RETURNTRANSFER, true); + curl_setopt($request, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($request, CURLOPT_HTTPHEADER, [ + 'Authorization: Basic '.$auth, + 'X-Experience-API-Version: 1.0.0', + 'Content-Type: application/json', + ]); + + $responsetext = curl_exec($request); + $responsecode = curl_getinfo($request, CURLINFO_RESPONSE_CODE); + curl_close($request); + + if ($responsecode !== 200) { + throw new \Exception($responsetext); + } + }; + return utils\load_in_batches($config, $events, $sendhttpstatements); } \ No newline at end of file diff --git a/src/loader/moodle_curl_lrs.php b/src/loader/moodle_curl_lrs.php new file mode 100644 index 000000000..f78cfc068 --- /dev/null +++ b/src/loader/moodle_curl_lrs.php @@ -0,0 +1,54 @@ +. + +namespace src\loader\moodle_curl_lrs; +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +if (!isset($CFG)) { + $CFG = (object) [ 'libdir' => 'utils' ]; +} +require_once($CFG->libdir . '/filelib.php'); + +use src\loader\utils as utils; + +function load(array $config, array $events) { + $sendhttpstatements = function (array $config, array $statements) { + $endpoint = $config['lrs_endpoint']; + $username = $config['lrs_username']; + $password = $config['lrs_password']; + $proxyendpoint = $config['lrs_proxy_endpoint']; + + $url = utils\correct_endpoint($endpoint).'/statements'; + $auth = base64_encode($username.':'.$password); + $postdata = json_encode($statements); + + $request = new \curl(); + $responsetext = $request->post($url, $postdata, [ + 'CURLOPT_HTTPHEADER' => [ + 'Authorization: Basic '.$auth, + 'X-Experience-API-Version: 1.0.0', + 'Content-Type: application/json', + ], + ]); + $responsecode = $request->info['http_code']; + + if ($responsecode !== 200) { + throw new \Exception($responsetext); + } + }; + return utils\load_in_batches($config, $events, $sendhttpstatements); +} \ No newline at end of file diff --git a/src/loader/utils/correct_endpoint.php b/src/loader/utils/correct_endpoint.php new file mode 100644 index 000000000..d88fb5277 --- /dev/null +++ b/src/loader/utils/correct_endpoint.php @@ -0,0 +1,27 @@ +. + +namespace src\loader\utils; + +defined('MOODLE_INTERNAL') || die(); + +function correct_endpoint($endpoint) { + $endswithstatements = substr($endpoint, -11) === "/statements"; + if ($endswithstatements) { + return substr($endpoint, 0, -11); + } + return rtrim($endpoint, '/'); +} \ No newline at end of file diff --git a/src/loader/utils/filelib.php b/src/loader/utils/filelib.php new file mode 100644 index 000000000..1a679cb51 --- /dev/null +++ b/src/loader/utils/filelib.php @@ -0,0 +1,23 @@ +. + +namespace src\loader\utils; + +defined('MOODLE_INTERNAL') || die(); + +class curl { + // This is just a dummy file to avoid failures in CI. +} diff --git a/src/loader/utils/get_event_batches.php b/src/loader/utils/get_event_batches.php new file mode 100644 index 000000000..b8643fb87 --- /dev/null +++ b/src/loader/utils/get_event_batches.php @@ -0,0 +1,27 @@ +. + +namespace src\loader\utils; + +defined('MOODLE_INTERNAL') || die(); + +function get_event_batches(array $config, array $transformedevents) { + $maxbatchsize = $config['lrs_max_batch_size']; + if (!empty($maxbatchsize) && $maxbatchsize < count($transformedevents)) { + return array_chunk($transformedevents, $maxbatchsize); + } + return [$transformedevents]; +} \ No newline at end of file diff --git a/src/loader/utils/load_batch.php b/src/loader/utils/load_batch.php new file mode 100644 index 000000000..3ef49289d --- /dev/null +++ b/src/loader/utils/load_batch.php @@ -0,0 +1,37 @@ +. + +namespace src\loader\utils; + +defined('MOODLE_INTERNAL') || die(); + +function load_batch(array $config, array $transformedevents, callable $loader) { + try { + $statements = array_reduce($transformedevents, function ($result, $transformedevent) { + $eventstatements = $transformedevent['statements']; + return array_merge($result, $eventstatements); + }, []); + $loader($config, $statements); + $loadedevents = construct_loaded_events($transformedevents, true); + return $loadedevents; + } catch (\Exception $e) { + $logerror = $config['log_error']; + $logerror("Failed load for event id #" . $eventobj->id . ": " . $e->getMessage()); + $logerror($e->getTraceAsString()); + $loadedevents = construct_loaded_events($transformedevents, false); + return $loadedevents; + } +} diff --git a/src/loader/utils/load_in_batches.php b/src/loader/utils/load_in_batches.php new file mode 100644 index 000000000..b2ebb4c5d --- /dev/null +++ b/src/loader/utils/load_in_batches.php @@ -0,0 +1,36 @@ +. + +namespace src\loader\utils; + +defined('MOODLE_INTERNAL') || die(); + +function load_in_batches(array $config, array $events, callable $loader) { + // Attempts to load events that were transformed successfully in batches. + $successfultransformevents = filter_transformed_events($events, true); + $batches = get_event_batches($config, $successfultransformevents); + $loadedevents = array_reduce($batches, function ($result, $batch) use ($config, $loader) { + $loadedbatchevents = load_batch($config, $batch, $loader); + return array_merge($result, $loadedbatchevents); + }, []); + + // Flags events that weren't transformed successfully as events that didn't load. + $failedtransformevents = filter_transformed_events($events, false); + $nonloadedevents = construct_loaded_events($failedtransformevents, false); + + // Returns loaded and non-loaded events to avoid re-processing. + return array_merge($loadedevents, $nonloadedevents); +}