Skip to content

Commit

Permalink
Merge pull request #3 from vmurin/master
Browse files Browse the repository at this point in the history
Fix Content type and non directory iterations, add commands
  • Loading branch information
dittto authored May 16, 2018
2 parents a49c0e0 + a6d3d08 commit 8f4dff1
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 16 deletions.
26 changes: 20 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Sometimes you have some fixed assets that need to be uploaded to S3, such as ima

To use this, you'll need to specify the files to copy to your bucket. You do this in serverless.yml:

```
```yaml
plugins:
- serverless-s3-assets

Expand All @@ -30,19 +30,33 @@ The `test-html` is the relative folder name of the files to upload to the bucket
- isPublic: set this to true or false to override any ACL setting. True is the same as `public-read` and false is the same as `private`.
- cacheControl: Any cache control settings.
- cacheTime: This overrides any existing cache-control settings and sets a max-age for the file.
- contentType: The type of content we're uploading, if not obvious.
- contentType: default type of content we're uploading, if not obvious. Plugin will try to derive the type from file extention first.
- metadata: Any extra metadata to upload.

Any other options specified will be treated as sub-folder names, like `templates` above. This also shows you how to have sub-folders with different settings. By default, all files and folders within the specified root folder name will be uploaded with the same options as that root folder.

### Commands

The code will trigger automatically during a `serverless deploy` and `serverless remove`.

You can also carry out S3 deployment or removal independent from stack deployments:

```yaml
sls s3delpoy
sls s3remove
```

If you have defined multiple assets (folders) you can limit the action to a single one:

```yaml
sls s3delpoy --asset test-html
```

### Gotchas

Make sure you don't add any additional files to your bucket that you're specifying in s3Assets. If you do and then run `serverless remove` then those additional files will also be removed.

### Permissions

You don't need any special permissions for your Lambda as the code is run by Serverless instead.

### Commands

The code will trigger automatically during a `serverless deploy`.
33 changes: 31 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,49 @@ class S3Assets {
const interpreter = new ConfigIntepreter(FS, S3File, serverless.cli);
const s3Uploader = new S3Uploader(new AWS.S3({ signatureVersion: 'v4' }), FS, serverless.cli);
const config = serverless.service.custom && serverless.service.custom.s3Assets ? serverless.service.custom.s3Assets : [];
this.options = options;
this.commands = {
s3deploy: {
usage: 'Deploy assets to S3 bucket',
lifecycleEvents: [
'deploy'
],
options: {
asset: {
usage: 'Limit the deployment to a specific asset',
shortcut: 'a'
}
}
},
s3remove: {
usage: 'Remove deployed assets from S3 bucket',
lifecycleEvents: [
'remove'
],
options: {
asset: {
usage: 'Limit the removement to a specific asset',
shortcut: 'a'
}
}
}
};
this.hooks = {
's3deploy:deploy': this.deploy.bind(this, interpreter, s3Uploader, config),
's3remove:remove': this.remove.bind(this, interpreter, s3Uploader, config),
'after:deploy:deploy': this.deploy.bind(this, interpreter, s3Uploader, config),
'before:remove:remove': this.remove.bind(this, interpreter, s3Uploader, config)
};
}

deploy(interpreter, s3Uploader, config) {
const files = interpreter.get(config);
const files = interpreter.get(config, this.options);
return s3Uploader.uploadAllToBucket(files);
}

