diff --git a/packages/backend-common/src/reading/GitlabUrlReader.test.ts b/packages/backend-common/src/reading/GitlabUrlReader.test.ts index 9c2e428239..a2f873c580 100644 --- a/packages/backend-common/src/reading/GitlabUrlReader.test.ts +++ b/packages/backend-common/src/reading/GitlabUrlReader.test.ts @@ -79,7 +79,7 @@ describe('GitlabUrlReader', () => { const worker = setupServer(); msw.setupDefaultHandlers(worker); - describe('implementation', () => { + describe('read', () => { beforeEach(() => { worker.use( rest.get('*/api/v4/projects/:name', (_, res, ctx) => @@ -180,6 +180,53 @@ describe('GitlabUrlReader', () => { }); }); + describe('readUrl', () => { + const [{ reader }] = GitlabUrlReader.factory({ + config: new ConfigReader({}), + logger, + treeResponseFactory, + }); + + it('should throw NotModified on HTTP 304', async () => { + worker.use( + rest.get('*/api/v4/projects/:name', (_, res, ctx) => + res(ctx.status(200), ctx.json({ id: 12345 })), + ), + rest.get('*', (req, res, ctx) => { + expect(req.headers.get('If-None-Match')).toBe('999'); + return res(ctx.status(304)); + }), + ); + + await expect( + reader.readUrl!( + 'https://gitlab.com/groupA/teams/teamA/subgroupA/repoA/-/blob/branch/my/path/to/file.yaml', + { + etag: '999', + }, + ), + ).rejects.toThrow(NotModifiedError); + }); + + it('should return etag in response', async () => { + worker.use( + rest.get('*/api/v4/projects/:name', (_, res, ctx) => + res(ctx.status(200), ctx.json({ id: 12345 })), + ), + rest.get('*', (req, res, ctx) => { + return res(ctx.status(200), ctx.set('ETag', '999'), ctx.body('foo')); + }), + ); + + const result = await reader.readUrl!( + 'https://gitlab.com/groupA/teams/teamA/subgroupA/repoA/-/blob/branch/my/path/to/file.yaml', + ); + expect(result.etag).toBe('999'); + const content = await result.buffer(); + expect(content.toString()).toBe('foo'); + }); + }); + describe('readTree', () => { const archiveBuffer = fs.readFileSync( path.resolve('src', 'reading', '__fixtures__', 'gitlab-archive.tar.gz'), diff --git a/packages/backend-common/src/reading/GitlabUrlReader.ts b/packages/backend-common/src/reading/GitlabUrlReader.ts index aa215aee47..3fd13e324c 100644 --- a/packages/backend-common/src/reading/GitlabUrlReader.ts +++ b/packages/backend-common/src/reading/GitlabUrlReader.ts @@ -34,6 +34,8 @@ import { SearchOptions, SearchResponse, UrlReader, + ReadUrlResponse, + ReadUrlOptions, } from './types'; export class GitlabUrlReader implements UrlReader { @@ -54,20 +56,37 @@ export class GitlabUrlReader implements UrlReader { ) {} async read(url: string): Promise { + const response = await this.readUrl(url); + return response.buffer(); + } + + async readUrl( + url: string, + options?: ReadUrlOptions, + ): Promise { const builtUrl = await getGitLabFileFetchUrl(url, this.integration.config); let response: Response; try { - response = await fetch( - builtUrl, - getGitLabRequestOptions(this.integration.config), - ); + response = await fetch(builtUrl, { + headers: { + ...getGitLabRequestOptions(this.integration.config).headers, + ...(options?.etag && { 'If-None-Match': options.etag }), + }, + }); } catch (e) { throw new Error(`Unable to read ${url}, ${e}`); } + if (response.status === 304) { + throw new NotModifiedError(); + } + if (response.ok) { - return Buffer.from(await response.text()); + const etag = response.headers.get('ETag') + ? response.headers.get('ETag')! + : undefined; + return { buffer: async () => Buffer.from(await response.text()), etag }; } const message = `${url} could not be read as ${builtUrl}, ${response.status} ${response.statusText}`;