Skip to content

Commit

Permalink
add a globFileUploadParamsList option to set properties on uploaded f…
Browse files Browse the repository at this point in the history
…iles
  • Loading branch information
maxletourneur committed Apr 18, 2022
1 parent 37e26a6 commit 67ffca0
Show file tree
Hide file tree
Showing 7 changed files with 34,498 additions and 61 deletions.
42 changes: 39 additions & 3 deletions README.md
Expand Up @@ -43,10 +43,11 @@
```

4. After these steps your `angular.json` is updated with a new builder:

```json
"deploy": {
"builder": "@jefiozie/ngx-aws-deploy:deploy",
"options": {}
"builder": "@jefiozie/ngx-aws-deploy:deploy",
"options": {}
}
```

Expand All @@ -62,7 +63,42 @@ npx cross-env NG_DEPLOY_AWS_ACCESS_KEY_ID=1234 NG_DEPLOY_AWS_SECRET_ACCESS_KEY=3
npx cross-env ... NG_DEPLOY_AWS_CF_DISTRIBUTION_ID=1234 ... ng deploy
```

7. Run `ng deploy` to deploy your application to Amazon S3.
7. To apply properties on uploaded files, use the `NG_DEPLOY_AWS_GLOB_FILE_UPLOAD_PARAMS_LIST` variable or the target option `globFileUploadParamsList` as shown below.
We use an array of objects to represent the different configurations.
Each config object needs a `glob` property to define on which files the params will be set.
Other properties on the config object are the params to apply.
For informations about the possible params (ACL, Bucket, CacheControl, etc), checkout the documentation of the `s3.upload` method (https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#upload-property)

```json
"deploy": {
"builder": "@jefiozie/ngx-aws-deploy:deploy",
"options": {
"globFileUploadParamsList": [
{
"glob": "*",
"ACL": "public-read",
"CacheControl": "max-age=3600"
},
{
"glob": "*.html",
"CacheControl": "max-age=300"
}
]
}
},
```

The order of the config objects matters, the one that comes last is the one that will be used, as shown above:

- CacheControl is set for all files but it's overidden by the next config object only for html files. Convenient for declaring exceptions and not repeat global settings.

To set this using the environment variable `NG_DEPLOY_AWS_GLOB_FILE_UPLOAD_PARAMS_LIST`, simply stringify the config object to JSON with `JSON.stringify`.

```bash
npx cross-env ... NG_DEPLOY_AWS_GLOB_FILE_UPLOAD_PARAMS_LIST='[{"glob":"*","ACL":"public-read","CacheControl":"max-age=3600"},{"glob":"*.html","CacheControl":"max-age=300"}]' ... ng deploy
```

8. Run `ng deploy` to deploy your application to Amazon S3.

🚀**_Happy deploying!_** 🚀

Expand Down
52 changes: 46 additions & 6 deletions libs/ngx-aws-deploy/src/lib/deploy/config.ts
@@ -1,25 +1,65 @@
import { Schema } from './schema';
import { GlobFileUploadParamsList, Schema } from './schema';

export const getAccessKeyId = (): string => {
return process.env.NG_DEPLOY_AWS_ACCESS_KEY_ID as string || process.env.AWS_ACCESS_KEY_ID as string;
return (
(process.env.NG_DEPLOY_AWS_ACCESS_KEY_ID as string) ||
(process.env.AWS_ACCESS_KEY_ID as string)
);
};

export const getSecretAccessKey = (): string => {
return process.env.NG_DEPLOY_AWS_SECRET_ACCESS_KEY as string || process.env.AWS_SECRET_ACCESS_KEY as string;
return (
(process.env.NG_DEPLOY_AWS_SECRET_ACCESS_KEY as string) ||
(process.env.AWS_SECRET_ACCESS_KEY as string)
);
};

export const getBucket = (builderConfig: Schema): string => {
return process.env.NG_DEPLOY_AWS_BUCKET || (builderConfig.bucket as string);
};

export const getRegion = (builderConfig: Schema): string => {
return process.env.NG_DEPLOY_AWS_REGION || (builderConfig.region as string) || process.env.AWS_DEFAULT_REGION;
return (
process.env.NG_DEPLOY_AWS_REGION ||
(builderConfig.region as string) ||
process.env.AWS_DEFAULT_REGION
);
};

export const getSubFolder = (builderConfig: Schema): string => {
return process.env.NG_DEPLOY_AWS_SUB_FOLDER || (builderConfig.subFolder as string);
return (
process.env.NG_DEPLOY_AWS_SUB_FOLDER || (builderConfig.subFolder as string)
);
};

const validateGlobFileUploadParamsList = (
paramsList: GlobFileUploadParamsList
) => Array.isArray(paramsList) && !paramsList.some((params) => !params.glob);

export const getGlobFileUploadParamsList = (
builderConfig: Schema
): GlobFileUploadParamsList => {
let globFileUploadParamsList = [];
try {
globFileUploadParamsList = process.env
.NG_DEPLOY_AWS_GLOB_FILE_UPLOAD_PARAMS_LIST
? JSON.parse(process.env.NG_DEPLOY_AWS_GLOB_FILE_UPLOAD_PARAMS_LIST)
: builderConfig.globFileUploadParamsList || [];
} catch (e) {
console.error(
'Invalid JSON for NG_DEPLOY_AWS_GLOB_FILE_UPLOAD_PARAMS_LIST',
e
);
}

return validateGlobFileUploadParamsList(globFileUploadParamsList)
? globFileUploadParamsList
: [];
};

export const getCfDistributionId = (builderConfig: Schema): string => {
return process.env.NG_DEPLOY_AWS_CF_DISTRIBUTION_ID || (builderConfig.cfDistributionId as string);
return (
process.env.NG_DEPLOY_AWS_CF_DISTRIBUTION_ID ||
(builderConfig.cfDistributionId as string)
);
};
26 changes: 18 additions & 8 deletions libs/ngx-aws-deploy/src/lib/deploy/index.ts
Expand Up @@ -44,13 +44,21 @@ export default createBuilder(
targetString += `:${context.target.configuration}`;
}

const { bucket, region, subFolder } = await context.getTargetOptions(
targetFromTargetString(targetString)
);
const {
bucket,
region,
subFolder,
globFileUploadParamsList,
} = await context.getTargetOptions(targetFromTargetString(targetString));

const deployConfig = { bucket, region, subFolder } as Pick<
const deployConfig = {
bucket,
region,
subFolder,
globFileUploadParamsList,
} as Pick<
Schema,
'bucket' | 'region' | 'subFolder'
'bucket' | 'region' | 'subFolder' | 'globFileUploadParamsList'
>;

let buildResult: BuilderOutput;
Expand Down Expand Up @@ -82,8 +90,10 @@ export default createBuilder(
context.logger.info(`✔ Build Completed`);
}
if (buildResult.success) {
const filesPath = buildResult.outputPath as string;
const files = getFiles(filesPath);
const { outputPath } = await context.getTargetOptions(
targetFromTargetString(buildTarget.name)
);
const files = getFiles(`${outputPath}`);

if (files.length === 0) {
throw new Error(
Expand All @@ -93,7 +103,7 @@ export default createBuilder(
if (getAccessKeyId() || getSecretAccessKey()) {
context.logger.info('Start uploading files...');
const uploader = new Uploader(context, deployConfig);
const success = await uploader.upload(files, filesPath);
const success = await uploader.upload(files, `${outputPath}`);
if (success) {
context.logger.info('✔ Finished uploading files...');

Expand Down
3 changes: 3 additions & 0 deletions libs/ngx-aws-deploy/src/lib/deploy/schema.d.ts
@@ -1,3 +1,5 @@
export type GlobFileUploadParams = { glob: string } & PutObjectRequest;
export type GlobFileUploadParamsList = Array<GlobFileUploadParams>;
export interface Schema {
configuration?: string;
buildTarget?: string;
Expand All @@ -9,4 +11,5 @@ export interface Schema {
bucket?: string;
subFolder?: string;
cfDistributionId?: string;
globFileUploadParamsList?: GlobFileUploadParamsList;
}
4 changes: 4 additions & 0 deletions libs/ngx-aws-deploy/src/lib/deploy/schema.json
Expand Up @@ -50,6 +50,10 @@
"buildTarget": {
"type": "string",
"description": "A named build target, as specified in the `configurations` section of angular.json . Each named target is accompanied by a configuration of option defaults for that target. This is equivalent as calling the command `ng build --configuration=XXX`."
},
"globFileUploadParamsList": {
"type": "string",
"description": "A stringified array of objects representing params to pass to the s3.upload method (https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#upload-property). In addition to the params, each object has a glob property allowing to target params to specific files."
}
}
}
29 changes: 27 additions & 2 deletions libs/ngx-aws-deploy/src/lib/deploy/uploader.ts
Expand Up @@ -4,12 +4,19 @@ import { HeadBucketRequest, PutObjectRequest } from 'aws-sdk/clients/s3';
import * as mimeTypes from 'mime-types';
import * as fs from 'fs';
import * as path from 'path';
import { Schema } from './schema';
import * as minimatch from 'minimatch';
import {
GlobFileUploadParams,
GlobFileUploadParamsList,
Schema,
} from './schema';
import {
getAccessKeyId,
getBucket,
getGlobFileUploadParamsList,
getRegion,
getSecretAccessKey, getSubFolder
getSecretAccessKey,
getSubFolder,
} from './config';

export class Uploader {
Expand All @@ -19,13 +26,17 @@ export class Uploader {
private _region: string;
private _subFolder: string;
private _builderConfig: Schema;
private _globFileUploadParamsList: GlobFileUploadParamsList;

constructor(context: BuilderContext, builderConfig: Schema) {
this._context = context;
this._builderConfig = builderConfig;
this._bucket = getBucket(this._builderConfig);
this._region = getRegion(this._builderConfig);
this._subFolder = getSubFolder(this._builderConfig);
this._globFileUploadParamsList = getGlobFileUploadParamsList(
this._builderConfig
);

AWS.config.update({ region: this._region });
this._s3 = new AWS.S3({
Expand Down Expand Up @@ -76,6 +87,19 @@ export class Uploader {

public async uploadFile(localFilePath: string, originFilePath: string) {
const fileName = path.basename(localFilePath);
const globFileUploadParamsForFile = this._globFileUploadParamsList.filter(
(params: GlobFileUploadParams) => minimatch(originFilePath, params.glob)
);

const mergedParamsForFile = globFileUploadParamsForFile.reduce(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
(acc, { glob, ...params }) => ({
...acc,
...params,
}),
{}
);

const body = fs.createReadStream(localFilePath);
body.on('error', function (err) {
console.log('File Error', err);
Expand All @@ -88,6 +112,7 @@ export class Uploader {
: originFilePath,
Body: body,
ContentType: mimeTypes.lookup(fileName) || undefined,
...mergedParamsForFile,
};

await this._s3
Expand Down

0 comments on commit 67ffca0

Please sign in to comment.