mirror of https://github.com/Budibase/budibase.git
37 changed files with 3480 additions and 1587 deletions
@ -0,0 +1,29 @@ |
|||
|
|||
export interface Table { |
|||
_id: string, |
|||
_rev?: string, |
|||
type?: string, |
|||
views?: {}, |
|||
name?: string, |
|||
primary?: string[], |
|||
schema: { |
|||
[key: string]: { |
|||
// TODO: replace with field types enum when done
|
|||
type: string, |
|||
fieldName?: string, |
|||
name: string, |
|||
constraints?: { |
|||
type?: string, |
|||
email?: boolean, |
|||
inclusion?: string[], |
|||
length?: { |
|||
minimum?: string | number, |
|||
maximum?: string | number, |
|||
}, |
|||
presence?: boolean, |
|||
}, |
|||
} |
|||
}, |
|||
primaryDisplay?: string, |
|||
sourceId?: string, |
|||
} |
|||
@ -1,15 +0,0 @@ |
|||
exports.QUERY_TYPES = { |
|||
SQL: "sql", |
|||
JSON: "json", |
|||
FIELDS: "fields", |
|||
} |
|||
|
|||
exports.FIELD_TYPES = { |
|||
STRING: "string", |
|||
BOOLEAN: "boolean", |
|||
NUMBER: "number", |
|||
PASSWORD: "password", |
|||
LIST: "list", |
|||
OBJECT: "object", |
|||
JSON: "json", |
|||
} |
|||
@ -1,130 +0,0 @@ |
|||
const Airtable = require("airtable") |
|||
const { FIELD_TYPES, QUERY_TYPES } = require("./Integration") |
|||
|
|||
const SCHEMA = { |
|||
docs: "https://airtable.com/api", |
|||
description: |
|||
"Airtable is a spreadsheet-database hybrid, with the features of a database but applied to a spreadsheet.", |
|||
friendlyName: "Airtable", |
|||
datasource: { |
|||
apiKey: { |
|||
type: FIELD_TYPES.PASSWORD, |
|||
default: "enter api key", |
|||
required: true, |
|||
}, |
|||
base: { |
|||
type: FIELD_TYPES.STRING, |
|||
default: "mybase", |
|||
required: true, |
|||
}, |
|||
}, |
|||
query: { |
|||
create: { |
|||
type: QUERY_TYPES.FIELDS, |
|||
customisable: true, |
|||
fields: { |
|||
table: { |
|||
type: FIELD_TYPES.STRING, |
|||
required: true, |
|||
}, |
|||
}, |
|||
}, |
|||
read: { |
|||
type: QUERY_TYPES.FIELDS, |
|||
fields: { |
|||
table: { |
|||
type: FIELD_TYPES.STRING, |
|||
required: true, |
|||
}, |
|||
view: { |
|||
type: FIELD_TYPES.STRING, |
|||
required: true, |
|||
}, |
|||
numRecords: { |
|||
type: FIELD_TYPES.NUMBER, |
|||
default: 10, |
|||
}, |
|||
}, |
|||
}, |
|||
update: { |
|||
type: QUERY_TYPES.FIELDS, |
|||
customisable: true, |
|||
fields: { |
|||
id: { |
|||
type: FIELD_TYPES.STRING, |
|||
required: true, |
|||
}, |
|||
}, |
|||
}, |
|||
delete: { |
|||
type: QUERY_TYPES.JSON, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
class AirtableIntegration { |
|||
constructor(config) { |
|||
this.config = config |
|||
this.client = new Airtable(config).base(config.base) |
|||
} |
|||
|
|||
async create(query) { |
|||
const { table, json } = query |
|||
|
|||
try { |
|||
const records = await this.client(table).create([ |
|||
{ |
|||
fields: json, |
|||
}, |
|||
]) |
|||
return records |
|||
} catch (err) { |
|||
console.error("Error writing to airtable", err) |
|||
throw err |
|||
} |
|||
} |
|||
|
|||
async read(query) { |
|||
try { |
|||
const records = await this.client(query.table) |
|||
.select({ maxRecords: query.numRecords || 10, view: query.view }) |
|||
.firstPage() |
|||
return records.map(({ fields }) => fields) |
|||
} catch (err) { |
|||
console.error("Error writing to airtable", err) |
|||
return [] |
|||
} |
|||
} |
|||
|
|||
async update(query) { |
|||
const { table, id, json } = query |
|||
|
|||
try { |
|||
const records = await this.client(table).update([ |
|||
{ |
|||
id, |
|||
fields: json, |
|||
}, |
|||
]) |
|||
return records |
|||
} catch (err) { |
|||
console.error("Error writing to airtable", err) |
|||
throw err |
|||
} |
|||
} |
|||
|
|||
async delete(query) { |
|||
try { |
|||
const records = await this.client(query.table).destroy(query.ids) |
|||
return records |
|||
} catch (err) { |
|||
console.error("Error writing to airtable", err) |
|||
throw err |
|||
} |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
schema: SCHEMA, |
|||
integration: AirtableIntegration, |
|||
} |
|||
@ -0,0 +1,139 @@ |
|||
import { Integration, DatasourceFieldTypes, QueryTypes } from "./base/definitions" |
|||
|
|||
module AirtableModule { |
|||
const Airtable = require("airtable") |
|||
|
|||
interface AirtableConfig { |
|||
apiKey: string |
|||
base: string |
|||
} |
|||
|
|||
const SCHEMA: Integration = { |
|||
docs: "https://airtable.com/api", |
|||
description: |
|||
"Airtable is a spreadsheet-database hybrid, with the features of a database but applied to a spreadsheet.", |
|||
friendlyName: "Airtable", |
|||
datasource: { |
|||
apiKey: { |
|||
type: DatasourceFieldTypes.PASSWORD, |
|||
default: "enter api key", |
|||
required: true, |
|||
}, |
|||
base: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
default: "mybase", |
|||
required: true, |
|||
}, |
|||
}, |
|||
query: { |
|||
create: { |
|||
type: QueryTypes.FIELDS, |
|||
customisable: true, |
|||
fields: { |
|||
table: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
required: true, |
|||
}, |
|||
}, |
|||
}, |
|||
read: { |
|||
type: QueryTypes.FIELDS, |
|||
fields: { |
|||
table: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
required: true, |
|||
}, |
|||
view: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
required: true, |
|||
}, |
|||
numRecords: { |
|||
type: DatasourceFieldTypes.NUMBER, |
|||
default: 10, |
|||
}, |
|||
}, |
|||
}, |
|||
update: { |
|||
type: QueryTypes.FIELDS, |
|||
customisable: true, |
|||
fields: { |
|||
id: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
required: true, |
|||
}, |
|||
}, |
|||
}, |
|||
delete: { |
|||
type: QueryTypes.JSON, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
class AirtableIntegration { |
|||
private config: AirtableConfig |
|||
private client: any |
|||
|
|||
constructor(config: AirtableConfig) { |
|||
this.config = config |
|||
this.client = new Airtable(config).base(config.base) |
|||
} |
|||
|
|||
async create(query: { table: any; json: any }) { |
|||
const { table, json } = query |
|||
|
|||
try { |
|||
return await this.client(table).create([ |
|||
{ |
|||
fields: json, |
|||
}, |
|||
]) |
|||
} catch (err) { |
|||
console.error("Error writing to airtable", err) |
|||
throw err |
|||
} |
|||
} |
|||
|
|||
async read(query: { table: any; numRecords: any; view: any }) { |
|||
try { |
|||
const records = await this.client(query.table) |
|||
.select({ maxRecords: query.numRecords || 10, view: query.view }) |
|||
.firstPage() |
|||
// @ts-ignore
|
|||
return records.map(({ fields }) => fields) |
|||
} catch (err) { |
|||
console.error("Error writing to airtable", err) |
|||
return [] |
|||
} |
|||
} |
|||
|
|||
async update(query: { table: any; id: any; json: any }) { |
|||
const { table, id, json } = query |
|||
|
|||
try { |
|||
return await this.client(table).update([ |
|||
{ |
|||
id, |
|||
fields: json, |
|||
}, |
|||
]) |
|||
} catch (err) { |
|||
console.error("Error writing to airtable", err) |
|||
throw err |
|||
} |
|||
} |
|||
|
|||
async delete(query: { table: any; ids: any }) { |
|||
try { |
|||
return await this.client(query.table).destroy(query.ids) |
|||
} catch (err) { |
|||
console.error("Error writing to airtable", err) |
|||
throw err |
|||
} |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
schema: SCHEMA, |
|||
integration: AirtableIntegration, |
|||
} |
|||
} |
|||
@ -1,86 +0,0 @@ |
|||
const { Database, aql } = require("arangojs") |
|||
const { FIELD_TYPES, QUERY_TYPES } = require("./Integration") |
|||
|
|||
const SCHEMA = { |
|||
docs: "https://github.com/arangodb/arangojs", |
|||
friendlyName: "ArangoDB", |
|||
description: |
|||
"ArangoDB is a scalable open-source multi-model database natively supporting graph, document and search. All supported data models & access patterns can be combined in queries allowing for maximal flexibility. ", |
|||
datasource: { |
|||
url: { |
|||
type: FIELD_TYPES.STRING, |
|||
default: "http://localhost:8529", |
|||
required: true, |
|||
}, |
|||
username: { |
|||
type: FIELD_TYPES.STRING, |
|||
default: "root", |
|||
required: true, |
|||
}, |
|||
password: { |
|||
type: FIELD_TYPES.PASSWORD, |
|||
required: true, |
|||
}, |
|||
databaseName: { |
|||
type: FIELD_TYPES.STRING, |
|||
default: "_system", |
|||
required: true, |
|||
}, |
|||
collection: { |
|||
type: FIELD_TYPES.STRING, |
|||
required: true, |
|||
}, |
|||
}, |
|||
query: { |
|||
read: { |
|||
type: QUERY_TYPES.SQL, |
|||
}, |
|||
create: { |
|||
type: QUERY_TYPES.JSON, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
class ArangoDBIntegration { |
|||
constructor(config) { |
|||
config.auth = { |
|||
username: config.username, |
|||
password: config.password, |
|||
} |
|||
|
|||
this.config = config |
|||
this.client = new Database(config) |
|||
} |
|||
|
|||
async read(query) { |
|||
try { |
|||
const result = await this.client.query(query.sql) |
|||
return result.all() |
|||
} catch (err) { |
|||
console.error("Error querying arangodb", err.message) |
|||
throw err |
|||
} finally { |
|||
this.client.close() |
|||
} |
|||
} |
|||
|
|||
async create(query) { |
|||
const clc = this.client.collection(this.config.collection) |
|||
try { |
|||
const result = await this.client.query( |
|||
aql`INSERT ${query.json} INTO ${clc} RETURN NEW` |
|||
) |
|||
return result.all() |
|||
} catch (err) { |
|||
console.error("Error querying arangodb", err.message) |
|||
throw err |
|||
} finally { |
|||
this.client.close() |
|||
} |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
schema: SCHEMA, |
|||
integration: ArangoDBIntegration, |
|||
} |
|||
@ -0,0 +1,102 @@ |
|||
import { Integration, DatasourceFieldTypes, QueryTypes } from "./base/definitions" |
|||
|
|||
module ArangoModule { |
|||
const { Database, aql } = require("arangojs") |
|||
|
|||
interface ArangodbConfig { |
|||
url: string |
|||
username: string |
|||
password: string |
|||
databaseName: string |
|||
collection: string |
|||
} |
|||
|
|||
const SCHEMA: Integration = { |
|||
docs: "https://github.com/arangodb/arangojs", |
|||
friendlyName: "ArangoDB", |
|||
description: |
|||
"ArangoDB is a scalable open-source multi-model database natively supporting graph, document and search. All supported data models & access patterns can be combined in queries allowing for maximal flexibility. ", |
|||
datasource: { |
|||
url: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
default: "http://localhost:8529", |
|||
required: true, |
|||
}, |
|||
username: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
default: "root", |
|||
required: true, |
|||
}, |
|||
password: { |
|||
type: DatasourceFieldTypes.PASSWORD, |
|||
required: true, |
|||
}, |
|||
databaseName: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
default: "_system", |
|||
required: true, |
|||
}, |
|||
collection: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
required: true, |
|||
}, |
|||
}, |
|||
query: { |
|||
read: { |
|||
type: QueryTypes.SQL, |
|||
}, |
|||
create: { |
|||
type: QueryTypes.JSON, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
class ArangoDBIntegration { |
|||
private config: ArangodbConfig |
|||
private client: any |
|||
|
|||
constructor(config: ArangodbConfig) { |
|||
const newConfig = { |
|||
auth: { |
|||
username: config.username, |
|||
password: config.password, |
|||
}, |
|||
} |
|||
|
|||
this.config = config |
|||
this.client = new Database(newConfig) |
|||
} |
|||
|
|||
async read(query: { sql: any }) { |
|||
try { |
|||
const result = await this.client.query(query.sql) |
|||
return result.all() |
|||
} catch (err) { |
|||
console.error("Error querying arangodb", err.message) |
|||
throw err |
|||
} finally { |
|||
this.client.close() |
|||
} |
|||
} |
|||
|
|||
async create(query: { json: any }) { |
|||
const clc = this.client.collection(this.config.collection) |
|||
try { |
|||
const result = await this.client.query( |
|||
aql`INSERT ${query.json} INTO ${clc} RETURN NEW` |
|||
) |
|||
return result.all() |
|||
} catch (err) { |
|||
console.error("Error querying arangodb", err.message) |
|||
throw err |
|||
} finally { |
|||
this.client.close() |
|||
} |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
schema: SCHEMA, |
|||
integration: ArangoDBIntegration, |
|||
} |
|||
} |
|||
@ -1,11 +0,0 @@ |
|||
exports.Operation = { |
|||
CREATE: "CREATE", |
|||
READ: "READ", |
|||
UPDATE: "UPDATE", |
|||
DELETE: "DELETE", |
|||
} |
|||
|
|||
exports.SortDirection = { |
|||
ASCENDING: "ASCENDING", |
|||
DESCENDING: "DESCENDING", |
|||
} |
|||
@ -0,0 +1,102 @@ |
|||
export enum Operation { |
|||
CREATE = "CREATE", |
|||
READ = "READ", |
|||
UPDATE = "UPDATE", |
|||
DELETE = "DELETE", |
|||
} |
|||
|
|||
export enum SortDirection { |
|||
ASCENDING = "ASCENDING", |
|||
DESCENDING = "DESCENDING", |
|||
} |
|||
|
|||
export enum QueryTypes { |
|||
SQL = "sql", |
|||
JSON = "json", |
|||
FIELDS = "fields", |
|||
} |
|||
|
|||
export enum DatasourceFieldTypes { |
|||
STRING = "string", |
|||
BOOLEAN = "boolean", |
|||
NUMBER = "number", |
|||
PASSWORD = "password", |
|||
LIST = "list", |
|||
OBJECT = "object", |
|||
JSON = "json", |
|||
} |
|||
|
|||
export interface QueryDefinition { |
|||
type: QueryTypes, |
|||
displayName?: string, |
|||
readable?: boolean, |
|||
customisable?: boolean, |
|||
fields?: object, |
|||
urlDisplay?: boolean, |
|||
} |
|||
|
|||
export interface Integration { |
|||
docs: string, |
|||
plus?: boolean, |
|||
description: string, |
|||
friendlyName: string, |
|||
datasource: {}, |
|||
query: { |
|||
[key: string]: QueryDefinition, |
|||
} |
|||
} |
|||
|
|||
export interface SearchFilters { |
|||
allOr: boolean, |
|||
string?: { |
|||
[key: string]: string, |
|||
}, |
|||
fuzzy?: { |
|||
[key: string]: string, |
|||
}, |
|||
range?: { |
|||
[key: string]: { |
|||
high: number | string, |
|||
low: number | string, |
|||
}, |
|||
}, |
|||
equal?: { |
|||
[key: string]: any, |
|||
}, |
|||
notEqual?: { |
|||
[key: string]: any, |
|||
}, |
|||
empty?: { |
|||
[key: string]: any, |
|||
}, |
|||
notEmpty?: { |
|||
[key: string]: any, |
|||
}, |
|||
} |
|||
|
|||
export interface QueryJson { |
|||
endpoint: { |
|||
datasourceId: string, |
|||
entityId: string, |
|||
operation: Operation, |
|||
}, |
|||
resource: { |
|||
fields: string[], |
|||
}, |
|||
filters?: SearchFilters, |
|||
sort?: { |
|||
[key: string]: SortDirection, |
|||
}, |
|||
paginate?: { |
|||
limit: number, |
|||
page: string | number, |
|||
}, |
|||
body?: object, |
|||
extra: { |
|||
idFilter?: SearchFilters, |
|||
}, |
|||
} |
|||
|
|||
export interface QueryOptions { |
|||
disableReturning?: boolean, |
|||
} |
|||
@ -1,95 +0,0 @@ |
|||
const PouchDB = require("pouchdb") |
|||
const { FIELD_TYPES, QUERY_TYPES } = require("./Integration") |
|||
|
|||
const SCHEMA = { |
|||
docs: "https://docs.couchdb.org/en/stable/", |
|||
friendlyName: "CouchDB", |
|||
description: |
|||
"Apache CouchDB is an open-source document-oriented NoSQL database, implemented in Erlang.", |
|||
datasource: { |
|||
url: { |
|||
type: FIELD_TYPES.STRING, |
|||
required: true, |
|||
default: "http://localhost:5984", |
|||
}, |
|||
database: { |
|||
type: FIELD_TYPES.STRING, |
|||
required: true, |
|||
}, |
|||
}, |
|||
query: { |
|||
create: { |
|||
type: QUERY_TYPES.JSON, |
|||
}, |
|||
read: { |
|||
type: QUERY_TYPES.JSON, |
|||
}, |
|||
update: { |
|||
type: QUERY_TYPES.JSON, |
|||
}, |
|||
delete: { |
|||
type: QUERY_TYPES.FIELDS, |
|||
fields: { |
|||
id: { |
|||
type: FIELD_TYPES.STRING, |
|||
required: true, |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
class CouchDBIntegration { |
|||
constructor(config) { |
|||
this.config = config |
|||
this.client = new PouchDB(`${config.url}/${config.database}`) |
|||
} |
|||
|
|||
async create(query) { |
|||
try { |
|||
const result = await this.client.post(query.json) |
|||
return result |
|||
} catch (err) { |
|||
console.error("Error writing to couchDB", err) |
|||
throw err |
|||
} |
|||
} |
|||
|
|||
async read(query) { |
|||
try { |
|||
const result = await this.client.allDocs({ |
|||
include_docs: true, |
|||
...query.json, |
|||
}) |
|||
return result.rows.map(row => row.doc) |
|||
} catch (err) { |
|||
console.error("Error querying couchDB", err) |
|||
throw err |
|||
} |
|||
} |
|||
|
|||
async update(query) { |
|||
try { |
|||
const result = await this.client.put(query.json) |
|||
return result |
|||
} catch (err) { |
|||
console.error("Error updating couchDB document", err) |
|||
throw err |
|||
} |
|||
} |
|||
|
|||
async delete(query) { |
|||
try { |
|||
const result = await this.client.remove(query.id) |
|||
return result |
|||
} catch (err) { |
|||
console.error("Error deleting couchDB document", err) |
|||
throw err |
|||
} |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
schema: SCHEMA, |
|||
integration: CouchDBIntegration, |
|||
} |
|||
@ -0,0 +1,103 @@ |
|||
import { Integration, DatasourceFieldTypes, QueryTypes } from "./base/definitions" |
|||
|
|||
module CouchDBModule { |
|||
const PouchDB = require("pouchdb") |
|||
|
|||
interface CouchDBConfig { |
|||
url: string |
|||
database: string |
|||
} |
|||
|
|||
const SCHEMA: Integration = { |
|||
docs: "https://docs.couchdb.org/en/stable/", |
|||
friendlyName: "CouchDB", |
|||
description: |
|||
"Apache CouchDB is an open-source document-oriented NoSQL database, implemented in Erlang.", |
|||
datasource: { |
|||
url: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
required: true, |
|||
default: "http://localhost:5984", |
|||
}, |
|||
database: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
required: true, |
|||
}, |
|||
}, |
|||
query: { |
|||
create: { |
|||
type: QueryTypes.JSON, |
|||
}, |
|||
read: { |
|||
type: QueryTypes.JSON, |
|||
}, |
|||
update: { |
|||
type: QueryTypes.JSON, |
|||
}, |
|||
delete: { |
|||
type: QueryTypes.FIELDS, |
|||
fields: { |
|||
id: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
required: true, |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
class CouchDBIntegration { |
|||
private config: CouchDBConfig |
|||
private client: any |
|||
|
|||
constructor(config: CouchDBConfig) { |
|||
this.config = config |
|||
this.client = new PouchDB(`${config.url}/${config.database}`) |
|||
} |
|||
|
|||
async create(query: { json: object }) { |
|||
try { |
|||
return this.client.post(query.json) |
|||
} catch (err) { |
|||
console.error("Error writing to couchDB", err) |
|||
throw err |
|||
} |
|||
} |
|||
|
|||
async read(query: { json: object }) { |
|||
try { |
|||
const result = await this.client.allDocs({ |
|||
include_docs: true, |
|||
...query.json, |
|||
}) |
|||
return result.rows.map((row: { doc: object }) => row.doc) |
|||
} catch (err) { |
|||
console.error("Error querying couchDB", err) |
|||
throw err |
|||
} |
|||
} |
|||
|
|||
async update(query: { json: object }) { |
|||
try { |
|||
return this.client.put(query.json) |
|||
} catch (err) { |
|||
console.error("Error updating couchDB document", err) |
|||
throw err |
|||
} |
|||
} |
|||
|
|||
async delete(query: { id: string }) { |
|||
try { |
|||
return await this.client.remove(query.id) |
|||
} catch (err) { |
|||
console.error("Error deleting couchDB document", err) |
|||
throw err |
|||
} |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
schema: SCHEMA, |
|||
integration: CouchDBIntegration, |
|||
} |
|||
} |
|||
@ -1,200 +0,0 @@ |
|||
const AWS = require("aws-sdk") |
|||
const { FIELD_TYPES, QUERY_TYPES } = require("./Integration") |
|||
const { AWS_REGION } = require("../db/dynamoClient") |
|||
|
|||
const SCHEMA = { |
|||
docs: "https://github.com/dabit3/dynamodb-documentclient-cheat-sheet", |
|||
description: |
|||
"Amazon DynamoDB is a key-value and document database that delivers single-digit millisecond performance at any scale.", |
|||
friendlyName: "DynamoDB", |
|||
datasource: { |
|||
region: { |
|||
type: FIELD_TYPES.STRING, |
|||
required: true, |
|||
default: "us-east-1", |
|||
}, |
|||
accessKeyId: { |
|||
type: FIELD_TYPES.PASSWORD, |
|||
required: true, |
|||
}, |
|||
secretAccessKey: { |
|||
type: FIELD_TYPES.PASSWORD, |
|||
required: true, |
|||
}, |
|||
endpoint: { |
|||
type: FIELD_TYPES.STRING, |
|||
required: false, |
|||
default: "https://dynamodb.us-east-1.amazonaws.com", |
|||
}, |
|||
}, |
|||
query: { |
|||
create: { |
|||
type: QUERY_TYPES.FIELDS, |
|||
customisable: true, |
|||
fields: { |
|||
table: { |
|||
type: FIELD_TYPES.STRING, |
|||
required: true, |
|||
}, |
|||
}, |
|||
}, |
|||
read: { |
|||
type: QUERY_TYPES.FIELDS, |
|||
customisable: true, |
|||
readable: true, |
|||
fields: { |
|||
table: { |
|||
type: FIELD_TYPES.STRING, |
|||
required: true, |
|||
}, |
|||
index: { |
|||
type: FIELD_TYPES.STRING, |
|||
}, |
|||
}, |
|||
}, |
|||
scan: { |
|||
type: QUERY_TYPES.FIELDS, |
|||
customisable: true, |
|||
readable: true, |
|||
fields: { |
|||
table: { |
|||
type: FIELD_TYPES.STRING, |
|||
required: true, |
|||
}, |
|||
index: { |
|||
type: FIELD_TYPES.STRING, |
|||
}, |
|||
}, |
|||
}, |
|||
get: { |
|||
type: QUERY_TYPES.FIELDS, |
|||
customisable: true, |
|||
readable: true, |
|||
fields: { |
|||
table: { |
|||
type: FIELD_TYPES.STRING, |
|||
required: true, |
|||
}, |
|||
}, |
|||
}, |
|||
update: { |
|||
type: QUERY_TYPES.FIELDS, |
|||
customisable: true, |
|||
fields: { |
|||
table: { |
|||
type: FIELD_TYPES.STRING, |
|||
required: true, |
|||
}, |
|||
}, |
|||
}, |
|||
delete: { |
|||
type: QUERY_TYPES.FIELDS, |
|||
customisable: true, |
|||
fields: { |
|||
table: { |
|||
type: FIELD_TYPES.STRING, |
|||
required: true, |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
class DynamoDBIntegration { |
|||
constructor(config) { |
|||
this.config = config |
|||
this.connect() |
|||
let options = { |
|||
correctClockSkew: true, |
|||
} |
|||
if (config.endpoint) { |
|||
options.endpoint = config.endpoint |
|||
} |
|||
this.client = new AWS.DynamoDB.DocumentClient({ |
|||
correctClockSkew: true, |
|||
}) |
|||
} |
|||
|
|||
end() { |
|||
this.disconnect() |
|||
} |
|||
|
|||
connect() { |
|||
AWS.config.update(this.config) |
|||
} |
|||
|
|||
disconnect() { |
|||
AWS.config.update({ |
|||
secretAccessKey: undefined, |
|||
accessKeyId: undefined, |
|||
region: AWS_REGION, |
|||
}) |
|||
} |
|||
|
|||
async create(query) { |
|||
const params = { |
|||
TableName: query.table, |
|||
...query.json, |
|||
} |
|||
return this.client.put(params).promise() |
|||
} |
|||
|
|||
async read(query) { |
|||
const params = { |
|||
TableName: query.table, |
|||
...query.json, |
|||
} |
|||
if (query.index) { |
|||
params.IndexName = query.index |
|||
} |
|||
const response = await this.client.query(params).promise() |
|||
if (response.Items) { |
|||
return response.Items |
|||
} |
|||
return response |
|||
} |
|||
|
|||
async scan(query) { |
|||
const params = { |
|||
TableName: query.table, |
|||
...query.json, |
|||
} |
|||
if (query.index) { |
|||
params.IndexName = query.index |
|||
} |
|||
const response = await this.client.scan(params).promise() |
|||
if (response.Items) { |
|||
return response.Items |
|||
} |
|||
return response |
|||
} |
|||
|
|||
async get(query) { |
|||
const params = { |
|||
TableName: query.table, |
|||
...query.json, |
|||
} |
|||
return this.client.get(params).promise() |
|||
} |
|||
|
|||
async update(query) { |
|||
const params = { |
|||
TableName: query.table, |
|||
...query.json, |
|||
} |
|||
return this.client.update(params).promise() |
|||
} |
|||
|
|||
async delete(query) { |
|||
const params = { |
|||
TableName: query.table, |
|||
...query.json, |
|||
} |
|||
return this.client.delete(params).promise() |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
schema: SCHEMA, |
|||
integration: DynamoDBIntegration, |
|||
} |
|||
@ -0,0 +1,207 @@ |
|||
import { Integration, DatasourceFieldTypes, QueryTypes } from "./base/definitions" |
|||
|
|||
module DynamoModule { |
|||
const AWS = require("aws-sdk") |
|||
const { AWS_REGION } = require("../db/dynamoClient") |
|||
|
|||
interface DynamoDBConfig { |
|||
region: string |
|||
accessKeyId: string |
|||
secretAccessKey: string |
|||
endpoint: string |
|||
} |
|||
|
|||
const SCHEMA: Integration = { |
|||
docs: "https://github.com/dabit3/dynamodb-documentclient-cheat-sheet", |
|||
description: |
|||
"Amazon DynamoDB is a key-value and document database that delivers single-digit millisecond performance at any scale.", |
|||
friendlyName: "DynamoDB", |
|||
datasource: { |
|||
region: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
required: true, |
|||
default: "us-east-1", |
|||
}, |
|||
accessKeyId: { |
|||
type: DatasourceFieldTypes.PASSWORD, |
|||
required: true, |
|||
}, |
|||
secretAccessKey: { |
|||
type: DatasourceFieldTypes.PASSWORD, |
|||
required: true, |
|||
}, |
|||
endpoint: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
required: false, |
|||
default: "https://dynamodb.us-east-1.amazonaws.com", |
|||
}, |
|||
}, |
|||
query: { |
|||
create: { |
|||
type: QueryTypes.FIELDS, |
|||
customisable: true, |
|||
fields: { |
|||
table: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
required: true, |
|||
}, |
|||
}, |
|||
}, |
|||
read: { |
|||
type: QueryTypes.FIELDS, |
|||
customisable: true, |
|||
readable: true, |
|||
fields: { |
|||
table: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
required: true, |
|||
}, |
|||
index: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
}, |
|||
}, |
|||
}, |
|||
scan: { |
|||
type: QueryTypes.FIELDS, |
|||
customisable: true, |
|||
readable: true, |
|||
fields: { |
|||
table: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
required: true, |
|||
}, |
|||
index: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
}, |
|||
}, |
|||
}, |
|||
get: { |
|||
type: QueryTypes.FIELDS, |
|||
customisable: true, |
|||
readable: true, |
|||
fields: { |
|||
table: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
required: true, |
|||
}, |
|||
}, |
|||
}, |
|||
update: { |
|||
type: QueryTypes.FIELDS, |
|||
customisable: true, |
|||
fields: { |
|||
table: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
required: true, |
|||
}, |
|||
}, |
|||
}, |
|||
delete: { |
|||
type: QueryTypes.FIELDS, |
|||
customisable: true, |
|||
fields: { |
|||
table: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
required: true, |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
class DynamoDBIntegration { |
|||
private config: DynamoDBConfig |
|||
private client: any |
|||
|
|||
constructor(config: DynamoDBConfig) { |
|||
this.config = config |
|||
this.connect() |
|||
let options = { |
|||
correctClockSkew: true, |
|||
endpoint: config.endpoint ? config.endpoint : undefined, |
|||
} |
|||
this.client = new AWS.DynamoDB.DocumentClient(options) |
|||
} |
|||
|
|||
end() { |
|||
this.disconnect() |
|||
} |
|||
|
|||
connect() { |
|||
AWS.config.update(this.config) |
|||
} |
|||
|
|||
disconnect() { |
|||
AWS.config.update({ |
|||
secretAccessKey: undefined, |
|||
accessKeyId: undefined, |
|||
region: AWS_REGION, |
|||
}) |
|||
} |
|||
|
|||
async create(query: { table: string; json: object }) { |
|||
const params = { |
|||
TableName: query.table, |
|||
...query.json, |
|||
} |
|||
return this.client.put(params).promise() |
|||
} |
|||
|
|||
async read(query: { table: string; json: object; index: null | string }) { |
|||
const params = { |
|||
TableName: query.table, |
|||
IndexName: query.index ? query.index : undefined, |
|||
...query.json, |
|||
} |
|||
if (query.index) { |
|||
const response = await this.client.query(params).promise() |
|||
if (response.Items) { |
|||
return response.Items |
|||
} |
|||
return response |
|||
} |
|||
} |
|||
|
|||
async scan(query: { table: string; json: object; index: null | string }) { |
|||
const params = { |
|||
TableName: query.table, |
|||
IndexName: query.index ? query.index : undefined, |
|||
...query.json, |
|||
} |
|||
const response = await this.client.scan(params).promise() |
|||
if (response.Items) { |
|||
return response.Items |
|||
} |
|||
return response |
|||
} |
|||
|
|||
async get(query: { table: string; json: object }) { |
|||
const params = { |
|||
TableName: query.table, |
|||
...query.json, |
|||
} |
|||
return this.client.get(params).promise() |
|||
} |
|||
|
|||
async update(query: { table: string; json: object }) { |
|||
const params = { |
|||
TableName: query.table, |
|||
...query.json, |
|||
} |
|||
return this.client.update(params).promise() |
|||
} |
|||
|
|||
async delete(query: { table: string; json: object }) { |
|||
const params = { |
|||
TableName: query.table, |
|||
...query.json, |
|||
} |
|||
return this.client.delete(params).promise() |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
schema: SCHEMA, |
|||
integration: DynamoDBIntegration, |
|||
} |
|||
} |
|||
@ -1,139 +0,0 @@ |
|||
const { Client } = require("@elastic/elasticsearch") |
|||
const { QUERY_TYPES, FIELD_TYPES } = require("./Integration") |
|||
|
|||
const SCHEMA = { |
|||
docs: "https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/index.html", |
|||
description: |
|||
"Elasticsearch is a search engine based on the Lucene library. It provides a distributed, multitenant-capable full-text search engine with an HTTP web interface and schema-free JSON documents.", |
|||
friendlyName: "ElasticSearch", |
|||
datasource: { |
|||
url: { |
|||
type: "string", |
|||
required: true, |
|||
default: "http://localhost:9200", |
|||
}, |
|||
}, |
|||
query: { |
|||
create: { |
|||
type: QUERY_TYPES.FIELDS, |
|||
customisable: true, |
|||
fields: { |
|||
index: { |
|||
type: FIELD_TYPES.STRING, |
|||
required: true, |
|||
}, |
|||
}, |
|||
}, |
|||
read: { |
|||
type: QUERY_TYPES.FIELDS, |
|||
customisable: true, |
|||
fields: { |
|||
index: { |
|||
type: FIELD_TYPES.STRING, |
|||
required: true, |
|||
}, |
|||
}, |
|||
}, |
|||
update: { |
|||
type: QUERY_TYPES.FIELDS, |
|||
customisable: true, |
|||
fields: { |
|||
id: { |
|||
type: FIELD_TYPES.STRING, |
|||
required: true, |
|||
}, |
|||
index: { |
|||
type: FIELD_TYPES.STRING, |
|||
required: true, |
|||
}, |
|||
}, |
|||
}, |
|||
delete: { |
|||
type: QUERY_TYPES.FIELDS, |
|||
fields: { |
|||
index: { |
|||
type: FIELD_TYPES.STRING, |
|||
required: true, |
|||
}, |
|||
id: { |
|||
type: FIELD_TYPES.STRING, |
|||
required: true, |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
class ElasticSearchIntegration { |
|||
constructor(config) { |
|||
this.config = config |
|||
this.client = new Client({ node: config.url }) |
|||
} |
|||
|
|||
async create(query) { |
|||
const { index, json } = query |
|||
|
|||
try { |
|||
const result = await this.client.index({ |
|||
index, |
|||
body: json, |
|||
}) |
|||
return result.body |
|||
} catch (err) { |
|||
console.error("Error writing to elasticsearch", err) |
|||
throw err |
|||
} finally { |
|||
await this.client.close() |
|||
} |
|||
} |
|||
|
|||
async read(query) { |
|||
const { index, json } = query |
|||
try { |
|||
const result = await this.client.search({ |
|||
index: index, |
|||
body: json, |
|||
}) |
|||
return result.body.hits.hits.map(({ _source }) => _source) |
|||
} catch (err) { |
|||
console.error("Error querying elasticsearch", err) |
|||
throw err |
|||
} finally { |
|||
await this.client.close() |
|||
} |
|||
} |
|||
|
|||
async update(query) { |
|||
const { id, index, json } = query |
|||
try { |
|||
const result = await this.client.update({ |
|||
id, |
|||
index, |
|||
body: json, |
|||
}) |
|||
return result.body |
|||
} catch (err) { |
|||
console.error("Error querying elasticsearch", err) |
|||
throw err |
|||
} finally { |
|||
await this.client.close() |
|||
} |
|||
} |
|||
|
|||
async delete(query) { |
|||
try { |
|||
const result = await this.client.delete(query) |
|||
return result.body |
|||
} catch (err) { |
|||
console.error("Error deleting from elasticsearch", err) |
|||
throw err |
|||
} finally { |
|||
await this.client.close() |
|||
} |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
schema: SCHEMA, |
|||
integration: ElasticSearchIntegration, |
|||
} |
|||
@ -0,0 +1,149 @@ |
|||
import { Integration, DatasourceFieldTypes, QueryTypes } from "./base/definitions" |
|||
|
|||
module ElasticsearchModule { |
|||
const { Client } = require("@elastic/elasticsearch") |
|||
|
|||
interface ElasticsearchConfig { |
|||
url: string |
|||
} |
|||
|
|||
const SCHEMA: Integration = { |
|||
docs: "https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/index.html", |
|||
description: |
|||
"Elasticsearch is a search engine based on the Lucene library. It provides a distributed, multitenant-capable full-text search engine with an HTTP web interface and schema-free JSON documents.", |
|||
friendlyName: "ElasticSearch", |
|||
datasource: { |
|||
url: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
required: true, |
|||
default: "http://localhost:9200", |
|||
}, |
|||
}, |
|||
query: { |
|||
create: { |
|||
type: QueryTypes.FIELDS, |
|||
customisable: true, |
|||
fields: { |
|||
index: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
required: true, |
|||
}, |
|||
}, |
|||
}, |
|||
read: { |
|||
type: QueryTypes.FIELDS, |
|||
customisable: true, |
|||
fields: { |
|||
index: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
required: true, |
|||
}, |
|||
}, |
|||
}, |
|||
update: { |
|||
type: QueryTypes.FIELDS, |
|||
customisable: true, |
|||
fields: { |
|||
id: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
required: true, |
|||
}, |
|||
index: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
required: true, |
|||
}, |
|||
}, |
|||
}, |
|||
delete: { |
|||
type: QueryTypes.FIELDS, |
|||
fields: { |
|||
index: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
required: true, |
|||
}, |
|||
id: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
required: true, |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
class ElasticSearchIntegration { |
|||
private config: ElasticsearchConfig |
|||
private client: any |
|||
|
|||
constructor(config: ElasticsearchConfig) { |
|||
this.config = config |
|||
this.client = new Client({ node: config.url }) |
|||
} |
|||
|
|||
async create(query: { index: string; json: object }) { |
|||
const { index, json } = query |
|||
|
|||
try { |
|||
const result = await this.client.index({ |
|||
index, |
|||
body: json, |
|||
}) |
|||
return result.body |
|||
} catch (err) { |
|||
console.error("Error writing to elasticsearch", err) |
|||
throw err |
|||
} finally { |
|||
await this.client.close() |
|||
} |
|||
} |
|||
|
|||
async read(query: { index: string; json: object }) { |
|||
const { index, json } = query |
|||
try { |
|||
const result = await this.client.search({ |
|||
index: index, |
|||
body: json, |
|||
}) |
|||
return result.body.hits.hits.map(({ _source }: any) => _source) |
|||
} catch (err) { |
|||
console.error("Error querying elasticsearch", err) |
|||
throw err |
|||
} finally { |
|||
await this.client.close() |
|||
} |
|||
} |
|||
|
|||
async update(query: { id: string; index: string; json: object }) { |
|||
const { id, index, json } = query |
|||
try { |
|||
const result = await this.client.update({ |
|||
id, |
|||
index, |
|||
body: json, |
|||
}) |
|||
return result.body |
|||
} catch (err) { |
|||
console.error("Error querying elasticsearch", err) |
|||
throw err |
|||
} finally { |
|||
await this.client.close() |
|||
} |
|||
} |
|||
|
|||
async delete(query: object) { |
|||
try { |
|||
const result = await this.client.delete(query) |
|||
return result.body |
|||
} catch (err) { |
|||
console.error("Error deleting from elasticsearch", err) |
|||
throw err |
|||
} finally { |
|||
await this.client.close() |
|||
} |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
schema: SCHEMA, |
|||
integration: ElasticSearchIntegration, |
|||
} |
|||
} |
|||
@ -1,123 +0,0 @@ |
|||
const sqlServer = require("mssql") |
|||
const { FIELD_TYPES } = require("./Integration") |
|||
const Sql = require("./base/sql") |
|||
|
|||
const SCHEMA = { |
|||
docs: "https://github.com/tediousjs/node-mssql", |
|||
description: |
|||
"Microsoft SQL Server is a relational database management system developed by Microsoft. ", |
|||
friendlyName: "MS SQL Server", |
|||
datasource: { |
|||
user: { |
|||
type: FIELD_TYPES.STRING, |
|||
required: true, |
|||
default: "localhost", |
|||
}, |
|||
password: { |
|||
type: FIELD_TYPES.PASSWORD, |
|||
required: true, |
|||
}, |
|||
server: { |
|||
type: FIELD_TYPES.STRING, |
|||
default: "localhost", |
|||
}, |
|||
port: { |
|||
type: FIELD_TYPES.NUMBER, |
|||
required: false, |
|||
default: 1433, |
|||
}, |
|||
database: { |
|||
type: FIELD_TYPES.STRING, |
|||
default: "root", |
|||
}, |
|||
encrypt: { |
|||
type: FIELD_TYPES.BOOLEAN, |
|||
default: true, |
|||
}, |
|||
}, |
|||
query: { |
|||
create: { |
|||
type: "sql", |
|||
}, |
|||
read: { |
|||
type: "sql", |
|||
}, |
|||
update: { |
|||
type: "sql", |
|||
}, |
|||
delete: { |
|||
type: "sql", |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
async function internalQuery(client, query) { |
|||
const sql = typeof query === "string" ? query : query.sql |
|||
const bindings = typeof query === "string" ? {} : query.bindings |
|||
try { |
|||
return await client.query(sql, bindings) |
|||
} catch (err) { |
|||
throw new Error(err) |
|||
} |
|||
} |
|||
|
|||
class SqlServerIntegration extends Sql { |
|||
static pool |
|||
|
|||
constructor(config) { |
|||
super("mssql") |
|||
this.config = config |
|||
this.config.options = { |
|||
encrypt: this.config.encrypt, |
|||
} |
|||
delete this.config.encrypt |
|||
if (!this.pool) { |
|||
this.pool = new sqlServer.ConnectionPool(this.config) |
|||
} |
|||
} |
|||
|
|||
async connect() { |
|||
try { |
|||
const client = await this.pool.connect() |
|||
this.client = client.request() |
|||
} catch (err) { |
|||
throw new Error(err) |
|||
} |
|||
} |
|||
|
|||
async read(query) { |
|||
await this.connect() |
|||
const response = await internalQuery(this.client, query) |
|||
return response.recordset |
|||
} |
|||
|
|||
async create(query) { |
|||
await this.connect() |
|||
const response = await internalQuery(this.client, query) |
|||
return response.recordset || [{ created: true }] |
|||
} |
|||
|
|||
async update(query) { |
|||
await this.connect() |
|||
const response = await internalQuery(this.client, query) |
|||
return response.recordset || [{ updated: true }] |
|||
} |
|||
|
|||
async delete(query) { |
|||
await this.connect() |
|||
const response = await internalQuery(this.client, query) |
|||
return response.recordset || [{ deleted: true }] |
|||
} |
|||
|
|||
async query(json) { |
|||
const operation = this._operation(json).toLowerCase() |
|||
const input = this._query(json) |
|||
const response = await internalQuery(this.client, input) |
|||
return response.recordset ? response.recordset : [{ [operation]: true }] |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
schema: SCHEMA, |
|||
integration: SqlServerIntegration, |
|||
} |
|||
@ -0,0 +1,140 @@ |
|||
import {Integration, DatasourceFieldTypes, QueryTypes, QueryJson} from "./base/definitions" |
|||
|
|||
module MSSQLModule { |
|||
const sqlServer = require("mssql") |
|||
const Sql = require("./base/sql") |
|||
|
|||
interface MSSQLConfig { |
|||
user: string |
|||
password: string |
|||
server: string |
|||
port: number |
|||
database: string |
|||
encrypt?: boolean |
|||
} |
|||
|
|||
const SCHEMA: Integration = { |
|||
docs: "https://github.com/tediousjs/node-mssql", |
|||
description: |
|||
"Microsoft SQL Server is a relational database management system developed by Microsoft. ", |
|||
friendlyName: "MS SQL Server", |
|||
datasource: { |
|||
user: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
required: true, |
|||
default: "localhost", |
|||
}, |
|||
password: { |
|||
type: DatasourceFieldTypes.PASSWORD, |
|||
required: true, |
|||
}, |
|||
server: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
default: "localhost", |
|||
}, |
|||
port: { |
|||
type: DatasourceFieldTypes.NUMBER, |
|||
required: false, |
|||
default: 1433, |
|||
}, |
|||
database: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
default: "root", |
|||
}, |
|||
encrypt: { |
|||
type: DatasourceFieldTypes.BOOLEAN, |
|||
default: true, |
|||
}, |
|||
}, |
|||
query: { |
|||
create: { |
|||
type: QueryTypes.SQL, |
|||
}, |
|||
read: { |
|||
type: QueryTypes.SQL, |
|||
}, |
|||
update: { |
|||
type: QueryTypes.SQL, |
|||
}, |
|||
delete: { |
|||
type: QueryTypes.SQL, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
async function internalQuery( |
|||
client: any, |
|||
query: { sql: string; bindings?: object } |
|||
) { |
|||
try { |
|||
return await client.query(query.sql, query.bindings || {}) |
|||
} catch (err) { |
|||
throw new Error(err) |
|||
} |
|||
} |
|||
|
|||
class SqlServerIntegration extends Sql { |
|||
private readonly config: MSSQLConfig |
|||
static pool: any |
|||
|
|||
constructor(config: MSSQLConfig) { |
|||
super("mssql") |
|||
this.config = config |
|||
const clientCfg = { |
|||
...this.config, |
|||
options: { |
|||
encrypt: this.config.encrypt, |
|||
}, |
|||
} |
|||
delete clientCfg.encrypt |
|||
if (!this.pool) { |
|||
this.pool = new sqlServer.ConnectionPool(clientCfg) |
|||
} |
|||
} |
|||
|
|||
async connect() { |
|||
try { |
|||
const client = await this.pool.connect() |
|||
this.client = client.request() |
|||
} catch (err) { |
|||
throw new Error(err) |
|||
} |
|||
} |
|||
|
|||
async read(query: string) { |
|||
await this.connect() |
|||
const response = await internalQuery(this.client, { sql: query }) |
|||
return response.recordset |
|||
} |
|||
|
|||
async create(query: string) { |
|||
await this.connect() |
|||
const response = await internalQuery(this.client, { sql: query }) |
|||
return response.recordset || [{ created: true }] |
|||
} |
|||
|
|||
async update(query: string) { |
|||
await this.connect() |
|||
const response = await internalQuery(this.client, { sql: query }) |
|||
return response.recordset || [{ updated: true }] |
|||
} |
|||
|
|||
async delete(query: string) { |
|||
await this.connect() |
|||
const response = await internalQuery(this.client, { sql: query }) |
|||
return response.recordset || [{ deleted: true }] |
|||
} |
|||
|
|||
async query(json: QueryJson) { |
|||
const operation = this._operation(json).toLowerCase() |
|||
const input = this._query(json) |
|||
const response = await internalQuery(this.client, input) |
|||
return response.recordset ? response.recordset : [{ [operation]: true }] |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
schema: SCHEMA, |
|||
integration: SqlServerIntegration, |
|||
} |
|||
} |
|||
@ -1,78 +0,0 @@ |
|||
const { MongoClient } = require("mongodb") |
|||
const { FIELD_TYPES, QUERY_TYPES } = require("./Integration") |
|||
|
|||
const SCHEMA = { |
|||
docs: "https://github.com/mongodb/node-mongodb-native", |
|||
friendlyName: "MongoDB", |
|||
description: |
|||
"MongoDB is a general purpose, document-based, distributed database built for modern application developers and for the cloud era.", |
|||
datasource: { |
|||
connectionString: { |
|||
type: FIELD_TYPES.STRING, |
|||
required: true, |
|||
default: "mongodb://localhost:27017", |
|||
}, |
|||
db: { |
|||
type: FIELD_TYPES.STRING, |
|||
required: true, |
|||
}, |
|||
collection: { |
|||
type: FIELD_TYPES.STRING, |
|||
required: true, |
|||
}, |
|||
}, |
|||
query: { |
|||
create: { |
|||
type: QUERY_TYPES.JSON, |
|||
}, |
|||
read: { |
|||
type: QUERY_TYPES.JSON, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
class MongoIntegration { |
|||
constructor(config) { |
|||
this.config = config |
|||
this.client = new MongoClient(config.connectionString) |
|||
} |
|||
|
|||
async connect() { |
|||
return this.client.connect() |
|||
} |
|||
|
|||
async create(query) { |
|||
try { |
|||
await this.connect() |
|||
const db = this.client.db(this.config.db) |
|||
const collection = db.collection(this.config.collection) |
|||
const result = await collection.insertOne(query.json) |
|||
return result |
|||
} catch (err) { |
|||
console.error("Error writing to mongodb", err) |
|||
throw err |
|||
} finally { |
|||
await this.client.close() |
|||
} |
|||
} |
|||
|
|||
async read(query) { |
|||
try { |
|||
await this.connect() |
|||
const db = this.client.db(this.config.db) |
|||
const collection = db.collection(this.config.collection) |
|||
const result = await collection.find(query.json).toArray() |
|||
return result |
|||
} catch (err) { |
|||
console.error("Error querying mongodb", err) |
|||
throw err |
|||
} finally { |
|||
await this.client.close() |
|||
} |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
schema: SCHEMA, |
|||
integration: MongoIntegration, |
|||
} |
|||
@ -0,0 +1,88 @@ |
|||
import { Integration, DatasourceFieldTypes, QueryTypes } from "./base/definitions" |
|||
|
|||
module MongoDBModule { |
|||
const { MongoClient } = require("mongodb") |
|||
|
|||
interface MongoDBConfig { |
|||
connectionString: string |
|||
db: string |
|||
collection: string |
|||
} |
|||
|
|||
const SCHEMA: Integration = { |
|||
docs: "https://github.com/mongodb/node-mongodb-native", |
|||
friendlyName: "MongoDB", |
|||
description: |
|||
"MongoDB is a general purpose, document-based, distributed database built for modern application developers and for the cloud era.", |
|||
datasource: { |
|||
connectionString: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
required: true, |
|||
default: "mongodb://localhost:27017", |
|||
}, |
|||
db: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
required: true, |
|||
}, |
|||
collection: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
required: true, |
|||
}, |
|||
}, |
|||
query: { |
|||
create: { |
|||
type: QueryTypes.JSON, |
|||
}, |
|||
read: { |
|||
type: QueryTypes.JSON, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
class MongoIntegration { |
|||
private config: MongoDBConfig |
|||
private client: any |
|||
|
|||
constructor(config: MongoDBConfig) { |
|||
this.config = config |
|||
this.client = new MongoClient(config.connectionString) |
|||
} |
|||
|
|||
async connect() { |
|||
return this.client.connect() |
|||
} |
|||
|
|||
async create(query: { json: object }) { |
|||
try { |
|||
await this.connect() |
|||
const db = this.client.db(this.config.db) |
|||
const collection = db.collection(this.config.collection) |
|||
return collection.insertOne(query.json) |
|||
} catch (err) { |
|||
console.error("Error writing to mongodb", err) |
|||
throw err |
|||
} finally { |
|||
await this.client.close() |
|||
} |
|||
} |
|||
|
|||
async read(query: { json: object }) { |
|||
try { |
|||
await this.connect() |
|||
const db = this.client.db(this.config.db) |
|||
const collection = db.collection(this.config.collection) |
|||
return collection.find(query.json).toArray() |
|||
} catch (err) { |
|||
console.error("Error querying mongodb", err) |
|||
throw err |
|||
} finally { |
|||
await this.client.close() |
|||
} |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
schema: SCHEMA, |
|||
integration: MongoIntegration, |
|||
} |
|||
} |
|||
@ -1,225 +0,0 @@ |
|||
const mysql = require("mysql") |
|||
const { FIELD_TYPES, QUERY_TYPES } = require("./Integration") |
|||
const Sql = require("./base/sql") |
|||
const { buildExternalTableId, convertType } = require("./utils") |
|||
const { FieldTypes } = require("../constants") |
|||
const { Operation } = require("./base/constants") |
|||
|
|||
const TYPE_MAP = { |
|||
text: FieldTypes.LONGFORM, |
|||
blob: FieldTypes.LONGFORM, |
|||
enum: FieldTypes.STRING, |
|||
varchar: FieldTypes.STRING, |
|||
int: FieldTypes.NUMBER, |
|||
numeric: FieldTypes.NUMBER, |
|||
bigint: FieldTypes.NUMBER, |
|||
mediumint: FieldTypes.NUMBER, |
|||
decimal: FieldTypes.NUMBER, |
|||
dec: FieldTypes.NUMBER, |
|||
double: FieldTypes.NUMBER, |
|||
real: FieldTypes.NUMBER, |
|||
fixed: FieldTypes.NUMBER, |
|||
smallint: FieldTypes.NUMBER, |
|||
timestamp: FieldTypes.DATETIME, |
|||
date: FieldTypes.DATETIME, |
|||
datetime: FieldTypes.DATETIME, |
|||
time: FieldTypes.DATETIME, |
|||
tinyint: FieldTypes.BOOLEAN, |
|||
json: FIELD_TYPES.JSON, |
|||
} |
|||
|
|||
const SCHEMA = { |
|||
docs: "https://github.com/mysqljs/mysql", |
|||
plus: true, |
|||
friendlyName: "MySQL", |
|||
description: |
|||
"MySQL Database Service is a fully managed database service to deploy cloud-native applications. ", |
|||
datasource: { |
|||
host: { |
|||
type: FIELD_TYPES.STRING, |
|||
default: "localhost", |
|||
required: true, |
|||
}, |
|||
port: { |
|||
type: FIELD_TYPES.NUMBER, |
|||
default: 3306, |
|||
required: false, |
|||
}, |
|||
user: { |
|||
type: FIELD_TYPES.STRING, |
|||
default: "root", |
|||
required: true, |
|||
}, |
|||
password: { |
|||
type: FIELD_TYPES.PASSWORD, |
|||
default: "root", |
|||
required: true, |
|||
}, |
|||
database: { |
|||
type: FIELD_TYPES.STRING, |
|||
required: true, |
|||
}, |
|||
ssl: { |
|||
type: FIELD_TYPES.OBJECT, |
|||
required: false, |
|||
}, |
|||
}, |
|||
query: { |
|||
create: { |
|||
type: QUERY_TYPES.SQL, |
|||
}, |
|||
read: { |
|||
type: QUERY_TYPES.SQL, |
|||
}, |
|||
update: { |
|||
type: QUERY_TYPES.SQL, |
|||
}, |
|||
delete: { |
|||
type: QUERY_TYPES.SQL, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
function internalQuery(client, query, connect = true) { |
|||
const sql = typeof query === "string" ? query : query.sql |
|||
const bindings = typeof query === "string" ? {} : query.bindings |
|||
// Node MySQL is callback based, so we must wrap our call in a promise
|
|||
return new Promise((resolve, reject) => { |
|||
if (connect) { |
|||
client.connect() |
|||
} |
|||
return client.query(sql, bindings, (error, results) => { |
|||
if (error) { |
|||
reject(error) |
|||
} else { |
|||
resolve(results) |
|||
} |
|||
if (connect) { |
|||
client.end() |
|||
} |
|||
}) |
|||
}) |
|||
} |
|||
|
|||
class MySQLIntegration extends Sql { |
|||
constructor(config) { |
|||
super("mysql") |
|||
this.config = config |
|||
if (config.ssl && Object.keys(config.ssl).length === 0) { |
|||
delete config.ssl |
|||
} |
|||
this.client = mysql.createConnection(config) |
|||
} |
|||
|
|||
async buildSchema(datasourceId) { |
|||
const tables = {} |
|||
const database = this.config.database |
|||
this.client.connect() |
|||
|
|||
// get the tables first
|
|||
const tablesResp = await internalQuery(this.client, "SHOW TABLES;", false) |
|||
const tableNames = tablesResp.map(obj => obj[`Tables_in_${database}`]) |
|||
for (let tableName of tableNames) { |
|||
const primaryKeys = [] |
|||
const schema = {} |
|||
const descResp = await internalQuery( |
|||
this.client, |
|||
`DESCRIBE ${tableName};`, |
|||
false |
|||
) |
|||
for (let column of descResp) { |
|||
const columnName = column.Field |
|||
if (column.Key === "PRI") { |
|||
primaryKeys.push(columnName) |
|||
} |
|||
const constraints = {} |
|||
if (column.Null !== "YES") { |
|||
constraints.required = true |
|||
} |
|||
schema[columnName] = { |
|||
name: columnName, |
|||
type: convertType(column.Type, TYPE_MAP), |
|||
constraints, |
|||
} |
|||
} |
|||
// for now just default to first column
|
|||
if (primaryKeys.length === 0) { |
|||
primaryKeys.push(descResp[0].Field) |
|||
} |
|||
if (!tables[tableName]) { |
|||
tables[tableName] = { |
|||
_id: buildExternalTableId(datasourceId, tableName), |
|||
primary: primaryKeys, |
|||
name: tableName, |
|||
schema, |
|||
} |
|||
} |
|||
} |
|||
|
|||
this.client.end() |
|||
this.tables = tables |
|||
} |
|||
|
|||
async create(query) { |
|||
const results = await internalQuery(this.client, query) |
|||
return results.length ? results : [{ created: true }] |
|||
} |
|||
|
|||
read(query) { |
|||
return internalQuery(this.client, query) |
|||
} |
|||
|
|||
async update(query) { |
|||
const results = await internalQuery(this.client, query) |
|||
return results.length ? results : [{ updated: true }] |
|||
} |
|||
|
|||
async delete(query) { |
|||
const results = await internalQuery(this.client, query) |
|||
return results.length ? results : [{ deleted: true }] |
|||
} |
|||
|
|||
async getReturningRow(json) { |
|||
if (!json.extra.idFilter) { |
|||
return {} |
|||
} |
|||
const input = this._query({ |
|||
endpoint: { |
|||
...json.endpoint, |
|||
operation: Operation.READ, |
|||
}, |
|||
fields: [], |
|||
filters: json.extra.idFilter, |
|||
paginate: { |
|||
limit: 1, |
|||
}, |
|||
}) |
|||
return internalQuery(this.client, input, false) |
|||
} |
|||
|
|||
async query(json) { |
|||
const operation = this._operation(json) |
|||
this.client.connect() |
|||
const input = this._query(json, { disableReturning: true }) |
|||
let row |
|||
// need to manage returning, a feature mySQL can't do
|
|||
if (operation === Operation.DELETE) { |
|||
row = this.getReturningRow(json) |
|||
} |
|||
const results = await internalQuery(this.client, input, false) |
|||
// same as delete, manage returning
|
|||
if (operation === Operation.CREATE || operation === Operation.UPDATE) { |
|||
row = this.getReturningRow(json) |
|||
} |
|||
this.client.end() |
|||
if (operation !== Operation.READ) { |
|||
return row |
|||
} |
|||
return results.length ? results : [{ [operation.toLowerCase()]: true }] |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
schema: SCHEMA, |
|||
integration: MySQLIntegration, |
|||
} |
|||
@ -0,0 +1,248 @@ |
|||
import { Integration, DatasourceFieldTypes, QueryTypes, Operation, QueryJson } from "./base/definitions" |
|||
|
|||
module MySQLModule { |
|||
const mysql = require("mysql") |
|||
const Sql = require("./base/sql") |
|||
const { buildExternalTableId, convertType } = require("./utils") |
|||
const { FieldTypes } = require("../constants") |
|||
|
|||
interface MySQLConfig { |
|||
host: string |
|||
port: number |
|||
user: string |
|||
password: string |
|||
database: string |
|||
ssl?: object |
|||
} |
|||
|
|||
const TYPE_MAP = { |
|||
text: FieldTypes.LONGFORM, |
|||
blob: FieldTypes.LONGFORM, |
|||
enum: FieldTypes.STRING, |
|||
varchar: FieldTypes.STRING, |
|||
int: FieldTypes.NUMBER, |
|||
numeric: FieldTypes.NUMBER, |
|||
bigint: FieldTypes.NUMBER, |
|||
mediumint: FieldTypes.NUMBER, |
|||
decimal: FieldTypes.NUMBER, |
|||
dec: FieldTypes.NUMBER, |
|||
double: FieldTypes.NUMBER, |
|||
real: FieldTypes.NUMBER, |
|||
fixed: FieldTypes.NUMBER, |
|||
smallint: FieldTypes.NUMBER, |
|||
timestamp: FieldTypes.DATETIME, |
|||
date: FieldTypes.DATETIME, |
|||
datetime: FieldTypes.DATETIME, |
|||
time: FieldTypes.DATETIME, |
|||
tinyint: FieldTypes.BOOLEAN, |
|||
json: DatasourceFieldTypes.JSON, |
|||
} |
|||
|
|||
const SCHEMA: Integration = { |
|||
docs: "https://github.com/mysqljs/mysql", |
|||
plus: true, |
|||
friendlyName: "MySQL", |
|||
description: |
|||
"MySQL Database Service is a fully managed database service to deploy cloud-native applications. ", |
|||
datasource: { |
|||
host: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
default: "localhost", |
|||
required: true, |
|||
}, |
|||
port: { |
|||
type: DatasourceFieldTypes.NUMBER, |
|||
default: 3306, |
|||
required: false, |
|||
}, |
|||
user: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
default: "root", |
|||
required: true, |
|||
}, |
|||
password: { |
|||
type: DatasourceFieldTypes.PASSWORD, |
|||
default: "root", |
|||
required: true, |
|||
}, |
|||
database: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
required: true, |
|||
}, |
|||
ssl: { |
|||
type: DatasourceFieldTypes.OBJECT, |
|||
required: false, |
|||
}, |
|||
}, |
|||
query: { |
|||
create: { |
|||
type: QueryTypes.SQL, |
|||
}, |
|||
read: { |
|||
type: QueryTypes.SQL, |
|||
}, |
|||
update: { |
|||
type: QueryTypes.SQL, |
|||
}, |
|||
delete: { |
|||
type: QueryTypes.SQL, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
function internalQuery( |
|||
client: any, |
|||
query: { sql: string; bindings?: object }, |
|||
connect: boolean = true |
|||
): Promise<any[]> { |
|||
// Node MySQL is callback based, so we must wrap our call in a promise
|
|||
return new Promise((resolve, reject) => { |
|||
if (connect) { |
|||
client.connect() |
|||
} |
|||
return client.query( |
|||
query.sql, |
|||
query.bindings || {}, |
|||
(error: any, results: object[]) => { |
|||
if (error) { |
|||
reject(error) |
|||
} else { |
|||
resolve(results) |
|||
} |
|||
if (connect) { |
|||
client.end() |
|||
} |
|||
} |
|||
) |
|||
}) |
|||
} |
|||
|
|||
class MySQLIntegration extends Sql { |
|||
private config: MySQLConfig |
|||
private readonly client: any |
|||
|
|||
constructor(config: MySQLConfig) { |
|||
super("mysql") |
|||
this.config = config |
|||
if (config.ssl && Object.keys(config.ssl).length === 0) { |
|||
delete config.ssl |
|||
} |
|||
this.client = mysql.createConnection(config) |
|||
} |
|||
|
|||
async buildSchema(datasourceId: string) { |
|||
const tables: any = {} |
|||
const database = this.config.database |
|||
this.client.connect() |
|||
|
|||
// get the tables first
|
|||
const tablesResp = await internalQuery( |
|||
this.client, |
|||
{ sql: "SHOW TABLES;" }, |
|||
false |
|||
) |
|||
const tableNames = tablesResp.map((obj: any) => obj[`Tables_in_${database}`]) |
|||
for (let tableName of tableNames) { |
|||
const primaryKeys = [] |
|||
const schema: any = {} |
|||
const descResp = await internalQuery( |
|||
this.client, |
|||
{ sql: `DESCRIBE ${tableName};` }, |
|||
false |
|||
) |
|||
for (let column of descResp) { |
|||
const columnName = column.Field |
|||
if (column.Key === "PRI") { |
|||
primaryKeys.push(columnName) |
|||
} |
|||
const constraints = { |
|||
required: column.Null !== "YES", |
|||
} |
|||
schema[columnName] = { |
|||
name: columnName, |
|||
type: convertType(column.Type, TYPE_MAP), |
|||
constraints, |
|||
} |
|||
} |
|||
// for now just default to first column
|
|||
if (primaryKeys.length === 0) { |
|||
primaryKeys.push(descResp[0].Field) |
|||
} |
|||
if (!tables[tableName]) { |
|||
tables[tableName] = { |
|||
_id: buildExternalTableId(datasourceId, tableName), |
|||
primary: primaryKeys, |
|||
name: tableName, |
|||
schema, |
|||
} |
|||
} |
|||
} |
|||
|
|||
this.client.end() |
|||
this.tables = tables |
|||
} |
|||
|
|||
async create(query: string) { |
|||
const results = await internalQuery(this.client, { sql: query }) |
|||
return results.length ? results : [{ created: true }] |
|||
} |
|||
|
|||
read(query: string) { |
|||
return internalQuery(this.client, { sql: query }) |
|||
} |
|||
|
|||
async update(query: string) { |
|||
const results = await internalQuery(this.client, { sql: query }) |
|||
return results.length ? results : [{ updated: true }] |
|||
} |
|||
|
|||
async delete(query: string) { |
|||
const results = await internalQuery(this.client, { sql: query }) |
|||
return results.length ? results : [{ deleted: true }] |
|||
} |
|||
|
|||
async getReturningRow(json: QueryJson) { |
|||
if (!json.extra.idFilter) { |
|||
return {} |
|||
} |
|||
const input = this._query({ |
|||
endpoint: { |
|||
...json.endpoint, |
|||
operation: Operation.READ, |
|||
}, |
|||
fields: [], |
|||
filters: json.extra.idFilter, |
|||
paginate: { |
|||
limit: 1, |
|||
}, |
|||
}) |
|||
return internalQuery(this.client, input, false) |
|||
} |
|||
|
|||
async query(json: QueryJson) { |
|||
const operation = this._operation(json) |
|||
this.client.connect() |
|||
const input = this._query(json, { disableReturning: true }) |
|||
let row |
|||
// need to manage returning, a feature mySQL can't do
|
|||
if (operation === "awdawd") { |
|||
row = this.getReturningRow(json) |
|||
} |
|||
const results = await internalQuery(this.client, input, false) |
|||
// same as delete, manage returning
|
|||
if (operation === Operation.CREATE || operation === Operation.UPDATE) { |
|||
row = this.getReturningRow(json) |
|||
} |
|||
this.client.end() |
|||
if (operation !== Operation.READ) { |
|||
return row |
|||
} |
|||
return results.length ? results : [{ [operation.toLowerCase()]: true }] |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
schema: SCHEMA, |
|||
integration: MySQLIntegration, |
|||
} |
|||
} |
|||
@ -1,191 +0,0 @@ |
|||
const { Pool } = require("pg") |
|||
const { FIELD_TYPES } = require("./Integration") |
|||
const Sql = require("./base/sql") |
|||
const { FieldTypes } = require("../constants") |
|||
const { buildExternalTableId, convertType } = require("./utils") |
|||
|
|||
const SCHEMA = { |
|||
docs: "https://node-postgres.com", |
|||
plus: true, |
|||
friendlyName: "PostgreSQL", |
|||
description: |
|||
"PostgreSQL, also known as Postgres, is a free and open-source relational database management system emphasizing extensibility and SQL compliance.", |
|||
datasource: { |
|||
host: { |
|||
type: FIELD_TYPES.STRING, |
|||
default: "localhost", |
|||
required: true, |
|||
}, |
|||
port: { |
|||
type: FIELD_TYPES.NUMBER, |
|||
required: true, |
|||
default: 5432, |
|||
}, |
|||
database: { |
|||
type: FIELD_TYPES.STRING, |
|||
default: "postgres", |
|||
required: true, |
|||
}, |
|||
user: { |
|||
type: FIELD_TYPES.STRING, |
|||
default: "root", |
|||
required: true, |
|||
}, |
|||
password: { |
|||
type: FIELD_TYPES.PASSWORD, |
|||
default: "root", |
|||
required: true, |
|||
}, |
|||
ssl: { |
|||
type: FIELD_TYPES.BOOLEAN, |
|||
default: false, |
|||
required: false, |
|||
}, |
|||
}, |
|||
query: { |
|||
create: { |
|||
type: "sql", |
|||
}, |
|||
read: { |
|||
type: "sql", |
|||
}, |
|||
update: { |
|||
type: "sql", |
|||
}, |
|||
delete: { |
|||
type: "sql", |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
const TYPE_MAP = { |
|||
text: FieldTypes.LONGFORM, |
|||
varchar: FieldTypes.STRING, |
|||
integer: FieldTypes.NUMBER, |
|||
bigint: FieldTypes.NUMBER, |
|||
decimal: FieldTypes.NUMBER, |
|||
smallint: FieldTypes.NUMBER, |
|||
timestamp: FieldTypes.DATETIME, |
|||
time: FieldTypes.DATETIME, |
|||
boolean: FieldTypes.BOOLEAN, |
|||
json: FIELD_TYPES.JSON, |
|||
} |
|||
|
|||
async function internalQuery(client, query) { |
|||
const sql = typeof query === "string" ? query : query.sql |
|||
const bindings = typeof query === "string" ? {} : query.bindings |
|||
try { |
|||
return await client.query(sql, bindings) |
|||
} catch (err) { |
|||
throw new Error(err) |
|||
} |
|||
} |
|||
|
|||
class PostgresIntegration extends Sql { |
|||
static pool |
|||
|
|||
COLUMNS_SQL = |
|||
"select * from information_schema.columns where table_schema = 'public'" |
|||
|
|||
PRIMARY_KEYS_SQL = ` |
|||
select tc.table_schema, tc.table_name, kc.column_name as primary_key |
|||
from information_schema.table_constraints tc |
|||
join |
|||
information_schema.key_column_usage kc on kc.table_name = tc.table_name |
|||
and kc.table_schema = tc.table_schema |
|||
and kc.constraint_name = tc.constraint_name |
|||
where tc.constraint_type = 'PRIMARY KEY'; |
|||
` |
|||
|
|||
constructor(config) { |
|||
super("pg") |
|||
this.config = config |
|||
if (this.config.ssl) { |
|||
this.config.ssl = { |
|||
rejectUnauthorized: true, |
|||
} |
|||
} |
|||
|
|||
if (!this.pool) { |
|||
this.pool = new Pool(this.config) |
|||
} |
|||
|
|||
this.client = this.pool |
|||
} |
|||
|
|||
/** |
|||
* Fetches the tables from the postgres table and assigns them to the datasource. |
|||
* @param {*} datasourceId - datasourceId to fetch |
|||
*/ |
|||
async buildSchema(datasourceId) { |
|||
let tableKeys = {} |
|||
try { |
|||
const primaryKeysResponse = await this.client.query(this.PRIMARY_KEYS_SQL) |
|||
for (let table of primaryKeysResponse.rows) { |
|||
const tableName = table.table_name |
|||
if (!tableKeys[tableName]) { |
|||
tableKeys[tableName] = [] |
|||
} |
|||
tableKeys[tableName].push(table.column_name || table.primary_key) |
|||
} |
|||
} catch (err) { |
|||
tableKeys = {} |
|||
} |
|||
|
|||
const columnsResponse = await this.client.query(this.COLUMNS_SQL) |
|||
const tables = {} |
|||
|
|||
for (let column of columnsResponse.rows) { |
|||
const tableName = column.table_name |
|||
const columnName = column.column_name |
|||
|
|||
// table key doesn't exist yet
|
|||
if (!tables[tableName]) { |
|||
tables[tableName] = { |
|||
_id: buildExternalTableId(datasourceId, tableName), |
|||
primary: tableKeys[tableName] || ["id"], |
|||
name: tableName, |
|||
schema: {}, |
|||
} |
|||
} |
|||
|
|||
tables[tableName].schema[columnName] = { |
|||
name: columnName, |
|||
type: convertType(column.data_type, TYPE_MAP), |
|||
} |
|||
} |
|||
this.tables = tables |
|||
} |
|||
|
|||
async create(sql) { |
|||
const response = await internalQuery(this.client, sql) |
|||
return response.rows.length ? response.rows : [{ created: true }] |
|||
} |
|||
|
|||
async read(sql) { |
|||
const response = await internalQuery(this.client, sql) |
|||
return response.rows |
|||
} |
|||
|
|||
async update(sql) { |
|||
const response = await internalQuery(this.client, sql) |
|||
return response.rows.length ? response.rows : [{ updated: true }] |
|||
} |
|||
|
|||
async delete(sql) { |
|||
const response = await internalQuery(this.client, sql) |
|||
return response.rows.length ? response.rows : [{ deleted: true }] |
|||
} |
|||
|
|||
async query(json) { |
|||
const operation = this._operation(json).toLowerCase() |
|||
const input = this._query(json) |
|||
const response = await internalQuery(this.client, input) |
|||
return response.rows.length ? response.rows : [{ [operation]: true }] |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
schema: SCHEMA, |
|||
integration: PostgresIntegration, |
|||
} |
|||
@ -0,0 +1,204 @@ |
|||
import { Integration, DatasourceFieldTypes, QueryTypes, QueryJson } from "./base/definitions" |
|||
import { Table } from "../constants/definitions" |
|||
|
|||
module PostgresModule { |
|||
const { Pool } = require("pg") |
|||
const Sql = require("./base/sql") |
|||
const { FieldTypes } = require("../constants") |
|||
const { buildExternalTableId, convertType } = require("./utils") |
|||
|
|||
interface PostgresConfig { |
|||
host: string, |
|||
port: number, |
|||
database: string, |
|||
user: string, |
|||
password: string, |
|||
ssl?: boolean, |
|||
} |
|||
|
|||
const SCHEMA: Integration = { |
|||
docs: "https://node-postgres.com", |
|||
plus: true, |
|||
friendlyName: "PostgreSQL", |
|||
description: |
|||
"PostgreSQL, also known as Postgres, is a free and open-source relational database management system emphasizing extensibility and SQL compliance.", |
|||
datasource: { |
|||
host: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
default: "localhost", |
|||
required: true, |
|||
}, |
|||
port: { |
|||
type: DatasourceFieldTypes.NUMBER, |
|||
required: true, |
|||
default: 5432, |
|||
}, |
|||
database: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
default: "postgres", |
|||
required: true, |
|||
}, |
|||
user: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
default: "root", |
|||
required: true, |
|||
}, |
|||
password: { |
|||
type: DatasourceFieldTypes.PASSWORD, |
|||
default: "root", |
|||
required: true, |
|||
}, |
|||
ssl: { |
|||
type: DatasourceFieldTypes.BOOLEAN, |
|||
default: false, |
|||
required: false, |
|||
}, |
|||
}, |
|||
query: { |
|||
create: { |
|||
type: QueryTypes.SQL, |
|||
}, |
|||
read: { |
|||
type: QueryTypes.SQL, |
|||
}, |
|||
update: { |
|||
type: QueryTypes.SQL, |
|||
}, |
|||
delete: { |
|||
type: QueryTypes.SQL, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
const TYPE_MAP = { |
|||
text: FieldTypes.LONGFORM, |
|||
varchar: FieldTypes.STRING, |
|||
integer: FieldTypes.NUMBER, |
|||
bigint: FieldTypes.NUMBER, |
|||
decimal: FieldTypes.NUMBER, |
|||
smallint: FieldTypes.NUMBER, |
|||
timestamp: FieldTypes.DATETIME, |
|||
time: FieldTypes.DATETIME, |
|||
boolean: FieldTypes.BOOLEAN, |
|||
json: FieldTypes.JSON, |
|||
} |
|||
|
|||
async function internalQuery(client: any, query: { sql: string, bindings?: object }) { |
|||
try { |
|||
return await client.query(query.sql, query.bindings || {}) |
|||
} catch (err) { |
|||
throw new Error(err) |
|||
} |
|||
} |
|||
|
|||
class PostgresIntegration extends Sql { |
|||
static pool: any |
|||
private readonly client: any |
|||
private readonly config: PostgresConfig |
|||
|
|||
COLUMNS_SQL = |
|||
"select * from information_schema.columns where table_schema = 'public'" |
|||
|
|||
PRIMARY_KEYS_SQL = ` |
|||
select tc.table_schema, tc.table_name, kc.column_name as primary_key |
|||
from information_schema.table_constraints tc |
|||
join |
|||
information_schema.key_column_usage kc on kc.table_name = tc.table_name |
|||
and kc.table_schema = tc.table_schema |
|||
and kc.constraint_name = tc.constraint_name |
|||
where tc.constraint_type = 'PRIMARY KEY'; |
|||
` |
|||
|
|||
constructor(config: PostgresConfig) { |
|||
super("pg") |
|||
this.config = config |
|||
|
|||
let newConfig = { |
|||
...this.config, |
|||
ssl: this.config.ssl ? { rejectUnauthorized: true } : undefined, |
|||
} |
|||
if (!this.pool) { |
|||
this.pool = new Pool(newConfig) |
|||
} |
|||
|
|||
this.client = this.pool |
|||
} |
|||
|
|||
/** |
|||
* Fetches the tables from the postgres table and assigns them to the datasource. |
|||
* @param {*} datasourceId - datasourceId to fetch |
|||
*/ |
|||
async buildSchema(datasourceId: string) { |
|||
let tableKeys: { [key: string]: string[] } = {} |
|||
try { |
|||
const primaryKeysResponse = await this.client.query(this.PRIMARY_KEYS_SQL) |
|||
for (let table of primaryKeysResponse.rows) { |
|||
const tableName = table.table_name |
|||
if (!tableKeys[tableName]) { |
|||
tableKeys[tableName] = [] |
|||
} |
|||
tableKeys[tableName].push(table.column_name || table.primary_key) |
|||
} |
|||
} catch (err) { |
|||
tableKeys = {} |
|||
} |
|||
|
|||
const columnsResponse = await this.client.query(this.COLUMNS_SQL) |
|||
const tables: { [key: string]: Table } = {} |
|||
|
|||
for (let column of columnsResponse.rows) { |
|||
const tableName: string = column.table_name |
|||
const columnName: string = column.column_name |
|||
|
|||
// table key doesn't exist yet
|
|||
if (!tables[tableName] || !tables[tableName].schema) { |
|||
tables[tableName] = { |
|||
_id: buildExternalTableId(datasourceId, tableName), |
|||
primary: tableKeys[tableName] || ["id"], |
|||
name: tableName, |
|||
schema: {}, |
|||
} |
|||
} |
|||
|
|||
const type: string = convertType(column.data_type, TYPE_MAP) |
|||
tables[tableName].schema[columnName] = { |
|||
name: columnName, |
|||
type, |
|||
} |
|||
} |
|||
this.tables = tables |
|||
} |
|||
|
|||
async create(sql: string) { |
|||
const response = await internalQuery(this.client, { sql }) |
|||
return response.rows.length ? response.rows : [{created: true}] |
|||
} |
|||
|
|||
async read(sql: string) { |
|||
const response = await internalQuery(this.client, { sql }) |
|||
return response.rows |
|||
} |
|||
|
|||
async update(sql: string) { |
|||
const response = await internalQuery(this.client, { sql }) |
|||
return response.rows.length ? response.rows : [{updated: true}] |
|||
} |
|||
|
|||
async delete(sql: string) { |
|||
const response = await internalQuery(this.client, { sql }) |
|||
return response.rows.length ? response.rows : [{deleted: true}] |
|||
} |
|||
|
|||
async query(json: QueryJson) { |
|||
const operation = this._operation(json).toLowerCase() |
|||
const input = this._query(json) |
|||
const response = await internalQuery(this.client, input) |
|||
return response.rows.length ? response.rows : [{[operation]: true}] |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
schema: SCHEMA, |
|||
integration: PostgresIntegration, |
|||
} |
|||
} |
|||
@ -1,178 +0,0 @@ |
|||
const fetch = require("node-fetch") |
|||
const { FIELD_TYPES, QUERY_TYPES } = require("./Integration") |
|||
|
|||
const SCHEMA = { |
|||
docs: "https://github.com/node-fetch/node-fetch", |
|||
description: |
|||
"Representational state transfer (REST) is a de-facto standard for a software architecture for interactive applications that typically use multiple Web services. ", |
|||
friendlyName: "REST API", |
|||
datasource: { |
|||
url: { |
|||
type: FIELD_TYPES.STRING, |
|||
default: "localhost", |
|||
required: true, |
|||
}, |
|||
defaultHeaders: { |
|||
type: FIELD_TYPES.OBJECT, |
|||
required: false, |
|||
default: {}, |
|||
}, |
|||
}, |
|||
query: { |
|||
create: { |
|||
readable: true, |
|||
displayName: "POST", |
|||
type: QUERY_TYPES.FIELDS, |
|||
urlDisplay: true, |
|||
fields: { |
|||
path: { |
|||
type: FIELD_TYPES.STRING, |
|||
}, |
|||
queryString: { |
|||
type: FIELD_TYPES.STRING, |
|||
}, |
|||
headers: { |
|||
type: FIELD_TYPES.OBJECT, |
|||
}, |
|||
requestBody: { |
|||
type: FIELD_TYPES.JSON, |
|||
}, |
|||
}, |
|||
}, |
|||
read: { |
|||
displayName: "GET", |
|||
readable: true, |
|||
type: QUERY_TYPES.FIELDS, |
|||
urlDisplay: true, |
|||
fields: { |
|||
path: { |
|||
type: FIELD_TYPES.STRING, |
|||
}, |
|||
queryString: { |
|||
type: FIELD_TYPES.STRING, |
|||
}, |
|||
headers: { |
|||
type: FIELD_TYPES.OBJECT, |
|||
}, |
|||
}, |
|||
}, |
|||
update: { |
|||
displayName: "PUT", |
|||
readable: true, |
|||
type: QUERY_TYPES.FIELDS, |
|||
urlDisplay: true, |
|||
fields: { |
|||
path: { |
|||
type: FIELD_TYPES.STRING, |
|||
}, |
|||
queryString: { |
|||
type: FIELD_TYPES.STRING, |
|||
}, |
|||
headers: { |
|||
type: FIELD_TYPES.OBJECT, |
|||
}, |
|||
requestBody: { |
|||
type: FIELD_TYPES.JSON, |
|||
}, |
|||
}, |
|||
}, |
|||
delete: { |
|||
displayName: "DELETE", |
|||
type: QUERY_TYPES.FIELDS, |
|||
urlDisplay: true, |
|||
fields: { |
|||
path: { |
|||
type: FIELD_TYPES.STRING, |
|||
}, |
|||
queryString: { |
|||
type: FIELD_TYPES.STRING, |
|||
}, |
|||
headers: { |
|||
type: FIELD_TYPES.OBJECT, |
|||
}, |
|||
requestBody: { |
|||
type: FIELD_TYPES.JSON, |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
class RestIntegration { |
|||
constructor(config) { |
|||
this.config = config |
|||
} |
|||
|
|||
async parseResponse(response) { |
|||
switch (this.headers.Accept) { |
|||
case "application/json": |
|||
return await response.json() |
|||
case "text/html": |
|||
return await response.text() |
|||
default: |
|||
return await response.json() |
|||
} |
|||
} |
|||
|
|||
async create({ path = "", queryString = "", headers = {}, json }) { |
|||
this.headers = { |
|||
...this.config.defaultHeaders, |
|||
...headers, |
|||
} |
|||
|
|||
const response = await fetch(this.config.url + path + queryString, { |
|||
method: "POST", |
|||
headers: this.headers, |
|||
body: JSON.stringify(json), |
|||
}) |
|||
|
|||
return await this.parseResponse(response) |
|||
} |
|||
|
|||
async read({ path = "", queryString = "", headers = {} }) { |
|||
this.headers = { |
|||
...this.config.defaultHeaders, |
|||
...headers, |
|||
} |
|||
|
|||
const response = await fetch(this.config.url + path + queryString, { |
|||
headers: this.headers, |
|||
}) |
|||
|
|||
return await this.parseResponse(response) |
|||
} |
|||
|
|||
async update({ path = "", queryString = "", headers = {}, json }) { |
|||
this.headers = { |
|||
...this.config.defaultHeaders, |
|||
...headers, |
|||
} |
|||
|
|||
const response = await fetch(this.config.url + path + queryString, { |
|||
method: "POST", |
|||
headers: this.headers, |
|||
body: JSON.stringify(json), |
|||
}) |
|||
|
|||
return await this.parseResponse(response) |
|||
} |
|||
|
|||
async delete({ path = "", queryString = "", headers = {} }) { |
|||
this.headers = { |
|||
...this.config.defaultHeaders, |
|||
...headers, |
|||
} |
|||
|
|||
const response = await fetch(this.config.url + path + queryString, { |
|||
method: "DELETE", |
|||
headers: this.headers, |
|||
}) |
|||
|
|||
return await this.parseResponse(response) |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
schema: SCHEMA, |
|||
integration: RestIntegration, |
|||
} |
|||
@ -0,0 +1,193 @@ |
|||
import { Integration, DatasourceFieldTypes, QueryTypes } from "./base/definitions" |
|||
|
|||
module RestModule { |
|||
const fetch = require("node-fetch") |
|||
|
|||
interface RestConfig { |
|||
url: string, |
|||
defaultHeaders: { |
|||
[key: string]: any, |
|||
} |
|||
} |
|||
|
|||
const SCHEMA: Integration = { |
|||
docs: "https://github.com/node-fetch/node-fetch", |
|||
description: |
|||
"Representational state transfer (REST) is a de-facto standard for a software architecture for interactive applications that typically use multiple Web services. ", |
|||
friendlyName: "REST API", |
|||
datasource: { |
|||
url: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
default: "localhost", |
|||
required: true, |
|||
}, |
|||
defaultHeaders: { |
|||
type: DatasourceFieldTypes.OBJECT, |
|||
required: false, |
|||
default: {}, |
|||
}, |
|||
}, |
|||
query: { |
|||
create: { |
|||
readable: true, |
|||
displayName: "POST", |
|||
type: QueryTypes.FIELDS, |
|||
urlDisplay: true, |
|||
fields: { |
|||
path: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
}, |
|||
queryString: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
}, |
|||
headers: { |
|||
type: DatasourceFieldTypes.OBJECT, |
|||
}, |
|||
requestBody: { |
|||
type: DatasourceFieldTypes.JSON, |
|||
}, |
|||
}, |
|||
}, |
|||
read: { |
|||
displayName: "GET", |
|||
readable: true, |
|||
type: QueryTypes.FIELDS, |
|||
urlDisplay: true, |
|||
fields: { |
|||
path: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
}, |
|||
queryString: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
}, |
|||
headers: { |
|||
type: DatasourceFieldTypes.OBJECT, |
|||
}, |
|||
}, |
|||
}, |
|||
update: { |
|||
displayName: "PUT", |
|||
readable: true, |
|||
type: QueryTypes.FIELDS, |
|||
urlDisplay: true, |
|||
fields: { |
|||
path: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
}, |
|||
queryString: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
}, |
|||
headers: { |
|||
type: DatasourceFieldTypes.OBJECT, |
|||
}, |
|||
requestBody: { |
|||
type: DatasourceFieldTypes.JSON, |
|||
}, |
|||
}, |
|||
}, |
|||
delete: { |
|||
displayName: "DELETE", |
|||
type: QueryTypes.FIELDS, |
|||
urlDisplay: true, |
|||
fields: { |
|||
path: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
}, |
|||
queryString: { |
|||
type: DatasourceFieldTypes.STRING, |
|||
}, |
|||
headers: { |
|||
type: DatasourceFieldTypes.OBJECT, |
|||
}, |
|||
requestBody: { |
|||
type: DatasourceFieldTypes.JSON, |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
class RestIntegration { |
|||
private config: RestConfig |
|||
private headers: { |
|||
[key: string]: string |
|||
} = {} |
|||
|
|||
constructor(config: RestConfig) { |
|||
this.config = config |
|||
} |
|||
|
|||
async parseResponse(response: any) { |
|||
switch (this.headers.Accept) { |
|||
case "application/json": |
|||
return await response.json() |
|||
case "text/html": |
|||
return await response.text() |
|||
default: |
|||
return await response.json() |
|||
} |
|||
} |
|||
|
|||
async create({ path = "", queryString = "", headers = {}, json = {} }) { |
|||
this.headers = { |
|||
...this.config.defaultHeaders, |
|||
...headers, |
|||
} |
|||
|
|||
const response = await fetch(this.config.url + path + queryString, { |
|||
method: "POST", |
|||
headers: this.headers, |
|||
body: JSON.stringify(json), |
|||
}) |
|||
|
|||
return await this.parseResponse(response) |
|||
} |
|||
|
|||
async read({path = "", queryString = "", headers = {}}) { |
|||
this.headers = { |
|||
...this.config.defaultHeaders, |
|||
...headers, |
|||
} |
|||
|
|||
const response = await fetch(this.config.url + path + queryString, { |
|||
headers: this.headers, |
|||
}) |
|||
|
|||
return await this.parseResponse(response) |
|||
} |
|||
|
|||
async update({path = "", queryString = "", headers = {}, json = {}}) { |
|||
this.headers = { |
|||
...this.config.defaultHeaders, |
|||
...headers, |
|||
} |
|||
|
|||
const response = await fetch(this.config.url + path + queryString, { |
|||
method: "POST", |
|||
headers: this.headers, |
|||
body: JSON.stringify(json), |
|||
}) |
|||
|
|||
return await this.parseResponse(response) |
|||
} |
|||
|
|||
async delete({path = "", queryString = "", headers = {}}) { |
|||
this.headers = { |
|||
...this.config.defaultHeaders, |
|||
...headers, |
|||
} |
|||
|
|||
const response = await fetch(this.config.url + path + queryString, { |
|||
method: "DELETE", |
|||
headers: this.headers, |
|||
}) |
|||
|
|||
return await this.parseResponse(response) |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
schema: SCHEMA, |
|||
integration: RestIntegration, |
|||
} |
|||
} |
|||
@ -1,60 +0,0 @@ |
|||
const AWS = require("aws-sdk") |
|||
|
|||
const SCHEMA = { |
|||
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", |
|||
datasource: { |
|||
region: { |
|||
type: "string", |
|||
required: true, |
|||
default: "us-east-1", |
|||
}, |
|||
accessKeyId: { |
|||
type: "password", |
|||
required: true, |
|||
}, |
|||
secretAccessKey: { |
|||
type: "password", |
|||
required: true, |
|||
}, |
|||
}, |
|||
query: { |
|||
read: { |
|||
type: "fields", |
|||
fields: { |
|||
bucket: { |
|||
type: "string", |
|||
required: true, |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
class S3Integration { |
|||
constructor(config) { |
|||
this.config = config |
|||
this.connect() |
|||
this.client = new AWS.S3() |
|||
} |
|||
|
|||
async connect() { |
|||
AWS.config.update(this.config) |
|||
} |
|||
|
|||
async read(query) { |
|||
const response = await this.client |
|||
.listObjects({ |
|||
Bucket: query.bucket, |
|||
}) |
|||
.promise() |
|||
return response.Contents |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
schema: SCHEMA, |
|||
integration: S3Integration, |
|||
} |
|||
@ -0,0 +1,74 @@ |
|||
import { Integration, QueryTypes } from "./base/definitions" |
|||
|
|||
module S3Module { |
|||
const AWS = require("aws-sdk") |
|||
|
|||
interface S3Config { |
|||
region: string, |
|||
accessKeyId: string, |
|||
secretAccessKey: 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", |
|||
datasource: { |
|||
region: { |
|||
type: "string", |
|||
required: true, |
|||
default: "us-east-1", |
|||
}, |
|||
accessKeyId: { |
|||
type: "password", |
|||
required: true, |
|||
}, |
|||
secretAccessKey: { |
|||
type: "password", |
|||
required: true, |
|||
}, |
|||
}, |
|||
query: { |
|||
read: { |
|||
type: QueryTypes.FIELDS, |
|||
fields: { |
|||
bucket: { |
|||
type: "string", |
|||
required: true, |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
class S3Integration { |
|||
private readonly config: S3Config |
|||
private client: any |
|||
private connectionPromise: Promise<any> |
|||
|
|||
constructor(config: S3Config) { |
|||
this.config = config |
|||
this.connectionPromise = this.connect() |
|||
this.client = new AWS.S3() |
|||
} |
|||
|
|||
async connect() { |
|||
AWS.config.update(this.config) |
|||
} |
|||
|
|||
async read(query: { bucket: string }) { |
|||
const response = await this.client |
|||
.listObjects({ |
|||
Bucket: query.bucket, |
|||
}) |
|||
.promise() |
|||
return response.Contents |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
schema: SCHEMA, |
|||
integration: S3Integration, |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
{ |
|||
"compilerOptions": { |
|||
"target": "es5", |
|||
"module": "commonjs", |
|||
"lib": ["es6"], |
|||
"allowJs": true, |
|||
"outDir": "dist", |
|||
"strict": true, |
|||
"noImplicitAny": true, |
|||
"esModuleInterop": true, |
|||
"resolveJsonModule": true |
|||
}, |
|||
"include": [ |
|||
"./src/**/*" |
|||
], |
|||
"exclude": [ |
|||
"node_modules", |
|||
"**/*.json", |
|||
"**/*.spec.ts", |
|||
"**/*.spec.js" |
|||
] |
|||
} |
|||
File diff suppressed because it is too large
Loading…
Reference in new issue