Add fields permissions

This commit is contained in:
rijkvanzanten
2020-07-14 16:53:32 -04:00
parent 0deaa77f0b
commit 54a2b3b74d
5 changed files with 238 additions and 150 deletions

View File

@@ -1,16 +1,24 @@
import { AST, NestedCollectionAST } from '../types/ast';
import { uniq } from 'lodash';
import { uniq, pick } from 'lodash';
import database, { schemaInspector } from './index';
import { Filter, Query } from '../types';
import { QueryBuilder } from 'knex';
export default async function runAST(ast: AST, query = ast.query) {
const toplevelFields: string[] = [];
const tempFields: string[] = [];
const nestedCollections: NestedCollectionAST[] = [];
const primaryKeyField = await schemaInspector.primary(ast.name);
const columnsInCollection = (await schemaInspector.columns(ast.name)).map(
({ column }) => column
);
for (const child of ast.children) {
if (child.type === 'field') {
toplevelFields.push(child.name);
if (columnsInCollection.includes(child.name)) {
toplevelFields.push(child.name);
}
continue;
}
@@ -24,7 +32,12 @@ export default async function runAST(ast: AST, query = ast.query) {
nestedCollections.push(child);
}
let dbQuery = database.select(toplevelFields).from(ast.name);
/** Always fetch primary key in case there's a nested relation that needs it */
if (toplevelFields.includes(primaryKeyField) === false) {
tempFields.push(primaryKeyField);
}
let dbQuery = database.select([...toplevelFields, ...tempFields]).from(ast.name);
if (query.filter) {
applyFilter(dbQuery, query.filter);
@@ -74,8 +87,18 @@ export default async function runAST(ast: AST, query = ast.query) {
const m2o = isM2O(batch);
let batchQuery: Query = {};
let tempField: string = null;
if (m2o) {
// Make sure we always fetch the nested items primary key field to ensure we have the key to match the item by
const toplevelFields = batch.children
.filter(({ type }) => type === 'field')
.map(({ name }) => name);
if (toplevelFields.includes(batch.relation.primary_one) === false) {
tempField = batch.relation.primary_one;
batch.children.push({ type: 'field', name: batch.relation.primary_one });
}
batchQuery = {
...batch.query,
filter: {
@@ -88,6 +111,17 @@ export default async function runAST(ast: AST, query = ast.query) {
},
};
} else {
// o2m
// Make sure we always fetch the related m2o field to ensure we have the foreign key to
// match the items by
const toplevelFields = batch.children
.filter(({ type }) => type === 'field')
.map(({ name }) => name);
if (toplevelFields.includes(batch.relation.field_many) === false) {
tempField = batch.relation.field_many;
batch.children.push({ type: 'field', name: batch.relation.field_many });
}
batchQuery = {
...batch.query,
filter: {
@@ -103,34 +137,53 @@ export default async function runAST(ast: AST, query = ast.query) {
results = results.map((record) => {
if (m2o) {
const nestedResult =
nestedResults.find((nestedRecord) => {
return nestedRecord[batch.relation.primary_one] === record[batch.fieldKey];
}) || null;
if (tempField && nestedResult) {
delete nestedResult[tempField];
}
return {
...record,
[batch.fieldKey]:
nestedResults.find((nestedRecord) => {
return (
nestedRecord[batch.relation.primary_one] === record[batch.fieldKey]
);
}) || null,
[batch.fieldKey]: nestedResult,
};
}
return {
// o2m
const newRecord = {
...record,
[batch.fieldKey]: nestedResults.filter((nestedRecord) => {
/**
* @todo
* pull the name ID from somewhere real
*/
return (
nestedRecord[batch.relation.field_many] === record.id ||
nestedRecord[batch.relation.field_many]?.id === record.id
);
}),
[batch.fieldKey]: nestedResults
.filter((nestedRecord) => {
/**
* @todo
* pull the name ID from somewhere real
*/
return (
nestedRecord[batch.relation.field_many] === record.id ||
nestedRecord[batch.relation.field_many]?.id === record.id
);
})
.map((nestedRecord) => {
if (tempField) {
delete nestedRecord[tempField];
}
return nestedRecord;
}),
};
return newRecord;
});
}
return results;
const nestedCollectionKeys = nestedCollections.map(({ fieldKey }) => fieldKey);
return results.map((result) =>
pick(result, uniq([...nestedCollectionKeys, ...toplevelFields]))
);
}
function isM2O(child: NestedCollectionAST) {
@@ -143,31 +196,29 @@ function applyFilter(dbQuery: QueryBuilder, filter: Filter) {
for (const [key, value] of Object.entries(filter)) {
if (key.startsWith('_') === false) {
let operator = Object.keys(value)[0];
operator = operator.slice(1);
operator = operator.toLowerCase();
const compareValue = Object.values(value)[0];
if (operator === 'eq') {
if (operator === '_eq') {
dbQuery.where({ [key]: compareValue });
}
if (operator === 'neq') {
if (operator === '_neq') {
dbQuery.whereNot({ [key]: compareValue });
}
if (operator === 'in') {
if (operator === '_in') {
let value = compareValue;
if (typeof value === 'string') value = value.split(',');
dbQuery.whereIn(key, value as string[]);
}
if (operator === 'null') {
if (operator === '_null') {
dbQuery.whereNull(key);
}
if (operator === 'nnull') {
if (operator === '_nnull') {
dbQuery.whereNotNull(key);
}
}