Skip to content

Commit 55468fe

Browse files
authored
Sprint 33 master (#40)
* Add bucket name validation rules * Fix `Error! Cannot convert undefined or null to object` error on no attachments object in message * Add attachment size check on file reading * Add empty response to Delete file action when file already not exists * Add Get New And Updated files trigger * Add files metadata polling trigger * Add possibility to retrieve more than 1,000 files for 'Get filenames' action
1 parent 22b4992 commit 55468fe

25 files changed

+775
-155
lines changed

.circleci/config.yml

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,8 @@ jobs:
1717
- run:
1818
name: Running Mocha Unit&Integration Tests
1919
command: npm test && npm run integration-test
20-
docs:
21-
docker:
22-
- image: circleci/node:12-stretch
23-
steps:
24-
- checkout
25-
- run:
26-
name: Download Script
27-
command: mkdir $HOME/script && curl -o $HOME/script/create_pr.bash https://raw.githubusercontent.com/elasticio/elasticio.github.io/master/scripts/create_pr.bash
28-
- run:
29-
shell: /bin/bash
30-
name: Create PR
31-
command: bash $HOME/script/create_pr.bash -p $PWD -n sftp
32-
3320
workflows:
3421
version: 2
3522
build_and_test:
3623
jobs:
3724
- test
38-
deploy_docs:
39-
jobs:
40-
- docs:
41-
filters:
42-
branches:
43-
only:
44-
- master

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
# 1.3.0 (February 13, 2020)
2+
3+
## General Changes
4+
* Add Get New and Updated S3 Objects trigger
5+
* Fix `Error! Cannot convert undefined or null to object` error on no attachments object in message in Write file action
6+
* Add attachment size limitation
7+
* Add empty response to Delete file action when file already not exists
8+
* Add possibility to retrieve more than 1,000 files for 'Get filenames' action
9+
* Improved error handling for Get filenames action
10+
* Removed invalid docs job from circle ci
11+
112
# 1.2.1 (December 26, 2019)
213

314
## General Changes

README.md

Lines changed: 64 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
* [Description](#description)
66
* [Purpose](#purpose)
77
* [Completeness Matrix](#completeness-matrix)
8-
* [How works. SDK version](#how-works.-sdk-version)
8+
* [How works. SDK version](#how-works-sdk-version)
99
* [Requirements](#requirements)
1010
* [Environment variables](#environment-variables)
1111
* [Credentials](#credentials)
1212
* [Access Key Id](#access-key-id)
1313
* [Secret Access Key](#secret-access-key)
1414
* [Region](#region)
15+
* [Triggers](#triggers)
16+
* [Get New and Updated S3 Objects](#get-new-and-updated-s3-objects)
1517
* [Actions](#actions)
1618
* [Write file](#write-file)
1719
* [Read file](#read-file)
@@ -30,7 +32,7 @@ This is the component for working with AWS S3 object storage service on [elastic
3032
The component provides ability to connect to Amazon Simple Storage Service (Amazon S3) object storage service.
3133

3234
### Completeness Matrix
33-
![image](https://user-images.githubusercontent.com/36419533/70732156-97b31680-1d10-11ea-826f-5a2dd55b8251.png)
35+
![image](https://user-images.githubusercontent.com/22715422/73740795-93740a00-4751-11ea-869d-ff9433f39c4f.png)
3436

3537
[Completeness Matrix](https://docs.google.com/spreadsheets/d/1LhKgsTvF32YAmBRh742YxnkrMEGlPEERJc9B6pj4L6E/edit#gid=0)
3638

@@ -40,17 +42,13 @@ The component is based on [AWS S3 SDK](https://aws.amazon.com/sdk-for-node-js/ '
4042
## Requirements
4143

4244
#### Environment variables
43-
For integration-tests is required to specify following environment variables:
44-
45-
`ACCESS_KEY_ID` - access key ID;
46-
47-
`ACCESS_KEY_SECRET` - secret access key.
48-
49-
`REGION` - region.
50-
51-
For debugging purposes there is:
52-
53-
`LOG_LEVEL` - `trace` | `debug` | `info` | `warning` | `error` that controls logger level.
45+
Name|Mandatory|Description|Values|
46+
|----|---------|-----------|------|
47+
|`LOG_LEVEL`| false | Controls logger level | `trace`, `debug`, `info`, `warning`, `error` |
48+
|`ATTACHMENT_MAX_SIZE`| false | For `elastic.io` attachments configuration. Maximal possible attachment size in bytes. By default set to 1000000 and according to platform limitations CAN'T be bigger than that. | Up to `1000000` bytes|
49+
|`ACCESS_KEY_ID`| false | For integration-tests is required to specify this variable | |
50+
|`ACCESS_KEY_SECRET`| false | For integration-tests is required to specify this variable | |
51+
|`REGION` | false | For integration-tests is required to specify this variable | |
5452

5553
## Credentials
5654
Access keys consist of two parts: an access key ID and a secret access key.
@@ -64,7 +62,59 @@ A secret access key (for example, `wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY`).
6462

6563
### Region
6664
Example: `ca-central-1`.
67-
65+
66+
67+
## Triggers
68+
### Get New and Updated S3 Objects
69+
Triggers to get all new and updated s3 objects since last polling.
70+
71+
#### List of Expected Config fields
72+
- **Bucket Name and folder** - name of S3 bucket to read files from
73+
- **Emit Behaviour**: Options are: default is `Emit Individually` emits each object in separate message, `Fetch All` emits all objects in one message
74+
- **Start Time**: Start datetime of polling. Default min date:`-271821-04-20T00:00:00.000Z`
75+
- **End Time**: End datetime of polling. Default max date: `+275760-09-13T00:00:00.000Z`
76+
- **Enable File Attachments**: End datetime of polling. Default max date: `+275760-09-13T00:00:00.000Z`
77+
78+
<details>
79+
<summary>Output metadata</summary>
80+
81+
```json
82+
{
83+
"type": "object",
84+
"properties": {
85+
"Key": {
86+
"type": "string",
87+
"required": true
88+
},
89+
"LastModified": {
90+
"type": "string",
91+
"required": true
92+
},
93+
"ETag": {
94+
"type": "string",
95+
"required": true
96+
},
97+
"Size": {
98+
"type": "number",
99+
"required": true
100+
},
101+
"StorageClass": {
102+
"type": "string",
103+
"required": true
104+
},
105+
"Owner": {
106+
"type": "object",
107+
"properties": {
108+
"ID": {
109+
"type": "string",
110+
"required": true
111+
}
112+
}
113+
}
114+
}
115+
}
116+
```
117+
</details>
68118

69119
## Actions
70120
### Write file

component.json

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,86 @@
2222
}
2323
}
2424
},
25+
"triggers": {
26+
"pollingTrigger": {
27+
"title": "Get New and Updated S3 Objects",
28+
"type": "polling",
29+
"description": "Get New and Updated S3 Objects by provided time limits",
30+
"main": "./lib/triggers/pollingTrigger.js",
31+
"fields": {
32+
"bucketName": {
33+
"viewClass": "TextFieldView",
34+
"label": "Bucket Name and Folder",
35+
"placeholder": "my-bucket/my-folder",
36+
"required": true
37+
},
38+
"emitBehaviour": {
39+
"label": "Emit Behaviour",
40+
"viewClass": "SelectView",
41+
"required": false,
42+
"prompt": "Default Emit Individually",
43+
"model": {
44+
"emitIndividually": "Emit Individually",
45+
"fetchAll": "Fetch All"
46+
}
47+
},
48+
"startTime": {
49+
"label": "Start Time",
50+
"viewClass": "TextFieldView",
51+
"required": false,
52+
"note": "Input format: ISO 8601. Default: minimum time",
53+
"placeholder": "2020-01-01T01:01:01Z"
54+
},
55+
"endTime": {
56+
"label": "End Time",
57+
"viewClass": "TextFieldView",
58+
"required": false,
59+
"note": "Input format: ISO 8601. Default: maximum time",
60+
"placeholder": "2020-01-01T01:01:01Z"
61+
},
62+
"enableFileAttachments": {
63+
"viewClass": "CheckBoxView",
64+
"label": "Enable File Attachments"
65+
}
66+
},
67+
"metadata": {
68+
"out": {
69+
"type": "object",
70+
"properties": {
71+
"Key": {
72+
"type": "string",
73+
"required": true
74+
},
75+
"LastModified": {
76+
"type": "string",
77+
"required": true
78+
},
79+
"ETag": {
80+
"type": "string",
81+
"required": true
82+
},
83+
"Size": {
84+
"type": "number",
85+
"required": true
86+
},
87+
"StorageClass": {
88+
"type": "string",
89+
"required": true
90+
},
91+
"Owner": {
92+
"type": "object",
93+
"properties": {
94+
"ID": {
95+
"type": "string",
96+
"required": true
97+
}
98+
}
99+
}
100+
}
101+
}
102+
}
103+
}
104+
},
25105
"actions": {
26106
"streamToFile": {
27107
"title": "Write file",

lib/actions/deleteObject.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,19 @@ const { Client } = require('../client');
55
exports.process = async function (msg, cfg) {
66
const client = new Client(this.logger, cfg);
77
const bucketName = msg.body.bucketName ? msg.body.bucketName : cfg.bucketName;
8+
9+
try {
10+
await client.getObjectMetadata(bucketName, msg.body.filename);
11+
} catch (err) {
12+
if (err.code === 'NotFound') {
13+
this.logger.warn('Provided filename is not found: nothing to delete');
14+
return messages.newEmptyMessage();
15+
}
16+
this.logger.error('Error occurred while getting file metadata: %j', err);
17+
throw err;
18+
}
19+
20+
this.logger.info('Provided filename is found: deleting...');
821
await client.deleteObject(bucketName, msg.body.filename);
9-
await this.emit('data', messages.newMessageWithBody({ filename: msg.body.filename }));
22+
return messages.newMessageWithBody({ filename: msg.body.filename });
1023
};

lib/actions/getAllFilesInBucket.js

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,25 @@ const { createAWSInputs } = require('../utils/utils');
66
exports.process = async function (msg, cfg) {
77
const client = new Client(this.logger, cfg);
88
const bucketName = msg.body.bucketName ? msg.body.bucketName : cfg.bucketName;
9-
const data = await client.listObjects(createAWSInputs(bucketName));
10-
data.Contents.forEach((c) => {
11-
this.emit('data', messages.newMessageWithBody({ filename: c.Key }));
12-
});
13-
this.emit('end');
9+
if (!bucketName) {
10+
throw new Error(`Bucket name cant be empty. Provided bucket name: ${bucketName}`);
11+
}
12+
const awsInput = createAWSInputs(bucketName);
13+
if (awsInput.Prefix === null || awsInput.Prefix === undefined) {
14+
awsInput.Prefix = '/';
15+
awsInput.Delimiter = '/';
16+
}
17+
const exist = await client.exist(awsInput);
18+
if (!exist) {
19+
throw new Error(`Bucket ${bucketName} not exist`);
20+
}
21+
const data = await client.listObjects(awsInput);
22+
if (data.length === 0) {
23+
this.emit('data', messages.newEmptyMessage());
24+
}
25+
26+
for (let i = 0; i < data.length; i += 1) {
27+
/* eslint-disable-next-line no-await-in-loop */
28+
await this.emit('data', messages.newMessageWithBody({ filename: data[i].Key }));
29+
}
1430
};

lib/actions/readFile.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const convert = require('xml-js');
44
const mime = require('mime-types');
55
const iconv = require('iconv-lite');
66
const { Client } = require('../client');
7+
const params = require('../parameters');
78

89
const attachmentProcessor = require('../utils/attachmentProcessor');
910

@@ -14,6 +15,13 @@ exports.process = async function (msg, cfg) {
1415

1516
const result = await client.getObject(bucketName, filename);
1617

18+
if (result.ContentLength > params.ATTACHMENT_MAX_SIZE) {
19+
this.logger.error('File %s with size %d bytes is too big for attachment usage. '
20+
+ 'Current attachment max size is %d bytes', filename, result.ContentLength, params.ATTACHMENT_MAX_SIZE);
21+
throw new Error(`File ${filename} with size ${result.ContentLength} bytes is too big for attachment usage. `
22+
+ `Current attachment max size is ${params.ATTACHMENT_MAX_SIZE} bytes`);
23+
}
24+
1725
const fileContent = iconv.decode(result.Body, 'iso-8859-15');
1826
const contentType = mime.lookup(filename);
1927

lib/actions/streamToFile.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ exports.process = async function (msg, cfg) {
1010
const client = new Client(this.logger, cfg);
1111
const bucketName = msg.body.bucketName ? msg.body.bucketName : cfg.bucketName;
1212

13+
if (!msg.attachments) {
14+
throw new Error('Message has no attachments object (no attachments)');
15+
} else if (Object.keys(msg.attachments).length > 1) {
16+
throw new Error('More than 1 attachment');
17+
} else if (Object.keys(msg.attachments).length === 0) {
18+
throw new Error('Has no attachment');
19+
}
1320
// eslint-disable-next-line arrow-body-style
1421
const attachments = Object.keys(msg.attachments).map((attachmentKey) => {
1522
return {
@@ -18,12 +25,6 @@ exports.process = async function (msg, cfg) {
1825
};
1926
});
2027

21-
if (attachments.length > 1) {
22-
throw new Error('More than 1 attachment');
23-
} else if (attachments.length === 0) {
24-
throw new Error('Has no attachment');
25-
}
26-
2728
this.logger.trace('trying to get attachment from %j', attachments[0].url.url);
2829

2930
const passthrough = new PassThrough();

0 commit comments

Comments
 (0)