fix(techdocs): typos + add tests + requested changes

This commit is contained in:
Remi
2020-12-21 01:03:01 +01:00
parent 134888fb21
commit b9e6d305e2
9 changed files with 166 additions and 95 deletions
@@ -34,6 +34,7 @@ const createMockEntity = (annotations = {}) => {
const logger = winston.createLogger();
jest.spyOn(logger, 'info').mockReturnValue(logger);
jest.spyOn(logger, 'error').mockReturnValue(logger);
let publisher: PublisherBase;
@@ -55,63 +56,117 @@ beforeEach(() => {
});
describe('AwsS3Publish', () => {
it('should publish a directory', async () => {
mockFs({
'/path/to/generatedDirectory': {
'index.html': '',
'404.html': '',
assets: {
'main.css': '',
describe('publish', () => {
it('should publish a directory', async () => {
mockFs({
'/path/to/generatedDirectory': {
'index.html': '',
'404.html': '',
assets: {
'main.css': '',
},
},
},
});
const entity = createMockEntity();
expect(
await publisher.publish({
entity,
directory: '/path/to/generatedDirectory',
}),
).toBeUndefined();
mockFs.restore();
});
const entity = createMockEntity();
expect(
it('should fail to publish a directory', async () => {
mockFs({
'/path/to/generatedDirectory': {
'index.html': 'mock-error',
'404.html': '',
assets: {
'main.css': '',
},
},
});
const entity = createMockEntity();
await publisher.publish({
entity,
directory: '/path/to/generatedDirectory',
}),
).toBeUndefined();
mockFs.restore();
}).catch(error => expect(error).toBe(`Unable to upload file(s) to AWS S3. Error`))
mockFs.restore();
});
});
it('should return true if docs has been generated', async () => {
const entityMock = {
apiVersion: 'apiVersion',
kind: 'kind',
metadata: {
namespace: '/namespace',
describe('hasDocsBeenGenerated', () => {
it('should return true if docs has been generated', async () => {
const entityMock = {
apiVersion: 'apiVersion',
kind: 'kind',
metadata: {
namespace: '/namespace',
name: 'name',
},
};
const entityRootDir = `${entityMock.metadata.namespace}/${entityMock.kind}/${entityMock.metadata.name}`;
mockFs({
[entityRootDir]: {
'index.html': 'file-content',
},
});
expect(await publisher.hasDocsBeenGenerated(entityMock)).toBe(true);
mockFs.restore();
});
it('should return false if docs has not been generated', async () => {
const entityMock = {
apiVersion: 'apiVersion',
kind: 'kind',
metadata: {
namespace: 'namespace',
name: 'name',
},
};
expect(await publisher.hasDocsBeenGenerated(entityMock)).toBe(false);
});
});
describe('fetchTechDocsMetadata', () => {
it('should return tech docs metadata', async () => {
const entityNameMock = {
name: 'name',
},
};
const entityRootDir = `${entityMock.metadata.namespace}/${entityMock.kind}/${entityMock.metadata.name}`;
mockFs({
[entityRootDir]: {
'index.html': 'file-content',
},
namespace: '/namespace',
kind: 'kind',
};
const entityRootDir = `${entityNameMock.namespace}/${entityNameMock.kind}/${entityNameMock.name}`;
mockFs({
[entityRootDir]: {
'techdocs_metadata.json': 'file-content',
},
});
expect(await publisher.fetchTechDocsMetadata(entityNameMock)).toBe(
'file-content',
);
mockFs.restore();
});
expect(await publisher.hasDocsBeenGenerated(entityMock)).toBe(true);
mockFs.restore();
});
it('should return tech docs metadata', async () => {
const entityNameMock = {
name: 'name',
namespace: '/namespace',
kind: 'kind',
};
const entityRootDir = `${entityNameMock.namespace}/${entityNameMock.kind}/${entityNameMock.name}`;
mockFs({
[entityRootDir]: {
'techdocs_metadata.json': 'file-content',
},
it('should return an error if the techdocs_metadata.json file is not present', async () => {
const entityNameMock = {
name: 'name',
namespace: 'namespace',
kind: 'kind',
};
const entityRootDir = `${entityNameMock.namespace}/${entityNameMock.kind}/${entityNameMock.name}`;
await publisher
.fetchTechDocsMetadata(entityNameMock)
.catch(error =>
expect(error).toBe(
`The file ${entityRootDir}/techdocs_metadata.json doest not exist !`,
),
);
});
expect(await publisher.fetchTechDocsMetadata(entityNameMock)).toBe(
'file-content',
);
mockFs.restore();
});
});
@@ -55,26 +55,26 @@ export class AwsS3Publish implements PublisherBase {
// Check if the defined bucket exists. Being able to connect means the configuration is good
// and the storage client will work.
storageClient
.headObject({
storageClient.headBucket(
{
Bucket: bucketName,
Key: (credentialsJson as CredentialsOptions).accessKeyId,
})
.promise()
.then(() => {
logger.info(
`Successfully connected to the AWS S3 bucket ${bucketName}.`,
);
})
.catch(reason => {
logger.error(
`Could not retrieve metadata about the AWS S3 bucket ${bucketName}. ` +
'Make sure the AWS project and the bucket exists and the access key located at the path ' +
"techdocs.publisher.awsS3.credentials defined in app config has the role 'Storage Object Creator'. " +
'Refer to https://backstage.io/docs/features/techdocs/using-cloud-storage',
);
throw new Error(`from AWS client library: ${reason.message}`);
});
},
err => {
if (err) {
logger.error(
`Could not retrieve metadata about the AWS S3 bucket ${bucketName}. ` +
'Make sure the AWS project and the bucket exists and the access key located at the path ' +
"techdocs.publisher.awsS3.credentials defined in app config has the role 'Storage Object Creator'. " +
'Refer to https://backstage.io/docs/features/techdocs/using-cloud-storage',
);
throw new Error(`from AWS client library: ${err.message}`);
} else {
logger.info(
`Successfully connected to the AWS S3 bucket ${bucketName}.`,
);
}
},
);
return new AwsS3Publish(storageClient, bucketName, logger);
}
@@ -95,7 +95,7 @@ export class AwsS3Publish implements PublisherBase {
*/
publish({ entity, directory }: PublishRequest): Promise<void> {
return new Promise(async (resolve, reject) => {
// Note: GCS manages creation of parent directories if they do not exist.
// Note: S3 manages creation of parent directories if they do not exist.
// So collecting path of only the files is good enough.
const allFilesToUpload = await getFileTreeRecursively(directory);
@@ -123,7 +123,6 @@ export class AwsS3Publish implements PublisherBase {
uploadPromises.push(this.storageClient.upload(params).promise());
});
});
Promise.all(uploadPromises)
.then(() => {
this.logger.info(
@@ -212,7 +211,7 @@ export class AwsS3Publish implements PublisherBase {
return new Promise(resolve => {
const entityRootDir = `${entity.metadata.namespace}/${entity.kind}/${entity.metadata.name}`;
this.storageClient
.getObject({
.headObject({
Bucket: this.bucketName,
Key: `${entityRootDir}/index.html`,
})