(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:
Alex Hall
2021-12-07 13:21:16 +02:00
parent 116fb15eda
commit faec8177ab
18 changed files with 157 additions and 126 deletions

View File

@@ -38,6 +38,7 @@ import {isHiddenCol} from 'app/common/gristTypes';
import {isObject} from 'app/common/gutil';
import * as roles from 'app/common/roles';
import {SchemaTypes} from 'app/common/schema';
import {MetaRowRecord} from 'app/common/TableData';
import {ANONYMOUS_USER_EMAIL, EVERYONE_EMAIL, getRealAccess} from 'app/common/UserAPI';
import {
BaseObservable,
@@ -216,11 +217,11 @@ export class AccessRules extends Disposable {
// ACL tables (they may have changed by other users). So our changes will win.
const docData = this._gristDoc.docData;
const resourcesTable = docData.getTable('_grist_ACLResources')!;
const rulesTable = docData.getTable('_grist_ACLRules')!;
const resourcesTable = docData.getMetaTable('_grist_ACLResources');
const rulesTable = docData.getMetaTable('_grist_ACLRules');
// Add/remove resources to have just the ones we need.
const newResources: RowRecord[] = flatten(
const newResources: MetaRowRecord<'_grist_ACLResources'>[] = flatten(
[{tableId: '*', colIds: '*'}],
this._specialRules.get()?.getResources() || [],
...this._tableRules.get().map(t => t.getResources()))

View File

@@ -675,7 +675,7 @@ export class GristDoc extends DisposableWithEvents {
}
public hasGranularAccessRules(): boolean {
const rulesTable = this.docData.getTable('_grist_ACLRules')!;
const rulesTable = this.docData.getMetaTable('_grist_ACLRules');
// To check if there are rules, ignore the default no-op rule created for an older incarnation
// of ACLs. It exists in older documents, and is still created for new ones. We detect it by
// the use of the deprecated 'permissions' field, and not the new 'permissionsText' field.

View File

@@ -90,7 +90,7 @@ async function updateViewSections(gristDoc: GristDoc, destViewSections: ViewSect
const records: RowRecord[] = [];
for (const srcViewSection of srcViewSections) {
const viewSectionLayoutSpec = patchLayoutSpec(srcViewSection.layoutSpecObj.peek(), fieldsMap);
const record = gristDoc.docData.getTable('_grist_Views_section')!.getRecord(srcViewSection.getRowId())!;
const record = gristDoc.docData.getMetaTable('_grist_Views_section').getRecord(srcViewSection.getRowId())!;
records.push({
...record,
layoutSpec: JSON.stringify(viewSectionLayoutSpec),
@@ -126,7 +126,7 @@ async function updateViewFields(gristDoc: GristDoc, destViewSections: ViewSectio
const srcViewFields: ViewFieldRec[] = srcViewSection!.viewFields.peek().peek();
const parentId = destViewSection!.getRowId();
for (const field of srcViewFields) {
const record = docData.getTable('_grist_Views_section_field')!.getRecord(field.getRowId())!;
const record = docData.getMetaTable('_grist_Views_section_field').getRecord(field.getRowId())!;
fieldsToAdd.push({...record, parentId});
}
}

View File

@@ -5,10 +5,11 @@
*/
import {DocComm} from 'app/client/components/DocComm';
import {TableData} from 'app/client/models/TableData';
import {MetaTableData, TableData} from 'app/client/models/TableData';
import {ApplyUAOptions, ApplyUAResult} from 'app/common/ActiveDocAPI';
import {CellValue, TableDataAction, UserAction} from 'app/common/DocActions';
import {DocData as BaseDocData} from 'app/common/DocData';
import {SchemaTypes} from 'app/common/schema';
import {ColTypeMap} from 'app/common/TableData';
import * as bluebird from 'bluebird';
import {Emitter} from 'grainjs';
@@ -51,6 +52,11 @@ export class DocData extends BaseDocData {
return super.getTable(tableId) as TableData;
}
// Version of inherited getMetaTable() which returns the enhanced TableData type.
public getMetaTable<TableId extends keyof SchemaTypes>(tableId: TableId): MetaTableData<TableId> {
return super.getMetaTable(tableId) as any;
}
/**
* Finds up to n most likely target columns for the given values in the document.
*/

View File

@@ -1,19 +1,20 @@
/**
* TableData maintains a single table's data.
*/
import { ColumnACIndexes } from 'app/client/models/ColumnACIndexes';
import { ColumnCache } from 'app/client/models/ColumnCache';
import { DocData } from 'app/client/models/DocData';
import { DocAction, ReplaceTableData, TableDataAction, UserAction } from 'app/common/DocActions';
import { isRaisedException } from 'app/common/gristTypes';
import { countIf } from 'app/common/gutil';
import { TableData as BaseTableData, ColTypeMap } from 'app/common/TableData';
import { Emitter } from 'grainjs';
import {ColumnACIndexes} from 'app/client/models/ColumnACIndexes';
import {ColumnCache} from 'app/client/models/ColumnCache';
import {DocData} from 'app/client/models/DocData';
import {DocAction, ReplaceTableData, TableDataAction, UserAction} from 'app/common/DocActions';
import {isRaisedException} from 'app/common/gristTypes';
import {countIf} from 'app/common/gutil';
import {SchemaTypes} from 'app/common/schema';
import {ColTypeMap, MetaTableData as MetaTableDataBase, TableData as TableDataBase} from 'app/common/TableData';
import {Emitter} from 'grainjs';
/**
* TableData class to maintain a single table's data.
*/
export class TableData extends BaseTableData {
export class TableData extends TableDataBase {
public readonly tableActionEmitter = new Emitter();
public readonly dataLoadedEmitter = new Emitter();
@@ -108,3 +109,5 @@ export class TableData extends BaseTableData {
return applied;
}
}
export type MetaTableData<TableId extends keyof SchemaTypes> = MetaTableDataBase<TableId> & TableData;

View File

@@ -7,7 +7,7 @@ import {dom, LiveIndex, makeLiveIndex, styled} from 'grainjs';
import {DocComm} from 'app/client/components/DocComm';
import {selectFiles, uploadFiles} from 'app/client/lib/uploads';
import {DocData} from 'app/client/models/DocData';
import {TableData} from 'app/client/models/TableData';
import {MetaTableData} from 'app/client/models/TableData';
import {basicButton, basicButtonLink, cssButtonGroup} from 'app/client/ui2018/buttons';
import {colors, testId, vars} from 'app/client/ui2018/cssVars';
import {editableLabel} from 'app/client/ui2018/editableLabel';
@@ -47,7 +47,7 @@ interface Attachment {
* download, add or remove attachments in the edited cell.
*/
export class AttachmentsEditor extends NewBaseEditor {
private _attachmentsTable: TableData;
private _attachmentsTable: MetaTableData<'_grist_Attachments'>;
private _docComm: DocComm;
private _rowIds: MutableObsArray<number>;
@@ -64,15 +64,15 @@ export class AttachmentsEditor extends NewBaseEditor {
// editValue is abused slightly to indicate a 1-based index of the attachment.
const initRowIndex: number|undefined = (options.editValue && parseInt(options.editValue, 0) - 1) || 0;
this._attachmentsTable = docData.getTable('_grist_Attachments')!;
this._attachmentsTable = docData.getMetaTable('_grist_Attachments');
this._docComm = docData.docComm;
this._rowIds = obsArray(Array.isArray(cellValue) ? cellValue.slice(1) as number[] : []);
this._attachments = computedArray(this._rowIds, (val: number): Attachment => {
const fileIdent: string = this._attachmentsTable.getValue(val, 'fileIdent') as string;
const fileIdent: string = this._attachmentsTable.getValue(val, 'fileIdent')!;
const fileType = mimeTypes.lookup(fileIdent) || 'application/octet-stream';
const filename: Observable<string> =
observable(this._attachmentsTable.getValue(val, 'fileName') as string);
observable(this._attachmentsTable.getValue(val, 'fileName')!);
return {
rowId: val,
fileIdent,
@@ -187,7 +187,7 @@ export class AttachmentsEditor extends NewBaseEditor {
private async _renameAttachment(att: Attachment, fileName: string): Promise<void> {
await this._attachmentsTable.sendTableAction(['UpdateRecord', att.rowId, {fileName}]);
// Update the observable, since it's not on its own observing changes.
att.filename.set(this._attachmentsTable.getValue(att.rowId, 'fileName') as string);
att.filename.set(this._attachmentsTable.getValue(att.rowId, 'fileName')!);
}
private _getUrl(fileIdent: string, filename: string, inline?: boolean): string {

View File

@@ -7,7 +7,7 @@ import {cssRow} from 'app/client/ui/RightPanel';
import {colors, vars} from 'app/client/ui2018/cssVars';
import {NewAbstractWidget} from 'app/client/widgets/NewAbstractWidget';
import {encodeQueryParams} from 'app/common/gutil';
import {TableData} from 'app/common/TableData';
import {MetaTableData} from 'app/client/models/TableData';
import {UploadResult} from 'app/common/uploads';
import {extname} from 'path';
@@ -66,7 +66,7 @@ export interface SavingObservable<T> extends ko.Observable<T> {
*/
export class AttachmentsWidget extends NewAbstractWidget {
private _attachmentsTable: TableData;
private _attachmentsTable: MetaTableData<'_grist_Attachments'>;
private _height: SavingObservable<string>;
constructor(field: any) {
@@ -74,7 +74,7 @@ export class AttachmentsWidget extends NewAbstractWidget {
// TODO: the Attachments table currently treated as metadata, and loaded on open,
// but should probably be loaded on demand as it contains user data, which may be large.
this._attachmentsTable = this._getDocData().getTable('_grist_Attachments')!;
this._attachmentsTable = this._getDocData().getMetaTable('_grist_Attachments');
this._height = this.options.prop('height') as SavingObservable<string>;
@@ -122,12 +122,12 @@ export class AttachmentsWidget extends NewAbstractWidget {
}
protected _buildAttachment(value: number, allValues: Computed<number[]>): Element {
const filename: string = this._attachmentsTable.getValue(value, 'fileName') as string;
const fileIdent: string = this._attachmentsTable.getValue(value, 'fileIdent') as string;
const height: number = this._attachmentsTable.getValue(value, 'imageHeight') as number;
const width: number = this._attachmentsTable.getValue(value, 'imageWidth') as number;
const hasPreview: boolean = Boolean(height);
const ratio: number = hasPreview ? (width / height) : 1;
const filename = this._attachmentsTable.getValue(value, 'fileName')!;
const fileIdent = this._attachmentsTable.getValue(value, 'fileIdent')!;
const height = this._attachmentsTable.getValue(value, 'imageHeight')!;
const width = this._attachmentsTable.getValue(value, 'imageWidth')!;
const hasPreview = Boolean(height);
const ratio = hasPreview ? (width / height) : 1;
return attachmentPreview({title: filename}, // Add a filename tooltip to the previews.
dom.style('height', (use) => `${use(this._height)}px`),
@@ -146,7 +146,7 @@ export class AttachmentsWidget extends NewAbstractWidget {
// Returns the attachment download url.
private _getUrl(rowId: number): string {
const ident = this._attachmentsTable.getValue(rowId, 'fileIdent') as string;
const ident = this._attachmentsTable.getValue(rowId, 'fileIdent');
if (!ident) {
return '';
} else {
@@ -154,7 +154,7 @@ export class AttachmentsWidget extends NewAbstractWidget {
return docComm.docUrl('attachment') + '?' + encodeQueryParams({
...docComm.getUrlParams(),
ident,
name: this._attachmentsTable.getValue(rowId, 'fileName') as string
name: this._attachmentsTable.getValue(rowId, 'fileName')
});
}
}