Handle relationships properly
This commit is contained in:
parent
d8462ba961
commit
bbf7142bd7
|
@ -56,10 +56,10 @@ export const getQueryableFields = async (
|
||||||
fields: string[],
|
fields: string[],
|
||||||
table: Table
|
table: Table
|
||||||
): Promise<string[]> => {
|
): Promise<string[]> => {
|
||||||
const handledTables = new Set<string>([table._id!])
|
|
||||||
const extractTableFields = async (
|
const extractTableFields = async (
|
||||||
table: Table,
|
table: Table,
|
||||||
allowedFields: string[]
|
allowedFields: string[],
|
||||||
|
fromTables: string[]
|
||||||
): Promise<string[]> => {
|
): Promise<string[]> => {
|
||||||
const result = []
|
const result = []
|
||||||
for (const field of Object.keys(table.schema).filter(
|
for (const field of Object.keys(table.schema).filter(
|
||||||
|
@ -67,15 +67,15 @@ export const getQueryableFields = async (
|
||||||
)) {
|
)) {
|
||||||
const subSchema = table.schema[field]
|
const subSchema = table.schema[field]
|
||||||
if (subSchema.type === FieldType.LINK) {
|
if (subSchema.type === FieldType.LINK) {
|
||||||
if (handledTables.has(`${table._id}_${subSchema.tableId}`)) {
|
if (fromTables.includes(subSchema.tableId)) {
|
||||||
// avoid circular loops
|
// avoid circular loops
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
handledTables.add(`${table._id}_${subSchema.tableId}`)
|
|
||||||
const relatedTable = await sdk.tables.getTable(subSchema.tableId)
|
const relatedTable = await sdk.tables.getTable(subSchema.tableId)
|
||||||
const relatedFields = await extractTableFields(
|
const relatedFields = await extractTableFields(
|
||||||
relatedTable,
|
relatedTable,
|
||||||
Object.keys(relatedTable.schema)
|
Object.keys(relatedTable.schema),
|
||||||
|
[...fromTables, subSchema.tableId]
|
||||||
)
|
)
|
||||||
|
|
||||||
result.push(
|
result.push(
|
||||||
|
@ -96,7 +96,7 @@ export const getQueryableFields = async (
|
||||||
"_id", // Querying by _id is always allowed, even if it's never part of the schema
|
"_id", // Querying by _id is always allowed, even if it's never part of the schema
|
||||||
]
|
]
|
||||||
|
|
||||||
result.push(...(await extractTableFields(table, fields)))
|
result.push(...(await extractTableFields(table, fields, [table._id!])))
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,26 +195,26 @@ describe("query utils", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it("returns table schema fields and _id", async () => {
|
it("returns table schema fields and _id", async () => {
|
||||||
const table: Table = {
|
const table: Table = await config.api.table.save({
|
||||||
...structures.basicTable(),
|
...structures.basicTable(),
|
||||||
schema: {
|
schema: {
|
||||||
name: { name: "name", type: FieldType.STRING },
|
name: { name: "name", type: FieldType.STRING },
|
||||||
age: { name: "age", type: FieldType.NUMBER },
|
age: { name: "age", type: FieldType.NUMBER },
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
|
|
||||||
const result = await getQueryableFields(Object.keys(table.schema), table)
|
const result = await getQueryableFields(Object.keys(table.schema), table)
|
||||||
expect(result).toEqual(["_id", "name", "age"])
|
expect(result).toEqual(["_id", "name", "age"])
|
||||||
})
|
})
|
||||||
|
|
||||||
it("excludes hidden fields", async () => {
|
it("excludes hidden fields", async () => {
|
||||||
const table: Table = {
|
const table: Table = await config.api.table.save({
|
||||||
...structures.basicTable(),
|
...structures.basicTable(),
|
||||||
schema: {
|
schema: {
|
||||||
name: { name: "name", type: FieldType.STRING },
|
name: { name: "name", type: FieldType.STRING },
|
||||||
age: { name: "age", type: FieldType.NUMBER, visible: false },
|
age: { name: "age", type: FieldType.NUMBER, visible: false },
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
|
|
||||||
const result = await getQueryableFields(Object.keys(table.schema), table)
|
const result = await getQueryableFields(Object.keys(table.schema), table)
|
||||||
expect(result).toEqual(["_id", "name"])
|
expect(result).toEqual(["_id", "name"])
|
||||||
|
@ -230,7 +230,7 @@ describe("query utils", () => {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const table: Table = {
|
const table: Table = await config.api.table.save({
|
||||||
...structures.basicTable(),
|
...structures.basicTable(),
|
||||||
schema: {
|
schema: {
|
||||||
name: { name: "name", type: FieldType.STRING },
|
name: { name: "name", type: FieldType.STRING },
|
||||||
|
@ -242,7 +242,7 @@ describe("query utils", () => {
|
||||||
fieldName: "table",
|
fieldName: "table",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
|
|
||||||
const result = await config.doInContext(config.appId, () => {
|
const result = await config.doInContext(config.appId, () => {
|
||||||
return getQueryableFields(Object.keys(table.schema), table)
|
return getQueryableFields(Object.keys(table.schema), table)
|
||||||
|
@ -267,7 +267,7 @@ describe("query utils", () => {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const table: Table = {
|
const table: Table = await config.api.table.save({
|
||||||
...structures.basicTable(),
|
...structures.basicTable(),
|
||||||
schema: {
|
schema: {
|
||||||
name: { name: "name", type: FieldType.STRING },
|
name: { name: "name", type: FieldType.STRING },
|
||||||
|
@ -279,7 +279,7 @@ describe("query utils", () => {
|
||||||
fieldName: "table",
|
fieldName: "table",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
|
|
||||||
const result = await config.doInContext(config.appId, () => {
|
const result = await config.doInContext(config.appId, () => {
|
||||||
return getQueryableFields(Object.keys(table.schema), table)
|
return getQueryableFields(Object.keys(table.schema), table)
|
||||||
|
@ -297,7 +297,7 @@ describe("query utils", () => {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const table: Table = {
|
const table: Table = await config.api.table.save({
|
||||||
...structures.basicTable(),
|
...structures.basicTable(),
|
||||||
schema: {
|
schema: {
|
||||||
name: { name: "name", type: FieldType.STRING },
|
name: { name: "name", type: FieldType.STRING },
|
||||||
|
@ -310,7 +310,7 @@ describe("query utils", () => {
|
||||||
visible: false,
|
visible: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
|
|
||||||
const result = await config.doInContext(config.appId, () => {
|
const result = await config.doInContext(config.appId, () => {
|
||||||
return getQueryableFields(Object.keys(table.schema), table)
|
return getQueryableFields(Object.keys(table.schema), table)
|
||||||
|
@ -318,61 +318,82 @@ describe("query utils", () => {
|
||||||
expect(result).toEqual(["_id", "name"])
|
expect(result).toEqual(["_id", "name"])
|
||||||
})
|
})
|
||||||
|
|
||||||
it("includes nested relationship fields", async () => {
|
describe("nested relationship", () => {
|
||||||
const aux1: Table = await config.api.table.save({
|
let table: Table, aux1: Table, aux2: Table
|
||||||
...structures.basicTable(),
|
|
||||||
name: "aux1Table",
|
beforeAll(async () => {
|
||||||
schema: {
|
aux1 = await config.api.table.save({
|
||||||
name: { name: "name", type: FieldType.STRING },
|
...structures.basicTable(),
|
||||||
},
|
name: "aux1Table",
|
||||||
})
|
schema: {
|
||||||
const aux2: Table = await config.api.table.save({
|
name: { name: "name", type: FieldType.STRING },
|
||||||
...structures.basicTable(),
|
|
||||||
name: "aux2Table",
|
|
||||||
schema: {
|
|
||||||
title: { name: "title", type: FieldType.STRING },
|
|
||||||
aux1: {
|
|
||||||
name: "aux1",
|
|
||||||
type: FieldType.LINK,
|
|
||||||
tableId: aux1._id!,
|
|
||||||
relationshipType: RelationshipType.ONE_TO_MANY,
|
|
||||||
fieldName: "aux2",
|
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
})
|
aux2 = await config.api.table.save({
|
||||||
|
...structures.basicTable(),
|
||||||
const table: Table = {
|
name: "aux2Table",
|
||||||
...structures.basicTable(),
|
schema: {
|
||||||
schema: {
|
title: { name: "title", type: FieldType.STRING },
|
||||||
name: { name: "name", type: FieldType.STRING },
|
aux1_1: {
|
||||||
aux: {
|
name: "aux1_1",
|
||||||
name: "aux",
|
type: FieldType.LINK,
|
||||||
type: FieldType.LINK,
|
tableId: aux1._id!,
|
||||||
tableId: aux2._id!,
|
relationshipType: RelationshipType.ONE_TO_MANY,
|
||||||
relationshipType: RelationshipType.ONE_TO_MANY,
|
fieldName: "aux2_1",
|
||||||
fieldName: "table",
|
},
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
}
|
|
||||||
|
|
||||||
const result = await config.doInContext(config.appId, () => {
|
table = await config.api.table.save({
|
||||||
return getQueryableFields(Object.keys(table.schema), table)
|
...structures.basicTable(),
|
||||||
|
schema: {
|
||||||
|
name: { name: "name", type: FieldType.STRING },
|
||||||
|
aux1: {
|
||||||
|
name: "aux1",
|
||||||
|
type: FieldType.LINK,
|
||||||
|
tableId: aux1._id!,
|
||||||
|
relationshipType: RelationshipType.ONE_TO_MANY,
|
||||||
|
fieldName: "table",
|
||||||
|
},
|
||||||
|
aux2: {
|
||||||
|
name: "aux2",
|
||||||
|
type: FieldType.LINK,
|
||||||
|
tableId: aux2._id!,
|
||||||
|
relationshipType: RelationshipType.ONE_TO_MANY,
|
||||||
|
fieldName: "table",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
})
|
})
|
||||||
expect(result).toEqual([
|
|
||||||
"_id",
|
|
||||||
"name",
|
|
||||||
// Aux primitive props
|
|
||||||
"aux.title",
|
|
||||||
"aux2Table.title",
|
|
||||||
|
|
||||||
// Aux deep 1 primitive props
|
it("includes nested relationship fields from main table", async () => {
|
||||||
"aux.aux1.name",
|
const result = await config.doInContext(config.appId, () => {
|
||||||
"aux2Table.aux1.name",
|
return getQueryableFields(Object.keys(table.schema), table)
|
||||||
|
})
|
||||||
|
expect(result).toEqual([
|
||||||
|
"_id",
|
||||||
|
"name",
|
||||||
|
// deep 1 aux1 primitive props
|
||||||
|
"aux1.name",
|
||||||
|
"aux1Table.name",
|
||||||
|
|
||||||
// Aux deep 2 primitive props
|
// deep 2 aux1 primitive props
|
||||||
"aux.aux1Table.name",
|
"aux1.aux2_1.title",
|
||||||
"aux2Table.aux1Table.name",
|
"aux1Table.aux2_1.title",
|
||||||
])
|
"aux1.aux2Table.title",
|
||||||
|
"aux1Table.aux2Table.title",
|
||||||
|
|
||||||
|
// deep 1 aux2 primitive props
|
||||||
|
"aux2.title",
|
||||||
|
"aux2Table.title",
|
||||||
|
|
||||||
|
// deep 2 aux2 primitive props
|
||||||
|
"aux2.aux1_1.name",
|
||||||
|
"aux2Table.aux1_1.name",
|
||||||
|
"aux2.aux1Table.name",
|
||||||
|
"aux2Table.aux1Table.name",
|
||||||
|
])
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue