Viewing File: /usr/local/cpanel/share/libraries/cjt2/src/io/uapi.js
/*
# api/io/uapi.js Copyright 2022 cPanel, L.L.C.
# All rights reserved.
# copyright@cpanel.net http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
*/
/* --------------------------*/
/* DEFINE GLOBALS FOR LINT
/*--------------------------*/
/* eslint-env amd */
/* eslint camelcase: "off" */
/* --------------------------*/
// TODO: Add tests for these
/**
* Contain the IAPI Driver implementation used to process cpanel uapi
* request/response messages.
*
* @module cjt2/io/uapi
* @example
*
* require([
* "cjt/io/api",
* "cjt/io/uapi-request",
* "cjt/io/uapi", // IMPORTANT: Load the driver so its ready
* ], function(API, APIREQUEST) {
* return {
* getUsers: function {
* var apiCall = new APIREQUEST.Class();
* apiCall.initialize(UserManager, "list_users");
* return API.promise(apiCall.getRunArguments());
* }
* };
* });
*/
define([
"lodash",
"cjt/io/base",
"cjt/util/test",
"cjt/util/parse",
"cjt/util/query"
], function(_, BASE, TEST, PARSE, QUERY) {
"use strict";
// ------------------------------
// Module
// ------------------------------
var MODULE_NAME = "cjt/io/uapi"; // requirejs(["cjt/io/uapi"], function(api) {}); -> cjt/io/uapi.js || cjt/io/uapi.debug.js
var MODULE_DESC = "Contains the unique bits for integration with UAPI calls.";
var MODULE_VERSION = "2.0";
// ------------------------------
// Shortcuts
// ------------------------------
// Since UAPI doesn’t return the information on which API call
// we called, we need to iterate through the response data and supply that.
// Compare to whm-v1.js (cf. is_batch_response() in that module).
function _expand_response_with_module_and_func(response, args_obj) {
// This way we don’t alter anything that was passed in.
var resp = _.assign({}, response);
if ( args_obj.batch ) {
resp.module = "Batch";
// Possibly offer a "loose" mode in the API at some point?
// That will prompt more logic to be created here.
resp.func = "strict";
if (Array.isArray(resp.data)) {
// Unlike WHM API v1, UAPI batching allows nested batches.
// It could be useful if we implement a “loose” batch method.
resp.data = resp.data.map( function( di, idx ) {
return _expand_response_with_module_and_func( di, args_obj.batch[idx] );
} );
}
} else {
resp.module = args_obj.module;
resp.func = args_obj.func;
}
return resp;
}
function _get_module(args_obj) {
return ( args_obj.batch ? "Batch" : args_obj.module );
}
function _get_func(args_obj) {
return ( args_obj.batch ? "strict" : args_obj.func );
}
/**
* IAPIDriver for uapi
*
* @exports module:cjt/io/uapi:UapiDriver
*/
var uapi = {
MODULE_NAME: MODULE_NAME,
MODULE_DESC: MODULE_DESC,
MODULE_VERSION: MODULE_VERSION,
/**
* Parse a YUI asyncRequest response object to extract
* the interesting parts of a cPanel UAPI call response.
*
* @static
* @param {object} response The asyncRequest response object
* @return {object} See api_base._parse_response for the format of this object.
*/
parse_response: function(response, type, args_obj) {
// BASE._parse_response does this for us, but we can’t
// depend on that since we have to twiddle with the response
// to expand any batches.
response = JSON.parse(response);
response = _expand_response_with_module_and_func(response, args_obj);
return BASE._parse_response(uapi, response);
},
/**
* Determine if the response is a batch
*
* @static
* @param {Object} response
* @return {Boolean} true if this is a batch, false otherwise
* @see module:cjt/io/whm-v1
*/
is_batch_response: function(response) {
// We might include other batch functions in here in the future.
return ( (response.module === "Batch") && (response.func === "strict") );
},
/**
* Return a list of messages from a cPanel UAPI response, normalized as a
* list of [ { level:"info|warn|error", content:"..." }, ... ]
*
* @static
* @param {object} response The parsed API JSON response
* @return {array} The messages that the API call returned
*/
find_messages: function(response) {
if (!response ) {
return [{
level: "error",
content: BASE._unknown_error_msg()
}];
}
if ("errors" in response ) {
var err = response.errors;
if ( err ) {
return [{
level: "error",
content: err.length ? _.escape(err.join("\n")) : BASE._unknown_error_msg()
}];
}
}
if ("messages" in response) {
var messages = response.messages;
if ( messages ) {
return [{
level: "msg",
content: messages.length ? _.escape(messages.join("\n")) : BASE._unknown_error_msg()
}];
}
}
return [];
},
/**
* Indicates whether this module’s find_messages() function
* HTML-escapes.
*/
HTML_ESCAPES_MESSAGES: true,
/**
* Return what a cPanel UAPI call says about whether it succeeded or not
*
* @static
* @param {object} response The parsed API JSON response
* @return {boolean} Whether the API call says it succeeded
*/
find_status: function(response) {
try {
var status = false;
if (response) {
if (typeof (response.status) !== "undefined") {
status = PARSE.parsePerlBoolean(response.status);
} else {
if (window.console) {
window.console.log("The response does not conform to UAPI standards: A status field is required.");
}
}
}
return status;
} catch (e) {
return false;
}
},
/**
* Return normalized data from a UAPI call
*
* @static
* @param {object} response The parsed API JSON response
* @return {array} The data that the API returned
*/
get_data: function(response) {
return response.data;
},
/**
* Return normalized data from a cPanel API 2 call
*
* @static
* @param {object} response The parsed API JSON response
* @return {array} The data that the API returned
*/
get_meta: function(response) {
var meta = {
paginate: {
is_paged: false,
total_records: 0,
current_record: 0,
total_pages: 0,
current_page: 0,
page_size: 0
},
filter: {
is_filtered: false,
records_before_filter: NaN,
records_filtered: NaN
}
};
if (TEST.objectHasPath(response, "metadata.paginate")) {
var paginate = meta.paginate;
paginate.is_paged = true;
paginate.total_records = response.metadata.paginate.total_results || paginate.total_records || 0;
paginate.current_record = response.metadata.paginate.start_result || paginate.current_record || 0;
paginate.total_pages = response.metadata.paginate.total_pages || paginate.total_pages || 0;
paginate.current_page = response.metadata.paginate.current_page || paginate.current_page || 0;
paginate.page_size = response.metadata.paginate.results_per_page || paginate.page_size || 0;
}
if (TEST.objectHasPath(response, "metadata.filter")) {
meta.filter.is_filtered = true;
meta.filter.records_before_filter = response.metadata.records_before_filter || 0;
}
// Copy any custom meta data properties.
if (TEST.objectHasPath(response, "metadata")) {
for (var key in response.metadata) {
if (response.metadata.hasOwnProperty(key) &&
(key !== "filter" && key !== "paginate")) {
meta[key] = response.metadata[key];
}
}
}
return meta;
},
_assemble_batch: function(batch_list) {
var commands = batch_list.map( function(b) {
if (b.args) {
b = Object.create(b);
b.args = QUERY.expand_arrays_for_cpanel_api(b.args);
}
return JSON.stringify([
b.module,
b.func,
uapi.build_query(b),
]);
} );
return {
command: commands
};
},
/**
* Build the call structure from the arguments and data.
*
* @static
* @param {Object} args_obj Arguments passed to the call.
* @return {Object} Object representation of the call arguments
*/
build_query: function(args_obj) {
if (args_obj.batch) {
return this._assemble_batch(args_obj.batch);
}
// Utility variables, used in specific contexts below.
var s, cur_sort, f, cur_filter;
var api_prefix = "api.";
var api_call = {};
if (args_obj.args) {
_.extend(api_call, args_obj.args);
}
if (args_obj.meta) {
if (args_obj.meta.sort) {
var sort_count = args_obj.meta.sort.length;
if (sort_count === 1) {
cur_sort = args_obj.meta.sort[0];
if (cur_sort instanceof Array) {
api_call[api_prefix + "sort_method"] = cur_sort[1];
cur_sort = cur_sort[0];
}
if (cur_sort.charAt(0) === "!") {
api_call[api_prefix + "sort_reverse"] = 1;
cur_sort = cur_sort.substr(1);
}
api_call[api_prefix + "sort_column"] = cur_sort;
} else {
for (s = 0; s < sort_count; s++) {
cur_sort = args_obj.meta.sort[s];
if (cur_sort instanceof Array) {
api_call[api_prefix + "sort_method_" + s] = cur_sort[1];
cur_sort = cur_sort[0];
}
if (cur_sort.charAt(0) === "!") {
api_call[api_prefix + "sort_reverse_" + s] = 1;
cur_sort = cur_sort.substr(1);
}
api_call[api_prefix + "sort_column_" + s] = cur_sort;
}
}
}
if (args_obj.meta.filter) {
var filter_count = args_obj.meta.filter.length;
if (filter_count === 1) {
cur_filter = args_obj.meta.filter[0];
api_call[api_prefix + "filter_column"] = cur_filter[0];
api_call[api_prefix + "filter_type"] = cur_filter[1];
api_call[api_prefix + "filter_term"] = cur_filter[2];
} else {
for (f = 0; f < filter_count; f++) {
cur_filter = args_obj.meta.filter[f];
api_call[api_prefix + "filter_column_" + f] = cur_filter[0];
api_call[api_prefix + "filter_type_" + f] = cur_filter[1];
api_call[api_prefix + "filter_term_" + f] = cur_filter[2];
}
}
}
if (args_obj.meta.paginate) {
if ("start" in args_obj.meta.paginate) {
api_call[api_prefix + "paginate_start"] = args_obj.meta.paginate.start;
}
if ("size" in args_obj.meta.paginate) {
api_call[api_prefix + "paginate_size"] = args_obj.meta.paginate.size;
}
}
delete args_obj.meta;
}
if (args_obj.analytics) {
api_call[api_prefix + "analytics"] = args_obj.analytics.serialize();
}
return api_call;
},
/**
* Assemble the url for the request.
*
* @static
* @param {String} token cPanel Security Token
* @param {Object} args_obj Arguments passed to the call.
* @return {String} Url prefix for the call
*/
get_url: function(token, args_obj ) {
return token + [
"",
"execute",
_get_module(args_obj),
_get_func(args_obj),
].map(encodeURIComponent).join("/");
}
};
return uapi;
});
Back to Directory
File Manager