dx improvement; use input/output in specifications

This commit is contained in:
Chris Trevino 2022-04-08 15:42:04 -07:00
Родитель 07cab2509c
Коммит 48b16a0b98
32 изменённых файлов: 86 добавлений и 79 удалений

Просмотреть файл

@ -43,7 +43,7 @@ export abstract class BaseNode<T, Config> implements Node_2<T, Config> {
get config(): Maybe<Config>;
set config(value: Maybe<Config>);
protected abstract doRecalculate(): Promise<void> | void;
protected emit: (value: Maybe<T>, output?: string) => void;
protected emit: (value: Maybe<T>, output?: NodeOutput) => void;
protected emitError: (error: unknown) => void;
protected getInputErrors(): Record<SocketName, unknown>;
protected getInputValues(): Record<SocketName, Maybe<T>>;
@ -725,6 +725,11 @@ export type InputBinding = {
output?: string;
};
// Warning: (ae-missing-release-tag) "InputBindingSpecification" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type InputBindingSpecification = string | InputBinding;
// Warning: (ae-missing-release-tag) "InputColumnArgs" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@ -954,6 +959,14 @@ export enum NodeInput {
Source = "source"
}
// Warning: (ae-missing-release-tag) "NodeOutput" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export enum NodeOutput {
// (undocumented)
Target = "target"
}
// Warning: (ae-missing-release-tag) "NumericComparisonOperator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@ -1274,17 +1287,17 @@ export function stats(table: ColumnTable, columns?: string[]): Record<string, Co
export interface Step<T extends object = any> {
args: T;
id: string;
inputs: {
input: {
others?: InputBinding[];
} & Record<string, InputBinding>;
outputs: Record<string, string>;
output: Record<string, string>;
verb: Verb;
}
// Warning: (ae-missing-release-tag) "step" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
export function step<T extends object>({ verb, args, id, inputs, outputs, }: StepSpecification<T>): Step<T>;
export function step<T extends object>({ verb, args, id, input, output, }: StepSpecification<T>): Step<T>;
// Warning: (ae-missing-release-tag) "StepFunction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@ -1294,7 +1307,7 @@ export type StepFunction<T, Args> = (source: T, args: Args) => Promise<T> | T;
// Warning: (ae-missing-release-tag) "StepInput" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type StepInput = CopyWithPartial<Step<any>, 'args' | 'id' | 'inputs' | 'outputs'>;
export type StepInput = CopyWithPartial<Step<any>, 'args' | 'id' | 'input' | 'output'>;
// Warning: (ae-missing-release-tag) "StepNode" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@ -1317,10 +1330,10 @@ export interface StepSpecification<T extends object = any> {
args?: T;
description?: string;
id?: string;
inputs?: string | ({
others?: (string | InputBinding)[];
} & Record<string, string | InputBinding>);
outputs?: string | Record<string, string>;
input?: string | ({
others?: InputBindingSpecification[];
} & Record<string, InputBindingSpecification>);
output?: string | Record<string, string>;
verb: Verb;
}

Просмотреть файл

