Skip to content

Commit c7b301d

Browse files
authored
feat: Add table types (#32)
1 parent 2feca44 commit c7b301d

File tree

8 files changed

+166
-7
lines changed

8 files changed

+166
-7
lines changed

.eslintrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"plugin:you-dont-need-lodash-underscore/all"
2020
],
2121
"rules": {
22+
"unicorn/no-null": 0,
2223
"unused-imports/no-unused-imports": "error",
2324
"no-console": "error",
2425
"@typescript-eslint/no-unused-vars": 0,
@@ -40,7 +41,6 @@
4041
{
4142
"files": ["src/grpc/**/*.ts"],
4243
"rules": {
43-
"unicorn/no-null": 0,
4444
"@typescript-eslint/naming-convention": 0
4545
}
4646
},

src/scalar/bool.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import { Bool as ArrowBool } from '@apache-arrow/esnext-esm';
22
import { boolean, isBooleanable } from 'boolean';
33

4+
import { Scalar } from './scalar.js';
45
import { isInvalid, NULL_VALUE } from './util.js';
56

6-
export class Bool {
7+
export class Bool implements Scalar<boolean> {
78
private _valid = false;
89
private _value = false;
910

1011
public constructor(v: unknown) {
11-
this.valid = v;
12+
this.value = v;
1213
return this;
1314
}
1415

@@ -24,7 +25,7 @@ export class Bool {
2425
return this._value;
2526
}
2627

27-
public set valid(value: unknown) {
28+
public set value(value: unknown) {
2829
if (isInvalid(value)) {
2930
this._valid = false;
3031
return;

src/scalar/scalar.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { DataType } from '@apache-arrow/esnext-esm';
2+
3+
export interface Scalar<T> {
4+
toString: () => string;
5+
get valid(): boolean;
6+
get value(): T;
7+
set value(value: unknown);
8+
get dataType(): DataType;
9+
}
10+
11+
export type Vector = Scalar<unknown>[];

src/schema/column.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ import { isDeepStrictEqual } from 'node:util';
33
import { DataType, Field, Bool } from '@apache-arrow/esnext-esm';
44

55
import * as arrow from './arrow.js';
6+
import { ClientMeta } from './meta.js';
7+
import { Resource } from './resource.js';
8+
9+
export type ColumnResolver = (meta: ClientMeta, resource: Resource, c: Column) => void;
610

711
export type Column = {
812
name: string;
@@ -12,6 +16,8 @@ export type Column = {
1216
notNull: boolean;
1317
incrementalKey: boolean;
1418
unique: boolean;
19+
resolver?: ColumnResolver;
20+
ignoreInTests: boolean;
1521
};
1622

1723
export const createColumn = ({
@@ -30,6 +36,7 @@ export const createColumn = ({
3036
notNull,
3137
incrementalKey,
3238
unique,
39+
ignoreInTests: false,
3340
});
3441

3542
export const formatColumn = (column: Column): string => {
@@ -58,13 +65,12 @@ export const fromArrowField = (field: Field): Column => {
5865
const unique = metadata.get(arrow.METADATA_UNIQUE) === arrow.METADATA_TRUE;
5966
const incrementalKey = metadata.get(arrow.METADATA_INCREMENTAL) === arrow.METADATA_TRUE;
6067

61-
return {
68+
return createColumn({
6269
name,
6370
type,
64-
description: '',
6571
primaryKey,
6672
notNull: !nullable,
6773
unique,
6874
incrementalKey,
69-
};
75+
});
7076
};

src/schema/meta.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { Binary, TimeNanosecond } from '@apache-arrow/esnext-esm';
2+
3+
import { UUIDType } from '../types/uuid.js';
4+
5+
import { Column, createColumn, ColumnResolver } from './column.js';
6+
import { Resource } from './resource.js';
7+
8+
export type ClientMeta = {
9+
id: () => string;
10+
};
11+
12+
export const parentCqUUIDResolver = (): ColumnResolver => {
13+
return (_: ClientMeta, r: Resource, c: Column) => {
14+
if (r.parent === null) {
15+
return r.setColumData(c.name, null);
16+
}
17+
const parentCqID = r.parent.getColumnData(cqIDColumn.name);
18+
if (parentCqID == null) {
19+
return r.setColumData(c.name, null);
20+
}
21+
return r.setColumData(c.name, parentCqID);
22+
};
23+
};
24+
25+
export const cqIDColumn = createColumn({
26+
name: '_cq_id',
27+
type: new UUIDType(),
28+
description: 'Internal CQ ID of the row',
29+
notNull: true,
30+
unique: true,
31+
});
32+
export const cqParentIDColumn = createColumn({
33+
name: '_cq_parent_id',
34+
type: new UUIDType(),
35+
description: 'Internal CQ ID of the parent row',
36+
resolver: parentCqUUIDResolver(),
37+
ignoreInTests: true,
38+
});
39+
export const cqSyncTimeColumn = createColumn({
40+
name: '_cq_sync_time',
41+
type: new TimeNanosecond(),
42+
description: 'Internal CQ row of when sync was started (this will be the same for all rows in a single fetch)',
43+
resolver: parentCqUUIDResolver(),
44+
ignoreInTests: true,
45+
});
46+
export const cqSourceNameColumn = createColumn({
47+
name: '_cq_source_name',
48+
type: new Binary(),
49+
description: 'Internal CQ row that references the source plugin name data was retrieved',
50+
resolver: parentCqUUIDResolver(),
51+
ignoreInTests: true,
52+
});

src/schema/resource.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { Scalar, Vector } from '../scalar/scalar.js';
2+
3+
import { Table } from './table.js';
4+
import { Nullable } from './types.js';
5+
6+
export class Resource {
7+
item: unknown;
8+
parent: Nullable<Resource>;
9+
table: Table;
10+
data: Vector;
11+
12+
constructor(table: Table, parent: Nullable<Resource>, item: unknown) {
13+
this.table = table;
14+
this.parent = parent;
15+
this.item = item;
16+
// TODO: Init from table columns
17+
this.data = [];
18+
}
19+
20+
getColumnData(columnName: string): Scalar<unknown> {
21+
const columnIndex = this.table.columns.findIndex((c) => c.name === columnName);
22+
if (columnIndex === undefined) {
23+
throw new Error(`Column '${columnName}' not found`);
24+
}
25+
return this.data[columnIndex];
26+
}
27+
28+
setColumData(columnName: string, value: unknown): void {
29+
const columnIndex = this.table.columns.findIndex((c) => c.name === columnName);
30+
if (columnIndex === undefined) {
31+
throw new Error(`Column '${columnName}' not found`);
32+
}
33+
this.data[columnIndex].value = value;
34+
}
35+
36+
getItem(): unknown {
37+
return this.item;
38+
}
39+
40+
setItem(item: unknown): void {
41+
this.item = item;
42+
}
43+
}

src/schema/table.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { Writable } from 'node:stream';
2+
3+
import { Column } from './column.js';
4+
import { ClientMeta } from './meta.js';
5+
import { Resource } from './resource.js';
6+
import { Nullable } from './types.js';
7+
8+
export type TableResolver = (clientMeta: ClientMeta, parent: Nullable<Resource>, stream: Writable) => void;
9+
export type RowResolver = (clientMeta: ClientMeta, resource: Resource) => void;
10+
export type Multiplexer = (clientMeta: ClientMeta) => ClientMeta[];
11+
export type Transform = (table: Table) => void;
12+
13+
export type Table = {
14+
name: string;
15+
title: string;
16+
description: string;
17+
columns: Column[];
18+
relations: Table[];
19+
transform: Transform;
20+
resolver: TableResolver;
21+
multiplexer: Multiplexer;
22+
postResourceResolver: RowResolver;
23+
preResourceResolver: RowResolver;
24+
isIncremental: boolean;
25+
ignoreInTests: boolean;
26+
parent: Nullable<Table>;
27+
pkConstraintName: string;
28+
};
29+
30+
export const getTablesNames = (tables: Table[]) => tables.map((table) => table.name);
31+
export const getTopLevelTableByName = (tables: Table[], name: string): Table | undefined =>
32+
tables.find((table) => table.name === name);
33+
34+
export const getTableByName = (tables: Table[], name: string): Table | undefined => {
35+
const table = tables.find((table) => table.name === name);
36+
if (table) {
37+
return table;
38+
}
39+
for (const table of tables) {
40+
const found = getTableByName(table.relations, name);
41+
if (found) {
42+
return found;
43+
}
44+
}
45+
};

src/schema/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export type Nullable<T> = T | null;

0 commit comments

Comments
 (0)