Setup o2m and m2m relationships
This commit is contained in:
parent
d0fe2691ca
commit
c02900f0de
|
@ -1,2 +1,2 @@
|
||||||
nodejs 14.19.3
|
nodejs 14.19.3
|
||||||
python 3.11.1
|
python 3.10.0
|
|
@ -142,7 +142,11 @@ function cleanupConfig(config: RunConfig, table: Table): RunConfig {
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateIdForRow(row: Row | undefined, table: Table): string {
|
function generateIdForRow(
|
||||||
|
row: Row | undefined,
|
||||||
|
table: Table,
|
||||||
|
isLinked: boolean = false
|
||||||
|
): string {
|
||||||
const primary = table.primary
|
const primary = table.primary
|
||||||
if (!row || !primary) {
|
if (!row || !primary) {
|
||||||
return ""
|
return ""
|
||||||
|
@ -151,7 +155,10 @@ function generateIdForRow(row: Row | undefined, table: Table): string {
|
||||||
let idParts = []
|
let idParts = []
|
||||||
for (let field of primary) {
|
for (let field of primary) {
|
||||||
// need to handle table name + field or just field, depending on if relationships used
|
// need to handle table name + field or just field, depending on if relationships used
|
||||||
const fieldValue = row[`${table.name}.${field}`] || row[field]
|
let fieldValue = row[`${table.name}.${field}`]
|
||||||
|
if (!fieldValue && !isLinked) {
|
||||||
|
fieldValue = row[field]
|
||||||
|
}
|
||||||
if (fieldValue) {
|
if (fieldValue) {
|
||||||
idParts.push(fieldValue)
|
idParts.push(fieldValue)
|
||||||
}
|
}
|
||||||
|
@ -174,23 +181,20 @@ function getEndpoint(tableId: string | undefined, operation: string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function basicProcessing(row: Row, table: Table): Row {
|
function basicProcessing(row: Row, table: Table, isLinked: boolean): Row {
|
||||||
const thisRow: Row = {}
|
const thisRow: Row = {}
|
||||||
// filter the row down to what is actually the row (not joined)
|
// filter the row down to what is actually the row (not joined)
|
||||||
for (let field of Object.values(table.schema)) {
|
for (let field of Object.values(table.schema)) {
|
||||||
const fieldName = field.name
|
const fieldName = field.name
|
||||||
|
|
||||||
const pathValue = row[`${table.name}.${fieldName}`]
|
const pathValue = row[`${table.name}.${fieldName}`]
|
||||||
const value =
|
const value = pathValue != null || isLinked ? pathValue : row[fieldName]
|
||||||
pathValue != null || field.type === FieldTypes.LINK
|
|
||||||
? pathValue
|
|
||||||
: row[fieldName]
|
|
||||||
// all responses include "select col as table.col" so that overlaps are handled
|
// all responses include "select col as table.col" so that overlaps are handled
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
thisRow[fieldName] = value
|
thisRow[fieldName] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
thisRow._id = generateIdForRow(row, table)
|
thisRow._id = generateIdForRow(row, table, isLinked)
|
||||||
thisRow.tableId = table._id
|
thisRow.tableId = table._id
|
||||||
thisRow._rev = "rev"
|
thisRow._rev = "rev"
|
||||||
return processFormulas(table, thisRow)
|
return processFormulas(table, thisRow)
|
||||||
|
@ -385,15 +389,7 @@ export class ExternalRequest {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
let linked = basicProcessing(row, linkedTable, true)
|
||||||
relationship.from &&
|
|
||||||
row[fromColumn] === undefined &&
|
|
||||||
row[relationship.from] === null
|
|
||||||
) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
let linked = basicProcessing(row, linkedTable)
|
|
||||||
if (!linked._id) {
|
if (!linked._id) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -441,7 +437,7 @@ export class ExternalRequest {
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const thisRow = fixArrayTypes(basicProcessing(row, table), table)
|
const thisRow = fixArrayTypes(basicProcessing(row, table, false), table)
|
||||||
if (thisRow._id == null) {
|
if (thisRow._id == null) {
|
||||||
throw "Unable to generate row ID for SQL rows"
|
throw "Unable to generate row ID for SQL rows"
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,11 +23,19 @@ jest.setTimeout(30000)
|
||||||
|
|
||||||
jest.unmock("pg")
|
jest.unmock("pg")
|
||||||
|
|
||||||
|
interface RandomForeignKeyConfig {
|
||||||
|
createOne2Many?: boolean
|
||||||
|
createMany2One?: number
|
||||||
|
createMany2Many?: number
|
||||||
|
}
|
||||||
|
|
||||||
describe("row api - postgres", () => {
|
describe("row api - postgres", () => {
|
||||||
let makeRequest: MakeRequestResponse,
|
let makeRequest: MakeRequestResponse,
|
||||||
postgresDatasource: Datasource,
|
postgresDatasource: Datasource,
|
||||||
primaryPostgresTable: Table,
|
primaryPostgresTable: Table,
|
||||||
auxPostgresTable: Table
|
o2mInfo: { table: Table; fieldName: string },
|
||||||
|
m2oInfo: { table: Table; fieldName: string },
|
||||||
|
m2mInfo: { table: Table; fieldName: string }
|
||||||
|
|
||||||
let host: string
|
let host: string
|
||||||
let port: number
|
let port: number
|
||||||
|
@ -67,31 +75,46 @@ describe("row api - postgres", () => {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
auxPostgresTable = await config.createTable({
|
async function createAuxTable(prefix: string) {
|
||||||
name: generator.word({ length: 10 }),
|
return await config.createTable({
|
||||||
type: "external",
|
name: `${prefix}_${generator.word({ length: 6 })}`,
|
||||||
primary: ["id"],
|
type: "external",
|
||||||
schema: {
|
primary: ["id"],
|
||||||
id: {
|
schema: {
|
||||||
name: "id",
|
id: {
|
||||||
type: FieldType.AUTO,
|
name: "id",
|
||||||
constraints: {
|
type: FieldType.AUTO,
|
||||||
presence: true,
|
constraints: {
|
||||||
|
presence: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
name: "title",
|
||||||
|
type: FieldType.STRING,
|
||||||
|
constraints: {
|
||||||
|
presence: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
title: {
|
sourceId: postgresDatasource._id,
|
||||||
name: "title",
|
})
|
||||||
type: FieldType.STRING,
|
}
|
||||||
constraints: {
|
|
||||||
presence: true,
|
o2mInfo = {
|
||||||
},
|
table: await createAuxTable("o2m"),
|
||||||
},
|
fieldName: "oneToManyRelation",
|
||||||
},
|
}
|
||||||
sourceId: postgresDatasource._id,
|
m2oInfo = {
|
||||||
})
|
table: await createAuxTable("m2o"),
|
||||||
|
fieldName: "manyToOneRelation",
|
||||||
|
}
|
||||||
|
m2mInfo = {
|
||||||
|
table: await createAuxTable("m2m"),
|
||||||
|
fieldName: "manyToManyRelation",
|
||||||
|
}
|
||||||
|
|
||||||
primaryPostgresTable = await config.createTable({
|
primaryPostgresTable = await config.createTable({
|
||||||
name: generator.word({ length: 10 }),
|
name: `p_${generator.word({ length: 6 })}`,
|
||||||
type: "external",
|
type: "external",
|
||||||
primary: ["id"],
|
primary: ["id"],
|
||||||
schema: {
|
schema: {
|
||||||
|
@ -117,25 +140,45 @@ describe("row api - postgres", () => {
|
||||||
name: "value",
|
name: "value",
|
||||||
type: FieldType.NUMBER,
|
type: FieldType.NUMBER,
|
||||||
},
|
},
|
||||||
linkedField: {
|
oneToManyRelation: {
|
||||||
type: FieldType.LINK,
|
type: FieldType.LINK,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "array",
|
type: "array",
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
fieldName: "foreignField",
|
fieldName: o2mInfo.fieldName,
|
||||||
name: "linkedField",
|
name: "oneToManyRelation",
|
||||||
relationshipType: RelationshipTypes.ONE_TO_MANY,
|
relationshipType: RelationshipTypes.ONE_TO_MANY,
|
||||||
tableId: auxPostgresTable._id,
|
tableId: o2mInfo.table._id,
|
||||||
|
},
|
||||||
|
manyToOneRelation: {
|
||||||
|
type: FieldType.LINK,
|
||||||
|
constraints: {
|
||||||
|
type: "array",
|
||||||
|
presence: false,
|
||||||
|
},
|
||||||
|
fieldName: m2oInfo.fieldName,
|
||||||
|
name: "manyToOneRelation",
|
||||||
|
relationshipType: RelationshipTypes.MANY_TO_ONE,
|
||||||
|
tableId: m2oInfo.table._id,
|
||||||
|
},
|
||||||
|
manyToManyRelation: {
|
||||||
|
type: FieldType.LINK,
|
||||||
|
constraints: {
|
||||||
|
type: "array",
|
||||||
|
presence: false,
|
||||||
|
},
|
||||||
|
fieldName: m2mInfo.fieldName,
|
||||||
|
name: "manyToManyRelation",
|
||||||
|
relationshipType: RelationshipTypes.MANY_TO_MANY,
|
||||||
|
tableId: m2mInfo.table._id,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
sourceId: postgresDatasource._id,
|
sourceId: postgresDatasource._id,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(config.end)
|
||||||
await config.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
function generateRandomPrimaryRowData() {
|
function generateRandomPrimaryRowData() {
|
||||||
return {
|
return {
|
||||||
|
@ -153,19 +196,20 @@ describe("row api - postgres", () => {
|
||||||
|
|
||||||
async function createPrimaryRow(opts: {
|
async function createPrimaryRow(opts: {
|
||||||
rowData: PrimaryRowData
|
rowData: PrimaryRowData
|
||||||
createForeignRow?: boolean
|
createForeignRows?: RandomForeignKeyConfig
|
||||||
}) {
|
}) {
|
||||||
let { rowData } = opts
|
let { rowData } = opts
|
||||||
let foreignRow: Row | undefined
|
let foreignRow: Row | undefined
|
||||||
if (opts?.createForeignRow) {
|
|
||||||
|
if (opts?.createForeignRows?.createOne2Many) {
|
||||||
foreignRow = await config.createRow({
|
foreignRow = await config.createRow({
|
||||||
tableId: auxPostgresTable._id,
|
tableId: o2mInfo.table._id,
|
||||||
title: generator.name(),
|
title: generator.name(),
|
||||||
})
|
})
|
||||||
|
|
||||||
rowData = {
|
rowData = {
|
||||||
...rowData,
|
...rowData,
|
||||||
[`fk_${auxPostgresTable.name}_foreignField`]: foreignRow.id,
|
[`fk_${o2mInfo.table.name}_${o2mInfo.fieldName}`]: foreignRow.id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,9 +241,7 @@ describe("row api - postgres", () => {
|
||||||
|
|
||||||
async function populatePrimaryRows(
|
async function populatePrimaryRows(
|
||||||
count: number,
|
count: number,
|
||||||
opts?: {
|
opts?: RandomForeignKeyConfig
|
||||||
createForeignRow?: boolean
|
|
||||||
}
|
|
||||||
) {
|
) {
|
||||||
return await Promise.all(
|
return await Promise.all(
|
||||||
Array(count)
|
Array(count)
|
||||||
|
@ -210,7 +252,7 @@ describe("row api - postgres", () => {
|
||||||
rowData,
|
rowData,
|
||||||
...(await createPrimaryRow({
|
...(await createPrimaryRow({
|
||||||
rowData,
|
rowData,
|
||||||
createForeignRow: opts?.createForeignRow,
|
createForeignRows: opts,
|
||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -295,7 +337,7 @@ describe("row api - postgres", () => {
|
||||||
describe("given than a row exists", () => {
|
describe("given than a row exists", () => {
|
||||||
let row: Row
|
let row: Row
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
let rowResponse = _.sample(await populatePrimaryRows(10))!
|
let rowResponse = _.sample(await populatePrimaryRows(1))!
|
||||||
row = rowResponse.row
|
row = rowResponse.row
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -422,7 +464,7 @@ describe("row api - postgres", () => {
|
||||||
let foreignRow: Row
|
let foreignRow: Row
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
let [createdRow] = await populatePrimaryRows(1, {
|
let [createdRow] = await populatePrimaryRows(1, {
|
||||||
createForeignRow: true,
|
createOne2Many: true,
|
||||||
})
|
})
|
||||||
row = createdRow.row
|
row = createdRow.row
|
||||||
foreignRow = createdRow.foreignRow!
|
foreignRow = createdRow.foreignRow!
|
||||||
|
@ -437,16 +479,10 @@ describe("row api - postgres", () => {
|
||||||
...row,
|
...row,
|
||||||
_id: expect.any(String),
|
_id: expect.any(String),
|
||||||
_rev: expect.any(String),
|
_rev: expect.any(String),
|
||||||
|
[`fk_${o2mInfo.table.name}_${o2mInfo.fieldName}`]: foreignRow.id,
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(res.body.foreignField).toBeUndefined()
|
expect(res.body[o2mInfo.fieldName]).toBeUndefined()
|
||||||
|
|
||||||
expect(
|
|
||||||
res.body[`fk_${auxPostgresTable.name}_foreignField`]
|
|
||||||
).toBeDefined()
|
|
||||||
expect(res.body[`fk_${auxPostgresTable.name}_foreignField`]).toBe(
|
|
||||||
foreignRow.id
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -672,7 +708,7 @@ describe("row api - postgres", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const rowsInfo = await createPrimaryRow({
|
const rowsInfo = await createPrimaryRow({
|
||||||
rowData: generateRandomPrimaryRowData(),
|
rowData: generateRandomPrimaryRowData(),
|
||||||
createForeignRow: true,
|
createForeignRows: { createOne2Many: true },
|
||||||
})
|
})
|
||||||
|
|
||||||
row = rowsInfo.row
|
row = rowsInfo.row
|
||||||
|
@ -687,7 +723,7 @@ describe("row api - postgres", () => {
|
||||||
expect(foreignRow).toBeDefined()
|
expect(foreignRow).toBeDefined()
|
||||||
expect(res.body).toEqual({
|
expect(res.body).toEqual({
|
||||||
...row,
|
...row,
|
||||||
linkedField: [
|
[o2mInfo.fieldName]: [
|
||||||
{
|
{
|
||||||
...foreignRow,
|
...foreignRow,
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue