@snowtop/ent 0.1.0-alpha160-test4 → 0.1.0-alpha160-test6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/package.json +64 -0
- package/{scripts → dist/scripts}/custom_compiler.js +0 -0
- package/{scripts → dist/scripts}/custom_graphql.js +0 -0
- package/package.json +48 -6
- package/src/action/action.ts +330 -0
- package/src/action/executor.ts +453 -0
- package/src/action/experimental_action.ts +277 -0
- package/src/action/index.ts +31 -0
- package/src/action/operations.ts +967 -0
- package/src/action/orchestrator.ts +1527 -0
- package/src/action/privacy.ts +37 -0
- package/src/action/relative_value.ts +242 -0
- package/src/action/transaction.ts +38 -0
- package/src/auth/auth.ts +77 -0
- package/src/auth/index.ts +8 -0
- package/src/core/base.ts +367 -0
- package/src/core/clause.ts +1065 -0
- package/src/core/config.ts +219 -0
- package/src/core/const.ts +5 -0
- package/src/core/context.ts +135 -0
- package/src/core/convert.ts +106 -0
- package/src/core/date.ts +23 -0
- package/src/core/db.ts +498 -0
- package/src/core/ent.ts +1740 -0
- package/src/core/global_schema.ts +49 -0
- package/src/core/loaders/assoc_count_loader.ts +99 -0
- package/src/core/loaders/assoc_edge_loader.ts +250 -0
- package/src/core/loaders/index.ts +12 -0
- package/src/core/loaders/loader.ts +66 -0
- package/src/core/loaders/object_loader.ts +489 -0
- package/src/core/loaders/query_loader.ts +314 -0
- package/src/core/loaders/raw_count_loader.ts +175 -0
- package/src/core/logger.ts +49 -0
- package/src/core/privacy.ts +660 -0
- package/src/core/query/assoc_query.ts +240 -0
- package/src/core/query/custom_clause_query.ts +174 -0
- package/src/core/query/custom_query.ts +302 -0
- package/src/core/query/index.ts +9 -0
- package/src/core/query/query.ts +674 -0
- package/src/core/query_impl.ts +32 -0
- package/src/core/viewer.ts +52 -0
- package/src/ent.code-workspace +73 -0
- package/src/graphql/builtins/connection.ts +25 -0
- package/src/graphql/builtins/edge.ts +16 -0
- package/src/graphql/builtins/node.ts +12 -0
- package/src/graphql/graphql.ts +891 -0
- package/src/graphql/graphql_field_helpers.ts +221 -0
- package/src/graphql/index.ts +42 -0
- package/src/graphql/mutations/union.ts +39 -0
- package/src/graphql/node_resolver.ts +122 -0
- package/src/graphql/query/connection_type.ts +113 -0
- package/src/graphql/query/edge_connection.ts +171 -0
- package/src/graphql/query/page_info.ts +34 -0
- package/src/graphql/query/shared_edge_connection.ts +287 -0
- package/src/graphql/scalars/orderby_direction.ts +13 -0
- package/src/graphql/scalars/time.ts +38 -0
- package/src/imports/dataz/example1/_auth.ts +51 -0
- package/src/imports/dataz/example1/_viewer.ts +35 -0
- package/src/imports/index.ts +213 -0
- package/src/index.ts +145 -0
- package/src/parse_schema/parse.ts +585 -0
- package/src/schema/base_schema.ts +224 -0
- package/src/schema/field.ts +1087 -0
- package/src/schema/index.ts +53 -0
- package/src/schema/json_field.ts +94 -0
- package/src/schema/schema.ts +1028 -0
- package/src/schema/struct_field.ts +234 -0
- package/src/schema/union_field.ts +105 -0
- package/src/scripts/custom_compiler.ts +331 -0
- package/src/scripts/custom_graphql.ts +550 -0
- package/src/scripts/migrate_v0.1.ts +41 -0
- package/src/scripts/move_types.ts +131 -0
- package/src/scripts/read_schema.ts +67 -0
- package/src/setupPackage.js +42 -0
- package/src/testutils/action/complex_schemas.ts +517 -0
- package/src/testutils/builder.ts +422 -0
- package/src/testutils/context/test_context.ts +25 -0
- package/src/testutils/db/fixture.ts +32 -0
- package/src/testutils/db/temp_db.ts +941 -0
- package/src/testutils/db/value.ts +294 -0
- package/src/testutils/db_mock.ts +351 -0
- package/src/testutils/db_time_zone.ts +40 -0
- package/src/testutils/ent-graphql-tests/index.ts +653 -0
- package/src/testutils/fake_comms.ts +50 -0
- package/src/testutils/fake_data/const.ts +64 -0
- package/src/testutils/fake_data/events_query.ts +145 -0
- package/src/testutils/fake_data/fake_contact.ts +150 -0
- package/src/testutils/fake_data/fake_event.ts +150 -0
- package/src/testutils/fake_data/fake_tag.ts +139 -0
- package/src/testutils/fake_data/fake_user.ts +232 -0
- package/src/testutils/fake_data/index.ts +1 -0
- package/src/testutils/fake_data/internal.ts +8 -0
- package/src/testutils/fake_data/tag_query.ts +56 -0
- package/src/testutils/fake_data/test_helpers.ts +388 -0
- package/src/testutils/fake_data/user_query.ts +524 -0
- package/src/testutils/fake_log.ts +52 -0
- package/src/testutils/mock_date.ts +10 -0
- package/src/testutils/mock_log.ts +39 -0
- package/src/testutils/parse_sql.ts +685 -0
- package/src/testutils/test_edge_global_schema.ts +49 -0
- package/src/testutils/write.ts +70 -0
- package/src/tsc/ast.ts +351 -0
- package/src/tsc/compilerOptions.ts +85 -0
- package/src/tsc/move_generated.ts +191 -0
- package/src/tsc/transform.ts +226 -0
- package/src/tsc/transform_action.ts +224 -0
- package/src/tsc/transform_ent.ts +66 -0
- package/src/tsc/transform_schema.ts +546 -0
- package/tsconfig.json +20 -0
- package/core/query/shared_assoc_test.d.ts +0 -2
- package/core/query/shared_assoc_test.js +0 -804
- package/core/query/shared_test.d.ts +0 -21
- package/core/query/shared_test.js +0 -736
- package/graphql/query/shared_assoc_test.d.ts +0 -1
- package/graphql/query/shared_assoc_test.js +0 -203
- /package/{action → dist/action}/action.d.ts +0 -0
- /package/{action → dist/action}/action.js +0 -0
- /package/{action → dist/action}/executor.d.ts +0 -0
- /package/{action → dist/action}/executor.js +0 -0
- /package/{action → dist/action}/experimental_action.d.ts +0 -0
- /package/{action → dist/action}/experimental_action.js +0 -0
- /package/{action → dist/action}/index.d.ts +0 -0
- /package/{action → dist/action}/index.js +0 -0
- /package/{action → dist/action}/operations.d.ts +0 -0
- /package/{action → dist/action}/operations.js +0 -0
- /package/{action → dist/action}/orchestrator.d.ts +0 -0
- /package/{action → dist/action}/orchestrator.js +0 -0
- /package/{action → dist/action}/privacy.d.ts +0 -0
- /package/{action → dist/action}/privacy.js +0 -0
- /package/{action → dist/action}/relative_value.d.ts +0 -0
- /package/{action → dist/action}/relative_value.js +0 -0
- /package/{action → dist/action}/transaction.d.ts +0 -0
- /package/{action → dist/action}/transaction.js +0 -0
- /package/{auth → dist/auth}/auth.d.ts +0 -0
- /package/{auth → dist/auth}/auth.js +0 -0
- /package/{auth → dist/auth}/index.d.ts +0 -0
- /package/{auth → dist/auth}/index.js +0 -0
- /package/{core → dist/core}/base.d.ts +0 -0
- /package/{core → dist/core}/base.js +0 -0
- /package/{core → dist/core}/clause.d.ts +0 -0
- /package/{core → dist/core}/clause.js +0 -0
- /package/{core → dist/core}/config.d.ts +0 -0
- /package/{core → dist/core}/config.js +0 -0
- /package/{core → dist/core}/const.d.ts +0 -0
- /package/{core → dist/core}/const.js +0 -0
- /package/{core → dist/core}/context.d.ts +0 -0
- /package/{core → dist/core}/context.js +0 -0
- /package/{core → dist/core}/convert.d.ts +0 -0
- /package/{core → dist/core}/convert.js +0 -0
- /package/{core → dist/core}/date.d.ts +0 -0
- /package/{core → dist/core}/date.js +0 -0
- /package/{core → dist/core}/db.d.ts +0 -0
- /package/{core → dist/core}/db.js +0 -0
- /package/{core → dist/core}/ent.d.ts +0 -0
- /package/{core → dist/core}/ent.js +0 -0
- /package/{core → dist/core}/global_schema.d.ts +0 -0
- /package/{core → dist/core}/global_schema.js +0 -0
- /package/{core → dist/core}/loaders/assoc_count_loader.d.ts +0 -0
- /package/{core → dist/core}/loaders/assoc_count_loader.js +0 -0
- /package/{core → dist/core}/loaders/assoc_edge_loader.d.ts +0 -0
- /package/{core → dist/core}/loaders/assoc_edge_loader.js +0 -0
- /package/{core → dist/core}/loaders/index.d.ts +0 -0
- /package/{core → dist/core}/loaders/index.js +0 -0
- /package/{core → dist/core}/loaders/loader.d.ts +0 -0
- /package/{core → dist/core}/loaders/loader.js +0 -0
- /package/{core → dist/core}/loaders/object_loader.d.ts +0 -0
- /package/{core → dist/core}/loaders/object_loader.js +0 -0
- /package/{core → dist/core}/loaders/query_loader.d.ts +0 -0
- /package/{core → dist/core}/loaders/query_loader.js +0 -0
- /package/{core → dist/core}/loaders/raw_count_loader.d.ts +0 -0
- /package/{core → dist/core}/loaders/raw_count_loader.js +0 -0
- /package/{core → dist/core}/logger.d.ts +0 -0
- /package/{core → dist/core}/logger.js +0 -0
- /package/{core → dist/core}/privacy.d.ts +0 -0
- /package/{core → dist/core}/privacy.js +0 -0
- /package/{core → dist/core}/query/assoc_query.d.ts +0 -0
- /package/{core → dist/core}/query/assoc_query.js +0 -0
- /package/{core → dist/core}/query/custom_clause_query.d.ts +0 -0
- /package/{core → dist/core}/query/custom_clause_query.js +0 -0
- /package/{core → dist/core}/query/custom_query.d.ts +0 -0
- /package/{core → dist/core}/query/custom_query.js +0 -0
- /package/{core → dist/core}/query/index.d.ts +0 -0
- /package/{core → dist/core}/query/index.js +0 -0
- /package/{core → dist/core}/query/query.d.ts +0 -0
- /package/{core → dist/core}/query/query.js +0 -0
- /package/{core → dist/core}/query_impl.d.ts +0 -0
- /package/{core → dist/core}/query_impl.js +0 -0
- /package/{core → dist/core}/viewer.d.ts +0 -0
- /package/{core → dist/core}/viewer.js +0 -0
- /package/{graphql → dist/graphql}/builtins/connection.d.ts +0 -0
- /package/{graphql → dist/graphql}/builtins/connection.js +0 -0
- /package/{graphql → dist/graphql}/builtins/edge.d.ts +0 -0
- /package/{graphql → dist/graphql}/builtins/edge.js +0 -0
- /package/{graphql → dist/graphql}/builtins/node.d.ts +0 -0
- /package/{graphql → dist/graphql}/builtins/node.js +0 -0
- /package/{graphql → dist/graphql}/graphql.d.ts +0 -0
- /package/{graphql → dist/graphql}/graphql.js +0 -0
- /package/{graphql → dist/graphql}/graphql_field_helpers.d.ts +0 -0
- /package/{graphql → dist/graphql}/graphql_field_helpers.js +0 -0
- /package/{graphql → dist/graphql}/index.d.ts +0 -0
- /package/{graphql → dist/graphql}/index.js +0 -0
- /package/{graphql → dist/graphql}/mutations/union.d.ts +0 -0
- /package/{graphql → dist/graphql}/mutations/union.js +0 -0
- /package/{graphql → dist/graphql}/node_resolver.d.ts +0 -0
- /package/{graphql → dist/graphql}/node_resolver.js +0 -0
- /package/{graphql → dist/graphql}/query/connection_type.d.ts +0 -0
- /package/{graphql → dist/graphql}/query/connection_type.js +0 -0
- /package/{graphql → dist/graphql}/query/edge_connection.d.ts +0 -0
- /package/{graphql → dist/graphql}/query/edge_connection.js +0 -0
- /package/{graphql → dist/graphql}/query/page_info.d.ts +0 -0
- /package/{graphql → dist/graphql}/query/page_info.js +0 -0
- /package/{graphql → dist/graphql}/query/shared_edge_connection.d.ts +0 -0
- /package/{graphql → dist/graphql}/query/shared_edge_connection.js +0 -0
- /package/{graphql → dist/graphql}/scalars/orderby_direction.d.ts +0 -0
- /package/{graphql → dist/graphql}/scalars/orderby_direction.js +0 -0
- /package/{graphql → dist/graphql}/scalars/time.d.ts +0 -0
- /package/{graphql → dist/graphql}/scalars/time.js +0 -0
- /package/{imports → dist/imports}/dataz/example1/_auth.d.ts +0 -0
- /package/{imports → dist/imports}/dataz/example1/_auth.js +0 -0
- /package/{imports → dist/imports}/dataz/example1/_viewer.d.ts +0 -0
- /package/{imports → dist/imports}/dataz/example1/_viewer.js +0 -0
- /package/{imports → dist/imports}/index.d.ts +0 -0
- /package/{imports → dist/imports}/index.js +0 -0
- /package/{index.d.ts → dist/index.d.ts} +0 -0
- /package/{index.js → dist/index.js} +0 -0
- /package/{parse_schema → dist/parse_schema}/parse.d.ts +0 -0
- /package/{parse_schema → dist/parse_schema}/parse.js +0 -0
- /package/{schema → dist/schema}/base_schema.d.ts +0 -0
- /package/{schema → dist/schema}/base_schema.js +0 -0
- /package/{schema → dist/schema}/field.d.ts +0 -0
- /package/{schema → dist/schema}/field.js +0 -0
- /package/{schema → dist/schema}/index.d.ts +0 -0
- /package/{schema → dist/schema}/index.js +0 -0
- /package/{schema → dist/schema}/json_field.d.ts +0 -0
- /package/{schema → dist/schema}/json_field.js +0 -0
- /package/{schema → dist/schema}/schema.d.ts +0 -0
- /package/{schema → dist/schema}/schema.js +0 -0
- /package/{schema → dist/schema}/struct_field.d.ts +0 -0
- /package/{schema → dist/schema}/struct_field.js +0 -0
- /package/{schema → dist/schema}/union_field.d.ts +0 -0
- /package/{schema → dist/schema}/union_field.js +0 -0
- /package/{scripts → dist/scripts}/custom_compiler.d.ts +0 -0
- /package/{scripts → dist/scripts}/custom_graphql.d.ts +0 -0
- /package/{scripts → dist/scripts}/migrate_v0.1.d.ts +0 -0
- /package/{scripts → dist/scripts}/migrate_v0.1.js +0 -0
- /package/{scripts → dist/scripts}/move_types.d.ts +0 -0
- /package/{scripts → dist/scripts}/move_types.js +0 -0
- /package/{scripts → dist/scripts}/read_schema.d.ts +0 -0
- /package/{scripts → dist/scripts}/read_schema.js +0 -0
- /package/{testutils → dist/testutils}/action/complex_schemas.d.ts +0 -0
- /package/{testutils → dist/testutils}/action/complex_schemas.js +0 -0
- /package/{testutils → dist/testutils}/builder.d.ts +0 -0
- /package/{testutils → dist/testutils}/builder.js +0 -0
- /package/{testutils → dist/testutils}/context/test_context.d.ts +0 -0
- /package/{testutils → dist/testutils}/context/test_context.js +0 -0
- /package/{testutils → dist/testutils}/db/fixture.d.ts +0 -0
- /package/{testutils → dist/testutils}/db/fixture.js +0 -0
- /package/{testutils → dist/testutils}/db/temp_db.d.ts +0 -0
- /package/{testutils → dist/testutils}/db/temp_db.js +0 -0
- /package/{testutils → dist/testutils}/db/value.d.ts +0 -0
- /package/{testutils → dist/testutils}/db/value.js +0 -0
- /package/{testutils → dist/testutils}/db_mock.d.ts +0 -0
- /package/{testutils → dist/testutils}/db_mock.js +0 -0
- /package/{testutils → dist/testutils}/db_time_zone.d.ts +0 -0
- /package/{testutils → dist/testutils}/db_time_zone.js +0 -0
- /package/{testutils → dist/testutils}/ent-graphql-tests/index.d.ts +0 -0
- /package/{testutils → dist/testutils}/ent-graphql-tests/index.js +0 -0
- /package/{testutils → dist/testutils}/fake_comms.d.ts +0 -0
- /package/{testutils → dist/testutils}/fake_comms.js +0 -0
- /package/{testutils → dist/testutils}/fake_data/const.d.ts +0 -0
- /package/{testutils → dist/testutils}/fake_data/const.js +0 -0
- /package/{testutils → dist/testutils}/fake_data/events_query.d.ts +0 -0
- /package/{testutils → dist/testutils}/fake_data/events_query.js +0 -0
- /package/{testutils → dist/testutils}/fake_data/fake_contact.d.ts +0 -0
- /package/{testutils → dist/testutils}/fake_data/fake_contact.js +0 -0
- /package/{testutils → dist/testutils}/fake_data/fake_event.d.ts +0 -0
- /package/{testutils → dist/testutils}/fake_data/fake_event.js +0 -0
- /package/{testutils → dist/testutils}/fake_data/fake_tag.d.ts +0 -0
- /package/{testutils → dist/testutils}/fake_data/fake_tag.js +0 -0
- /package/{testutils → dist/testutils}/fake_data/fake_user.d.ts +0 -0
- /package/{testutils → dist/testutils}/fake_data/fake_user.js +0 -0
- /package/{testutils → dist/testutils}/fake_data/index.d.ts +0 -0
- /package/{testutils → dist/testutils}/fake_data/index.js +0 -0
- /package/{testutils → dist/testutils}/fake_data/internal.d.ts +0 -0
- /package/{testutils → dist/testutils}/fake_data/internal.js +0 -0
- /package/{testutils → dist/testutils}/fake_data/tag_query.d.ts +0 -0
- /package/{testutils → dist/testutils}/fake_data/tag_query.js +0 -0
- /package/{testutils → dist/testutils}/fake_data/test_helpers.d.ts +0 -0
- /package/{testutils → dist/testutils}/fake_data/test_helpers.js +0 -0
- /package/{testutils → dist/testutils}/fake_data/user_query.d.ts +0 -0
- /package/{testutils → dist/testutils}/fake_data/user_query.js +0 -0
- /package/{testutils → dist/testutils}/fake_log.d.ts +0 -0
- /package/{testutils → dist/testutils}/fake_log.js +0 -0
- /package/{testutils → dist/testutils}/mock_date.d.ts +0 -0
- /package/{testutils → dist/testutils}/mock_date.js +0 -0
- /package/{testutils → dist/testutils}/mock_log.d.ts +0 -0
- /package/{testutils → dist/testutils}/mock_log.js +0 -0
- /package/{testutils → dist/testutils}/parse_sql.d.ts +0 -0
- /package/{testutils → dist/testutils}/parse_sql.js +0 -0
- /package/{testutils → dist/testutils}/test_edge_global_schema.d.ts +0 -0
- /package/{testutils → dist/testutils}/test_edge_global_schema.js +0 -0
- /package/{testutils → dist/testutils}/write.d.ts +0 -0
- /package/{testutils → dist/testutils}/write.js +0 -0
- /package/{tsc → dist/tsc}/ast.d.ts +0 -0
- /package/{tsc → dist/tsc}/ast.js +0 -0
- /package/{tsc → dist/tsc}/compilerOptions.d.ts +0 -0
- /package/{tsc → dist/tsc}/compilerOptions.js +0 -0
- /package/{tsc → dist/tsc}/move_generated.d.ts +0 -0
- /package/{tsc → dist/tsc}/move_generated.js +0 -0
- /package/{tsc → dist/tsc}/transform.d.ts +0 -0
- /package/{tsc → dist/tsc}/transform.js +0 -0
- /package/{tsc → dist/tsc}/transform_action.d.ts +0 -0
- /package/{tsc → dist/tsc}/transform_action.js +0 -0
- /package/{tsc → dist/tsc}/transform_ent.d.ts +0 -0
- /package/{tsc → dist/tsc}/transform_ent.js +0 -0
- /package/{tsc → dist/tsc}/transform_schema.d.ts +0 -0
- /package/{tsc → dist/tsc}/transform_schema.js +0 -0
|
@@ -0,0 +1,941 @@
|
|
|
1
|
+
import { Client as PGClient } from "pg";
|
|
2
|
+
import DB, { Sqlite, Dialect, Client, SyncClient } from "../../core/db";
|
|
3
|
+
// this should only be used in tests so we expect to be able to import without shenanigans
|
|
4
|
+
import sqlite, { Database as SqliteDatabase } from "better-sqlite3";
|
|
5
|
+
import { loadConfig } from "../../core/config";
|
|
6
|
+
import * as fs from "fs";
|
|
7
|
+
import { ConstraintType, DBType, Field, getFields } from "../../schema";
|
|
8
|
+
import { snakeCase } from "snake-case";
|
|
9
|
+
import { BuilderSchema, getTableName } from "../builder";
|
|
10
|
+
import { Ent } from "../../core/base";
|
|
11
|
+
import { testEdgeGlobalSchema } from "../test_edge_global_schema";
|
|
12
|
+
|
|
13
|
+
interface SchemaItem {
|
|
14
|
+
name: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface Column extends SchemaItem {
|
|
18
|
+
datatype(): string;
|
|
19
|
+
nullable?: boolean; // defaults to false
|
|
20
|
+
primaryKey?: boolean;
|
|
21
|
+
unique?: boolean;
|
|
22
|
+
default?: string;
|
|
23
|
+
index?: boolean | indexOptions;
|
|
24
|
+
foreignKey?: { table: string; col: string };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface Constraint extends SchemaItem {
|
|
28
|
+
generate(): string;
|
|
29
|
+
postCreate?(): boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface Index extends SchemaItem {
|
|
33
|
+
generate(): string;
|
|
34
|
+
postCreate?(): boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface PostCreateIndex extends Index {
|
|
38
|
+
postCreate(): boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// TODO need a better shared name for Table|Type
|
|
42
|
+
export interface CoreConcept {
|
|
43
|
+
name: string;
|
|
44
|
+
|
|
45
|
+
create(): string;
|
|
46
|
+
postCreate?(): string[];
|
|
47
|
+
drop(): string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface Table extends CoreConcept {
|
|
51
|
+
columns: Column[];
|
|
52
|
+
constraints?: Constraint[];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
type options = Pick<
|
|
56
|
+
Column,
|
|
57
|
+
"nullable" | "primaryKey" | "default" | "foreignKey" | "unique" | "index"
|
|
58
|
+
>;
|
|
59
|
+
|
|
60
|
+
export function primaryKey(name: string, cols: string[]): Constraint {
|
|
61
|
+
return {
|
|
62
|
+
name: name,
|
|
63
|
+
generate() {
|
|
64
|
+
return `CONSTRAINT ${name} PRIMARY KEY(${cols.join(",")})`;
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function foreignKey(
|
|
70
|
+
name: string,
|
|
71
|
+
cols: string[],
|
|
72
|
+
fkey: { table: string; cols: string[] },
|
|
73
|
+
): Constraint {
|
|
74
|
+
return {
|
|
75
|
+
name,
|
|
76
|
+
generate() {
|
|
77
|
+
return `CONSTRAINT ${name} FOREIGN KEY(${cols.join(",")}) REFERENCES ${
|
|
78
|
+
fkey.table
|
|
79
|
+
}(${fkey.cols.join(",")})`;
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function check(name: string, condition: string): Constraint {
|
|
85
|
+
return {
|
|
86
|
+
name,
|
|
87
|
+
generate() {
|
|
88
|
+
return `CONSTRAINT ${name} CHECK(${condition})`;
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function unique(name: string, cols: string[], tableName: string): Constraint {
|
|
94
|
+
return {
|
|
95
|
+
name,
|
|
96
|
+
generate() {
|
|
97
|
+
if (Dialect.SQLite === DB.getDialect()) {
|
|
98
|
+
return `UNIQUE (${cols.join(",")})`;
|
|
99
|
+
}
|
|
100
|
+
return `ALTER TABLE ${tableName} ADD CONSTRAINT ${name} UNIQUE (${cols.join(
|
|
101
|
+
", ",
|
|
102
|
+
)});`;
|
|
103
|
+
},
|
|
104
|
+
postCreate() {
|
|
105
|
+
return Dialect.Postgres === DB.getDialect();
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
interface indexOptions {
|
|
111
|
+
type?: string;
|
|
112
|
+
unique?: boolean;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function isPostCreateIndex(s: SchemaItem): s is PostCreateIndex {
|
|
116
|
+
return (
|
|
117
|
+
(s as PostCreateIndex).postCreate !== undefined &&
|
|
118
|
+
(s as PostCreateIndex).postCreate()
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function index(
|
|
123
|
+
tableName: string,
|
|
124
|
+
cols: string[],
|
|
125
|
+
opts?: indexOptions,
|
|
126
|
+
): Index {
|
|
127
|
+
const name = `${tableName}_${cols.join("_")}_idx`;
|
|
128
|
+
return {
|
|
129
|
+
name,
|
|
130
|
+
generate() {
|
|
131
|
+
if (opts?.unique && Dialect.SQLite === DB.getDialect()) {
|
|
132
|
+
return `UNIQUE (${cols.join(",")})`;
|
|
133
|
+
}
|
|
134
|
+
return `CREATE ${
|
|
135
|
+
opts?.unique ? "UNIQUE " : ""
|
|
136
|
+
}INDEX ${name} ON ${tableName} USING ${
|
|
137
|
+
opts?.type || "btree"
|
|
138
|
+
} (${cols.join(",")});`;
|
|
139
|
+
},
|
|
140
|
+
postCreate() {
|
|
141
|
+
return Dialect.Postgres === DB.getDialect() && !!opts?.unique;
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function uuid(name: string, opts?: options): Column {
|
|
147
|
+
return {
|
|
148
|
+
name,
|
|
149
|
+
datatype() {
|
|
150
|
+
return "uuid";
|
|
151
|
+
},
|
|
152
|
+
...opts,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function text(name: string, opts?: options): Column {
|
|
157
|
+
return {
|
|
158
|
+
name,
|
|
159
|
+
datatype() {
|
|
160
|
+
return "TEXT";
|
|
161
|
+
},
|
|
162
|
+
...opts,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function enumCol(name: string, type: string): Column {
|
|
167
|
+
return {
|
|
168
|
+
name,
|
|
169
|
+
datatype() {
|
|
170
|
+
return type;
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function timestamp(name: string, opts?: options): Column {
|
|
176
|
+
return {
|
|
177
|
+
name,
|
|
178
|
+
datatype() {
|
|
179
|
+
return "TIMESTAMP WITHOUT TIME ZONE";
|
|
180
|
+
},
|
|
181
|
+
...opts,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function timestamptz(name: string, opts?: options): Column {
|
|
186
|
+
return {
|
|
187
|
+
name,
|
|
188
|
+
datatype() {
|
|
189
|
+
if (DB.getDialect() === Dialect.Postgres) {
|
|
190
|
+
return "TIMESTAMP WITH TIME ZONE";
|
|
191
|
+
} else {
|
|
192
|
+
return "TEXT";
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
...opts,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export function time(name: string, opts?: options): Column {
|
|
200
|
+
return {
|
|
201
|
+
name,
|
|
202
|
+
datatype() {
|
|
203
|
+
return "TIME WITHOUT TIME ZONE";
|
|
204
|
+
},
|
|
205
|
+
...opts,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function timetz(name: string, opts?: options): Column {
|
|
210
|
+
return {
|
|
211
|
+
name,
|
|
212
|
+
datatype() {
|
|
213
|
+
return "TIME WITH TIME ZONE";
|
|
214
|
+
},
|
|
215
|
+
...opts,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function date(name: string, opts?: options): Column {
|
|
220
|
+
return {
|
|
221
|
+
name,
|
|
222
|
+
datatype() {
|
|
223
|
+
return "DATE";
|
|
224
|
+
},
|
|
225
|
+
...opts,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export function bool(name: string, opts?: options): Column {
|
|
230
|
+
const dialect = DB.getDialect();
|
|
231
|
+
if (opts?.default === "FALSE" && dialect === Dialect.SQLite) {
|
|
232
|
+
opts.default = "0";
|
|
233
|
+
}
|
|
234
|
+
return {
|
|
235
|
+
name,
|
|
236
|
+
datatype() {
|
|
237
|
+
if (dialect === Dialect.Postgres) {
|
|
238
|
+
return "BOOLEAN";
|
|
239
|
+
}
|
|
240
|
+
return "INTEGER";
|
|
241
|
+
},
|
|
242
|
+
...opts,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export function integer(name: string, opts?: options): Column {
|
|
247
|
+
return {
|
|
248
|
+
name,
|
|
249
|
+
datatype() {
|
|
250
|
+
return "INTEGER";
|
|
251
|
+
},
|
|
252
|
+
...opts,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export function float(name: string, opts?: options): Column {
|
|
257
|
+
return {
|
|
258
|
+
name,
|
|
259
|
+
datatype() {
|
|
260
|
+
return "REAL";
|
|
261
|
+
},
|
|
262
|
+
...opts,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export function json(name: string, opts?: options): Column {
|
|
267
|
+
return {
|
|
268
|
+
name,
|
|
269
|
+
datatype() {
|
|
270
|
+
return "JSON";
|
|
271
|
+
},
|
|
272
|
+
...opts,
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export function jsonb(name: string, opts?: options): Column {
|
|
277
|
+
return {
|
|
278
|
+
name,
|
|
279
|
+
datatype() {
|
|
280
|
+
return "JSONB";
|
|
281
|
+
},
|
|
282
|
+
...opts,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function list(name: string, col: Column, opts?: options): Column {
|
|
287
|
+
return {
|
|
288
|
+
name,
|
|
289
|
+
datatype() {
|
|
290
|
+
return `${col.datatype()}[]`;
|
|
291
|
+
},
|
|
292
|
+
...opts,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export function textList(name: string, opts?: options): Column {
|
|
297
|
+
return list(name, text(name), opts);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export function integerList(name: string, opts?: options): Column {
|
|
301
|
+
return list(name, integer(name), opts);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export function uuidList(name: string, opts?: options): Column {
|
|
305
|
+
return list(name, uuid(name), opts);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export function timestampList(name: string, opts?: options): Column {
|
|
309
|
+
return list(name, timestamp(name), opts);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export function timestamptzList(name: string, opts?: options): Column {
|
|
313
|
+
return list(name, timestamptz(name), opts);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export function timeList(name: string, opts?: options): Column {
|
|
317
|
+
return list(name, time(name), opts);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export function timetzList(name: string, opts?: options): Column {
|
|
321
|
+
return list(name, timetz(name), opts);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export function dateList(name: string, opts?: options): Column {
|
|
325
|
+
return list(name, date(name), opts);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export function boolList(name: string, opts?: options): Column {
|
|
329
|
+
return list(name, bool(name), opts);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export function table(name: string, ...items: SchemaItem[]): Table {
|
|
333
|
+
let cols: Column[] = [];
|
|
334
|
+
let constraints: Constraint[] = [];
|
|
335
|
+
let indexes: Index[] = [];
|
|
336
|
+
|
|
337
|
+
for (const item of items) {
|
|
338
|
+
if ((item as Column).datatype !== undefined) {
|
|
339
|
+
const col = item as Column;
|
|
340
|
+
if (col.index) {
|
|
341
|
+
let opts: indexOptions = {
|
|
342
|
+
type: "btree",
|
|
343
|
+
};
|
|
344
|
+
if (col.index === true) {
|
|
345
|
+
opts = {
|
|
346
|
+
type: "btree",
|
|
347
|
+
};
|
|
348
|
+
} else {
|
|
349
|
+
opts = col.index;
|
|
350
|
+
}
|
|
351
|
+
indexes.push(index(name, [col.name], opts));
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// add it as a constraint
|
|
355
|
+
if (col.foreignKey) {
|
|
356
|
+
constraints.push(
|
|
357
|
+
foreignKey(`${name}_${col.name}_fkey`, [col.name], {
|
|
358
|
+
table: col.foreignKey.table,
|
|
359
|
+
cols: [col.foreignKey.col],
|
|
360
|
+
}),
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
cols.push(item as Column);
|
|
364
|
+
} else if ((item as Constraint).generate !== undefined) {
|
|
365
|
+
if (isPostCreateIndex(item) && item.postCreate()) {
|
|
366
|
+
indexes.push(item);
|
|
367
|
+
} else {
|
|
368
|
+
constraints.push(item as Constraint);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
return {
|
|
374
|
+
name,
|
|
375
|
+
columns: cols,
|
|
376
|
+
constraints: constraints,
|
|
377
|
+
create() {
|
|
378
|
+
let schemaStr = cols.map((col) => {
|
|
379
|
+
let parts = [col.name, col.datatype()];
|
|
380
|
+
if (!col.nullable) {
|
|
381
|
+
parts.push("NOT NULL");
|
|
382
|
+
}
|
|
383
|
+
if (col.primaryKey) {
|
|
384
|
+
parts.push("PRIMARY KEY");
|
|
385
|
+
}
|
|
386
|
+
if (col.default !== undefined) {
|
|
387
|
+
if (Dialect.SQLite === DB.getDialect()) {
|
|
388
|
+
parts.push(`DEFAULT "${col.default}"`);
|
|
389
|
+
} else {
|
|
390
|
+
parts.push(`DEFAULT ${col.default}`);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (col.unique) {
|
|
395
|
+
parts.push("UNIQUE");
|
|
396
|
+
}
|
|
397
|
+
return parts.join(" ");
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
constraints.forEach((constraint) =>
|
|
401
|
+
schemaStr.push(constraint.generate()),
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
return `CREATE TABLE IF NOT EXISTS ${name} (\n ${schemaStr})`;
|
|
405
|
+
},
|
|
406
|
+
postCreate() {
|
|
407
|
+
return indexes.map((index) => index.generate());
|
|
408
|
+
},
|
|
409
|
+
drop() {
|
|
410
|
+
return `DROP TABLE IF EXISTS ${name}`;
|
|
411
|
+
},
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
export function enumType(name: string, values: string[]): CoreConcept {
|
|
416
|
+
return {
|
|
417
|
+
name,
|
|
418
|
+
drop() {
|
|
419
|
+
return `DROP TYPE ${name}`;
|
|
420
|
+
},
|
|
421
|
+
create() {
|
|
422
|
+
return `CREATE TYPE ${name} as ENUM(${values.join(", ")})`;
|
|
423
|
+
},
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function randomDB(): string {
|
|
428
|
+
let str = Math.random().toString(16).substring(2);
|
|
429
|
+
|
|
430
|
+
// always ensure it starts with an alpha character
|
|
431
|
+
return "abcdefghijklmnopqrstuvwxyz"[Math.floor(Math.random() * 26)] + str;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
interface TempDBOptions {
|
|
435
|
+
dialect: Dialect;
|
|
436
|
+
sqliteConnString?: string;
|
|
437
|
+
tables?: CoreConcept[] | (() => CoreConcept[]);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
export class TempDB {
|
|
441
|
+
private db: string;
|
|
442
|
+
private client: PGClient;
|
|
443
|
+
private dbClient: PGClient;
|
|
444
|
+
private tables = new Map<string, CoreConcept>();
|
|
445
|
+
private dialect: Dialect;
|
|
446
|
+
private sqlite: SqliteDatabase;
|
|
447
|
+
private setTables: CoreConcept[] | (() => CoreConcept[]) | undefined;
|
|
448
|
+
private sqliteConnString: string | undefined;
|
|
449
|
+
|
|
450
|
+
constructor(dialect: Dialect, tables?: CoreConcept[] | (() => CoreConcept[]));
|
|
451
|
+
constructor(opts: TempDBOptions);
|
|
452
|
+
constructor(
|
|
453
|
+
dialect: Dialect | TempDBOptions,
|
|
454
|
+
tables?: CoreConcept[] | (() => CoreConcept[]),
|
|
455
|
+
) {
|
|
456
|
+
if (typeof dialect === "string") {
|
|
457
|
+
this.dialect = dialect;
|
|
458
|
+
this.setTables = tables;
|
|
459
|
+
} else {
|
|
460
|
+
this.dialect = dialect.dialect;
|
|
461
|
+
this.setTables = dialect.tables;
|
|
462
|
+
this.sqliteConnString = dialect.sqliteConnString;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
getDialect() {
|
|
467
|
+
return this.dialect;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// NB: this won't be set until after beforeAll() is called since it depends on
|
|
471
|
+
// dialect being correctly set
|
|
472
|
+
__getTables() {
|
|
473
|
+
return this.tables;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
async beforeAll(setupConnString: boolean = true) {
|
|
477
|
+
if (this.dialect === Dialect.Postgres) {
|
|
478
|
+
const user = process.env.POSTGRES_USER || "";
|
|
479
|
+
const password = process.env.POSTGRES_PASSWORD || "";
|
|
480
|
+
|
|
481
|
+
this.client = new PGClient({
|
|
482
|
+
host: "localhost",
|
|
483
|
+
user,
|
|
484
|
+
password,
|
|
485
|
+
});
|
|
486
|
+
await this.client.connect();
|
|
487
|
+
|
|
488
|
+
this.db = randomDB();
|
|
489
|
+
|
|
490
|
+
await this.client.query(`CREATE DATABASE ${this.db}`);
|
|
491
|
+
|
|
492
|
+
if (setupConnString) {
|
|
493
|
+
delete process.env.DB_CONNECTION_STRING;
|
|
494
|
+
let connStr = "";
|
|
495
|
+
if (user && password) {
|
|
496
|
+
connStr = `postgres://${user}:${password}@localhost:5432/${this.db}`;
|
|
497
|
+
} else {
|
|
498
|
+
connStr = `postgres://localhost/${this.db}?`;
|
|
499
|
+
}
|
|
500
|
+
DB.initDB({
|
|
501
|
+
connectionString: connStr,
|
|
502
|
+
cfg: {
|
|
503
|
+
max: 100,
|
|
504
|
+
idleTimeoutMillis: 100,
|
|
505
|
+
},
|
|
506
|
+
});
|
|
507
|
+
} else {
|
|
508
|
+
// will probably be setup via loadConfig
|
|
509
|
+
delete process.env.DB_CONNECTION_STRING;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
this.dbClient = new PGClient({
|
|
513
|
+
host: "localhost",
|
|
514
|
+
database: this.db,
|
|
515
|
+
user,
|
|
516
|
+
password,
|
|
517
|
+
});
|
|
518
|
+
await this.dbClient.connect();
|
|
519
|
+
} else {
|
|
520
|
+
let connString: string;
|
|
521
|
+
if (this.sqliteConnString) {
|
|
522
|
+
connString = this.sqliteConnString;
|
|
523
|
+
} else {
|
|
524
|
+
if (process.env.DB_CONNECTION_STRING === undefined) {
|
|
525
|
+
throw new Error(
|
|
526
|
+
`DB_CONNECTION_STRING required for sqlite if sqliteConnString is not set`,
|
|
527
|
+
);
|
|
528
|
+
}
|
|
529
|
+
connString = process.env.DB_CONNECTION_STRING;
|
|
530
|
+
}
|
|
531
|
+
const filePath = connString.substr(10);
|
|
532
|
+
this.sqlite = sqlite(filePath);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
if (this.setTables) {
|
|
536
|
+
let tables: CoreConcept[] = [];
|
|
537
|
+
if (typeof this.setTables === "function") {
|
|
538
|
+
tables = this.setTables();
|
|
539
|
+
} else {
|
|
540
|
+
tables = this.setTables;
|
|
541
|
+
}
|
|
542
|
+
tables.forEach((table) => this.tables.set(table.name, table));
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
for (const [_, table] of this.tables) {
|
|
546
|
+
await this.createImpl(table);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
async createImpl(table: CoreConcept) {
|
|
551
|
+
if (this.dialect == Dialect.Postgres) {
|
|
552
|
+
await this.dbClient.query(table.create());
|
|
553
|
+
if (table.postCreate) {
|
|
554
|
+
for (const q of table.postCreate()) {
|
|
555
|
+
await this.dbClient.query(q);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
} else {
|
|
559
|
+
this.sqlite.exec(table.create());
|
|
560
|
+
if (table.postCreate) {
|
|
561
|
+
for (const q of table.postCreate()) {
|
|
562
|
+
this.sqlite.exec(q);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
getSqliteClient(): SqliteDatabase {
|
|
569
|
+
return this.sqlite;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
getPostgresClient(): PGClient {
|
|
573
|
+
return this.dbClient;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
async afterAll() {
|
|
577
|
+
if (this.dialect === Dialect.SQLite) {
|
|
578
|
+
this.sqlite.close();
|
|
579
|
+
if (!this.sqlite.memory) {
|
|
580
|
+
const f = this.getSqliteClient().name;
|
|
581
|
+
fs.rmSync(f);
|
|
582
|
+
fs.rmSync(`${f}-shm`, {
|
|
583
|
+
force: true,
|
|
584
|
+
});
|
|
585
|
+
fs.rmSync(`${f}-wal`, {
|
|
586
|
+
force: true,
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// end our connection to db
|
|
593
|
+
await this.dbClient.end();
|
|
594
|
+
// end any pool connection
|
|
595
|
+
await DB.getInstance().endPool();
|
|
596
|
+
|
|
597
|
+
// drop db
|
|
598
|
+
await this.client.query(`DROP DATABASE ${this.db}`);
|
|
599
|
+
// console.log(this.db);
|
|
600
|
+
|
|
601
|
+
await this.client.end();
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
getDB(): string {
|
|
605
|
+
return this.db;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
async dropAll() {
|
|
609
|
+
for (const [t, _] of this.tables) {
|
|
610
|
+
await this.drop(t);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
async drop(...tables: string[]) {
|
|
615
|
+
for (const tableName of tables) {
|
|
616
|
+
const table = this.tables.get(tableName);
|
|
617
|
+
if (!table) {
|
|
618
|
+
continue;
|
|
619
|
+
}
|
|
620
|
+
if (this.dialect === Dialect.Postgres) {
|
|
621
|
+
await this.dbClient.query(table.drop());
|
|
622
|
+
} else {
|
|
623
|
+
this.sqlite.exec(table.drop());
|
|
624
|
+
}
|
|
625
|
+
this.tables.delete(tableName);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
async create(...tables: CoreConcept[]) {
|
|
630
|
+
for (const table of tables) {
|
|
631
|
+
if (this.tables.has(table.name)) {
|
|
632
|
+
throw new Error(`table with name ${table.name} already exists`);
|
|
633
|
+
}
|
|
634
|
+
await this.createImpl(table);
|
|
635
|
+
this.tables.set(table.name, table);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
export function assoc_edge_config_table() {
|
|
641
|
+
return table(
|
|
642
|
+
"assoc_edge_config",
|
|
643
|
+
// edge_type and inverse_edge_type are text intentionally instead of uuid...
|
|
644
|
+
text("edge_type", { primaryKey: true }),
|
|
645
|
+
text("edge_name"),
|
|
646
|
+
bool("symmetric_edge", { default: "FALSE" }),
|
|
647
|
+
text("inverse_edge_type", { nullable: true }),
|
|
648
|
+
text("edge_table"),
|
|
649
|
+
timestamptz("created_at"),
|
|
650
|
+
timestamptz("updated_at"),
|
|
651
|
+
);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// if global flag is true, add any column from testEdgeGlobalSchema
|
|
655
|
+
// up to caller to set/clear that as needed
|
|
656
|
+
export function assoc_edge_table(
|
|
657
|
+
name: string,
|
|
658
|
+
global?: boolean,
|
|
659
|
+
unique_edge?: boolean,
|
|
660
|
+
) {
|
|
661
|
+
const items: SchemaItem[] = [
|
|
662
|
+
uuid("id1"),
|
|
663
|
+
text("id1_type"),
|
|
664
|
+
// same as in assoc_edge_config_table
|
|
665
|
+
text("edge_type"),
|
|
666
|
+
uuid("id2"),
|
|
667
|
+
text("id2_type"),
|
|
668
|
+
timestamptz("time"),
|
|
669
|
+
text("data", { nullable: true }),
|
|
670
|
+
primaryKey(`${name}_pkey`, ["id1", "id2", "edge_type"]),
|
|
671
|
+
];
|
|
672
|
+
if (unique_edge) {
|
|
673
|
+
items.push(
|
|
674
|
+
unique(`${name}_unique_id1_edge_type`, ["id1", "edge_type"], name),
|
|
675
|
+
);
|
|
676
|
+
}
|
|
677
|
+
const t = table(name, ...items);
|
|
678
|
+
|
|
679
|
+
if (global) {
|
|
680
|
+
for (const k in testEdgeGlobalSchema.extraEdgeFields) {
|
|
681
|
+
const col = getColumnFromField(
|
|
682
|
+
k,
|
|
683
|
+
testEdgeGlobalSchema.extraEdgeFields[k],
|
|
684
|
+
Dialect.Postgres,
|
|
685
|
+
);
|
|
686
|
+
t.columns.push(col);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
return t;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
interface setupOptions {
|
|
693
|
+
disableDeleteAfterEachTest?: boolean;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
export function setupSqlite(
|
|
697
|
+
connString: string,
|
|
698
|
+
tables: () => Table[],
|
|
699
|
+
opts?: setupOptions,
|
|
700
|
+
) {
|
|
701
|
+
let tdb: TempDB = new TempDB({
|
|
702
|
+
dialect: Dialect.SQLite,
|
|
703
|
+
tables,
|
|
704
|
+
sqliteConnString: connString,
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
beforeAll(async () => {
|
|
708
|
+
loadConfig({
|
|
709
|
+
dbConnectionString: connString,
|
|
710
|
+
});
|
|
711
|
+
await tdb.beforeAll();
|
|
712
|
+
|
|
713
|
+
const conn = DB.getInstance().getConnection();
|
|
714
|
+
expect((conn as Sqlite).db.memory).toBe(false);
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
if (!opts?.disableDeleteAfterEachTest) {
|
|
718
|
+
afterEach(async () => {
|
|
719
|
+
const client = await DB.getInstance().getNewClient();
|
|
720
|
+
for (const [key, _] of tdb.__getTables()) {
|
|
721
|
+
const query = `delete from ${key}`;
|
|
722
|
+
if (isSyncClient(client))
|
|
723
|
+
if (client.execSync) {
|
|
724
|
+
client.execSync(query);
|
|
725
|
+
} else {
|
|
726
|
+
await client.exec(query);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
afterAll(async () => {
|
|
733
|
+
await tdb.afterAll();
|
|
734
|
+
|
|
735
|
+
delete process.env.DB_CONNECTION_STRING;
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
return tdb;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
export function setupPostgres(tables: () => Table[], opts?: setupOptions) {
|
|
742
|
+
let tdb: TempDB;
|
|
743
|
+
beforeAll(async () => {
|
|
744
|
+
tdb = new TempDB(Dialect.Postgres, tables());
|
|
745
|
+
await tdb.beforeAll();
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
// TODO need to fix this implementation...
|
|
749
|
+
if (!opts?.disableDeleteAfterEachTest) {
|
|
750
|
+
afterEach(async () => {
|
|
751
|
+
const client = await DB.getInstance().getNewClient();
|
|
752
|
+
for (const [key, _] of tdb.__getTables()) {
|
|
753
|
+
const query = `delete from ${key}`;
|
|
754
|
+
await client.exec(query);
|
|
755
|
+
}
|
|
756
|
+
client.release();
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
afterAll(async () => {
|
|
761
|
+
await tdb.afterAll();
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
export async function doSQLiteTestFromSchemas(
|
|
766
|
+
schemas: BuilderSchema<Ent>[],
|
|
767
|
+
doTest: () => Promise<void>,
|
|
768
|
+
db?: string,
|
|
769
|
+
) {
|
|
770
|
+
const connString = `sqlite:///${db || randomDB()}.db`;
|
|
771
|
+
const tables = schemas.map((schema) =>
|
|
772
|
+
getSchemaTable(schema, Dialect.SQLite),
|
|
773
|
+
);
|
|
774
|
+
let tdb: TempDB = new TempDB(Dialect.SQLite, tables);
|
|
775
|
+
|
|
776
|
+
process.env.DB_CONNECTION_STRING = connString;
|
|
777
|
+
loadConfig();
|
|
778
|
+
await tdb.beforeAll();
|
|
779
|
+
|
|
780
|
+
await doTest();
|
|
781
|
+
|
|
782
|
+
await tdb.afterAll();
|
|
783
|
+
delete process.env.DB_CONNECTION_STRING;
|
|
784
|
+
|
|
785
|
+
return tdb;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
export function getSchemaTable(schema: BuilderSchema<Ent>, dialect: Dialect) {
|
|
789
|
+
const fields = getFields(schema);
|
|
790
|
+
|
|
791
|
+
const items: SchemaItem[] = [];
|
|
792
|
+
for (const [fieldName, field] of fields) {
|
|
793
|
+
items.push(getColumnFromField(fieldName, field, dialect));
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
const tableName = getTableName(schema);
|
|
797
|
+
|
|
798
|
+
if (schema.constraints) {
|
|
799
|
+
for (const constraint of schema.constraints) {
|
|
800
|
+
switch (constraint.type) {
|
|
801
|
+
case ConstraintType.PrimaryKey:
|
|
802
|
+
items.push(primaryKey(constraint.name, constraint.columns));
|
|
803
|
+
break;
|
|
804
|
+
case ConstraintType.ForeignKey:
|
|
805
|
+
if (!constraint.fkey) {
|
|
806
|
+
throw new Error(`need 'fkey' field for foreign key constraint`);
|
|
807
|
+
}
|
|
808
|
+
items.push(
|
|
809
|
+
foreignKey(constraint.name, constraint.columns, {
|
|
810
|
+
table: constraint.fkey.tableName,
|
|
811
|
+
cols: constraint.fkey.columns,
|
|
812
|
+
}),
|
|
813
|
+
);
|
|
814
|
+
break;
|
|
815
|
+
|
|
816
|
+
case ConstraintType.Check:
|
|
817
|
+
if (!constraint.condition) {
|
|
818
|
+
throw new Error(`need 'condition' field for check constraint`);
|
|
819
|
+
}
|
|
820
|
+
items.push(check(constraint.name, constraint.condition));
|
|
821
|
+
break;
|
|
822
|
+
|
|
823
|
+
case ConstraintType.Unique:
|
|
824
|
+
items.push(unique(constraint.name, constraint.columns, tableName));
|
|
825
|
+
break;
|
|
826
|
+
|
|
827
|
+
default:
|
|
828
|
+
throw new Error(`unknown constraint type ${constraint.type}`);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
return table(tableName, ...items);
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
function getColumnForDbType(
|
|
836
|
+
t: DBType,
|
|
837
|
+
dialect: Dialect,
|
|
838
|
+
): ((name: string) => Column) | undefined {
|
|
839
|
+
switch (t) {
|
|
840
|
+
case DBType.UUID:
|
|
841
|
+
if (dialect === Dialect.Postgres) {
|
|
842
|
+
return uuid;
|
|
843
|
+
}
|
|
844
|
+
return text;
|
|
845
|
+
case DBType.Int64ID:
|
|
846
|
+
case DBType.Int:
|
|
847
|
+
return integer;
|
|
848
|
+
case DBType.Boolean:
|
|
849
|
+
return bool;
|
|
850
|
+
case DBType.Timestamp:
|
|
851
|
+
return timestamp;
|
|
852
|
+
case DBType.Timestamptz:
|
|
853
|
+
return timestamptz;
|
|
854
|
+
case DBType.String:
|
|
855
|
+
case DBType.StringEnum:
|
|
856
|
+
return text;
|
|
857
|
+
case DBType.Float:
|
|
858
|
+
return float;
|
|
859
|
+
case DBType.Date:
|
|
860
|
+
return date;
|
|
861
|
+
case DBType.Time:
|
|
862
|
+
return time;
|
|
863
|
+
case DBType.Timetz:
|
|
864
|
+
return timetz;
|
|
865
|
+
case DBType.JSONB:
|
|
866
|
+
return jsonb;
|
|
867
|
+
case DBType.JSON:
|
|
868
|
+
return json;
|
|
869
|
+
|
|
870
|
+
default:
|
|
871
|
+
return undefined;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
export function getColumnFromField(
|
|
876
|
+
fieldName: string,
|
|
877
|
+
f: Field,
|
|
878
|
+
dialect: Dialect,
|
|
879
|
+
) {
|
|
880
|
+
switch (f.type.dbType) {
|
|
881
|
+
case DBType.List:
|
|
882
|
+
const elemType = f.type.listElemType;
|
|
883
|
+
if (elemType === undefined) {
|
|
884
|
+
throw new Error(`unsupported list type with no elem type`);
|
|
885
|
+
}
|
|
886
|
+
const elemFn = getColumnForDbType(elemType.dbType, dialect);
|
|
887
|
+
if (elemFn === undefined) {
|
|
888
|
+
throw new Error(`unsupported type for ${elemType}`);
|
|
889
|
+
}
|
|
890
|
+
return list(storageKey(fieldName, f), elemFn("ignore"), buildOpts(f));
|
|
891
|
+
|
|
892
|
+
default:
|
|
893
|
+
const fn = getColumnForDbType(f.type.dbType, dialect);
|
|
894
|
+
if (fn === undefined) {
|
|
895
|
+
throw new Error(`unsupported type ${f.type.dbType}`);
|
|
896
|
+
}
|
|
897
|
+
return getColumn(fieldName, f, fn);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
function getColumn(
|
|
902
|
+
fieldName: string,
|
|
903
|
+
f: Field,
|
|
904
|
+
col: (name: string, opts?: options) => Column,
|
|
905
|
+
) {
|
|
906
|
+
return col(storageKey(fieldName, f), buildOpts(f));
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
function buildOpts(f: Field): options {
|
|
910
|
+
let ret: options = {};
|
|
911
|
+
if (f.primaryKey) {
|
|
912
|
+
ret.primaryKey = true;
|
|
913
|
+
}
|
|
914
|
+
if (f.nullable) {
|
|
915
|
+
ret.nullable = true;
|
|
916
|
+
}
|
|
917
|
+
if (f.foreignKey !== undefined) {
|
|
918
|
+
console.error("TODO:foreign key not yet converted");
|
|
919
|
+
// ret.foreignKey =
|
|
920
|
+
}
|
|
921
|
+
if (f.serverDefault) {
|
|
922
|
+
ret.default = f.serverDefault;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
if (f.unique) {
|
|
926
|
+
ret.unique = true;
|
|
927
|
+
}
|
|
928
|
+
return ret;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
function storageKey(fieldName: string, f: Field): string {
|
|
932
|
+
if (f.storageKey) {
|
|
933
|
+
return f.storageKey;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
return snakeCase(fieldName);
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
function isSyncClient(client: Client): client is SyncClient {
|
|
940
|
+
return (client as SyncClient).execSync !== undefined;
|
|
941
|
+
}
|