From 3d3a5e334a4b2e105cdaec169e1356a9b3da3633 Mon Sep 17 00:00:00 2001 From: George Gevoian Date: Tue, 24 May 2022 13:22:41 -0700 Subject: [PATCH] (core) Update Plugin API documentation Summary: Updates to Plugin API documentation. Test Plan: Tested manually in grist-help. Reviewers: jarek Reviewed By: jarek Subscribers: jarek Differential Revision: https://phab.getgrist.com/D3447 --- app/plugin/GristAPI.ts | 23 +++++++------ app/plugin/GristData-ti.ts | 6 ++++ app/plugin/GristData.ts | 62 ++++++++++++++++++++++++++++++++++ app/plugin/GristTable.ts | 13 +++---- app/plugin/TableOperations.ts | 20 ++++++----- app/plugin/grist-plugin-api.ts | 6 ++-- 6 files changed, 100 insertions(+), 30 deletions(-) diff --git a/app/plugin/GristAPI.ts b/app/plugin/GristAPI.ts index dc604ac0..8c6c0d23 100644 --- a/app/plugin/GristAPI.ts +++ b/app/plugin/GristAPI.ts @@ -69,7 +69,7 @@ export interface GristAPI { } /** - * Allows getting information from and nteracting with the Grist document to which a plugin or widget is attached. + * Allows getting information from and interacting with the Grist document to which a plugin or widget is attached. */ export interface GristDocAPI { /** @@ -83,19 +83,20 @@ export interface GristDocAPI { listTables(): Promise; /** - * Returns a complete table of data in the format {colId: [values]}, including the 'id' column. - * Do not modify the returned arrays in-place, especially if used directly (not over RPC). - * TODO: return type is Promise{[colId: string]: CellValue[]}> but cannot be specified because - * ts-interface-builder does not properly support index-signature. + * Returns a complete table of data as [[RowRecords]], including the + * 'id' column. Do not modify the returned arrays in-place, especially if used + * directly (not over RPC). */ fetchTable(tableId: string): Promise; + // TODO: return type is Promise{[colId: string]: CellValue[]}> but cannot be specified + // because ts-interface-builder does not properly support index-signature. /** * Applies an array of user actions. - * TODO: return type should be Promise, but this requires importing modules from - * `app/common` which is not currently supported by the build. */ applyUserActions(actions: any[][], options?: any): Promise; + // TODO: return type should be Promise, but this requires importing + // modules from `app/common` which is not currently supported by the build. } /** @@ -104,15 +105,17 @@ export interface GristDocAPI { export interface GristView { /** * Like [[GristDocAPI.fetchTable]], but gets data for the custom section specifically, if there is any. - * TODO: return type is Promise{[colId: string]: CellValue[]}> but cannot be specified because - * ts-interface-builder does not properly support index-signature. */ fetchSelectedTable(): Promise; + // TODO: return type is Promise{[colId: string]: CellValue[]}> but cannot be specified + // because ts-interface-builder does not properly support index-signature. /** - * Similar TODO to `fetchSelectedTable()` for return type. + * Fetches selected record by its `rowId`. */ fetchSelectedRecord(rowId: number): Promise; + // TODO: return type is Promise{[colId: string]: CellValue}> but cannot be specified + // because ts-interface-builder does not properly support index-signature. /** * Allow custom widget to be listed as a possible source for linking with SELECT BY. diff --git a/app/plugin/GristData-ti.ts b/app/plugin/GristData-ti.ts index a5be53fa..dbfa40b7 100644 --- a/app/plugin/GristData-ti.ts +++ b/app/plugin/GristData-ti.ts @@ -31,6 +31,11 @@ export const RowRecord = t.iface([], { [t.indexKey]: "CellValue", }); +export const RowRecords = t.iface([], { + "id": t.array("number"), + [t.indexKey]: t.array("CellValue"), +}); + export const GristType = t.union(t.lit('Any'), t.lit('Attachments'), t.lit('Blob'), t.lit('Bool'), t.lit('Choice'), t.lit('ChoiceList'), t.lit('Date'), t.lit('DateTime'), t.lit('Id'), t.lit('Int'), t.lit('ManualSortPos'), t.lit('Numeric'), t.lit('PositionNumber'), t.lit('Ref'), t.lit('RefList'), t.lit('Text')); const exportedTypeSuite: t.ITypeSuite = { @@ -38,6 +43,7 @@ const exportedTypeSuite: t.ITypeSuite = { CellValue, BulkColValues, RowRecord, + RowRecords, GristType, }; export default exportedTypeSuite; diff --git a/app/plugin/GristData.ts b/app/plugin/GristData.ts index 7559a505..35f2b006 100644 --- a/app/plugin/GristData.ts +++ b/app/plugin/GristData.ts @@ -18,11 +18,73 @@ export const enum GristObjCode { export type CellValue = number|string|boolean|null|[GristObjCode, ...unknown[]]; export interface BulkColValues { [colId: string]: CellValue[]; } +/** + * Map of column ids to `CellValue`s. + * + * ### CellValue + * + * Each `CellValue` may either be a primitive (e.g. `true`, `123`, `"hello"`, `null`) + * or a tuple (JavaScript Array) representing a Grist object. The first element of the tuple + * is a string character representing the object code. For example, `["L", "foo", "bar"]` + * is a `CellValue` of a Choice List column, where `"L"` is the type, and `"foo"` and + * `"bar"` are the choices. + * + * ### Grist Object Types + * + * | Code | Type | + * | ---- | -------------- | + * | L | List | + * | l | LookUp | + * | O | Dict | + * | D | DateTime | + * | d | Date | + * | C | Censored | + * | R | Reference | + * | r | ReferenceList | + * | E | Exception | + * | P | Pending | + * | U | Unmarshallable | + * | V | Version | + */ export interface RowRecord { id: number; [colId: string]: CellValue; } +/** + * Map of column ids to `CellValue` arrays, where array indexes correspond to + * rows. + * + * ### CellValue + * + * Each `CellValue` may either be a primitive (e.g. `true`, `123`, `"hello"`, `null`) + * or a tuple (JavaScript Array) representing a Grist object. The first element of the tuple + * is a string character representing the object code. For example, `["L", "foo", "bar"]` + * is a `CellValue` of a Choice List column, where `"L"` is the type, and `"foo"` and + * `"bar"` are the choices. + * + * ### Grist Object Types + * + * | Code | Type | + * | ---- | -------------- | + * | L | List | + * | l | LookUp | + * | O | Dict | + * | D | DateTime | + * | d | Date | + * | C | Censored | + * | R | Reference | + * | r | ReferenceList | + * | E | Exception | + * | P | Pending | + * | U | Unmarshallable | + * | V | Version | + */ +export interface RowRecords { + id: number[]; + [colId: string]: CellValue[]; +} + export type GristType = 'Any' | 'Attachments' | 'Blob' | 'Bool' | 'Choice' | 'ChoiceList' | 'Date' | 'DateTime' | 'Id' | 'Int' | 'ManualSortPos' | 'Numeric' | 'PositionNumber' | 'Ref' | 'RefList' | 'Text'; diff --git a/app/plugin/GristTable.ts b/app/plugin/GristTable.ts index f871b2b8..d6538497 100644 --- a/app/plugin/GristTable.ts +++ b/app/plugin/GristTable.ts @@ -3,14 +3,13 @@ */ /** - * - * Metadata and data for a table. This is documenting what is currently returned by the - * core plugins. Could be worth reconciling with: - * https://phab.getgrist.com/w/grist_data_format/ - * Capitalization is python-style. - * + * Metadata and data for a table. */ export interface GristTable { + // This is documenting what is currently returned by the core plugins. Capitalization + // is python-style. + // + // TODO: could be worth reconciling with: https://phab.getgrist.com/w/grist_data_format/. table_name: string | null; // currently allow names to be null column_metadata: GristColumn[]; table_data: any[][]; @@ -21,9 +20,7 @@ export interface GristTables { } /** - * * Metadata about a single column. - * */ export interface GristColumn { id: string; diff --git a/app/plugin/TableOperations.ts b/app/plugin/TableOperations.ts index 9ca64d09..065a7177 100644 --- a/app/plugin/TableOperations.ts +++ b/app/plugin/TableOperations.ts @@ -37,20 +37,22 @@ export interface TableOperations { /** * General options for table operations. - * By default, string field values will be parsed based on the column type. - * This can be disabled. */ export interface OpOptions { - parseStrings?: boolean; /** whether to parse strings based on the column type. */ + /** Whether to parse strings based on the column type. Defaults to true. */ + parseStrings?: boolean; } /** - * Extra options for upserts. By default, add and update are true, - * onMany is first, and allowEmptyRequire is false. + * Extra options for upserts. */ export interface UpsertOptions extends OpOptions { - add?: boolean; /** permit inserting a record */ - update?: boolean; /** permit updating a record */ - onMany?: 'none' | 'first' | 'all'; /** whether to update none, one, or all matching records */ - allowEmptyRequire?: boolean; /** allow "wildcard" operation */ + /** Permit inserting a record. Defaults to true. */ + add?: boolean; + /** Permit updating a record. Defaults to true. */ + update?: boolean; + /** Whether to update none, one, or all matching records. Defaults to "first". */ + onMany?: 'none' | 'first' | 'all'; + /** Allow "wildcard" operation. Defaults to false. */ + allowEmptyRequire?: boolean; } diff --git a/app/plugin/grist-plugin-api.ts b/app/plugin/grist-plugin-api.ts index d52c662d..43709648 100644 --- a/app/plugin/grist-plugin-api.ts +++ b/app/plugin/grist-plugin-api.ts @@ -33,6 +33,7 @@ import { WidgetAPI } from './WidgetAPI'; export * from './TypeCheckers'; export * from './FileParserAPI'; export * from './GristAPI'; +export * from './GristData'; export * from './GristTable'; export * from './ImportSourceAPI'; export * from './StorageAPI'; @@ -78,7 +79,7 @@ export const setSelectedRows = viewApi.setSelectedRows; * but decoding data by default, replacing e.g. ['D', timestamp] with * a moment date. Option `keepEncoded` skips the decoding step. */ -export async function fetchSelectedTable(options: {keepEncoded?: boolean} = {}) { + export async function fetchSelectedTable(options: {keepEncoded?: boolean} = {}) { const table = await viewApi.fetchSelectedTable(); return options.keepEncoded ? table : mapValues(table, (col) => col.map(decodeObject)); @@ -305,10 +306,9 @@ export function mapColumnNamesBack(data: any, options?: { * by some value within the row potentially changing. Handler may * in the future be called with null if the cursor moves away from * any row. - * TODO: currently this will be called even if the content of a different row - * changes. */ export function onRecord(callback: (data: RowRecord | null, mappings: WidgetColumnMap | null) => unknown) { + // TODO: currently this will be called even if the content of a different row changes. on('message', async function(msg) { if (!msg.tableId || !msg.rowId || msg.rowId === 'new') { return; } const rec = await docApi.fetchSelectedRecord(msg.rowId);