mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
(core) Use GristObjCode in CellValue
Summary: Makes type checking a bit stronger Test Plan: it just has to compile Reviewers: jarek Reviewed By: jarek Differential Revision: https://phab.getgrist.com/D3065
This commit is contained in:
parent
62db263d1f
commit
a64fb105e3
@ -8,7 +8,7 @@ import { TableData } from 'app/client/models/TableData';
|
|||||||
import { createEmptyTableDelta, getTableIdAfter, getTableIdBefore, TableDelta } from 'app/common/ActionSummary';
|
import { createEmptyTableDelta, getTableIdAfter, getTableIdBefore, TableDelta } from 'app/common/ActionSummary';
|
||||||
import { DisposableWithEvents } from 'app/common/DisposableWithEvents';
|
import { DisposableWithEvents } from 'app/common/DisposableWithEvents';
|
||||||
import { CellVersions, UserAction } from 'app/common/DocActions';
|
import { CellVersions, UserAction } from 'app/common/DocActions';
|
||||||
import { GristObjCode } from "app/common/gristTypes";
|
import { GristObjCode } from 'app/plugin/GristData';
|
||||||
import { CellDelta } from 'app/common/TabularDiff';
|
import { CellDelta } from 'app/common/TabularDiff';
|
||||||
import { DocStateComparisonDetails } from 'app/common/UserAPI';
|
import { DocStateComparisonDetails } from 'app/common/UserAPI';
|
||||||
import { CellValue } from 'app/plugin/GristData';
|
import { CellValue } from 'app/plugin/GristData';
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { CellValue, CellVersions } from 'app/common/DocActions';
|
import { CellValue, CellVersions } from 'app/common/DocActions';
|
||||||
|
import { GristObjCode } from 'app/plugin/GristData';
|
||||||
import isString = require('lodash/isString');
|
import isString = require('lodash/isString');
|
||||||
import { removePrefix } from "./gutil";
|
import { removePrefix } from "./gutil";
|
||||||
|
|
||||||
@ -14,23 +15,6 @@ export type GristTypeInfo =
|
|||||||
{type: 'RefList', tableId: string} |
|
{type: 'RefList', tableId: string} |
|
||||||
{type: Exclude<GristType, 'DateTime'|'Ref'|'RefList'>};
|
{type: Exclude<GristType, 'DateTime'|'Ref'|'RefList'>};
|
||||||
|
|
||||||
|
|
||||||
// Letter codes for CellValue types encoded as [code, args...] tuples.
|
|
||||||
export const enum GristObjCode {
|
|
||||||
List = 'L',
|
|
||||||
Dict = 'O',
|
|
||||||
DateTime = 'D',
|
|
||||||
Date = 'd',
|
|
||||||
Skip = 'S',
|
|
||||||
Censored = 'C',
|
|
||||||
Reference = 'R',
|
|
||||||
ReferenceList = 'r',
|
|
||||||
Exception = 'E',
|
|
||||||
Pending = 'P',
|
|
||||||
Unmarshallable = 'U',
|
|
||||||
Versions = 'V',
|
|
||||||
}
|
|
||||||
|
|
||||||
export const MANUALSORT = 'manualSort';
|
export const MANUALSORT = 'manualSort';
|
||||||
|
|
||||||
// Whether a column is internal and should be hidden.
|
// Whether a column is internal and should be hidden.
|
||||||
@ -93,9 +77,9 @@ export function extractInfoFromColType(colType: string): GristTypeInfo {
|
|||||||
export function reencodeAsAny(value: CellValue, typeInfo: GristTypeInfo): CellValue {
|
export function reencodeAsAny(value: CellValue, typeInfo: GristTypeInfo): CellValue {
|
||||||
if (typeof value === 'number') {
|
if (typeof value === 'number') {
|
||||||
switch (typeInfo.type) {
|
switch (typeInfo.type) {
|
||||||
case 'Date': return ['d', value];
|
case 'Date': return [GristObjCode.Date, value];
|
||||||
case 'DateTime': return ['D', value, typeInfo.timezone];
|
case 'DateTime': return [GristObjCode.DateTime, value, typeInfo.timezone];
|
||||||
case 'Ref': return ['R', typeInfo.tableId, value];
|
case 'Ref': return [GristObjCode.Reference, typeInfo.tableId, value];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
@ -105,7 +89,7 @@ export function reencodeAsAny(value: CellValue, typeInfo: GristTypeInfo): CellVa
|
|||||||
/**
|
/**
|
||||||
* Returns whether a value (as received in a DocAction) represents a custom object.
|
* Returns whether a value (as received in a DocAction) represents a custom object.
|
||||||
*/
|
*/
|
||||||
export function isObject(value: CellValue): value is [string, any?] {
|
export function isObject(value: CellValue): value is [GristObjCode, any?] {
|
||||||
return Array.isArray(value);
|
return Array.isArray(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
/*** THIS FILE IS AUTO-GENERATED BY core/sandbox/gen_js_schema.py ***/
|
/*** THIS FILE IS AUTO-GENERATED BY core/sandbox/gen_js_schema.py ***/
|
||||||
|
|
||||||
|
import { GristObjCode } from "app/plugin/GristData";
|
||||||
|
|
||||||
// tslint:disable:object-literal-key-quotes
|
// tslint:disable:object-literal-key-quotes
|
||||||
|
|
||||||
export const SCHEMA_VERSION = 24;
|
export const SCHEMA_VERSION = 24;
|
||||||
@ -216,7 +219,7 @@ export interface SchemaTypes {
|
|||||||
displayCol: number;
|
displayCol: number;
|
||||||
visibleCol: number;
|
visibleCol: number;
|
||||||
recalcWhen: number;
|
recalcWhen: number;
|
||||||
recalcDeps: ['L', ...number[]]|null;
|
recalcDeps: [GristObjCode.List, ...number[]]|null;
|
||||||
};
|
};
|
||||||
|
|
||||||
"_grist_Imports": {
|
"_grist_Imports": {
|
||||||
@ -328,7 +331,7 @@ export interface SchemaTypes {
|
|||||||
|
|
||||||
"_grist_Triggers": {
|
"_grist_Triggers": {
|
||||||
tableRef: number;
|
tableRef: number;
|
||||||
eventTypes: ['L', ...string[]]|null;
|
eventTypes: [GristObjCode.List, ...string[]]|null;
|
||||||
isReadyColRef: number;
|
isReadyColRef: number;
|
||||||
actions: string;
|
actions: string;
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,20 @@
|
|||||||
export type CellValue = number|string|boolean|null|[string, ...unknown[]];
|
// Letter codes for CellValue types encoded as [code, args...] tuples.
|
||||||
|
export const enum GristObjCode {
|
||||||
|
List = 'L',
|
||||||
|
Dict = 'O',
|
||||||
|
DateTime = 'D',
|
||||||
|
Date = 'd',
|
||||||
|
Skip = 'S',
|
||||||
|
Censored = 'C',
|
||||||
|
Reference = 'R',
|
||||||
|
ReferenceList = 'r',
|
||||||
|
Exception = 'E',
|
||||||
|
Pending = 'P',
|
||||||
|
Unmarshallable = 'U',
|
||||||
|
Versions = 'V',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CellValue = number|string|boolean|null|[GristObjCode, ...unknown[]];
|
||||||
|
|
||||||
export interface RowRecord {
|
export interface RowRecord {
|
||||||
id: number;
|
id: number;
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
// tslint:disable:max-classes-per-file
|
// tslint:disable:max-classes-per-file
|
||||||
|
|
||||||
import {CellValue} from 'app/plugin/GristData';
|
import { CellValue, GristObjCode } from 'app/plugin/GristData';
|
||||||
import isPlainObject = require('lodash/isPlainObject');
|
import isPlainObject = require('lodash/isPlainObject');
|
||||||
|
|
||||||
// The text to show on cells whose values are pending.
|
// The text to show on cells whose values are pending.
|
||||||
@ -165,32 +165,32 @@ export function encodeObject(value: unknown): CellValue {
|
|||||||
if (value == null) {
|
if (value == null) {
|
||||||
return null;
|
return null;
|
||||||
} else if (value instanceof Reference) {
|
} else if (value instanceof Reference) {
|
||||||
return ['R', value.tableId, value.rowId];
|
return [GristObjCode.Reference, value.tableId, value.rowId];
|
||||||
} else if (value instanceof ReferenceList) {
|
} else if (value instanceof ReferenceList) {
|
||||||
return ['r', value.tableId, value.rowIds];
|
return [GristObjCode.ReferenceList, value.tableId, value.rowIds];
|
||||||
} else if (value instanceof Date) {
|
} else if (value instanceof Date) {
|
||||||
const timestamp = value.valueOf() / 1000;
|
const timestamp = value.valueOf() / 1000;
|
||||||
if ('timezone' in value) {
|
if ('timezone' in value) {
|
||||||
return ['D', timestamp, (value as GristDateTime).timezone];
|
return [GristObjCode.DateTime, timestamp, (value as GristDateTime).timezone];
|
||||||
} else {
|
} else {
|
||||||
// TODO Depending on how it's used, may want to return ['d', timestamp] for UTC midnight.
|
// TODO Depending on how it's used, may want to return ['d', timestamp] for UTC midnight.
|
||||||
return ['D', timestamp, 'UTC'];
|
return [GristObjCode.DateTime, timestamp, 'UTC'];
|
||||||
}
|
}
|
||||||
} else if (value instanceof CensoredValue) {
|
} else if (value instanceof CensoredValue) {
|
||||||
return ['C'];
|
return [GristObjCode.Censored];
|
||||||
} else if (value instanceof RaisedException) {
|
} else if (value instanceof RaisedException) {
|
||||||
return ['E', value.name, value.message, value.details];
|
return [GristObjCode.Exception, value.name, value.message, value.details];
|
||||||
} else if (Array.isArray(value)) {
|
} else if (Array.isArray(value)) {
|
||||||
return ['L', ...value.map(encodeObject)];
|
return [GristObjCode.List, ...value.map(encodeObject)];
|
||||||
} else if (isPlainObject(value)) {
|
} else if (isPlainObject(value)) {
|
||||||
return ['O', mapValues(value as any, encodeObject, {sort: true})];
|
return [GristObjCode.Dict, mapValues(value as any, encodeObject, {sort: true})];
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Fall through to return a best-effort representation.
|
// Fall through to return a best-effort representation.
|
||||||
}
|
}
|
||||||
// We either don't know how to convert the value, or failed during the conversion. Instead we
|
// We either don't know how to convert the value, or failed during the conversion. Instead we
|
||||||
// return an "UnmarshallableValue" object, with repr() of the value to show to the user.
|
// return an "UnmarshallableValue" object, with repr() of the value to show to the user.
|
||||||
return ['U', UnknownValue.safeRepr(value)];
|
return [GristObjCode.Unmarshallable, UnknownValue.safeRepr(value)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ import {BulkColValues, DocAction, TableColValues, TableDataAction, toTableDataAc
|
|||||||
import * as gristTypes from 'app/common/gristTypes';
|
import * as gristTypes from 'app/common/gristTypes';
|
||||||
import * as marshal from 'app/common/marshal';
|
import * as marshal from 'app/common/marshal';
|
||||||
import * as schema from 'app/common/schema';
|
import * as schema from 'app/common/schema';
|
||||||
|
import {GristObjCode} from "app/plugin/GristData";
|
||||||
import {ActionHistoryImpl} from 'app/server/lib/ActionHistoryImpl';
|
import {ActionHistoryImpl} from 'app/server/lib/ActionHistoryImpl';
|
||||||
import {ExpandedQuery} from 'app/server/lib/ExpandedQuery';
|
import {ExpandedQuery} from 'app/server/lib/ExpandedQuery';
|
||||||
import {IDocStorageManager} from 'app/server/lib/IDocStorageManager';
|
import {IDocStorageManager} from 'app/server/lib/IDocStorageManager';
|
||||||
@ -35,7 +36,7 @@ const debuglog = util.debuglog('db');
|
|||||||
|
|
||||||
const maxSQLiteVariables = 500; // Actually could be 999, so this is playing it safe.
|
const maxSQLiteVariables = 500; // Actually could be 999, so this is playing it safe.
|
||||||
|
|
||||||
const PENDING_VALUE = [gristTypes.GristObjCode.Pending];
|
const PENDING_VALUE = [GristObjCode.Pending];
|
||||||
|
|
||||||
export class DocStorage implements ISQLiteDB {
|
export class DocStorage implements ISQLiteDB {
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import {ApiError} from 'app/common/ApiError';
|
|||||||
import { DocData } from 'app/common/DocData';
|
import { DocData } from 'app/common/DocData';
|
||||||
import { parseFormula } from 'app/common/Formula';
|
import { parseFormula } from 'app/common/Formula';
|
||||||
import { removePrefix } from 'app/common/gutil';
|
import { removePrefix } from 'app/common/gutil';
|
||||||
|
import { GristObjCode } from 'app/plugin/GristData';
|
||||||
import { quoteIdent } from 'app/server/lib/SQLiteDB';
|
import { quoteIdent } from 'app/server/lib/SQLiteDB';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -19,7 +20,7 @@ export interface ExpandedQuery extends ServerQuery {
|
|||||||
// step. That means we need to pass the error message along
|
// step. That means we need to pass the error message along
|
||||||
// explicitly.
|
// explicitly.
|
||||||
constants?: {
|
constants?: {
|
||||||
[colId: string]: ['E', string] | ['P'];
|
[colId: string]: [GristObjCode.Exception, string] | [GristObjCode.Pending];
|
||||||
};
|
};
|
||||||
|
|
||||||
// A list of join clauses to bring in data from other tables.
|
// A list of join clauses to bring in data from other tables.
|
||||||
@ -115,7 +116,7 @@ export function expandQuery(iquery: ServerQuery, docData: DocData, onDemandFormu
|
|||||||
// We add a trivial selection, and store errors in the query for substitution later.
|
// We add a trivial selection, and store errors in the query for substitution later.
|
||||||
sqlFormula = '0';
|
sqlFormula = '0';
|
||||||
if (!query.constants) { query.constants = {}; }
|
if (!query.constants) { query.constants = {}; }
|
||||||
query.constants[colId] = ['E', error];
|
query.constants[colId] = [GristObjCode.Exception, error];
|
||||||
}
|
}
|
||||||
if (sqlFormula) {
|
if (sqlFormula) {
|
||||||
selects.add(`${sqlFormula} as ${quoteIdent(colId)}`);
|
selects.add(`${sqlFormula} as ${quoteIdent(colId)}`);
|
||||||
|
@ -19,6 +19,7 @@ import { getSetMapValue, isObject, pruneArray } from 'app/common/gutil';
|
|||||||
import { canEdit, canView, isValidRole, Role } from 'app/common/roles';
|
import { canEdit, canView, isValidRole, Role } from 'app/common/roles';
|
||||||
import { FullUser, UserAccessData } from 'app/common/UserAPI';
|
import { FullUser, UserAccessData } from 'app/common/UserAPI';
|
||||||
import { HomeDBManager } from 'app/gen-server/lib/HomeDBManager';
|
import { HomeDBManager } from 'app/gen-server/lib/HomeDBManager';
|
||||||
|
import { GristObjCode } from 'app/plugin/GristData';
|
||||||
import { compileAclFormula } from 'app/server/lib/ACLFormula';
|
import { compileAclFormula } from 'app/server/lib/ACLFormula';
|
||||||
import { DocClients } from 'app/server/lib/DocClients';
|
import { DocClients } from 'app/server/lib/DocClients';
|
||||||
import { getDocSessionAccess, getDocSessionUser, OptDocSession } from 'app/server/lib/DocSession';
|
import { getDocSessionAccess, getDocSessionUser, OptDocSession } from 'app/server/lib/DocSession';
|
||||||
@ -1104,9 +1105,9 @@ export class GranularAccess implements GranularAccessForBundle {
|
|||||||
if (colValues === undefined) {
|
if (colValues === undefined) {
|
||||||
censorAt = () => 1;
|
censorAt = () => 1;
|
||||||
} else if (Array.isArray(action[2])) {
|
} else if (Array.isArray(action[2])) {
|
||||||
censorAt = (colId, idx) => (copyOnNeed() as BulkColValues)[colId][idx] = ['C']; // censored
|
censorAt = (colId, idx) => (copyOnNeed() as BulkColValues)[colId][idx] = [GristObjCode.Censored];
|
||||||
} else {
|
} else {
|
||||||
censorAt = (colId) => (copyOnNeed() as ColValues)[colId] = ['C']; // censored
|
censorAt = (colId) => (copyOnNeed() as ColValues)[colId] = [GristObjCode.Censored];
|
||||||
}
|
}
|
||||||
|
|
||||||
// These map an index of a row in the action to its index in rowsBefore and in rowsAfter.
|
// These map an index of a row in the action to its index in rowsBefore and in rowsAfter.
|
||||||
|
@ -12,8 +12,8 @@ _ts_types = {
|
|||||||
"Int": "number",
|
"Int": "number",
|
||||||
"PositionNumber": "number",
|
"PositionNumber": "number",
|
||||||
"Ref": "number",
|
"Ref": "number",
|
||||||
"RefList": "['L', ...number[]]|null", # Non-primitive values are encoded
|
"RefList": "[GristObjCode.List, ...number[]]|null", # Non-primitive values are encoded
|
||||||
"ChoiceList": "['L', ...string[]]|null",
|
"ChoiceList": "[GristObjCode.List, ...string[]]|null",
|
||||||
"Text": "string",
|
"Text": "string",
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,6 +24,9 @@ def get_ts_type(col_type):
|
|||||||
def main():
|
def main():
|
||||||
print("""\
|
print("""\
|
||||||
/*** THIS FILE IS AUTO-GENERATED BY %s ***/
|
/*** THIS FILE IS AUTO-GENERATED BY %s ***/
|
||||||
|
|
||||||
|
import { GristObjCode } from "app/plugin/GristData";
|
||||||
|
|
||||||
// tslint:disable:object-literal-key-quotes
|
// tslint:disable:object-literal-key-quotes
|
||||||
|
|
||||||
export const SCHEMA_VERSION = %d;
|
export const SCHEMA_VERSION = %d;
|
||||||
|
Loading…
Reference in New Issue
Block a user