UNPKG

@controlplane/cli

Version:

Control Plane Corporation CLI

959 lines (958 loc) 37.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Update = exports.guessDescription = exports.ViewAccessReport = exports.Clone = exports.withCloneOptions = exports.Tag = exports.Apply = exports.ListPermissions = exports.Eventlog = exports.Query = exports.Edit = exports.Patch = exports.DeleteFromFiles = exports.Delete = exports.Get = exports.withMultipleRefs = exports.withSingleRef = exports.withReadyOption = exports.withK8sOption = exports.withFileInputOptions = exports.withOptionalFileInputOptions = exports.withMultipleLocationsOption = exports.withSingleLocationOption = exports.withTagOptions = exports.withTagRemovableOptions = exports.fromRemoveTagOptions = exports.arrayToTags = exports.tagsToArray = exports.fromTagOptions = void 0; const _ = require("lodash"); const options_1 = require("./options"); const resolver_1 = require("./resolver"); const query_1 = require("./query"); const update_1 = require("../update/update"); const tags_1 = require("./tags"); const io_1 = require("../util/io"); const functions_1 = require("../util/functions"); const objects_1 = require("../util/objects"); const command_1 = require("../cli/command"); const resultFetcher_1 = require("../rest/resultFetcher"); const workload_1 = require("../util/workload"); const logger_1 = require("../util/logger"); const format_1 = require("../util/format"); const converter_1 = require("../k8s-converter/converter"); const format_2 = require("../format/format"); const links_1 = require("../util/links"); const FIVE_SECONDS = 5; const FIVE_MINUTES = FIVE_SECONDS * 60; function fromTagOptions(opts) { return (0, tags_1.expressionToTags)(opts.tag); } exports.fromTagOptions = fromTagOptions; function tagsToArray(opts) { const tags = fromTagOptions(opts); return Object.keys(tags).map((key) => { return { [key]: tags[key] }; }); } exports.tagsToArray = tagsToArray; function arrayToTags(opts) { let tags = {}; opts.forEach((obj) => { for (let key in obj) { tags[key] = obj[key]; } }); return tags; } exports.arrayToTags = arrayToTags; function fromRemoveTagOptions(opts) { return (0, tags_1.withNullValues)((0, objects_1.toArray)(opts.remove)); } exports.fromRemoveTagOptions = fromRemoveTagOptions; function withTagRemovableOptions(yargs) { return yargs.options({ tag: { description: 'Attach tags (e.g., --tag drink=water)', requiresArg: true, multiple: true, }, remove: { description: 'Remove tags (e.g., --remove tagname)', requiresArg: true, multiple: true, }, }); } exports.withTagRemovableOptions = withTagRemovableOptions; function withTagOptions(yargs) { return yargs.options({ tag: { description: 'Attach tags (e.g., --tag drink=water)', requiresArg: true, multiple: true, }, }); } exports.withTagOptions = withTagOptions; function withSingleLocationOption(isRequired) { return function (yargs) { return yargs.options({ location: { type: 'string', requiresArg: true, demandOption: isRequired, description: 'A global virtual cloud location', }, }); }; } exports.withSingleLocationOption = withSingleLocationOption; function withMultipleLocationsOption(yargs) { return yargs.options({ location: { type: 'string', multiple: true, description: 'A global virtual cloud location', }, }); } exports.withMultipleLocationsOption = withMultipleLocationsOption; function withOptionalFileInputOptions(yargs) { return yargs.options({ file: { alias: 'f', multiple: true, requiresArg: true, description: 'File to load and use for the command. Use `--file -` to enable input from stdin.', }, }); } exports.withOptionalFileInputOptions = withOptionalFileInputOptions; function withFileInputOptions(yargs) { return yargs.options({ file: { alias: 'f', requiresArg: true, demandOption: true, description: 'File to load and use for the command. Use `--file -` to enable input from stdin.', }, }); } exports.withFileInputOptions = withFileInputOptions; function withK8sOption(yargs) { return yargs.options({ k8s: { requiresArg: true, boolean: true, description: 'Set this true if input file is k8s config file', }, }); } exports.withK8sOption = withK8sOption; function withReadyOption(yargs) { return yargs.options({ ready: { boolean: true, description: 'Set this true if apply should wait for objects to be ready before exiting', }, }); } exports.withReadyOption = withReadyOption; function withSingleRef(yargs) { return yargs.positional('ref', { description: 'The resource reference. Usually it is the name of the resource.', }); } exports.withSingleRef = withSingleRef; function withMultipleRefs(yargs) { return yargs.positional('ref', { description: 'One or more resource references. Usually it is the name of the resource.', demandOption: true, multiple: true, }); } exports.withMultipleRefs = withMultipleRefs; class Get extends command_1.Command { constructor(commandName, resolve, ...funcs) { super(); this.resolve = resolve; this.command = 'get [ref...]'; this.describe = 'Retrieve resources by their name'; if (funcs.length == 0) { this.func = (0, functions_1.pipe)(withMultipleRefs, options_1.withAllOptions); } else { this.func = (0, functions_1.pipe)(withMultipleRefs, ...funcs); } if (commandName) { this.describe = `Retrieve one or more referenced ${commandName}`; } } builder(yargs) { return this.func(yargs); } async list(args) { const link = this.resolve.homeLink(this.session.context); const body = await this.client.get(link); await (0, resultFetcher_1.fetchPages)(this.client, this.session.format.max, body); this.session.outFormat(body); } async get(args) { var _a; args.ref = _.uniq((0, objects_1.toArray)(args.ref)); if (((_a = args.ref) === null || _a === void 0 ? void 0 : _a.length) == 1) { const link = this.resolve.resourceLink(args.ref[0], this.session.context); const body = await this.client.get(link); this.session.outFormat(body); return; } const accumulator = { kind: 'list', itemKind: this.resolve.kind, items: [], links: [], }; for (let ref of args.ref) { const link = this.resolve.resourceLink(ref, this.session.context); const body = await this.client.get(link); accumulator.items.push(body); } if (accumulator.items.length > 0) { this.session.outFormat(accumulator); } } async handle(args) { if (_.isEmpty(args.ref)) { await this.list(args); return; } await this.get(args); } } exports.Get = Get; class Delete extends command_1.Command { constructor(commandName, resolve, ...funcs) { super(); this.resolve = resolve; this.command = 'delete <ref...>'; this.describe = 'Delete resources by name'; if (funcs.length == 0) { this.func = (0, functions_1.pipe)(withMultipleRefs, options_1.withAllOptions); } else { this.func = (0, functions_1.pipe)(withMultipleRefs, ...funcs); } if (commandName) { this.describe = `Delete one or more referenced ${commandName}`; } } builder(yargs) { return this.func(yargs); } async handle(args) { args.ref = _.uniq((0, objects_1.toArray)(args.ref)); const failures = []; for (let ref of args.ref) { const link = this.resolve.resourceLink(ref, this.session.context); try { await this.client.delete(link); } catch (e) { failures.push(e); } } if (failures.length > 0) { this.session.abort({ error: failures }); } } } exports.Delete = Delete; class DeleteFromFiles extends command_1.Command { constructor() { super(...arguments); this.command = 'delete'; this.describe = 'Delete resources from files'; this.currentKindToEnsureDeletion = undefined; this.resourceLinksToEnsureDeletion = []; } builder(yargs) { return (0, functions_1.pipe)( // withFileInputOptions, withK8sOption, options_1.withAllOptions)(yargs); } async handle(args) { let failures = []; const resources = []; // Accumulate the path of every file const filePaths = (0, io_1.readFileInputsOption)((0, objects_1.toArray)(args.file)); if (args.k8s) { // Convert K8s to cpln resources const k8sConverter = new converter_1.K8sConverter(filePaths, this.session.context, this.session); try { const convertedK8s = await k8sConverter.convert(); resources.push(...convertedK8s); // If a GVC is specified and there were pull secrets found, remove them from the GVC if (this.session.context.gvc && k8sConverter.pullSecrets.length != 0) { // Fetch the GVC const gvcLink = (0, resolver_1.resolveToLink)('gvc', this.session.context.gvc, this.session.context); const gvc = (0, format_2.slimObject)(await this.client.get(gvcLink)); // Add the pull secrets to the GVC if (gvc.spec && gvc.spec.pullSecretLinks && gvc.spec.pullSecretLinks.length != 0) { // Remove K8s converter pull secrets from the specified GVC gvc.spec.pullSecretLinks = gvc.spec.pullSecretLinks.filter((gvcPullSecret) => { for (const converterPullSecret of k8sConverter.pullSecrets) { if (gvcPullSecret.includes((0, links_1.getLastPartOfLink)(converterPullSecret))) { // Remove this pull secret return false; } } // Keep this pull secret return true; }); // Update GVC let gvcParentLink = (0, resolver_1.kindResolver)('gvc').parentLink(this.session.context); await this.client.put(gvcParentLink, gvc); this.session.out(`Updated ${gvc.kind} '${gvc.name}'`); } } } catch (e) { this.session.abort({ error: e }); } } else { // Process cpln resources for (let filePath of filePaths) { const textFile = await (0, io_1.readTextFile)(filePath); const loadedResources = (0, io_1.loadAllObject)(textFile, filePath); for (const loadedResource of loadedResources) { if (!loadedResource.kind) { this.session.abort({ message: `Resource in ${filePath} does not contain a "kind" property`, }); } if (loadedResource.hasOwnProperty('gvc')) { const hasGvc = loadedResource; if (hasGvc.gvc && args.gvc && hasGvc.gvc !== args.gvc) { this.session.abort({ message: `ERROR: The specified --gvc option '${args.gvc}' does not match the gvc value '${hasGvc.gvc}' that is specified in the file '${filePath}' in ${loadedResource.kind} '${loadedResource.name}'.`, }); } } resources.push(loadedResource); } } } // Sort by priority for deletion const sortedResources = (0, objects_1.sortByKindPriority)(resources, true); for (let resource of sortedResources) { this.currentKindToEnsureDeletion = undefined; this.resourceLinksToEnsureDeletion = []; // Delete resource const applyRes = await this.deleteItem(resource); if (applyRes !== true) { failures.push(applyRes); } } // Abort if failures were found if (failures.length > 0) { this.session.abort({ error: failures }); } } async deleteItem(item) { let ctx = this.session.context; if (item.gvc) { ctx.gvc = item.gvc; } let selfLink = (0, resolver_1.kindResolver)(item.kind).resourceLink(item.name, ctx); // Ensure the deletion of a group of resources of the same kind if (this.currentKindToEnsureDeletion && this.currentKindToEnsureDeletion !== item.kind) { this.currentKindToEnsureDeletion = undefined; await Promise.all(this.resourceLinksToEnsureDeletion.map((link) => this.ensureDeletion(link))); this.resourceLinksToEnsureDeletion = []; } // Consider these kinds of resources to ensure their deletion later if (item.kind == 'volumeset' || item.kind == 'workload') { this.currentKindToEnsureDeletion = item.kind; this.resourceLinksToEnsureDeletion.push(selfLink); } try { await this.client.axios.delete(selfLink); this.session.out(`Deleted ${item.kind} '${item.name}'`); return true; } catch (e) { return e; } } } exports.DeleteFromFiles = DeleteFromFiles; class Patch extends command_1.Command { constructor(commandName, resolve, ...funcs) { super(); this.resolve = resolve; this.command = 'patch <ref>'; this.describe = 'Patch a resource by providing the metadata in file'; if (funcs.length == 0) { this.func = (0, functions_1.pipe)(withSingleRef, this.withBodyOptions, options_1.withAllOptions); } else { this.func = (0, functions_1.pipe)(withSingleRef, this.withBodyOptions, ...funcs); } if (commandName) { this.describe = `Update the referenced ${commandName}\'s metadata using an input file`; } } builder(yargs) { return this.func(yargs); } async handle(args) { let bodyAsString; if (args.file) { bodyAsString = await (0, io_1.readTextFile)(args.file); } else { this.session.abort({ message: 'No input file provided' }); } const reqBody = (0, io_1.loadObject)(bodyAsString, args.file); const link = this.resolve.resourceLink(args.ref, this.session.context); let respBody = await this.client.patch(link, reqBody); this.session.outFormat(respBody); } withBodyOptions(yargs) { return yargs.options({ file: { alias: 'f', description: 'File to load the patch from. Use `--file -` to enable input from stdin.', nargs: 1, demandOption: true, requiresArg: true, }, }); } } exports.Patch = Patch; class Edit extends command_1.Command { constructor(commandName, resolve, ...funcs) { super(); this.resolve = resolve; this.command = 'edit <ref>'; this.describe = 'Edit a resource as yaml in an editor'; if (funcs.length == 0) { this.func = (0, functions_1.pipe)(withSingleRef, options_1.withAllOptions, this.withReplaceOptions); } else { this.func = (0, functions_1.pipe)(withSingleRef, this.withReplaceOptions, ...funcs); } if (commandName) { this.describe = `Edit the referenced ${commandName}, as YAML, within an editor`; } } withReplaceOptions(yargs) { return yargs.options({ replace: { alias: 'r', description: 'Replace instead of patch/merge', type: 'boolean', default: false, }, }); } builder(yargs) { return this.func(yargs); } async handle(args) { var _a; const link = this.resolve.resourceLink(args.ref, this.session.context); let body = await this.client.get(link); let action = 'PATCH'; if (args.replace) { action = 'REPLACE'; } const header = `# You are about to ${action} ${link} # You are performing this operation as ${((_a = this.session.profile.authInfo) === null || _a === void 0 ? void 0 : _a.email) || '<system>'} `; const editableYaml = header + (0, format_1.toSortedYamlString)(body); const editMod = require('../editor/editor'); const newContent = await editMod.edit(editableYaml, 'yaml'); if (newContent === undefined) { this.session.end('Resource was not changed, will not try to PATCH it'); } const newPayload = (0, io_1.loadObject)(newContent, '<editor>'); if (_.isEqual(body, newPayload)) { this.session.end('Resource was not changed, will not try to PATCH it'); } if (args.replace) { body = await this.client.put(link, newPayload); } else { body = await this.client.patch(link, newPayload); } this.session.outFormat(body); } } exports.Edit = Edit; class Query extends command_1.Command { constructor(commandName, resolve, schema, ...funcs) { super(); this.resolve = resolve; this.schema = schema; this.command = 'query'; this.describe = 'Find all items of a kind based on search criteria'; if (funcs.length == 0) { this.func = (0, functions_1.pipe)((0, query_1.withQueryOptions)(this.schema), options_1.withAllOptions); } else { this.func = (0, functions_1.pipe)((0, query_1.withQueryOptions)(this.schema), ...funcs); } if (commandName) { this.describe = `Find all the ${commandName} based on the given query`; } } builder(yargs) { return this.func(yargs); } async handle(args) { let link = this.resolve.homeLink(this.session.context); link += '/-query'; const query = (0, query_1.fromQueryOptions)(args); logger_1.logger.debug(`Parsed into query ${JSON.stringify(query, undefined, 2)}`); const body = await this.client.post(link, query); await (0, resultFetcher_1.fetchPages)(this.client, this.session.format.max, body); this.session.outFormat(body); } } exports.Query = Query; class Eventlog extends command_1.Command { constructor(commandName, resolve, ...funcs) { super(); this.resolve = resolve; this.command = 'eventlog <ref>'; this.aliases = ['log']; this.describe = 'Show the event log for a resource'; if (funcs.length == 0) { this.func = (0, functions_1.pipe)(withSingleRef, options_1.withAllOptions); } else { this.func = (0, functions_1.pipe)(withSingleRef, ...funcs); } if (commandName) { this.describe = `Show the event log of the referenced ${commandName}`; } } builder(yargs) { return this.func(yargs); } async handle(args) { let link = this.resolve.resourceLink(args.ref, this.session.context); link += '/-eventlog'; const body = await this.client.get(link); await (0, resultFetcher_1.fetchPages)(this.client, this.session.format.max, body); this.session.outFormat(body); } } exports.Eventlog = Eventlog; class ListPermissions extends command_1.Command { constructor(commandName, resolve, ...funcs) { super(); this.resolve = resolve; this.command = 'permissions'; this.describe = 'Show the grantable permissions for a kind'; if (funcs.length == 0) { this.func = (0, functions_1.pipe)(options_1.withAllOptions); } else { this.func = (0, functions_1.pipe)(...funcs); } if (commandName) { this.describe = `Show the grantable permissions for ${commandName} object type`; } } builder(yargs) { return this.func(yargs); } async handle(args) { let link = this.resolve.homeLink(this.session.context); link += '/-schema/permissions'; const body = await this.client.get(link); if (this.session.format.output != 'text') { this.session.outFormat(body, args); } else { // if out=text format a single permissions object as list // make it an array const flattened = []; for (let perm of body.items) { flattened.push({ name: perm.name, description: perm.description, implied: body.implied, }); } // pick the correct provider args._hints = { kind: 'flattenedPermissions', }; this.session.outFormat(flattened, args); } } } exports.ListPermissions = ListPermissions; class Apply extends command_1.Command { constructor() { super(...arguments); this.command = 'apply'; this.describe = 'Create or update a resource using an input file'; } builder(yargs) { return (0, functions_1.pipe)( // withFileInputOptions, withK8sOption, options_1.withAllOptions, withReadyOption)(yargs); } async handle(args) { const failures = []; const resources = []; const appliedWorkloads = []; // Accumulate the path of every file const filePaths = (0, io_1.readFileInputsOption)((0, objects_1.toArray)(args.file)); // Process files if (args.k8s) { // Convert K8s to cpln resources const k8sConverter = new converter_1.K8sConverter(filePaths, this.session.context, this.session); try { const convertedK8s = await k8sConverter.convert(); resources.push(...convertedK8s); // If a GVC is specified and there were pull secrets found, add them to the GVC if (this.session.context.gvc && k8sConverter.pullSecrets.length != 0) { // Fetch the GVC const gvcLink = (0, resolver_1.resolveToLink)('gvc', this.session.context.gvc, this.session.context); const gvc = (0, format_2.slimObject)(await this.client.get(gvcLink)); // Add the pull secrets to the GVC if (!gvc.spec) { gvc.spec = { pullSecretLinks: [], }; } if (!gvc.spec.pullSecretLinks) { gvc.spec.pullSecretLinks = []; } gvc.spec.pullSecretLinks.push(...k8sConverter.pullSecrets); // Add GVC to the resource list that will be applied resources.push(gvc); } } catch (e) { this.session.abort({ error: e }); } } else { // Process cpln resources for (let filePath of filePaths) { const textFile = await (0, io_1.readTextFile)(filePath); const loadedResources = (0, io_1.loadAllObject)(textFile, filePath); for (const loadedResource of loadedResources) { if (!loadedResource.kind) { this.session.abort({ message: `Resource in ${filePath} does not contain a "kind" property`, }); } if (loadedResource.hasOwnProperty('gvc')) { const hasGvc = loadedResource; if (hasGvc.gvc && args.gvc && hasGvc.gvc !== args.gvc) { this.session.abort({ message: `ERROR: The specified --gvc option '${args.gvc}' does not match the gvc value '${hasGvc.gvc}' that is specified in the file '${filePath}' in ${loadedResource.kind} '${loadedResource.name}'.`, }); } } resources.push(loadedResource); } } } // Apply resources const sortedResources = (0, objects_1.sortByKindPriority)(resources); for (let resource of sortedResources) { const applyResult = await this.applyItem(resource); if (applyResult !== true) { failures.push(applyResult); } else if (resource.kind === 'workload') { appliedWorkloads.push(resource); } } // Abort if failures were found if (failures.length > 0) { this.session.abort({ error: failures }); } // Wait for workloads to become ready if specified if (args.ready) { const workloadPaths = appliedWorkloads.map((w) => { return (0, resolver_1.resolveToLink)('workload', w.name, this.session.context); }); const deploymentPaths = appliedWorkloads.map((w) => { return (0, resolver_1.deploymentPath)(w, this.session.context); }); let consecutive_successes = 0; const RETRY_INTERVAL = FIVE_SECONDS; const MAX_RETRIES = Math.floor(FIVE_MINUTES / RETRY_INTERVAL); const MIN_CONSECUTIVE_SUCCESS = 3; const success = await (0, functions_1.retryFn)(async () => { for (const idx in appliedWorkloads) { const workload = (await this.client.axios.get(workloadPaths[idx])).data; const deployments = (await this.client.axios.get(deploymentPaths[idx])).data.items; const { readyLatest } = (0, workload_1.getWorkloadHealth)(deployments, workload); if (!readyLatest) { consecutive_successes = 0; throw Error('Not all deployments ready'); } else { consecutive_successes++; if (consecutive_successes < MIN_CONSECUTIVE_SUCCESS) throw Error('Needs more consecutive successes to confirm readiness'); } } }, { repeatTimes: MAX_RETRIES, intervalSec: RETRY_INTERVAL }); if (success === false) { this.session.abort({ message: 'Workload(s) could not start successfully' }); } } } async applyItem(item) { let ctx = this.session.context; if (item.gvc) { ctx.gvc = item.gvc; } let path = (0, resolver_1.kindResolver)(item.kind).parentLink(ctx); try { // use low-level axios api to get the Location header const response = await this.client.axios.put(path, item); if (response.status == 201) { this.session.out(`Created ${response.headers.location}`); } else { this.session.out(`Updated ${item.kind} '${item.name}'`); } return true; } catch (e) { return e; } } } exports.Apply = Apply; class Tag extends command_1.Command { constructor(commandName, resolve, ...funcs) { super(); this.resolve = resolve; this.command = 'tag <ref...>'; this.describe = 'Attach tags to resources'; if (funcs.length == 0) { this.func = (0, functions_1.pipe)(withMultipleRefs, withTagRemovableOptions, options_1.withAllOptions); } else { this.func = (0, functions_1.pipe)(withMultipleRefs, withTagRemovableOptions, ...funcs); } if (commandName) { this.describe = `Manage the tags belonging to one or more referenced ${commandName}`; } } builder(yargs) { return this.func(yargs); } async handle(args) { const accumulator = { kind: 'list', itemKind: this.resolve.kind, items: [], links: [], }; const failures = []; args.ref = _.uniq((0, objects_1.toArray)(args.ref)); for (let ref of args.ref) { let link = this.resolve.resourceLink(ref, this.session.context); try { const tags = { ...fromTagOptions(args), ...fromRemoveTagOptions(args) }; const obj = await this.client.patch(link, { tags, }); accumulator.items.push(obj); } catch (e) { failures.push(e); } } if (accumulator.items.length > 0) { this.session.outFormat(accumulator); } if (failures.length > 0) { this.session.abort({ error: failures }); } } } exports.Tag = Tag; function withCloneOptions(yargs) { return (0, functions_1.pipe)( // (yargs) => { return yargs.options({ name: { requiresArg: true, description: 'Set the name for the clone', demandOption: true, }, description: { requiresArg: true, description: 'Optional description, defaults to the name if not set', }, }); }, withTagOptions)(yargs); } exports.withCloneOptions = withCloneOptions; class Clone extends command_1.Command { constructor(commandName, resolve, ...funcs) { super(); this.resolve = resolve; this.command = 'clone <ref>'; this.aliases = ['copy']; this.describe = `Create a clone of the referenced ${this.resolve.kind}; this will only duplicate its spec.`; if (funcs.length == 0) { this.func = (0, functions_1.pipe)( // withSingleRef, withCloneOptions, options_1.withAllOptions); } else { this.func = (0, functions_1.pipe)( // withSingleRef, withCloneOptions, ...funcs); } if (commandName) { this.describe = `Create a clone of the referenced ${commandName}; this will only duplicate its spec.`; } } builder(yargs) { return this.func(yargs); } async handle(args) { const resourceLink = this.resolve.resourceLink(args.ref, this.session.context); const resource = await this.client.get(resourceLink); resource.tags = { ...resource.tags, ...fromTagOptions(args) }; // new description if (args.description) { resource.description = args.description; } else { resource.description = guessDescription(resource); } // override the name, this should stay after description to prevent naming bug resource.name = args.name; const path = this.resolve.parentLink(this.session.context); const data = await this.client.create(path, resource); this.session.outFormat(data); } } exports.Clone = Clone; class ViewAccessReport extends command_1.Command { constructor(commandName, resolve, ...funcs) { super(); this.resolve = resolve; this.command = 'access-report <ref>'; this.describe = 'Show the access report for the referenced object'; if (funcs.length == 0) { this.func = (0, functions_1.pipe)(withSingleRef, options_1.withAllOptions); } else { this.func = (0, functions_1.pipe)(withSingleRef, ...funcs); } if (commandName) { this.describe = `Show the access report for the referenced ${commandName}`; } } builder(yargs) { return this.func(yargs); } async handle(args) { const link = this.resolve.resourceLink(args.ref, this.session.context) + '/-accessreport'; const body = await this.client.get(link); if (this.session.format.output != 'text') { this.session.outFormat(body, args); } else { // if out=text format a single permissions object as list // flatten access report const flattened = []; for (let perm of body.permissions) { for (let bi of perm.bindings) { flattened.push({ permission: perm.name, principalLink: bi.principalLink, grantedPermissions: bi.grantedPermissions, }); } } _.sortBy(flattened, 'perm'); this.session.outFormat(flattened, { ...this.session.format, _hints: { kind: 'flattenedAccessReport', }, }); } } } exports.ViewAccessReport = ViewAccessReport; const desc = 'Update the following properties (e.g., --set description="Updated Description"):'; const descArray = 'Arrays can be appended to, replaced, or removed using the `+=`, `=`, or `-=` operators, respectively.'; function guessDescription(original) { return `Clone of ${original.name}`; } exports.guessDescription = guessDescription; class Update extends command_1.Command { constructor(commandName, resolve, paths, ...funcs) { super(); this.resolve = resolve; this.paths = paths; this.command = 'update <ref>'; this.describe = 'Update properties of the referenced object'; this.longestProperty = 0; this.updateDescribe = ''; if (funcs.length == 0) { this.func = (0, functions_1.pipe)(withSingleRef, this.options.bind(this), options_1.withAllOptions); } else { this.func = (0, functions_1.pipe)(withSingleRef, this.options.bind(this), ...funcs); } if (commandName) { this.describe = `Update properties of the referenced ${commandName}`; this.longestProperty = paths .map((p) => p.path) .reduce((a, b) => { return a.length >= b.length ? a : b; }).length; this.updateDescribe = `${desc}\n\n${this.paths.map(this.describePath, this).join('\n')}`; if (this.paths.find((p) => p.array)) { this.updateDescribe += `\n\n${descArray}`; } } } options(yargs) { return yargs.options({ set: { requiresArg: true, group: 'Update Properties:', //desc: desc + '\n\n' + this.paths.map(this.describePath).join('\n'), desc: this.updateDescribe, multiple: true, demandOption: true, paths: this.paths, }, }); } describePath(p) { var _a; let type = (_a = p.type) !== null && _a !== void 0 ? _a : 'string'; if (p.choices) { type = `{ ${p.choices.map((c) => `'${c}'`).join(' | ')} }`; } let res = `${p.path}${' '.repeat(this.longestProperty - p.path.length + 4)}`; if (p.array) { res += `${type}[]`; } else { res += type; } return res; } builder(yargs) { return this.func(yargs); } async handle(args) { const link = this.resolve.resourceLink(args.ref, this.session.context); const body = await this.client.get(link); const patch = (0, update_1.makePatch)(this.paths, (0, objects_1.toArray)(args.set), this.session.context); const patchRes = await this.client.patch(link, { // add these for optimistic locking version: body.version, id: body.id, // inline the patch ...patch, }); this.session.outFormat(patchRes); } } exports.Update = Update; //# sourceMappingURL=generic.js.map