const CouchDB = require("../../db") const { generateDatasourceID, getDatasourceParams, getQueryParams, DocumentTypes, BudibaseInternalDB, getTableParams, } = require("../../db/utils") const { BuildSchemaErrors } = require("../../constants") const { integrations } = require("../../integrations") const { makeExternalQuery } = require("./row/utils") exports.fetch = async function (ctx) { const database = new CouchDB(ctx.appId) // Get internal tables const db = new CouchDB(ctx.appId) const internalTables = await db.allDocs( getTableParams(null, { include_docs: true, }) ) const internal = internalTables.rows.map(row => row.doc) const bbInternalDb = { ...BudibaseInternalDB, entities: internal, } // Get external datasources const datasources = ( await database.allDocs( getDatasourceParams(null, { include_docs: true, }) ) ).rows.map(row => row.doc) ctx.body = [bbInternalDb, ...datasources] } exports.buildSchemaFromDb = async function (ctx) { const db = new CouchDB(ctx.appId) const datasource = await db.get(ctx.params.datasourceId) const { tables, error } = await buildSchemaHelper(datasource) datasource.entities = tables const dbResp = await db.put(datasource) datasource._rev = dbResp.rev const response = { datasource } if (error) { response.error = error } ctx.body = response } exports.update = async function (ctx) { const db = new CouchDB(ctx.appId) const datasourceId = ctx.params.datasourceId let datasource = await db.get(datasourceId) datasource = { ...datasource, ...ctx.request.body } const response = await db.put(datasource) datasource._rev = response.rev // Drain connection pools when configuration is changed if (datasource.source) { const source = integrations[datasource.source] if (source && source.pool) { await source.pool.end() } } ctx.status = 200 ctx.message = "Datasource saved successfully." ctx.body = datasource } exports.save = async function (ctx) { const db = new CouchDB(ctx.appId) const plus = ctx.request.body.datasource.plus const fetchSchema = ctx.request.body.fetchSchema const datasource = { _id: generateDatasourceID({ plus }), type: plus ? DocumentTypes.DATASOURCE_PLUS : DocumentTypes.DATASOURCE, ...ctx.request.body.datasource, } let schemaError = null if (fetchSchema) { const { tables, error } = await buildSchemaHelper(datasource) schemaError = error datasource.entities = tables } const dbResp = await db.put(datasource) datasource._rev = dbResp.rev // Drain connection pools when configuration is changed if (datasource.source) { const source = integrations[datasource.source] if (source && source.pool) { await source.pool.end() } } const response = { datasource } if (schemaError) { response.error = schemaError } ctx.body = response } exports.destroy = async function (ctx) { const db = new CouchDB(ctx.appId) // Delete all queries for the datasource const rows = await db.allDocs(getQueryParams(ctx.params.datasourceId, null)) await db.bulkDocs(rows.rows.map(row => ({ ...row.doc, _deleted: true }))) // delete the datasource await db.remove(ctx.params.datasourceId, ctx.params.revId) ctx.message = `Datasource deleted.` ctx.status = 200 } exports.find = async function (ctx) { const database = new CouchDB(ctx.appId) ctx.body = await database.get(ctx.params.datasourceId) } // dynamic query functionality exports.query = async function (ctx) { const queryJson = ctx.request.body try { ctx.body = await makeExternalQuery(ctx.appId, queryJson) } catch (err) { ctx.throw(400, err) } } const buildSchemaHelper = async datasource => { const Connector = integrations[datasource.source] // Connect to the DB and build the schema const connector = new Connector(datasource.config) await connector.buildSchema(datasource._id, datasource.entities) datasource.entities = connector.tables const errors = connector.schemaErrors let error = null if (errors && Object.keys(errors).length > 0) { const noKeyTables = Object.entries(errors) .filter(entry => entry[1] === BuildSchemaErrors.NO_KEY) .map(([name]) => name) error = `No primary key constraint found for the following: ${noKeyTables.join( ", " )}` } return { tables: connector.tables, error } }