@controlplane/cli
Version:
Control Plane Corporation CLI
258 lines • 11 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.RequestList = exports.RequestGet = exports.RequestCreate = exports.QuotaRequestCmd = exports.QuotaCmd = void 0;
exports.withRequestCreateOptions = withRequestCreateOptions;
exports.withRequestListOptions = withRequestListOptions;
const generic_1 = require("./generic");
const command_1 = require("../cli/command");
const query_1 = require("./query");
const resolver_1 = require("./resolver");
const options_1 = require("./options");
const functions_1 = require("../util/functions");
const api_1 = require("../rest/api");
const client_1 = require("../session/client");
// ANCHOR - Constants
// The service discovery key under which the billing service (billing-ng) exposes its endpoint.
const BILLING_NG_SERVICE_NAME = 'billing-ng';
// SECTION - Functions
/**
* Adds the positional argument identifying the quota to request an increase for.
*
* @param {Argv} yargs - The yargs instance to extend.
* @returns {Argv} The yargs instance with the quota name positional registered.
*/
function withQuotaName(yargs) {
return yargs.positional('name', {
description: 'The name of the quota to request an increase for (e.g., gvcs).',
type: 'string',
});
}
/**
* Adds the positional argument identifying the quota increase request to retrieve.
*
* @param {Argv} yargs - The yargs instance to extend.
* @returns {Argv} The yargs instance with the request ID positional registered.
*/
function withIncreaseRequestId(yargs) {
return yargs.positional('id', {
description: 'The ID of the quota increase request to retrieve (as returned by `cpln quota request create`).',
type: 'string',
});
}
/**
* Adds the options specific to the quota request create subcommand.
*
* @param {Argv} yargs - The yargs instance to extend.
* @returns {Argv} The yargs instance with the request create options registered.
*/
function withRequestCreateOptions(yargs) {
return yargs.options({
'requested-max': {
description: 'The new maximum value being requested for the quota.',
requiresArg: true,
demandOption: true,
number: true,
},
});
}
/**
* Adds the options specific to the quota request list subcommand.
*
* @param {Argv} yargs - The yargs instance to extend.
* @returns {Argv} The yargs instance with the request list options registered.
*/
function withRequestListOptions(yargs) {
return yargs.options({
status: {
description: 'Only list requests with this status.',
requiresArg: true,
choices: ['pending', 'approved', 'denied'],
},
});
}
/**
* Resolves the billing service endpoint from service discovery and returns a REST client targeting it.
*
* Quota increase requests are handled by the billing service (billing-ng), which is not reachable
* through the data-service endpoint, so its base URL must be resolved from service discovery.
*
* @param {Session} session - The active session used to reach discovery and authorize requests.
* @returns {Promise<RestClient>} A promise that resolves to a REST client targeting the billing service.
*/
async function makeBillingClient(session) {
// Resolve the billing service base URL from service discovery
const serviceEndpoint = await (0, api_1.getDiscoveryEndpoint)(session.request.endpoint, BILLING_NG_SERVICE_NAME);
// Create a client targeting the billing service
return (0, client_1.makeSessionClient)(session, serviceEndpoint);
}
// !SECTION
// ANCHOR - Command
class QuotaCmd extends command_1.Command {
constructor() {
super(...arguments);
this.command = 'quota';
this.describe = 'Show quotas';
}
builder(yargs) {
const resolver = (0, resolver_1.kindResolver)('quota');
const schema = {
props: [...query_1.defaultProps, 'origin', 'max', 'current', 'unit'],
};
const opts = [options_1.withOrgOptions, options_1.withStandardOptions];
const commandName = 'quota';
const commandNamePlural = 'quotas';
const commandNameA = 'a quota';
return (yargs
.demandCommand()
.version(false)
.help()
// generic
.command(new generic_1.Get(commandNamePlural, resolver, ...opts).toYargs())
.command(new generic_1.Query(commandNamePlural, resolver, schema, ...opts).toYargs())
.command(new generic_1.ListPermissions(commandNameA, resolver, ...opts).toYargs())
.command(new generic_1.Edit(commandName, resolver, ...opts).toYargs())
.command(new generic_1.Patch(commandName, resolver, ...opts).toYargs())
// specific
.command(new QuotaRequestCmd().toYargs()));
}
handle() { }
}
exports.QuotaCmd = QuotaCmd;
class QuotaRequestCmd extends command_1.Command {
constructor() {
super(...arguments);
this.command = 'request';
this.describe = 'Manage quota increase requests';
}
builder(yargs) {
return yargs
.demandCommand()
.version(false)
.help()
.command(new RequestCreate().toYargs())
.command(new RequestGet().toYargs())
.command(new RequestList().toYargs());
}
handle() { }
}
exports.QuotaRequestCmd = QuotaRequestCmd;
class RequestCreate extends command_1.Command {
constructor() {
super(...arguments);
this.command = 'create <name>';
this.describe = 'Submit a quota increase request';
}
builder(yargs) {
return (0, functions_1.pipe)(
//
withQuotaName, withRequestCreateOptions, options_1.withOrgOptions, options_1.withStandardOptions)(yargs);
}
async handle(args) {
// An organization is required to scope the request
this.requireOrg();
// Resolve the requested maximum (yargs guarantees presence via demandOption)
const requestedMax = args.requestedMax;
// Reject negative maximums to match the billing service validation
if (requestedMax < 0) {
this.session.abort({ message: 'ERROR: The requested maximum (--requested-max) must be a non-negative number.' });
}
// Build the request body for the billing service
const requestBody = {
quotaName: args.name,
requestedMax: requestedMax,
};
// Build a client targeting the billing service, which owns quota increase requests
const billingClient = await makeBillingClient(this.session);
// Construct the billing service path for quota increase requests
const requestPath = `/org/${this.session.context.org}/quota-increase-requests`;
// Submit the quota increase request
const response = await billingClient.post(requestPath, requestBody);
// Inform the user of the outcome on stderr so the machine-readable body stays clean on stdout
if (response.status === 'approved') {
const how = response.autoApproved ? 'automatically approved' : 'approved';
this.session.err(`Quota increase request for '${response.quotaName}' was ${how}. The maximum is now ${response.requestedMax}.`);
}
else if (response.status === 'pending') {
this.session.err(`Quota increase request for '${response.quotaName}' (max ${response.requestedMax}) was submitted and is pending administrator approval.`);
}
else {
this.session.err(`Quota increase request for '${response.quotaName}' was denied.`);
}
// Output the request record in the requested format. The billing service response carries no
// `kind`, so hint the text formatter to use the quota increase request model for table parity.
await this.session.outFormat(response, {
...this.session.format,
_hints: {
kind: 'quotaIncreaseRequest',
},
});
}
}
exports.RequestCreate = RequestCreate;
class RequestGet extends command_1.Command {
constructor() {
super(...arguments);
this.command = 'get <id>';
this.describe = 'Get a quota increase request and its status';
}
builder(yargs) {
return (0, functions_1.pipe)(
//
withIncreaseRequestId, options_1.withOrgOptions, options_1.withStandardOptions)(yargs);
}
async handle(args) {
// The request ID is required (yargs enforces the positional)
const id = args.id;
// Build a client targeting the billing service, which owns quota increase requests
const billingClient = await makeBillingClient(this.session);
// Construct the billing service path for the individual request. The request is authorized
// against the org it belongs to, so the ID alone identifies it.
const requestPath = `/quota-increase-requests/${encodeURIComponent(id)}`;
// Retrieve the quota increase request
const response = await billingClient.get(requestPath);
// Output the request record in the requested format. The billing service response carries no
// `kind`, so hint the text formatter to use the quota increase request model for table parity.
await this.session.outFormat(response, {
...this.session.format,
_hints: {
kind: 'quotaIncreaseRequest',
},
});
}
}
exports.RequestGet = RequestGet;
class RequestList extends command_1.Command {
constructor() {
super(...arguments);
this.command = 'list';
this.describe = "List the org's quota increase requests, optionally filtered by status";
}
builder(yargs) {
return (0, functions_1.pipe)(
//
withRequestListOptions, options_1.withOrgOptions, options_1.withStandardOptions)(yargs);
}
async handle(args) {
// An organization is required to scope the listing
this.requireOrg();
// Build a client targeting the billing service, which owns quota increase requests
const billingClient = await makeBillingClient(this.session);
// Scope the listing to the org. The optional status filter is only sent when provided.
const params = { org: this.session.context.org };
if (args.status) {
params.status = args.status;
}
// Retrieve the org's quota increase requests
const response = await billingClient.get('/quota-increase-requests', { params });
// Output the request records in the requested format. The billing service response carries no
// `kind`, so hint the text formatter to use the quota increase request model for table parity.
await this.session.outFormat(response, {
...this.session.format,
_hints: {
kind: 'quotaIncreaseRequest',
},
});
}
}
exports.RequestList = RequestList;
//# sourceMappingURL=quota.js.map