From 1a8e210dc874ea87a6cd60a1c18dc4e64b323294 Mon Sep 17 00:00:00 2001 From: James Pitts Date: Mon, 11 Aug 2025 11:10:33 -0500 Subject: [PATCH] add support for api/folders --- .gitignore | 2 + dist/index.d.ts | 3 -- dist/index.d.ts.map | 1 - dist/index.js | 83 --------------------------------------- dist/index.js.map | 1 - src/index.ts | 96 +++++++++++++++++++++++++++++++++++++++++++-- tests.csv | 13 +++++- 7 files changed, 107 insertions(+), 92 deletions(-) delete mode 100644 dist/index.d.ts delete mode 100644 dist/index.d.ts.map delete mode 100644 dist/index.js delete mode 100644 dist/index.js.map diff --git a/.gitignore b/.gitignore index f03eaec..e1f5447 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ node_modules #!.yarn/cache .pnp.* + +dist/ \ No newline at end of file diff --git a/dist/index.d.ts b/dist/index.d.ts deleted file mode 100644 index dc1ec89..0000000 --- a/dist/index.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env node -export {}; -//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/dist/index.d.ts.map b/dist/index.d.ts.map deleted file mode 100644 index 535b86d..0000000 --- a/dist/index.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/index.js b/dist/index.js deleted file mode 100644 index 3cefaf4..0000000 --- a/dist/index.js +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env node -import { Command } from 'commander'; -import fetch from 'node-fetch'; -class TestQualityClient { - baseUrl; - apiKey; - constructor(baseUrl = 'https://api.testquality.com', apiKey) { - this.baseUrl = baseUrl; - this.apiKey = apiKey; - } - async getProjects() { - try { - const headers = { - 'Content-Type': 'application/json', - }; - if (this.apiKey) { - headers['Authorization'] = `Bearer ${this.apiKey}`; - } - const response = await fetch(`${this.baseUrl}/api/project`, { - method: 'GET', - headers, - }); - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - const data = await response.json(); - if (data.error) { - throw new Error(data.error); - } - return data.data || []; - } - catch (error) { - if (error instanceof Error) { - throw new Error(`Failed to fetch projects: ${error.message}`); - } - throw new Error('Failed to fetch projects: Unknown error'); - } - } -} -const program = new Command(); -program - .name('tq-cli') - .description('TestQuality CLI - Command line interface for TestQuality API') - .version('1.0.0'); -program - .command('projects') - .description('List all projects from TestQuality') - .option('-k, --api-key ', 'API key for authentication') - .option('-u, --url ', 'Base URL for TestQuality API', 'https://api.testquality.com') - .option('-j, --json', 'Output as JSON') - .action(async (options) => { - try { - const client = new TestQualityClient(options.url, options.apiKey); - const projects = await client.getProjects(); - if (projects.length === 0) { - console.log('No projects found.'); - return; - } - if (options.json) { - console.log(JSON.stringify(projects, null, 2)); - } - else { - console.log('\nTestQuality Projects:'); - console.log('====================='); - projects.forEach((project, index) => { - console.log(`\n${index + 1}. ${project.name}`); - console.log(` ID: ${project.id}`); - if (project.description) { - console.log(` Description: ${project.description}`); - } - }); - } - } - catch (error) { - console.error('Error:', error instanceof Error ? error.message : 'Unknown error'); - process.exit(1); - } -}); -program.parse(process.argv); -if (!process.argv.slice(2).length) { - program.outputHelp(); -} -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/index.js.map b/dist/index.js.map deleted file mode 100644 index 1ac3e0f..0000000 --- a/dist/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 7f3a147..02baaea 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,8 +9,15 @@ interface Project { [key: string]: any; } -interface ApiResponse { - data?: Project[]; +interface Folder { + id: number; + name: string; + parent_id?: number; + [key: string]: any; +} + +interface ApiResponse { + data?: T; error?: string; } @@ -42,7 +49,7 @@ class TestQualityClient { throw new Error(`HTTP error! status: ${response.status}`); } - const data = await response.json() as ApiResponse; + const data = await response.json() as ApiResponse; if (data.error) { throw new Error(data.error); @@ -56,6 +63,44 @@ class TestQualityClient { throw new Error('Failed to fetch projects: Unknown error'); } } + + async getFolders(projectId?: number): Promise { + try { + const headers: Record = { + 'Content-Type': 'application/json', + }; + + if (this.apiKey) { + headers['Authorization'] = `Bearer ${this.apiKey}`; + } + + const endpoint = projectId + ? `${this.baseUrl}/api/folder?project_id=${projectId}` + : `${this.baseUrl}/api/folder`; + + const response = await fetch(endpoint, { + method: 'GET', + headers, + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json() as ApiResponse; + + if (data.error) { + throw new Error(data.error); + } + + return data.data || []; + } catch (error) { + if (error instanceof Error) { + throw new Error(`Failed to fetch folders: ${error.message}`); + } + throw new Error('Failed to fetch folders: Unknown error'); + } + } } const program = new Command(); @@ -100,6 +145,51 @@ program } }); +program + .command('folders') + .description('List all folders from TestQuality') + .option('-k, --api-key ', 'API key for authentication') + .option('-u, --url ', 'Base URL for TestQuality API', 'https://api.testquality.com') + .option('-p, --project-id ', 'Filter folders by project ID') + .option('-j, --json', 'Output as JSON') + .action(async (options) => { + try { + const client = new TestQualityClient(options.url, options.apiKey); + const projectId = options.projectId ? parseInt(options.projectId, 10) : undefined; + + if (projectId && isNaN(projectId)) { + throw new Error('Invalid project ID. Must be a number.'); + } + + const folders = await client.getFolders(projectId); + + if (folders.length === 0) { + console.log('No folders found.'); + return; + } + + if (options.json) { + console.log(JSON.stringify(folders, null, 2)); + } else { + console.log('\nTestQuality Folders:'); + console.log('===================='); + folders.forEach((folder, index) => { + console.log(`\n${index + 1}. ${folder.name}`); + console.log(` ID: ${folder.id}`); + if (folder.parent_id) { + console.log(` Parent ID: ${folder.parent_id}`); + } + if (folder.description) { + console.log(` Description: ${folder.description}`); + } + }); + } + } catch (error) { + console.error('Error:', error instanceof Error ? error.message : 'Unknown error'); + process.exit(1); + } + }); + program.parse(process.argv); if (!process.argv.slice(2).length) { diff --git a/tests.csv b/tests.csv index 4960b5c..516f1c5 100644 --- a/tests.csv +++ b/tests.csv @@ -18,4 +18,15 @@ Production,Run Compiled Version,Run 'yarn start projects' after build,Execute co Error Handling,Invalid Command,Run 'yarn tq invalid-command',Display error message for unknown command Error Handling,Missing Required Options,Run command that requires options without providing them,Display appropriate error message Error Handling,API Response Error,Mock API returning error status,Display HTTP error status message -Error Handling,Malformed JSON Response,Mock API returning invalid JSON,Display parsing error message \ No newline at end of file +Error Handling,Malformed JSON Response,Mock API returning invalid JSON,Display parsing error message +Folder Commands,Display Folders Help,Run 'yarn tq folders --help',Display folders command help with available options +Folder Commands,Fetch All Folders,Run 'yarn tq folders',Either display folders list or authentication error message +Folder Commands,Fetch Folders With Auth,Run 'yarn tq folders -k ' with valid API key,Display list of folders from TestQuality API +Folder Commands,Fetch Folders By Project,Run 'yarn tq folders -p ' with valid project ID,Display folders filtered by project ID +Folder Commands,Fetch Folders JSON Format,Run 'yarn tq folders --json',Output folders in JSON format with proper indentation +Folder Commands,Invalid Project ID,Run 'yarn tq folders -p invalid',Display error message for invalid project ID +Folder Commands,Handle Empty Folders List,Run command when no folders exist,Display 'No folders found.' message +Folder Commands,Custom API URL for Folders,Run 'yarn tq folders -u https://custom.api.url',Attempt to fetch folders from custom URL +Folder Commands,Folders With Parent ID,Run 'yarn tq folders' with folders that have parent_id,Display folders showing parent ID relationship +Folder Commands,Network Error for Folders,Disconnect network and run 'yarn tq folders',Display network error message gracefully +Folder Commands,Invalid API Key for Folders,Run 'yarn tq folders -k INVALID_KEY',Display authentication error message \ No newline at end of file