Skip to content

Commit

Permalink
Issue #167: curl step
Browse files Browse the repository at this point in the history
  • Loading branch information
marcghaly committed Jul 4, 2022
1 parent a6462fc commit cad2ac8
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 4 deletions.
162 changes: 160 additions & 2 deletions classes/local/step/connector_curl.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,176 @@
*/
class connector_curl extends connector_step {

/** @var int[] number of input flows (min, max). */
protected $inputflows = [0, 0];

/** @var int[] number of output connectors (min, max). */
protected $inputconnectors = [0, 1];

/** @var bool whether or not this step type (potentially) contains a side effect or not */
protected $hassideeffect = true;

/** @var int Time after which curl request is aborted */
protected $timeout = 60;

/**
* Return the definition of the fields available in this form.
*
* @return array
*/
protected static function form_define_fields(): array {
return [
'curl' => ['type' => PARAM_URL],
'destination' => ['type' => PARAM_PATH],
'headers' => ['type' => PARAM_RAW],
'method' => ['type' => PARAM_TEXT],
'rawpostdata' => ['type' => PARAM_RAW],
'sideeffects' => ['type' => PARAM_RAW],
'timeout' => ['type' => PARAM_INT],
];
}

/**
* Allows each step type to determine a list of optional/required form
* inputs for their configuration
*
* It's recommended you prefix the additional config related fields to avoid
* conflicts with any existing fields.
*
* @param \MoodleQuickForm &$mform
*/
public function form_add_custom_inputs(\MoodleQuickForm &$mform) {
$mform->addElement('text', 'config_curl', get_string('connector_curl:curl', 'tool_dataflows'),
['cols' => 50, 'rows' => 7]);
$mform->addElement('text', 'config_destination', get_string('connector_curl:destination', 'tool_dataflows'));
$mform->addHelpButton('config_destination', 'connector_curl:destination', 'tool_dataflows');
$mform->addElement('textarea', 'config_headers', get_string('connector_curl:headers', 'tool_dataflows'),
['cols' => 50, 'rows' => 7]);
$mform->addElement('select', 'config_method', get_string('connector_curl:method', 'tool_dataflows'),
['get' => 'GET', 'post' => 'POST', 'put' => 'PUT']);
$mform->addElement('textarea' , 'config_rawpostdata', get_string('connector_curl:rawpostdata', 'tool_dataflows'),
['cols' => 50, 'rows' => 7]);
$mform->addElement('checkbox', 'config_sideeffects', get_string('connector_curl:sideeffects', 'tool_dataflows'),
get_string('yes'));
$mform->hideIf('config_rawpostdata', 'config_method', 'eq', 'get');
$mform->disabledIf('config_rawpostdata', 'config_method', 'eq', 'get');
$mform->addElement('text', 'config_timeout', get_string('connector_curl:timeout', 'tool_dataflows'));
$mform->addHelpButton('config_timeout', 'connector_curl:timeout', 'tool_dataflows');
}

/**
* Validate the configuration settings.
*
* @param object $config
* @return true|\lang_string[] true if valid, an array of errors otherwise
*/
public function validate_config($config) {
$errors = [];
if (empty($config->curl)) {
$errors['config_curl'] = get_string('config_field_missing', 'tool_dataflows', 'curl', true);
}
if (empty($config->rawpostdata) && ($config->method === 'put' || $config->method === 'post')) {
$errors['config_rawpostdata'] = get_string('config_field_missing', 'tool_dataflows', 'rawpostdata', true);
}
return empty($errors) ? true : $errors;
}

/**
* Executes the step
*
* TODO: This will perform a curl call
* Performs a curl call according to given parameters.
*
* @return bool Returns true if successful, false otherwise.
*/
public function execute(): bool {
// TODO: Implement.
global $CFG;
// Get variables.
$config = $this->enginestep->stepdef->config;
$isdryrun = $this->enginestep->engine->isdryrun;
$method = $config->method;
$postdata = null;
$result = null;

if (!empty($config->timeout)) {
$this->timeout = (int) $config->timeout;
}

$options = ['CURLOPT_TIMEOUT' => $this->timeout];

if ($method == 'post') {
$options['CURLOPT_POST'] = 1;
}

if ($method == 'put') {
$options['CURLOPT_PUT'] = 1;
}

$curl = new \curl();

if (!empty($config->sideeffects)) {
$this->hassideeffect = true;
} else {
$this->hassideeffect = false;
}

// Provided a header is specified add header to request.
if (!empty($config->headers)) {
$headers = $config->headers;
if (!is_array($headers)) {
$headers = json_decode($headers, true);
}
$curl->setHeader($headers);
}

if (!empty($config->rawpostdata)) {
$postdata = json_decode($config->rawpostdata, true);
}

// Perform call or download/call.
if (!empty($config->destination) && !$isdryrun) {
// Download the response.
if ($config->destination[0] === '/') {
$config->destination = substr($config->destination, 1);
}
if (isset($postdata)) {
$options['CURLOPT_POSTFIELDS'] = $postdata;
}
$file = fopen($CFG->dirroot . '/' . $config->destination, 'w');
$result = $curl->$method($config->curl, $postdata, $options);
fwrite($file, $result);
fclose($file);
} else {
if (!$this->hassideeffect && !$isdryrun) {
// Simple call without download.
if ($config->method != 'get') {
$result = $curl->$method($config->curl, $postdata, $options);
$this->hassideeffect = true;
} else {
$result = $curl->get($config->curl, [], $options);
}
}
}

$info = $curl->get_info();
// Stores response to be reusable by other steps.
$response = $curl->getResponse();
$httpcode = $info['http_code'] ?? null;
$connecttime = $info['connect_time'] ?? null;
$totaltime = $info['total_time'] ?? null;
$sizeupload = $info['size_upload'] ?? null;
$errno = $curl->get_errno();

if (($httpcode >= 400 || empty($response) || $errno == 28) && !$isdryrun) {
$codestatustext = null;
if (is_string($result)) {
$result = json_decode($result, true)['error'] ?? $result;
$codestatustext = !is_array($result) ? $httpcode . '/' . $result : $httpcode . '/' . reset($result);
}
throw new \moodle_exception($codestatustext);
}

$this->enginestep->stepdef->set_var('result', $result);

return true;
}
}
12 changes: 12 additions & 0 deletions lang/en/tool_dataflows.php
Original file line number Diff line number Diff line change
Expand Up @@ -231,3 +231,15 @@
$string['trigger_cron:crontab_desc'] = 'The schedule is edited as five values: minute, hour, day, month and day of month, in that order. The values are in crontab format.';
$string['trigger_cron:strftime_datetime'] = '%d %b %Y, %H:%M';
$string['trigger_cron:next_run_time'] = 'Next run time: {$a}';

// Connector cURL.
$string['connector_curl:curl'] = 'Client URL';
$string['connector_curl:destination'] = 'File / Response destination';
$string['connector_curl:destination_help'] = 'Should this field be left blank,
response will be stored in step definition to be reused subsequently by other steps.';
$string['connector_curl:headers'] = 'Headers';
$string['connector_curl:method'] = 'HTTP request method';
$string['connector_curl:rawpostdata'] = 'Raw post data';
$string['connector_curl:sideeffects'] = 'Does this request have side effects?';
$string['connector_curl:timeout'] = 'Timeout';
$string['connector_curl:timeout_help'] = 'Time after which curl request will abort, default is 60 seconds.';
4 changes: 2 additions & 2 deletions version.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@

defined('MOODLE_INTERNAL') || die();

$plugin->version = 2022062900;
$plugin->release = 2022062900;
$plugin->version = 2022063000;
$plugin->release = 2022063000;

$plugin->requires = 2017051500; // Our lowest supported Moodle (3.3.0).

Expand Down

0 comments on commit cad2ac8

Please sign in to comment.