279 lines
6.2 KiB
TypeScript
279 lines
6.2 KiB
TypeScript
import {
|
|
Integration,
|
|
QueryType,
|
|
IntegrationBase,
|
|
DatasourceFieldType,
|
|
DatasourceFeature,
|
|
ConnectionInfo,
|
|
} from "@budibase/types"
|
|
|
|
import AWS from "aws-sdk"
|
|
import csv from "csvtojson"
|
|
|
|
interface S3Config {
|
|
region: string
|
|
accessKeyId: string
|
|
secretAccessKey: string
|
|
s3ForcePathStyle: boolean
|
|
endpoint?: string
|
|
}
|
|
|
|
const SCHEMA: Integration = {
|
|
docs: "https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html",
|
|
description:
|
|
"Amazon Simple Storage Service (Amazon S3) is an object storage service that offers industry-leading scalability, data availability, security, and performance.",
|
|
friendlyName: "Amazon S3",
|
|
type: "Object store",
|
|
features: {
|
|
[DatasourceFeature.CONNECTION_CHECKING]: true,
|
|
},
|
|
datasource: {
|
|
region: {
|
|
type: DatasourceFieldType.STRING,
|
|
required: false,
|
|
default: "us-east-1",
|
|
},
|
|
accessKeyId: {
|
|
type: DatasourceFieldType.PASSWORD,
|
|
required: true,
|
|
},
|
|
secretAccessKey: {
|
|
type: DatasourceFieldType.PASSWORD,
|
|
required: true,
|
|
},
|
|
endpoint: {
|
|
type: DatasourceFieldType.STRING,
|
|
required: false,
|
|
},
|
|
signatureVersion: {
|
|
type: DatasourceFieldType.STRING,
|
|
required: false,
|
|
default: "v4",
|
|
},
|
|
},
|
|
query: {
|
|
create: {
|
|
type: QueryType.FIELDS,
|
|
fields: {
|
|
bucket: {
|
|
display: "New Bucket",
|
|
type: DatasourceFieldType.STRING,
|
|
required: true,
|
|
},
|
|
location: {
|
|
required: true,
|
|
default: "us-east-1",
|
|
type: DatasourceFieldType.STRING,
|
|
},
|
|
grantFullControl: {
|
|
display: "Grant full control",
|
|
type: DatasourceFieldType.STRING,
|
|
},
|
|
grantRead: {
|
|
display: "Grant read",
|
|
type: DatasourceFieldType.STRING,
|
|
},
|
|
grantReadAcp: {
|
|
display: "Grant read ACP",
|
|
type: DatasourceFieldType.STRING,
|
|
},
|
|
grantWrite: {
|
|
display: "Grant write",
|
|
type: DatasourceFieldType.STRING,
|
|
},
|
|
grantWriteAcp: {
|
|
display: "Grant write ACP",
|
|
type: DatasourceFieldType.STRING,
|
|
},
|
|
},
|
|
},
|
|
read: {
|
|
type: QueryType.FIELDS,
|
|
fields: {
|
|
bucket: {
|
|
type: DatasourceFieldType.STRING,
|
|
required: true,
|
|
},
|
|
delimiter: {
|
|
type: DatasourceFieldType.STRING,
|
|
},
|
|
marker: {
|
|
type: DatasourceFieldType.STRING,
|
|
},
|
|
maxKeys: {
|
|
type: DatasourceFieldType.NUMBER,
|
|
display: "Max Keys",
|
|
},
|
|
prefix: {
|
|
type: DatasourceFieldType.STRING,
|
|
},
|
|
},
|
|
},
|
|
readCsv: {
|
|
displayName: "Read CSV",
|
|
type: QueryType.FIELDS,
|
|
readable: true,
|
|
fields: {
|
|
bucket: {
|
|
type: DatasourceFieldType.STRING,
|
|
required: true,
|
|
},
|
|
key: {
|
|
type: DatasourceFieldType.STRING,
|
|
required: true,
|
|
},
|
|
},
|
|
},
|
|
delete: {
|
|
type: QueryType.FIELDS,
|
|
fields: {
|
|
bucket: {
|
|
type: DatasourceFieldType.STRING,
|
|
required: true,
|
|
},
|
|
delete: {
|
|
type: DatasourceFieldType.JSON,
|
|
required: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
extra: {
|
|
acl: {
|
|
required: false,
|
|
displayName: "ACL",
|
|
type: DatasourceFieldType.LIST,
|
|
data: {
|
|
create: [
|
|
"private",
|
|
"public-read",
|
|
"public-read-write",
|
|
"authenticated-read",
|
|
],
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
class S3Integration implements IntegrationBase {
|
|
private readonly config: S3Config
|
|
private client
|
|
|
|
constructor(config: S3Config) {
|
|
this.config = config
|
|
if (this.config.endpoint) {
|
|
this.config.s3ForcePathStyle = true
|
|
} else {
|
|
delete this.config.endpoint
|
|
}
|
|
|
|
this.client = new AWS.S3(this.config)
|
|
}
|
|
|
|
async testConnection() {
|
|
const response: ConnectionInfo = {
|
|
connected: false,
|
|
}
|
|
try {
|
|
await this.client.listBuckets().promise()
|
|
response.connected = true
|
|
} catch (e: any) {
|
|
response.error = e.message as string
|
|
}
|
|
return response
|
|
}
|
|
|
|
async create(query: {
|
|
bucket: string
|
|
location: string
|
|
grantFullControl: string
|
|
grantRead: string
|
|
grantReadAcp: string
|
|
grantWrite: string
|
|
grantWriteAcp: string
|
|
extra: {
|
|
acl: string
|
|
}
|
|
}) {
|
|
let params: any = {
|
|
Bucket: query.bucket,
|
|
ACL: query.extra?.acl,
|
|
GrantFullControl: query.grantFullControl,
|
|
GrantRead: query.grantRead,
|
|
GrantReadACP: query.grantReadAcp,
|
|
GrantWrite: query.grantWrite,
|
|
GrantWriteACP: query.grantWriteAcp,
|
|
}
|
|
if (query.location) {
|
|
params["CreateBucketConfiguration"] = {
|
|
LocationConstraint: query.location,
|
|
}
|
|
}
|
|
return await this.client.createBucket(params).promise()
|
|
}
|
|
|
|
async read(query: {
|
|
bucket: string
|
|
delimiter: string
|
|
expectedBucketOwner: string
|
|
marker: string
|
|
maxKeys: number
|
|
prefix: string
|
|
}) {
|
|
const response = await this.client
|
|
.listObjects({
|
|
Bucket: query.bucket,
|
|
Delimiter: query.delimiter,
|
|
Marker: query.marker,
|
|
MaxKeys: query.maxKeys,
|
|
Prefix: query.prefix,
|
|
})
|
|
.promise()
|
|
return response.Contents
|
|
}
|
|
|
|
async readCsv(query: { bucket: string; key: string }) {
|
|
const stream = this.client
|
|
.getObject({
|
|
Bucket: query.bucket,
|
|
Key: query.key,
|
|
})
|
|
.createReadStream()
|
|
|
|
let csvError = false
|
|
return new Promise((resolve, reject) => {
|
|
stream.on("error", (err: Error) => {
|
|
reject(err)
|
|
})
|
|
const response = csv()
|
|
.fromStream(stream)
|
|
.on("error", () => {
|
|
csvError = true
|
|
})
|
|
stream.on("finish", () => {
|
|
resolve(response)
|
|
})
|
|
}).catch(err => {
|
|
if (csvError) {
|
|
throw new Error("Could not read CSV")
|
|
} else {
|
|
throw err
|
|
}
|
|
})
|
|
}
|
|
|
|
async delete(query: { bucket: string; delete: string }) {
|
|
return await this.client
|
|
.deleteObjects({
|
|
Bucket: query.bucket,
|
|
Delete: JSON.parse(query.delete),
|
|
})
|
|
.promise()
|
|
}
|
|
}
|
|
|
|
export default {
|
|
schema: SCHEMA,
|
|
integration: S3Integration,
|
|
}
|