Merge pull request #7838 from egnwd/fix-s3-integration-etags

Fix: [backstage-common] AWSS3UrlReader should throw NotModifiedError when etag matches
This commit is contained in:
Fredrik Adelöw
2021-11-06 10:37:45 +01:00
committed by GitHub
3 changed files with 147 additions and 73 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-common': patch
---
AWSS3UrlReader now throws a `NotModifiedError` (exported from @backstage/backend-common) when s3 returns a 304 response.
@@ -27,6 +27,7 @@ import { UrlReaderPredicateTuple } from './types';
import AWSMock from 'aws-sdk-mock';
import aws from 'aws-sdk';
import path from 'path';
import { NotModifiedError } from '@backstage/errors';
const treeResponseFactory = DefaultReadTreeResponseFactory.create({
config: new ConfigReader({}),
@@ -131,29 +132,37 @@ describe('AwsS3UrlReader', () => {
});
describe('read', () => {
AWSMock.setSDKInstance(aws);
AWSMock.mock(
'S3',
'getObject',
Buffer.from(
require('fs').readFileSync(
path.resolve(__dirname, '__fixtures__/awsS3/awsS3-mock-object.yaml'),
let awsS3UrlReader: AwsS3UrlReader;
beforeAll(() => {
AWSMock.setSDKInstance(aws);
AWSMock.mock(
'S3',
'getObject',
Buffer.from(
require('fs').readFileSync(
path.resolve(
__dirname,
'__fixtures__/awsS3/awsS3-mock-object.yaml',
),
),
),
),
);
const s3 = new aws.S3();
const awsS3UrlReader = new AwsS3UrlReader(
new AwsS3Integration(
readAwsS3IntegrationConfig(
new ConfigReader({
host: 'amazonaws.com',
accessKeyId: 'fake-access-key',
secretAccessKey: 'fake-secret-key',
}),
);
const s3 = new aws.S3();
awsS3UrlReader = new AwsS3UrlReader(
new AwsS3Integration(
readAwsS3IntegrationConfig(
new ConfigReader({
host: 'amazonaws.com',
accessKeyId: 'fake-access-key',
secretAccessKey: 'fake-secret-key',
}),
),
),
),
{ s3, treeResponseFactory },
);
{ s3, treeResponseFactory },
);
});
it('returns contents of an object in a bucket', async () => {
const response = await awsS3UrlReader.read(
@@ -176,32 +185,39 @@ describe('AwsS3UrlReader', () => {
});
describe('readUrl', () => {
AWSMock.setSDKInstance(aws);
let awsS3UrlReader: AwsS3UrlReader;
AWSMock.mock(
'S3',
'getObject',
Buffer.from(
require('fs').readFileSync(
path.resolve(__dirname, '__fixtures__/awsS3/awsS3-mock-object.yaml'),
beforeAll(() => {
AWSMock.setSDKInstance(aws);
AWSMock.mock(
'S3',
'getObject',
Buffer.from(
require('fs').readFileSync(
path.resolve(
__dirname,
'__fixtures__/awsS3/awsS3-mock-object.yaml',
),
),
),
),
);
);
const s3 = new aws.S3();
const s3 = new aws.S3();
const awsS3UrlReader = new AwsS3UrlReader(
new AwsS3Integration(
readAwsS3IntegrationConfig(
new ConfigReader({
host: 'amazonaws.com',
accessKeyId: 'fake-access-key',
secretAccessKey: 'fake-secret-key',
}),
awsS3UrlReader = new AwsS3UrlReader(
new AwsS3Integration(
readAwsS3IntegrationConfig(
new ConfigReader({
host: 'amazonaws.com',
accessKeyId: 'fake-access-key',
secretAccessKey: 'fake-secret-key',
}),
),
),
),
{ s3, treeResponseFactory },
);
{ s3, treeResponseFactory },
);
});
it('returns contents of an object in a bucket', async () => {
const response = await awsS3UrlReader.readUrl(
@@ -223,40 +239,89 @@ describe('AwsS3UrlReader', () => {
);
});
});
describe('readUrl with etag', () => {
let awsS3UrlReader: AwsS3UrlReader;
beforeAll(() => {
AWSMock.setSDKInstance(aws);
AWSMock.mock('S3', 'getObject', (_, callback) => {
callback({ statusCode: 304 }, null);
});
const s3 = new aws.S3();
awsS3UrlReader = new AwsS3UrlReader(
new AwsS3Integration(
readAwsS3IntegrationConfig(
new ConfigReader({
host: 'amazonaws.com',
accessKeyId: 'fake-access-key',
secretAccessKey: 'fake-secret-key',
}),
),
),
{ s3, treeResponseFactory },
);
});
it('returns contents of an object in a bucket', async () => {
await expect(
awsS3UrlReader.readUrl(
'https://test-bucket.s3.us-east-2.amazonaws.com/awsS3-mock-object.yaml',
{
etag: 'abc123',
},
),
).rejects.toThrow(NotModifiedError);
});
});
describe('readTree', () => {
const object: aws.S3.Types.Object = {
Key: 'awsS3-mock-object.yaml',
};
const objectList: aws.S3.ObjectList = [object];
const output: aws.S3.Types.ListObjectsV2Output = {
Contents: objectList,
};
AWSMock.setSDKInstance(aws);
AWSMock.mock('S3', 'listObjectsV2', output);
let awsS3UrlReader: AwsS3UrlReader;
AWSMock.mock(
'S3',
'getObject',
Buffer.from(
require('fs').readFileSync(
path.resolve(__dirname, '__fixtures__/awsS3/awsS3-mock-object.yaml'),
),
),
);
beforeAll(() => {
const object: aws.S3.Types.Object = {
Key: 'awsS3-mock-object.yaml',
};
const s3 = new aws.S3();
const awsS3UrlReader = new AwsS3UrlReader(
new AwsS3Integration(
readAwsS3IntegrationConfig(
new ConfigReader({
host: '.amazonaws.com',
accessKeyId: 'fake-access-key',
secretAccessKey: 'fake-secret-key',
}),
const objectList: aws.S3.ObjectList = [object];
const output: aws.S3.Types.ListObjectsV2Output = {
Contents: objectList,
};
AWSMock.setSDKInstance(aws);
AWSMock.mock('S3', 'listObjectsV2', output);
AWSMock.mock(
'S3',
'getObject',
Buffer.from(
require('fs').readFileSync(
path.resolve(
__dirname,
'__fixtures__/awsS3/awsS3-mock-object.yaml',
),
),
),
),
{ s3, treeResponseFactory },
);
);
const s3 = new aws.S3();
awsS3UrlReader = new AwsS3UrlReader(
new AwsS3Integration(
readAwsS3IntegrationConfig(
new ConfigReader({
host: '.amazonaws.com',
accessKeyId: 'fake-access-key',
secretAccessKey: 'fake-secret-key',
}),
),
),
{ s3, treeResponseFactory },
);
});
it('returns contents of an object in a bucket', async () => {
const response = await awsS3UrlReader.readTree(
'https://test.s3.us-east-2.amazonaws.com',
@@ -27,7 +27,7 @@ import {
} from './types';
import getRawBody from 'raw-body';
import { AwsS3Integration, ScmIntegrations } from '@backstage/integration';
import { ForwardedError } from '@backstage/errors';
import { ForwardedError, NotModifiedError } from '@backstage/errors';
import { ListObjectsV2Output, ObjectList } from 'aws-sdk/clients/s3';
const parseURL = (
@@ -163,6 +163,10 @@ export class AwsS3UrlReader implements UrlReader {
etag: etag,
};
} catch (e) {
if (e.statusCode === 304) {
throw new NotModifiedError();
}
throw new ForwardedError('Could not retrieve file from S3', e);
}
}