mirror of https://github.com/Budibase/budibase.git
committed by
GitHub
78 changed files with 3960 additions and 3135 deletions
File diff suppressed because it is too large
@ -1,4 +1,4 @@ |
|||
const elastic = {} |
|||
const elastic: any = {} |
|||
|
|||
elastic.Client = function () { |
|||
this.index = jest.fn().mockResolvedValue({ body: [] }) |
|||
@ -1,18 +0,0 @@ |
|||
class Email { |
|||
constructor() { |
|||
this.apiKey = null |
|||
} |
|||
|
|||
setApiKey(apiKey) { |
|||
this.apiKey = apiKey |
|||
} |
|||
|
|||
async send(msg) { |
|||
if (msg.to === "invalid@test.com") { |
|||
throw "Invalid" |
|||
} |
|||
return msg |
|||
} |
|||
} |
|||
|
|||
module.exports = new Email() |
|||
@ -0,0 +1,22 @@ |
|||
module SendgridMock { |
|||
class Email { |
|||
constructor() { |
|||
// @ts-ignore
|
|||
this.apiKey = null |
|||
} |
|||
|
|||
setApiKey(apiKey: any) { |
|||
// @ts-ignore
|
|||
this.apiKey = apiKey |
|||
} |
|||
|
|||
async send(msg: any) { |
|||
if (msg.to === "invalid@test.com") { |
|||
throw "Invalid" |
|||
} |
|||
return msg |
|||
} |
|||
} |
|||
|
|||
module.exports = new Email() |
|||
} |
|||
@ -1,5 +0,0 @@ |
|||
function Airtable() { |
|||
this.base = jest.fn() |
|||
} |
|||
|
|||
module.exports = Airtable |
|||
@ -0,0 +1,8 @@ |
|||
module AirtableMock { |
|||
function Airtable() { |
|||
// @ts-ignore
|
|||
this.base = jest.fn() |
|||
} |
|||
|
|||
module.exports = Airtable |
|||
} |
|||
@ -1,21 +0,0 @@ |
|||
const arangodb = {} |
|||
|
|||
arangodb.Database = function () { |
|||
this.query = jest.fn(() => ({ |
|||
all: jest.fn(), |
|||
})) |
|||
this.collection = jest.fn(() => "collection") |
|||
this.close = jest.fn() |
|||
} |
|||
|
|||
arangodb.aql = (strings, ...args) => { |
|||
let str = strings.join("{}") |
|||
|
|||
for (let arg of args) { |
|||
str = str.replace("{}", arg) |
|||
} |
|||
|
|||
return str |
|||
} |
|||
|
|||
module.exports = arangodb |
|||
@ -0,0 +1,24 @@ |
|||
module ArangoMock { |
|||
const arangodb: any = {} |
|||
|
|||
arangodb.Database = function () { |
|||
this.query = jest.fn(() => ({ |
|||
all: jest.fn(), |
|||
})) |
|||
this.collection = jest.fn(() => "collection") |
|||
this.close = jest.fn() |
|||
} |
|||
|
|||
// @ts-ignore
|
|||
arangodb.aql = (strings, ...args) => { |
|||
let str = strings.join("{}") |
|||
|
|||
for (let arg of args) { |
|||
str = str.replace("{}", arg) |
|||
} |
|||
|
|||
return str |
|||
} |
|||
|
|||
module.exports = arangodb |
|||
} |
|||
@ -1,38 +0,0 @@ |
|||
const aws = {} |
|||
|
|||
const response = body => () => ({ promise: () => body }) |
|||
|
|||
function DocumentClient() { |
|||
this.put = jest.fn(response({})) |
|||
this.query = jest.fn( |
|||
response({ |
|||
Items: [], |
|||
}) |
|||
) |
|||
this.scan = jest.fn( |
|||
response({ |
|||
Items: [ |
|||
{ |
|||
Name: "test", |
|||
}, |
|||
], |
|||
}) |
|||
) |
|||
this.get = jest.fn(response({})) |
|||
this.update = jest.fn(response({})) |
|||
this.delete = jest.fn(response({})) |
|||
} |
|||
|
|||
function S3() { |
|||
this.listObjects = jest.fn( |
|||
response({ |
|||
Contents: {}, |
|||
}) |
|||
) |
|||
} |
|||
|
|||
aws.DynamoDB = { DocumentClient } |
|||
aws.S3 = S3 |
|||
aws.config = { update: jest.fn() } |
|||
|
|||
module.exports = aws |
|||
@ -0,0 +1,47 @@ |
|||
module AwsMock { |
|||
const aws: any = {} |
|||
|
|||
const response = (body: any) => () => ({promise: () => body}) |
|||
|
|||
function DocumentClient() { |
|||
// @ts-ignore
|
|||
this.put = jest.fn(response({})) |
|||
// @ts-ignore
|
|||
this.query = jest.fn( |
|||
response({ |
|||
Items: [], |
|||
}) |
|||
) |
|||
// @ts-ignore
|
|||
this.scan = jest.fn( |
|||
response({ |
|||
Items: [ |
|||
{ |
|||
Name: "test", |
|||
}, |
|||
], |
|||
}) |
|||
) |
|||
// @ts-ignore
|
|||
this.get = jest.fn(response({})) |
|||
// @ts-ignore
|
|||
this.update = jest.fn(response({})) |
|||
// @ts-ignore
|
|||
this.delete = jest.fn(response({})) |
|||
} |
|||
|
|||
function S3() { |
|||
// @ts-ignore
|
|||
this.listObjects = jest.fn( |
|||
response({ |
|||
Contents: {}, |
|||
}) |
|||
) |
|||
} |
|||
|
|||
aws.DynamoDB = {DocumentClient} |
|||
aws.S3 = S3 |
|||
aws.config = {update: jest.fn()} |
|||
|
|||
module.exports = aws |
|||
} |
|||
@ -1,19 +0,0 @@ |
|||
const mongodb = {} |
|||
|
|||
mongodb.MongoClient = function () { |
|||
this.connect = jest.fn() |
|||
this.close = jest.fn() |
|||
this.insertOne = jest.fn() |
|||
this.find = jest.fn(() => ({ toArray: () => [] })) |
|||
|
|||
this.collection = jest.fn(() => ({ |
|||
insertOne: this.insertOne, |
|||
find: this.find, |
|||
})) |
|||
|
|||
this.db = () => ({ |
|||
collection: this.collection, |
|||
}) |
|||
} |
|||
|
|||
module.exports = mongodb |
|||
@ -0,0 +1,21 @@ |
|||
module MongoMock { |
|||
const mongodb: any = {} |
|||
|
|||
mongodb.MongoClient = function () { |
|||
this.connect = jest.fn() |
|||
this.close = jest.fn() |
|||
this.insertOne = jest.fn() |
|||
this.find = jest.fn(() => ({toArray: () => []})) |
|||
|
|||
this.collection = jest.fn(() => ({ |
|||
insertOne: this.insertOne, |
|||
find: this.find, |
|||
})) |
|||
|
|||
this.db = () => ({ |
|||
collection: this.collection, |
|||
}) |
|||
} |
|||
|
|||
module.exports = mongodb |
|||
} |
|||
@ -1,22 +0,0 @@ |
|||
const mssql = {} |
|||
|
|||
mssql.query = jest.fn(() => ({ |
|||
recordset: [ |
|||
{ |
|||
a: "string", |
|||
b: 1, |
|||
}, |
|||
], |
|||
})) |
|||
|
|||
// mssql.connect = jest.fn(() => ({ recordset: [] }))
|
|||
|
|||
mssql.ConnectionPool = jest.fn(() => ({ |
|||
connect: jest.fn(() => ({ |
|||
request: jest.fn(() => ({ |
|||
query: jest.fn(() => ({})), |
|||
})), |
|||
})), |
|||
})) |
|||
|
|||
module.exports = mssql |
|||
@ -0,0 +1,24 @@ |
|||
module MsSqlMock { |
|||
const mssql: any = {} |
|||
|
|||
mssql.query = jest.fn(() => ({ |
|||
recordset: [ |
|||
{ |
|||
a: "string", |
|||
b: 1, |
|||
}, |
|||
], |
|||
})) |
|||
|
|||
// mssql.connect = jest.fn(() => ({ recordset: [] }))
|
|||
|
|||
mssql.ConnectionPool = jest.fn(() => ({ |
|||
connect: jest.fn(() => ({ |
|||
request: jest.fn(() => ({ |
|||
query: jest.fn(() => ({})), |
|||
})), |
|||
})), |
|||
})) |
|||
|
|||
module.exports = mssql |
|||
} |
|||
@ -1,12 +0,0 @@ |
|||
const mysql = {} |
|||
|
|||
const client = { |
|||
connect: jest.fn(), |
|||
query: jest.fn((query, bindings, fn) => { |
|||
fn(null, []) |
|||
}), |
|||
} |
|||
|
|||
mysql.createConnection = jest.fn(() => client) |
|||
|
|||
module.exports = mysql |
|||
@ -0,0 +1,14 @@ |
|||
module MySQLMock { |
|||
const mysql: any = {} |
|||
|
|||
const client = { |
|||
connect: jest.fn(), |
|||
query: jest.fn((query, bindings, fn) => { |
|||
fn(null, []) |
|||
}), |
|||
} |
|||
|
|||
mysql.createConnection = jest.fn(() => client) |
|||
|
|||
module.exports = mysql |
|||
} |
|||
@ -1,58 +0,0 @@ |
|||
const fetch = jest.requireActual("node-fetch") |
|||
|
|||
module.exports = async (url, opts) => { |
|||
function json(body, status = 200) { |
|||
return { |
|||
status, |
|||
headers: { |
|||
get: () => { |
|||
return ["application/json"] |
|||
}, |
|||
}, |
|||
json: async () => { |
|||
return body |
|||
}, |
|||
} |
|||
} |
|||
|
|||
if (url.includes("/api/admin")) { |
|||
return json({ |
|||
email: "test@test.com", |
|||
_id: "us_test@test.com", |
|||
status: "active", |
|||
}) |
|||
} |
|||
// mocked data based on url
|
|||
else if (url.includes("api/apps")) { |
|||
return json({ |
|||
app1: { |
|||
url: "/app1", |
|||
}, |
|||
}) |
|||
} else if (url.includes("test.com")) { |
|||
return json({ |
|||
body: opts.body, |
|||
url, |
|||
method: opts.method, |
|||
}) |
|||
} else if (url.includes("invalid.com")) { |
|||
return json( |
|||
{ |
|||
invalid: true, |
|||
}, |
|||
404 |
|||
) |
|||
} else if (url.includes("_search")) { |
|||
return json({ |
|||
rows: [ |
|||
{ |
|||
doc: { |
|||
_id: "test", |
|||
}, |
|||
}, |
|||
], |
|||
bookmark: "test", |
|||
}) |
|||
} |
|||
return fetch(url, opts) |
|||
} |
|||
@ -0,0 +1,60 @@ |
|||
module FetchMock { |
|||
const fetch = jest.requireActual("node-fetch") |
|||
|
|||
module.exports = async (url: any, opts: any) => { |
|||
function json(body: any, status = 200) { |
|||
return { |
|||
status, |
|||
headers: { |
|||
get: () => { |
|||
return ["application/json"] |
|||
}, |
|||
}, |
|||
json: async () => { |
|||
return body |
|||
}, |
|||
} |
|||
} |
|||
|
|||
if (url.includes("/api/admin")) { |
|||
return json({ |
|||
email: "test@test.com", |
|||
_id: "us_test@test.com", |
|||
status: "active", |
|||
}) |
|||
} |
|||
// mocked data based on url
|
|||
else if (url.includes("api/apps")) { |
|||
return json({ |
|||
app1: { |
|||
url: "/app1", |
|||
}, |
|||
}) |
|||
} else if (url.includes("test.com")) { |
|||
return json({ |
|||
body: opts.body, |
|||
url, |
|||
method: opts.method, |
|||
}) |
|||
} else if (url.includes("invalid.com")) { |
|||
return json( |
|||
{ |
|||
invalid: true, |
|||
}, |
|||
404 |
|||
) |
|||
} else if (url.includes("_search")) { |
|||
return json({ |
|||
rows: [ |
|||
{ |
|||
doc: { |
|||
_id: "test", |
|||
}, |
|||
}, |
|||
], |
|||
bookmark: "test", |
|||
}) |
|||
} |
|||
return fetch(url, opts) |
|||
} |
|||
} |
|||
@ -1,29 +0,0 @@ |
|||
const pg = {} |
|||
|
|||
const query = jest.fn(() => ({ |
|||
rows: [ |
|||
{ |
|||
a: "string", |
|||
b: 1, |
|||
}, |
|||
], |
|||
})) |
|||
|
|||
// constructor
|
|||
function Client() {} |
|||
|
|||
Client.prototype.query = query |
|||
Client.prototype.connect = jest.fn() |
|||
Client.prototype.release = jest.fn() |
|||
|
|||
function Pool() {} |
|||
Pool.prototype.query = query |
|||
Pool.prototype.connect = jest.fn(() => { |
|||
return new Client() |
|||
}) |
|||
|
|||
pg.Client = Client |
|||
pg.Pool = Pool |
|||
pg.queryMock = query |
|||
|
|||
module.exports = pg |
|||
@ -0,0 +1,35 @@ |
|||
module PgMock { |
|||
const pg: any = {} |
|||
|
|||
const query = jest.fn(() => ({ |
|||
rows: [ |
|||
{ |
|||
a: "string", |
|||
b: 1, |
|||
}, |
|||
], |
|||
})) |
|||
|
|||
// constructor
|
|||
function Client() { |
|||
} |
|||
|
|||
Client.prototype.query = query |
|||
Client.prototype.connect = jest.fn() |
|||
Client.prototype.release = jest.fn() |
|||
|
|||
function Pool() { |
|||
} |
|||
|
|||
Pool.prototype.query = query |
|||
Pool.prototype.connect = jest.fn(() => { |
|||
// @ts-ignore
|
|||
return new Client() |
|||
}) |
|||
|
|||
pg.Client = Client |
|||
pg.Pool = Pool |
|||
pg.queryMock = query |
|||
|
|||
module.exports = pg |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
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,143 @@ |
|||
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,106 @@ |
|||
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,109 @@ |
|||
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 SqlQuery { |
|||
sql: string |
|||
bindings?: { |
|||
[key: string]: any |
|||
} |
|||
} |
|||
|
|||
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,107 @@ |
|||
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,211 @@ |
|||
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,153 @@ |
|||
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,144 @@ |
|||
import { |
|||
Integration, |
|||
DatasourceFieldTypes, |
|||
QueryTypes, |
|||
QueryJson, |
|||
SqlQuery, |
|||
} from "./base/definitions" |
|||
import { getSqlQuery } from "./utils" |
|||
|
|||
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: SqlQuery) { |
|||
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: SqlQuery | string) { |
|||
await this.connect() |
|||
const response = await internalQuery(this.client, getSqlQuery(query)) |
|||
return response.recordset |
|||
} |
|||
|
|||
async create(query: SqlQuery | string) { |
|||
await this.connect() |
|||
const response = await internalQuery(this.client, getSqlQuery(query)) |
|||
return response.recordset || [{ created: true }] |
|||
} |
|||
|
|||
async update(query: SqlQuery | string) { |
|||
await this.connect() |
|||
const response = await internalQuery(this.client, getSqlQuery(query)) |
|||
return response.recordset || [{ updated: true }] |
|||
} |
|||
|
|||
async delete(query: SqlQuery | string) { |
|||
await this.connect() |
|||
const response = await internalQuery(this.client, getSqlQuery(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,92 @@ |
|||
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,258 @@ |
|||
import { |
|||
Integration, |
|||
DatasourceFieldTypes, |
|||
QueryTypes, |
|||
Operation, |
|||
QueryJson, |
|||
SqlQuery, |
|||
} from "./base/definitions" |
|||
import { getSqlQuery } from "./utils" |
|||
|
|||
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: SqlQuery, |
|||
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: SqlQuery | string) { |
|||
const results = await internalQuery(this.client, getSqlQuery(query)) |
|||
return results.length ? results : [{ created: true }] |
|||
} |
|||
|
|||
read(query: SqlQuery | string) { |
|||
return internalQuery(this.client, getSqlQuery(query)) |
|||
} |
|||
|
|||
async update(query: SqlQuery | string) { |
|||
const results = await internalQuery(this.client, getSqlQuery(query)) |
|||
return results.length ? results : [{ updated: true }] |
|||
} |
|||
|
|||
async delete(query: SqlQuery | string) { |
|||
const results = await internalQuery(this.client, getSqlQuery(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,213 @@ |
|||
import { |
|||
Integration, |
|||
DatasourceFieldTypes, |
|||
QueryTypes, |
|||
QueryJson, |
|||
SqlQuery, |
|||
} from "./base/definitions" |
|||
import { Table } from "../constants/definitions" |
|||
import { getSqlQuery } from "./utils" |
|||
|
|||
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: SqlQuery) { |
|||
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(query: SqlQuery | string) { |
|||
const response = await internalQuery(this.client, getSqlQuery(query)) |
|||
return response.rows.length ? response.rows : [{ created: true }] |
|||
} |
|||
|
|||
async read(query: SqlQuery | string) { |
|||
const response = await internalQuery(this.client, getSqlQuery(query)) |
|||
return response.rows |
|||
} |
|||
|
|||
async update(query: SqlQuery | string) { |
|||
const response = await internalQuery(this.client, getSqlQuery(query)) |
|||
return response.rows.length ? response.rows : [{ updated: true }] |
|||
} |
|||
|
|||
async delete(query: SqlQuery | string) { |
|||
const response = await internalQuery(this.client, getSqlQuery(query)) |
|||
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,197 @@ |
|||
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