Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ Avatar URL: 'https://app.box.com/api/avatar/large/77777'
* [`box folders`](docs/folders.md) - Manage folders
* [`box groups`](docs/groups.md) - List all groups
* [`box help`](docs/help.md) - Display help for the Box CLI
* [`box hubs`](docs/hubs.md) - List Box Hubs for the current user
* [`box integration-mappings`](docs/integration-mappings.md) - List Slack integration mappings
* [`box legal-hold-policies`](docs/legal-hold-policies.md) - List legal hold policies
* [`box login`](docs/login.md) - Sign in with OAuth 2.0 and create a new environment (or update an existing one with --reauthorize).
Expand Down
4 changes: 2 additions & 2 deletions docs/files.md
Original file line number Diff line number Diff line change
Expand Up @@ -833,20 +833,20 @@ Update the metadata attached to a file
USAGE
$ box files:metadata:update ID --template-key <value> [-t <value>] [--as-user <value>] [--no-color] [--json | --csv] [-s
| --save-to-file-path <value>] [--fields <value>] [--bulk-file-path <value>] [-h] [-v] [-y] [-q] [--scope <value>]
[-a <value>...] [-c <value>...] [-m <value>...] [--remove <value>...] [--replace <value>...] [-t <value>...]
[-a <value>...] [-c <value>...] [-m <value>...] [--remove <value>...] [--replace <value>...] [-T <value>...]
ARGUMENTS
ID ID of the file to update metadata on
FLAGS
-T, --test=<value>... Test that a metadata key contains a specific value; must be in the form key=value
-a, --add=<value>... Add a key to the metadata document; must be in the form key=value
-c, --copy=<value>... Copy a metadata value to another key; must be in the form sourceKey>destinationKey
-h, --help Show CLI help
-m, --move=<value>... Move a metadata value from one key to another; must be in the form
sourceKey>destinationKey
-q, --quiet Suppress any non-error output to stderr
-s, --save Save report to default reports folder on disk
-t, --test=<value>... Test that a metadata key contains a specific value; must be in the form key=value
-t, --token=<value> Provide a token to perform this call
-v, --verbose Show verbose output, which can be helpful for debugging
-y, --yes Automatically respond yes to all confirmation prompts
Expand Down
4 changes: 2 additions & 2 deletions docs/folders.md
Original file line number Diff line number Diff line change
Expand Up @@ -1018,20 +1018,20 @@ Update the metadata attached to a folder
USAGE
$ box folders:metadata:update ID --template-key <value> [-t <value>] [--as-user <value>] [--no-color] [--json | --csv] [-s
| --save-to-file-path <value>] [--fields <value>] [--bulk-file-path <value>] [-h] [-v] [-y] [-q] [--scope <value>]
[-a <value>...] [-c <value>...] [-m <value>...] [--remove <value>...] [--replace <value>...] [-t <value>...]
[-a <value>...] [-c <value>...] [-m <value>...] [--remove <value>...] [--replace <value>...] [-T <value>...]

ARGUMENTS
ID ID of the folder to update metadata on

FLAGS
-T, --test=<value>... Test that a metadata key contains a specific value; must be in the form key=value
-a, --add=<value>... Add a key to the metadata document; must be in the form key=value
-c, --copy=<value>... Copy a metadata value to another key; must be in the form sourceKey>destinationKey
-h, --help Show CLI help
-m, --move=<value>... Move a metadata value from one key to another; must be in the form
sourceKey>destinationKey
-q, --quiet Suppress any non-error output to stderr
-s, --save Save report to default reports folder on disk
-t, --test=<value>... Test that a metadata key contains a specific value; must be in the form key=value
-t, --token=<value> Provide a token to perform this call
-v, --verbose Show verbose output, which can be helpful for debugging
-y, --yes Automatically respond yes to all confirmation prompts
Expand Down
355 changes: 355 additions & 0 deletions docs/hubs.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions docs/tokens.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@ Get a token. Returns the service account token by default

