Skip to content

Commit

Permalink
Merge pull request #22722 from colemanw/restExplorer
Browse files Browse the repository at this point in the history
APIv4 Explorer - Add REST syntax
  • Loading branch information
colemanw authored Feb 8, 2022
2 parents 35caa82 + 5087250 commit 7b61c1d
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 1 deletion.
3 changes: 3 additions & 0 deletions CRM/Api4/Page/Api4Explorer.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class CRM_Api4_Page_Api4Explorer extends CRM_Core_Page {
public function run() {
$apiDoc = new ReflectionFunction('civicrm_api4');
$groupOptions = civicrm_api4('Group', 'getFields', ['loadOptions' => TRUE, 'select' => ['options', 'name'], 'where' => [['name', 'IN', ['visibility', 'group_type']]]]);
$extensions = \CRM_Extension_System::singleton()->getMapper();

$vars = [
'operators' => CoreUtil::getOperators(),
Expand All @@ -30,6 +31,8 @@ public function run() {
'docs' => \Civi\Api4\Utils\ReflectionUtils::parseDocBlock($apiDoc->getDocComment()),
'functions' => self::getSqlFunctions(),
'groupOptions' => array_column((array) $groupOptions, 'options', 'name'),
'authxEnabled' => $extensions->isActiveModule('authx'),
'restUrl' => rtrim(CRM_Utils_System::url('civicrm/ajax/api4/CRMAPI4ENTITY/CRMAPI4ACTION', NULL, TRUE, NULL, FALSE), '/'),
];
Civi::resources()
->addVars('api4', $vars)
Expand Down
20 changes: 20 additions & 0 deletions ang/api4Explorer/Explorer.html
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,26 @@ <h4 ng-bind-html="helpContent.description"></h4>
</ul>
</div>
<div class="panel-body">
<div class="alert alert-danger" ng-if="selectedTab.code === 'rest' && !$ctrl.authxEnabled">
<p>
{{:: ts('To enable REST authentication, the AuthX extension must be installed.') }}
<a target="_blank" ng-href="{{ crmUrl('civicrm/admin/extensions') }}">
<i class="crm-i fa-gear"> {{:: ts('Manage Extensions') }}</i>
</a>
</p>
</div>
<div class="alert alert-warning" ng-if="selectedTab.code === 'rest' && $ctrl.authxEnabled">
<p>
<a target="_blank" ng-href="{{ crmUrl('civicrm/admin/setting/authx', {reset: 1}) }}">
<i class="crm-i fa-gear"> {{:: ts('Configure REST Authentication') }}</i>
</a>
</p>
<p>
<a target="_blank" href="https://docs.civicrm.org/dev/en/latest/api/v4/rest/">
<i class="crm-i fa-external-link"> {{:: ts('REST Documentation') }}</i>
</a>
</p>
</div>
<div ng-repeat="style in code[selectedTab.code]">
<label>{{:: style.label }}</label>
<div><pre class="prettyprint" ng-bind-html="style.code"></pre></div>
Expand Down
72 changes: 71 additions & 1 deletion ang/api4Explorer/Explorer.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
params = $scope.params = {};
$scope.index = '';
$scope.selectedTab = {result: 'result'};
$scope.crmUrl = CRM.url;
$scope.perm = {
accessDebugOutput: CRM.checkPerm('access debug output'),
editGroups: CRM.checkPerm('edit groups')
Expand All @@ -53,7 +54,7 @@
$scope.status = 'default';
$scope.loading = false;
$scope.controls = {};
$scope.langs = ['php', 'js', 'ang', 'cli'];
$scope.langs = ['php', 'js', 'ang', 'cli', 'rest'];
$scope.joinTypes = [{k: 'LEFT', v: 'LEFT JOIN'}, {k: 'INNER', v: 'INNER JOIN'}, {k: 'EXCLUDE', v: 'EXCLUDE'}];
$scope.bridgeEntities = _.filter(schema, function(entity) {return _.includes(entity.type, 'EntityBridge');});
$scope.code = {
Expand All @@ -73,6 +74,11 @@
{name: 'short', label: ts('CV (short)'), code: ''},
{name: 'long', label: ts('CV (long)'), code: ''},
{name: 'pipe', label: ts('CV (pipe)'), code: ''}
],
rest: [
{name: 'curl', label: ts('Curl'), code: ''},
{name: 'restphp', label: ts('PHP (std)'), code: ''},
{name: 'guzzle', label: ts('PHP + Guzzle'), code: ''}
]
};
this.resultFormats = [
Expand All @@ -85,6 +91,7 @@
label: ts('View as PHP')
},
];
this.authxEnabled = CRM.vars.api4.authxEnabled;

if (!entities.length) {
formatForSelect2(schema, entities, 'name', ['description', 'icon']);
Expand Down Expand Up @@ -669,6 +676,14 @@
return str.trim();
}

// Url-encode suitable for use in a bash script
function curlEscape(str) {
return encodeURIComponent(str).
replace(/['()*]/g, function(c) {
return "%" + c.charCodeAt(0).toString(16);
});
}

function writeCode() {
var code = {},
entity = $scope.entity,
Expand Down Expand Up @@ -777,6 +792,61 @@
code.short += ' ' + key + '=' + (typeof param === 'string' ? cliFormat(param) : cliFormat(JSON.stringify(param)));
}
});
break;

