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
4 changes: 3 additions & 1 deletion src/toolset/api/control.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,9 @@ export class ToolControlAPI {

const response = await client.listToolsetsWithOptions(input, headers || {}, runtime);

logger.debug(`API listToolsets called, Request ID: ${response.body?.requestId}`);
logger.debug(
`API listToolsets called, Request ID: ${response?.headers?.['x-acs-request-id']}`
);

if (!response.body) {
throw new Error('Empty response body');
Expand Down
100 changes: 62 additions & 38 deletions src/toolset/toolset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import { Config } from '../utils/config';
import { logger } from '../utils/log';
import { updateObjectProperties } from '../utils/resource';
import { listAllResourcesFunction, updateObjectProperties } from '../utils/resource';

import {
ToolSetCreateInput,
Expand Down Expand Up @@ -50,6 +50,8 @@ export class ToolSet implements ToolSetData {
return new ToolSetClient();
}

uniqIdCallback = () => this.name;

/**
* Create a new ToolSet
*/
Expand Down Expand Up @@ -77,48 +79,70 @@ export class ToolSet implements ToolSetData {
/**
* List ToolSets
*/
static async list(input?: ToolSetListInput, config?: Config): Promise<ToolSet[]> {
return await ToolSet.getClient().list({ input, config });
}

/**
* List all ToolSets with pagination
*/
static async listAll(
options?: { prefix?: string; labels?: Record<string, string> },
config?: Config
): Promise<ToolSet[]> {
const toolsets: ToolSet[] = [];
const pageSize = 50;

// eslint-disable-next-line no-constant-condition
while (true) {
const result = await ToolSet.list(
{
prefix: options?.prefix,
labels: options?.labels,
pageSize,
},
config
);

toolsets.push(...result);
static list: /**
* @deprecated
*/
| ((input?: ToolSetListInput, config?: Config) => Promise<ToolSet[]>)
/**
* 枚举 ToolSet 列表 / List ToolSet list
*/
| ((params?: { input?: ToolSetListInput; config?: Config }) => Promise<ToolSet[]>) = async (
...args: any
): Promise<ToolSet[]> => {
let input: ToolSetListInput | undefined;
let config: Config | undefined;

if (args.length >= 1 && 'input' in args[0]) {
input = args[0].input;
} else {
input = args[0];
}

if (result.length < pageSize) {
break;
}
if (args.length >= 1 && 'config' in args[0]) {
config = args[0].config;
} else if (args.length > 1 && args[1] instanceof Config) {
config = args[1];
}

// Deduplicate
const seen = new Set<string>();
return toolsets.filter(t => {
if (!t.uid || seen.has(t.uid)) {
return false;
}
seen.add(t.uid);
return true;
return await this.getClient().list({
input: {
...input,
} as ToolSetListInput,
config,
});
}
};

static listAll: /**
* @deprecated
*/
| ((
options?: { prefix?: string; labels?: Record<string, string> },
config?: Config
) => Promise<ToolSet[]>)
/**
* 枚举 ToolSet 列表 / List ToolSet list
*/
| ((params?: { input?: ToolSetListInput; config?: Config }) => Promise<ToolSet[]>) = async (
...args: any
) => {
let input: ToolSetListInput | undefined;
let config: Config | undefined;

if (args.length >= 1 && 'input' in args[0]) {
input = args[0].input;
} else {
input = args[0];
}

if (args.length >= 1 && 'config' in args[0]) {
config = args[0].config;
} else if (args.length > 1 && args[1] instanceof Config) {
config = args[1];
}

return await listAllResourcesFunction(this.list as any)({ ...input, config });
};

/**
* Update a ToolSet by Name
Expand Down
23 changes: 7 additions & 16 deletions tests/unittests/toolset/toolset-resource.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -356,30 +356,19 @@ describe('ToolSet Module', () => {
describe('listAll', () => {
it('should list all toolsets with pagination and deduplication', async () => {
mockToolSetClient.list.mockResolvedValue([
{ name: 'toolset-1', uid: 'uid-1' },
{ name: 'toolset-2', uid: 'uid-2' },
new ToolSet({ name: 'toolset-1', uid: 'uid-1' }),
new ToolSet({ name: 'toolset-2', uid: 'uid-2' }),
]);

const result = await ToolSet.listAll();

expect(result.length).toBeGreaterThanOrEqual(1);
});

it('should deduplicate by uid', async () => {
mockToolSetClient.list.mockResolvedValue([
{ name: 'toolset-1', uid: 'uid-1' },
{ name: 'toolset-1-dup', uid: 'uid-1' }, // Same uid
]);

const result = await ToolSet.listAll();

expect(result).toHaveLength(1);
});

it('should filter out items without uid', async () => {
mockToolSetClient.list.mockResolvedValue([
{ name: 'toolset-1', uid: 'uid-1' },
{ name: 'toolset-no-uid' }, // No uid
new ToolSet({ name: 'toolset-1' }),
new ToolSet({ name: 'toolset-1' }), // No uid
]);

const result = await ToolSet.listAll();
Expand All @@ -389,7 +378,9 @@ describe('ToolSet Module', () => {
});

it('should support prefix and labels options', async () => {
mockToolSetClient.list.mockResolvedValue([{ name: 'my-toolset', uid: 'uid-1' }]);
mockToolSetClient.list.mockResolvedValue([
new ToolSet({ name: 'my-toolset', uid: 'uid-1' }),
]);

const result = await ToolSet.listAll({
prefix: 'my-',
Expand Down
Loading