Skip to content

Commit

Permalink
[cli] Allow ls to specify a limit up to 100 (vercel#8735)
Browse files Browse the repository at this point in the history
### Related Issues

Allow the `ls` commands to have a `--limit` option that allows a user to fetch up to 100 records per page. Currently the default is 20, but the API allows up to 100. This keeps the default, if unspecified at 20. 

Fixes: https://linear.app/vercel/issue/VCCLI-244/add-limit-option-all-the-ls-subcommands

This adds in `ls --limit` into:
- [x] `alias ls`
- [x] `certs ls`
- [x] `dns ls`
- [x] `domains ls`

I note that `env` has an `ls` command, but it doesn't have a pagination command and [looking at the API](https://vercel.com/docs/rest-api#endpoints/projects/retrieve-the-environment-variables-of-a-project-by-id-or-name) it doesn't support pagination or limit.

Wasn't sure if I should add in `-L` as a short cut to `--limit`, seems like a good idea.

~Couldn't find any tests that cover this API, but I could be looking in the wrong place, this is the first pull request, so my apologies if I've missed it. But I'll take another look tomorrow and leave it in the draft state for now.~

Added in unit tests for each of the commands and mocks for those unit tests, which is has caused this PR to get quite a bit larger, sorry about that.

Of note for reviewers, there were a few places where I changed `console.log` to `output.log` to get the output passed to the tests - as far as I can tell everything works on the command line and in tests.

### 📋 Checklist


#### Tests

- [x] The code changed/added as part of this PR has been covered with tests
- [x] All tests pass locally with `yarn test-unit`

#### Code Review

- [x] This PR has a concise title and thorough description useful to a reviewer
- [x] Issue from task tracker has a link to this PR
  • Loading branch information
Andy McKay authored Dec 7, 2022
1 parent cd487bc commit 9b92a81
Show file tree
Hide file tree
Showing 22 changed files with 366 additions and 54 deletions.
4 changes: 4 additions & 0 deletions packages/cli/src/commands/alias/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ const help = () => {
-S, --scope Set a custom scope
-N, --next Show next page of results
-y, --yes Skip the confirmation prompt when removing an alias
--limit=${chalk.bold.underline(
'VALUE'
)} Number of results to return per page (default: 20, max: 100)
${chalk.dim('Examples:')}
Expand Down Expand Up @@ -81,6 +84,7 @@ export default async function main(client: Client) {
'--json': Boolean,
'--yes': Boolean,
'--next': Number,
'--limit': Number,
'-y': '--yes',
'-N': '--next',
});
Expand Down
24 changes: 13 additions & 11 deletions packages/cli/src/commands/alias/ls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,30 @@ import table from 'text-table';
import Client from '../../util/client';
import getAliases from '../../util/alias/get-aliases';
import getScope from '../../util/get-scope';
import {
PaginationOptions,
getPaginationOpts,
} from '../../util/get-pagination-opts';
import stamp from '../../util/output/stamp';
import strlen from '../../util/strlen';
import getCommandFlags from '../../util/get-command-flags';
import { getCommandName } from '../../util/pkg-name';

import { Alias } from '../../types';

interface Options {
'--next'?: number;
}

export default async function ls(
client: Client,
opts: Options,
opts: PaginationOptions,
args: string[]
) {
const { output } = client;
const { '--next': nextTimestamp } = opts;
const { contextName } = await getScope(client);

if (typeof nextTimestamp !== undefined && Number.isNaN(nextTimestamp)) {
output.error('Please provide a number for flag --next');
let paginationOptions;

try {
paginationOptions = getPaginationOpts(opts);
} catch (err: unknown) {
output.prettyError(err);
return 1;
}

Expand All @@ -46,10 +48,10 @@ export default async function ls(
const { aliases, pagination } = await getAliases(
client,
undefined,
nextTimestamp
...paginationOptions
);
output.log(`aliases found under ${chalk.bold(contextName)} ${lsStamp()}`);
console.log(printAliasTable(aliases));
output.log(printAliasTable(aliases));

if (pagination && pagination.count === 20) {
const flags = getCommandFlags(opts, ['_', '--next']);
Expand Down
4 changes: 4 additions & 0 deletions packages/cli/src/commands/certs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ const help = () => {
'FILE'
)} CA certificate chain file
-N, --next Show next page of results
--limit=${chalk.bold.underline(
'VALUE'
)} Number of results to return per page (default: 20, max: 100)
${chalk.dim('Examples:')}
Expand Down Expand Up @@ -92,6 +95,7 @@ export default async function main(client: Client) {
'--ca': String,
'--next': Number,
'-N': '--next',
'--limit': Number,
});
} catch (err) {
handleError(err);
Expand Down
29 changes: 17 additions & 12 deletions packages/cli/src/commands/certs/ls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,34 @@ import ms from 'ms';
import table from 'text-table';
import Client from '../../util/client';
import getScope from '../../util/get-scope';
import {
PaginationOptions,
getPaginationOpts,
} from '../../util/get-pagination-opts';
import stamp from '../../util/output/stamp';
import getCerts from '../../util/certs/get-certs';
import strlen from '../../util/strlen';
import { Cert } from '../../types';
import getCommandFlags from '../../util/get-command-flags';
import { getCommandName } from '../../util/pkg-name';

interface Options {
'--next'?: number;
}

async function ls(
client: Client,
opts: Options,
opts: PaginationOptions,
args: string[]
): Promise<number> {
const { output } = client;
const { '--next': nextTimestamp } = opts;
const { contextName } = await getScope(client);

if (typeof nextTimestamp !== 'undefined' && Number.isNaN(nextTimestamp)) {
output.error('Please provide a number for flag --next');
let paginationOptions;

try {
paginationOptions = getPaginationOpts(opts);
} catch (err: unknown) {
output.prettyError(err);
return 1;
}

const lsStamp = stamp();

if (args.length !== 0) {
Expand All @@ -39,9 +43,10 @@ async function ls(
}

// Get the list of certificates
const { certs, pagination } = await getCerts(client, nextTimestamp).catch(
err => err
);
const { certs, pagination } = await getCerts(
client,
...paginationOptions
).catch(err => err);

output.log(
`${
Expand All @@ -50,7 +55,7 @@ async function ls(
);

if (certs.length > 0) {
console.log(formatCertsTable(certs));
output.log(formatCertsTable(certs));
}

if (pagination && pagination.count === 20) {
Expand Down
9 changes: 8 additions & 1 deletion packages/cli/src/commands/dns/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ const help = () => {
)} Login token
-S, --scope Set a custom scope
-N, --next Show next page of results
--limit=${chalk.bold.underline(
'VALUE'
)} Number of results to return per page (default: 20, max: 100)
${chalk.dim('Examples:')}
Expand Down Expand Up @@ -100,7 +103,11 @@ export default async function main(client: Client) {
let argv;

try {
argv = getArgs(client.argv.slice(2), { '--next': Number, '-N': '--next' });
argv = getArgs(client.argv.slice(2), {
'--next': Number,
'-N': '--next',
'--limit': Number,
});
} catch (error) {
handleError(error);
return 1;
Expand Down
29 changes: 16 additions & 13 deletions packages/cli/src/commands/dns/ls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,20 @@ import getDNSRecords, {
} from '../../util/dns/get-dns-records';
import getDomainDNSRecords from '../../util/dns/get-domain-dns-records';
import getScope from '../../util/get-scope';
import {
PaginationOptions,
getPaginationOpts,
} from '../../util/get-pagination-opts';
import stamp from '../../util/output/stamp';
import getCommandFlags from '../../util/get-command-flags';
import { getCommandName } from '../../util/pkg-name';

type Options = {
'--next'?: number;
};

export default async function ls(
client: Client,
opts: Options,
opts: PaginationOptions,
args: string[]
) {
const { output } = client;
const { '--next': nextTimestamp } = opts;
const { contextName } = await getScope(client);

const [domainName] = args;
Expand All @@ -38,8 +37,12 @@ export default async function ls(
return 1;
}

if (typeof nextTimestamp !== 'undefined' && Number.isNaN(nextTimestamp)) {
output.error('Please provide a number for flag --next');
let paginationOptions;

try {
paginationOptions = getPaginationOpts(opts);
} catch (err: unknown) {
output.prettyError(err);
return 1;
}

Expand All @@ -48,8 +51,8 @@ export default async function ls(
output,
client,
domainName,
nextTimestamp,
4
4,
...paginationOptions
);
if (data instanceof DomainNotFound) {
output.error(
Expand All @@ -67,7 +70,7 @@ export default async function ls(
records.length > 0 ? 'Records' : 'No records'
} found under ${chalk.bold(contextName)} ${chalk.gray(lsStamp())}`
);
console.log(getDNSRecordsTable([{ domainName, records }]));
output.log(getDNSRecordsTable([{ domainName, records }]));

if (pagination && pagination.count === 20) {
const flags = getCommandFlags(opts, ['_', '--next']);
Expand All @@ -85,15 +88,15 @@ export default async function ls(
output,
client,
contextName,
nextTimestamp
...paginationOptions
);
const nRecords = dnsRecords.reduce((p, r) => r.records.length + p, 0);
output.log(
`${nRecords > 0 ? 'Records' : 'No records'} found under ${chalk.bold(
contextName
)} ${chalk.gray(lsStamp())}`
);
console.log(getDNSRecordsTable(dnsRecords));
output.log(getDNSRecordsTable(dnsRecords));
if (pagination && pagination.count === 20) {
const flags = getCommandFlags(opts, ['_', '--next']);
output.log(
Expand Down
4 changes: 4 additions & 0 deletions packages/cli/src/commands/domains/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ const help = () => {
)} Login token
-S, --scope Set a custom scope
-N, --next Show next page of results
--limit=${chalk.bold.underline(
'VALUE'
)} Number of results to return per page (default: 20, max: 100)
-y, --yes Skip the confirmation prompt when removing a domain
${chalk.dim('Examples:')}
Expand Down Expand Up @@ -94,6 +97,7 @@ export default async function main(client: Client) {
'--next': Number,
'-N': '--next',
'-y': '--yes',
'--limit': Number,
});
} catch (error) {
handleError(error);
Expand Down
24 changes: 15 additions & 9 deletions packages/cli/src/commands/domains/ls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,27 @@ import formatTable from '../../util/format-table';
import { formatDateWithoutTime } from '../../util/format-date';
import { Domain } from '../../types';
import getCommandFlags from '../../util/get-command-flags';
import {
PaginationOptions,
getPaginationOpts,
} from '../../util/get-pagination-opts';
import { getCommandName } from '../../util/pkg-name';
import isDomainExternal from '../../util/domains/is-domain-external';
import { getDomainRegistrar } from '../../util/domains/get-domain-registrar';

type Options = {
'--next': number;
};

export default async function ls(
client: Client,
opts: Partial<Options>,
opts: Partial<PaginationOptions>,
args: string[]
) {
const { output } = client;
const { '--next': nextTimestamp } = opts;

if (typeof nextTimestamp !== undefined && Number.isNaN(nextTimestamp)) {
output.error('Please provide a number for flag --next');
let paginationOptions;

try {
paginationOptions = getPaginationOpts(opts);
} catch (err: unknown) {
output.prettyError(err);
return 1;
}

Expand All @@ -46,7 +49,10 @@ export default async function ls(

output.spinner(`Fetching Domains under ${chalk.bold(contextName)}`);

const { domains, pagination } = await getDomains(client, nextTimestamp);
const { domains, pagination } = await getDomains(
client,
...paginationOptions
);

output.log(
`${plural('Domain', domains.length, true)} found under ${chalk.bold(
Expand Down
6 changes: 4 additions & 2 deletions packages/cli/src/util/alias/get-aliases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ type Response = {
export default async function getAliases(
client: Client,
deploymentId?: string,
next?: number
next?: number,
limit = 20
) {
let aliasUrl = `/v3/now/aliases?limit=20`;
let aliasUrl = `/v3/now/aliases?limit=${limit}`;
if (next) {
aliasUrl += `&until=${next}`;
}

const to = deploymentId
? `/now/deployments/${deploymentId}/aliases`
: aliasUrl;
Expand Down
8 changes: 6 additions & 2 deletions packages/cli/src/util/certs/get-certs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ type Response = {
pagination: PaginationOptions;
};

export default async function getCerts(client: Client, next?: number) {
let certsUrl = `/v4/now/certs?limit=20`;
export default async function getCerts(
client: Client,
next?: number,
limit = 20
) {
let certsUrl = `/v4/now/certs?limit=${limit}`;

if (next) {
certsUrl += `&until=${next}`;
Expand Down
5 changes: 3 additions & 2 deletions packages/cli/src/util/dns/get-domain-dns-records.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ export default async function getDomainDNSRecords(
output: Output,
client: Client,
domain: string,
apiVersion = 3,
nextTimestamp?: number,
apiVersion = 3
limit = 20
) {
output.debug(`Fetching for DNS records of domain ${domain}`);
try {
let url = `/v${apiVersion}/domains/${encodeURIComponent(
domain
)}/records?limit=20`;
)}/records?limit=${limit}`;

if (nextTimestamp) {
url += `&until=${nextTimestamp}`;
Expand Down
8 changes: 6 additions & 2 deletions packages/cli/src/util/domains/get-domains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ type Response = {
pagination: PaginationOptions;
};

export default async function getDomains(client: Client, next?: number) {
let domainUrl = `/v5/domains?limit=20`;
export default async function getDomains(
client: Client,
next?: number,
limit = 20
) {
let domainUrl = `/v5/domains?limit=${limit}`;
if (next) {
domainUrl += `&until=${next}`;
}
Expand Down
Loading

0 comments on commit 9b92a81

Please sign in to comment.