From 7d8de7e09a63451c3a574fdc6ddcda066fae573c Mon Sep 17 00:00:00 2001 From: Tristan Vu Date: Thu, 9 Apr 2026 12:00:00 -0400 Subject: [PATCH 1/7] Add design spec for getUsersByIds function Documents the design for a new ZendeskApiService method to fetch multiple users by IDs using the show_many endpoint, following the pattern established by getZendeskTickets(). --- .../2026-04-09-get-users-by-ids-design.md | 144 ++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 docs/superpowers/specs/2026-04-09-get-users-by-ids-design.md diff --git a/docs/superpowers/specs/2026-04-09-get-users-by-ids-design.md b/docs/superpowers/specs/2026-04-09-get-users-by-ids-design.md new file mode 100644 index 0000000..58c68f1 --- /dev/null +++ b/docs/superpowers/specs/2026-04-09-get-users-by-ids-design.md @@ -0,0 +1,144 @@ +# getUsersByIds Function Design + +## Overview + +Add a new function to `ZendeskApiService` that retrieves multiple Zendesk users using comma-separated IDs in a single API call. + +## Requirements + +- Fetch multiple users by their IDs efficiently +- Match the pattern established by `getZendeskTickets()` +- Support generic typing for custom user fields +- Enforce a maximum limit of 100 users per request + +## Function Signature + +```typescript +public async getUsersByIds(userIds: number[]): Promise[]> +``` + +**Location:** `src/services/zendesk-api-service.ts`, positioned near the existing `getUser()` method (around line 182) + +**Parameters:** +- `userIds: number[]` - Array of Zendesk user IDs to fetch +- Generic type parameter `T` for custom user field values (defaults to `IZendeskUserFieldValue`) + +**Returns:** +- `Promise[]>` - Array of user objects + +## Implementation + +### API Endpoint + +- **URL:** `/api/v2/users/show_many.json?ids={comma-separated-ids}` +- **Method:** GET +- **Content Type:** application/json + +### Request Flow + +1. **Validation:** Check if `userIds.length > 100` + - If true, throw error: `"A limit of 100 users can be retrieved at a time."` + - This matches the behavior of `getZendeskTickets()` which uses `MAX_TICKETS_PER_REQUEST = 100` + +2. **API Call:** Make single request with IDs joined by commas + ```typescript + await this.client.request>({ + url: `/api/v2/users/show_many.json?ids=${userIds.join(",")}`, + type: "GET", + contentType: "application/json" + }) + ``` + +3. **Response Extraction:** Extract and return the `users` array from the response + +### Response Type + +The API returns a response matching the existing `ISearchUserResults` interface: +```typescript +interface ISearchUserResults extends IZendeskResponse { + users: IZendeskUser[]; +} +``` + +### Error Handling + +- **Invalid input:** Throw error if more than 100 user IDs are provided +- **API errors:** Let Zendesk API errors bubble up naturally (non-existent user IDs, permission errors, etc.) + +## Testing + +Add tests to `__tests__/services/zendesk-api-service.spec.ts` in the "User" section. + +### Test Cases + +1. **Retrieve multiple users by IDs** + - Mock successful API response with multiple users + - Verify correct endpoint URL with comma-separated IDs + - Verify returned array matches expected data + +2. **Handle single user ID** + - Test edge case with array containing one ID + - Verify request is made correctly + +3. **Enforce 100-user limit** + - Create array with 101 user IDs + - Verify error is thrown with correct message + - Verify no API request is made + +### Test Structure + +```typescript +describe("getUsersByIds", () => { + it("should retrieve multiple users by their IDs", async () => { + // Mock response, make request, verify URL and results + }) + + it("should handle single user ID", async () => { + // Test single ID case + }) + + it("should throw an error when trying to retrieve more than 100 users", async () => { + // Test limit enforcement + }) +}) +``` + +## Design Rationale + +### Why Approach 1 (show_many endpoint)? + +This approach was chosen over alternatives for several reasons: + +**vs. Batch individual requests:** +- Single API call vs. N requests (where N = number of users) +- Better performance and lower rate limit consumption +- More scalable + +**vs. Search endpoint:** +- Direct lookup vs. search query +- More reliable and consistent +- Proper use of API semantics + +### Consistency with Existing Code + +- Mirrors `getZendeskTickets()` implementation pattern exactly +- Uses same 100-item limit constant concept +- Follows established error handling conventions +- Maintains type safety with generic parameters + +## Files Modified + +1. `src/services/zendesk-api-service.ts` - Add new method +2. `__tests__/services/zendesk-api-service.spec.ts` - Add test suite + +## Risks and Considerations + +- **Assumption:** Zendesk API supports `/api/v2/users/show_many.json` endpoint + - This is a standard Zendesk pattern (tickets use it) + - If endpoint doesn't exist, fallback to individual `getUser()` calls with `Promise.all()` + +- **User order:** API may not return users in the same order as requested IDs + - Document this behavior if confirmed + +- **Missing users:** API may silently omit non-existent or inaccessible user IDs + - This is expected Zendesk behavior, not an error condition From 223a532db24727a031d193019a0812d2ea7ea4f8 Mon Sep 17 00:00:00 2001 From: Tristan Vu Date: Thu, 9 Apr 2026 12:00:42 -0400 Subject: [PATCH 2/7] Fix endpoint URL consistency in design spec Remove .json extension from show_many endpoint to match the pattern used by getZendeskTickets() --- .../superpowers/specs/2026-04-09-get-users-by-ids-design.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/superpowers/specs/2026-04-09-get-users-by-ids-design.md b/docs/superpowers/specs/2026-04-09-get-users-by-ids-design.md index 58c68f1..27543b4 100644 --- a/docs/superpowers/specs/2026-04-09-get-users-by-ids-design.md +++ b/docs/superpowers/specs/2026-04-09-get-users-by-ids-design.md @@ -30,7 +30,7 @@ public async getUsersByIds(userIds: number[]): Promi ### API Endpoint -- **URL:** `/api/v2/users/show_many.json?ids={comma-separated-ids}` +- **URL:** `/api/v2/users/show_many?ids={comma-separated-ids}` - **Method:** GET - **Content Type:** application/json @@ -43,7 +43,7 @@ public async getUsersByIds(userIds: number[]): Promi 2. **API Call:** Make single request with IDs joined by commas ```typescript await this.client.request>({ - url: `/api/v2/users/show_many.json?ids=${userIds.join(",")}`, + url: `/api/v2/users/show_many?ids=${userIds.join(",")}`, type: "GET", contentType: "application/json" }) @@ -133,7 +133,7 @@ This approach was chosen over alternatives for several reasons: ## Risks and Considerations -- **Assumption:** Zendesk API supports `/api/v2/users/show_many.json` endpoint +- **Assumption:** Zendesk API supports `/api/v2/users/show_many` endpoint - This is a standard Zendesk pattern (tickets use it) - If endpoint doesn't exist, fallback to individual `getUser()` calls with `Promise.all()` From 3a875fbb9a1267f34394ae44444c5e338405d7b0 Mon Sep 17 00:00:00 2001 From: Tristan Vu Date: Thu, 9 Apr 2026 12:34:57 -0400 Subject: [PATCH 3/7] Add implementation plan for getUsersByIds Detailed TDD-based plan for adding getUsersByIds method to ZendeskApiService with complete test coverage and step-by-step implementation instructions. --- .../plans/2026-04-09-get-users-by-ids.md | 288 ++++++++++++++++++ 1 file changed, 288 insertions(+) create mode 100644 docs/superpowers/plans/2026-04-09-get-users-by-ids.md diff --git a/docs/superpowers/plans/2026-04-09-get-users-by-ids.md b/docs/superpowers/plans/2026-04-09-get-users-by-ids.md new file mode 100644 index 0000000..8d9896c --- /dev/null +++ b/docs/superpowers/plans/2026-04-09-get-users-by-ids.md @@ -0,0 +1,288 @@ +# getUsersByIds Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Add a method to ZendeskApiService to fetch multiple Zendesk users by IDs in a single API call. + +**Architecture:** Mirror the existing `getZendeskTickets()` pattern - use the `/api/v2/users/show_many` endpoint with comma-separated IDs, enforce a 100-user limit, and support generic typing for custom user fields. + +**Tech Stack:** TypeScript, Jest, Zendesk API v2 + +--- + +## File Structure + +**Files to modify:** +- `src/services/zendesk-api-service.ts` - Add `getUsersByIds()` method after line 181 +- `__tests__/services/zendesk-api-service.spec.ts` - Add test suite in "User" section after line 277 + +## Task 1: Add Tests for getUsersByIds + +**Files:** +- Modify: `__tests__/services/zendesk-api-service.spec.ts:277` + +Add a complete test suite for the new `getUsersByIds()` method following TDD. + +- [ ] **Step 1: Write the test suite** + +Add this test suite after the `getUser` describe block (after line 277): + +```typescript + describe("getUsersByIds", () => { + const mockUsers: IZendeskUser[] = [ + { + id: 1, + name: "Test User 1", + email: "test1@example.com", + created_at: "2023-01-01T00:00:00Z", + updated_at: "2023-01-01T00:00:00Z", + url: "https://example.zendesk.com/api/v2/users/1.json", + time_zone: "UTC", + iana_time_zone: "UTC", + phone: null, + photo: null, + locale_id: 1, + locale: "en-US", + organization_id: 100, + role: "end-user", + verified: true, + external_id: null, + tags: [], + alias: "", + active: true, + shared: false, + shared_agent: false, + shared_phone_number: null, + last_login_at: "2023-01-01T00:00:00Z", + two_factor_auth_enabled: null, + signature: "", + details: "", + notes: "", + role_type: 0, + custom_role_id: 0, + moderator: false, + ticket_restriction: null, + only_private_comments: false, + restricted_agent: false, + suspended: false, + default_group_id: 0, + report_csv: false, + user_fields: {} + }, + { + id: 2, + name: "Test User 2", + email: "test2@example.com", + created_at: "2023-01-02T00:00:00Z", + updated_at: "2023-01-02T00:00:00Z", + url: "https://example.zendesk.com/api/v2/users/2.json", + time_zone: "UTC", + iana_time_zone: "UTC", + phone: null, + photo: null, + locale_id: 1, + locale: "en-US", + organization_id: 100, + role: "end-user", + verified: true, + external_id: null, + tags: [], + alias: "", + active: true, + shared: false, + shared_agent: false, + shared_phone_number: null, + last_login_at: "2023-01-02T00:00:00Z", + two_factor_auth_enabled: null, + signature: "", + details: "", + notes: "", + role_type: 0, + custom_role_id: 0, + moderator: false, + ticket_restriction: null, + only_private_comments: false, + restricted_agent: false, + suspended: false, + default_group_id: 0, + report_csv: false, + user_fields: {} + } + ]; + + it("should retrieve multiple users by their IDs", async () => { + requestMock.mockResolvedValueOnce({ users: mockUsers }); + + const result = await service.getUsersByIds([1, 2]); + + expect(requestMock).toHaveBeenCalledWith({ + url: `/api/v2/users/show_many?ids=1,2`, + type: "GET", + contentType: "application/json" + }); + expect(result).toEqual(mockUsers); + }); + + it("should handle single user ID", async () => { + requestMock.mockResolvedValueOnce({ users: [mockUsers[0]] }); + + const result = await service.getUsersByIds([1]); + + expect(requestMock).toHaveBeenCalledWith({ + url: `/api/v2/users/show_many?ids=1`, + type: "GET", + contentType: "application/json" + }); + expect(result).toEqual([mockUsers[0]]); + }); + + it("should throw an error when trying to retrieve more than 100 users", async () => { + const manyUserIds = Array.from({ length: 101 }, (_, index) => index + 1); + + await expect(service.getUsersByIds(manyUserIds)).rejects.toThrow( + "A limit of 100 users can be retrieved at a time." + ); + + expect(requestMock).not.toHaveBeenCalled(); + }); + }); +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run the test suite: + +```bash +npm test -- __tests__/services/zendesk-api-service.spec.ts -t "getUsersByIds" +``` + +Expected output: All 3 tests FAIL with error indicating `service.getUsersByIds is not a function` + +- [ ] **Step 3: Commit the failing tests** + +```bash +git add __tests__/services/zendesk-api-service.spec.ts +git commit -m "test: add failing tests for getUsersByIds method" +``` + +## Task 2: Implement getUsersByIds Method + +**Files:** +- Modify: `src/services/zendesk-api-service.ts:182` + +Implement the `getUsersByIds()` method to make all tests pass. + +- [ ] **Step 1: Add the method implementation** + +Add this method after the `getUser()` method (after line 181): + +```typescript + /** + * Retrieve multiple zendesk users + * A limit of 100 users can be retrieved at a time. + */ + public async getUsersByIds(userIds: number[]): Promise[]> { + if (userIds.length > MAX_TICKETS_PER_REQUEST) { + throw new Error("A limit of 100 users can be retrieved at a time."); + } + + const { users } = await this.client.request>({ + url: `/api/v2/users/show_many?ids=${userIds.join(",")}`, + type: "GET", + contentType: "application/json" + }); + + return users; + } +``` + +- [ ] **Step 2: Run tests to verify they pass** + +Run the test suite: + +```bash +npm test -- __tests__/services/zendesk-api-service.spec.ts -t "getUsersByIds" +``` + +Expected output: All 3 tests PASS + +- [ ] **Step 3: Run full test suite to ensure no regressions** + +Run all tests: + +```bash +npm test -- __tests__/services/zendesk-api-service.spec.ts +``` + +Expected output: All tests PASS (including the new getUsersByIds tests) + +- [ ] **Step 4: Run linter** + +```bash +npm run lint +``` + +Expected output: No linting errors + +- [ ] **Step 5: Commit the implementation** + +```bash +git add src/services/zendesk-api-service.ts +git commit -m "feat: add getUsersByIds method to ZendeskApiService + +- Retrieves multiple users by IDs using show_many endpoint +- Enforces 100-user limit matching getZendeskTickets pattern +- Supports generic typing for custom user fields" +``` + +## Task 3: Verification and Documentation + +**Files:** +- Check: All modified files + +Final verification that everything works correctly. + +- [ ] **Step 1: Run full test suite** + +```bash +npm test +``` + +Expected output: All tests PASS with coverage maintained + +- [ ] **Step 2: Run build to verify TypeScript compilation** + +```bash +npm run build +``` + +Expected output: Build succeeds with no TypeScript errors + +- [ ] **Step 3: Run prettier check** + +```bash +npm run prettier +``` + +Expected output: No formatting issues + +- [ ] **Step 4: Review changes** + +```bash +git diff HEAD~2 +``` + +Verify: +- Method signature matches design spec +- Tests cover all specified cases +- Implementation follows existing patterns +- No unintended changes + +## Success Criteria + +✅ All tests pass including 3 new tests for getUsersByIds +✅ Method enforces 100-user limit +✅ Method uses correct API endpoint with comma-separated IDs +✅ Generic typing works for custom user fields +✅ No linting or TypeScript errors +✅ Code follows existing patterns (matches getZendeskTickets) From 1490207728745419a88b32bf257baca1b1978a60 Mon Sep 17 00:00:00 2001 From: Tristan Vu Date: Thu, 9 Apr 2026 12:40:08 -0400 Subject: [PATCH 4/7] test: add failing tests for getUsersByIds method --- .../services/zendesk-api-service.spec.ts | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/__tests__/services/zendesk-api-service.spec.ts b/__tests__/services/zendesk-api-service.spec.ts index 487edd9..1b590b1 100644 --- a/__tests__/services/zendesk-api-service.spec.ts +++ b/__tests__/services/zendesk-api-service.spec.ts @@ -276,6 +276,138 @@ describe("ZendeskService", () => { }); }); + describe("getUsersByIds", () => { + const mockUsers: IZendeskUser[] = [ + { + id: 1, + name: "Test User 1", + email: "test1@example.com", + created_at: "2023-01-01T00:00:00Z", + updated_at: "2023-01-01T00:00:00Z", + url: "https://example.zendesk.com/api/v2/users/1.json", + time_zone: "UTC", + iana_time_zone: "UTC", + phone: null, + photo: null, + locale_id: 1, + locale: "en-US", + organization_id: 100, + role: "end-user", + verified: true, + external_id: null, + tags: [], + alias: "", + active: true, + shared: false, + shared_agent: false, + shared_phone_number: null, + last_login_at: "2023-01-01T00:00:00Z", + two_factor_auth_enabled: null, + signature: "", + details: "", + notes: "", + role_type: 0, + custom_role_id: 0, + moderator: false, + ticket_restriction: null, + only_private_comments: false, + restricted_agent: false, + suspended: false, + default_group_id: 0, + report_csv: false, + user_fields: {} + }, + { + id: 2, + name: "Test User 2", + email: "test2@example.com", + created_at: "2023-01-02T00:00:00Z", + updated_at: "2023-01-02T00:00:00Z", + url: "https://example.zendesk.com/api/v2/users/2.json", + time_zone: "UTC", + iana_time_zone: "UTC", + phone: null, + photo: null, + locale_id: 1, + locale: "en-US", + organization_id: 100, + role: "end-user", + verified: true, + external_id: null, + tags: [], + alias: "", + active: true, + shared: false, + shared_agent: false, + shared_phone_number: null, + last_login_at: "2023-01-02T00:00:00Z", + two_factor_auth_enabled: null, + signature: "", + details: "", + notes: "", + role_type: 0, + custom_role_id: 0, + moderator: false, + ticket_restriction: null, + only_private_comments: false, + restricted_agent: false, + suspended: false, + default_group_id: 0, + report_csv: false, + user_fields: {} + } + ]; + + it("should retrieve multiple users by their IDs", async () => { + requestMock.mockResolvedValueOnce({ users: mockUsers }); + + const result = await service.getUsersByIds([1, 2]); + + expect(requestMock).toHaveBeenCalledWith({ + url: `/api/v2/users/show_many?ids=1,2`, + type: "GET", + contentType: "application/json" + }); + expect(result).toEqual(mockUsers); + }); + + it("should handle single user ID", async () => { + requestMock.mockResolvedValueOnce({ users: [mockUsers[0]] }); + + const result = await service.getUsersByIds([1]); + + expect(requestMock).toHaveBeenCalledWith({ + url: `/api/v2/users/show_many?ids=1`, + type: "GET", + contentType: "application/json" + }); + expect(result).toEqual([mockUsers[0]]); + }); + + it("should throw an error when trying to retrieve more than 100 users", async () => { + const manyUserIds = Array.from({ length: 101 }, (_, index) => index + 1); + + await expect(service.getUsersByIds(manyUserIds)).rejects.toThrow( + "A limit of 100 users can be retrieved at a time." + ); + + expect(requestMock).not.toHaveBeenCalled(); + }); + + it("should handle empty user array when user IDs don't exist", async () => { + requestMock.mockResolvedValueOnce({ users: [] }); + + const result = await service.getUsersByIds([99999]); + + expect(requestMock).toHaveBeenCalledWith({ + url: `/api/v2/users/show_many?ids=99999`, + type: "GET", + contentType: "application/json" + }); + expect(result).toEqual([]); + }); + }); + describe("searchUsers", () => { const searchQuery = "searchQuery"; From 049b8548dbff3d393fdca7e16921d5a0877b7ffa Mon Sep 17 00:00:00 2001 From: Tristan Vu Date: Thu, 9 Apr 2026 12:51:21 -0400 Subject: [PATCH 5/7] feat: add getUsersByIds method to ZendeskApiService - Retrieves multiple users by IDs using show_many endpoint - Enforces 100-user limit matching getZendeskTickets pattern - Supports generic typing for custom user fields --- src/services/zendesk-api-service.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/services/zendesk-api-service.ts b/src/services/zendesk-api-service.ts index 86859e1..3eca439 100644 --- a/src/services/zendesk-api-service.ts +++ b/src/services/zendesk-api-service.ts @@ -180,6 +180,24 @@ export class ZendeskApiService { return result.user; } + /** + * Retrieve multiple zendesk users + * A limit of 100 users can be retrieved at a time. + */ + public async getUsersByIds(userIds: number[]): Promise[]> { + if (userIds.length > MAX_TICKETS_PER_REQUEST) { + throw new Error("A limit of 100 users can be retrieved at a time."); + } + + const { users } = await this.client.request>({ + url: `/api/v2/users/show_many?ids=${userIds.join(",")}`, + type: "GET", + contentType: "application/json" + }); + + return users; + } + /** * Searches the users corresponding to the given query * From a4e38c4c1c5658b786ae9b2d9ce28d72f6fe3b1f Mon Sep 17 00:00:00 2001 From: Tristan Vu Date: Thu, 9 Apr 2026 13:13:51 -0400 Subject: [PATCH 6/7] chore: remove outdated design and implementation plan for getUsersByIds --- .../plans/2026-04-09-get-users-by-ids.md | 288 ------------------ .../2026-04-09-get-users-by-ids-design.md | 144 --------- 2 files changed, 432 deletions(-) delete mode 100644 docs/superpowers/plans/2026-04-09-get-users-by-ids.md delete mode 100644 docs/superpowers/specs/2026-04-09-get-users-by-ids-design.md diff --git a/docs/superpowers/plans/2026-04-09-get-users-by-ids.md b/docs/superpowers/plans/2026-04-09-get-users-by-ids.md deleted file mode 100644 index 8d9896c..0000000 --- a/docs/superpowers/plans/2026-04-09-get-users-by-ids.md +++ /dev/null @@ -1,288 +0,0 @@ -# getUsersByIds Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Add a method to ZendeskApiService to fetch multiple Zendesk users by IDs in a single API call. - -**Architecture:** Mirror the existing `getZendeskTickets()` pattern - use the `/api/v2/users/show_many` endpoint with comma-separated IDs, enforce a 100-user limit, and support generic typing for custom user fields. - -**Tech Stack:** TypeScript, Jest, Zendesk API v2 - ---- - -## File Structure - -**Files to modify:** -- `src/services/zendesk-api-service.ts` - Add `getUsersByIds()` method after line 181 -- `__tests__/services/zendesk-api-service.spec.ts` - Add test suite in "User" section after line 277 - -## Task 1: Add Tests for getUsersByIds - -**Files:** -- Modify: `__tests__/services/zendesk-api-service.spec.ts:277` - -Add a complete test suite for the new `getUsersByIds()` method following TDD. - -- [ ] **Step 1: Write the test suite** - -Add this test suite after the `getUser` describe block (after line 277): - -```typescript - describe("getUsersByIds", () => { - const mockUsers: IZendeskUser[] = [ - { - id: 1, - name: "Test User 1", - email: "test1@example.com", - created_at: "2023-01-01T00:00:00Z", - updated_at: "2023-01-01T00:00:00Z", - url: "https://example.zendesk.com/api/v2/users/1.json", - time_zone: "UTC", - iana_time_zone: "UTC", - phone: null, - photo: null, - locale_id: 1, - locale: "en-US", - organization_id: 100, - role: "end-user", - verified: true, - external_id: null, - tags: [], - alias: "", - active: true, - shared: false, - shared_agent: false, - shared_phone_number: null, - last_login_at: "2023-01-01T00:00:00Z", - two_factor_auth_enabled: null, - signature: "", - details: "", - notes: "", - role_type: 0, - custom_role_id: 0, - moderator: false, - ticket_restriction: null, - only_private_comments: false, - restricted_agent: false, - suspended: false, - default_group_id: 0, - report_csv: false, - user_fields: {} - }, - { - id: 2, - name: "Test User 2", - email: "test2@example.com", - created_at: "2023-01-02T00:00:00Z", - updated_at: "2023-01-02T00:00:00Z", - url: "https://example.zendesk.com/api/v2/users/2.json", - time_zone: "UTC", - iana_time_zone: "UTC", - phone: null, - photo: null, - locale_id: 1, - locale: "en-US", - organization_id: 100, - role: "end-user", - verified: true, - external_id: null, - tags: [], - alias: "", - active: true, - shared: false, - shared_agent: false, - shared_phone_number: null, - last_login_at: "2023-01-02T00:00:00Z", - two_factor_auth_enabled: null, - signature: "", - details: "", - notes: "", - role_type: 0, - custom_role_id: 0, - moderator: false, - ticket_restriction: null, - only_private_comments: false, - restricted_agent: false, - suspended: false, - default_group_id: 0, - report_csv: false, - user_fields: {} - } - ]; - - it("should retrieve multiple users by their IDs", async () => { - requestMock.mockResolvedValueOnce({ users: mockUsers }); - - const result = await service.getUsersByIds([1, 2]); - - expect(requestMock).toHaveBeenCalledWith({ - url: `/api/v2/users/show_many?ids=1,2`, - type: "GET", - contentType: "application/json" - }); - expect(result).toEqual(mockUsers); - }); - - it("should handle single user ID", async () => { - requestMock.mockResolvedValueOnce({ users: [mockUsers[0]] }); - - const result = await service.getUsersByIds([1]); - - expect(requestMock).toHaveBeenCalledWith({ - url: `/api/v2/users/show_many?ids=1`, - type: "GET", - contentType: "application/json" - }); - expect(result).toEqual([mockUsers[0]]); - }); - - it("should throw an error when trying to retrieve more than 100 users", async () => { - const manyUserIds = Array.from({ length: 101 }, (_, index) => index + 1); - - await expect(service.getUsersByIds(manyUserIds)).rejects.toThrow( - "A limit of 100 users can be retrieved at a time." - ); - - expect(requestMock).not.toHaveBeenCalled(); - }); - }); -``` - -- [ ] **Step 2: Run tests to verify they fail** - -Run the test suite: - -```bash -npm test -- __tests__/services/zendesk-api-service.spec.ts -t "getUsersByIds" -``` - -Expected output: All 3 tests FAIL with error indicating `service.getUsersByIds is not a function` - -- [ ] **Step 3: Commit the failing tests** - -```bash -git add __tests__/services/zendesk-api-service.spec.ts -git commit -m "test: add failing tests for getUsersByIds method" -``` - -## Task 2: Implement getUsersByIds Method - -**Files:** -- Modify: `src/services/zendesk-api-service.ts:182` - -Implement the `getUsersByIds()` method to make all tests pass. - -- [ ] **Step 1: Add the method implementation** - -Add this method after the `getUser()` method (after line 181): - -```typescript - /** - * Retrieve multiple zendesk users - * A limit of 100 users can be retrieved at a time. - */ - public async getUsersByIds(userIds: number[]): Promise[]> { - if (userIds.length > MAX_TICKETS_PER_REQUEST) { - throw new Error("A limit of 100 users can be retrieved at a time."); - } - - const { users } = await this.client.request>({ - url: `/api/v2/users/show_many?ids=${userIds.join(",")}`, - type: "GET", - contentType: "application/json" - }); - - return users; - } -``` - -- [ ] **Step 2: Run tests to verify they pass** - -Run the test suite: - -```bash -npm test -- __tests__/services/zendesk-api-service.spec.ts -t "getUsersByIds" -``` - -Expected output: All 3 tests PASS - -- [ ] **Step 3: Run full test suite to ensure no regressions** - -Run all tests: - -```bash -npm test -- __tests__/services/zendesk-api-service.spec.ts -``` - -Expected output: All tests PASS (including the new getUsersByIds tests) - -- [ ] **Step 4: Run linter** - -```bash -npm run lint -``` - -Expected output: No linting errors - -- [ ] **Step 5: Commit the implementation** - -```bash -git add src/services/zendesk-api-service.ts -git commit -m "feat: add getUsersByIds method to ZendeskApiService - -- Retrieves multiple users by IDs using show_many endpoint -- Enforces 100-user limit matching getZendeskTickets pattern -- Supports generic typing for custom user fields" -``` - -## Task 3: Verification and Documentation - -**Files:** -- Check: All modified files - -Final verification that everything works correctly. - -- [ ] **Step 1: Run full test suite** - -```bash -npm test -``` - -Expected output: All tests PASS with coverage maintained - -- [ ] **Step 2: Run build to verify TypeScript compilation** - -```bash -npm run build -``` - -Expected output: Build succeeds with no TypeScript errors - -- [ ] **Step 3: Run prettier check** - -```bash -npm run prettier -``` - -Expected output: No formatting issues - -- [ ] **Step 4: Review changes** - -```bash -git diff HEAD~2 -``` - -Verify: -- Method signature matches design spec -- Tests cover all specified cases -- Implementation follows existing patterns -- No unintended changes - -## Success Criteria - -✅ All tests pass including 3 new tests for getUsersByIds -✅ Method enforces 100-user limit -✅ Method uses correct API endpoint with comma-separated IDs -✅ Generic typing works for custom user fields -✅ No linting or TypeScript errors -✅ Code follows existing patterns (matches getZendeskTickets) diff --git a/docs/superpowers/specs/2026-04-09-get-users-by-ids-design.md b/docs/superpowers/specs/2026-04-09-get-users-by-ids-design.md deleted file mode 100644 index 27543b4..0000000 --- a/docs/superpowers/specs/2026-04-09-get-users-by-ids-design.md +++ /dev/null @@ -1,144 +0,0 @@ -# getUsersByIds Function Design - -## Overview - -Add a new function to `ZendeskApiService` that retrieves multiple Zendesk users using comma-separated IDs in a single API call. - -## Requirements - -- Fetch multiple users by their IDs efficiently -- Match the pattern established by `getZendeskTickets()` -- Support generic typing for custom user fields -- Enforce a maximum limit of 100 users per request - -## Function Signature - -```typescript -public async getUsersByIds(userIds: number[]): Promise[]> -``` - -**Location:** `src/services/zendesk-api-service.ts`, positioned near the existing `getUser()` method (around line 182) - -**Parameters:** -- `userIds: number[]` - Array of Zendesk user IDs to fetch -- Generic type parameter `T` for custom user field values (defaults to `IZendeskUserFieldValue`) - -**Returns:** -- `Promise[]>` - Array of user objects - -## Implementation - -### API Endpoint - -- **URL:** `/api/v2/users/show_many?ids={comma-separated-ids}` -- **Method:** GET -- **Content Type:** application/json - -### Request Flow - -1. **Validation:** Check if `userIds.length > 100` - - If true, throw error: `"A limit of 100 users can be retrieved at a time."` - - This matches the behavior of `getZendeskTickets()` which uses `MAX_TICKETS_PER_REQUEST = 100` - -2. **API Call:** Make single request with IDs joined by commas - ```typescript - await this.client.request>({ - url: `/api/v2/users/show_many?ids=${userIds.join(",")}`, - type: "GET", - contentType: "application/json" - }) - ``` - -3. **Response Extraction:** Extract and return the `users` array from the response - -### Response Type - -The API returns a response matching the existing `ISearchUserResults` interface: -```typescript -interface ISearchUserResults extends IZendeskResponse { - users: IZendeskUser[]; -} -``` - -### Error Handling - -- **Invalid input:** Throw error if more than 100 user IDs are provided -- **API errors:** Let Zendesk API errors bubble up naturally (non-existent user IDs, permission errors, etc.) - -## Testing - -Add tests to `__tests__/services/zendesk-api-service.spec.ts` in the "User" section. - -### Test Cases - -1. **Retrieve multiple users by IDs** - - Mock successful API response with multiple users - - Verify correct endpoint URL with comma-separated IDs - - Verify returned array matches expected data - -2. **Handle single user ID** - - Test edge case with array containing one ID - - Verify request is made correctly - -3. **Enforce 100-user limit** - - Create array with 101 user IDs - - Verify error is thrown with correct message - - Verify no API request is made - -### Test Structure - -```typescript -describe("getUsersByIds", () => { - it("should retrieve multiple users by their IDs", async () => { - // Mock response, make request, verify URL and results - }) - - it("should handle single user ID", async () => { - // Test single ID case - }) - - it("should throw an error when trying to retrieve more than 100 users", async () => { - // Test limit enforcement - }) -}) -``` - -## Design Rationale - -### Why Approach 1 (show_many endpoint)? - -This approach was chosen over alternatives for several reasons: - -**vs. Batch individual requests:** -- Single API call vs. N requests (where N = number of users) -- Better performance and lower rate limit consumption -- More scalable - -**vs. Search endpoint:** -- Direct lookup vs. search query -- More reliable and consistent -- Proper use of API semantics - -### Consistency with Existing Code - -- Mirrors `getZendeskTickets()` implementation pattern exactly -- Uses same 100-item limit constant concept -- Follows established error handling conventions -- Maintains type safety with generic parameters - -## Files Modified - -1. `src/services/zendesk-api-service.ts` - Add new method -2. `__tests__/services/zendesk-api-service.spec.ts` - Add test suite - -## Risks and Considerations - -- **Assumption:** Zendesk API supports `/api/v2/users/show_many` endpoint - - This is a standard Zendesk pattern (tickets use it) - - If endpoint doesn't exist, fallback to individual `getUser()` calls with `Promise.all()` - -- **User order:** API may not return users in the same order as requested IDs - - Document this behavior if confirmed - -- **Missing users:** API may silently omit non-existent or inaccessible user IDs - - This is expected Zendesk behavior, not an error condition From bc38de19bc6fe6371e95627a6f6fab7e9e72a942 Mon Sep 17 00:00:00 2001 From: tristan-vu Date: Thu, 9 Apr 2026 17:35:11 +0000 Subject: [PATCH 7/7] [BOT] Bump version from 1.5.1 to 1.6.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 710f8c2..c99600c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@zendesk/zaf-toolbox", - "version": "1.5.1", + "version": "1.6.0", "description": "A toolbox for ZAF application built with 🩷 by Zendesk Labs", "main": "lib/src/index.js", "types": "lib/src/index.d.ts",