case 'rest':
var restUrl = CRM.vars.api4.restUrl
.replace('CRMAPI4ENTITY', entity)
.replace('CRMAPI4ACTION', action);
var cleanUrl;
if (CRM.vars.api4.restUrl.endsWith('/CRMAPI4ENTITY/CRMAPI4ACTION')) {
cleanUrl = CRM.vars.api4.restUrl.replace('/CRMAPI4ENTITY/CRMAPI4ACTION', '/');
}
var restCred = 'Bearer MY_API_KEY';

// CURL
code.curl =
"CRM_URL='" + restUrl + "'\n" +
"CRM_AUTH='X-Civi-Auth: " + restCred + "'\n\n" +
'curl -X POST -H "$CRM_AUTH" "$CRM_URL" \\' + "\n" +
"-d 'params=" + curlEscape(JSON.stringify(params));
if (index || index === 0) {
code.curl += '&index=' + curlEscape(JSON.stringify(index));
}
code.curl += "'";

var queryParams = "['params' => json_encode($params)" +
((typeof index === 'number') ? ", 'index' => " + JSON.stringify(index) : '') +
((index && typeof index !== 'number') ? ", 'index' => json_encode(" + phpFormat(index) + ')' : '') +
"]";

// Guzzle
code.guzzle =
"$params = " + phpFormat(params, 2) + ";\n" +
"$client = new \\GuzzleHttp\\Client([\n" +
(cleanUrl ? " 'base_uri' => '" + cleanUrl + "',\n" : '') +
" 'headers' => ['X-Civi-Auth' => " + phpFormat(restCred) + "],\n" +
"]);\n" +
"$response = $client->get('" + (cleanUrl ? entity + '/' + action : restUrl) + "', [\n" +
" 'form_params' => " + queryParams + ",\n" +
"]);\n" +
'$' + results + " = json_decode((string) $response->getBody(), TRUE);";

// PHP StdLib
code.restphp =
"$url = '" + restUrl + "';\n" +
"$params = " + phpFormat(params, 2) + ";\n" +
"$request = stream_context_create([\n" +
" 'http' => [\n" +
" 'method' => 'POST',\n" +
" 'header' => [\n" +
" 'Content-Type: application/x-www-form-urlencoded',\n" +
" " + phpFormat('X-Civi-Auth: ' + restCred) + ",\n" +
" ],\n" +
" 'content' => http_build_query(" + queryParams + "),\n" +
" ]\n" +
"]);\n" +
'$' + results + " = json_decode(file_get_contents($url, FALSE, $request), TRUE);\n";
}
}
_.each($scope.code, function(vals) {
Expand Down

0 comments on commit 7b61c1d

Please sign in to comment.