From fc60b2acef18bb685b84809c8b3bd351b924c0a7 Mon Sep 17 00:00:00 2001 From: Timmy Huang Date: Tue, 10 Aug 2021 18:38:33 -0700 Subject: [PATCH 1/7] fix: thuang-fix-tsconfig-path (#2372) Thanks so much for the quick review, Bruce!! --- client/configuration/eslint/eslint.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/configuration/eslint/eslint.js b/client/configuration/eslint/eslint.js index a0767384..c44a2853 100644 --- a/client/configuration/eslint/eslint.js +++ b/client/configuration/eslint/eslint.js @@ -37,7 +37,10 @@ module.exports = { jsx: true, generators: true, }, - project: "./tsconfig.json", + // (thuang): Pairing with `tsconfigRootDir`, which points to the directory + // of eslint.js + project: "../../tsconfig.json", + tsconfigRootDir: __dirname, }, rules: { "react/jsx-no-target-blank": "off", From 59c475b82117e5fbf5d1882ba44a6ab550ece299 Mon Sep 17 00:00:00 2001 From: Timmy Huang Date: Wed, 11 Aug 2021 20:12:21 -0700 Subject: [PATCH 2/7] chore: extract schema types (#2375) * chore: extract schema types * address comments --- .../util/annoMatrix/serverMocks/routes.ts | 2 +- .../util/annoMatrix/serverMocks/schema.ts | 2 +- .../util/stateManager/sampleResponses.ts | 2 +- client/src/common/types/entities.ts | 59 +------------------ client/src/common/types/schema.ts | 58 ++++++++++++++++++ .../util/stateManager/annotationsHelpers.ts | 2 +- client/src/util/stateManager/schemaHelpers.ts | 10 ++-- 7 files changed, 68 insertions(+), 67 deletions(-) create mode 100644 client/src/common/types/schema.ts diff --git a/client/__tests__/util/annoMatrix/serverMocks/routes.ts b/client/__tests__/util/annoMatrix/serverMocks/routes.ts index f195026a..a5b9c34c 100644 --- a/client/__tests__/util/annoMatrix/serverMocks/routes.ts +++ b/client/__tests__/util/annoMatrix/serverMocks/routes.ts @@ -34,7 +34,7 @@ function makeMockColumn(s: any, length: any) { return new Array(length).fill(s.categories[0]); default: - throw new Error("unkonwn type"); + throw new Error("unknown type"); } } diff --git a/client/__tests__/util/annoMatrix/serverMocks/schema.ts b/client/__tests__/util/annoMatrix/serverMocks/schema.ts index f957a352..2bdb63d5 100644 --- a/client/__tests__/util/annoMatrix/serverMocks/schema.ts +++ b/client/__tests__/util/annoMatrix/serverMocks/schema.ts @@ -1,4 +1,4 @@ -import { RawSchema } from "../../../../src/common/types/entities"; +import { RawSchema } from "../../../../src/common/types/schema"; export const schema: { schema: RawSchema } = { schema: { diff --git a/client/__tests__/util/stateManager/sampleResponses.ts b/client/__tests__/util/stateManager/sampleResponses.ts index 77c15689..f5ef9254 100644 --- a/client/__tests__/util/stateManager/sampleResponses.ts +++ b/client/__tests__/util/stateManager/sampleResponses.ts @@ -5,7 +5,7 @@ import zip from "lodash.zip"; import _ from "lodash"; import { flatbuffers } from "flatbuffers"; import { NetEncoding } from "../../../src/util/stateManager/matrix_generated"; -import { RawSchema } from "../../../src/common/types/entities"; +import { RawSchema } from "../../../src/common/types/schema"; /* test data mocking REST 0.2 API responses. Used in several tests. diff --git a/client/src/common/types/entities.ts b/client/src/common/types/entities.ts index c1f2e8e4..fabef218 100644 --- a/client/src/common/types/entities.ts +++ b/client/src/common/types/entities.ts @@ -1,60 +1,3 @@ // If a globally shared type or interface doesn't have a clear owner, put it here -export type Category = number | string | boolean; - -export interface AnnotationColumn { - categories?: Category[]; - name: string; - type: "string" | "float32" | "int32" | "categorical" | "boolean"; - writable: boolean; -} - -interface DataFrame { - nObs: number; - nVar: number; - // TODO(thuang): Not sure what other types are available - type: "float32"; -} - -export interface LayoutColumn { - dims: string[]; - name: string; - // TODO(thuang): Not sure what other types are available - type: "float32"; -} -interface RawLayout { - obs: LayoutColumn[]; - var?: LayoutColumn[]; -} - -interface RawAnnotations { - obs: { - columns: AnnotationColumn[]; - index: string; - }; - var: { - columns: AnnotationColumn[]; - index: string; - }; -} - -export interface RawSchema { - annotations: RawAnnotations; - dataframe: DataFrame; - layout: RawLayout; -} - -interface Annotations extends RawAnnotations { - obsByName: { [name: string]: AnnotationColumn }; - varByName: { [name: string]: AnnotationColumn }; -} - -interface Layout extends RawLayout { - obsByName: { [name: string]: LayoutColumn }; - varByName: { [name: string]: LayoutColumn }; -} - -export interface Schema extends RawSchema { - annotations: Annotations; - layout: Layout; -} +export {}; diff --git a/client/src/common/types/schema.ts b/client/src/common/types/schema.ts new file mode 100644 index 00000000..12dbb34f --- /dev/null +++ b/client/src/common/types/schema.ts @@ -0,0 +1,58 @@ +type Category = number | string | boolean; + +export interface AnnotationColumnSchema { + categories?: Category[]; + name: string; + type: "string" | "float32" | "int32" | "categorical" | "boolean"; + writable: boolean; +} + +interface XMatrixSchema { + nObs: number; + nVar: number; + // TODO(thuang): Not sure what other types are available + type: "float32"; +} + +export interface EmbeddingSchema { + dims: string[]; + name: string; + // TODO(thuang): Not sure what other types are available + type: "float32"; +} +interface RawLayoutSchema { + obs: EmbeddingSchema[]; + var?: EmbeddingSchema[]; +} + +interface RawAnnotationsSchema { + obs: { + columns: AnnotationColumnSchema[]; + index: string; + }; + var: { + columns: AnnotationColumnSchema[]; + index: string; + }; +} + +export interface RawSchema { + annotations: RawAnnotationsSchema; + dataframe: XMatrixSchema; + layout: RawLayoutSchema; +} + +interface AnnotationsSchema extends RawAnnotationsSchema { + obsByName: { [name: string]: AnnotationColumnSchema }; + varByName: { [name: string]: AnnotationColumnSchema }; +} + +interface LayoutSchema extends RawLayoutSchema { + obsByName: { [name: string]: EmbeddingSchema }; + varByName: { [name: string]: EmbeddingSchema }; +} + +export interface Schema extends RawSchema { + annotations: AnnotationsSchema; + layout: LayoutSchema; +} diff --git a/client/src/util/stateManager/annotationsHelpers.ts b/client/src/util/stateManager/annotationsHelpers.ts index 0ff6e9d0..f6b30d8d 100644 --- a/client/src/util/stateManager/annotationsHelpers.ts +++ b/client/src/util/stateManager/annotationsHelpers.ts @@ -3,7 +3,7 @@ Helper functions for user-editable annotations state management. See also reducers/annotations.js */ -import { Schema } from "../../common/types/entities"; +import { Schema } from "../../common/types/schema"; /* There are a number of state constraints assumed throughout the diff --git a/client/src/util/stateManager/schemaHelpers.ts b/client/src/util/stateManager/schemaHelpers.ts index eaf2aeed..84056754 100644 --- a/client/src/util/stateManager/schemaHelpers.ts +++ b/client/src/util/stateManager/schemaHelpers.ts @@ -11,9 +11,9 @@ import catLabelSort from "../catLabelSort"; import { RawSchema, Schema, - LayoutColumn, - AnnotationColumn, -} from "../../common/types/entities"; + EmbeddingSchema, + AnnotationColumnSchema, +} from "../../common/types/schema"; /* System wide schema assumptions: @@ -86,7 +86,7 @@ export function removeObsAnnoColumn(schema: Schema, name: string): Schema { export function addObsAnnoColumn( schema: Schema, _: string, - defn: AnnotationColumn + defn: AnnotationColumnSchema ): Schema { const newSchema = _copyObsAnno(schema); @@ -142,7 +142,7 @@ export function addObsAnnoCategory(schema: any, name: any, category: any) { return newSchema; } -export function addObsLayout(schema: Schema, layout: LayoutColumn): Schema { +export function addObsLayout(schema: Schema, layout: EmbeddingSchema): Schema { /* add or replace a layout */ const newSchema = _copyObsLayout(schema); newSchema.layout.obs.push(layout); From 660dff256c3cfdc27e6ba62b9eb4849edf03a896 Mon Sep 17 00:00:00 2001 From: Bruce Martin Date: Thu, 12 Aug 2021 07:17:05 -0700 Subject: [PATCH 3/7] add array type foundations (#2376) --- client/src/annoMatrix/loader.ts | 4 +- client/src/common/types/arraytypes.ts | 85 +++++++++++++++++++ client/src/util/dataframe/dataframe.ts | 11 +-- client/src/util/dataframe/histogram.ts | 2 +- client/src/util/dataframe/util.ts | 2 - client/src/util/stateManager/matrix.ts | 6 +- client/src/util/typeHelpers.ts | 32 ------- .../src/util/typedCrossfilter/crossfilter.ts | 12 +-- client/src/util/typedCrossfilter/sort.ts | 14 +-- 9 files changed, 103 insertions(+), 65 deletions(-) create mode 100644 client/src/common/types/arraytypes.ts delete mode 100644 client/src/util/typeHelpers.ts diff --git a/client/src/annoMatrix/loader.ts b/client/src/annoMatrix/loader.ts index 25b263c9..f2b12f6e 100644 --- a/client/src/annoMatrix/loader.ts +++ b/client/src/annoMatrix/loader.ts @@ -8,7 +8,7 @@ import { removeObsAnnoCategory, addObsLayout, } from "../util/stateManager/schemaHelpers"; -import { isArrayOrTypedArray } from "../util/typeHelpers"; +import { isAnyArray } from "../common/types/arraytypes"; import { _whereCacheCreate } from "./whereCache"; import AnnoMatrix from "./annoMatrix"; import PromiseLimit from "../util/promiseLimit"; @@ -134,7 +134,7 @@ export default class AnnoMatrixLoader extends AnnoMatrix { const newAnnoMatrix = this._clone(); let data; - if (isArrayOrTypedArray(value)) { + if (isAnyArray(value)) { if (value.constructor !== Ctor) throw new Error("Mismatched value array type"); if (value.length !== this.nObs) diff --git a/client/src/common/types/arraytypes.ts b/client/src/common/types/arraytypes.ts new file mode 100644 index 00000000..753527e7 --- /dev/null +++ b/client/src/common/types/arraytypes.ts @@ -0,0 +1,85 @@ +/** + * Utility type and interface definitions. + */ + +/** + * TypedArrays that can be assigned to a number. + */ +export type TypedArray = + | Int8Array + | Uint8Array + | Int16Array + | Uint16Array + | Int32Array + | Uint32Array + | Float32Array + | Float64Array; + +export type UnsignedTypedArray = Uint8Array | Uint16Array | Uint32Array; +export type FloatTypedArray = Float32Array | Float64Array; + +export type TypedArrayConstructor = + | Int8ArrayConstructor + | Uint8ArrayConstructor + | Int16ArrayConstructor + | Uint16ArrayConstructor + | Int32ArrayConstructor + | Uint32ArrayConstructor + | Float32ArrayConstructor + | Float64ArrayConstructor; + +export type AnyArray = Array | TypedArray; + +export type NumberArray = Array | TypedArray; + +export type Int8 = Int8Array[0]; +export type Uint8 = Uint8Array[0]; +export type Int16 = Int16Array[0]; +export type Uint16 = Uint16Array[0]; +export type Int32 = Int32Array[0]; +export type Uint32 = Uint32Array[0]; +export type Float32 = Float32Array[0]; +export type Float64 = Float64Array[0]; + +/** + * Test if the parameter is a TypedArray. + * @param tbd - value to be tested + * @returns true if `tbd` is a TypedArray, false if not. + */ +export function isTypedArray(tbd: unknown): tbd is TypedArray { + return ( + ArrayBuffer.isView(tbd) && + Object.prototype.toString.call(tbd) !== "[object DataView]" + ); +} + +/** + * Test if the paramter is a float TypedArray + * @param tbd - value to be tested + * @returns - true if `tbd` is a float typed array. + */ +export function isFloatTypedArray(tbd: unknown): tbd is FloatTypedArray { + return tbd instanceof Float32Array || tbd instanceof Float64Array; +} + +/** + * Test if the paramter is a float TypedArray + * @param tbd - value to be tested + * @returns - true if `tbd` is a float typed array. + */ +export function isUnsignedTypedArray(tbd: unknown): tbd is UnsignedTypedArray { + return ( + tbd instanceof Uint8Array || + tbd instanceof Uint16Array || + tbd instanceof Uint32Array + ); +} + +/** + * Test if the parameter is a TypedArray or Array + * @param tbd - value to be tested + * @returns - true if `tbd` is a TypedArray or Array + */ +export function isAnyArray(tbd: unknown): tbd is AnyArray { + return Array.isArray(tbd) || isTypedArray(tbd); +} diff --git a/client/src/util/dataframe/dataframe.ts b/client/src/util/dataframe/dataframe.ts index 2fb2e873..4274576d 100644 --- a/client/src/util/dataframe/dataframe.ts +++ b/client/src/util/dataframe/dataframe.ts @@ -1,12 +1,7 @@ import { IdentityInt32Index, isLabelIndex } from "./labelIndex"; // weird cross-dependency that we should clean up someday... -import { - isTypedArray, - isArrayOrTypedArray, - callOnceLazy, - memoize, - __getMemoId, -} from "./util"; +import { callOnceLazy, memoize, __getMemoId } from "./util"; +import { isTypedArray, isAnyArray } from "../../common/types/arraytypes"; import { summarizeContinuous, summarizeCategorical as _summarizeCategorical, @@ -163,7 +158,7 @@ class Dataframe { if (!Array.isArray(columnarData)) { throw new TypeError("Dataframe constructor requires array of columns"); } - if (!columnarData.every((c) => isArrayOrTypedArray(c))) { + if (!columnarData.every((c) => isAnyArray(c))) { throw new TypeError("Dataframe columns must all be Array or TypedArray"); } if (!isLabelIndex(rowIndex)) { diff --git a/client/src/util/dataframe/histogram.ts b/client/src/util/dataframe/histogram.ts index 4949955a..1717d92e 100644 --- a/client/src/util/dataframe/histogram.ts +++ b/client/src/util/dataframe/histogram.ts @@ -1,7 +1,7 @@ /* Dataframe histogram */ -import { isTypedArray } from "./util"; +import { isTypedArray } from "../../common/types/arraytypes"; // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. function _histogramContinuous(column: any, bins: any, min: any, max: any) { diff --git a/client/src/util/dataframe/util.ts b/client/src/util/dataframe/util.ts index ed27258f..8333d4d8 100644 --- a/client/src/util/dataframe/util.ts +++ b/client/src/util/dataframe/util.ts @@ -2,8 +2,6 @@ Private utility code for dataframe */ -export { isTypedArray, isArrayOrTypedArray } from "../typeHelpers"; - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS. export function callOnceLazy(f: any) { /* diff --git a/client/src/util/stateManager/matrix.ts b/client/src/util/stateManager/matrix.ts index 7e76f129..3582689e 100644 --- a/client/src/util/stateManager/matrix.ts +++ b/client/src/util/stateManager/matrix.ts @@ -1,6 +1,6 @@ import { flatbuffers } from "flatbuffers"; import { NetEncoding } from "./matrix_generated"; -import { isTypedArray, isFpTypedArray } from "../typeHelpers"; +import { isTypedArray, isFloatTypedArray } from "../../common/types/arraytypes"; import { Dataframe, IdentityInt32Index, @@ -188,7 +188,7 @@ function promoteTypedArray(o: any) { TODO - future optimization: not all int32/uint32 data series require promotion to float64. We COULD simply look at the data to decide. */ - if (isFpTypedArray(o) || Array.isArray(o)) return o; + if (isFloatTypedArray(o) || Array.isArray(o)) return o; let TypedArrayCtor; switch (o.constructor) { @@ -246,7 +246,7 @@ export function matrixFBSToDataframe(arrayBuffers: any) { .map((fb: any) => // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. fb.columns.map((c: any) => { - if (isFpTypedArray(c) || Array.isArray(c)) return c; + if (isFloatTypedArray(c) || Array.isArray(c)) return c; return promoteTypedArray(c); }) ) diff --git a/client/src/util/typeHelpers.ts b/client/src/util/typeHelpers.ts deleted file mode 100644 index 179be5c9..00000000 --- a/client/src/util/typeHelpers.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* -Various type and schema related helper functions. -*/ - -/* -Utility function to test for a typed array -*/ -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS. -export function isTypedArray(x: any) { - return ( - ArrayBuffer.isView(x) && - Object.prototype.toString.call(x) !== "[object DataView]" - ); -} - -/* -Test for float typed array, ie, Float32TypedArray or Float64TypedArray -*/ -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS. -export function isFpTypedArray(x: any) { - let constructor; - const isFloatArray = - x && - ({ constructor } = x) && - (constructor === Float32Array || constructor === Float64Array); - return isFloatArray; -} - -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS. -export function isArrayOrTypedArray(x: any) { - return Array.isArray(x) || isTypedArray(x); -} diff --git a/client/src/util/typedCrossfilter/crossfilter.ts b/client/src/util/typedCrossfilter/crossfilter.ts index 7b4ec264..94968976 100644 --- a/client/src/util/typedCrossfilter/crossfilter.ts +++ b/client/src/util/typedCrossfilter/crossfilter.ts @@ -9,6 +9,7 @@ import { upperBoundIndirect, } from "./sort"; import { makeSortIndex } from "./util"; +import { isAnyArray } from "../../common/types/arraytypes"; class NotImplementedError extends Error { // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. @@ -450,7 +451,7 @@ class ImmutableScalarDimension extends _ImmutableBaseDimension { value, new ValueArrayType(data.length) ); - } else if (isArrayOrTypedArray(value)) { + } else if (isAnyArray(value)) { // Create value array from user-provided array. Typically used // only by enumerated dimensions array = this._createValueArray( @@ -729,15 +730,6 @@ export const DimTypes = { spatial: ImmutableSpatialDimension, }; -// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. -function isArrayOrTypedArray(x: any) { - return ( - Array.isArray(x) || - (ArrayBuffer.isView(x) && - Object.prototype.toString.call(x) !== "[object DataView]") - ); -} - /* return bounding box of the polygon */ // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. function polygonBoundingBox(polygon: any) { diff --git a/client/src/util/typedCrossfilter/sort.ts b/client/src/util/typedCrossfilter/sort.ts index f23eea49..8a41f989 100644 --- a/client/src/util/typedCrossfilter/sort.ts +++ b/client/src/util/typedCrossfilter/sort.ts @@ -1,4 +1,4 @@ -import { isTypedArray, isFpTypedArray } from "../typeHelpers"; +import { isTypedArray, isFloatTypedArray } from "../../common/types/arraytypes"; /* eslint-disable no-bitwise -- code relies on bitwise ops */ @@ -219,7 +219,7 @@ export function sortArray(arr: any) { return quicksort(arr, 0, arr.length - 1); } if (isTypedArray(arr)) { - if (isFpTypedArray(arr)) { + if (isFloatTypedArray(arr)) { return quicksortFloats(arr, 0, arr.length - 1); } return quicksort(arr, 0, arr.length - 1); @@ -230,7 +230,7 @@ export function sortArray(arr: any) { // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS. export function sortIndex(index: any, source: any) { - if (isFpTypedArray(source)) + if (isFloatTypedArray(source)) return quicksortFloatsIndirect(index, source, 0, index.length - 1); return quicksortIndirect(index, source, 0, index.length - 1); } @@ -293,7 +293,7 @@ function lowerBoundFloat(valueArray: any, value: any, first: any, last: any) { // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS. export function lowerBound(valueArray: any, value: any, first: any, last: any) { - if (isFpTypedArray(valueArray)) { + if (isFloatTypedArray(valueArray)) { return lowerBoundFloat(valueArray, value, first, last); } return lowerBoundNonFloat(valueArray, value, first, last); @@ -366,7 +366,7 @@ export function lowerBoundIndirect( // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS. last: any ) { - if (isFpTypedArray(valueArray)) { + if (isFloatTypedArray(valueArray)) { return lowerBoundFloatIndirect(valueArray, indexArray, value, first, last); } return lowerBoundNonFloatIndirect(valueArray, indexArray, value, first, last); @@ -425,7 +425,7 @@ function upperBoundFloat(valueArray: any, value: any, first: any, last: any) { // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS. export function upperBound(valueArray: any, value: any, first: any, last: any) { - if (isFpTypedArray(valueArray)) { + if (isFloatTypedArray(valueArray)) { return upperBoundFloat(valueArray, value, first, last); } return upperBoundNonFloat(valueArray, value, first, last); @@ -498,7 +498,7 @@ export function upperBoundIndirect( // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS. last: any ) { - if (isFpTypedArray(valueArray)) { + if (isFloatTypedArray(valueArray)) { return upperBoundFloatIndirect(valueArray, indexArray, value, first, last); } return upperBoundNonFloatIndirect(valueArray, indexArray, value, first, last); From 925b785b1fa6f27ee10e0a265a2a14427d9bf165 Mon Sep 17 00:00:00 2001 From: Severiano Badajoz Date: Thu, 12 Aug 2021 11:34:04 -0700 Subject: [PATCH 4/7] fix: disable FE auth testing on compatibility tests (#2377) --- .github/workflows/compatibility_tests.yml | 1 + client/Makefile | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/compatibility_tests.yml b/.github/workflows/compatibility_tests.yml index 190dbd29..e3e3ba8f 100644 --- a/.github/workflows/compatibility_tests.yml +++ b/.github/workflows/compatibility_tests.yml @@ -9,6 +9,7 @@ on: env: JEST_ENV: prod + CXG_AUTH_TYPE: none jobs: docker-build: diff --git a/client/Makefile b/client/Makefile index fbf7dab4..9b94a995 100644 --- a/client/Makefile +++ b/client/Makefile @@ -1,11 +1,13 @@ include ../common.mk ANNOTATIONS := $(if $(ANNOTATIONS),$(ANNOTATIONS),../backend/test/fixtures/pbmc3k-annotations.csv) -GENE_SETS := $(if $(GENE_SETS),$(GENE_SETS),../backend/test/fixtures/pbmc3k-genesets.csv) +GENE_SETS := $(if $(GENE_SETS),$(GENE_SETS),../backend/test/fixtures/pbmc3k-genesets.csv) ANNOTATIONS_FILENAME := $(shell basename $(ANNOTATIONS)) GENE_SETS_FILENAME := $(shell basename $(GENE_SETS)) -CXG_CONFIG := $(if $(CXG_CONFIG), $(CXG_CONFIG), ./__tests__/e2e/test_config.yaml) +CXG_CONFIG := $(if $(CXG_CONFIG), $(CXG_CONFIG),./__tests__/e2e/test_config.yaml) + +CXG_AUTH_TYPE := $(if $(CXG_AUTH_TYPE), $(CXG_AUTH_TYPE),"test") # Packaging @@ -38,7 +40,7 @@ smoke-test: start_server_and_test \ 'CXG_OPTIONS="--config-file $(CXG_CONFIG)" $(MAKE) start-server' \ $(CXG_SERVER_PORT) \ - 'CXG_URL_BASE="http://localhost:$(CXG_SERVER_PORT)" CXG_AUTH_TYPE="test" npm run e2e -- --verbose false' + 'CXG_URL_BASE="http://localhost:$(CXG_SERVER_PORT)" CXG_AUTH_TYPE=$(CXG_AUTH_TYPE) npm run e2e -- --verbose false' # start an instance of cellxgene and run the end-to-end annotations tests .PHONY: smoke-test-annotations From 4b417cb5a5135bbbdcf29333d05a748f3a85f66b Mon Sep 17 00:00:00 2001 From: Bruce Martin Date: Thu, 12 Aug 2021 17:14:09 -0700 Subject: [PATCH 5/7] undoable TS typing (#2374) * type undoable-related TS * style change to type declaration --- client/__tests__/reducers/undoable.test.ts | 17 +- client/src/reducers/undoable.ts | 119 +++++++++----- client/src/reducers/undoableConfig.ts | 178 ++++++++++++--------- client/src/reducers/undoableFsm.ts | 50 +++--- client/src/util/statemachine/index.ts | 97 ++++++----- 5 files changed, 266 insertions(+), 195 deletions(-) diff --git a/client/__tests__/reducers/undoable.test.ts b/client/__tests__/reducers/undoable.test.ts index 86e787e5..d58913cc 100644 --- a/client/__tests__/reducers/undoable.test.ts +++ b/client/__tests__/reducers/undoable.test.ts @@ -1,10 +1,12 @@ +import { Reducer } from "redux"; import undoable from "../../src/reducers/undoable"; describe("create", () => { test("no keys", () => { - // @ts-expect-error ts-migrate(2554) FIXME: Expected 2-3 arguments, but got 1. - expect(() => undoable(() => {})).toThrow(); - expect(() => undoable(() => {}, null)).toThrow(); + expect(() => + undoable(() => {}, undefined as unknown as string[]) + ).toThrow(); + expect(() => undoable(() => {}, null as unknown as string[])).toThrow(); expect(() => undoable(() => {}, [])).toThrow(); expect(() => undoable(() => {}, [], {})).toThrow(); }); @@ -24,8 +26,7 @@ describe("create", () => { describe("undo", () => { test("expected state modifications", () => { const initialState = { a: 0, b: 1000 }; - // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. - const reducer = (state: any) => ({ a: state.a + 1, b: state.b + 1 }); + const reducer: Reducer = (state) => ({ a: state.a + 1, b: state.b + 1 }); const undoableReducer = undoable(reducer, ["a"]); const s1 = undoableReducer(initialState, { type: "test" }); @@ -43,10 +44,8 @@ describe("undo", () => { describe("redo", () => { const initialState = { a: 0, b: 1000 }; - // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. - const reducer = (state: any) => ({ a: state.a + 1, b: state.b + 1 }); - // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. - let UR: any; + const reducer: Reducer = (state) => ({ a: state.a + 1, b: state.b + 1 }); + let UR: Reducer; beforeEach(() => { UR = undoable(reducer, ["a"]); diff --git a/client/src/reducers/undoable.ts b/client/src/reducers/undoable.ts index f9269989..225b958e 100644 --- a/client/src/reducers/undoable.ts +++ b/client/src/reducers/undoable.ts @@ -51,27 +51,55 @@ history state processing. The undoable action object contents, by key: filter state are entirely at the discretion of the action filter. */ +import { Reducer, AnyAction } from "redux"; import fromEntries from "../util/fromEntries"; -const historyKeyPrefix = "@@undoable/"; -const pastKey = `${historyKeyPrefix}past`; -const futureKey = `${historyKeyPrefix}future`; -const filterStateKey = `${historyKeyPrefix}filterState`; -const filterActionKey = `${historyKeyPrefix}filterAction`; -const pendingKey = `${historyKeyPrefix}pending`; +export const pastKey = "@@undoable/past"; +export const futureKey = "@@undoable/future"; +export const filterStateKey = "@@undoable/filterState"; +export const filterActionKey = "@@undoable/filterAction"; +export const pendingKey = "@@undoable/pending"; const defaultHistoryLimit = -100; -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS. -const Undoable = (reducer: any, undoableKeys: any, options = {}) => { - // @ts-expect-error ts-migrate(2339) FIXME: Property 'debug' does not exist on type '{}'. - const { debug } = options; - // @ts-expect-error ts-migrate(2339) FIXME: Property 'historyLimit' does not exist on type '{}... Remove this comment to see the full error message +export interface UndoableFilterState { + [name: string]: unknown; +} + +export interface UndoableConfig { + debug?: boolean | number; + historyLimit?: number; + actionFilter?: ActionFilterFn; +} + +export interface UndoableAction { + [filterActionKey]: string; + [filterStateKey]?: FilterStateType; +} + +export type ActionFilterFn = ( + undoableState: UndoableState, + action: AnyAction, + filterState?: FilterStateType +) => UndoableAction; + +export interface UndoableState { + [pastKey]: [string, unknown][][]; + [futureKey]: [string, unknown][][]; + [pendingKey]: [string, unknown][] | null; + [filterStateKey]: FilterStateType | undefined; +} + +const Undoable = ( + reducer: Reducer, + undoableKeys: string[], + options: UndoableConfig = {} +): Reducer => { + const debug = options?.debug ?? false; let { historyLimit } = options; if (!historyLimit) historyLimit = defaultHistoryLimit; if (historyLimit > 0) historyLimit = -historyLimit; - const actionFilter = - // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. - (options as any).actionFilter || (() => ({ [filterActionKey]: "save" })); + const actionFilter: ActionFilterFn = + options?.actionFilter ?? (() => ({ [filterActionKey]: "save" })); if (!Array.isArray(undoableKeys) || undoableKeys.length === 0) throw new Error("undoable keys array must be specified"); @@ -80,8 +108,9 @@ const Undoable = (reducer: any, undoableKeys: any, options = {}) => { /* Undo the current to previous history */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. - function undo(currentState: any) { + function undo( + currentState: UndoableState + ): UndoableState { const past = currentState[pastKey]; const future = currentState[futureKey]; if (past.length === 0) return currentState; @@ -89,7 +118,7 @@ const Undoable = (reducer: any, undoableKeys: any, options = {}) => { undoableKeysSet.has(kv[0]) ); const newPast = [...past]; - const newState = newPast.pop(); + const newState = newPast.pop() || []; const newFuture = push(future, currentUndoableState); const nextState = { ...currentState, @@ -104,8 +133,9 @@ const Undoable = (reducer: any, undoableKeys: any, options = {}) => { /* Replay future, previously undone. */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. - function redo(currentState: any) { + function redo( + currentState: UndoableState + ): UndoableState { const past = currentState[pastKey] || []; const future = currentState[futureKey] || []; if (future.length === 0) return currentState; @@ -113,7 +143,7 @@ const Undoable = (reducer: any, undoableKeys: any, options = {}) => { undoableKeysSet.has(kv[0]) ); const newFuture = [...future]; - const newState = newFuture.pop(); + const newState = newFuture.pop() || []; const newPast = push(past, currentUndoableState); const nextState = { ...currentState, @@ -128,13 +158,14 @@ const Undoable = (reducer: any, undoableKeys: any, options = {}) => { /* Clear the history state. No side-effects on current state. */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. - function clear(currentState: any) { + function clear( + currentState: UndoableState + ): UndoableState { return { ...currentState, [pastKey]: [], [futureKey]: [], - [filterStateKey]: {}, + [filterStateKey]: undefined, [pendingKey]: null, }; } @@ -142,8 +173,11 @@ const Undoable = (reducer: any, undoableKeys: any, options = {}) => { /* Reduce current action, with no history side-effects */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. - function skip(currentState: any, action: any, filterState: any) { + function skip( + currentState: UndoableState, + action: AnyAction, + filterState: UndoableFilterState + ): UndoableState { const past = currentState[pastKey] || []; const future = currentState[futureKey] || []; const pending = currentState[pendingKey]; @@ -160,8 +194,11 @@ const Undoable = (reducer: any, undoableKeys: any, options = {}) => { /* Save current state in the history, then reduce action. */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. - function save(currentState: any, action: any, filterState: any) { + function save( + currentState: UndoableState, + action: AnyAction, + filterState: UndoableFilterState + ): UndoableState { const past = currentState[pastKey] || []; const currentUndoableState = Object.entries(currentState).filter((kv) => undoableKeysSet.has(kv[0]) @@ -181,8 +218,9 @@ const Undoable = (reducer: any, undoableKeys: any, options = {}) => { /* Save current state as pending history change. No other side effects. */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. - function stashPending(currentState: any) { + function stashPending( + currentState: UndoableState + ): UndoableState { const currentUndoableState = Object.entries(currentState).filter((kv) => undoableKeysSet.has(kv[0]) ); @@ -195,8 +233,9 @@ const Undoable = (reducer: any, undoableKeys: any, options = {}) => { /* Cancel pending history state change. No other side effects. */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. - function cancelPending(currentState: any) { + function cancelPending( + currentState: UndoableState + ): UndoableState { return { ...currentState, [pendingKey]: null, @@ -206,10 +245,12 @@ const Undoable = (reducer: any, undoableKeys: any, options = {}) => { /* Push pending state onto the history stack */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. - function applyPending(currentState: any) { - const past = currentState[pastKey] || []; + function applyPending( + currentState: UndoableState + ): UndoableState { + const past = currentState[pastKey]; const pendingState = currentState[pendingKey]; + if (pendingState === null) return currentState; const newPast = push(past, pendingState, historyLimit); const nextState = { ...currentState, @@ -221,14 +262,13 @@ const Undoable = (reducer: any, undoableKeys: any, options = {}) => { } return ( - currentState = { + currentState: UndoableState = { [pastKey]: [], [futureKey]: [], - [filterStateKey]: {}, + [filterStateKey]: undefined, [pendingKey]: null, }, - // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. - action: any + action: AnyAction ) => { if (debug > 1) console.log("---- ACTION", action.type); const aType = action.type; @@ -288,8 +328,7 @@ const Undoable = (reducer: any, undoableKeys: any, options = {}) => { }; }; -// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. -function push(arr: any, val: any, limit = undefined) { +function push(arr: T[], val: T, limit?: number) { /* functional array push, with a max length limit to the new array. Like Array.push, except it returns new array and discards as needed diff --git a/client/src/reducers/undoableConfig.ts b/client/src/reducers/undoableConfig.ts index ea961e4f..df0ef1f5 100644 --- a/client/src/reducers/undoableConfig.ts +++ b/client/src/reducers/undoableConfig.ts @@ -1,13 +1,19 @@ -import StateMachine from "../util/statemachine"; +import { AnyAction } from "redux"; +import { StateMachine, FsmActionFn, FsmErrorFn } from "../util/statemachine"; +import { + UndoableConfig, + UndoableState, + UndoableFilterState, + UndoableAction, + filterActionKey, + filterStateKey, +} from "./undoable"; import createFsmTransitions from "./undoableFsm"; -const actionKey = "@@undoable/filterAction"; -const stateKey = "@@undoable/filterState"; - /* these actions will not affect history */ -const skipOnActions = new Set([ +const skipOnActions = new Set([ "annoMatrix: init complete", "url changed", "initial data load start", @@ -58,12 +64,12 @@ const skipOnActions = new Set([ identical, repeated occurances of these action types will be debounced. Entire action must be identical (all keys). */ -const debounceOnActions = new Set([]); +const debounceOnActions = new Set([]); /* history will be cleared when these actions occur */ -const clearOnActions = new Set([ +const clearOnActions = new Set([ "initial data load complete", "initial data load error", ]); @@ -71,7 +77,7 @@ const clearOnActions = new Set([ /* An immediate history save will be done for these */ -const saveOnActions = new Set([ +const saveOnActions = new Set([ "categorical metadata filter select", "categorical metadata filter deselect", "categorical metadata filter all of these", @@ -119,35 +125,43 @@ StateMachine - processing complex action handling - see FSM graph for actual structure, in undoableFsm.js **/ +interface MyFilterState extends UndoableFilterState { + prevAction?: AnyAction; + fsm: StateMachine | null; +} +type MyUndoableAction = UndoableAction; + /* Default FSM actions. Used to side-effect transitions in the graph. See graph definition for the transitions that use each. Signature: (fsm, transition, reducerState, reducerAction) => undoableAction */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. -const stashPending = (fsm: any) => ({ - [actionKey]: "stashPending", - [stateKey]: { fsm }, +const stashPending: FsmActionFn = ( + fsm: StateMachine +) => ({ + [filterActionKey]: "stashPending", + [filterStateKey]: { fsm }, }); -const cancelPending = () => ({ - [actionKey]: "cancelPending", - [stateKey]: { fsm: null }, +const cancelPending: FsmActionFn = () => ({ + [filterActionKey]: "cancelPending", + [filterStateKey]: { fsm: null }, }); -const applyPending = () => ({ - [actionKey]: "applyPending", - [stateKey]: { fsm: null }, +const applyPending: FsmActionFn = () => ({ + [filterActionKey]: "applyPending", + [filterStateKey]: { fsm: null }, }); -// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'fsm' implicitly has an 'any' type. -const skip = (fsm, transition) => ({ - [actionKey]: "skip", - [stateKey]: { fsm: transition.to !== "done" ? fsm : null }, +const skip: FsmActionFn = (fsm, transition) => ({ + [filterActionKey]: "skip", + [filterStateKey]: { fsm: transition.to !== "done" ? fsm : null }, }); -const clear = () => ({ [actionKey]: "clear", [stateKey]: { fsm: null } }); -// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'fsm' implicitly has an 'any' type. -const save = (fsm, transition) => ({ - [actionKey]: "save", - [stateKey]: { fsm: transition.to !== "done" ? fsm : null }, +const clear: FsmActionFn = () => ({ + [filterActionKey]: "clear", + [filterStateKey]: { fsm: null }, +}); +const save: FsmActionFn = (fsm, transition) => ({ + [filterActionKey]: "save", + [filterStateKey]: { fsm: transition.to !== "done" ? fsm : null }, }); /* @@ -156,11 +170,13 @@ StateMachine when it doesn't know what to do. Signature: (fsm, event, from) => undoableAction */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. -const onFsmError = (fsm: any, event: any, from: any) => { +const onFsmError: FsmErrorFn = (fsm, event, from) => { console.error(`FSM error [event: "${event}", state: "${from}"]`, fsm); // In production, try to recover gracefully if we have unexpected state - return clear(); + return { + [filterActionKey]: "clear", + [filterStateKey]: { fsm: null }, + }; }; /* @@ -175,7 +191,11 @@ const fsmTransitions = createFsmTransitions( save ); /* State machine we clone whenever we need to run it */ -const seedFsm = new StateMachine("init", fsmTransitions, onFsmError); +const seedFsm = new StateMachine( + "init", + fsmTransitions, + onFsmError +); /* See undoable.js for description action filter interface description. @@ -185,53 +205,52 @@ Basic approach: * only implement complex state machines where absolutely required (eg, multi-event selection and the like) */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. -const actionFilter = (debug: any) => ( - // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. - state: any, - // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. - action: any, - // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. - prevFilterState: any -) => { - const actionType = action.type; - const filterState = { - ...prevFilterState, - prevAction: action, - }; - if (skipOnActions.has(actionType)) { - return { [actionKey]: "skip", [stateKey]: filterState }; - } - if ( - // @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'any' is not assignable to parame... Remove this comment to see the full error message - debounceOnActions.has(actionType) && - shallowObjectEq(action, prevFilterState.prevAction) - ) { - return { [actionKey]: "skip", [stateKey]: filterState }; - } - if (clearOnActions.has(actionType)) { - return { [actionKey]: "clear", [stateKey]: filterState }; - } - if (saveOnActions.has(actionType)) { - return { [actionKey]: "save", [stateKey]: filterState }; - } +const actionFilter = + (debug: boolean) => + ( + state: UndoableState, + action: AnyAction, + prevFilterState: MyFilterState | undefined + ): UndoableAction => { + const actionType = action.type; + prevFilterState = prevFilterState || { fsm: null }; + const filterState: MyFilterState = { + ...prevFilterState, + prevAction: action, + }; + if (skipOnActions.has(actionType)) { + return { [filterActionKey]: "skip", [filterStateKey]: filterState }; + } + if ( + debounceOnActions.has(actionType) && + prevFilterState.prevAction && + shallowObjectEq(action, prevFilterState.prevAction) + ) { + return { [filterActionKey]: "skip", [filterStateKey]: filterState }; + } + if (clearOnActions.has(actionType)) { + return { [filterActionKey]: "clear", [filterStateKey]: filterState }; + } + if (saveOnActions.has(actionType)) { + return { [filterActionKey]: "save", [filterStateKey]: filterState }; + } - /* + /* Else, something more complex OR unknown to us.... */ - if (seedFsm.events.has(actionType)) { - let { fsm } = filterState; - if (!fsm) { - /* no active FSM, so create one in init state */ - fsm = seedFsm.clone("init"); + if (seedFsm.events.has(actionType)) { + let { fsm } = filterState; + if (!fsm) { + /* no active FSM, so create one in init state */ + fsm = seedFsm.clone("init"); + } + return fsm.next(action.type, { state, action }); } - return fsm.next(action.type, { state, action }); - } - /* else, we have no idea what this is - skip it */ - if (debug) console.log("**** ACTION FILTER EVENT HANDLER MISS", actionType); - return { [actionKey]: "skip", [stateKey]: filterState }; -}; + /* else, we have no idea what this is - skip it */ + if (debug) console.log("**** ACTION FILTER EVENT HANDLER MISS", actionType); + return { [filterActionKey]: "skip", [filterStateKey]: filterState }; + }; /* return true if objA and objB are ===, OR if: @@ -239,8 +258,10 @@ return true if objA and objB are ===, OR if: - have same own properties - all values are strict equal (===) */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. -function shallowObjectEq(objA: any, objB: any) { +function shallowObjectEq( + objA: Record, + objB: Record +) { if (objA === objB) return true; if (!objA || !objB) return false; if (!shallowArrayEq(Object.keys(objA), Object.keys(objB))) return false; @@ -252,8 +273,7 @@ function shallowObjectEq(objA: any, objB: any) { return true if arrA and arrB contain the same strict-equal values, in the same order. */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. -function shallowArrayEq(arrA: any, arrB: any) { +function shallowArrayEq(arrA: unknown[], arrB: unknown[]) { if (arrA.length !== arrB.length) return false; for (let i = 0, l = arrA.length; i < l; i += 1) { if (arrA[i] !== arrB[i]) return false; @@ -268,7 +288,7 @@ Set to true or 1 for base logging, high number for more verbosity (currently onl or 2). */ const debug = false; -const undoableConfig = { +const undoableConfig: UndoableConfig = { debug, historyLimit: 50, // maximum history size actionFilter: actionFilter(debug), @@ -307,7 +327,7 @@ if (debug) { ); if (trivialOverlapWithFsm.size > 0) { console.error( - "Undoable misconfiguration - trivival action filter blocking FSM filter", + "Undoable misconfiguration - trivial action filter blocking FSM filter", [...trivialOverlapWithFsm] ); } diff --git a/client/src/reducers/undoableFsm.ts b/client/src/reducers/undoableFsm.ts index 642145fa..7d6b0a35 100644 --- a/client/src/reducers/undoableFsm.ts +++ b/client/src/reducers/undoableFsm.ts @@ -18,22 +18,16 @@ b) compound actions that should be collapsed into a single history change. */ -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS. -const createFsmTransitions = ( - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS. - stashPending: any, - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS. - cancelPending: any, - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS. - applyPending: any, - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS. - skip: any, - // @ts-expect-error ts-migrate(6133) FIXME: 'clear' is declared but its value is never read. - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS. - clear: any, - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS. - save: any -) => [ +import { StateMachine, FsmTransition, FsmActionFn } from "../util/statemachine"; + +const createFsmTransitions = ( + stashPending: FsmActionFn, + cancelPending: FsmActionFn, + applyPending: FsmActionFn, + skip: FsmActionFn, + _clear: FsmActionFn, + save: FsmActionFn +): FsmTransition[] => [ /* graph selection brushing */ { event: "graph brush start", @@ -52,12 +46,14 @@ const createFsmTransitions = ( from: "graph brush in progress", to: "done", /* if current selection is all, cancelPending. Else, applyPending */ - // @ts-expect-error ts-migrate(6133) FIXME: 'fsm' is declared but its value is never read. - // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. - action: (fsm: any, transition: any, data: any) => + action: ( + fsm: StateMachine, + transition: FsmTransition, + data: any // eslint-disable-line @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS. Requires state typing. + ) => data.state.graphSelection.selection.mode === "all" - ? cancelPending() - : applyPending(), + ? cancelPending(fsm, transition, data) + : applyPending(fsm, transition, data), }, { event: "graph brush end", @@ -84,12 +80,14 @@ const createFsmTransitions = ( from: "graph lasso in progress", to: "done", /* if current selection is all, cancelPending. Else, applyPending */ - // @ts-expect-error ts-migrate(6133) FIXME: 'fsm' is declared but its value is never read. - // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. - action: (fsm: any, transition: any, data: any) => + action: ( + fsm: StateMachine, + transition: FsmTransition, + data: any // eslint-disable-line @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS. Requires state typing. + ) => data.state.graphSelection.selection.mode === "all" - ? cancelPending() - : applyPending(), + ? cancelPending(fsm, transition, data) + : applyPending(fsm, transition, data), }, { event: "graph lasso end", diff --git a/client/src/util/statemachine/index.ts b/client/src/util/statemachine/index.ts index 05e7edd7..881bcfc6 100644 --- a/client/src/util/statemachine/index.ts +++ b/client/src/util/statemachine/index.ts @@ -13,18 +13,17 @@ Where: to: state_name_transitioning_to, from: state_name_transitioning_from, event: value_that_will_cause_transition, - action: optional_callback_upon_transition + action: callback_upon_transition } The transition will be provided to the action callback, so other data may be stored in the transition object for use by the action callback. * onErrorCallback - a callback function called if the FSM receives an event for which it has no defined transition. - Interface: - * states - property containing the state names. A Set(), contianing the + * states - property containing the state names. A Set(), containing the union of to: and from: values. - * events - property containing all of the accepted event values. Set(). + * events - property containing all of the accepted event values. Set(). * graph - a Map of Maps, organized as graph[eventValue][fromStateValue] * clone() - clone the entire statemachine. * next(eventValue) - drive the FSM to the next state. If the event @@ -40,47 +39,69 @@ Example: const fsm = new StateMachine("A", transitions, () => { throw new Error("oops") }); fsm.next("yo"); // returns 42 - */ -export default class StateMachine { - // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. - events: any; - // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. - graph: any; +export type FsmState = number | string; +export type FsmEvent = string; // by convention, we assume Events are redux action types, aka strings - // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. - onError: any; +export type FsmActionFn = ( + fsm: StateMachine, + transition: FsmTransition, + data: unknown +) => ActionReturnType; - // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. - state: any; +export interface FsmTransition { + from: FsmState; + to: FsmState; + event: FsmEvent; + action: FsmActionFn; +} - // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. - states: any; +export type FsmErrorFn = ( + fsm: StateMachine, + event: FsmEvent, + state: FsmState +) => ActionReturnType; - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS. - constructor(initState: any, transitions: any, onError: any) { - this.onError = onError || (() => undefined); +export class StateMachine { + events: Set; + + graph: Map>>; + + onError: FsmErrorFn; + + state: FsmState; + + states: Set; + + constructor( + initState: FsmState, + transitions: FsmTransition[], + onError: FsmErrorFn + ) { + this.onError = onError; this.state = initState; // all states this.states = new Set( - // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. - transitions.reduce((names: any, tsn: any) => { - names.push(tsn.from); - names.push(tsn.to); - return names; - }, []) + transitions.reduce( + (names: Array, tsn: FsmTransition) => { + names.push(tsn.from); + names.push(tsn.to); + return names; + }, + [] + ) ); // all transition names (aka events) - // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. - this.events = new Set(transitions.map((tsn: any) => tsn.event)); + this.events = new Set( + transitions.map((tsn: FsmTransition) => tsn.event) + ); // the transition graph. // graph[event][from] -> transition - // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. - this.graph = transitions.reduce((graph: any, tsn: any) => { + this.graph = transitions.reduce((graph, tsn) => { const { event, from } = tsn; if (!graph.has(event)) graph.set(event, new Map()); const tsnMap = graph.get(event); @@ -89,29 +110,23 @@ export default class StateMachine { }, new Map()); } - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS. - clone(initState: any) { - // @ts-expect-error ts-migrate(2554) FIXME: Expected 3 arguments, but got 2. - const fsm = new StateMachine(initState, []); - fsm.onError = this.onError; + clone(initState: FsmState): StateMachine { + const fsm = new StateMachine(initState, [], this.onError); fsm.states = this.states; fsm.events = this.events; fsm.graph = this.graph; return fsm; } - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS. - next(event: any, data: any) { + next(event: FsmEvent, data: unknown): ActionReturnType { const { graph, state } = this; const tsnMap = graph.get(event); - if (!tsnMap) return this.onError(this, event, state, undefined); + if (!tsnMap) return this.onError(this, event, state); const transition = tsnMap.get(state); - if (!transition) return this.onError(this, event, state, undefined); + if (!transition) return this.onError(this, event, state); this.state = transition.to; - return transition.action - ? transition.action(this, transition, data) - : undefined; + return transition.action(this, transition, data); } } From 3c3a7949860cff492d7acf9cc4dbad6212877c30 Mon Sep 17 00:00:00 2001 From: Severiano Badajoz Date: Thu, 12 Aug 2021 17:25:58 -0700 Subject: [PATCH 6/7] update: release process (#2277) Co-authored-by: maniarathi --- dev_docs/release_process.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev_docs/release_process.md b/dev_docs/release_process.md index f502a44c..3d09d2ad 100644 --- a/dev_docs/release_process.md +++ b/dev_docs/release_process.md @@ -26,7 +26,7 @@ Please scroll down the section below for how to release a patch version. Follow - Write the release title and release notes and add to [release notes document](https://docs.google.com/document/d/1KnHwkYfhyWO5H8BDcMu7y3ogjvq5Yi4OwpmZ8DB6w0Y/edit) 2. Create a release branch, eg, `release-version-0.16.0` 3. In the release branch, run `make create-release-candidate PART=[major | minor | patch]` where you choose major/minor/patch depending on which part of the version is being bumped (e.g., `0.2.9` -> `0.3.0` is minor version bump). This will bump the version and create a release *candidate* version (i.e. `0.3.0-rc.0`). -4. Commit and push the new branch. This will trigger tests to ensure that your branch isn't broken. +4. Push the new branch to origin and open a `DO NOT MERGE` PR, this will run tests on your branch. 5. Upload the release candidate to Test PyPI by running the command `make release-candidate-to-test-pypi`. (Make sure you are registered for PyPI and Test PyPI and you have write access to the cellxgene PyPI package for both). 6. Verify the release candidate in a fresh virtual environment by running `make install-release-test` which installs the cellxgene build you just uploaded the Test PyPI. 7. If you find errors with the release candidate, run `make recreate-release-candidate` to increment the release candidate version (i.e. `0.3.0-rc.0` -> `0.3.0-rc.1`). Then go back to Steps 5 and 6 to re-upload and re-test the new release candidate. From 3fdf5cac9d0f661642b58f637e3e6ee931ed4f3b Mon Sep 17 00:00:00 2001 From: Severiano Badajoz Date: Fri, 13 Aug 2021 10:01:10 -0700 Subject: [PATCH 7/7] fix: remove spaces in param setup (#2380) --- client/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/Makefile b/client/Makefile index 9b94a995..1169da30 100644 --- a/client/Makefile +++ b/client/Makefile @@ -5,9 +5,9 @@ GENE_SETS := $(if $(GENE_SETS),$(GENE_SETS),../backend/test/fixtures/pbmc3k-gene ANNOTATIONS_FILENAME := $(shell basename $(ANNOTATIONS)) GENE_SETS_FILENAME := $(shell basename $(GENE_SETS)) -CXG_CONFIG := $(if $(CXG_CONFIG), $(CXG_CONFIG),./__tests__/e2e/test_config.yaml) +CXG_CONFIG := $(if $(CXG_CONFIG),$(CXG_CONFIG),./__tests__/e2e/test_config.yaml) -CXG_AUTH_TYPE := $(if $(CXG_AUTH_TYPE), $(CXG_AUTH_TYPE),"test") +CXG_AUTH_TYPE := $(if $(CXG_AUTH_TYPE),$(CXG_AUTH_TYPE),"test") # Packaging