```
USAGE
$ box tokens:exchange SCOPE [--no-color] [-h] [-v] [-q] [-u <value> | -t <value>] [--file-id <value> | --folder-id
$ box tokens:exchange SCOPE [--no-color] [-h] [-v] [-q] [-u <value> | -T <value>] [--file-id <value> | --folder-id
<value>]

ARGUMENTS
SCOPE The scope(s) for the new token, separated by a comma if multiple are present

FLAGS
-T, --token=<value> Specify the token to exchange
-h, --help Show CLI help
-q, --quiet Suppress any non-error output to stderr
-t, --token=<value> Specify the token to exchange
-u, --user-id=<value> Get a user token from a user ID
-v, --verbose Show verbose output, which can be helpful for debugging
--file-id=<value> Scope the token to a specific file
Expand Down
10 changes: 5 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"@oclif/table": "^0.4.14",
"@octokit/rest": "^22.0.0",
"archiver": "^3.0.0",
"box-node-sdk": "^4.3.0",
"box-node-sdk": "^4.6.0",
"chalk": "^2.4.1",
"cli-progress": "^2.1.0",
"csv": "^6.3.3",
Expand Down
53 changes: 53 additions & 0 deletions src/box-command.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const BoxTSSDK = require('box-node-sdk/sdk-gen');
const BoxTsErrors = require('box-node-sdk/sdk-gen/box/errors');
const BoxCLIError = require('./cli-error');
const CLITokenCache = require('./token-cache');
const PaginationUtilities = require('./pagination-utils');
const utils = require('./util');
const pkg = require('../package.json');
const inquirer = require('./inquirer');
Expand Down Expand Up @@ -1283,6 +1284,58 @@ class BoxCommand extends Command {
return maxItems && itemsCount >= maxItems;
}

/**
* Fetch all marker-based pages from a TypeScript SDK endpoint.
*
* @param {Function} fetchPage Callback that receives query params and returns a paged response
* @param {Object} queryParams Base query params for each request
* @param {number} [maxItemsOverride] Optional max items override for advanced use
* @returns {Promise<Object[]>} Aggregated marker-based response entries
*/
async markerPagination(fetchPage, queryParams, maxItemsOverride) {
const normalizedQueryParams = queryParams || {};
const paginationFlags = {
'max-items':
maxItemsOverride === undefined
? this.flags['max-items']
: maxItemsOverride,
};
const paginationOptions =
PaginationUtilities.handlePagination(paginationFlags);
const maxItems =
paginationFlags['max-items'] === undefined
? paginationOptions.limit
: paginationFlags['max-items'];

let remaining = maxItems;
const entries = [];
const pageLimit = paginationOptions.limit;
let marker;

while (remaining > 0) {
const pageQueryParams = {
...normalizedQueryParams,
limit: Math.min(pageLimit, remaining),
};

if (marker) {
pageQueryParams.marker = marker;
}

const page = await fetchPage(pageQueryParams);
const pageEntries = page.entries || [];
entries.push(...pageEntries);
remaining -= pageEntries.length;
marker = page.nextMarker || page.next_marker;

if (!marker || pageEntries.length === 0) {
break;
}
}

return entries;
}

/**
* Prepare the output data by:
* 1) Unrolling an iterator into an array
Expand Down
2 changes: 1 addition & 1 deletion src/commands/files/metadata/update.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ FilesUpdateMetadataCommand.flags = {
parse: utilities.parseMetadataOp,
}),
test: Flags.string({
char: 't',
char: 'T',
description:
'Test that a metadata key contains a specific value; must be in the form key=value',
multiple: true,
Expand Down
2 changes: 1 addition & 1 deletion src/commands/folders/metadata/update.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ FoldersUpdateMetadataCommand.flags = {
parse: utilities.parseMetadataOp,
}),
test: Flags.string({
char: 't',
char: 'T',
description:
'Test that a metadata key contains a specific value; must be in the form key=value',
multiple: true,
Expand Down
53 changes: 53 additions & 0 deletions src/commands/hubs/copy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
'use strict';

const BoxCommand = require('../../box-command');
const { Flags, Args } = require('@oclif/core');
const utilities = require('../../util');

class HubsCopyCommand extends BoxCommand {
async run() {
const { flags, args } = await this.parse(HubsCopyCommand);
const requestBody = {};

if (flags.title) {
requestBody.title = flags.title;
}
if (flags.description) {
requestBody.description = flags.description;
}

const hub = await this.tsClient.hubs.copyHubV2025R0(args.id, requestBody);
delete hub.rawData;
await this.output(hub);
}
}

HubsCopyCommand.description = 'Copy a Box Hub';
HubsCopyCommand.examples = [
'box hubs:copy 12345 --title "Copied hub title" --description "Copied hub description"',
];
HubsCopyCommand._endpoint = 'post_hubs_id_copy';

HubsCopyCommand.flags = {
...BoxCommand.flags,
title: Flags.string({
char: 'T',
description: 'Optional title override for the copied hub',
}),
description: Flags.string({
char: 'd',
description: 'Optional description override for the copied hub',
parse: utilities.unescapeSlashes,
}),
};

HubsCopyCommand.args = {
id: Args.string({
name: 'id',
required: true,
hidden: false,
description: 'ID of the Box Hub to copy',
}),
};

module.exports = HubsCopyCommand;
46 changes: 46 additions & 0 deletions src/commands/hubs/create.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use strict';

const BoxCommand = require('../../box-command');
const { Flags, Args } = require('@oclif/core');
const utilities = require('../../util');

class HubsCreateCommand extends BoxCommand {
async run() {
const { flags, args } = await this.parse(HubsCreateCommand);
const requestBody = {
title: args.title,
};

if (flags.description) {
requestBody.description = flags.description;
}

const hub = await this.tsClient.hubs.createHubV2025R0(requestBody);
delete hub.rawData;
await this.output(hub);
}
}

HubsCreateCommand.description = 'Create a new Box Hub';
HubsCreateCommand.examples = ['box hubs:create "Roadmap Hub" --description "Q3 planning hub"'];
HubsCreateCommand._endpoint = 'post_hubs';

HubsCreateCommand.flags = {
...BoxCommand.flags,
description: Flags.string({
char: 'd',
description: 'Description of the Box Hub',
parse: utilities.unescapeSlashes,
}),
};

HubsCreateCommand.args = {
title: Args.string({
name: 'title',
required: true,
hidden: false,
description: 'Title for the Box Hub',
}),
};

module.exports = HubsCreateCommand;
31 changes: 31 additions & 0 deletions src/commands/hubs/delete.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
'use strict';

const { Args } = require('@oclif/core');
const BoxCommand = require('../../box-command');

class HubsDeleteCommand extends BoxCommand {
async run() {
const { args } = await this.parse(HubsDeleteCommand);
await this.tsClient.hubs.deleteHubByIdV2025R0(args.id);
this.info(`Deleted hub ${args.id}`);
}
}

HubsDeleteCommand.description = 'Delete a Box Hub';
HubsDeleteCommand.examples = ['box hubs:delete 12345'];
HubsDeleteCommand._endpoint = 'delete_hubs_id';

HubsDeleteCommand.flags = {
...BoxCommand.flags,
};

HubsDeleteCommand.args = {
id: Args.string({
name: 'id',
required: true,
hidden: false,
description: 'ID of the Box Hub to delete',
}),
};

module.exports = HubsDeleteCommand;
62 changes: 62 additions & 0 deletions src/commands/hubs/enterprise.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
'use strict';

const BoxCommand = require('../../box-command');
const { Flags } = require('@oclif/core');
const PaginationUtilities = require('../../pagination-utils');

class HubsEnterpriseListCommand extends BoxCommand {
async run() {
const { flags } = await this.parse(HubsEnterpriseListCommand);
const queryParams = {};

if (flags.query) {
queryParams.query = flags.query;
}
if (flags.sort) {
queryParams.sort = flags.sort;
}
if (flags.direction) {
queryParams.direction = flags.direction;
}

const hubs = await this.markerPagination(
(pageQueryParams) =>
this.tsClient.hubs.getEnterpriseHubsV2025R0(pageQueryParams),
queryParams
);
await this.output(hubs);
}
}

HubsEnterpriseListCommand.description =
'List Box Hubs across the enterprise. This call requires an admin or hub co-admin of an enterprise with GCM scope. Otherwise, Box returns a 403 status code with the message `The request requires higher privileges than provided by the access token.` See https://developer.box.com/guides/api-calls/permissions-and-errors/scopes#global-content-manager-gcm';
HubsEnterpriseListCommand.examples = [
'box hubs:enterprise',
'box hubs:enterprise --query "Roadmap" --sort updated_at --direction DESC',
];
HubsEnterpriseListCommand._endpoint = 'get_enterprise_hubs';

HubsEnterpriseListCommand.flags = {
...BoxCommand.flags,
...PaginationUtilities.flags,
query: Flags.string({
description: 'Search query for enterprise Box Hubs',
}),
sort: Flags.string({
description:
'Sort field for hubs. One of: name, updated_at, last_accessed_at, view_count, relevance',
options: [
'name',
'updated_at',
'last_accessed_at',
'view_count',
'relevance',
],
}),
direction: Flags.string({
description: 'Sort direction. One of: ASC, DESC',
options: ['ASC', 'DESC'],
}),
};

module.exports = HubsEnterpriseListCommand;
Loading
Loading