remove(interpreter, s3Uploader, config) {
const buckets = [];
const files = interpreter.get(config);
const files = interpreter.get(config, this.options);
for (let file of files) {
buckets[buckets.length] = file.getBucket();
}
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
{
"name": "serverless-s3-assets",
"version": "1.0.1",
"version": "1.0.2",
"description": "Uploads requested assets to S3 as part of Serverless deploy",
"main": "index.js",
"dependencies": {
"aws-sdk": "^2.6.9"
"aws-sdk": "^2.6.9",
"mime-types": "^2.1.18"
},
"devDependencies": {
"chai": "^3.5",
Expand Down
8 changes: 6 additions & 2 deletions src/ConfigInterpreter.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@ class ConfigInterpreter {
this.logger = logger;
}

get(config) {
get(config, options) {
// loop through each first-level of files / folders defined
const folders = [];
for (let folder of Object.keys(config)) {
if(options && options.asset && options.asset !== folder) {
this.logger.log('Skipping folder: ' + folder);
continue;
}
// gets the first level of data
const s3Folder = new this.S3File(folder, '', '', config[folder]);
s3Folder.addFiles(this.getFilesForFolder(folder), config[folder]);
Expand All @@ -35,7 +39,7 @@ class ConfigInterpreter {

getFilesForFolder(folder) {
try {
return this.FS.readdirSync(folder);
return this.FS.lstatSync(folder).isDirectory() ? this.FS.readdirSync(folder) : [];
} catch (error) {
this.logger.log('Failed to get files for "' + folder + '"');
return [];
Expand Down
4 changes: 3 additions & 1 deletion src/S3File.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use strict';

const mime = require('mime-types');

class S3File {
constructor(name, filePath, relativePath, settings) {
this.files = [];
Expand Down Expand Up @@ -69,7 +71,7 @@ class S3File {
}

getContentType() {
return this.settings.contentType || 'text/plain';
return mime.lookup(this.getFilePath()) || this.settings.contentType || 'text/plain';
}

/**
Expand Down
1 change: 0 additions & 1 deletion src/S3Uploader.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
'use strict';

class S3Uploader {
constructor(s3, FS, logger) {
this.s3 = s3;
Expand Down
65 changes: 63 additions & 2 deletions test/src/ConfigInterpreter.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,22 @@ describe('src/ConfigInterpreter', function () {
const FS = {
readdirSync: (folder) => {
return [folder, 'a', 'b'];
},
lstatSync: (folder) => {
return { isDirectory: () => true }
}
};
const config = new ConfigInterpreter(FS, {}, {});
const config = new ConfigInterpreter(FS, {}, console);
expect(config.getFilesForFolder('folder')).to.have.same.members(['folder', 'a', 'b']);
});

it('falls back to an empty array when trying to get files for a folder and an error occurs', function () {
const FS = {
readdirSync: (folder) => {
throw new Error('Test fail');
},
lstatSync: (folder) => {
return { isDirectory: () => true }
}
};
const logger = {
Expand Down Expand Up @@ -88,7 +94,10 @@ describe('src/ConfigInterpreter', function () {
return {getName: () => 'f', getFilePath: () => folder, getFiles: () => [FS.readdirSync('a/c/f/h'), FS.readdirSync('a/c/f/i')], addFiles: (files) => {FS.storedFiles.push({folder, files})}};
}
return {getName: () => folder, getFilePath: () => folder, getFiles: () => [], addFiles: (files) => {FS.storedFiles.push({folder, files})}};
}
},
lstatSync: (folder) => {
return { isDirectory: () => true }
}
};
const config = new ConfigInterpreter(FS, {}, {});
config.getSubs([FS.readdirSync('a')], {});
Expand All @@ -106,6 +115,9 @@ describe('src/ConfigInterpreter', function () {
return ['a/c/e', 'a/c/f', 'a/c/g'];
}
return [];
},
lstatSync: (folder) => {
return { isDirectory: () => true }
}
};
const config = new ConfigInterpreter(FS, FullS3FileTest, {});
Expand All @@ -117,4 +129,53 @@ describe('src/ConfigInterpreter', function () {
expect(files[0].getFiles()[1].getFiles()[1].getFilePath()).to.equal('a/c/f');
expect(files[0].getFiles()[1].getFiles()[2].getFilePath()).to.equal('a/c/g');
});

it('skip all folders except given in options', function () {
const FS = {
readdirSync: (folder) => {
if (folder === 'a') {
return ['a/b', 'a/c', 'a/d'];
}
if (folder === 'b') {
return ['b/a', 'b/b'];
}
return [];
},
lstatSync: (folder) => {
return { isDirectory: () => true }
}
};
const config = new ConfigInterpreter(FS, FullS3FileTest, console);
const files = config.get({'a': [], 'b': []}, {'asset': 'b' });
expect(files[0].getFilePath()).to.equal('b');
expect(files[0].getFiles()[0].getFilePath()).to.equal('b/a');
expect(files[0].getFiles()[1].getFilePath()).to.equal('b/b');
});

it('do not try to iterate files as folders', function () {
const FS = {
readdirSync: (folder) => {
if (folder === 'a') {
return ['a'];
}
if (folder === 'b') {
return ['b'];
}
return [];
},
lstatSync: (folder) => {
return { isDirectory: () => {
if (folder == 'a') {
return true
} else {
return false
}
}
}
}
};
const config = new ConfigInterpreter(FS, FullS3FileTest, console);
expect(config.getFilesForFolder('a')).to.have.same.members(['a']);
expect(config.getFilesForFolder('b')).to.have.same.members([]);
});
});

0 comments on commit 8f4dff1

Please sign in to comment.