WIP - basic override of foreign keys.

This commit is contained in:
mike12345567 2021-06-23 19:05:32 +01:00
parent e25c6d4f3f
commit fd4403037d
4 changed files with 117 additions and 19 deletions

View File

@ -1,6 +1,6 @@
const { makeExternalQuery } = require("./utils") const { makeExternalQuery } = require("./utils")
const { DataSourceOperation, SortDirection } = require("../../../constants") const { DataSourceOperation, SortDirection, FieldTypes, RelationshipTypes } = require("../../../constants")
const { getExternalTable } = require("../table/utils") const { getAllExternalTables } = require("../table/utils")
const { const {
breakExternalTableId, breakExternalTableId,
generateRowIdField, generateRowIdField,
@ -35,17 +35,56 @@ function generateIdForRow(row, table) {
return generateRowIdField(idParts) return generateRowIdField(idParts)
} }
function outputProcessing(rows, table) { function updateRelationshipColumns(rows, row, relationships, allTables) {
const columns = {}
for (let relationship of relationships) {
const linkedTable = allTables[relationship.tableName]
if (!linkedTable) {
continue
}
const display = linkedTable.primaryDisplay
const related = {}
if (display && row[display]) {
related.primaryDisplay = row[display]
}
related._id = row[relationship.to]
columns[relationship.from] = related
}
for (let [column, related] of Object.entries(columns)) {
if (!Array.isArray(rows[row._id][column])) {
rows[row._id][column] = []
}
rows[row._id][column].push(related)
}
return rows
}
function outputProcessing(rows, table, relationships, allTables) {
// if no rows this is what is returned? Might be PG only // if no rows this is what is returned? Might be PG only
if (rows[0].read === true) { if (rows[0].read === true) {
return [] return []
} }
let finalRows = {}
for (let row of rows) { for (let row of rows) {
row._id = generateIdForRow(row, table) row._id = generateIdForRow(row, table)
row.tableId = table._id // this is a relationship of some sort
row._rev = "rev" if (finalRows[row._id]) {
finalRows = updateRelationshipColumns(finalRows, row, relationships, allTables)
continue
} }
return rows const thisRow = {}
// filter the row down to what is actually the row (not joined)
for (let fieldName of Object.keys(table.schema)) {
thisRow[fieldName] = row[fieldName]
}
thisRow._id = row._id
thisRow.tableId = table._id
thisRow._rev = "rev"
finalRows[thisRow._id] = thisRow
// do this at end once its been added to the final rows
finalRows = updateRelationshipColumns(finalRows, row, relationships, allTables)
}
return Object.values(finalRows)
} }
function buildFilters(id, filters, table) { function buildFilters(id, filters, table) {
@ -83,6 +122,26 @@ function buildFilters(id, filters, table) {
} }
} }
function buildRelationships(table) {
const relationships = []
for (let [fieldName, field] of Object.entries(table.schema)) {
if (field.type !== FieldTypes.LINK) {
continue
}
// TODO: through field
if (field.relationshipType === RelationshipTypes.MANY_TO_MANY) {
continue
}
const broken = breakExternalTableId(field.tableId)
relationships.push({
from: fieldName,
to: field.fieldName,
tableName: broken.tableName,
})
}
return relationships
}
async function handleRequest( async function handleRequest(
appId, appId,
operation, operation,
@ -90,12 +149,14 @@ async function handleRequest(
{ id, row, filters, sort, paginate } = {} { id, row, filters, sort, paginate } = {}
) { ) {
let { datasourceId, tableName } = breakExternalTableId(tableId) let { datasourceId, tableName } = breakExternalTableId(tableId)
const table = await getExternalTable(appId, datasourceId, tableName) const tables = await getAllExternalTables(appId, datasourceId)
const table = tables[tableName]
if (!table) { if (!table) {
throw `Unable to process query, table "${tableName}" not defined.` throw `Unable to process query, table "${tableName}" not defined.`
} }
// clean up row on ingress using schema // clean up row on ingress using schema
filters = buildFilters(id, filters, table) filters = buildFilters(id, filters, table)
const relationships = buildRelationships(table)
row = inputProcessing(row, table) row = inputProcessing(row, table)
if ( if (
operation === DataSourceOperation.DELETE && operation === DataSourceOperation.DELETE &&
@ -116,6 +177,7 @@ async function handleRequest(
filters, filters,
sort, sort,
paginate, paginate,
relationships,
body: row, body: row,
// pass an id filter into extra, purely for mysql/returning // pass an id filter into extra, purely for mysql/returning
extra: { extra: {
@ -126,9 +188,9 @@ async function handleRequest(
const response = await makeExternalQuery(appId, json) const response = await makeExternalQuery(appId, json)
// we searched for rows in someway // we searched for rows in someway
if (operation === DataSourceOperation.READ && Array.isArray(response)) { if (operation === DataSourceOperation.READ && Array.isArray(response)) {
return outputProcessing(response, table) return outputProcessing(response, table, relationships, tables)
} else { } else {
row = outputProcessing(response, table)[0] row = outputProcessing(response, table, relationships, tables)[0]
return { row, table } return { row, table }
} }
} }
@ -270,7 +332,4 @@ exports.validate = async () => {
return { valid: true } return { valid: true }
} }
exports.fetchEnrichedRow = async () => { exports.fetchEnrichedRow = async () => {}
// TODO: How does this work
throw "Not Implemented"
}

View File

@ -204,15 +204,18 @@ class TableSaveFunctions {
} }
} }
exports.getExternalTable = async (appId, datasourceId, tableName) => { exports.getAllExternalTables = async (appId, datasourceId) => {
const db = new CouchDB(appId) const db = new CouchDB(appId)
const datasource = await db.get(datasourceId) const datasource = await db.get(datasourceId)
if (!datasource || !datasource.entities) { if (!datasource || !datasource.entities) {
throw "Datasource is not configured fully." throw "Datasource is not configured fully."
} }
return Object.values(datasource.entities).find( return datasource.entities
entity => entity.name === tableName }
)
exports.getExternalTable = async (appId, datasourceId, tableName) => {
const entities = await exports.getAllExternalTables(appId, datasourceId)
return entities[tableName]
} }
exports.TableSaveFunctions = TableSaveFunctions exports.TableSaveFunctions = TableSaveFunctions

View File

@ -55,6 +55,25 @@ function addFilters(query, filters) {
return query return query
} }
function addRelationships(query, fromTable, relationships) {
if (!relationships) {
return query
}
for (let relationship of relationships) {
const from = `${fromTable}.${relationship.from}`
const to = `${relationship.tableName}.${relationship.to}`
if (!relationship.through) {
query = query.innerJoin(relationship.tableName, from, to)
} else {
const through = relationship
query = query
.innerJoin(through.tableName, from, through.from)
.innerJoin(relationship.tableName, to, through.to)
}
}
return query
}
function buildCreate(knex, json, opts) { function buildCreate(knex, json, opts) {
const { endpoint, body } = json const { endpoint, body } = json
let query = knex(endpoint.entityId) let query = knex(endpoint.entityId)
@ -67,8 +86,9 @@ function buildCreate(knex, json, opts) {
} }
function buildRead(knex, json, limit) { function buildRead(knex, json, limit) {
let { endpoint, resource, filters, sort, paginate } = json let { endpoint, resource, filters, sort, paginate, relationships } = json
let query = knex(endpoint.entityId) const tableName = endpoint.entityId
let query = knex(tableName)
// select all if not specified // select all if not specified
if (!resource) { if (!resource) {
resource = { fields: [] } resource = { fields: [] }
@ -81,6 +101,8 @@ function buildRead(knex, json, limit) {
} }
// handle where // handle where
query = addFilters(query, filters) query = addFilters(query, filters)
// handle join
query = addRelationships(query, tableName, relationships)
// handle sorting // handle sorting
if (sort) { if (sort) {
for (let [key, value] of Object.entries(sort)) { for (let [key, value] of Object.entries(sort)) {

View File

@ -153,6 +153,20 @@ class PostgresIntegration extends Sql {
name: columnName, name: columnName,
type: convertType(column.data_type, TYPE_MAP), type: convertType(column.data_type, TYPE_MAP),
} }
// // TODO: hack for testing
// if (tableName === "persons") {
// tables[tableName].primaryDisplay = "firstname"
// }
// if (columnName.toLowerCase() === "personid" && tableName === "tasks") {
// tables[tableName].schema[columnName] = {
// name: columnName,
// type: "link",
// tableId: buildExternalTableId(datasourceId, "persons"),
// relationshipType: "one-to-many",
// fieldName: "personid",
// }
// }
} }
this.tables = tables this.tables = tables
} }