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:
@@ -4,20 +4,9 @@
|
||||
* change events.
|
||||
*/
|
||||
|
||||
import { ActionSummary } from "app/common/ActionSummary";
|
||||
import { DocTriggers } from "app/server/lib/Triggers";
|
||||
import * as assert from 'assert';
|
||||
import {Mutex} from 'async-mutex';
|
||||
import * as bluebird from 'bluebird';
|
||||
import {EventEmitter} from 'events';
|
||||
import {IMessage, MsgType} from 'grain-rpc';
|
||||
import * as imageSize from 'image-size';
|
||||
import * as moment from 'moment-timezone';
|
||||
import fetch from 'node-fetch';
|
||||
import * as tmp from 'tmp';
|
||||
|
||||
import {getEnvContent, LocalActionBundle, SandboxActionBundle, UserActionBundle} from 'app/common/ActionBundle';
|
||||
import { ActionGroup, MinimalActionGroup} from 'app/common/ActionGroup';
|
||||
import {ActionGroup, MinimalActionGroup} from 'app/common/ActionGroup';
|
||||
import {ActionSummary} from "app/common/ActionSummary";
|
||||
import {
|
||||
ApplyUAOptions,
|
||||
ApplyUAResult,
|
||||
@@ -36,7 +25,6 @@ import {mapGetOrSet, MapWithTTL} from 'app/common/AsyncCreate';
|
||||
import {
|
||||
CellValue,
|
||||
DocAction,
|
||||
RowRecord,
|
||||
TableDataAction,
|
||||
TableRecordValue,
|
||||
toTableDataAction,
|
||||
@@ -50,6 +38,7 @@ import {FormulaProperties, getFormulaProperties} from 'app/common/GranularAccess
|
||||
import {byteString, countIf, safeJsonParse} from 'app/common/gutil';
|
||||
import {InactivityTimer} from 'app/common/InactivityTimer';
|
||||
import {schema, SCHEMA_VERSION} from 'app/common/schema';
|
||||
import {MetaRowRecord} from 'app/common/TableData';
|
||||
import {FetchUrlOptions, UploadResult} from 'app/common/uploads';
|
||||
import {DocReplacementOptions, DocState, DocStateComparison} from 'app/common/UserAPI';
|
||||
import {ParseOptions} from 'app/plugin/FileParserAPI';
|
||||
@@ -66,7 +55,17 @@ import * as log from 'app/server/lib/log';
|
||||
import {LogMethods} from "app/server/lib/LogMethods";
|
||||
import {shortDesc} from 'app/server/lib/shortDesc';
|
||||
import {TableMetadataLoader} from 'app/server/lib/TableMetadataLoader';
|
||||
import {DocTriggers} from "app/server/lib/Triggers";
|
||||
import {fetchURL, FileUploadInfo, globalUploadSet, UploadInfo} from 'app/server/lib/uploads';
|
||||
import * as assert from 'assert';
|
||||
import {Mutex} from 'async-mutex';
|
||||
import * as bluebird from 'bluebird';
|
||||
import {EventEmitter} from 'events';
|
||||
import {IMessage, MsgType} from 'grain-rpc';
|
||||
import * as imageSize from 'image-size';
|
||||
import * as moment from 'moment-timezone';
|
||||
import fetch from 'node-fetch';
|
||||
import * as tmp from 'tmp';
|
||||
|
||||
import {ActionHistory} from './ActionHistory';
|
||||
import {ActionHistoryImpl} from './ActionHistoryImpl';
|
||||
@@ -89,8 +88,8 @@ import {findOrAddAllEnvelope, Sharing} from './Sharing';
|
||||
import cloneDeep = require('lodash/cloneDeep');
|
||||
import flatten = require('lodash/flatten');
|
||||
import remove = require('lodash/remove');
|
||||
import zipObject = require('lodash/zipObject');
|
||||
import without = require('lodash/without');
|
||||
import zipObject = require('lodash/zipObject');
|
||||
|
||||
bluebird.promisifyAll(tmp);
|
||||
|
||||
@@ -608,14 +607,14 @@ export class ActiveDoc extends EventEmitter {
|
||||
* Returns the record from _grist_Attachments table for the given attachment ID,
|
||||
* or throws an error if not found.
|
||||
*/
|
||||
public getAttachmentMetadata(attId: number|string): RowRecord {
|
||||
public getAttachmentMetadata(attId: number|string): MetaRowRecord<'_grist_Attachments'> {
|
||||
// docData should always be available after loadDoc() or createDoc().
|
||||
if (!this.docData) {
|
||||
throw new Error("No doc data");
|
||||
}
|
||||
// Parse strings into numbers to make more convenient to call from route handlers.
|
||||
const attachmentId: number = (typeof attId === 'string') ? parseInt(attId, 10) : attId;
|
||||
const attRecord = this.docData.getTable('_grist_Attachments')!.getRecord(attachmentId);
|
||||
const attRecord = this.docData.getMetaTable('_grist_Attachments').getRecord(attachmentId);
|
||||
if (!attRecord) {
|
||||
throw new ApiError(`Attachment not found: ${attId}`, 404);
|
||||
}
|
||||
@@ -1116,14 +1115,14 @@ export class ActiveDoc extends EventEmitter {
|
||||
throw new Error('Cannot list ACL resources');
|
||||
}
|
||||
const result: {[tableId: string]: string[]} = {};
|
||||
const tables = this.docData.getTable('_grist_Tables')!;
|
||||
for (const tableId of tables.getColValues('tableId')!) {
|
||||
result[tableId as string] = ['id'];
|
||||
const tables = this.docData.getMetaTable('_grist_Tables');
|
||||
for (const tableId of tables.getColValues('tableId')) {
|
||||
result[tableId] = ['id'];
|
||||
}
|
||||
const columns = this.docData.getTable('_grist_Tables_column')!;
|
||||
const columns = this.docData.getMetaTable('_grist_Tables_column');
|
||||
for (const col of columns.getRecords()) {
|
||||
const tableId = tables.getValue(col.parentId as number, 'tableId');
|
||||
result[tableId as string].push(col.colId as string);
|
||||
const tableId = tables.getValue(col.parentId, 'tableId')!;
|
||||
result[tableId].push(col.colId);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -33,8 +33,8 @@ class GristDocAPIImpl implements GristDocAPI {
|
||||
public async getDocName() { return this._activeDoc.docName; }
|
||||
|
||||
public async listTables(): Promise<string[]> {
|
||||
const table = this._activeDoc.docData!.getTable('_grist_Tables')!;
|
||||
return (table.getColValues('tableId') as string[])
|
||||
const table = this._activeDoc.docData!.getMetaTable('_grist_Tables');
|
||||
return table.getColValues('tableId')
|
||||
.filter(id => !id.startsWith("GristSummary_")).sort();
|
||||
}
|
||||
|
||||
|
||||
@@ -56,8 +56,8 @@ export function expandQuery(iquery: ServerQuery, docData: DocData, onDemandFormu
|
||||
// Iterate through all formulas, adding joins and selects as we go.
|
||||
if (onDemandFormulas) {
|
||||
// Look up the main table for the query.
|
||||
const tables = docData.getTable('_grist_Tables')!;
|
||||
const columns = docData.getTable('_grist_Tables_column')!;
|
||||
const tables = docData.getMetaTable('_grist_Tables');
|
||||
const columns = docData.getMetaTable('_grist_Tables_column');
|
||||
const tableRef = tables.findRow('tableId', query.tableId);
|
||||
if (!tableRef) { throw new ApiError('table not found', 404); }
|
||||
|
||||
@@ -81,7 +81,7 @@ export function expandQuery(iquery: ServerQuery, docData: DocData, onDemandFormu
|
||||
let error = "";
|
||||
if (formula.kind === 'foreignColumn') {
|
||||
const altTableId = references.get(formula.refColId);
|
||||
const altTableRef = tables.findRow('tableId', altTableId);
|
||||
const altTableRef = tables.findRow('tableId', altTableId!);
|
||||
if (altTableId && altTableRef) {
|
||||
const altColumn = columns.filterRecords({parentId: altTableRef, isFormula: false, colId: formula.colId});
|
||||
// TODO: deal with a formula column in the other table.
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import {ApiError} from 'app/common/ApiError';
|
||||
import {buildColFilter} from 'app/common/ColumnFilterFunc';
|
||||
import {RowRecord} from 'app/common/DocActions';
|
||||
import {DocData} from 'app/common/DocData';
|
||||
import {DocumentSettings} from 'app/common/DocumentSettings';
|
||||
import * as gristTypes from 'app/common/gristTypes';
|
||||
@@ -9,7 +8,7 @@ import {buildRowFilter} from 'app/common/RowFilterFunc';
|
||||
import {SchemaTypes} from 'app/common/schema';
|
||||
import {SortFunc} from 'app/common/SortFunc';
|
||||
import {Sort} from 'app/common/SortSpec';
|
||||
import {TableData} from 'app/common/TableData';
|
||||
import {MetaRowRecord, MetaTableData} from 'app/common/TableData';
|
||||
import {ActiveDoc} from 'app/server/lib/ActiveDoc';
|
||||
import {RequestWithLogin} from 'app/server/lib/Authorizer';
|
||||
import {docSessionFromRequest} from 'app/server/lib/DocSession';
|
||||
@@ -103,12 +102,14 @@ function safe<T>(value: T, msg: string) {
|
||||
}
|
||||
|
||||
// Helper to for getting table from docData.
|
||||
const safeTable = (docData: DocData, name: keyof SchemaTypes) => safe(docData.getTable(name),
|
||||
`No table '${name}' in document with id ${docData}`);
|
||||
function safeTable<TableId extends keyof SchemaTypes>(docData: DocData, name: TableId) {
|
||||
return safe(docData.getMetaTable(name), `No table '${name}' in document with id ${docData}`);
|
||||
}
|
||||
|
||||
// Helper for getting record safe
|
||||
const safeRecord = (table: TableData, id: number) => safe(table.getRecord(id),
|
||||
`No record ${id} in table ${table.tableId}`);
|
||||
function safeRecord<TableId extends keyof SchemaTypes>(table: MetaTableData<TableId>, id: number) {
|
||||
return safe(table.getRecord(id), `No record ${id} in table ${table.tableId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds export for all raw tables that are in doc.
|
||||
@@ -138,9 +139,9 @@ export async function exportTable(
|
||||
req: express.Request): Promise<ExportData> {
|
||||
const docData = safe(activeDoc.docData, "No docData in active document");
|
||||
const tables = safeTable(docData, '_grist_Tables');
|
||||
const table = safeRecord(tables, tableId) as GristTables;
|
||||
const tableColumns = (safeTable(docData, '_grist_Tables_column')
|
||||
.getRecords() as GristTablesColumn[])
|
||||
const table = safeRecord(tables, tableId);
|
||||
const tableColumns = safeTable(docData, '_grist_Tables_column')
|
||||
.getRecords()
|
||||
// remove manual sort column
|
||||
.filter(col => col.colId !== gristTypes.MANUALSORT);
|
||||
// Produce a column description matching what user will see / expect to export
|
||||
@@ -183,11 +184,11 @@ export async function exportTable(
|
||||
if (table.primaryViewId) {
|
||||
const viewId = table.primaryViewId;
|
||||
const views = safeTable(docData, '_grist_Views');
|
||||
const view = safeRecord(views, viewId) as GristView;
|
||||
const view = safeRecord(views, viewId);
|
||||
tableName = view.name;
|
||||
}
|
||||
|
||||
const docInfo = safeRecord(safeTable(docData, '_grist_DocInfo'), 1) as DocInfo;
|
||||
const docInfo = safeRecord(safeTable(docData, '_grist_DocInfo'), 1);
|
||||
const docSettings = gutil.safeJsonParse(docInfo.documentSettings, {});
|
||||
return {
|
||||
tableName,
|
||||
@@ -211,15 +212,15 @@ export async function exportSection(
|
||||
|
||||
const docData = safe(activeDoc.docData, "No docData in active document");
|
||||
const viewSections = safeTable(docData, '_grist_Views_section');
|
||||
const viewSection = safeRecord(viewSections, viewSectionId) as GristViewsSection;
|
||||
const viewSection = safeRecord(viewSections, viewSectionId);
|
||||
const tables = safeTable(docData, '_grist_Tables');
|
||||
const table = safeRecord(tables, viewSection.tableRef) as GristTables;
|
||||
const table = safeRecord(tables, viewSection.tableRef);
|
||||
const columns = safeTable(docData, '_grist_Tables_column')
|
||||
.filterRecords({ parentId: table.id }) as GristTablesColumn[];
|
||||
.filterRecords({parentId: table.id});
|
||||
const viewSectionFields = safeTable(docData, '_grist_Views_section_field');
|
||||
const fields = viewSectionFields.filterRecords({ parentId: viewSection.id }) as GristViewsSectionField[];
|
||||
const fields = viewSectionFields.filterRecords({parentId: viewSection.id});
|
||||
const savedFilters = safeTable(docData, '_grist_Filters')
|
||||
.filterRecords({ viewSectionRef: viewSection.id }) as GristFilter[];
|
||||
.filterRecords({viewSectionRef: viewSection.id});
|
||||
|
||||
const tableColsById = _.indexBy(columns, 'id');
|
||||
const fieldsByColRef = _.indexBy(fields, 'colRef');
|
||||
@@ -279,7 +280,7 @@ export async function exportSection(
|
||||
// filter rows numbers
|
||||
rowIds = rowIds.filter(rowFilter);
|
||||
|
||||
const docInfo = safeRecord(safeTable(docData, '_grist_DocInfo'), 1) as DocInfo;
|
||||
const docInfo = safeRecord(safeTable(docData, '_grist_DocInfo'), 1);
|
||||
const docSettings = gutil.safeJsonParse(docInfo.documentSettings, {});
|
||||
|
||||
return {
|
||||
@@ -292,17 +293,8 @@ export async function exportSection(
|
||||
};
|
||||
}
|
||||
|
||||
// Type helpers for types used in this export
|
||||
type RowModel<TName extends keyof SchemaTypes> = RowRecord & {
|
||||
[ColId in keyof SchemaTypes[TName]]: SchemaTypes[TName][ColId];
|
||||
};
|
||||
type GristViewsSection = RowModel<'_grist_Views_section'>
|
||||
type GristTables = RowModel<'_grist_Tables'>
|
||||
type GristViewsSectionField = RowModel<'_grist_Views_section_field'>
|
||||
type GristTablesColumn = RowModel<'_grist_Tables_column'>
|
||||
type GristView = RowModel<'_grist_Views'>
|
||||
type GristFilter = RowModel<'_grist_Filters'>
|
||||
type DocInfo = RowModel<'_grist_DocInfo'>
|
||||
type GristViewsSectionField = MetaRowRecord<'_grist_Views_section_field'>
|
||||
type GristTablesColumn = MetaRowRecord<'_grist_Tables_column'>
|
||||
|
||||
// Type for filters passed from the client
|
||||
export interface Filter { colRef: number, filter: string }
|
||||
|
||||
@@ -74,7 +74,7 @@ export async function makeCSVFromTable(
|
||||
}
|
||||
|
||||
// Look up the table to make a CSV from.
|
||||
const tables = activeDoc.docData.getTable('_grist_Tables')!;
|
||||
const tables = activeDoc.docData.getMetaTable('_grist_Tables');
|
||||
const tableRef = tables.findRow('tableId', tableId);
|
||||
|
||||
if (tableRef === 0) {
|
||||
|
||||
@@ -265,10 +265,10 @@ export class GranularAccess implements GranularAccessForBundle {
|
||||
// Create a tmpDocData with just the tables we care about, then update docActions to it.
|
||||
const tmpDocData: DocData = new DocData(
|
||||
(tableId) => { throw new Error("Unexpected DocData fetch"); }, {
|
||||
_grist_Tables: this._docData.getTable('_grist_Tables')!.getTableDataAction(),
|
||||
_grist_Tables_column: this._docData.getTable('_grist_Tables_column')!.getTableDataAction(),
|
||||
_grist_ACLResources: this._docData.getTable('_grist_ACLResources')!.getTableDataAction(),
|
||||
_grist_ACLRules: this._docData.getTable('_grist_ACLRules')!.getTableDataAction(),
|
||||
_grist_Tables: this._docData.getMetaTable('_grist_Tables').getTableDataAction(),
|
||||
_grist_Tables_column: this._docData.getMetaTable('_grist_Tables_column').getTableDataAction(),
|
||||
_grist_ACLResources: this._docData.getMetaTable('_grist_ACLResources').getTableDataAction(),
|
||||
_grist_ACLRules: this._docData.getMetaTable('_grist_ACLRules').getTableDataAction(),
|
||||
});
|
||||
for (const da of docActions) {
|
||||
tmpDocData.receiveAction(da);
|
||||
|
||||
@@ -22,8 +22,8 @@ export interface OnDemandStorage {
|
||||
*/
|
||||
export class OnDemandActions {
|
||||
|
||||
private _tablesMeta: TableData = this._docData.getTable('_grist_Tables')!;
|
||||
private _columnsMeta: TableData = this._docData.getTable('_grist_Tables_column')!;
|
||||
private _tablesMeta: TableData = this._docData.getMetaTable('_grist_Tables');
|
||||
private _columnsMeta: TableData = this._docData.getMetaTable('_grist_Tables_column');
|
||||
|
||||
constructor(private _storage: OnDemandStorage, private _docData: DocData) {}
|
||||
|
||||
|
||||
@@ -138,8 +138,8 @@ export class DocTriggers {
|
||||
} // Happens on doc creation while processing InitNewDoc action.
|
||||
|
||||
const triggersTable = docData.getMetaTable("_grist_Triggers");
|
||||
const getTableId = docData.getMetaTable("_grist_Tables").getMetaRowPropFunc("tableId");
|
||||
this._getColId = docData.getMetaTable("_grist_Tables_column").getMetaRowPropFunc("colId");
|
||||
const getTableId = docData.getMetaTable("_grist_Tables").getRowPropFunc("tableId");
|
||||
this._getColId = docData.getMetaTable("_grist_Tables_column").getRowPropFunc("colId");
|
||||
|
||||
const triggersByTableRef = _.groupBy(triggersTable.getRecords(), "tableRef");
|
||||
const triggersByTableId: Array<[string, Trigger[]]> = [];
|
||||
|
||||
Reference in New Issue
Block a user