mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +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:
parent
116fb15eda
commit
faec8177ab
@ -38,6 +38,7 @@ import {isHiddenCol} from 'app/common/gristTypes';
|
|||||||
import {isObject} from 'app/common/gutil';
|
import {isObject} from 'app/common/gutil';
|
||||||
import * as roles from 'app/common/roles';
|
import * as roles from 'app/common/roles';
|
||||||
import {SchemaTypes} from 'app/common/schema';
|
import {SchemaTypes} from 'app/common/schema';
|
||||||
|
import {MetaRowRecord} from 'app/common/TableData';
|
||||||
import {ANONYMOUS_USER_EMAIL, EVERYONE_EMAIL, getRealAccess} from 'app/common/UserAPI';
|
import {ANONYMOUS_USER_EMAIL, EVERYONE_EMAIL, getRealAccess} from 'app/common/UserAPI';
|
||||||
import {
|
import {
|
||||||
BaseObservable,
|
BaseObservable,
|
||||||
@ -216,11 +217,11 @@ export class AccessRules extends Disposable {
|
|||||||
// ACL tables (they may have changed by other users). So our changes will win.
|
// ACL tables (they may have changed by other users). So our changes will win.
|
||||||
|
|
||||||
const docData = this._gristDoc.docData;
|
const docData = this._gristDoc.docData;
|
||||||
const resourcesTable = docData.getTable('_grist_ACLResources')!;
|
const resourcesTable = docData.getMetaTable('_grist_ACLResources');
|
||||||
const rulesTable = docData.getTable('_grist_ACLRules')!;
|
const rulesTable = docData.getMetaTable('_grist_ACLRules');
|
||||||
|
|
||||||
// Add/remove resources to have just the ones we need.
|
// Add/remove resources to have just the ones we need.
|
||||||
const newResources: RowRecord[] = flatten(
|
const newResources: MetaRowRecord<'_grist_ACLResources'>[] = flatten(
|
||||||
[{tableId: '*', colIds: '*'}],
|
[{tableId: '*', colIds: '*'}],
|
||||||
this._specialRules.get()?.getResources() || [],
|
this._specialRules.get()?.getResources() || [],
|
||||||
...this._tableRules.get().map(t => t.getResources()))
|
...this._tableRules.get().map(t => t.getResources()))
|
||||||
|
@ -675,7 +675,7 @@ export class GristDoc extends DisposableWithEvents {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public hasGranularAccessRules(): boolean {
|
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
|
// 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
|
// 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.
|
// the use of the deprecated 'permissions' field, and not the new 'permissionsText' field.
|
||||||
|
@ -90,7 +90,7 @@ async function updateViewSections(gristDoc: GristDoc, destViewSections: ViewSect
|
|||||||
const records: RowRecord[] = [];
|
const records: RowRecord[] = [];
|
||||||
for (const srcViewSection of srcViewSections) {
|
for (const srcViewSection of srcViewSections) {
|
||||||
const viewSectionLayoutSpec = patchLayoutSpec(srcViewSection.layoutSpecObj.peek(), fieldsMap);
|
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({
|
records.push({
|
||||||
...record,
|
...record,
|
||||||
layoutSpec: JSON.stringify(viewSectionLayoutSpec),
|
layoutSpec: JSON.stringify(viewSectionLayoutSpec),
|
||||||
@ -126,7 +126,7 @@ async function updateViewFields(gristDoc: GristDoc, destViewSections: ViewSectio
|
|||||||
const srcViewFields: ViewFieldRec[] = srcViewSection!.viewFields.peek().peek();
|
const srcViewFields: ViewFieldRec[] = srcViewSection!.viewFields.peek().peek();
|
||||||
const parentId = destViewSection!.getRowId();
|
const parentId = destViewSection!.getRowId();
|
||||||
for (const field of srcViewFields) {
|
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});
|
fieldsToAdd.push({...record, parentId});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {DocComm} from 'app/client/components/DocComm';
|
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 {ApplyUAOptions, ApplyUAResult} from 'app/common/ActiveDocAPI';
|
||||||
import {CellValue, TableDataAction, UserAction} from 'app/common/DocActions';
|
import {CellValue, TableDataAction, UserAction} from 'app/common/DocActions';
|
||||||
import {DocData as BaseDocData} from 'app/common/DocData';
|
import {DocData as BaseDocData} from 'app/common/DocData';
|
||||||
|
import {SchemaTypes} from 'app/common/schema';
|
||||||
import {ColTypeMap} from 'app/common/TableData';
|
import {ColTypeMap} from 'app/common/TableData';
|
||||||
import * as bluebird from 'bluebird';
|
import * as bluebird from 'bluebird';
|
||||||
import {Emitter} from 'grainjs';
|
import {Emitter} from 'grainjs';
|
||||||
@ -51,6 +52,11 @@ export class DocData extends BaseDocData {
|
|||||||
return super.getTable(tableId) as TableData;
|
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.
|
* Finds up to n most likely target columns for the given values in the document.
|
||||||
*/
|
*/
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
/**
|
/**
|
||||||
* TableData maintains a single table's data.
|
* TableData maintains a single table's data.
|
||||||
*/
|
*/
|
||||||
import { ColumnACIndexes } from 'app/client/models/ColumnACIndexes';
|
import {ColumnACIndexes} from 'app/client/models/ColumnACIndexes';
|
||||||
import { ColumnCache } from 'app/client/models/ColumnCache';
|
import {ColumnCache} from 'app/client/models/ColumnCache';
|
||||||
import { DocData } from 'app/client/models/DocData';
|
import {DocData} from 'app/client/models/DocData';
|
||||||
import { DocAction, ReplaceTableData, TableDataAction, UserAction } from 'app/common/DocActions';
|
import {DocAction, ReplaceTableData, TableDataAction, UserAction} from 'app/common/DocActions';
|
||||||
import { isRaisedException } from 'app/common/gristTypes';
|
import {isRaisedException} from 'app/common/gristTypes';
|
||||||
import { countIf } from 'app/common/gutil';
|
import {countIf} from 'app/common/gutil';
|
||||||
import { TableData as BaseTableData, ColTypeMap } from 'app/common/TableData';
|
import {SchemaTypes} from 'app/common/schema';
|
||||||
import { Emitter } from 'grainjs';
|
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.
|
* 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 tableActionEmitter = new Emitter();
|
||||||
public readonly dataLoadedEmitter = new Emitter();
|
public readonly dataLoadedEmitter = new Emitter();
|
||||||
|
|
||||||
@ -108,3 +109,5 @@ export class TableData extends BaseTableData {
|
|||||||
return applied;
|
return applied;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type MetaTableData<TableId extends keyof SchemaTypes> = MetaTableDataBase<TableId> & TableData;
|
||||||
|
@ -7,7 +7,7 @@ import {dom, LiveIndex, makeLiveIndex, styled} from 'grainjs';
|
|||||||
import {DocComm} from 'app/client/components/DocComm';
|
import {DocComm} from 'app/client/components/DocComm';
|
||||||
import {selectFiles, uploadFiles} from 'app/client/lib/uploads';
|
import {selectFiles, uploadFiles} from 'app/client/lib/uploads';
|
||||||
import {DocData} from 'app/client/models/DocData';
|
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 {basicButton, basicButtonLink, cssButtonGroup} from 'app/client/ui2018/buttons';
|
||||||
import {colors, testId, vars} from 'app/client/ui2018/cssVars';
|
import {colors, testId, vars} from 'app/client/ui2018/cssVars';
|
||||||
import {editableLabel} from 'app/client/ui2018/editableLabel';
|
import {editableLabel} from 'app/client/ui2018/editableLabel';
|
||||||
@ -47,7 +47,7 @@ interface Attachment {
|
|||||||
* download, add or remove attachments in the edited cell.
|
* download, add or remove attachments in the edited cell.
|
||||||
*/
|
*/
|
||||||
export class AttachmentsEditor extends NewBaseEditor {
|
export class AttachmentsEditor extends NewBaseEditor {
|
||||||
private _attachmentsTable: TableData;
|
private _attachmentsTable: MetaTableData<'_grist_Attachments'>;
|
||||||
private _docComm: DocComm;
|
private _docComm: DocComm;
|
||||||
|
|
||||||
private _rowIds: MutableObsArray<number>;
|
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.
|
// 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;
|
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._docComm = docData.docComm;
|
||||||
|
|
||||||
this._rowIds = obsArray(Array.isArray(cellValue) ? cellValue.slice(1) as number[] : []);
|
this._rowIds = obsArray(Array.isArray(cellValue) ? cellValue.slice(1) as number[] : []);
|
||||||
this._attachments = computedArray(this._rowIds, (val: number): Attachment => {
|
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 fileType = mimeTypes.lookup(fileIdent) || 'application/octet-stream';
|
||||||
const filename: Observable<string> =
|
const filename: Observable<string> =
|
||||||
observable(this._attachmentsTable.getValue(val, 'fileName') as string);
|
observable(this._attachmentsTable.getValue(val, 'fileName')!);
|
||||||
return {
|
return {
|
||||||
rowId: val,
|
rowId: val,
|
||||||
fileIdent,
|
fileIdent,
|
||||||
@ -187,7 +187,7 @@ export class AttachmentsEditor extends NewBaseEditor {
|
|||||||
private async _renameAttachment(att: Attachment, fileName: string): Promise<void> {
|
private async _renameAttachment(att: Attachment, fileName: string): Promise<void> {
|
||||||
await this._attachmentsTable.sendTableAction(['UpdateRecord', att.rowId, {fileName}]);
|
await this._attachmentsTable.sendTableAction(['UpdateRecord', att.rowId, {fileName}]);
|
||||||
// Update the observable, since it's not on its own observing changes.
|
// 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 {
|
private _getUrl(fileIdent: string, filename: string, inline?: boolean): string {
|
||||||
|
@ -7,7 +7,7 @@ import {cssRow} from 'app/client/ui/RightPanel';
|
|||||||
import {colors, vars} from 'app/client/ui2018/cssVars';
|
import {colors, vars} from 'app/client/ui2018/cssVars';
|
||||||
import {NewAbstractWidget} from 'app/client/widgets/NewAbstractWidget';
|
import {NewAbstractWidget} from 'app/client/widgets/NewAbstractWidget';
|
||||||
import {encodeQueryParams} from 'app/common/gutil';
|
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 {UploadResult} from 'app/common/uploads';
|
||||||
import {extname} from 'path';
|
import {extname} from 'path';
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ export interface SavingObservable<T> extends ko.Observable<T> {
|
|||||||
*/
|
*/
|
||||||
export class AttachmentsWidget extends NewAbstractWidget {
|
export class AttachmentsWidget extends NewAbstractWidget {
|
||||||
|
|
||||||
private _attachmentsTable: TableData;
|
private _attachmentsTable: MetaTableData<'_grist_Attachments'>;
|
||||||
private _height: SavingObservable<string>;
|
private _height: SavingObservable<string>;
|
||||||
|
|
||||||
constructor(field: any) {
|
constructor(field: any) {
|
||||||
@ -74,7 +74,7 @@ export class AttachmentsWidget extends NewAbstractWidget {
|
|||||||
|
|
||||||
// TODO: the Attachments table currently treated as metadata, and loaded on open,
|
// 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.
|
// 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>;
|
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 {
|
protected _buildAttachment(value: number, allValues: Computed<number[]>): Element {
|
||||||
const filename: string = this._attachmentsTable.getValue(value, 'fileName') as string;
|
const filename = this._attachmentsTable.getValue(value, 'fileName')!;
|
||||||
const fileIdent: string = this._attachmentsTable.getValue(value, 'fileIdent') as string;
|
const fileIdent = this._attachmentsTable.getValue(value, 'fileIdent')!;
|
||||||
const height: number = this._attachmentsTable.getValue(value, 'imageHeight') as number;
|
const height = this._attachmentsTable.getValue(value, 'imageHeight')!;
|
||||||
const width: number = this._attachmentsTable.getValue(value, 'imageWidth') as number;
|
const width = this._attachmentsTable.getValue(value, 'imageWidth')!;
|
||||||
const hasPreview: boolean = Boolean(height);
|
const hasPreview = Boolean(height);
|
||||||
const ratio: number = hasPreview ? (width / height) : 1;
|
const ratio = hasPreview ? (width / height) : 1;
|
||||||
|
|
||||||
return attachmentPreview({title: filename}, // Add a filename tooltip to the previews.
|
return attachmentPreview({title: filename}, // Add a filename tooltip to the previews.
|
||||||
dom.style('height', (use) => `${use(this._height)}px`),
|
dom.style('height', (use) => `${use(this._height)}px`),
|
||||||
@ -146,7 +146,7 @@ export class AttachmentsWidget extends NewAbstractWidget {
|
|||||||
|
|
||||||
// Returns the attachment download url.
|
// Returns the attachment download url.
|
||||||
private _getUrl(rowId: number): string {
|
private _getUrl(rowId: number): string {
|
||||||
const ident = this._attachmentsTable.getValue(rowId, 'fileIdent') as string;
|
const ident = this._attachmentsTable.getValue(rowId, 'fileIdent');
|
||||||
if (!ident) {
|
if (!ident) {
|
||||||
return '';
|
return '';
|
||||||
} else {
|
} else {
|
||||||
@ -154,7 +154,7 @@ export class AttachmentsWidget extends NewAbstractWidget {
|
|||||||
return docComm.docUrl('attachment') + '?' + encodeQueryParams({
|
return docComm.docUrl('attachment') + '?' + encodeQueryParams({
|
||||||
...docComm.getUrlParams(),
|
...docComm.getUrlParams(),
|
||||||
ident,
|
ident,
|
||||||
name: this._attachmentsTable.getValue(rowId, 'fileName') as string
|
name: this._attachmentsTable.getValue(rowId, 'fileName')
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { parsePermissions } from 'app/common/ACLPermissions';
|
import {parsePermissions} from 'app/common/ACLPermissions';
|
||||||
import { ILogger } from 'app/common/BaseAPI';
|
import {ILogger} from 'app/common/BaseAPI';
|
||||||
import { CellValue, RowRecord } from 'app/common/DocActions';
|
import {DocData} from 'app/common/DocData';
|
||||||
import { DocData } from 'app/common/DocData';
|
import {AclMatchFunc, ParsedAclFormula, RulePart, RuleSet, UserAttributeRule} from 'app/common/GranularAccessClause';
|
||||||
import { AclMatchFunc, ParsedAclFormula, RulePart, RuleSet, UserAttributeRule } from 'app/common/GranularAccessClause';
|
import {getSetMapValue} from 'app/common/gutil';
|
||||||
import { getSetMapValue } from 'app/common/gutil';
|
import {MetaRowRecord} from 'app/common/TableData';
|
||||||
import sortBy = require('lodash/sortBy');
|
import sortBy = require('lodash/sortBy');
|
||||||
|
|
||||||
const defaultMatchFunc: AclMatchFunc = () => true;
|
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.
|
* Check that all references to table and column IDs in ACL rules are valid.
|
||||||
*/
|
*/
|
||||||
public checkDocEntities(docData: DocData) {
|
public checkDocEntities(docData: DocData) {
|
||||||
const tablesTable = docData.getTable('_grist_Tables')!;
|
const tablesTable = docData.getMetaTable('_grist_Tables');
|
||||||
const columnsTable = docData.getTable('_grist_Tables_column')!;
|
const columnsTable = docData.getMetaTable('_grist_Tables_column');
|
||||||
|
|
||||||
// Collect valid tableIds and check rules against those.
|
// Collect valid tableIds and check rules against those.
|
||||||
const validTableIds = new Set(tablesTable.getColValues('tableId'));
|
const validTableIds = new Set(tablesTable.getColValues('tableId'));
|
||||||
@ -242,9 +242,9 @@ export class ACLRuleCollection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Collect valid columns, grouped by tableRef (rowId of table record).
|
// 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 validColumns = new Map<number, Set<string>>(); // Map from tableRef to set of colIds.
|
||||||
const colTableRefs = columnsTable.getColValues('parentId')!;
|
const colTableRefs = columnsTable.getColValues('parentId');
|
||||||
for (const [i, colId] of columnsTable.getColValues('colId')!.entries()) {
|
for (const [i, colId] of columnsTable.getColValues('colId').entries()) {
|
||||||
getSetMapValue(validColumns, colTableRefs[i], () => new Set()).add(colId);
|
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.
|
* UserAttributeRules. This is used by both client-side code and server-side.
|
||||||
*/
|
*/
|
||||||
function readAclRules(docData: DocData, {log, compile}: ReadAclOptions): ReadAclResults {
|
function readAclRules(docData: DocData, {log, compile}: ReadAclOptions): ReadAclResults {
|
||||||
const resourcesTable = docData.getTable('_grist_ACLResources')!;
|
const resourcesTable = docData.getMetaTable('_grist_ACLResources');
|
||||||
const rulesTable = docData.getTable('_grist_ACLRules')!;
|
const rulesTable = docData.getMetaTable('_grist_ACLRules');
|
||||||
|
|
||||||
const ruleSets: RuleSet[] = [];
|
const ruleSets: RuleSet[] = [];
|
||||||
const userAttributes: UserAttributeRule[] = [];
|
const userAttributes: UserAttributeRule[] = [];
|
||||||
|
|
||||||
// Group rules by resource first, ordering by rulePos. Each group will become a RuleSet.
|
// 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')) {
|
for (const ruleRecord of sortBy(rulesTable.getRecords(), 'rulePos')) {
|
||||||
getSetMapValue(rulesByResource, ruleRecord.resource, () => []).push(ruleRecord);
|
getSetMapValue(rulesByResource, ruleRecord.resource, () => []).push(ruleRecord);
|
||||||
}
|
}
|
||||||
@ -325,8 +325,8 @@ function readAclRules(docData: DocData, {log, compile}: ReadAclOptions): ReadAcl
|
|||||||
// intentionally ignore and skip.
|
// intentionally ignore and skip.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const tableId = resourceRec.tableId as string;
|
const tableId = resourceRec.tableId;
|
||||||
const colIds = resourceRec.colIds === '*' ? '*' : (resourceRec.colIds as string).split(',');
|
const colIds = resourceRec.colIds === '*' ? '*' : resourceRec.colIds.split(',');
|
||||||
|
|
||||||
const body: RulePart[] = [];
|
const body: RulePart[] = [];
|
||||||
for (const rule of rules) {
|
for (const rule of rules) {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { PartialPermissionSet } from 'app/common/ACLPermissions';
|
import {PartialPermissionSet} from 'app/common/ACLPermissions';
|
||||||
import { CellValue, RowRecord } from 'app/common/DocActions';
|
import {CellValue, RowRecord} from 'app/common/DocActions';
|
||||||
import { Role } from './roles';
|
import {MetaRowRecord} from 'app/common/TableData';
|
||||||
|
import {Role} from './roles';
|
||||||
|
|
||||||
export interface RuleSet {
|
export interface RuleSet {
|
||||||
tableId: '*' | string;
|
tableId: '*' | string;
|
||||||
@ -11,7 +12,7 @@ export interface RuleSet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface RulePart {
|
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;
|
aclFormula: string;
|
||||||
permissions: PartialPermissionSet;
|
permissions: PartialPermissionSet;
|
||||||
permissionsText: string; // The text version of PermissionSet, as stored.
|
permissionsText: string; // The text version of PermissionSet, as stored.
|
||||||
|
@ -13,7 +13,6 @@ import fromPairs = require('lodash/fromPairs');
|
|||||||
|
|
||||||
export interface ColTypeMap { [colId: string]: string; }
|
export interface ColTypeMap { [colId: string]: string; }
|
||||||
|
|
||||||
type RowFunc<T> = (rowId: number) => T;
|
|
||||||
type UIRowFunc<T> = (rowId: UIRowId) => T;
|
type UIRowFunc<T> = (rowId: UIRowId) => T;
|
||||||
|
|
||||||
interface ColData {
|
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
|
* 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.
|
* 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]}));
|
const props = Object.keys(properties).map(p => ({col: this._columns.get(p)!, value: properties[p]}));
|
||||||
if (!props.every((p) => p.col)) {
|
if (!props.every((p) => p.col)) {
|
||||||
return 0;
|
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.
|
* 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);
|
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>> {
|
public getRecords(): Array<MetaRowRecord<TableId>> {
|
||||||
return super.getRecords() as any;
|
return super.getRecords() as any;
|
||||||
}
|
}
|
||||||
@ -498,14 +510,31 @@ export class MetaTableData<TableId extends keyof SchemaTypes> extends TableData
|
|||||||
return super.getRecord(rowId) as any;
|
return super.getRecord(rowId) as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public filterRecords(properties: Partial<MetaRowRecord<TableId>>): Array<MetaRowRecord<TableId>> {
|
||||||
* Same as getRowPropFunc, but I couldn't get a direct override to compile.
|
return super.filterRecords(properties) as any;
|
||||||
*/
|
}
|
||||||
public getMetaRowPropFunc<ColId extends keyof SchemaTypes[TableId]>(
|
|
||||||
|
public findMatchingRowId(properties: Partial<MetaRowRecord<TableId>>): number {
|
||||||
|
return super.findMatchingRowId(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getRowPropFunc<ColId extends MetaColId<TableId>>(
|
||||||
colId: ColId
|
colId: ColId
|
||||||
): RowFunc<SchemaTypes[TableId][ColId]|undefined> {
|
): UIRowFunc<MetaRowRecord<TableId>[ColId]> {
|
||||||
return super.getRowPropFunc(colId as any) as any;
|
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 {
|
function reassignArray<T>(targetArray: T[], sourceArray: T[]): void {
|
||||||
|
@ -4,20 +4,9 @@
|
|||||||
* change events.
|
* 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 {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 {
|
import {
|
||||||
ApplyUAOptions,
|
ApplyUAOptions,
|
||||||
ApplyUAResult,
|
ApplyUAResult,
|
||||||
@ -36,7 +25,6 @@ import {mapGetOrSet, MapWithTTL} from 'app/common/AsyncCreate';
|
|||||||
import {
|
import {
|
||||||
CellValue,
|
CellValue,
|
||||||
DocAction,
|
DocAction,
|
||||||
RowRecord,
|
|
||||||
TableDataAction,
|
TableDataAction,
|
||||||
TableRecordValue,
|
TableRecordValue,
|
||||||
toTableDataAction,
|
toTableDataAction,
|
||||||
@ -50,6 +38,7 @@ import {FormulaProperties, getFormulaProperties} from 'app/common/GranularAccess
|
|||||||
import {byteString, countIf, safeJsonParse} from 'app/common/gutil';
|
import {byteString, countIf, safeJsonParse} from 'app/common/gutil';
|
||||||
import {InactivityTimer} from 'app/common/InactivityTimer';
|
import {InactivityTimer} from 'app/common/InactivityTimer';
|
||||||
import {schema, SCHEMA_VERSION} from 'app/common/schema';
|
import {schema, SCHEMA_VERSION} from 'app/common/schema';
|
||||||
|
import {MetaRowRecord} from 'app/common/TableData';
|
||||||
import {FetchUrlOptions, UploadResult} from 'app/common/uploads';
|
import {FetchUrlOptions, UploadResult} from 'app/common/uploads';
|
||||||
import {DocReplacementOptions, DocState, DocStateComparison} from 'app/common/UserAPI';
|
import {DocReplacementOptions, DocState, DocStateComparison} from 'app/common/UserAPI';
|
||||||
import {ParseOptions} from 'app/plugin/FileParserAPI';
|
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 {LogMethods} from "app/server/lib/LogMethods";
|
||||||
import {shortDesc} from 'app/server/lib/shortDesc';
|
import {shortDesc} from 'app/server/lib/shortDesc';
|
||||||
import {TableMetadataLoader} from 'app/server/lib/TableMetadataLoader';
|
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 {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 {ActionHistory} from './ActionHistory';
|
||||||
import {ActionHistoryImpl} from './ActionHistoryImpl';
|
import {ActionHistoryImpl} from './ActionHistoryImpl';
|
||||||
@ -89,8 +88,8 @@ import {findOrAddAllEnvelope, Sharing} from './Sharing';
|
|||||||
import cloneDeep = require('lodash/cloneDeep');
|
import cloneDeep = require('lodash/cloneDeep');
|
||||||
import flatten = require('lodash/flatten');
|
import flatten = require('lodash/flatten');
|
||||||
import remove = require('lodash/remove');
|
import remove = require('lodash/remove');
|
||||||
import zipObject = require('lodash/zipObject');
|
|
||||||
import without = require('lodash/without');
|
import without = require('lodash/without');
|
||||||
|
import zipObject = require('lodash/zipObject');
|
||||||
|
|
||||||
bluebird.promisifyAll(tmp);
|
bluebird.promisifyAll(tmp);
|
||||||
|
|
||||||
@ -608,14 +607,14 @@ export class ActiveDoc extends EventEmitter {
|
|||||||
* Returns the record from _grist_Attachments table for the given attachment ID,
|
* Returns the record from _grist_Attachments table for the given attachment ID,
|
||||||
* or throws an error if not found.
|
* 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().
|
// docData should always be available after loadDoc() or createDoc().
|
||||||
if (!this.docData) {
|
if (!this.docData) {
|
||||||
throw new Error("No doc data");
|
throw new Error("No doc data");
|
||||||
}
|
}
|
||||||
// Parse strings into numbers to make more convenient to call from route handlers.
|
// Parse strings into numbers to make more convenient to call from route handlers.
|
||||||
const attachmentId: number = (typeof attId === 'string') ? parseInt(attId, 10) : attId;
|
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) {
|
if (!attRecord) {
|
||||||
throw new ApiError(`Attachment not found: ${attId}`, 404);
|
throw new ApiError(`Attachment not found: ${attId}`, 404);
|
||||||
}
|
}
|
||||||
@ -1116,14 +1115,14 @@ export class ActiveDoc extends EventEmitter {
|
|||||||
throw new Error('Cannot list ACL resources');
|
throw new Error('Cannot list ACL resources');
|
||||||
}
|
}
|
||||||
const result: {[tableId: string]: string[]} = {};
|
const result: {[tableId: string]: string[]} = {};
|
||||||
const tables = this.docData.getTable('_grist_Tables')!;
|
const tables = this.docData.getMetaTable('_grist_Tables');
|
||||||
for (const tableId of tables.getColValues('tableId')!) {
|
for (const tableId of tables.getColValues('tableId')) {
|
||||||
result[tableId as string] = ['id'];
|
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()) {
|
for (const col of columns.getRecords()) {
|
||||||
const tableId = tables.getValue(col.parentId as number, 'tableId');
|
const tableId = tables.getValue(col.parentId, 'tableId')!;
|
||||||
result[tableId as string].push(col.colId as string);
|
result[tableId].push(col.colId);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -33,8 +33,8 @@ class GristDocAPIImpl implements GristDocAPI {
|
|||||||
public async getDocName() { return this._activeDoc.docName; }
|
public async getDocName() { return this._activeDoc.docName; }
|
||||||
|
|
||||||
public async listTables(): Promise<string[]> {
|
public async listTables(): Promise<string[]> {
|
||||||
const table = this._activeDoc.docData!.getTable('_grist_Tables')!;
|
const table = this._activeDoc.docData!.getMetaTable('_grist_Tables');
|
||||||
return (table.getColValues('tableId') as string[])
|
return table.getColValues('tableId')
|
||||||
.filter(id => !id.startsWith("GristSummary_")).sort();
|
.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.
|
// Iterate through all formulas, adding joins and selects as we go.
|
||||||
if (onDemandFormulas) {
|
if (onDemandFormulas) {
|
||||||
// Look up the main table for the query.
|
// Look up the main table for the query.
|
||||||
const tables = docData.getTable('_grist_Tables')!;
|
const tables = docData.getMetaTable('_grist_Tables');
|
||||||
const columns = docData.getTable('_grist_Tables_column')!;
|
const columns = docData.getMetaTable('_grist_Tables_column');
|
||||||
const tableRef = tables.findRow('tableId', query.tableId);
|
const tableRef = tables.findRow('tableId', query.tableId);
|
||||||
if (!tableRef) { throw new ApiError('table not found', 404); }
|
if (!tableRef) { throw new ApiError('table not found', 404); }
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ export function expandQuery(iquery: ServerQuery, docData: DocData, onDemandFormu
|
|||||||
let error = "";
|
let error = "";
|
||||||
if (formula.kind === 'foreignColumn') {
|
if (formula.kind === 'foreignColumn') {
|
||||||
const altTableId = references.get(formula.refColId);
|
const altTableId = references.get(formula.refColId);
|
||||||
const altTableRef = tables.findRow('tableId', altTableId);
|
const altTableRef = tables.findRow('tableId', altTableId!);
|
||||||
if (altTableId && altTableRef) {
|
if (altTableId && altTableRef) {
|
||||||
const altColumn = columns.filterRecords({parentId: altTableRef, isFormula: false, colId: formula.colId});
|
const altColumn = columns.filterRecords({parentId: altTableRef, isFormula: false, colId: formula.colId});
|
||||||
// TODO: deal with a formula column in the other table.
|
// TODO: deal with a formula column in the other table.
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import {ApiError} from 'app/common/ApiError';
|
import {ApiError} from 'app/common/ApiError';
|
||||||
import {buildColFilter} from 'app/common/ColumnFilterFunc';
|
import {buildColFilter} from 'app/common/ColumnFilterFunc';
|
||||||
import {RowRecord} from 'app/common/DocActions';
|
|
||||||
import {DocData} from 'app/common/DocData';
|
import {DocData} from 'app/common/DocData';
|
||||||
import {DocumentSettings} from 'app/common/DocumentSettings';
|
import {DocumentSettings} from 'app/common/DocumentSettings';
|
||||||
import * as gristTypes from 'app/common/gristTypes';
|
import * as gristTypes from 'app/common/gristTypes';
|
||||||
@ -9,7 +8,7 @@ import {buildRowFilter} from 'app/common/RowFilterFunc';
|
|||||||
import {SchemaTypes} from 'app/common/schema';
|
import {SchemaTypes} from 'app/common/schema';
|
||||||
import {SortFunc} from 'app/common/SortFunc';
|
import {SortFunc} from 'app/common/SortFunc';
|
||||||
import {Sort} from 'app/common/SortSpec';
|
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 {ActiveDoc} from 'app/server/lib/ActiveDoc';
|
||||||
import {RequestWithLogin} from 'app/server/lib/Authorizer';
|
import {RequestWithLogin} from 'app/server/lib/Authorizer';
|
||||||
import {docSessionFromRequest} from 'app/server/lib/DocSession';
|
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.
|
// Helper to for getting table from docData.
|
||||||
const safeTable = (docData: DocData, name: keyof SchemaTypes) => safe(docData.getTable(name),
|
function safeTable<TableId extends keyof SchemaTypes>(docData: DocData, name: TableId) {
|
||||||
`No table '${name}' in document with id ${docData}`);
|
return safe(docData.getMetaTable(name), `No table '${name}' in document with id ${docData}`);
|
||||||
|
}
|
||||||
|
|
||||||
// Helper for getting record safe
|
// Helper for getting record safe
|
||||||
const safeRecord = (table: TableData, id: number) => safe(table.getRecord(id),
|
function safeRecord<TableId extends keyof SchemaTypes>(table: MetaTableData<TableId>, id: number) {
|
||||||
`No record ${id} in table ${table.tableId}`);
|
return safe(table.getRecord(id), `No record ${id} in table ${table.tableId}`);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds export for all raw tables that are in doc.
|
* Builds export for all raw tables that are in doc.
|
||||||
@ -138,9 +139,9 @@ export async function exportTable(
|
|||||||
req: express.Request): Promise<ExportData> {
|
req: express.Request): Promise<ExportData> {
|
||||||
const docData = safe(activeDoc.docData, "No docData in active document");
|
const docData = safe(activeDoc.docData, "No docData in active document");
|
||||||
const tables = safeTable(docData, '_grist_Tables');
|
const tables = safeTable(docData, '_grist_Tables');
|
||||||
const table = safeRecord(tables, tableId) as GristTables;
|
const table = safeRecord(tables, tableId);
|
||||||
const tableColumns = (safeTable(docData, '_grist_Tables_column')
|
const tableColumns = safeTable(docData, '_grist_Tables_column')
|
||||||
.getRecords() as GristTablesColumn[])
|
.getRecords()
|
||||||
// remove manual sort column
|
// remove manual sort column
|
||||||
.filter(col => col.colId !== gristTypes.MANUALSORT);
|
.filter(col => col.colId !== gristTypes.MANUALSORT);
|
||||||
// Produce a column description matching what user will see / expect to export
|
// Produce a column description matching what user will see / expect to export
|
||||||
@ -183,11 +184,11 @@ export async function exportTable(
|
|||||||
if (table.primaryViewId) {
|
if (table.primaryViewId) {
|
||||||
const viewId = table.primaryViewId;
|
const viewId = table.primaryViewId;
|
||||||
const views = safeTable(docData, '_grist_Views');
|
const views = safeTable(docData, '_grist_Views');
|
||||||
const view = safeRecord(views, viewId) as GristView;
|
const view = safeRecord(views, viewId);
|
||||||
tableName = view.name;
|
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, {});
|
const docSettings = gutil.safeJsonParse(docInfo.documentSettings, {});
|
||||||
return {
|
return {
|
||||||
tableName,
|
tableName,
|
||||||
@ -211,15 +212,15 @@ export async function exportSection(
|
|||||||
|
|
||||||
const docData = safe(activeDoc.docData, "No docData in active document");
|
const docData = safe(activeDoc.docData, "No docData in active document");
|
||||||
const viewSections = safeTable(docData, '_grist_Views_section');
|
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 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')
|
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 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')
|
const savedFilters = safeTable(docData, '_grist_Filters')
|
||||||
.filterRecords({ viewSectionRef: viewSection.id }) as GristFilter[];
|
.filterRecords({viewSectionRef: viewSection.id});
|
||||||
|
|
||||||
const tableColsById = _.indexBy(columns, 'id');
|
const tableColsById = _.indexBy(columns, 'id');
|
||||||
const fieldsByColRef = _.indexBy(fields, 'colRef');
|
const fieldsByColRef = _.indexBy(fields, 'colRef');
|
||||||
@ -279,7 +280,7 @@ export async function exportSection(
|
|||||||
// filter rows numbers
|
// filter rows numbers
|
||||||
rowIds = rowIds.filter(rowFilter);
|
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, {});
|
const docSettings = gutil.safeJsonParse(docInfo.documentSettings, {});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -292,17 +293,8 @@ export async function exportSection(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type helpers for types used in this export
|
type GristViewsSectionField = MetaRowRecord<'_grist_Views_section_field'>
|
||||||
type RowModel<TName extends keyof SchemaTypes> = RowRecord & {
|
type GristTablesColumn = MetaRowRecord<'_grist_Tables_column'>
|
||||||
[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 for filters passed from the client
|
// Type for filters passed from the client
|
||||||
export interface Filter { colRef: number, filter: string }
|
export interface Filter { colRef: number, filter: string }
|
||||||
|
@ -74,7 +74,7 @@ export async function makeCSVFromTable(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Look up the table to make a CSV from.
|
// 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);
|
const tableRef = tables.findRow('tableId', tableId);
|
||||||
|
|
||||||
if (tableRef === 0) {
|
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.
|
// Create a tmpDocData with just the tables we care about, then update docActions to it.
|
||||||
const tmpDocData: DocData = new DocData(
|
const tmpDocData: DocData = new DocData(
|
||||||
(tableId) => { throw new Error("Unexpected DocData fetch"); }, {
|
(tableId) => { throw new Error("Unexpected DocData fetch"); }, {
|
||||||
_grist_Tables: this._docData.getTable('_grist_Tables')!.getTableDataAction(),
|
_grist_Tables: this._docData.getMetaTable('_grist_Tables').getTableDataAction(),
|
||||||
_grist_Tables_column: this._docData.getTable('_grist_Tables_column')!.getTableDataAction(),
|
_grist_Tables_column: this._docData.getMetaTable('_grist_Tables_column').getTableDataAction(),
|
||||||
_grist_ACLResources: this._docData.getTable('_grist_ACLResources')!.getTableDataAction(),
|
_grist_ACLResources: this._docData.getMetaTable('_grist_ACLResources').getTableDataAction(),
|
||||||
_grist_ACLRules: this._docData.getTable('_grist_ACLRules')!.getTableDataAction(),
|
_grist_ACLRules: this._docData.getMetaTable('_grist_ACLRules').getTableDataAction(),
|
||||||
});
|
});
|
||||||
for (const da of docActions) {
|
for (const da of docActions) {
|
||||||
tmpDocData.receiveAction(da);
|
tmpDocData.receiveAction(da);
|
||||||
|
@ -22,8 +22,8 @@ export interface OnDemandStorage {
|
|||||||
*/
|
*/
|
||||||
export class OnDemandActions {
|
export class OnDemandActions {
|
||||||
|
|
||||||
private _tablesMeta: TableData = this._docData.getTable('_grist_Tables')!;
|
private _tablesMeta: TableData = this._docData.getMetaTable('_grist_Tables');
|
||||||
private _columnsMeta: TableData = this._docData.getTable('_grist_Tables_column')!;
|
private _columnsMeta: TableData = this._docData.getMetaTable('_grist_Tables_column');
|
||||||
|
|
||||||
constructor(private _storage: OnDemandStorage, private _docData: DocData) {}
|
constructor(private _storage: OnDemandStorage, private _docData: DocData) {}
|
||||||
|
|
||||||
|
@ -138,8 +138,8 @@ export class DocTriggers {
|
|||||||
} // Happens on doc creation while processing InitNewDoc action.
|
} // Happens on doc creation while processing InitNewDoc action.
|
||||||
|
|
||||||
const triggersTable = docData.getMetaTable("_grist_Triggers");
|
const triggersTable = docData.getMetaTable("_grist_Triggers");
|
||||||
const getTableId = docData.getMetaTable("_grist_Tables").getMetaRowPropFunc("tableId");
|
const getTableId = docData.getMetaTable("_grist_Tables").getRowPropFunc("tableId");
|
||||||
this._getColId = docData.getMetaTable("_grist_Tables_column").getMetaRowPropFunc("colId");
|
this._getColId = docData.getMetaTable("_grist_Tables_column").getRowPropFunc("colId");
|
||||||
|
|
||||||
const triggersByTableRef = _.groupBy(triggersTable.getRecords(), "tableRef");
|
const triggersByTableRef = _.groupBy(triggersTable.getRecords(), "tableRef");
|
||||||
const triggersByTableId: Array<[string, Trigger[]]> = [];
|
const triggersByTableId: Array<[string, Trigger[]]> = [];
|
||||||
|
Loading…
Reference in New Issue
Block a user