From 357c6b0ad8bd3fdfe22a3f55f4b7c971b6d0bf55 Mon Sep 17 00:00:00 2001 From: Xavier Abad <77491413+xabg2@users.noreply.github.com> Date: Tue, 3 Nov 2026 11:20:14 +0100 Subject: [PATCH] feat(photos): add files in folders search to the Photos client Also bumps the version to 1.25.0. --- package.json | 2 +- src/drive/photos/index.ts | 6 ++++- src/drive/photos/types.ts | 8 ++++++ test/drive/photos/index.test.ts | 43 ++++++++++++++++++++++++++++++++- 4 files changed, 56 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 4a574c1a..ec257be5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@internxt/sdk", "author": "Internxt ", - "version": "1.24.0", + "version": "1.25.0", "description": "An sdk for interacting with Internxt's services", "repository": { "type": "git", diff --git a/src/drive/photos/index.ts b/src/drive/photos/index.ts index 19ba9a7e..a7f35803 100644 --- a/src/drive/photos/index.ts +++ b/src/drive/photos/index.ts @@ -1,7 +1,7 @@ import { ApiSecurity, ApiUrl, AppDetails } from '../../shared'; import { headersWithToken } from '../../shared/headers'; import { HttpClient } from '../../shared/http/client'; -import { PhotoDevice } from './types'; +import { GetPhotoFilesInFoldersPayload, GetPhotoFilesInFoldersResponse, PhotoDevice } from './types'; export class Photos { private readonly client: HttpClient; @@ -38,6 +38,10 @@ export class Photos { return this.client.patch(`/photos/devices/${uuid}`, { deviceName }, this.headers()); } + public getFilesInFolders(payload: GetPhotoFilesInFoldersPayload): Promise { + return this.client.post('/photos/folders/files/delta/search', payload, this.headers()); + } + private headers() { return headersWithToken({ clientName: this.appDetails.clientName, diff --git a/src/drive/photos/types.ts b/src/drive/photos/types.ts index 63dd87bf..f746a63b 100644 --- a/src/drive/photos/types.ts +++ b/src/drive/photos/types.ts @@ -1,6 +1,14 @@ +import { components } from '../../schema'; + export type PhotoDevice = { uuid: string; plainName: string; bucket: string; status: 'EXISTS' | 'TRASHED' | 'DELETED'; }; + +export type GetPhotoFilesInFoldersPayload = components['schemas']['GetFilesInFoldersDto']; + +export type GetPhotoFilesInFoldersResponse = components['schemas']['GetFilesInFoldersResponseDto']; + +export type PhotoFolderFile = GetPhotoFilesInFoldersResponse['files'][number]; diff --git a/test/drive/photos/index.test.ts b/test/drive/photos/index.test.ts index af259908..0d6738b5 100644 --- a/test/drive/photos/index.test.ts +++ b/test/drive/photos/index.test.ts @@ -1,6 +1,6 @@ import { beforeEach, describe, expect, test, vi } from 'vitest'; import { Photos } from '../../../src/drive/photos'; -import { PhotoDevice } from '../../../src/drive/photos/types'; +import { PhotoDevice, PhotoFolderFile } from '../../../src/drive/photos/types'; import { ApiSecurity, AppDetails } from '../../../src/shared'; import { headersWithToken } from '../../../src/shared/headers'; import { HttpClient } from '../../../src/shared/http/client'; @@ -124,3 +124,44 @@ describe('renameDevice', () => { await expect(client.renameDevice('device-uuid-1', 'New Name')).rejects.toThrow('server error'); }); }); + +describe('getFilesInFolders', () => { + const photoFile = { + uuid: 'file-uuid-1', + plainName: 'IMG_0001', + type: 'heic', + folderUuid: 'day-folder-uuid-1', + status: 'EXISTS', + thumbnails: [], + } as unknown as PhotoFolderFile; + + test('when files are requested by folders and date, then the files and next cursor are returned', async () => { + const response = { files: [photoFile], nextCursor: 'next-cursor' }; + vi.spyOn(HttpClient.prototype, 'post').mockResolvedValue(response); + const { client, headers } = clientAndHeaders(); + const payload = { folderUuids: ['day-folder-uuid-1', 'day-folder-uuid-2'], updatedAt: '1970-01-01T00:00:00.000Z' }; + + const result = await client.getFilesInFolders(payload); + + expect(result).toEqual(response); + expect(HttpClient.prototype.post).toHaveBeenCalledWith('/photos/folders/files/delta/search', payload, headers); + }); + + test('when a cursor and a limit are given, then they are sent to fetch the next page', async () => { + vi.spyOn(HttpClient.prototype, 'post').mockResolvedValue({ files: [], nextCursor: null }); + const { client, headers } = clientAndHeaders(); + const payload = { folderUuids: ['day-folder-uuid-1'], cursor: 'next-cursor', limit: 50 }; + + const result = await client.getFilesInFolders(payload); + + expect(result).toEqual({ files: [], nextCursor: null }); + expect(HttpClient.prototype.post).toHaveBeenCalledWith('/photos/folders/files/delta/search', payload, headers); + }); + + test('when the server throws, then the error is propagated', async () => { + vi.spyOn(HttpClient.prototype, 'post').mockRejectedValue(new Error('payment required')); + const { client } = clientAndHeaders(); + + await expect(client.getFilesInFolders({ folderUuids: ['day-folder-uuid-1'] })).rejects.toThrow('payment required'); + }); +});