Skip to content

Commit aef21ef

Browse files
authored
Add Upsert File (#49)
* Update dependencies. * 1.4.0
1 parent 11c0de4 commit aef21ef

File tree

11 files changed

+1767
-808
lines changed

11 files changed

+1767
-808
lines changed

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ version: 2
22
jobs:
33
test:
44
docker:
5-
- image: circleci/node:8-stretch
5+
- image: circleci/node:14-stretch
66
steps:
77
- checkout
88
- restore_cache:

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
# 1.4.0 (June 5, 2020)
2+
3+
## General Changes
4+
* Add Upsert File Action
5+
* Update dependencies
6+
* Verify Credentials now checks for access to buckets.
7+
* Update to Node v 14
8+
* Replace Component completeness matrix to version 2.3
9+
10+
111
# 1.3.1 (May 22, 2020)
212

313
## General Changes

README.md

Lines changed: 88 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@
1515
* [Triggers](#triggers)
1616
* [Get New and Updated S3 Objects](#get-new-and-updated-s3-objects)
1717
* [Actions](#actions)
18-
* [Write file](#write-file)
18+
* [Write File to S3 From a Provided Attachment](#write-file-to-s3-from-a-provided-attachment)
1919
* [Read file](#read-file)
2020
* [Get filenames](#get-filenames)
2121
* [Delete file](#delete-file)
2222
* [Rename file](#rename-file)
23+
* [Write file](#write-file)
24+
* [Stream to CSV](#stream-to-csv)
2325
* [Known Limitations](#known-limitations)
2426
* [License](#license)
2527

@@ -32,12 +34,12 @@ This is the component for working with AWS S3 object storage service on [elastic
3234
The component provides ability to connect to Amazon Simple Storage Service (Amazon S3) object storage service.
3335

3436
### Completeness Matrix
35-
![image](https://user-images.githubusercontent.com/22715422/73740795-93740a00-4751-11ea-869d-ff9433f39c4f.png)
37+
![Completeness Matrix](https://user-images.githubusercontent.com/5710732/82918058-312e5780-9f42-11ea-9f80-9eb6cc9aed35.png)
3638

37-
[Completeness Matrix](https://docs.google.com/spreadsheets/d/1LhKgsTvF32YAmBRh742YxnkrMEGlPEERJc9B6pj4L6E/edit#gid=0)
39+
[Completeness Matrix](https://docs.google.com/spreadsheets/d/1sptYGKkInnAbfRRbzLr5oOZUd3-COekQWGKpqQUYVfc/edit#gid=0)
3840

3941
### How works. SDK version
40-
The component is based on [AWS S3 SDK](https://aws.amazon.com/sdk-for-node-js/ 'SDK for NodeJS') version 2.314.0.
42+
The component is based on [AWS S3 SDK](https://aws.amazon.com/sdk-for-node-js/ 'SDK for NodeJS') version 2.683.0.
4143

4244
## Requirements
4345

@@ -51,7 +53,7 @@ Name|Mandatory|Description|Values|
5153
|`REGION` | false | For integration-tests is required to specify this variable | |
5254

5355
## Credentials
54-
Access keys consist of two parts: an access key ID and a secret access key.
56+
Access keys consist of three parts: an access key ID, a secret access key and a region.
5557
Like a user name and password, you must use both the access key ID and secret access key together to authenticate your requests.
5658
According to [AWS documentation](https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingBucket.html#access-bucket-intro) for buckets created in Regions launched after March 20, 2019 `Region` is required for AWS credential.
5759
### Access Key Id
@@ -73,7 +75,7 @@ Triggers to get all new and updated s3 objects since last polling.
7375
- **Emit Behaviour**: Options are: default is `Emit Individually` emits each object in separate message, `Fetch All` emits all objects in one message
7476
- **Start Time**: Start datetime of polling. Default min date:`-271821-04-20T00:00:00.000Z`
7577
- **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`
78+
- **Enable File Attachments**: If selected, the contents of the file will be exported in addition to the file metadata.
7779

7880
<details>
7981
<summary>Output metadata</summary>
@@ -117,69 +119,22 @@ Triggers to get all new and updated s3 objects since last polling.
117119
</details>
118120

119121
## Actions
120-
### Write file
121-
Put stream as file into S3 bucket.
122-
This action creates or rewrites a new file on S3 with the content that is passed as an input attachment.
123-
The name of the file would be the same to the attachment name.
124-
Be careful: this action can process only one attachment - if it would be more or no attachment at all the execution would fail with exception.
125-
#### List of Expected Config fields
126-
- **Default Bucket Name and folder** - name of S3 bucket to write file in (by default, if `bucketName` is not provided in metadata);
122+
### Write File to S3 From a Provided Attachment
123+
Given a filename and a URL to an attachment stored in the platform, transfers the contents of the attachment to AWS S3. The component returns a summary of the written file. AWS S3 always overwrites the contents of the file if it already exists.
127124

128125
#### Expected input metadata
129-
- **filename** - name of resulted file at S3 bucket (optional);
130-
- **bucketName** - name of S3 bucket to write file in (will replace `Default Bucket Name and folder` if provided, the field is optional).
131-
132-
![image](https://user-images.githubusercontent.com/40201204/59688384-448b5b80-91e6-11e9-8dd0-e007983055c8.png)
133-
134-
<details>
135-
<summary>Input metadata</summary>
136-
137-
```json
138-
{
139-
"type": "object",
140-
"properties": {
141-
"filename": {
142-
"type": "string",
143-
"required": false
144-
},
145-
"bucketName": {
146-
"type": "string",
147-
"required": false
148-
}
149-
}
150-
}
151-
```
152-
</details>
153-
154-
#### Expected output metadata
155-
156-
<details>
157-
<summary>Output metadata</summary>
158-
159-
```json
160-
{
161-
"type": "object",
162-
"properties": {
163-
"ETag": {
164-
"type": "string",
165-
"required": true
166-
},
167-
"Location": {
168-
"type": "string",
169-
"required": false
170-
},
171-
"Key": {
172-
"type": "string",
173-
"required": true
174-
},
175-
"Bucket": {
176-
"type": "string",
177-
"required": true
178-
}
179-
}
180-
}
181-
```
182-
</details>
126+
- **bucketName** - name of S3 bucket to write the file to; Sufficient write permission is required;
127+
- **fileName** - Name of file/S3 object to write. Use `/` characters in the filename to create folders;
128+
- **attachmentUrl** - Url to the attachment stored in the platform. The contents of this attachment will be written to S3 without any transformation;
129+
130+
#### Limitations
131+
* It is not possible to set the File/Object Metadata in S3
132+
* Files/Objects can not be so large that they can not fit in the memory of the component's docker container.
133+
* Files/Objects can not be more that 5 GB in size
134+
* It is not possible to set the AWS S3 Storage Class for written files/objects. They will always be written with the `standard` storage class.
135+
* It is not possible to set file/object tags
136+
* It is not possible to compression objects/files (with zip, gzip, etc)
137+
* It is not possible to encrypt object/files
183138

184139
### Read file
185140
Read file from S3 bucket.
@@ -343,7 +298,6 @@ This action removes file from S3 by provided name in selected bucket. The action
343298
```
344299
</details>
345300

346-
347301
### Rename file
348302
Rename file in S3 bucket and folder.
349303

@@ -428,8 +382,73 @@ The action will emit properties of renamed file.
428382
```
429383
</details>
430384

385+
### Write file
386+
**Deprecated in favor of [`Write File to S3 From a Provided Attachment`](#write-file-to-s3-from-a-provided-attachment)**
387+
Put stream as file into S3 bucket.
388+
This action creates or rewrites a new file on S3 with the content that is passed as an input attachment.
389+
The name of the file would be the same to the attachment name.
390+
Be careful: this action can process only one attachment - if it would be more or no attachment at all the execution would fail with exception.
391+
#### List of Expected Config fields
392+
- **Default Bucket Name and folder** - name of S3 bucket to write file in (by default, if `bucketName` is not provided in metadata);
393+
394+
#### Expected input metadata
395+
- **filename** - name of resulted file at S3 bucket (optional);
396+
- **bucketName** - name of S3 bucket to write file in (will replace `Default Bucket Name and folder` if provided, the field is optional).
397+
398+
![image](https://user-images.githubusercontent.com/40201204/59688384-448b5b80-91e6-11e9-8dd0-e007983055c8.png)
399+
400+
<details>
401+
<summary>Input metadata</summary>
402+
403+
```json
404+
{
405+
"type": "object",
406+
"properties": {
407+
"filename": {
408+
"type": "string",
409+
"required": false
410+
},
411+
"bucketName": {
412+
"type": "string",
413+
"required": false
414+
}
415+
}
416+
}
417+
```
418+
</details>
419+
420+
#### Expected output metadata
421+
422+
<details>
423+
<summary>Output metadata</summary>
424+
425+
```json
426+
{
427+
"type": "object",
428+
"properties": {
429+
"ETag": {
430+
"type": "string",
431+
"required": true
432+
},
433+
"Location": {
434+
"type": "string",
435+
"required": false
436+
},
437+
"Key": {
438+
"type": "string",
439+
"required": true
440+
},
441+
"Bucket": {
442+
"type": "string",
443+
"required": true
444+
}
445+
}
446+
}
447+
```
448+
</details>
449+
431450
### Stream to CSV
432-
Action is deprecated. Use `Write file` action instead.
451+
Action is deprecated. Use the csv & or batch component to create a csv file first, then write that file to S3.
433452

434453
## Known Limitations
435454

component.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,9 @@
108108
"actions": {
109109
"streamToFile": {
110110
"title": "Write file",
111+
"deprecated": true,
111112
"help": {
112-
"description": "Put stream as file into S3 bucket",
113+
"description": "Put stream as file into S3 bucket. Deprecated in favor of \"Write File to S3 From a Provided Attachment\"",
113114
"link": "/components/aws-s3/index.html#write-file"
114115
},
115116
"main": "./lib/actions/streamToFile.js",
@@ -380,6 +381,16 @@
380381
"metadata": {
381382
"in": {}
382383
}
384+
},
385+
"upsertFile": {
386+
"main": "./lib/actions/upsertFile.js",
387+
"title": "Write File to S3 From a Provided Attachment",
388+
"help": {
389+
"description": "Given a filename and a URL to an attachment stored in the platform, transfers the contents of the attachment to AWS S3. The component returns a summary of the written file. AWS S3 always overwrites the contents of the file if it already exists.",
390+
"link": "/components/aws-s3/index.html#write-file-to-s3-from-a-provided-attachment"
391+
},
392+
"fields": {},
393+
"dynamicMetadata": true
383394
}
384395
}
385396
}

lib/actions/upsertFile.js

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/* eslint-disable func-names */
2+
const { UpsertObjectById } = require('@elastic.io/oih-standard-library/lib/actions/upsert');
3+
const { AttachmentProcessor } = require('@elastic.io/component-commons-library');
4+
5+
const { Client } = require('../client');
6+
7+
class S3UpsertObject extends UpsertObjectById {
8+
constructor(logger, client) {
9+
super(logger);
10+
this.client = client;
11+
}
12+
13+
// eslint-disable-next-line no-unused-vars,class-methods-use-this
14+
getCriteria(msg, cfg) {
15+
return {
16+
bucketName: msg.body.bucketName,
17+
fileName: msg.body.fileName,
18+
};
19+
}
20+
21+
// eslint-disable-next-line no-unused-vars,class-methods-use-this
22+
async getType(cfg, msg) {
23+
return 'file/S3 Object';
24+
}
25+
26+
// eslint-disable-next-line no-unused-vars,class-methods-use-this
27+
async getObjectFromMessage(msg, cfg) {
28+
const fileStream = await new AttachmentProcessor().getAttachment(msg.body.attachmentUrl, 'stream');
29+
return fileStream.data;
30+
}
31+
32+
// eslint-disable-next-line no-unused-vars,class-methods-use-this
33+
async lookupObject(criteria, cfg) {
34+
return false;
35+
}
36+
37+
async createObject(object, cfg, msg) {
38+
const { bucketName, fileName } = this.getCriteria(msg, cfg);
39+
return this.client.upload(bucketName, fileName, object);
40+
}
41+
}
42+
43+
44+
exports.process = async function (msg, cfg) {
45+
const s3Client = new Client(this.logger, cfg);
46+
const upsertObjectAction = new S3UpsertObject(this.logger, s3Client);
47+
return upsertObjectAction.process(msg, cfg);
48+
};
49+
50+
exports.getMetaModel = async function (cfg) {
51+
const s3Client = new Client(this.logger, cfg);
52+
const bucketNames = await s3Client.listBucketNames();
53+
return {
54+
in: {
55+
type: 'object',
56+
required: true,
57+
properties: {
58+
bucketName: {
59+
title: 'Bucket Name',
60+
type: 'string',
61+
enum: bucketNames,
62+
required: true,
63+
order: 3,
64+
},
65+
fileName: {
66+
title: 'File Name (& any folders) (Key)',
67+
type: 'string',
68+
required: true,
69+
order: 2,
70+
},
71+
attachmentUrl: {
72+
title: 'Platform Attachment URL',
73+
type: 'string',
74+
required: true,
75+
order: 1,
76+
},
77+
},
78+
},
79+
out: {
80+
type: 'object',
81+
required: true,
82+
properties: {
83+
ETag: {
84+
title: 'ETag',
85+
type: 'string',
86+
required: true,
87+
},
88+
Key: {
89+
title: 'Key',
90+
type: 'string',
91+
required: true,
92+
},
93+
Location: {
94+
title: 'Location',
95+
type: 'string',
96+
required: true,
97+
},
98+
Bucket: {
99+
title: 'Bucket',
100+
type: 'string',
101+
required: true,
102+
},
103+
},
104+
},
105+
};
106+
};

lib/client.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,5 +110,10 @@ class Client {
110110
};
111111
return this.s3.upload(params).promise();
112112
}
113+
114+
async listBucketNames() {
115+
const bucketData = await this.s3.listBuckets({}).promise();
116+
return bucketData.Buckets.map((bucket) => bucket.Name);
117+
}
113118
}
114119
module.exports.Client = Client;

lib/utils/pollingUtil.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const { AttachmentProcessor } = require('@elastic.io/component-commons-library')
44
const { messages } = require('elasticio-node');
55
const path = require('path');
66

7-
const { createAWSInputs } = require('../utils/utils');
7+
const { createAWSInputs } = require('./utils');
88

99
class AwsS3Polling extends PollingTrigger {
1010
constructor(logger, context, client, cfg) {

0 commit comments

Comments
 (0)