diff --git a/src/services/file-service.ts b/src/services/file-service.ts index b246cb9a..0ffc42da 100644 --- a/src/services/file-service.ts +++ b/src/services/file-service.ts @@ -5,7 +5,7 @@ * Features: * - File upload via GraphQL fileUpload mutation * - File download with automatic authentication - * - Signed URL detection (skips Bearer token for signed URLs) + * - Signed URL detection (skips authentication for signed URLs) * - Directory creation and file existence checks * - Comprehensive error handling and status reporting */ @@ -139,7 +139,7 @@ export class FileService { * Downloads a file from Linear's private cloud storage. * * Automatically handles authentication for Linear URLs and creates directories - * as needed. Detects signed URLs to skip Bearer token authentication. + * as needed. Detects signed URLs to skip authentication. * * @param url - URL to Linear file (uploads.linear.app domain) * @param options - Download options including output path and overwrite behavior @@ -192,10 +192,9 @@ export class FileService { const urlObj = new URL(url); const isSignedUrl = urlObj.searchParams.has("signature"); - // Make HTTP request (with Bearer token only if not a signed URL) const headers: Record = {}; if (!isSignedUrl) { - headers["Authorization"] = `Bearer ${this.apiToken}`; + headers["Authorization"] = this.apiToken; } const response = await fetch(url, { diff --git a/tests/unit/services/file-service.test.ts b/tests/unit/services/file-service.test.ts index 52916ec4..ec348154 100644 --- a/tests/unit/services/file-service.test.ts +++ b/tests/unit/services/file-service.test.ts @@ -26,7 +26,7 @@ import { const mockFetch = vi.fn(); vi.stubGlobal("fetch", mockFetch); -const TEST_TOKEN = "lin_api_test_token"; +const TEST_TOKEN = "test_token"; beforeEach(() => { vi.clearAllMocks(); @@ -50,7 +50,10 @@ describe("downloadFile", () => { expect(mockFetch).not.toHaveBeenCalled(); }); - it("downloads file successfully", async () => { + it.each([ + ["a personal API key", "", { Authorization: TEST_TOKEN }], + ["no credentials on a signed URL", "?signature=test", {}], + ])("downloads with %s", async (_name, query, expectedHeaders) => { vi.mocked(isLinearUploadUrl).mockReturnValue(true); vi.mocked(extractFilenameFromUrl).mockReturnValue("image.png"); vi.mocked(access).mockRejectedValue(new Error("ENOENT")); // file doesn't exist @@ -65,19 +68,18 @@ describe("downloadFile", () => { }); const service = new FileService(TEST_TOKEN); - const result = await service.downloadFile( - "https://uploads.linear.app/org/file.png", - ); + const url = `https://uploads.linear.app/org/file.png${query}`; + const result = await service.downloadFile(url); expect(result).toEqual({ success: true, filePath: "image.png", }); expect(mockFetch).toHaveBeenCalledWith( - "https://uploads.linear.app/org/file.png", + url, expect.objectContaining({ method: "GET", - headers: { Authorization: `Bearer ${TEST_TOKEN}` }, + headers: expectedHeaders, }), ); expect(writeFile).toHaveBeenCalled();