masquerade-orm 0.1.0
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/LICENSE +21 -0
- package/README.md +149 -0
- package/bin/universalTsInit.js +59 -0
- package/docs/deletion.md +186 -0
- package/docs/find.md +265 -0
- package/docs/getting-started-javascript.md +112 -0
- package/docs/getting-started-typescript.md +159 -0
- package/docs/in-depth-class-definitions.md +193 -0
- package/docs/jsdoc-ux-tips.md +17 -0
- package/docs/managing-the-database.md +37 -0
- package/docs/saving-to-database.md +37 -0
- package/index.d.ts +7 -0
- package/index.js +11 -0
- package/jsconfig.json +29 -0
- package/package.json +40 -0
- package/src/ORM/DbManager.js +76 -0
- package/src/ORM/ORM.js +181 -0
- package/src/ORM/bootOrm.js +929 -0
- package/src/changeLogger/changeLogger.js +55 -0
- package/src/changeLogger/save.js +237 -0
- package/src/changeLogger/sqlClients/postgres.js +97 -0
- package/src/changeLogger/sqlClients/sqlite.js +120 -0
- package/src/entity/delete/delete.js +20 -0
- package/src/entity/delete/getDependents.js +46 -0
- package/src/entity/entity.d.ts +46 -0
- package/src/entity/entity.js +228 -0
- package/src/entity/find/find.js +157 -0
- package/src/entity/find/joins.js +52 -0
- package/src/entity/find/queryBuilder.js +95 -0
- package/src/entity/find/relations.js +23 -0
- package/src/entity/find/scopeProxies.js +60 -0
- package/src/entity/find/sqlClients/postgresFuncs.js +171 -0
- package/src/entity/find/sqlClients/sqliteFuncs.js +211 -0
- package/src/entity/find/where/relationalWhere.js +142 -0
- package/src/entity/find/where/where.js +244 -0
- package/src/entity/find/where/whereArgsFunctions.js +49 -0
- package/src/misc/classes.js +63 -0
- package/src/misc/constants.js +42 -0
- package/src/misc/miscFunctions.js +167 -0
- package/src/misc/ormStore.js +18 -0
- package/src/misc/types.d.ts +560 -0
- package/src/proxies/instanceProxy.js +544 -0
- package/src/proxies/nonRelationalArrayProxy.js +76 -0
- package/src/proxies/objectProxy.js +50 -0
- package/src/proxies/relationalArrayProxy.js +130 -0
- package/src/webpack/masquerade-loader.js +14 -0
- package/src/webpack/plugin.js +43 -0
- package/src/webpack/store.js +2 -0
- package/src/webpack/webpack.config.js +41 -0
- package/testing/dev-doc.md +7 -0
- package/testing/generationFuncs.js +21 -0
- package/testing/miscFunctions.js +97 -0
- package/testing/postgres.test.js +254 -0
- package/testing/sqlite.test.js +257 -0
- package/testing/testing-classes.js +102 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { ChangeLogger } from "../changeLogger/changeLogger.js"
|
|
2
|
+
import { getValidTypedArray } from "../misc/miscFunctions.js"
|
|
3
|
+
import { OrmStore } from "../misc/ormStore.js"
|
|
4
|
+
import { setUpdatedAtValue } from "./instanceProxy.js"
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export function createRelationalArrayProxy(instance, propertyName, array = [], /**@type {string | undefined}*/ arrElementValidType = undefined) {
|
|
8
|
+
const instanceId = instance.id
|
|
9
|
+
const instanceClass = instance.constructor.name
|
|
10
|
+
const classChangeObj = OrmStore.getClassChangesObj(instanceClass)
|
|
11
|
+
const eventListenersObj = {}
|
|
12
|
+
//if arrElementValidType is undefined, it just means that the array we are proxifying is an array we got from the db, so the typing is correct.
|
|
13
|
+
if (arrElementValidType) array = getValidTypedArray(array, arrElementValidType, true)
|
|
14
|
+
array.forEach(proxy => addEventListener2ArrayProxy(proxy, array, eventListenersObj, instance.id, propertyName, instanceClass))
|
|
15
|
+
|
|
16
|
+
return new Proxy(array, {
|
|
17
|
+
//ARRAY PROXIES ARE FOR RELATIONAL X:N
|
|
18
|
+
get(target, key, receiver) {
|
|
19
|
+
if (key === "source_") return target
|
|
20
|
+
else if (key === "eListener_") return eventListenersObj
|
|
21
|
+
else {
|
|
22
|
+
if (key === "includes") return (instance) => {
|
|
23
|
+
//@ts-ignore
|
|
24
|
+
if (eventListenersObj[instance.id]) return true
|
|
25
|
+
return false
|
|
26
|
+
}
|
|
27
|
+
return target[key]
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
set(/**@type {any[]}*/ target, key, value, receiver) {
|
|
31
|
+
if (key === "length") return Reflect.set(target, key, value, receiver)
|
|
32
|
+
return relationalArrayProxySetHandler(target, key, value, propertyName, instanceId, eventListenersObj, instanceClass)
|
|
33
|
+
},
|
|
34
|
+
deleteProperty(target, key) {
|
|
35
|
+
return relationalArrayProxyDeleteHandler(target, key, propertyName, instanceId, eventListenersObj, instanceClass)
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
export function relationalArrayProxySetHandler(target, key, newInstanceProxy, propertyName, instanceId, eventListenersObj, instanceClass) {
|
|
42
|
+
const index = parseInt(key)
|
|
43
|
+
if (index > -1) {
|
|
44
|
+
const oldInstanceProxy = target[index]
|
|
45
|
+
const classChangeObj = OrmStore.getClassChangesObj(instanceClass)
|
|
46
|
+
const instanceChangesObj = classChangeObj[instanceId] ??= {}
|
|
47
|
+
|
|
48
|
+
if (eventListenersObj[newInstanceProxy.id]) throw new Error(`Do not insert duplicate entity instances into a relational array. Use the 'includes' method for O(1) lookup.`)
|
|
49
|
+
const relationChangeLogger = instanceChangesObj[propertyName] ??= { added: [], removed: [] }
|
|
50
|
+
|
|
51
|
+
if (oldInstanceProxy) {
|
|
52
|
+
const oldValueInAdded = relationChangeLogger.added.indexOf(oldInstanceProxy.id)
|
|
53
|
+
if (oldValueInAdded > -1) relationChangeLogger.added.splice(oldValueInAdded, 1)
|
|
54
|
+
else relationChangeLogger.removed.push(oldInstanceProxy.id)
|
|
55
|
+
|
|
56
|
+
const oldProxyEventEmitter = oldInstanceProxy.eEmitter_
|
|
57
|
+
oldProxyEventEmitter.removeEventListener("delete", eventListenersObj[oldInstanceProxy.id])
|
|
58
|
+
delete eventListenersObj[oldInstanceProxy.id]
|
|
59
|
+
}
|
|
60
|
+
const newValueInRemoved = relationChangeLogger.removed.indexOf(newInstanceProxy.id)
|
|
61
|
+
if (newValueInRemoved > -1) relationChangeLogger.removed.splice(newValueInRemoved, 1)
|
|
62
|
+
else relationChangeLogger.added.push(newInstanceProxy.id)
|
|
63
|
+
|
|
64
|
+
target[index] = newInstanceProxy
|
|
65
|
+
addEventListener2ArrayProxy(newInstanceProxy, target, eventListenersObj, instanceId, propertyName, instanceClass)
|
|
66
|
+
setUpdatedAtValue(target, instanceChangesObj)
|
|
67
|
+
return true
|
|
68
|
+
}
|
|
69
|
+
else if (typeof index === `string` || typeof index === `symbol`) {
|
|
70
|
+
target[index] = newInstanceProxy
|
|
71
|
+
return true
|
|
72
|
+
}
|
|
73
|
+
return false
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function relationalArrayProxyDeleteHandler(target, key, propertyName, instanceId, eventListenersObj, instanceClass) {
|
|
77
|
+
const validProp = Object.hasOwn(target, key)
|
|
78
|
+
const deletedArrEl = target[key]
|
|
79
|
+
|
|
80
|
+
if (validProp) {
|
|
81
|
+
//@ts-ignore
|
|
82
|
+
const index = parseInt(key)
|
|
83
|
+
if (index > -1) {
|
|
84
|
+
const classChangeObj = OrmStore.getClassChangesObj(instanceClass)
|
|
85
|
+
const instanceChangesObj = classChangeObj[instanceId] ??= {}
|
|
86
|
+
const removedEventFunc = eventListenersObj[deletedArrEl.id]
|
|
87
|
+
deletedArrEl.eEmitter_.removeEventListener("delete", removedEventFunc)
|
|
88
|
+
logRelationalArrayRemoval(target, propertyName, deletedArrEl.id, instanceChangesObj)
|
|
89
|
+
}
|
|
90
|
+
delete target[key]
|
|
91
|
+
return true
|
|
92
|
+
}
|
|
93
|
+
return false
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function logRelationalArrayRemoval(instance, propertyName, removedId, instanceChangesObj) {
|
|
97
|
+
if (!instanceChangesObj[propertyName]) {
|
|
98
|
+
instanceChangesObj[propertyName] = { added: [], removed: [removedId] }
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
const relationalArrChangeLogger = instanceChangesObj[propertyName]
|
|
102
|
+
const oldIndex = relationalArrChangeLogger.added.indexOf(removedId)
|
|
103
|
+
if (oldIndex > -1) relationalArrChangeLogger.added.splice(oldIndex, 1)
|
|
104
|
+
else relationalArrChangeLogger.removed.push(removedId)
|
|
105
|
+
}
|
|
106
|
+
setUpdatedAtValue(instance, instanceChangesObj)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function addEventListener2ArrayProxy(proxy, array, eventListenersObj, idOfInstanceWithArray, propertyOfArray, instanceClass) {
|
|
110
|
+
const emitter = proxy.eEmitter_
|
|
111
|
+
const eventFunc = (event) => {
|
|
112
|
+
if (proxy._isDeleted_) return
|
|
113
|
+
const id2delete = event.detail.id
|
|
114
|
+
// console.log(`Delete event sent from ${id2delete}_${proxy.constructor.name} to proxy array`)
|
|
115
|
+
const index = array.findIndex(proxy => proxy.id === id2delete)
|
|
116
|
+
array.splice(index, 1)
|
|
117
|
+
delete eventListenersObj[id2delete]
|
|
118
|
+
const classChangeObj = OrmStore.getClassChangesObj(instanceClass)
|
|
119
|
+
const instanceChangesObj = classChangeObj[idOfInstanceWithArray] ??= {}
|
|
120
|
+
logRelationalArrayRemoval(proxy, propertyOfArray, id2delete, instanceChangesObj)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
emitter.addEventListener(
|
|
124
|
+
"delete",
|
|
125
|
+
eventFunc,
|
|
126
|
+
{ once: true }
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
eventListenersObj[proxy.id] = eventFunc
|
|
130
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { store } from "./store"
|
|
2
|
+
import { createSourceFile, SyntaxKind, ScriptKind, ScriptTarget } from "typescript"
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export default function (source) {
|
|
6
|
+
const nodes = Object.values(source).map(source => createSourceFile('', source.toString(), ScriptTarget.Latest, true, ScriptKind.TSX).statements[0])
|
|
7
|
+
for (const node of nodes) {
|
|
8
|
+
//@ts-ignore
|
|
9
|
+
if ((node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression) && node.heritageClauses)
|
|
10
|
+
//@ts-ignore
|
|
11
|
+
store.nodeArr.push(node)
|
|
12
|
+
}
|
|
13
|
+
return source
|
|
14
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
|
|
2
|
+
import { store } from "./store.js"
|
|
3
|
+
|
|
4
|
+
import { nodeArr2ClassDict } from "../ORM/bootOrm.js"
|
|
5
|
+
|
|
6
|
+
export class MasqueradePlugin {
|
|
7
|
+
apply(compiler) {
|
|
8
|
+
compiler.hooks.compilation.tap(this.constructor.name, (compilation) => {
|
|
9
|
+
compilation.hooks.processAssets.tap(
|
|
10
|
+
{
|
|
11
|
+
name: this.constructor.name,
|
|
12
|
+
stage: compilation.constructor.PROCESS_ASSETS_STAGE_ADDITIONS,
|
|
13
|
+
},
|
|
14
|
+
(assets) => {
|
|
15
|
+
const classDict = nodeArr2ClassDict(store.nodeArr)
|
|
16
|
+
const prefix = `globalThis.masqueradeClassDict_ = ${JSON.stringify(classDict)};\n`
|
|
17
|
+
|
|
18
|
+
for (const entry of compilation.entrypoints.values()) {
|
|
19
|
+
for (const file of entry.getFiles()) {
|
|
20
|
+
if (!file.endsWith(".js")) continue
|
|
21
|
+
|
|
22
|
+
const asset = compilation.getAsset(file)
|
|
23
|
+
const source = asset.source.source()
|
|
24
|
+
|
|
25
|
+
compilation.updateAsset(
|
|
26
|
+
file,
|
|
27
|
+
new compiler.webpack.sources.RawSource(prefix + source)
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
)
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import { MasqueradePlugin } from './plugin.js'
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
mode: 'development',
|
|
7
|
+
target: 'node',
|
|
8
|
+
entry: './src/project.ts',
|
|
9
|
+
devtool: 'source-map',
|
|
10
|
+
plugins: [
|
|
11
|
+
//other plugins...
|
|
12
|
+
new MasqueradePlugin() //this should be last
|
|
13
|
+
],
|
|
14
|
+
module: {
|
|
15
|
+
rules: [
|
|
16
|
+
{
|
|
17
|
+
test: /\.tsx?$/,
|
|
18
|
+
use: ['ts-loader', 'masquerade-loader'],
|
|
19
|
+
exclude: /node_modules/,
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
resolve: {
|
|
24
|
+
extensions: ['.tsx', '.ts', '.js'],
|
|
25
|
+
},
|
|
26
|
+
output: {
|
|
27
|
+
filename: 'bundle.js',
|
|
28
|
+
devtoolModuleFilenameTemplate: '[absolute-resource-path]', // Helps VS Code find original files
|
|
29
|
+
path: path.resolve(import.meta.dirname, 'dist'),
|
|
30
|
+
library: {
|
|
31
|
+
type: "module"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
experiments: {
|
|
35
|
+
outputModule: true
|
|
36
|
+
},
|
|
37
|
+
resolveLoader: {
|
|
38
|
+
modules: ["node_modules", path.resolve(import.meta.dirname, 'loaders')]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# SQL Client Differences
|
|
2
|
+
- major differences in the find (postgres gives back a nested object, while sqlite is flat and manually nested)
|
|
3
|
+
- slight differences in the save
|
|
4
|
+
- slight differences in type mapping
|
|
5
|
+
|
|
6
|
+
anything internal (proxies and their state management data structures) is agnostic to sql clients.
|
|
7
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { familyAgeGenerator, familyNameGenerator } from "./miscFunctions.js"
|
|
2
|
+
import { Person, House } from "./testing-classes.js"
|
|
3
|
+
|
|
4
|
+
export function generateFamiliesAndHouses() {
|
|
5
|
+
for (let i = 0; i < 30; i++) {
|
|
6
|
+
const familySize = 3 + Math.floor(Math.random() * 7)
|
|
7
|
+
const [fatherName, motherName, ...childrenNames] = familyNameGenerator(familySize)
|
|
8
|
+
const [fatherAge, motherAge, ...childrenAges] = familyAgeGenerator(familySize)
|
|
9
|
+
|
|
10
|
+
const father = new Person(fatherName, fatherAge)
|
|
11
|
+
const mother = new Person(motherName, motherAge)
|
|
12
|
+
const children = []
|
|
13
|
+
for (const i in childrenNames) {
|
|
14
|
+
const child = new Person(childrenNames[i], childrenAges[i], father, mother)
|
|
15
|
+
children.push(child)
|
|
16
|
+
}
|
|
17
|
+
father.children.push(...children)
|
|
18
|
+
mother.children.push(...children)
|
|
19
|
+
new House(father, [father, mother, ...children])
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
export function jsonGenerator() {
|
|
2
|
+
return {
|
|
3
|
+
booleanField: true,
|
|
4
|
+
floatVal: 15.7,
|
|
5
|
+
someInt: 5,
|
|
6
|
+
stringArr: ['a', 'b', 'c']
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const nameArrMale = [
|
|
11
|
+
"James",
|
|
12
|
+
"John",
|
|
13
|
+
"Robert",
|
|
14
|
+
"Michael",
|
|
15
|
+
"William",
|
|
16
|
+
"David",
|
|
17
|
+
"Richard",
|
|
18
|
+
"Joseph",
|
|
19
|
+
"Charles",
|
|
20
|
+
"Thomas",
|
|
21
|
+
"Daniel",
|
|
22
|
+
"Matthew",
|
|
23
|
+
"Anthony",
|
|
24
|
+
"Mark",
|
|
25
|
+
"Paul",
|
|
26
|
+
"Steven",
|
|
27
|
+
"Andrew",
|
|
28
|
+
"Kenneth",
|
|
29
|
+
"Joshua",
|
|
30
|
+
"Kevin"
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
const nameArrFemale = [
|
|
34
|
+
"Mary",
|
|
35
|
+
"Patricia",
|
|
36
|
+
"Jennifer",
|
|
37
|
+
"Linda",
|
|
38
|
+
"Elizabeth",
|
|
39
|
+
"Barbara",
|
|
40
|
+
"Susan",
|
|
41
|
+
"Jessica",
|
|
42
|
+
"Sarah",
|
|
43
|
+
"Karen",
|
|
44
|
+
"Nancy",
|
|
45
|
+
"Lisa",
|
|
46
|
+
"Margaret",
|
|
47
|
+
"Betty",
|
|
48
|
+
"Sandra",
|
|
49
|
+
"Ashley",
|
|
50
|
+
"Kimberly",
|
|
51
|
+
"Emily",
|
|
52
|
+
"Donna",
|
|
53
|
+
"Michelle"
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
const surnames = [
|
|
57
|
+
"Smith",
|
|
58
|
+
"Johnson",
|
|
59
|
+
"Williams",
|
|
60
|
+
"Brown",
|
|
61
|
+
"Jones",
|
|
62
|
+
"Garcia",
|
|
63
|
+
"Miller",
|
|
64
|
+
"Davis",
|
|
65
|
+
"Rodriguez",
|
|
66
|
+
"Martinez"
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
export function arrayRandomPick(array) {
|
|
70
|
+
return array[Math.floor(Math.random() * array.length)]
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function familyNameGenerator(/**@type {number}*/ familySize) {
|
|
74
|
+
const familyNames = []
|
|
75
|
+
let firstName = arrayRandomPick(nameArrMale)
|
|
76
|
+
const surname = arrayRandomPick(surnames)
|
|
77
|
+
familyNames.push(`${firstName} ${surname}`)
|
|
78
|
+
firstName = arrayRandomPick(nameArrFemale)
|
|
79
|
+
familyNames.push(`${firstName} ${surname}`)
|
|
80
|
+
|
|
81
|
+
const childrenFirstNames = [...nameArrMale, ...nameArrFemale]
|
|
82
|
+
for (let i = 0; i < familySize - 2; i++) familyNames.push(`${arrayRandomPick(childrenFirstNames)} ${surname}`)
|
|
83
|
+
return familyNames
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function familyAgeGenerator(/**@type {number}*/ familySize) {
|
|
87
|
+
const fatherAge = 18 + Math.floor(Math.random() * 20)
|
|
88
|
+
const motherAge = 18 + Math.floor(Math.random() * 20)
|
|
89
|
+
const familyAges = [fatherAge, motherAge]
|
|
90
|
+
for (let i = 0; i < familySize - 2; i++) familyAges.push(1 + Math.floor(Math.random() * (Math.min(motherAge, fatherAge) - 18)))
|
|
91
|
+
return familyAges
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function validateUpdatedAt(updatedAt, currentTime) {
|
|
95
|
+
return updatedAt.getTime() - currentTime.getTime() >= 0
|
|
96
|
+
}
|
|
97
|
+
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
|
|
2
|
+
import test from 'node:test'
|
|
3
|
+
import assert from "node:assert"
|
|
4
|
+
import * as classes from './testing-classes.js'
|
|
5
|
+
import { resetPostgresDb, initORM, createConfigObj } from "./testInit.js"
|
|
6
|
+
import { sql } from '..//src/entity/find/where/whereArgsFunctions.js'
|
|
7
|
+
import { generateFamiliesAndHouses } from "./generationFuncs.js"
|
|
8
|
+
import { validateUpdatedAt } from "./miscFunctions.js"
|
|
9
|
+
import { OrmStore } from '../src/misc/ormStore.js'
|
|
10
|
+
|
|
11
|
+
const { House, Person, NonRelationalClass2 } = classes
|
|
12
|
+
|
|
13
|
+
const configObj = createConfigObj()
|
|
14
|
+
await resetPostgresDb(configObj.dbConnection)
|
|
15
|
+
await initORM(configObj, classes)
|
|
16
|
+
let dbChanges = OrmStore.store.dbChangesObj
|
|
17
|
+
generateFamiliesAndHouses()
|
|
18
|
+
for (let i = 0; i < 3; i++) new NonRelationalClass2()
|
|
19
|
+
|
|
20
|
+
const nonRelationalTest = await NonRelationalClass2.find({ where: { json: sql`->>'someInt' = '5'` } })
|
|
21
|
+
test('test 1 - find basics and change logging', async (t) => {
|
|
22
|
+
|
|
23
|
+
await t.test('find basics', async (t) => {
|
|
24
|
+
|
|
25
|
+
await t.test('find result length is correct', async () => {
|
|
26
|
+
assert.strictEqual(nonRelationalTest.length, 3)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
await t.test('find returns correct typing', async () => {
|
|
30
|
+
assert.strictEqual(typeof nonRelationalTest[0].bigint, 'bigint')
|
|
31
|
+
assert.strictEqual(typeof nonRelationalTest[0].boolean, 'boolean')
|
|
32
|
+
assert.strictEqual(typeof nonRelationalTest[0].stringArr[0], 'string')
|
|
33
|
+
assert.strictEqual(typeof nonRelationalTest[0].id, 'number')
|
|
34
|
+
assert.strictEqual(typeof nonRelationalTest[0].json, 'object')
|
|
35
|
+
assert.strictEqual(typeof nonRelationalTest[0].jsonArr[0], 'object')
|
|
36
|
+
assert.ok(Array.isArray(nonRelationalTest[0].stringArr))
|
|
37
|
+
assert.ok(Array.isArray(nonRelationalTest[0].jsonArr))
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
await t.test('assignments and dbChanges', async (t) => {
|
|
42
|
+
const typeTestId = nonRelationalTest[0].id.toString()
|
|
43
|
+
nonRelationalTest[0].int = 157
|
|
44
|
+
|
|
45
|
+
const testInstanceChangeLog = dbChanges.NonRelationalClass2[typeTestId]
|
|
46
|
+
|
|
47
|
+
/** @type {any} */
|
|
48
|
+
let expectedVal = {
|
|
49
|
+
booleanField: false,
|
|
50
|
+
floatVal: 12.33,
|
|
51
|
+
someInt: 7,
|
|
52
|
+
stringArr: ['masquerade', 'orm', 'best', 'orm'],
|
|
53
|
+
unstructuredData: 'random data'
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let currentTime
|
|
57
|
+
|
|
58
|
+
await t.test('non-relational non-arrays', async () => {
|
|
59
|
+
currentTime = new Date()
|
|
60
|
+
nonRelationalTest[0].boolean = false
|
|
61
|
+
|
|
62
|
+
assert.strictEqual(testInstanceChangeLog.boolean, false)
|
|
63
|
+
assert.strictEqual(validateUpdatedAt(testInstanceChangeLog.updatedAt, currentTime), true)
|
|
64
|
+
|
|
65
|
+
currentTime = new Date()
|
|
66
|
+
nonRelationalTest[0].json = {
|
|
67
|
+
booleanField: false,
|
|
68
|
+
floatVal: 12.33,
|
|
69
|
+
someInt: 7,
|
|
70
|
+
stringArr: ['masquerade', 'orm']
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
nonRelationalTest[0].json.unstructuredData = 'random data'
|
|
74
|
+
nonRelationalTest[0].json.stringArr.push('best', 'orm')
|
|
75
|
+
|
|
76
|
+
assert.deepStrictEqual(testInstanceChangeLog.json, expectedVal)
|
|
77
|
+
assert.strictEqual(validateUpdatedAt(testInstanceChangeLog.updatedAt, currentTime), true)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
await t.test('non-relational arrays', async () => {
|
|
81
|
+
currentTime = new Date()
|
|
82
|
+
nonRelationalTest[0].stringArr = ['good', 'day']
|
|
83
|
+
nonRelationalTest[0].stringArr.push('sir')
|
|
84
|
+
|
|
85
|
+
assert.deepStrictEqual(testInstanceChangeLog.stringArr, ['good', 'day', 'sir'])
|
|
86
|
+
assert.strictEqual(validateUpdatedAt(testInstanceChangeLog.updatedAt, currentTime), true)
|
|
87
|
+
|
|
88
|
+
currentTime = new Date()
|
|
89
|
+
nonRelationalTest[0].jsonArr.push({
|
|
90
|
+
someInt: 7,
|
|
91
|
+
booleanField: false,
|
|
92
|
+
floatVal: 7.7,
|
|
93
|
+
stringArr: ['hola', 'mundo']
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
expectedVal =
|
|
97
|
+
'[{"booleanField":true,"floatVal":15.7,"someInt":5,"stringArr":["a","b","c"]},{"someInt":7,"booleanField":false,"floatVal":7.7,"stringArr":["hola","mundo"]}]'
|
|
98
|
+
|
|
99
|
+
assert.deepStrictEqual(testInstanceChangeLog.jsonArr, expectedVal)
|
|
100
|
+
assert.strictEqual(validateUpdatedAt(testInstanceChangeLog.updatedAt, currentTime), true)
|
|
101
|
+
|
|
102
|
+
currentTime = new Date()
|
|
103
|
+
nonRelationalTest[0].jsonArr[0].stringArr = ['someString']
|
|
104
|
+
|
|
105
|
+
expectedVal =
|
|
106
|
+
'[{"booleanField":true,"floatVal":15.7,"someInt":5,"stringArr":["someString"]},{"someInt":7,"booleanField":false,"floatVal":7.7,"stringArr":["hola","mundo"]}]'
|
|
107
|
+
|
|
108
|
+
assert.deepStrictEqual(testInstanceChangeLog.jsonArr, expectedVal)
|
|
109
|
+
assert.strictEqual(validateUpdatedAt(testInstanceChangeLog.updatedAt, currentTime), true)
|
|
110
|
+
})
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
let undefinedTest = await House.find({ relations: { owner: true, tenants: true } })
|
|
114
|
+
|
|
115
|
+
const lastHouse = undefinedTest[undefinedTest.length - 1]
|
|
116
|
+
lastHouse.owner = undefined
|
|
117
|
+
lastHouse.tenants = undefined
|
|
118
|
+
|
|
119
|
+
undefinedTest = await House.find({
|
|
120
|
+
where: { id: lastHouse.id },
|
|
121
|
+
relations: { owner: true, tenants: true }
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
await t.test('assigning undefined values to relational props', async () => {
|
|
125
|
+
assert.strictEqual(undefinedTest[0].owner, undefined)
|
|
126
|
+
assert.deepStrictEqual(undefinedTest[0].tenants, [])
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
await t.test('presave new instances', async (t) => {
|
|
130
|
+
await t.test('new instance proxies communicate with dbChanges correctly', async () => {
|
|
131
|
+
const preSaveChanges = new NonRelationalClass2()
|
|
132
|
+
preSaveChanges.stringArr.push('please rewrite this in rust')
|
|
133
|
+
preSaveChanges.bigint = 12n
|
|
134
|
+
|
|
135
|
+
const preSaveChangesObj =
|
|
136
|
+
dbChanges.NonRelationalClass2[preSaveChanges.id.toString()]
|
|
137
|
+
|
|
138
|
+
assert.deepStrictEqual(
|
|
139
|
+
preSaveChangesObj.stringArr,
|
|
140
|
+
['hello', 'world', 'please rewrite this in rust']
|
|
141
|
+
)
|
|
142
|
+
assert.deepStrictEqual(preSaveChangesObj.bigint, 12n)
|
|
143
|
+
})
|
|
144
|
+
})
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
test('test 2 - promises and instance logging', async (t) => {
|
|
149
|
+
|
|
150
|
+
await t.test('overwriting promises without loading', async (t) => {
|
|
151
|
+
let firstRelationalTest = (await House.find({ where: { id: 1 } }))[0]
|
|
152
|
+
const mrClean = (firstRelationalTest.owner = new Person('Mr Clean', 30))
|
|
153
|
+
firstRelationalTest.tenants = [mrClean, new Person('Mrs Clean', 24)]
|
|
154
|
+
|
|
155
|
+
firstRelationalTest = (await House.find({ where: { id: 1 } }))[0]
|
|
156
|
+
await initORM(configObj, classes)
|
|
157
|
+
|
|
158
|
+
firstRelationalTest = (
|
|
159
|
+
await House.find({
|
|
160
|
+
where: { id: 1 },
|
|
161
|
+
relations: { owner: true, tenants: true }
|
|
162
|
+
})
|
|
163
|
+
)[0]
|
|
164
|
+
|
|
165
|
+
await t.test('1-to-1 relational promise assignment', async () => {
|
|
166
|
+
assert.strictEqual(firstRelationalTest?.owner?.fullName, 'Mr Clean')
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
await t.test('1-to-many relational promise assignment', async () => {
|
|
170
|
+
assert.strictEqual(firstRelationalTest?.tenants?.length, 2)
|
|
171
|
+
assert.strictEqual(firstRelationalTest.tenants[0].fullName, 'Mr Clean')
|
|
172
|
+
assert.strictEqual(firstRelationalTest.tenants[1].fullName, 'Mrs Clean')
|
|
173
|
+
})
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
await t.test('check instance logging', async (t) => {
|
|
177
|
+
const original = (
|
|
178
|
+
await House.find({
|
|
179
|
+
where: { id: 2 },
|
|
180
|
+
relations: { owner: true, tenants: true }
|
|
181
|
+
})
|
|
182
|
+
)[0]
|
|
183
|
+
|
|
184
|
+
const sameInstance = (await House.find({ where: { id: 2 } }))[0]
|
|
185
|
+
|
|
186
|
+
await t.test('instance logging is working correctly', async () => {
|
|
187
|
+
assert.strictEqual(original === sameInstance, true)
|
|
188
|
+
})
|
|
189
|
+
})
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
test('test 3 - deletion', async (t) => {
|
|
193
|
+
let house = (await House.find({
|
|
194
|
+
relations:
|
|
195
|
+
{
|
|
196
|
+
owner:
|
|
197
|
+
{ children: true },
|
|
198
|
+
tenants:
|
|
199
|
+
{
|
|
200
|
+
father: true,
|
|
201
|
+
mother: true
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
where: { id: 5 }
|
|
205
|
+
}))[0]
|
|
206
|
+
|
|
207
|
+
const father = house.owner
|
|
208
|
+
const childrenCount = father?.children.length
|
|
209
|
+
const tenantCount = house.tenants?.length
|
|
210
|
+
//@ts-ignore
|
|
211
|
+
let children = [...father?.children]
|
|
212
|
+
const firstChild = children.shift()
|
|
213
|
+
|
|
214
|
+
await t.test('deletion events - relational arrays', async () => {
|
|
215
|
+
await firstChild.delete()
|
|
216
|
+
//@ts-ignore
|
|
217
|
+
assert.strictEqual(childrenCount - father?.children.length === 1, true)
|
|
218
|
+
assert.strictEqual(father?.children.includes(firstChild), false)
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
await t.test('deletion events - 1-to-1 relation', async () => {
|
|
222
|
+
await father?.delete()
|
|
223
|
+
await House.find({})
|
|
224
|
+
await initORM(configObj, classes)
|
|
225
|
+
|
|
226
|
+
house = (await House.find({
|
|
227
|
+
relations:
|
|
228
|
+
{
|
|
229
|
+
owner:
|
|
230
|
+
{ children: true },
|
|
231
|
+
tenants:
|
|
232
|
+
{
|
|
233
|
+
father: true,
|
|
234
|
+
mother: true,
|
|
235
|
+
children: true
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
where: { id: 5 }
|
|
239
|
+
}))[0]
|
|
240
|
+
|
|
241
|
+
const childrenIds = children.map(child => child.id)
|
|
242
|
+
house.tenants && tenantCount && assert.strictEqual(tenantCount - house.tenants?.length, 2)
|
|
243
|
+
assert.strictEqual(childrenIds.includes(firstChild.id), false)
|
|
244
|
+
for (const person of house.tenants ?? []) assert.strictEqual(person.father, undefined)
|
|
245
|
+
})
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
test.after(async () => {
|
|
249
|
+
await resetPostgresDb(configObj.dbConnection)
|
|
250
|
+
console.log('db reset')
|
|
251
|
+
// @ts-ignore
|
|
252
|
+
configObj.dbConnection.end()
|
|
253
|
+
})
|
|
254
|
+
|