@ -48,7 +48,7 @@ export class DefaultPipeline implements Pipeline {
public get outputs(): string[] {
const result: string[] = []
this._steps.forEach(s => {
for (const value of Object.values(s.outputs)) {
for (const value of Object.values(s.output)) {
result.push(value)
}
})
@ -57,7 +57,7 @@ export class DefaultPipeline implements Pipeline {
public create(verb: Verb): Step[] {
const base: Step = factory({ verb })
base.outputs = { target: base.id }
base.output = { target: base.id }
return this.add(base)
}
@ -104,7 +104,7 @@ export class DefaultPipeline implements Pipeline {
}
private _unregisterStep(step: Step) {
for (const value of Object.values(step.outputs)) {
for (const value of Object.values(step.output)) {
this.store.delete(value)
}
}

Просмотреть файл

@ -29,10 +29,8 @@ describe('stepGraph', () => {
to: 'filled',
value: 1,
},
inputs: {
source: 'input',
},
outputs: { target: 'output' },
input: 'input',
output: 'output',
}),
]
@ -49,17 +47,14 @@ describe('stepGraph', () => {
step({
id: 'output-1',
verb: Verb.Fill,
inputs: {
source: { node: 'input' },
},
input: 'input',
args: {
to: 'filled',
value: 1,
},
outputs: { target: 'output-1' },
output: 'output-1',
}),
step({
id: 'output-2',
verb: Verb.Fill,
args: {
to: 'filled2',
@ -67,10 +62,8 @@ describe('stepGraph', () => {
},
// Note: this input is being auto-configured to the output of the previous node
// todo: restore auto-wiring?
inputs: {
source: { node: 'output-1' },
},
outputs: { target: 'output-2' },
input: 'output-1',
output: 'output-2',
}),
]

Просмотреть файл

@ -27,7 +27,6 @@ export function createGraph(
steps: Step[],
store: Store<TableContainer>,
): Graph<TableContainer> {
console.log('GRAPH OF ', steps)
const graph = new DefaultGraph<TableContainer>()
function getNode(id: string): Node<TableContainer> {
return graph.hasNode(id)
@ -43,7 +42,7 @@ export function createGraph(
graph.add(node)
// wire pinned outputs into the store
for (const [output, name] of Object.entries(step.outputs || EMPTY)) {
for (const [output, name] of Object.entries(step.output || EMPTY)) {
store.set(name, node.output(output))
}
}
@ -53,7 +52,7 @@ export function createGraph(
const current = graph.node(step.id)
// if any inputs nodes are in the graph, bind them
for (const [input, binding] of Object.entries(step.inputs)) {
for (const [input, binding] of Object.entries(step.input)) {
if (input !== 'others') {
const b = binding as InputBinding
current.bind({ input, node: getNode(b.node), output: b.output })

Просмотреть файл

@ -16,7 +16,7 @@ import type { InputBinding, Step, StepSpecification } from './types.js'
export type StepInput = CopyWithPartial<
Step<any>,
'args' | 'id' | 'inputs' | 'outputs'
'args' | 'id' | 'input' | 'output'
>
/**
* Factory function to create new verb configs
@ -29,15 +29,15 @@ export function step<T extends object>({
verb,
args = {} as any,
id = uuid(),
inputs = {},
outputs = {},
input = {},
output = {},
}: StepSpecification<T>): Step<T> {
const base = {
id,
args,
verb,
inputs: fixInputs(inputs),
outputs: fixOutputs(outputs),
input: fixInputs(input),
output: fixOutputs(output),
}
switch (verb) {
case Verb.Bin:
@ -154,11 +154,11 @@ export function step<T extends object>({
return base
}
function fixInputs(inputs: StepSpecification['inputs']): Step['inputs'] {
function fixInputs(inputs: StepSpecification['input']): Step['input'] {
if (typeof inputs === 'string') {
return { source: { node: inputs } }
} else {
const result: Step['inputs'] = { ...inputs } as any
const result: Step['input'] = { ...inputs } as any
// rewrite any shorthand inputs into full inputs
Object.keys(result).forEach((k: string) => {
const binding = result[k]
@ -176,7 +176,7 @@ function fixInputs(inputs: StepSpecification['inputs']): Step['inputs'] {
}
}
function fixOutputs(outputs: StepSpecification['outputs']): Step['outputs'] {
function fixOutputs(outputs: StepSpecification['output']): Step['output'] {
if (typeof outputs === 'string') {
return { target: outputs }
} else {

Просмотреть файл

@ -38,6 +38,8 @@ import type {
export type InputBinding = { node: string; output?: string }
export type InputBindingSpecification = string | InputBinding
export interface StepSpecification<T extends object = any> {
/**
* A unique identifier for this step
@ -64,18 +66,18 @@ export interface StepSpecification<T extends object = any> {
* Key = Input Socket Name
* Value = Socket Binding to other node
*/
inputs?:
input?:
| string
| ({
others?: (string | InputBinding)[]
} & Record<string, string | InputBinding>)
others?: InputBindingSpecification[]
} & Record<string, InputBindingSpecification>)
/**
* The observed outputs to record.
* Key = output socket name
* Value = store table name
*/
outputs?: string | Record<string, string>
output?: string | Record<string, string>
}
export interface Step<T extends object = any> {
@ -99,7 +101,7 @@ export interface Step<T extends object = any> {
* Key = Input Socket Name
* Value = Socket Binding to other node
*/
inputs: {
input: {
others?: InputBinding[]
} & Record<string, InputBinding>
@ -108,7 +110,7 @@ export interface Step<T extends object = any> {
* Key = output socket name
* Value = store table name
*/
outputs: Record<string, string>
output: Record<string, string>
}
export interface Specification {

Просмотреть файл

@ -34,8 +34,8 @@ export function useInternalStep(
// should we be forcing the i/o table name?
const _step = factory({
verb: opt.key,
inputs: step?.inputs,
outputs: step?.outputs,
input: step?.input,
output: step?.output,
})
// merge with the previous step in case input/output columns have been controlled
_step.args = formattedColumnArg(_step, table?.columnNames() || [])

Просмотреть файл

@ -32,7 +32,7 @@ export function useInternalTableStep(
const handleVerbChange = useCallback(
(verb: Verb) => {
const id = newTableName(verb)
const _step = factory({ verb, id, outputs: { default: id } })
const _step = factory({ verb, id, output: { default: id } })
_step.args = formattedColumnArg(_step.args)
setInternal(_step)
},

Просмотреть файл

@ -41,7 +41,7 @@ export const VerbDescription: React.FC<VerbDescriptionProps> = memo(
return loop(rows)
}, [rows])
const shouldShowOutputColumn = showOutputColumn && isOutputColumnStep(step)
const input = step.inputs[NodeInput.Default]?.node
const input = step.input[NodeInput.Source]?.node
return (
<Container style={style}>
<Verb>{step.verb}</Verb>

Просмотреть файл

@ -33,7 +33,7 @@ export const Aggregate: React.FC<StepComponentProps> = memo(function Aggregate({
const internal = useMemo(() => step as AggregateStep, [step])
const tbl = useLoadTable(
input || internal.inputs[NodeInput.Default]?.node,
input || internal.input[NodeInput.Source]?.node,
table,
store,
)

Просмотреть файл

@ -22,7 +22,7 @@ export const BooleanLogic: React.FC<StepComponentProps> = memo(
function BooleanLogic({ step, store, table, onChange, input }) {
const internal = useMemo(() => step as BooleanStep, [step])
const tbl = useLoadTable(
input || internal.inputs[NodeInput.Default]?.node,
input || internal.input[NodeInput.Source]?.node,
table,
store,
)

Просмотреть файл

@ -3,7 +3,7 @@
* Licensed under the MIT license. See LICENSE file in the project.
*/
import type { ConvertStep } from '@data-wrangling-components/core'
import { DataType, NodeInput,ParseType } from '@data-wrangling-components/core'
import { DataType, NodeInput, ParseType } from '@data-wrangling-components/core'
import { TextField } from '@fluentui/react'
import cloneDeep from 'lodash-es/cloneDeep.js'
import set from 'lodash-es/set.js'
@ -35,7 +35,7 @@ export const Convert: React.FC<StepComponentProps> = memo(function Convert({
}) {
const internal = useMemo(() => step as ConvertStep, [step])
const tbl = useLoadTable(
input || internal.inputs[NodeInput.Default]?.node,
input || internal.input[NodeInput.Source]?.node,
table,
store,
)

Просмотреть файл

@ -29,7 +29,7 @@ export const Derive: React.FC<StepComponentProps> = memo(function Derive({
const internal = useMemo(() => step as DeriveStep, [step])
const tbl = useLoadTable(
input || step.inputs[NodeInput.Default]?.node,
input || step.input[NodeInput.Source]?.node,
table,
store,
)

Просмотреть файл

@ -27,7 +27,7 @@ export const Filter: React.FC<StepComponentProps> = memo(function Filter({
}) {
const internal = useMemo(() => step as FilterStep, [step])
const tbl = useLoadTable(
input || internal.inputs[NodeInput.Default]?.node,
input || internal.input[NodeInput.Source]?.node,
table,
store,
)

Просмотреть файл

@ -17,7 +17,7 @@ export const JoinDescription: React.FC<StepDescriptionProps> = memo(
return [
{
before: 'with',
value: internal.inputs[NodeInput.Other]?.node,
value: internal.input[NodeInput.Other]?.node,
},
{
before: 'on',

Просмотреть файл

@ -37,7 +37,7 @@ export const Lookup: React.FC<StepComponentProps> = memo(function Lookup({
step={step}
store={store}
onChange={onChange}
input={internal.inputs[NodeInput.Other]?.node}
input={internal.input[NodeInput.Other]?.node}
/>
</LeftAlignedColumn>
</Container>

Просмотреть файл

@ -25,7 +25,7 @@ export const LookupDescription: React.FC<StepDescriptionProps> = memo(
return [
{
before: 'lookup from',
value: internal.inputs[NodeInput.Other]?.node,
value: internal.input[NodeInput.Other]?.node,
},
{
before: 'on',

Просмотреть файл

@ -32,7 +32,7 @@ export const Merge: React.FC<StepComponentProps> = memo(function Merge({
}) {
const internal = useMemo(() => step as MergeStep, [step])
const tbl = useLoadTable(
input || internal.inputs[NodeInput.Default]?.node,
input || internal.input[NodeInput.Source]?.node,
table,
store,
)

Просмотреть файл

@ -31,7 +31,7 @@ export const Orderby: React.FC<StepComponentProps> = memo(function Orderby({
const internal = useMemo(() => step as OrderbyStep, [step])
const tbl = useLoadTable(
input || step.inputs[NodeInput.Default]?.node,
input || step.input[NodeInput.Source]?.node,
table,
store,
)

Просмотреть файл

@ -33,7 +33,7 @@ export const Pivot: React.FC<StepComponentProps> = memo(function Pivot({
const internal = useMemo(() => step as PivotStep, [step])
const tbl = useLoadTable(
input || internal.inputs[NodeInput.Default]?.node,
input || internal.input[NodeInput.Source]?.node,
table,
store,
)

Просмотреть файл

@ -38,7 +38,7 @@ export const Recode: React.FC<StepComponentProps> = memo(function Recode({
const internal = useMemo(() => step as RecodeStep, [step])
const tbl = useLoadTable(
input || step.inputs[NodeInput.Default]?.node,
input || step.input[NodeInput.Source]?.node,
table,
store,
)

Просмотреть файл

@ -38,7 +38,7 @@ export const Rename: React.FC<StepComponentProps> = memo(function Rename({
const internal = useMemo(() => step as RenameStep, [step])
const tbl = useLoadTable(
input || step.inputs[NodeInput.Default]?.node,
input || step.input[NodeInput.Source]?.node,
table,
store,
)

Просмотреть файл

@ -20,7 +20,7 @@ import type { StepComponentProps } from '../../types.js'
export const SetOperation: React.FC<StepComponentProps> = memo(
function SetOperation({ step, store, table, onChange = noop, input }) {
const tbl = useLoadTable(
input || step.inputs[NodeInput.Source]?.node,
input || step.input[NodeInput.Source]?.node,
table,
store,
)
@ -29,9 +29,9 @@ export const SetOperation: React.FC<StepComponentProps> = memo(
const handleButtonClick = useCallback(() => {
onChange({
...step,
inputs: {
...step.inputs,
others: [...(step.inputs.others || []), { node: '' }] as any,
input: {
...step.input,
others: [...(step.input.others || []), { node: '' }] as any,
},
})
}, [step, onChange])
@ -58,15 +58,15 @@ function useOthers(
store?: TableStore,
) {
return useMemo(() => {
return Object.keys(step.inputs)
return Object.keys(step.input)
.filter(k => k !== NodeInput.Source)
.map((inputName, index) => {
const input = step.inputs[inputName]!
const input = step.input[inputName]!
const other = input.node
// on delete, remove the input
const handleDeleteClick = () => {
const update = { ...step, inputs: { ...step.inputs } }
const update = { ...step, inputs: { ...step.input } }
delete update.inputs[inputName]
onChange(update)
}

Просмотреть файл

@ -26,5 +26,5 @@ export const SetOperationDescription: React.FC<StepDescriptionProps> = memo(
},
)
export function otherInputNames(step: Step): string[] {
return (step.inputs.others || []).map(i => i.node)
return (step.input.others || []).map(i => i.node)
}

Просмотреть файл

@ -31,7 +31,7 @@ export const Spread: React.FC<StepComponentProps> = memo(function Spread({
const internal = useMemo(() => step as SpreadStep, [step])
const tbl = useLoadTable(
input || step.inputs[NodeInput.Default]?.node,
input || step.input[NodeInput.Source]?.node,
table,
store,
)

Просмотреть файл

@ -29,7 +29,7 @@ export const Unfold: React.FC<StepComponentProps> = memo(function Unfold({
const internal = useMemo(() => step as PivotStep, [step])
const tbl = useLoadTable(
input || internal.inputs[NodeInput.Default]?.node,
input || internal.input[NodeInput.Source]?.node,
table,
store,
)

Просмотреть файл

@ -18,7 +18,7 @@ import type { StepSubcomponentProps } from '../../../types.js'
export const ColumnListInputs: React.FC<StepSubcomponentProps> = memo(
function ColumnListInputs({ step, store, table, onChange, input, label }) {
const tbl = useLoadTable(
input || step.inputs[NodeInput.Default]?.node,
input || step.input[NodeInput.Source]?.node,
table,
store,
)

Просмотреть файл

@ -23,12 +23,12 @@ export const JoinInputs: React.FC<StepComponentProps> = memo(
const internal = useMemo(() => step as JoinStep, [step])
const leftTable = useLoadTable(
input || internal.inputs[NodeInput.Default]?.node,
input || internal.input[NodeInput.Source]?.node,
table,
store,
)
const rightTable = useLoadTable(
internal.inputs[NodeInput.Other]?.node,
internal.input[NodeInput.Other]?.node,
table,
store,
)
@ -57,7 +57,7 @@ export const JoinInputs: React.FC<StepComponentProps> = memo(
<TableDropdown
store={store}
label={`${upperFirst(label)} table`}
selectedKey={internal.inputs[NodeInput.Other]?.node}
selectedKey={internal.input[NodeInput.Other]?.node}
onChange={handleRightTableChange}
/>
</LeftAlignedRow>

Просмотреть файл

@ -42,7 +42,7 @@ export function withInputColumnDropdown(
// which will then be passed around with the container and thereby cached
// useLoadTable should return a TableContainer
const tbl = useLoadTable(
input || step.inputs[NodeInput.Default]?.node,
input || step.input[NodeInput.Source]?.node,
table,
store,
)

Просмотреть файл

@ -35,7 +35,7 @@ export function withInputTableDropdown(
<TableDropdown
store={store}
label={label || 'Input table'}
selectedKey={step.inputs[NodeInput.Default]?.node}
selectedKey={step.input[NodeInput.Source]?.node}
onChange={handleTableChange}
/>
</LeftAlignedRow>

Просмотреть файл

@ -147,7 +147,7 @@ export const DebugPage: React.FC = memo(function DebugPage() {
</Section>
</InputsSection>
{steps.map((step, index) => {
const output = outputs?.get(step.outputs?.default)?.table
const output = outputs?.get(step.output?.target)?.table
return (
<StepBlock key={`step-${index}`} className="step-block">
<Section title={`Step ${index + 1}`} subtitle={step.verb}>

Просмотреть файл

@ -25,8 +25,8 @@
"type": "string",
"description": "the node id"
},
"inputs": { "$ref": "#/definitions/StepInputs" },
"outputs": { "$ref": "#/definitions/StepOutputs" }
"input": { "$ref": "#/definitions/StepInputs" },
"output": { "$ref": "#/definitions/StepOutputs" }
},
"required": ["id"],
"oneOf": [