Skip to content

Commit 9adc529

Browse files
committed
fix pagination, add --limit option for listing snapshots and blueprints
1 parent 8332fba commit 9adc529

7 files changed

Lines changed: 242 additions & 102 deletions

File tree

src/commands/blueprint/list.tsx

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -984,6 +984,7 @@ const ListBlueprintsUI = ({
984984

985985
interface ListBlueprintsOptions {
986986
name?: string;
987+
limit?: string;
987988
output?: string;
988989
}
989990

@@ -994,23 +995,45 @@ export async function listBlueprints(options: ListBlueprintsOptions = {}) {
994995
try {
995996
const client = getClient();
996997

997-
// Build query params
998-
const queryParams: Record<string, unknown> = {
999-
limit: DEFAULT_PAGE_SIZE,
1000-
};
1001-
if (options.name) {
1002-
queryParams.name = options.name;
1003-
}
998+
const maxResults = options.limit ? parseInt(options.limit, 10) : Infinity;
999+
const allBlueprints: unknown[] = [];
1000+
let startingAfter: string | undefined;
10041001

1005-
// Fetch blueprints
1006-
const page = (await client.blueprints.list(
1007-
queryParams,
1008-
)) as BlueprintsCursorIDPage<{ id: string }>;
1002+
do {
1003+
const remaining = maxResults - allBlueprints.length;
1004+
// Build query params
1005+
const queryParams: Record<string, unknown> = {
1006+
limit: Math.min(DEFAULT_PAGE_SIZE, remaining),
1007+
};
1008+
if (options.name) {
1009+
queryParams.name = options.name;
1010+
}
1011+
if (startingAfter) {
1012+
queryParams.starting_after = startingAfter;
1013+
}
10091014

1010-
// Extract blueprints array
1011-
const blueprints = page.blueprints || [];
1015+
// Fetch one page
1016+
const page = (await client.blueprints.list(
1017+
queryParams,
1018+
)) as BlueprintsCursorIDPage<{ id: string }>;
1019+
1020+
const pageBlueprints = page.blueprints || [];
1021+
allBlueprints.push(...pageBlueprints);
1022+
1023+
if (
1024+
page.has_more &&
1025+
pageBlueprints.length > 0 &&
1026+
allBlueprints.length < maxResults
1027+
) {
1028+
startingAfter = (
1029+
pageBlueprints[pageBlueprints.length - 1] as { id: string }
1030+
).id;
1031+
} else {
1032+
startingAfter = undefined;
1033+
}
1034+
} while (startingAfter !== undefined);
10121035

1013-
output(blueprints, { format: options.output, defaultFormat: "json" });
1036+
output(allBlueprints, { format: options.output, defaultFormat: "json" });
10141037
} catch (error) {
10151038
outputError("Failed to list blueprints", error);
10161039
}

src/commands/devbox/list.tsx

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -798,23 +798,45 @@ export async function listDevboxes(options: ListOptions) {
798798
try {
799799
const client = getClient();
800800

801-
// Build query params
802-
const queryParams: Record<string, unknown> = {
803-
limit: options.limit ? parseInt(options.limit, 10) : DEFAULT_PAGE_SIZE,
804-
};
805-
if (options.status) {
806-
queryParams.status = options.status;
807-
}
801+
const maxResults = options.limit ? parseInt(options.limit, 10) : Infinity;
802+
const allDevboxes: unknown[] = [];
803+
let startingAfter: string | undefined;
808804

809-
// Fetch devboxes
810-
const page = (await client.devboxes.list(
811-
queryParams,
812-
)) as DevboxesCursorIDPage<{ id: string }>;
805+
do {
806+
const remaining = maxResults - allDevboxes.length;
807+
// Build query params
808+
const queryParams: Record<string, unknown> = {
809+
limit: Math.min(DEFAULT_PAGE_SIZE, remaining),
810+
};
811+
if (options.status) {
812+
queryParams.status = options.status;
813+
}
814+
if (startingAfter) {
815+
queryParams.starting_after = startingAfter;
816+
}
813817

814-
// Extract devboxes array
815-
const devboxes = page.devboxes || [];
818+
// Fetch one page
819+
const page = (await client.devboxes.list(
820+
queryParams,
821+
)) as DevboxesCursorIDPage<{ id: string }>;
822+
823+
const pageDevboxes = page.devboxes || [];
824+
allDevboxes.push(...pageDevboxes);
825+
826+
if (
827+
page.has_more &&
828+
pageDevboxes.length > 0 &&
829+
allDevboxes.length < maxResults
830+
) {
831+
startingAfter = (
832+
pageDevboxes[pageDevboxes.length - 1] as { id: string }
833+
).id;
834+
} else {
835+
startingAfter = undefined;
836+
}
837+
} while (startingAfter !== undefined);
816838

817-
output(devboxes, { format: options.output, defaultFormat: "json" });
839+
output(allDevboxes, { format: options.output, defaultFormat: "json" });
818840
} catch (error) {
819841
outputError("Failed to list devboxes", error);
820842
}

src/commands/gateway-config/list.tsx

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { ConfirmationPrompt } from "../../components/ConfirmationPrompt.js";
2626

2727
interface ListOptions {
2828
name?: string;
29+
limit?: string;
2930
output?: string;
3031
}
3132

@@ -746,23 +747,44 @@ export async function listGatewayConfigs(options: ListOptions = {}) {
746747
try {
747748
const client = getClient();
748749

749-
// Build query params
750-
const queryParams: Record<string, unknown> = {
751-
limit: DEFAULT_PAGE_SIZE,
752-
};
753-
if (options.name) {
754-
queryParams.name = options.name;
755-
}
750+
const maxResults = options.limit ? parseInt(options.limit, 10) : Infinity;
751+
const allConfigs: unknown[] = [];
752+
let startingAfter: string | undefined;
756753

757-
// Fetch gateway configs
758-
const page = (await client.gatewayConfigs.list(
759-
queryParams,
760-
)) as GatewayConfigsCursorIDPage<{ id: string }>;
754+
do {
755+
const remaining = maxResults - allConfigs.length;
756+
// Build query params
757+
const queryParams: Record<string, unknown> = {
758+
limit: Math.min(DEFAULT_PAGE_SIZE, remaining),
759+
};
760+
if (options.name) {
761+
queryParams.name = options.name;
762+
}
763+
if (startingAfter) {
764+
queryParams.starting_after = startingAfter;
765+
}
761766

762-
// Extract gateway configs array
763-
const gatewayConfigs = page.gateway_configs || [];
767+
// Fetch one page
768+
const page = (await client.gatewayConfigs.list(
769+
queryParams,
770+
)) as GatewayConfigsCursorIDPage<{ id: string }>;
771+
772+
const pageConfigs = page.gateway_configs || [];
773+
allConfigs.push(...pageConfigs);
774+
775+
if (
776+
page.has_more &&
777+
pageConfigs.length > 0 &&
778+
allConfigs.length < maxResults
779+
) {
780+
startingAfter = (pageConfigs[pageConfigs.length - 1] as { id: string })
781+
.id;
782+
} else {
783+
startingAfter = undefined;
784+
}
785+
} while (startingAfter !== undefined);
764786

765-
output(gatewayConfigs, { format: options.output, defaultFormat: "json" });
787+
output(allConfigs, { format: options.output, defaultFormat: "json" });
766788
} catch (error) {
767789
outputError("Failed to list gateway configs", error);
768790
}

src/commands/network-policy/list.tsx

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { ConfirmationPrompt } from "../../components/ConfirmationPrompt.js";
2626

2727
interface ListOptions {
2828
name?: string;
29+
limit?: string;
2930
output?: string;
3031
}
3132

@@ -775,23 +776,45 @@ export async function listNetworkPolicies(options: ListOptions = {}) {
775776
try {
776777
const client = getClient();
777778

778-
// Build query params
779-
const queryParams: Record<string, unknown> = {
780-
limit: DEFAULT_PAGE_SIZE,
781-
};
782-
if (options.name) {
783-
queryParams.name = options.name;
784-
}
779+
const maxResults = options.limit ? parseInt(options.limit, 10) : Infinity;
780+
const allPolicies: unknown[] = [];
781+
let startingAfter: string | undefined;
785782

786-
// Fetch network policies
787-
const page = (await client.networkPolicies.list(
788-
queryParams,
789-
)) as NetworkPoliciesCursorIDPage<{ id: string }>;
783+
do {
784+
const remaining = maxResults - allPolicies.length;
785+
// Build query params
786+
const queryParams: Record<string, unknown> = {
787+
limit: Math.min(DEFAULT_PAGE_SIZE, remaining),
788+
};
789+
if (options.name) {
790+
queryParams.name = options.name;
791+
}
792+
if (startingAfter) {
793+
queryParams.starting_after = startingAfter;
794+
}
790795

791-
// Extract network policies array
792-
const networkPolicies = page.network_policies || [];
796+
// Fetch one page
797+
const page = (await client.networkPolicies.list(
798+
queryParams,
799+
)) as NetworkPoliciesCursorIDPage<{ id: string }>;
800+
801+
const pagePolicies = page.network_policies || [];
802+
allPolicies.push(...pagePolicies);
803+
804+
if (
805+
page.has_more &&
806+
pagePolicies.length > 0 &&
807+
allPolicies.length < maxResults
808+
) {
809+
startingAfter = (
810+
pagePolicies[pagePolicies.length - 1] as { id: string }
811+
).id;
812+
} else {
813+
startingAfter = undefined;
814+
}
815+
} while (startingAfter !== undefined);
793816

794-
output(networkPolicies, { format: options.output, defaultFormat: "json" });
817+
output(allPolicies, { format: options.output, defaultFormat: "json" });
795818
} catch (error) {
796819
outputError("Failed to list network policies", error);
797820
}

src/commands/object/list.tsx

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ interface ListOptions {
3030
contentType?: string;
3131
state?: string;
3232
public?: boolean;
33+
limit?: string;
3334
output?: string;
3435
}
3536

@@ -817,30 +818,53 @@ export async function listObjects(options: ListOptions) {
817818
try {
818819
const client = getClient();
819820

820-
// Build query params
821-
const queryParams: Record<string, unknown> = {
822-
limit: DEFAULT_PAGE_SIZE,
823-
};
824-
if (options.name) {
825-
queryParams.name = options.name;
826-
}
827-
if (options.contentType) {
828-
queryParams.content_type = options.contentType;
829-
}
830-
if (options.state) {
831-
queryParams.state = options.state;
832-
}
833-
if (options.public !== undefined) {
834-
queryParams.is_public = options.public;
835-
}
821+
const maxResults = options.limit ? parseInt(options.limit, 10) : Infinity;
822+
const allObjects: unknown[] = [];
823+
let startingAfter: string | undefined;
836824

837-
// Fetch objects
838-
const result = await client.objects.list(queryParams);
825+
do {
826+
const remaining = maxResults - allObjects.length;
827+
// Build query params
828+
const queryParams: Record<string, unknown> = {
829+
limit: Math.min(DEFAULT_PAGE_SIZE, remaining),
830+
};
831+
if (options.name) {
832+
queryParams.name = options.name;
833+
}
834+
if (options.contentType) {
835+
queryParams.content_type = options.contentType;
836+
}
837+
if (options.state) {
838+
queryParams.state = options.state;
839+
}
840+
if (options.public !== undefined) {
841+
queryParams.is_public = options.public;
842+
}
843+
if (startingAfter) {
844+
queryParams.starting_after = startingAfter;
845+
}
839846

840-
// Extract objects array
841-
const objects = result.objects || [];
847+
// Fetch one page
848+
const result = await client.objects.list(queryParams);
849+
const pageResult = result as unknown as {
850+
objects?: { id: string }[];
851+
has_more?: boolean;
852+
};
853+
const pageObjects = pageResult.objects || [];
854+
allObjects.push(...pageObjects);
855+
856+
if (
857+
pageResult.has_more &&
858+
pageObjects.length > 0 &&
859+
allObjects.length < maxResults
860+
) {
861+
startingAfter = pageObjects[pageObjects.length - 1].id;
862+
} else {
863+
startingAfter = undefined;
864+
}
865+
} while (startingAfter !== undefined);
842866

843-
output(objects, { format: options.output, defaultFormat: "json" });
867+
output(allObjects, { format: options.output, defaultFormat: "json" });
844868
} catch (error) {
845869
outputError("Failed to list storage objects", error);
846870
}

0 commit comments

Comments
 (0)