import ApiClient from './ApiClient'; import { mapDeviceResponse, mapV5DeviceToV3Device } from './DeviceClient'; import { mapPresentationResponse } from './PresentationClient'; import { mapPlaylistResponse } from './PlaylistClient'; import { mapResourceResponse } from './ResourceClient'; import * as F from './types/Folder'; import * as D from './types/Device'; import urls from './urls'; import { V4LibraryQueryParams } from './types/Device'; const mapNestedFolderResponse = ( f: F.NestedFolderResponse, ): F.NestedFolder => ({ id: f.id, name: f.name, type: f.type, description: f.description, resource: mapResourceResponse(f.resource), }); const mapFolderResponse = (f: F.FolderResponse): F.Folder => ({ id: f.id, name: f.name, type: f.type, path: f.path || [], description: f.description, devices: (f.devices || []).map(mapDeviceResponse), presentations: (f.presentations || []).map(mapPresentationResponse), playlists: (f.playlists || []).map(mapPlaylistResponse), folders: (f.folders || []).map(mapNestedFolderResponse), resource: mapResourceResponse(f.resource), }); const mapVirtualFolderResponse = ( f: F.VirtualFolderResponse, ): F.VirtualFolder => ({ devices: (f.devices || []).map(mapDeviceResponse), presentations: (f.presentations || []).map(mapPresentationResponse), playlists: (f.playlists || []).map(mapPlaylistResponse), folders: (f.folders || []).map(mapNestedFolderResponse), }); export default class FolderClient { async getDevicesFolder( this: ApiClient, query?: string, ): Promise { const data = await this.requestProtected< F.GetDevicesFolderRequest, F.GetDevicesFolderResponse >({ method: 'GET', url: urls.devicesFolder(query), }); return mapVirtualFolderResponse(data); } async getV4DevicesFolder( this: ApiClient, query?: string, ): Promise { const data = await this.requestProtected< F.GetDevicesFolderRequest, F.GetDevicesFolderResponse >({ method: 'GET', url: urls.v4DevicesFolder(query), }); return mapVirtualFolderResponse(data); } async getV5DevicesFolder( this: ApiClient, query?: string, ): Promise { const data = await this.requestProtected< D.GetV5DevicesRequest, D.GetV5DevicesResponse >({ method: 'GET', url: urls.v5Devices(query), }); const v5DevicesResponse = data.data || []; const v3DevicesResponse = v5DevicesResponse.map((d) => mapV5DeviceToV3Device(d), ); const virtualFolderResponse = { devices: v3DevicesResponse, presentations: [], playlists: [], folders: [], }; return mapVirtualFolderResponse(virtualFolderResponse); } async getLibraryFolder(this: ApiClient): Promise { const data = await this.requestProtected< F.GetDevicesFolderRequest, F.GetDevicesFolderResponse >({ method: 'GET', url: urls.libraryFolder(), }); return mapVirtualFolderResponse(data); } async getV4LibraryFolder( this: ApiClient, params?: V4LibraryQueryParams, ): Promise { const data = await this.requestProtected< F.GetDevicesFolderRequest, F.GetDevicesFolderResponse >({ method: 'GET', url: params ? `${urls.v4DevicesLibrary()}?${new URLSearchParams( params as any, ).toString()}` : `${urls.v4DevicesLibrary()}`, }); return mapVirtualFolderResponse(data); } async getV4LibraryLiteFolder( this: ApiClient, params?: V4LibraryQueryParams, ): Promise { const data = await this.requestProtected< F.GetDevicesFolderRequest, F.GetDevicesFolderResponse >({ method: 'GET', url: params ? `${urls.v4DevicesLibraryLite()}?${new URLSearchParams( params as any, ).toString()}` : `${urls.v4DevicesLibraryLite()}`, }); return mapVirtualFolderResponse(data); } async getFolder(this: ApiClient, folderId: string): Promise { const data = await this.requestProtected< F.GetFolderRequest, F.GetFolderResponse >({ method: 'GET', url: urls.folder(folderId), }); return mapFolderResponse(data); } async createFolder( this: ApiClient, folder: F.CreateFolder, ): Promise { const data = await this.requestProtected< F.CreateFolderRequest, F.CreateFolderResponse >({ method: 'POST', url: urls.folders(), body: { name: folder.name, description: folder.description, type: folder.type, }, }); return mapFolderResponse(data); } async updateFolder( this: ApiClient, folderId: string, folder: Partial, ): Promise { const data = await this.requestProtected< F.UpdateFolderRequest, F.UpdateFolderResponse >({ method: 'PATCH', url: urls.folder(folderId), body: { name: folder.name || '', description: folder.description || '', }, }); return mapFolderResponse(data); } async deleteFolder(this: ApiClient, folderId: string): Promise { const data = await this.requestProtected< F.DeleteFolderRequest, F.DeleteFolderResponse >({ method: 'DELETE', url: urls.folder(folderId), }); return data; } async moveFolderToFolder( this: ApiClient, folderId: string, parentFolderId: string | null, ): Promise { await this.requestProtected<{ folder_id: string | null }, void>({ method: 'POST', url: urls.folderMoveToFolder(folderId), body: { folder_id: parentFolderId, }, }); } async copyFolder( this: ApiClient, folderId: string, params: { name?: string; targetFolderId?: string; copyOutOfTreeUnowned?: boolean; } = {}, ): Promise { const data = await this.requestProtected< F.CopyFolderRequest, F.CopyFolderResponse >({ method: 'POST', url: urls.folderCopy(folderId), body: { name: params.name, ...(params.targetFolderId ? { target_folder_id: params.targetFolderId } : {}), ...(params.copyOutOfTreeUnowned !== undefined ? { copy_out_of_tree_unowned: params.copyOutOfTreeUnowned } : {}), }, }); // Preflight: when the server flags out-of-tree unowned content it copies // nothing and returns only the flag (no folder — so no `id` — to map), so // short-circuit. A confirmed copy always returns the folder (even if it // echoes the flag), so the `'id' in data` guard keeps it on the real branch. if (!('id' in data)) { return { hasOutOfTreeUnowned: true, supportingContentFolderCreated: false, }; } return { ...mapFolderResponse(data), supportingContentFolderCreated: !!data.supporting_content_folder_created, }; } }