UNPKG

@controlplane/cli

Version:

Control Plane Corporation CLI

258 lines 11 kB
"use strict"; 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