mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) Use MetaTableData more
Summary: Add more method overrides to MetaTableData for extra type safety. Use MetaTableData, MetaRowRecord, and getMetaTable in more places. Test Plan: Mostly it just has to compile. Tested manually that types are being checked more strictly now, e.g. by adding a typo to property names. Some type casting has also been removed. Reviewers: dsagal Reviewed By: dsagal Subscribers: dsagal Differential Revision: https://phab.getgrist.com/D3168
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import { parsePermissions } from 'app/common/ACLPermissions';
|
||||
import { ILogger } from 'app/common/BaseAPI';
|
||||
import { CellValue, RowRecord } from 'app/common/DocActions';
|
||||
import { DocData } from 'app/common/DocData';
|
||||
import { AclMatchFunc, ParsedAclFormula, RulePart, RuleSet, UserAttributeRule } from 'app/common/GranularAccessClause';
|
||||
import { getSetMapValue } from 'app/common/gutil';
|
||||
import {parsePermissions} from 'app/common/ACLPermissions';
|
||||
import {ILogger} from 'app/common/BaseAPI';
|
||||
import {DocData} from 'app/common/DocData';
|
||||
import {AclMatchFunc, ParsedAclFormula, RulePart, RuleSet, UserAttributeRule} from 'app/common/GranularAccessClause';
|
||||
import {getSetMapValue} from 'app/common/gutil';
|
||||
import {MetaRowRecord} from 'app/common/TableData';
|
||||
import sortBy = require('lodash/sortBy');
|
||||
|
||||
const defaultMatchFunc: AclMatchFunc = () => true;
|
||||
@@ -231,8 +231,8 @@ export class ACLRuleCollection {
|
||||
* Check that all references to table and column IDs in ACL rules are valid.
|
||||
*/
|
||||
public checkDocEntities(docData: DocData) {
|
||||
const tablesTable = docData.getTable('_grist_Tables')!;
|
||||
const columnsTable = docData.getTable('_grist_Tables_column')!;
|
||||
const tablesTable = docData.getMetaTable('_grist_Tables');
|
||||
const columnsTable = docData.getMetaTable('_grist_Tables_column');
|
||||
|
||||
// Collect valid tableIds and check rules against those.
|
||||
const validTableIds = new Set(tablesTable.getColValues('tableId'));
|
||||
@@ -242,9 +242,9 @@ export class ACLRuleCollection {
|
||||
}
|
||||
|
||||
// Collect valid columns, grouped by tableRef (rowId of table record).
|
||||
const validColumns = new Map<number, Set<CellValue>>(); // Map from tableRef to set of colIds.
|
||||
const colTableRefs = columnsTable.getColValues('parentId')!;
|
||||
for (const [i, colId] of columnsTable.getColValues('colId')!.entries()) {
|
||||
const validColumns = new Map<number, Set<string>>(); // Map from tableRef to set of colIds.
|
||||
const colTableRefs = columnsTable.getColValues('parentId');
|
||||
for (const [i, colId] of columnsTable.getColValues('colId').entries()) {
|
||||
getSetMapValue(validColumns, colTableRefs[i], () => new Set()).add(colId);
|
||||
}
|
||||
|
||||
@@ -302,14 +302,14 @@ export interface ReadAclResults {
|
||||
* UserAttributeRules. This is used by both client-side code and server-side.
|
||||
*/
|
||||
function readAclRules(docData: DocData, {log, compile}: ReadAclOptions): ReadAclResults {
|
||||
const resourcesTable = docData.getTable('_grist_ACLResources')!;
|
||||
const rulesTable = docData.getTable('_grist_ACLRules')!;
|
||||
const resourcesTable = docData.getMetaTable('_grist_ACLResources');
|
||||
const rulesTable = docData.getMetaTable('_grist_ACLRules');
|
||||
|
||||
const ruleSets: RuleSet[] = [];
|
||||
const userAttributes: UserAttributeRule[] = [];
|
||||
|
||||
// Group rules by resource first, ordering by rulePos. Each group will become a RuleSet.
|
||||
const rulesByResource = new Map<number, RowRecord[]>();
|
||||
const rulesByResource = new Map<number, Array<MetaRowRecord<'_grist_ACLRules'>>>();
|
||||
for (const ruleRecord of sortBy(rulesTable.getRecords(), 'rulePos')) {
|
||||
getSetMapValue(rulesByResource, ruleRecord.resource, () => []).push(ruleRecord);
|
||||
}
|
||||
@@ -325,8 +325,8 @@ function readAclRules(docData: DocData, {log, compile}: ReadAclOptions): ReadAcl
|
||||
// intentionally ignore and skip.
|
||||
continue;
|
||||
}
|
||||
const tableId = resourceRec.tableId as string;
|
||||
const colIds = resourceRec.colIds === '*' ? '*' : (resourceRec.colIds as string).split(',');
|
||||
const tableId = resourceRec.tableId;
|
||||
const colIds = resourceRec.colIds === '*' ? '*' : resourceRec.colIds.split(',');
|
||||
|
||||
const body: RulePart[] = [];
|
||||
for (const rule of rules) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { PartialPermissionSet } from 'app/common/ACLPermissions';
|
||||
import { CellValue, RowRecord } from 'app/common/DocActions';
|
||||
import { Role } from './roles';
|
||||
import {PartialPermissionSet} from 'app/common/ACLPermissions';
|
||||
import {CellValue, RowRecord} from 'app/common/DocActions';
|
||||
import {MetaRowRecord} from 'app/common/TableData';
|
||||
import {Role} from './roles';
|
||||
|
||||
export interface RuleSet {
|
||||
tableId: '*' | string;
|
||||
@@ -11,7 +12,7 @@ export interface RuleSet {
|
||||
}
|
||||
|
||||
export interface RulePart {
|
||||
origRecord?: RowRecord; // Original record used to create this RulePart.
|
||||
origRecord?: MetaRowRecord<'_grist_ACLRules'>; // Original record used to create this RulePart.
|
||||
aclFormula: string;
|
||||
permissions: PartialPermissionSet;
|
||||
permissionsText: string; // The text version of PermissionSet, as stored.
|
||||
|
||||
@@ -13,7 +13,6 @@ import fromPairs = require('lodash/fromPairs');
|
||||
|
||||
export interface ColTypeMap { [colId: string]: string; }
|
||||
|
||||
type RowFunc<T> = (rowId: number) => T;
|
||||
type UIRowFunc<T> = (rowId: UIRowId) => T;
|
||||
|
||||
interface ColData {
|
||||
@@ -325,7 +324,7 @@ export class TableData extends ActionDispatcher implements SkippableRows {
|
||||
* Returns the first rowId matching the given filters, or 0 if no match. If there are multiple
|
||||
* matches, it is unspecified which will be returned.
|
||||
*/
|
||||
public findMatchingRowId(properties: {[key: string]: CellValue}): number {
|
||||
public findMatchingRowId(properties: {[key: string]: CellValue | undefined}): number {
|
||||
const props = Object.keys(properties).map(p => ({col: this._columns.get(p)!, value: properties[p]}));
|
||||
if (!props.every((p) => p.col)) {
|
||||
return 0;
|
||||
@@ -480,7 +479,15 @@ export class TableData extends ActionDispatcher implements SkippableRows {
|
||||
}
|
||||
}
|
||||
|
||||
export type MetaRowRecord<TableId extends keyof SchemaTypes> = SchemaTypes[TableId] & RowRecord;
|
||||
// A type safe record of a meta table with types as defined in schema.ts
|
||||
// '&' is used because declaring the id field and the index signature in one block gives a syntax error.
|
||||
// The second part is basically equivalent to SchemaTypes[TableId]
|
||||
// but TS sees that as incompatible with RowRecord and doesn't allow simple overrides in MetaTableData.
|
||||
export type MetaRowRecord<TableId extends keyof SchemaTypes> =
|
||||
{ id: number } &
|
||||
{ [ColId in keyof SchemaTypes[TableId]]: SchemaTypes[TableId][ColId] & CellValue };
|
||||
|
||||
type MetaColId<TableId extends keyof SchemaTypes> = keyof MetaRowRecord<TableId> & string;
|
||||
|
||||
/**
|
||||
* Behaves the same as TableData, but uses SchemaTypes for type safety of its columns.
|
||||
@@ -490,6 +497,11 @@ export class MetaTableData<TableId extends keyof SchemaTypes> extends TableData
|
||||
super(tableId, tableData, colTypes);
|
||||
}
|
||||
|
||||
public getValue<ColId extends MetaColId<TableId>>(rowId: number, colId: ColId):
|
||||
MetaRowRecord<TableId>[ColId] | undefined {
|
||||
return super.getValue(rowId, colId) as any;
|
||||
}
|
||||
|
||||
public getRecords(): Array<MetaRowRecord<TableId>> {
|
||||
return super.getRecords() as any;
|
||||
}
|
||||
@@ -498,14 +510,31 @@ export class MetaTableData<TableId extends keyof SchemaTypes> extends TableData
|
||||
return super.getRecord(rowId) as any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as getRowPropFunc, but I couldn't get a direct override to compile.
|
||||
*/
|
||||
public getMetaRowPropFunc<ColId extends keyof SchemaTypes[TableId]>(
|
||||
public filterRecords(properties: Partial<MetaRowRecord<TableId>>): Array<MetaRowRecord<TableId>> {
|
||||
return super.filterRecords(properties) as any;
|
||||
}
|
||||
|
||||
public findMatchingRowId(properties: Partial<MetaRowRecord<TableId>>): number {
|
||||
return super.findMatchingRowId(properties);
|
||||
}
|
||||
|
||||
public getRowPropFunc<ColId extends MetaColId<TableId>>(
|
||||
colId: ColId
|
||||
): RowFunc<SchemaTypes[TableId][ColId]|undefined> {
|
||||
): UIRowFunc<MetaRowRecord<TableId>[ColId]> {
|
||||
return super.getRowPropFunc(colId as any) as any;
|
||||
}
|
||||
|
||||
public getColValues<ColId extends MetaColId<TableId>>(
|
||||
colId: ColId
|
||||
): ReadonlyArray<MetaRowRecord<TableId>[ColId]> {
|
||||
return super.getColValues(colId) as any;
|
||||
}
|
||||
|
||||
public findRow<ColId extends MetaColId<TableId>>(
|
||||
colId: ColId, colValue: MetaRowRecord<TableId>[ColId]
|
||||
): number {
|
||||
return super.findRow(colId, colValue);
|
||||
}
|
||||
}
|
||||
|
||||
function reassignArray<T>(targetArray: T[], sourceArray: T[]): void {
|
||||
|
||||
Reference in New Issue
Block a user