mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) Options for plugin API functions which fetch data from the selected table or record
Summary: Adds a new interface `FetchSelectedOptions` with three keys (including the preexisting `keepEncoded`) and adds/updates an optional `options: FetchSelectedOptions` to six related functions which fetch data from the selected table or record. The `keepEncoded` and `format` options have different default values for different methods for backwards compatibility, but otherwise the different methods now have much more similar behaviour. The new `includeColumns` option allows fetching all columns which was previously only possible using `docApi.fetchTable` (which wasn't always a great alternative) but this requires full access to avoid exposing more data than before and violating user expectations. Eventually, similar options should be added to `docApi.fetchTable` to make the API even more consistent. Discussion: https://grist.slack.com/archives/C0234CPPXPA/p1696510548994899 Test Plan: Added a new nbrowser test with a corresponding fixture site and document, showing how the functions have different default option values but are all configurable now. Reviewers: georgegevoian Reviewed By: georgegevoian Differential Revision: https://phab.getgrist.com/D4077
This commit is contained in:
@@ -239,7 +239,7 @@ export class CustomView extends Disposable {
|
||||
GristDocAPIImpl.defaultAccess);
|
||||
frame.exposeAPI(
|
||||
"GristView",
|
||||
new GristViewImpl(view), new MinimumLevel(AccessLevel.read_table));
|
||||
new GristViewImpl(view, access), new MinimumLevel(AccessLevel.read_table));
|
||||
frame.exposeAPI(
|
||||
"CustomSectionAPI",
|
||||
new CustomSectionAPIImpl(
|
||||
|
||||
@@ -11,8 +11,10 @@ import {DisposableWithEvents} from 'app/common/DisposableWithEvents';
|
||||
import {BulkColValues, fromTableDataAction, RowRecord} from 'app/common/DocActions';
|
||||
import {extractInfoFromColType, reencodeAsAny} from 'app/common/gristTypes';
|
||||
import {Theme} from 'app/common/ThemePrefs';
|
||||
import {AccessTokenOptions, CursorPos, CustomSectionAPI, GristDocAPI, GristView,
|
||||
InteractionOptionsRequest, WidgetAPI, WidgetColumnMap} from 'app/plugin/grist-plugin-api';
|
||||
import {
|
||||
AccessTokenOptions, CursorPos, CustomSectionAPI, FetchSelectedOptions, GristDocAPI, GristView,
|
||||
InteractionOptionsRequest, WidgetAPI, WidgetColumnMap
|
||||
} from 'app/plugin/grist-plugin-api';
|
||||
import {MsgType, Rpc} from 'grain-rpc';
|
||||
import {Computed, Disposable, dom, Observable} from 'grainjs';
|
||||
import noop = require('lodash/noop');
|
||||
@@ -374,13 +376,14 @@ export class GristDocAPIImpl implements GristDocAPI {
|
||||
* GristViewAPI implemented over BaseView.
|
||||
*/
|
||||
export class GristViewImpl implements GristView {
|
||||
constructor(private _baseView: BaseView) {}
|
||||
constructor(private _baseView: BaseView, private _access: AccessLevel) {
|
||||
}
|
||||
|
||||
public async fetchSelectedTable(): Promise<any> {
|
||||
public async fetchSelectedTable(options: FetchSelectedOptions = {}): Promise<any> {
|
||||
// If widget has a custom columns mapping, we will ignore hidden columns section.
|
||||
// Hidden/Visible columns will eventually reflect what is available, but this operation
|
||||
// is not instant - and widget can receive rows with fields that are not in the mapping.
|
||||
const columns: ColumnRec[] = this._visibleColumns();
|
||||
const columns: ColumnRec[] = this._visibleColumns(options);
|
||||
const rowIds = this._baseView.sortedRows.getKoArray().peek().filter(id => id != 'new');
|
||||
const data: BulkColValues = {};
|
||||
for (const column of columns) {
|
||||
@@ -394,13 +397,13 @@ export class GristViewImpl implements GristView {
|
||||
return data;
|
||||
}
|
||||
|
||||
public async fetchSelectedRecord(rowId: number): Promise<any> {
|
||||
public async fetchSelectedRecord(rowId: number, options: FetchSelectedOptions = {}): Promise<any> {
|
||||
// Prepare an object containing the fields available to the view
|
||||
// for the specified row. A RECORD()-generated rendering would be
|
||||
// more useful. but the data engine needs to know what information
|
||||
// the custom view depends on, so we shouldn't volunteer any untracked
|
||||
// information here.
|
||||
const columns: ColumnRec[] = this._visibleColumns();
|
||||
const columns: ColumnRec[] = this._visibleColumns(options);
|
||||
const data: RowRecord = {id: rowId};
|
||||
for (const column of columns) {
|
||||
const colId: string = column.displayColModel.peek().colId.peek();
|
||||
@@ -434,16 +437,32 @@ export class GristViewImpl implements GristView {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
private _visibleColumns() {
|
||||
private _visibleColumns(options: FetchSelectedOptions): ColumnRec[] {
|
||||
const columns: ColumnRec[] = this._baseView.viewSection.columns.peek();
|
||||
const hiddenCols = this._baseView.viewSection.hiddenColumns.peek().map(c => c.id.peek());
|
||||
const mappings = this._baseView.viewSection.mappedColumns.peek();
|
||||
const mappedColumns = new Set(flatMap(Object.values(mappings || {})));
|
||||
const notHidden = (col: ColumnRec) => !hiddenCols.includes(col.id.peek());
|
||||
const mapped = (col: ColumnRec) => mappings && mappedColumns.has(col.colId.peek());
|
||||
// If columns are mapped, return only those that are mapped.
|
||||
// Otherwise return all not hidden columns;
|
||||
return mappings ? columns.filter(mapped) : columns.filter(notHidden);
|
||||
const mappings = this._baseView.viewSection.mappedColumns.peek();
|
||||
if (mappings) {
|
||||
const mappedColumns = new Set(flatMap(Object.values(mappings)));
|
||||
const mapped = (col: ColumnRec) => mappedColumns.has(col.colId.peek());
|
||||
return columns.filter(mapped);
|
||||
} else if (options.includeColumns === 'shown' || !options.includeColumns) {
|
||||
// Return columns that have been shown by the user, i.e. have a corresponding view field.
|
||||
const hiddenCols = this._baseView.viewSection.hiddenColumns.peek().map(c => c.id.peek());
|
||||
const notHidden = (col: ColumnRec) => !hiddenCols.includes(col.id.peek());
|
||||
return columns.filter(notHidden);
|
||||
}
|
||||
// These options are newer and expose more data than the user may have intended,
|
||||
// so they require full access.
|
||||
if (this._access !== AccessLevel.full) {
|
||||
throwError(this._access);
|
||||
}
|
||||
if (options.includeColumns === 'normal') {
|
||||
// Return all 'normal' columns of the table, regardless of whether the user has shown them.
|
||||
return columns;
|
||||
} else {
|
||||
// Return *all* columns, including special invisible columns like manualSort.
|
||||
return this._baseView.viewSection.table.peek().columns.peek().all();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user