Compare commits
228 Commits
Author | SHA1 | Date | |
---|---|---|---|
3f11cbf595 | |||
cf1ec7f9eb | |||
54060deb8f | |||
48cffb5ac2 | |||
8301eb79a2 | |||
ad6366a294 | |||
cb6c03ebfe | |||
551916fe5c | |||
9b3892c1e8 | |||
c1b1af9c5d | |||
d3a3d5be9d | |||
c9a734d879 | |||
856e8e7d1f | |||
7a4d557724 | |||
7cbd0bd99b | |||
e10f6585a5 | |||
5c8dffdd9c | |||
846996eeac | |||
668df09ba0 | |||
03656f4ca0 | |||
c4c612f3a9 | |||
e357f7581c | |||
9697b1e48b | |||
aeb35705d4 | |||
236c8c6551 | |||
1f28db15e7 | |||
86d600e287 | |||
bb81530dac | |||
b9f9b36b87 | |||
df2fadfa01 | |||
8b2beb3485 | |||
144a620f43 | |||
c241247845 | |||
81e39d09e4 | |||
8e51b518b1 | |||
8308d8d03b | |||
97365ddf29 | |||
55d96fa68d | |||
54ec6accdf | |||
fc5d092b25 | |||
dfba057562 | |||
1ad05943b5 | |||
302e51a77f | |||
1330d03af2 | |||
a298e9d190 | |||
e571ef347b | |||
39a4c7ef3f | |||
0d3518d990 | |||
fbdde2268c | |||
8b6c26f45a | |||
97e82ed75a | |||
03baffd9fd | |||
f29ae67580 | |||
e550a8dbd6 | |||
cf6ef25a8d | |||
29c512b0cc | |||
105f69d1c9 | |||
4c375f8465 | |||
9466af6a4a | |||
c5aa747f42 | |||
b5f2474f65 | |||
85f0d99934 | |||
3b2d3d9072 | |||
3ff5c36fdf | |||
a5acc2fe4e | |||
9c81257101 | |||
f7342962f4 | |||
bcd10205d3 | |||
6cab20f32d | |||
5aaa6ad2d6 | |||
635256f2f6 | |||
f799d3efa5 | |||
f74020ba96 | |||
f6d6545ff5 | |||
85a196c8c1 | |||
adb198af01 | |||
6dce9f3ff8 | |||
2f6a4ce3e5 | |||
0984a1ade4 | |||
804701c96a | |||
a3759fa484 | |||
ef38df62be | |||
c17789e92e | |||
0bf2ba554d | |||
5cbf1a222a | |||
f075530838 | |||
efb83853fb | |||
73300ca4d3 | |||
1e946cdceb | |||
608ff93a41 | |||
6211953f14 | |||
99e520b776 | |||
eda8297356 | |||
ffa52a5883 | |||
1e83f0a0ef | |||
0203eabdfd | |||
72894e3ef1 | |||
bbe5f8c6a8 | |||
02af9f5c4b | |||
17de480272 | |||
776fd3ee4e | |||
3272bb7235 | |||
116dfbc3b0 | |||
3f714b1a33 | |||
e58fa57525 | |||
8e7ad5210f | |||
cea6c662ac | |||
6eb43a4b9f | |||
7f89cbeecd | |||
a042a589a0 | |||
75b5f6af08 | |||
e6a36f22ac | |||
246e3486f0 | |||
c1c4a29415 | |||
6448516aa0 | |||
24a7b2dbd3 | |||
afa7c2fe61 | |||
b768896fbe | |||
8d7d9adef1 | |||
ca6d3c60c0 | |||
38c1fcd53a | |||
e433b9e212 | |||
bf3656f792 | |||
7e9fc62ce7 | |||
670a0c63fb | |||
64e76b3ed1 | |||
25803330de | |||
c6cbff1308 | |||
3eeac7b936 | |||
761b742e21 | |||
e56def621c | |||
ab1799f4f2 | |||
9985b849d1 | |||
1800273b25 | |||
e715cc6d37 | |||
f7abc175aa | |||
bbcb3a614e | |||
941d2f0902 | |||
5c78f83a28 | |||
124f117352 | |||
f67da898ae | |||
3c8f70f77e | |||
3674d7d9a5 | |||
fe2a622488 | |||
f092eefbf3 | |||
8d0aa361f9 | |||
692a1223a8 | |||
ae7d101ab9 | |||
3825b15a65 | |||
3fc41678d2 | |||
39f1b7ada1 | |||
f763de2403 | |||
cba1d031b6 | |||
d31e6a5931 | |||
46514a2743 | |||
1cf02157c4 | |||
72efa14da8 | |||
a73c2084c0 | |||
5ed557a21a | |||
2b6c30ff55 | |||
12ef599333 | |||
191ea5d3c6 | |||
bdf33ed1ca | |||
1c76905dcd | |||
4d88cea69d | |||
8b4f7169ff | |||
2a1c45608f | |||
85a6444263 | |||
766ae1d1ff | |||
7630882312 | |||
4aec47f207 | |||
c2e3a1ae6e | |||
3d8f8646b1 | |||
aca9817c56 | |||
7e341f2b50 | |||
d2ca108ef8 | |||
e2d12f8c9c | |||
cfcd9ab386 | |||
3048035374 | |||
6c70cf05c4 | |||
04b0910883 | |||
3671fe4df4 | |||
bd30da1c4a | |||
05938bf2af | |||
cddb0caf1f | |||
1af51dd989 | |||
8101e49026 | |||
2d97ab9dc5 | |||
3ba2bc8446 | |||
5735ab8577 | |||
def671cbe4 | |||
14042151ba | |||
c74873d02c | |||
800cdd655a | |||
68c0ceeb14 | |||
154dc5c1b3 | |||
d5c027caf0 | |||
30776a7da0 | |||
fedc0bd8c8 | |||
7b78b47a1b | |||
a4acfc0f6e | |||
a934f3608f | |||
a4f3f23bed | |||
2039f1c807 | |||
57d1a6dd0a | |||
d504e90e47 | |||
7079340657 | |||
63ceded1b8 | |||
b45e7ee206 | |||
715709f3ec | |||
0b8adea736 | |||
90d9a25b3f | |||
e49811eecd | |||
9cbdf317dc | |||
d55339013a | |||
9acdbd2842 | |||
dfce8cfcc0 | |||
38446fc79b | |||
af8c41ad6c | |||
3a24f829b4 | |||
67db1325c1 | |||
6683c05c22 | |||
f7f7852b7f | |||
df4346e0a2 | |||
eb54fbcd0d | |||
f13c12cd47 | |||
08d53f22f5 | |||
5b6523e6f4 |
21
.gitignore
vendored
21
.gitignore
vendored
@ -1,5 +1,20 @@
|
|||||||
node_modules/
|
.nogit/
|
||||||
test/data
|
|
||||||
pages/
|
# artifacts
|
||||||
coverage/
|
coverage/
|
||||||
public/
|
public/
|
||||||
|
pages/
|
||||||
|
|
||||||
|
# installs
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# caches
|
||||||
|
.yarn/
|
||||||
|
.cache/
|
||||||
|
.rpt2_cache
|
||||||
|
|
||||||
|
# builds
|
||||||
|
dist/
|
||||||
|
dist_*/
|
||||||
|
|
||||||
|
# custom
|
127
.gitlab-ci.yml
127
.gitlab-ci.yml
@ -1,43 +1,142 @@
|
|||||||
image: hosttoday/ht-docker-node:npmts
|
# gitzone ci_default
|
||||||
|
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
||||||
|
|
||||||
|
cache:
|
||||||
|
paths:
|
||||||
|
- .npmci_cache/
|
||||||
|
key: '$CI_BUILD_STAGE'
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
|
- security
|
||||||
- test
|
- test
|
||||||
- release
|
- release
|
||||||
|
- metadata
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv EA312927
|
- apt-get update && apt-get install -y libcurl3 libssl-dev openssl libssl1.0.0 mongodb
|
||||||
- echo "deb http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.2 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-3.2.list
|
- npm install -g @shipzone/npmci
|
||||||
- apt-get update
|
|
||||||
- apt-get install -y mongodb-org
|
|
||||||
|
|
||||||
testLEGACY:
|
# ====================
|
||||||
stage: test
|
# security stage
|
||||||
|
# ====================
|
||||||
|
mirror:
|
||||||
|
stage: security
|
||||||
script:
|
script:
|
||||||
- npmci test legacy
|
- npmci git mirror
|
||||||
|
only:
|
||||||
|
- tags
|
||||||
|
tags:
|
||||||
|
- lossless
|
||||||
|
- docker
|
||||||
|
- notpriv
|
||||||
|
|
||||||
|
auditProductionDependencies:
|
||||||
|
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
||||||
|
stage: security
|
||||||
|
script:
|
||||||
|
- npmci npm prepare
|
||||||
|
- npmci command npm install --production --ignore-scripts
|
||||||
|
- npmci command npm config set registry https://registry.npmjs.org
|
||||||
|
- npmci command npm audit --audit-level=high --only=prod --production
|
||||||
tags:
|
tags:
|
||||||
- docker
|
- docker
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
|
|
||||||
testLTS:
|
auditDevDependencies:
|
||||||
|
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
||||||
|
stage: security
|
||||||
|
script:
|
||||||
|
- npmci npm prepare
|
||||||
|
- npmci command npm install --ignore-scripts
|
||||||
|
- npmci command npm config set registry https://registry.npmjs.org
|
||||||
|
- npmci command npm audit --audit-level=high --only=dev
|
||||||
|
tags:
|
||||||
|
- docker
|
||||||
|
allow_failure: true
|
||||||
|
|
||||||
|
# ====================
|
||||||
|
# test stage
|
||||||
|
# ====================
|
||||||
|
|
||||||
|
testStable:
|
||||||
stage: test
|
stage: test
|
||||||
script:
|
script:
|
||||||
- npmci test lts
|
- npmci npm prepare
|
||||||
|
- npmci node install stable
|
||||||
|
- npmci npm install
|
||||||
|
- npmci npm test
|
||||||
|
coverage: /\d+.?\d+?\%\s*coverage/
|
||||||
tags:
|
tags:
|
||||||
- docker
|
- docker
|
||||||
|
|
||||||
testSTABLE:
|
testBuild:
|
||||||
stage: test
|
stage: test
|
||||||
script:
|
script:
|
||||||
- npmci test stable
|
- npmci npm prepare
|
||||||
|
- npmci node install stable
|
||||||
|
- npmci npm install
|
||||||
|
- npmci command npm run build
|
||||||
|
coverage: /\d+.?\d+?\%\s*coverage/
|
||||||
tags:
|
tags:
|
||||||
- docker
|
- docker
|
||||||
|
|
||||||
release:
|
release:
|
||||||
stage: release
|
stage: release
|
||||||
environment: npm_registry
|
|
||||||
script:
|
script:
|
||||||
- npmci publish
|
- npmci node install stable
|
||||||
|
- npmci npm publish
|
||||||
only:
|
only:
|
||||||
- tags
|
- tags
|
||||||
tags:
|
tags:
|
||||||
|
- lossless
|
||||||
- docker
|
- docker
|
||||||
|
- notpriv
|
||||||
|
|
||||||
|
# ====================
|
||||||
|
# metadata stage
|
||||||
|
# ====================
|
||||||
|
codequality:
|
||||||
|
stage: metadata
|
||||||
|
allow_failure: true
|
||||||
|
only:
|
||||||
|
- tags
|
||||||
|
script:
|
||||||
|
- npmci command npm install -g tslint typescript
|
||||||
|
- npmci npm prepare
|
||||||
|
- npmci npm install
|
||||||
|
- npmci command "tslint -c tslint.json ./ts/**/*.ts"
|
||||||
|
tags:
|
||||||
|
- lossless
|
||||||
|
- docker
|
||||||
|
- priv
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
stage: metadata
|
||||||
|
script:
|
||||||
|
- npmci trigger
|
||||||
|
only:
|
||||||
|
- tags
|
||||||
|
tags:
|
||||||
|
- lossless
|
||||||
|
- docker
|
||||||
|
- notpriv
|
||||||
|
|
||||||
|
pages:
|
||||||
|
stage: metadata
|
||||||
|
script:
|
||||||
|
- npmci node install lts
|
||||||
|
- npmci command npm install -g @gitzone/tsdoc
|
||||||
|
- npmci npm prepare
|
||||||
|
- npmci npm install
|
||||||
|
- npmci command tsdoc
|
||||||
|
tags:
|
||||||
|
- lossless
|
||||||
|
- docker
|
||||||
|
- notpriv
|
||||||
|
only:
|
||||||
|
- tags
|
||||||
|
artifacts:
|
||||||
|
expire_in: 1 week
|
||||||
|
paths:
|
||||||
|
- public
|
||||||
|
allow_failure: true
|
||||||
|
11
.vscode/launch.json
vendored
Normal file
11
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"command": "npm test",
|
||||||
|
"name": "Run npm test",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "node-terminal"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
26
.vscode/settings.json
vendored
Normal file
26
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"json.schemas": [
|
||||||
|
{
|
||||||
|
"fileMatch": ["/npmextra.json"],
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"npmci": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "settings for npmci"
|
||||||
|
},
|
||||||
|
"gitzone": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "settings for gitzone",
|
||||||
|
"properties": {
|
||||||
|
"projectType": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["website", "element", "service", "npm", "wcc"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
89
README.md
89
README.md
@ -1,89 +0,0 @@
|
|||||||
# smartdata
|
|
||||||
|
|
||||||
> Note: Still in Beta
|
|
||||||
|
|
||||||
smartdata is a ODM that adheres to TypeScript practices and uses classes to organize data.
|
|
||||||
It uses MongoDB as persistent storage.
|
|
||||||
|
|
||||||
## Intention
|
|
||||||
There are many ODMs out there, however when we searched for a ODM that uses TypeScript,
|
|
||||||
acts smart while still embracing an easy the NoSQL idea we didn't find a matching solution.
|
|
||||||
This is why we started smartdata
|
|
||||||
|
|
||||||
How MongoDB terms map to smartdata classes
|
|
||||||
|
|
||||||
MongoDB term | smartdata class
|
|
||||||
--- | ---
|
|
||||||
Database | smartdata.Db
|
|
||||||
Collection | smartdata.DbCollection
|
|
||||||
Document | smartdata.DbDoc
|
|
||||||
|
|
||||||
### class Db
|
|
||||||
represents a Database. Naturally it has .connect() etc. methods on it.
|
|
||||||
Since it is a class you can have multiple DBs defined.
|
|
||||||
```typescript
|
|
||||||
import * as smartdata from 'smartdata'
|
|
||||||
|
|
||||||
let myDb1 = new smartdata.Db('someConnectionUrl')
|
|
||||||
let myDb2 = new smartdata.Db('someConnectionUrl')
|
|
||||||
|
|
||||||
myDb1.connect()
|
|
||||||
myDb2.connect()
|
|
||||||
|
|
||||||
// continues in next block...
|
|
||||||
```
|
|
||||||
|
|
||||||
### class DbCollection
|
|
||||||
represents a collection of objects.
|
|
||||||
A collection is defined by the object class (that is extending smartdata.dbdoc) it respresents
|
|
||||||
|
|
||||||
So to get to get access to a specific collection you document
|
|
||||||
```typescript
|
|
||||||
// continues from the block before...
|
|
||||||
|
|
||||||
class myObject extends smartdata.DbDoc { // read the next block about DbDoc
|
|
||||||
property1:string
|
|
||||||
property2:number
|
|
||||||
constructor(optionsArg:{
|
|
||||||
queryArg?:any,
|
|
||||||
dataArg?:{
|
|
||||||
property1:string,
|
|
||||||
property2:number
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
super(this,optionsArg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let myCollection = myDb1.getCollection(myObject)
|
|
||||||
```
|
|
||||||
|
|
||||||
> Alert: You NEVER instantiate a collection.
|
|
||||||
This is done for you!!!
|
|
||||||
|
|
||||||
### class DbDoc
|
|
||||||
represents a individual document in a collection
|
|
||||||
and thereby is ideally suited to extend the class you want to actually store.
|
|
||||||
|
|
||||||
DbDoc extends your class with the following methods:
|
|
||||||
|
|
||||||
* `.save()` will save (or update) the object you call it on only. Any referenced non-savable objects will not get stored.
|
|
||||||
* `.saveDeep()` does the same like `.save()`.
|
|
||||||
In addition it will look for properties that reference an object
|
|
||||||
that extends DbDoc as well and call .saveDeep() on them as well.
|
|
||||||
Loops are prevented
|
|
||||||
|
|
||||||
So now we can **store** instances of classes to Db...
|
|
||||||
How do we **get** a new class instance from a Doc in the DB?
|
|
||||||
Easy! Take a look at the constructor. When you specify `optionsArg.queryArg`
|
|
||||||
smartdata will fill in the data from the database!
|
|
||||||
But when you specify a `optionsArg.dataArg` instead
|
|
||||||
the data for the class is taken from there :)
|
|
||||||
|
|
||||||
## TypeScript
|
|
||||||
How does TypeScript play into this?
|
|
||||||
Since you define your classes in TapeScript and types flow through smartdata in a generic way
|
|
||||||
you should get all the Intellisense and type checking you love when using smartdata.
|
|
||||||
smartdata itself also bundles typings.
|
|
||||||
So you don't need to install any additional types whenimporting smartdata.
|
|
||||||
|
|
||||||
[](https://push.rocks)
|
|
2
dist/index.d.ts
vendored
2
dist/index.d.ts
vendored
@ -1,2 +0,0 @@
|
|||||||
export * from './smartdata.classes.dbcollection';
|
|
||||||
export * from './smartdata.classes.db';
|
|
7
dist/index.js
vendored
7
dist/index.js
vendored
@ -1,7 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
function __export(m) {
|
|
||||||
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
|
|
||||||
}
|
|
||||||
__export(require('./smartdata.classes.dbcollection'));
|
|
||||||
__export(require('./smartdata.classes.db'));
|
|
||||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7O0FBRUEsaUJBQWMsa0NBQ2QsQ0FBQyxFQUQrQztBQUNoRCxpQkFBYyx3QkFDZCxDQUFDLEVBRHFDIn0=
|
|
11
dist/smartdata.classes.db.d.ts
vendored
11
dist/smartdata.classes.db.d.ts
vendored
@ -1,11 +0,0 @@
|
|||||||
/// <reference types="q" />
|
|
||||||
import * as plugins from './smartdata.plugins';
|
|
||||||
export declare type TConnectionStatus = 'disconnected' | 'connected' | 'failed';
|
|
||||||
export declare class Db {
|
|
||||||
dbUrl: string;
|
|
||||||
db: plugins.mongodb.Db;
|
|
||||||
status: TConnectionStatus;
|
|
||||||
constructor(dbUrl: string);
|
|
||||||
connect(): plugins.q.Promise<any>;
|
|
||||||
close(): plugins.q.Promise<any>;
|
|
||||||
}
|
|
29
dist/smartdata.classes.db.js
vendored
29
dist/smartdata.classes.db.js
vendored
@ -1,29 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
const plugins = require('./smartdata.plugins');
|
|
||||||
class Db {
|
|
||||||
constructor(dbUrl) {
|
|
||||||
this.dbUrl = dbUrl;
|
|
||||||
}
|
|
||||||
connect() {
|
|
||||||
let done = plugins.q.defer();
|
|
||||||
plugins.mongodb.MongoClient.connect(this.dbUrl, (err, db) => {
|
|
||||||
if (err) {
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
plugins.assert.equal(null, err);
|
|
||||||
this.db = db;
|
|
||||||
plugins.beautylog.success(`connected to database at ${this.dbUrl}`);
|
|
||||||
done.resolve(this.db);
|
|
||||||
});
|
|
||||||
return done.promise;
|
|
||||||
}
|
|
||||||
close() {
|
|
||||||
let done = plugins.q.defer();
|
|
||||||
this.db.close();
|
|
||||||
plugins.beautylog.ok(`disconnected to database at ${this.dbUrl}`);
|
|
||||||
done.resolve();
|
|
||||||
return done.promise;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exports.Db = Db;
|
|
||||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRkYXRhLmNsYXNzZXMuZGIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydGRhdGEuY2xhc3Nlcy5kYi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsTUFBWSxPQUFPLFdBQU0scUJBRXpCLENBQUMsQ0FGNkM7QUFJOUM7SUFLSSxZQUFZLEtBQWE7UUFDckIsSUFBSSxDQUFDLEtBQUssR0FBRyxLQUFLLENBQUE7SUFDdEIsQ0FBQztJQUVELE9BQU87UUFDSCxJQUFJLElBQUksR0FBRyxPQUFPLENBQUMsQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFBO1FBQzVCLE9BQU8sQ0FBQyxPQUFPLENBQUMsV0FBVyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUMsR0FBRyxFQUFFLEVBQUU7WUFDcEQsRUFBRSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztnQkFBQyxPQUFPLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFBO1lBQUMsQ0FBQztZQUM3QixPQUFPLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUE7WUFDL0IsSUFBSSxDQUFDLEVBQUUsR0FBRyxFQUFFLENBQUE7WUFDWixPQUFPLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyw0QkFBNEIsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUE7WUFDbkUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUE7UUFDekIsQ0FBQyxDQUFDLENBQUE7UUFDRixNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQTtJQUN2QixDQUFDO0lBRUQsS0FBSztRQUNELElBQUksSUFBSSxHQUFHLE9BQU8sQ0FBQyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUE7UUFDNUIsSUFBSSxDQUFDLEVBQUUsQ0FBQyxLQUFLLEVBQUUsQ0FBQTtRQUNmLE9BQU8sQ0FBQyxTQUFTLENBQUMsRUFBRSxDQUFDLCtCQUErQixJQUFJLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQTtRQUNqRSxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUE7UUFDZCxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQTtJQUN2QixDQUFDO0FBQ0wsQ0FBQztBQTVCWSxVQUFFLEtBNEJkLENBQUEifQ==
|
|
24
dist/smartdata.classes.dbcollection.d.ts
vendored
24
dist/smartdata.classes.dbcollection.d.ts
vendored
@ -1,24 +0,0 @@
|
|||||||
/// <reference types="q" />
|
|
||||||
import * as plugins from './smartdata.plugins';
|
|
||||||
import { Db } from './smartdata.classes.db';
|
|
||||||
export declare class DbCollection<T> {
|
|
||||||
collection: plugins.mongodb.Collection;
|
|
||||||
constructor(nameArg: string, dbArg: Db);
|
|
||||||
/**
|
|
||||||
* adds a validation function that all newly inserted and updated objects have to pass
|
|
||||||
*/
|
|
||||||
addObjectValidation(funcArg: any): void;
|
|
||||||
/**
|
|
||||||
* finds an object in the DbCollection
|
|
||||||
*/
|
|
||||||
find(docMatchArg: T | any): plugins.q.Promise<T[]>;
|
|
||||||
/**
|
|
||||||
* inserts object into the DbCollection
|
|
||||||
*/
|
|
||||||
insertOne(docArg: T): plugins.q.Promise<void>;
|
|
||||||
/**
|
|
||||||
* inserts many objects at once into the DbCollection
|
|
||||||
*/
|
|
||||||
insertMany(docArrayArg: T[]): plugins.q.Promise<void>;
|
|
||||||
private checkDoc(doc);
|
|
||||||
}
|
|
57
dist/smartdata.classes.dbcollection.js
vendored
57
dist/smartdata.classes.dbcollection.js
vendored
@ -1,57 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
const plugins = require('./smartdata.plugins');
|
|
||||||
class DbCollection {
|
|
||||||
constructor(nameArg, dbArg) {
|
|
||||||
this.collection = dbArg.db.collection(nameArg);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* adds a validation function that all newly inserted and updated objects have to pass
|
|
||||||
*/
|
|
||||||
addObjectValidation(funcArg) { }
|
|
||||||
/**
|
|
||||||
* finds an object in the DbCollection
|
|
||||||
*/
|
|
||||||
find(docMatchArg) {
|
|
||||||
let done = plugins.q.defer();
|
|
||||||
this.collection.find(docMatchArg).toArray((err, docs) => {
|
|
||||||
if (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
done.resolve(docs);
|
|
||||||
});
|
|
||||||
return done.promise;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* inserts object into the DbCollection
|
|
||||||
*/
|
|
||||||
insertOne(docArg) {
|
|
||||||
let done = plugins.q.defer();
|
|
||||||
this.checkDoc(docArg).then(() => {
|
|
||||||
this.collection.insertOne(docArg)
|
|
||||||
.then(() => { done.resolve(); });
|
|
||||||
});
|
|
||||||
return done.promise;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* inserts many objects at once into the DbCollection
|
|
||||||
*/
|
|
||||||
insertMany(docArrayArg) {
|
|
||||||
let done = plugins.q.defer();
|
|
||||||
let checkDocPromiseArray = [];
|
|
||||||
for (let docArg of docArrayArg) {
|
|
||||||
checkDocPromiseArray.push(this.checkDoc(docArg));
|
|
||||||
}
|
|
||||||
plugins.q.all(checkDocPromiseArray).then(() => {
|
|
||||||
this.collection.insertMany(docArrayArg)
|
|
||||||
.then(() => { done.resolve(); });
|
|
||||||
});
|
|
||||||
return done.promise;
|
|
||||||
}
|
|
||||||
checkDoc(doc) {
|
|
||||||
let done = plugins.q.defer();
|
|
||||||
done.resolve();
|
|
||||||
return done.promise;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exports.DbCollection = DbCollection;
|
|
||||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRkYXRhLmNsYXNzZXMuZGJjb2xsZWN0aW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc21hcnRkYXRhLmNsYXNzZXMuZGJjb2xsZWN0aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQSxNQUFZLE9BQU8sV0FBTSxxQkFDekIsQ0FBQyxDQUQ2QztBQUc5QztJQUVJLFlBQVksT0FBZSxFQUFFLEtBQVM7UUFDbEMsSUFBSSxDQUFDLFVBQVUsR0FBRyxLQUFLLENBQUMsRUFBRSxDQUFDLFVBQVUsQ0FBQyxPQUFPLENBQUMsQ0FBQTtJQUNsRCxDQUFDO0lBRUQ7O09BRUc7SUFDSCxtQkFBbUIsQ0FBQyxPQUFPLElBQUksQ0FBQztJQUVoQzs7T0FFRztJQUNILElBQUksQ0FBQyxXQUFvQjtRQUNyQixJQUFJLElBQUksR0FBRyxPQUFPLENBQUMsQ0FBQyxDQUFDLEtBQUssRUFBTyxDQUFBO1FBQ2pDLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLEdBQUcsRUFBRSxJQUFJO1lBQ2hELEVBQUUsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7Z0JBQUMsTUFBTSxHQUFHLENBQUE7WUFBQyxDQUFDO1lBQ3RCLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUE7UUFDdEIsQ0FBQyxDQUFDLENBQUE7UUFDRixNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQTtJQUN2QixDQUFDO0lBRUQ7O09BRUc7SUFDSCxTQUFTLENBQUMsTUFBUztRQUNmLElBQUksSUFBSSxHQUFHLE9BQU8sQ0FBQyxDQUFDLENBQUMsS0FBSyxFQUFRLENBQUE7UUFDbEMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQyxJQUFJLENBQUM7WUFDdkIsSUFBSSxDQUFDLFVBQVUsQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDO2lCQUM1QixJQUFJLENBQUMsUUFBUSxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUEsQ0FBQyxDQUFDLENBQUMsQ0FBQTtRQUN2QyxDQUFDLENBQUMsQ0FBQTtRQUNGLE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFBO0lBQ3ZCLENBQUM7SUFFRDs7T0FFRztJQUNILFVBQVUsQ0FBQyxXQUFnQjtRQUN2QixJQUFJLElBQUksR0FBRyxPQUFPLENBQUMsQ0FBQyxDQUFDLEtBQUssRUFBUSxDQUFBO1FBQ2xDLElBQUksb0JBQW9CLEdBQThCLEVBQUUsQ0FBQTtRQUN4RCxHQUFHLENBQUMsQ0FBQyxJQUFJLE1BQU0sSUFBSSxXQUFXLENBQUMsQ0FBQSxDQUFDO1lBQzVCLG9CQUFvQixDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUE7UUFDcEQsQ0FBQztRQUNELE9BQU8sQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLG9CQUFvQixDQUFDLENBQUMsSUFBSSxDQUFDO1lBQ3JDLElBQUksQ0FBQyxVQUFVLENBQUMsVUFBVSxDQUFDLFdBQVcsQ0FBQztpQkFDbEMsSUFBSSxDQUFDLFFBQVEsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFBLENBQUMsQ0FBQyxDQUFDLENBQUE7UUFDdkMsQ0FBQyxDQUFDLENBQUE7UUFDRixNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQTtJQUN2QixDQUFDO0lBRU8sUUFBUSxDQUFDLEdBQU07UUFDbkIsSUFBSSxJQUFJLEdBQUcsT0FBTyxDQUFDLENBQUMsQ0FBQyxLQUFLLEVBQVEsQ0FBQTtRQUNsQyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUE7UUFDZCxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQTtJQUN2QixDQUFDO0FBQ0wsQ0FBQztBQXhEWSxvQkFBWSxlQXdEeEIsQ0FBQSJ9
|
|
0
dist/smartdata.classes.dbdoc.d.ts
vendored
0
dist/smartdata.classes.dbdoc.d.ts
vendored
1
dist/smartdata.classes.dbdoc.js
vendored
1
dist/smartdata.classes.dbdoc.js
vendored
@ -1 +0,0 @@
|
|||||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRkYXRhLmNsYXNzZXMuZGJkb2MuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydGRhdGEuY2xhc3Nlcy5kYmRvYy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiIn0=
|
|
5
dist/smartdata.plugins.d.ts
vendored
5
dist/smartdata.plugins.d.ts
vendored
@ -1,5 +0,0 @@
|
|||||||
import 'typings-global';
|
|
||||||
export import assert = require('assert');
|
|
||||||
export import beautylog = require('beautylog');
|
|
||||||
export import mongodb = require('mongodb');
|
|
||||||
export import q = require('q');
|
|
7
dist/smartdata.plugins.js
vendored
7
dist/smartdata.plugins.js
vendored
@ -1,7 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
require('typings-global');
|
|
||||||
exports.assert = require('assert');
|
|
||||||
exports.beautylog = require('beautylog');
|
|
||||||
exports.mongodb = require('mongodb');
|
|
||||||
exports.q = require('q');
|
|
||||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRkYXRhLnBsdWdpbnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydGRhdGEucGx1Z2lucy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsUUFBTyxnQkFDUCxDQUFDLENBRHNCO0FBQ1QsY0FBTSxXQUFXLFFBQVEsQ0FBQyxDQUFBO0FBQzFCLGlCQUFTLFdBQVcsV0FBVyxDQUFDLENBQUE7QUFDaEMsZUFBTyxXQUFXLFNBQVMsQ0FBQyxDQUFBO0FBQzVCLFNBQUMsV0FBVyxHQUFHLENBQUMsQ0FBQSJ9
|
|
@ -1,19 +0,0 @@
|
|||||||
# smartdata
|
|
||||||
|
|
||||||
smartdata is a ODM that adheres to TypeScript practices and uses classes to organize data.
|
|
||||||
It uses MongoDB as persistent storage.
|
|
||||||
|
|
||||||
## Intention
|
|
||||||
There are many ODMs out there, however when we searched for a ODM that uses TypeScript,
|
|
||||||
acts smart while still embracing an easy the NoSQL idea we didn't find a matching solution.
|
|
||||||
This is why we started smartdata
|
|
||||||
|
|
||||||
How MongoDB terms map to smartdata classes
|
|
||||||
|
|
||||||
MongoDB term | smartdata class
|
|
||||||
--- | ---
|
|
||||||
Database | smartdata.DbConnection
|
|
||||||
Collection | smartdata.DbCollection
|
|
||||||
Document | smartdata.DbDoc
|
|
||||||
|
|
||||||
[](https://push.rocks)
|
|
23
npmextra.json
Normal file
23
npmextra.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"npmdocker": {
|
||||||
|
"baseImage": "hosttoday/ht-docker-node:mongo",
|
||||||
|
"command": "npmci test stable",
|
||||||
|
"dockerSock": false
|
||||||
|
},
|
||||||
|
"npmci": {
|
||||||
|
"npmGlobalTools": [],
|
||||||
|
"npmAccessLevel": "public",
|
||||||
|
"npmRegistryUrl": "registry.npmjs.org"
|
||||||
|
},
|
||||||
|
"gitzone": {
|
||||||
|
"projectType": "npm",
|
||||||
|
"module": {
|
||||||
|
"githost": "gitlab.com",
|
||||||
|
"gitscope": "pushrocks",
|
||||||
|
"gitrepo": "smartdata",
|
||||||
|
"shortDescription": "do more with data",
|
||||||
|
"npmPackagename": "@pushrocks/smartdata",
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27935
package-lock.json
generated
Normal file
27935
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
64
package.json
64
package.json
@ -1,12 +1,14 @@
|
|||||||
{
|
{
|
||||||
"name": "smartdata",
|
"name": "@pushrocks/smartdata",
|
||||||
"version": "1.0.13",
|
"version": "4.0.21",
|
||||||
|
"private": false,
|
||||||
"description": "do more with data",
|
"description": "do more with data",
|
||||||
"main": "dist/index.js",
|
"main": "dist_ts/index.js",
|
||||||
"typings": "dist/index.d.ts",
|
"typings": "dist_ts/index.d.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "(npm run prepareMongo && npmts)",
|
"test": "(tstest test/)",
|
||||||
"prepareMongo": "(rm -rf ./test/data && mkdir ./test/data/)"
|
"testLocal": "(npmdocker)",
|
||||||
|
"build": "(tsbuild --web)"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -19,19 +21,43 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://gitlab.com/pushrocks/smartdata#README",
|
"homepage": "https://gitlab.com/pushrocks/smartdata#README",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/mongodb": "^2.1.32",
|
"@pushrocks/lik": "^5.0.0",
|
||||||
"@types/q": "0.0.30",
|
"@pushrocks/smartlog": "^2.0.44",
|
||||||
"beautylog": "^5.0.23",
|
"@pushrocks/smartpromise": "^3.1.5",
|
||||||
"mongodb": "^2.2.9",
|
"@pushrocks/smartstring": "^3.0.24",
|
||||||
"q": "^1.4.1",
|
"@pushrocks/smartunique": "^3.0.3",
|
||||||
"typings-global": "^1.0.14"
|
"@tsclass/tsclass": "^3.0.36",
|
||||||
|
"@types/lodash": "^4.14.176",
|
||||||
|
"@types/mongodb": "^4.0.7",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"mongodb": "^4.1.4",
|
||||||
|
"runtime-type-checks": "0.0.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/shelljs": "^0.3.30",
|
"@gitzone/tsbuild": "^2.1.28",
|
||||||
"@types/should": "^8.1.29",
|
"@gitzone/tstest": "^1.0.60",
|
||||||
"shelljs": "^0.7.4",
|
"@pushrocks/qenv": "^4.0.10",
|
||||||
"should": "^11.1.0",
|
"@pushrocks/tapbundle": "^3.2.14",
|
||||||
"smartstring": "^2.0.17",
|
"@types/mongodb-memory-server": "^2.3.0",
|
||||||
"typings-test": "^1.0.3"
|
"@types/node": "^16.11.7",
|
||||||
}
|
"@types/shortid": "0.0.29",
|
||||||
|
"mongodb-memory-server": "^8.0.2",
|
||||||
|
"tslint": "^6.1.3",
|
||||||
|
"tslint-config-prettier": "^1.18.0"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"ts/**/*",
|
||||||
|
"ts_web/**/*",
|
||||||
|
"dist/**/*",
|
||||||
|
"dist_*/**/*",
|
||||||
|
"dist_ts/**/*",
|
||||||
|
"dist_ts_web/**/*",
|
||||||
|
"assets/**/*",
|
||||||
|
"cli.js",
|
||||||
|
"npmextra.json",
|
||||||
|
"readme.md"
|
||||||
|
],
|
||||||
|
"browserslist": [
|
||||||
|
"last 1 chrome versions"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
156
readme.md
Normal file
156
readme.md
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
# @pushrocks/smartdata
|
||||||
|
do more with data
|
||||||
|
|
||||||
|
## Availabililty and Links
|
||||||
|
* [npmjs.org (npm package)](https://www.npmjs.com/package/@pushrocks/smartdata)
|
||||||
|
* [gitlab.com (source)](https://gitlab.com/pushrocks/smartdata)
|
||||||
|
* [github.com (source mirror)](https://github.com/pushrocks/smartdata)
|
||||||
|
* [docs (typedoc)](https://pushrocks.gitlab.io/smartdata/)
|
||||||
|
|
||||||
|
## Status for master
|
||||||
|
|
||||||
|
Status Category | Status Badge
|
||||||
|
-- | --
|
||||||
|
GitLab Pipelines | [](https://lossless.cloud)
|
||||||
|
GitLab Pipline Test Coverage | [](https://lossless.cloud)
|
||||||
|
npm | [](https://lossless.cloud)
|
||||||
|
Snyk | [](https://lossless.cloud)
|
||||||
|
TypeScript Support | [](https://lossless.cloud)
|
||||||
|
node Support | [](https://nodejs.org/dist/latest-v10.x/docs/api/)
|
||||||
|
Code Style | [](https://lossless.cloud)
|
||||||
|
PackagePhobia (total standalone install weight) | [](https://lossless.cloud)
|
||||||
|
PackagePhobia (package size on registry) | [](https://lossless.cloud)
|
||||||
|
BundlePhobia (total size when bundled) | [](https://lossless.cloud)
|
||||||
|
Platform support | [](https://lossless.cloud) [](https://lossless.cloud)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Use TypeScript for best in class instellisense.
|
||||||
|
|
||||||
|
smartdata is an ODM that adheres to TypeScript practices and uses classes to organize data.
|
||||||
|
It uses RethinkDB as persistent storage.
|
||||||
|
|
||||||
|
## Intention
|
||||||
|
|
||||||
|
There are many ODMs out there, however when we searched for an ODM that uses TypeScript,
|
||||||
|
acts smart while still embracing the NoSQL idea we didn't find a matching solution.
|
||||||
|
This is why we started smartdata.
|
||||||
|
|
||||||
|
How RethinkDB's terms map to the ones of smartdata:
|
||||||
|
|
||||||
|
| MongoDb term | smartdata class |
|
||||||
|
| ------------ | ----------------------------- |
|
||||||
|
| Database | smartdata.SmartdataDb |
|
||||||
|
| Collection | smartdata.SmartdataCollection |
|
||||||
|
| Document | smartdata.SmartadataDoc |
|
||||||
|
|
||||||
|
### class Db
|
||||||
|
|
||||||
|
represents a Database. Naturally it has .connect() etc. methods on it.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Assuming toplevel await
|
||||||
|
import * as smartdata from 'smartdata';
|
||||||
|
|
||||||
|
const smartdataDb = new smartdata.SmartdataDb({
|
||||||
|
mongoDbUrl: '//someurl',
|
||||||
|
mongoDbName: 'myDatabase',
|
||||||
|
mongoDbPass: 'mypassword',
|
||||||
|
});
|
||||||
|
|
||||||
|
await smartdataDb.connect();
|
||||||
|
```
|
||||||
|
|
||||||
|
### class DbCollection
|
||||||
|
|
||||||
|
represents a collection of objects.
|
||||||
|
A collection is defined by the object class (that is extending smartdata.dbdoc) it respresents
|
||||||
|
|
||||||
|
So to get to get access to a specific collection you document
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Assuming toplevel await
|
||||||
|
// continues from the block before...
|
||||||
|
|
||||||
|
@smartdata.Collection(smartdataDb)
|
||||||
|
class MyObject extends smartdata.DbDoc<MyObject /* ,[an optional interface to implement] */> {
|
||||||
|
// read the next block about DbDoc
|
||||||
|
@smartdata.svDb()
|
||||||
|
property1: string; // @smartdata.svDb() marks the property for db save
|
||||||
|
|
||||||
|
property2: number; // this one is not marked, so it won't be save upon calling this.save()
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(); // the super call is important ;) But you probably know that.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// start to instantiate instances of classes from scratch or database
|
||||||
|
|
||||||
|
const localObject = new MyObject({
|
||||||
|
property1: 'hi',
|
||||||
|
property2: {
|
||||||
|
deep: 3,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await localObject.save(); // saves the object to the database
|
||||||
|
|
||||||
|
// start retrieving instances
|
||||||
|
|
||||||
|
// .getInstance is staticly inheritied, yet fully typed static function to get instances with fully typed filters
|
||||||
|
const myInstance = await MyObject.getInstance({
|
||||||
|
property1: 'hi',
|
||||||
|
property2: {
|
||||||
|
deep: {
|
||||||
|
$gt: 2,
|
||||||
|
} as any,
|
||||||
|
},
|
||||||
|
}); // outputs a new instance of MyObject with the values from db assigned
|
||||||
|
```
|
||||||
|
|
||||||
|
### class DbDoc
|
||||||
|
|
||||||
|
represents a individual document in a collection
|
||||||
|
and thereby is ideally suited to extend the class you want to actually store.
|
||||||
|
|
||||||
|
### CRUD operations
|
||||||
|
|
||||||
|
smartdata supports full CRUD operations
|
||||||
|
|
||||||
|
**Store** or **Update** instances of classes to MongoDB:
|
||||||
|
DbDoc extends your class with the following methods:
|
||||||
|
|
||||||
|
- async `.save()` will save (or update) the object you call it on only. Any referenced non-savable objects will not get stored.
|
||||||
|
- async `.saveDeep()` does the same like `.save()`.
|
||||||
|
In addition it will look for properties that reference an object
|
||||||
|
that extends DbDoc as well and call .saveDeep() on them as well.
|
||||||
|
Loops are prevented
|
||||||
|
|
||||||
|
**Get** a new class instance from MongoDB:
|
||||||
|
DbDoc exposes a static method that allows you specify a filter to retrieve a cloned class of the one you used to that doc at some point later in time:
|
||||||
|
|
||||||
|
- static async `.getInstance({ /* filter props here */ })` gets you an instance that has the data of the first matched document as properties.
|
||||||
|
- static async `getInstances({ /* filter props here */ })` get you an array instances (one instance for every matched document).
|
||||||
|
|
||||||
|
**Delete** instances from MongoDb:
|
||||||
|
smartdata extends your class with a method to easily delete the doucment from DB:
|
||||||
|
|
||||||
|
- async `.delete()`will delete the document from DB.
|
||||||
|
|
||||||
|
## TypeScript
|
||||||
|
|
||||||
|
How does TypeScript play into this?
|
||||||
|
Since you define your classes in TypeScript and types flow through smartdata in a generic way
|
||||||
|
you should get all the Intellisense and type checking you love when using smartdata.
|
||||||
|
smartdata itself also bundles typings. You don't need to install any additional types for smartdata.
|
||||||
|
|
||||||
|
## Contribution
|
||||||
|
|
||||||
|
We are always happy for code contributions. If you are not the code contributing type that is ok. Still, maintaining Open Source repositories takes considerable time and thought. If you like the quality of what we do and our modules are useful to you we would appreciate a little monthly contribution: You can [contribute one time](https://lossless.link/contribute-onetime) or [contribute monthly](https://lossless.link/contribute). :)
|
||||||
|
|
||||||
|
For further information read the linked docs at the top of this readme.
|
||||||
|
|
||||||
|
> MIT licensed | **©** [Lossless GmbH](https://lossless.gmbh)
|
||||||
|
| By using this npm module you agree to our [privacy policy](https://lossless.gmbH/privacy)
|
||||||
|
|
||||||
|
[](https://maintainedby.lossless.com)
|
1
test/test.d.ts
vendored
1
test/test.d.ts
vendored
@ -1 +0,0 @@
|
|||||||
import 'typings-test';
|
|
62
test/test.easystore.ts
Normal file
62
test/test.easystore.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { tap, expect } from '@pushrocks/tapbundle';
|
||||||
|
import { Qenv } from '@pushrocks/qenv';
|
||||||
|
|
||||||
|
const testQenv = new Qenv(process.cwd(), process.cwd() + '/.nogit/');
|
||||||
|
|
||||||
|
console.log(process.memoryUsage());
|
||||||
|
|
||||||
|
// the tested module
|
||||||
|
import * as smartdata from '../ts/index';
|
||||||
|
|
||||||
|
import * as mongoPlugin from 'mongodb-memory-server';
|
||||||
|
import { smartunique } from '../ts/smartdata.plugins';
|
||||||
|
|
||||||
|
// =======================================
|
||||||
|
// Connecting to the database server
|
||||||
|
// =======================================
|
||||||
|
|
||||||
|
let testDb: smartdata.SmartdataDb;
|
||||||
|
let smartdataOptions: smartdata.IMongoDescriptor;
|
||||||
|
let mongod: mongoPlugin.MongoMemoryServer;
|
||||||
|
|
||||||
|
tap.skip.test('should create a testinstance as database', async () => {
|
||||||
|
mongod = await mongoPlugin.MongoMemoryServer.create();
|
||||||
|
console.log('created mongod instance');
|
||||||
|
console.log('mongod started');
|
||||||
|
smartdataOptions = {
|
||||||
|
mongoDbUrl: mongod.getUri(),
|
||||||
|
};
|
||||||
|
console.log(smartdataOptions);
|
||||||
|
testDb = new smartdata.SmartdataDb(smartdataOptions);
|
||||||
|
await testDb.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('should connect to atlas', async (tools) => {
|
||||||
|
const databaseName = `test-smartdata-${smartunique.shortId()}`;
|
||||||
|
testDb = new smartdata.SmartdataDb({
|
||||||
|
mongoDbUrl: testQenv.getEnvVarOnDemand('MONGO_URL'),
|
||||||
|
mongoDbName: databaseName,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let easyStore: smartdata.EasyStore<{
|
||||||
|
key1: string;
|
||||||
|
key2: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
tap.test('should create an easystore', async () => {
|
||||||
|
easyStore = await testDb.createEasyStore('hellothere');
|
||||||
|
await easyStore.writeKey('key1', 'hello');
|
||||||
|
const retrievedKey = await easyStore.readKey('key1');
|
||||||
|
expect(retrievedKey).to.equal('hello');
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('close', async () => {
|
||||||
|
testDb.close();
|
||||||
|
mongod.stop();
|
||||||
|
setTimeout(() => {
|
||||||
|
process.exit(0);
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.start();
|
79
test/test.js
79
test/test.js
@ -1,79 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
require('typings-test');
|
|
||||||
const shelljs = require('shelljs');
|
|
||||||
const should = require('should');
|
|
||||||
const smartstring = require('smartstring');
|
|
||||||
// the tested module
|
|
||||||
const smartdata = require('../dist/index');
|
|
||||||
let mongoChildProcess;
|
|
||||||
let testDb;
|
|
||||||
let testDbCollection;
|
|
||||||
class testCar {
|
|
||||||
constructor(colorArg) {
|
|
||||||
this.color = colorArg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
describe('mongodb', function () {
|
|
||||||
it('should start mongodb', function (done) {
|
|
||||||
this.timeout(30000);
|
|
||||||
mongoChildProcess = shelljs.exec('mongod --dbpath=./test/data --port 27017', { async: true, silent: true });
|
|
||||||
let doneCalled = false;
|
|
||||||
mongoChildProcess.stdout.on('data', function (data) {
|
|
||||||
console.log(smartstring.indent.indentWithPrefix(data, "*** MongoDB Process *** : "));
|
|
||||||
if (!doneCalled) {
|
|
||||||
if (/waiting for connections on port 27017/.test(data)) {
|
|
||||||
doneCalled = true;
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe('smartdata', function () {
|
|
||||||
it('should establish a connection to mongodb', function (done) {
|
|
||||||
testDb = new smartdata.Db('mongodb://localhost:27017/smartdata');
|
|
||||||
testDb.connect().then(() => { done(); });
|
|
||||||
});
|
|
||||||
it('should create a collection', function () {
|
|
||||||
testDbCollection = new smartdata.DbCollection('something', testDb);
|
|
||||||
});
|
|
||||||
it('should insert a doc into the collection', function (done) {
|
|
||||||
testDbCollection.insertOne({ value1: 'test' }).then(() => { done(); });
|
|
||||||
});
|
|
||||||
it('should find all docs of testDbCollection', function (done) {
|
|
||||||
testDbCollection.find({}).then((resultArray) => {
|
|
||||||
console.log(resultArray);
|
|
||||||
should(resultArray[0].value1).equal('test');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it('should insert many docs into the collection', function (done) {
|
|
||||||
testDbCollection.insertMany([
|
|
||||||
{ value1: 'test2' },
|
|
||||||
{ value1: 'test', value2: 3, value3: 'hi' }
|
|
||||||
]).then(() => { done(); });
|
|
||||||
});
|
|
||||||
it('should find a specified doc', function (done) {
|
|
||||||
testDbCollection.find({ 'value3': { '$exists': true } }).then((resultArray) => {
|
|
||||||
console.log(resultArray);
|
|
||||||
should(resultArray[0].value3).equal('hi');
|
|
||||||
done();
|
|
||||||
}).catch(console.log);
|
|
||||||
});
|
|
||||||
it('should close the db Connection', function () {
|
|
||||||
testDb.close();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe('mongodb', function () {
|
|
||||||
it('should kill mongodb', function (done) {
|
|
||||||
this.timeout(30000);
|
|
||||||
mongoChildProcess.stdout.on('data', function (data) {
|
|
||||||
if (/dbexit: rc: 0/.test(data)) {
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
shelljs.exec('mongod --dbpath=./test/data --shutdown');
|
|
||||||
mongoChildProcess.kill('SIGTERM');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInRlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLFFBQU8sY0FFUCxDQUFDLENBRm9CO0FBRXJCLE1BQVksT0FBTyxXQUFNLFNBQ3pCLENBQUMsQ0FEaUM7QUFDbEMsTUFBWSxNQUFNLFdBQU0sUUFDeEIsQ0FBQyxDQUQrQjtBQUNoQyxNQUFZLFdBQVcsV0FBTSxhQUc3QixDQUFDLENBSHlDO0FBRTFDLG9CQUFvQjtBQUNwQixNQUFZLFNBQVMsV0FBTSxlQUUzQixDQUFDLENBRnlDO0FBRTFDLElBQUksaUJBQWlCLENBQUE7QUFDckIsSUFBSSxNQUFvQixDQUFBO0FBUXhCLElBQUksZ0JBQXNELENBQUE7QUFFMUQ7SUFFSSxZQUFZLFFBQWU7UUFDdkIsSUFBSSxDQUFDLEtBQUssR0FBRyxRQUFRLENBQUE7SUFDekIsQ0FBQztBQUNMLENBQUM7QUFHRCxRQUFRLENBQUMsU0FBUyxFQUFFO0lBQ2hCLEVBQUUsQ0FBQyxzQkFBc0IsRUFBRSxVQUFVLElBQUk7UUFDckMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQTtRQUNuQixpQkFBaUIsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLDBDQUEwQyxFQUFFLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQTtRQUMzRyxJQUFJLFVBQVUsR0FBRyxLQUFLLENBQUE7UUFDdEIsaUJBQWlCLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsVUFBVSxJQUFJO1lBQzlDLE9BQU8sQ0FBQyxHQUFHLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFJLEVBQUUsNEJBQTRCLENBQUMsQ0FBQyxDQUFBO1lBQ3BGLEVBQUUsQ0FBQyxDQUFDLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQztnQkFDZCxFQUFFLENBQUMsQ0FBQyx1Q0FBdUMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO29CQUNyRCxVQUFVLEdBQUcsSUFBSSxDQUFBO29CQUNqQixJQUFJLEVBQUUsQ0FBQTtnQkFDVixDQUFDO1lBQ0wsQ0FBQztRQUNMLENBQUMsQ0FBQyxDQUFBO0lBQ04sQ0FBQyxDQUFDLENBQUE7QUFDTixDQUFDLENBQUMsQ0FBQTtBQUVGLFFBQVEsQ0FBQyxXQUFXLEVBQUU7SUFDbEIsRUFBRSxDQUFDLDBDQUEwQyxFQUFFLFVBQVUsSUFBSTtRQUN6RCxNQUFNLEdBQUcsSUFBSSxTQUFTLENBQUMsRUFBRSxDQUFDLHFDQUFxQyxDQUFDLENBQUE7UUFDaEUsTUFBTSxDQUFDLE9BQU8sRUFBRSxDQUFDLElBQUksQ0FBQyxRQUFRLElBQUksRUFBRSxDQUFBLENBQUMsQ0FBQyxDQUFDLENBQUE7SUFDM0MsQ0FBQyxDQUFDLENBQUE7SUFDRixFQUFFLENBQUMsNEJBQTRCLEVBQUU7UUFDN0IsZ0JBQWdCLEdBQUcsSUFBSSxTQUFTLENBQUMsWUFBWSxDQUFlLFdBQVcsRUFBRSxNQUFNLENBQUMsQ0FBQTtJQUNwRixDQUFDLENBQUMsQ0FBQTtJQUNGLEVBQUUsQ0FBQyx5Q0FBeUMsRUFBRSxVQUFVLElBQUk7UUFDeEQsZ0JBQWdCLENBQUMsU0FBUyxDQUFDLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLFFBQVEsSUFBSSxFQUFFLENBQUEsQ0FBQyxDQUFDLENBQUMsQ0FBQTtJQUN6RSxDQUFDLENBQUMsQ0FBQTtJQUNGLEVBQUUsQ0FBQywwQ0FBMEMsRUFBRSxVQUFVLElBQUk7UUFDekQsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLFdBQVc7WUFDdkMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxXQUFXLENBQUMsQ0FBQTtZQUN4QixNQUFNLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQTtZQUMzQyxJQUFJLEVBQUUsQ0FBQTtRQUNWLENBQUMsQ0FBQyxDQUFBO0lBQ04sQ0FBQyxDQUFDLENBQUE7SUFDRixFQUFFLENBQUMsNkNBQTZDLEVBQUUsVUFBVSxJQUFJO1FBQzVELGdCQUFnQixDQUFDLFVBQVUsQ0FBQztZQUN4QixFQUFFLE1BQU0sRUFBRSxPQUFPLEVBQUU7WUFDbkIsRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxDQUFDLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRTtTQUM5QyxDQUFDLENBQUMsSUFBSSxDQUFDLFFBQVEsSUFBSSxFQUFFLENBQUEsQ0FBQyxDQUFDLENBQUMsQ0FBQTtJQUM3QixDQUFDLENBQUMsQ0FBQTtJQUNGLEVBQUUsQ0FBQyw2QkFBNkIsRUFBRSxVQUFVLElBQUk7UUFDNUMsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLEVBQUMsUUFBUSxFQUFFLEVBQUMsU0FBUyxFQUFFLElBQUksRUFBQyxFQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxXQUFXO1lBQ2xFLE9BQU8sQ0FBQyxHQUFHLENBQUMsV0FBVyxDQUFDLENBQUE7WUFDeEIsTUFBTSxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUE7WUFDekMsSUFBSSxFQUFFLENBQUE7UUFDVixDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFBO0lBQ3pCLENBQUMsQ0FBQyxDQUFBO0lBQ0YsRUFBRSxDQUFDLGdDQUFnQyxFQUFFO1FBQ2pDLE1BQU0sQ0FBQyxLQUFLLEVBQUUsQ0FBQTtJQUNsQixDQUFDLENBQUMsQ0FBQTtBQUNOLENBQUMsQ0FBQyxDQUFBO0FBRUYsUUFBUSxDQUFDLFNBQVMsRUFBRTtJQUNoQixFQUFFLENBQUMscUJBQXFCLEVBQUUsVUFBVSxJQUFJO1FBQ3BDLElBQUksQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUE7UUFDbkIsaUJBQWlCLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsVUFBVSxJQUFJO1lBQzlDLEVBQUUsQ0FBQyxDQUFDLGdCQUFnQixDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7Z0JBQzlCLElBQUksRUFBRSxDQUFBO1lBQ1YsQ0FBQztRQUNMLENBQUMsQ0FBQyxDQUFBO1FBQ0YsT0FBTyxDQUFDLElBQUksQ0FBQyx3Q0FBd0MsQ0FBQyxDQUFBO1FBQ3RELGlCQUFpQixDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQTtJQUNyQyxDQUFDLENBQUMsQ0FBQTtBQUNOLENBQUMsQ0FBQyxDQUFBIn0=
|
|
286
test/test.ts
286
test/test.ts
@ -1,93 +1,223 @@
|
|||||||
import 'typings-test'
|
import { tap, expect } from '@pushrocks/tapbundle';
|
||||||
|
import { Qenv } from '@pushrocks/qenv';
|
||||||
|
|
||||||
import * as shelljs from 'shelljs'
|
const testQenv = new Qenv(process.cwd(), process.cwd() + '/.nogit/');
|
||||||
import * as should from 'should'
|
|
||||||
import * as smartstring from 'smartstring'
|
console.log(process.memoryUsage());
|
||||||
|
|
||||||
// the tested module
|
// the tested module
|
||||||
import * as smartdata from '../dist/index'
|
import * as smartdata from '../ts/index';
|
||||||
|
|
||||||
let mongoChildProcess
|
import * as mongoPlugin from 'mongodb-memory-server';
|
||||||
let testDb: smartdata.Db
|
import { smartunique } from '../ts/smartdata.plugins';
|
||||||
|
|
||||||
interface ITestObject1 {
|
// =======================================
|
||||||
value1: string
|
// Connecting to the database server
|
||||||
value2?: number
|
// =======================================
|
||||||
value3?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
let testDbCollection: smartdata.DbCollection<ITestObject1>
|
let testDb: smartdata.SmartdataDb;
|
||||||
|
let smartdataOptions: smartdata.IMongoDescriptor;
|
||||||
|
let mongod: mongoPlugin.MongoMemoryServer;
|
||||||
|
|
||||||
class testCar {
|
const totalCars = 2000;
|
||||||
color: string
|
|
||||||
constructor(colorArg:string) {
|
tap.skip.test('should create a testinstance as database', async () => {
|
||||||
this.color = colorArg
|
mongod = await mongoPlugin.MongoMemoryServer.create();
|
||||||
|
console.log('created mongod instance');
|
||||||
|
console.log('mongod started');
|
||||||
|
smartdataOptions = {
|
||||||
|
mongoDbUrl: mongod.getUri(),
|
||||||
|
};
|
||||||
|
console.log(smartdataOptions);
|
||||||
|
testDb = new smartdata.SmartdataDb(smartdataOptions);
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('should connect to atlas', async (tools) => {
|
||||||
|
const databaseName = `test-smartdata-${smartunique.shortId()}`;
|
||||||
|
testDb = new smartdata.SmartdataDb({
|
||||||
|
mongoDbUrl: testQenv.getEnvVarOnDemand('MONGO_URL'),
|
||||||
|
mongoDbName: databaseName,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('should establish a connection to mongod', async () => {
|
||||||
|
await testDb.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
// =======================================
|
||||||
|
// The actual tests
|
||||||
|
// =======================================
|
||||||
|
|
||||||
|
// ------
|
||||||
|
// Collections
|
||||||
|
// ------
|
||||||
|
|
||||||
|
@smartdata.Collection(() => {
|
||||||
|
return testDb;
|
||||||
|
})
|
||||||
|
class Car extends smartdata.SmartDataDbDoc<Car, Car> {
|
||||||
|
@smartdata.unI()
|
||||||
|
public index: string = smartunique.shortId();
|
||||||
|
|
||||||
|
@smartdata.svDb()
|
||||||
|
public color: string;
|
||||||
|
|
||||||
|
@smartdata.svDb()
|
||||||
|
public brand: string;
|
||||||
|
|
||||||
|
@smartdata.svDb()
|
||||||
|
deepData = {
|
||||||
|
sodeep: 'yes',
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(colorArg: string, brandArg: string) {
|
||||||
|
super();
|
||||||
|
this.color = colorArg;
|
||||||
|
this.brand = brandArg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tap.test('should save the car to the db', async () => {
|
||||||
|
const myCar = new Car('red', 'Volvo');
|
||||||
|
await myCar.save();
|
||||||
|
|
||||||
describe('mongodb', function () {
|
const myCar2 = new Car('red', 'Volvo');
|
||||||
it('should start mongodb', function (done) {
|
await myCar2.save();
|
||||||
this.timeout(30000)
|
|
||||||
mongoChildProcess = shelljs.exec('mongod --dbpath=./test/data --port 27017', { async: true, silent: true })
|
let counter = 0;
|
||||||
let doneCalled = false
|
process.memoryUsage();
|
||||||
mongoChildProcess.stdout.on('data', function (data) {
|
do {
|
||||||
console.log(smartstring.indent.indentWithPrefix(data, "*** MongoDB Process *** : "))
|
const myCar3 = new Car('red', 'Renault');
|
||||||
if (!doneCalled) {
|
await myCar3.save();
|
||||||
if (/waiting for connections on port 27017/.test(data)) {
|
counter++;
|
||||||
doneCalled = true
|
if (counter % 100 === 0) {
|
||||||
done()
|
console.log(
|
||||||
|
`Filled database with ${counter} of ${totalCars} Cars and memory usage ${
|
||||||
|
process.memoryUsage().rss / 1e6
|
||||||
|
} MB`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} while (counter < totalCars);
|
||||||
|
console.log(process.memoryUsage());
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('expect to get instance of Car with shallow match', async () => {
|
||||||
|
const totalQueryCycles = totalCars / 4;
|
||||||
|
let counter = 0;
|
||||||
|
do {
|
||||||
|
const timeStart = Date.now();
|
||||||
|
const myCars = await Car.getInstances({
|
||||||
|
brand: 'Renault',
|
||||||
|
});
|
||||||
|
if (counter % 10 === 0) {
|
||||||
|
console.log(
|
||||||
|
`performed ${counter} of ${totalQueryCycles} total query cycles: took ${
|
||||||
|
Date.now() - timeStart
|
||||||
|
}ms to query a set of 2000 with memory footprint ${process.memoryUsage().rss / 1e6} MB`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
expect(myCars[0].deepData.sodeep).to.equal('yes');
|
||||||
|
expect(myCars[0].brand).to.equal('Renault');
|
||||||
|
counter++;
|
||||||
|
} while (counter < totalQueryCycles);
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('expect to get instance of Car with deep match', async () => {
|
||||||
|
const totalQueryCycles = totalCars / 4;
|
||||||
|
let counter = 0;
|
||||||
|
do {
|
||||||
|
const timeStart = Date.now();
|
||||||
|
const myCars2 = await Car.getInstances({
|
||||||
|
deepData: {
|
||||||
|
sodeep: 'yes',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (counter % 10 === 0) {
|
||||||
|
console.log(
|
||||||
|
`performed ${counter} of ${totalQueryCycles} total query cycles: took ${
|
||||||
|
Date.now() - timeStart
|
||||||
|
}ms to deep query a set of 2000 with memory footprint ${process.memoryUsage().rss / 1e6} MB`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
expect(myCars2[0].deepData.sodeep).to.equal('yes');
|
||||||
|
expect(myCars2[0].brand).to.equal('Volvo');
|
||||||
|
counter++;
|
||||||
|
} while (counter < totalQueryCycles);
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('expect to get instance of Car and update it', async () => {
|
||||||
|
const myCar = await Car.getInstance<Car>({
|
||||||
|
brand: 'Volvo',
|
||||||
|
});
|
||||||
|
expect(myCar.color).to.equal('red');
|
||||||
|
myCar.color = 'blue';
|
||||||
|
await myCar.save();
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('should be able to delete an instance of car', async () => {
|
||||||
|
const myCars = await Car.getInstances({
|
||||||
|
brand: 'Volvo',
|
||||||
|
color: 'blue',
|
||||||
|
});
|
||||||
|
console.log(myCars);
|
||||||
|
expect(myCars[0].color).to.equal('blue');
|
||||||
|
for (const myCar of myCars) {
|
||||||
|
await myCar.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
const myCar2 = await Car.getInstance<Car>({
|
||||||
|
brand: 'Volvo',
|
||||||
|
});
|
||||||
|
expect(myCar2.color).to.equal('red');
|
||||||
|
});
|
||||||
|
|
||||||
|
// tslint:disable-next-line: max-classes-per-file
|
||||||
|
@smartdata.Collection(() => {
|
||||||
|
return testDb;
|
||||||
|
})
|
||||||
|
class Truck extends smartdata.SmartDataDbDoc<Car, Car> {
|
||||||
|
@smartdata.unI()
|
||||||
|
public id: string = smartunique.shortId();
|
||||||
|
|
||||||
|
@smartdata.svDb()
|
||||||
|
public color: string;
|
||||||
|
|
||||||
|
@smartdata.svDb()
|
||||||
|
public brand: string;
|
||||||
|
|
||||||
|
constructor(colorArg: string, brandArg: string) {
|
||||||
|
super();
|
||||||
|
this.color = colorArg;
|
||||||
|
this.brand = brandArg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('smartdata', function () {
|
tap.test('should store a new Truck', async () => {
|
||||||
it('should establish a connection to mongodb', function (done) {
|
const truck = new Truck('blue', 'MAN');
|
||||||
testDb = new smartdata.Db('mongodb://localhost:27017/smartdata')
|
await truck.save();
|
||||||
testDb.connect().then(() => { done() })
|
const myTruck2 = await Truck.getInstance({ color: 'blue' });
|
||||||
})
|
myTruck2.color = 'red';
|
||||||
it('should create a collection', function () {
|
await myTruck2.save();
|
||||||
testDbCollection = new smartdata.DbCollection<ITestObject1>('something', testDb)
|
const myTruck3 = await Truck.getInstance({ color: 'blue' });
|
||||||
})
|
console.log(myTruck3);
|
||||||
it('should insert a doc into the collection', function (done) {
|
});
|
||||||
testDbCollection.insertOne({ value1: 'test' }).then(() => { done() })
|
|
||||||
})
|
|
||||||
it('should find all docs of testDbCollection', function (done) {
|
|
||||||
testDbCollection.find({}).then((resultArray) => {
|
|
||||||
console.log(resultArray)
|
|
||||||
should(resultArray[0].value1).equal('test')
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
it('should insert many docs into the collection', function (done) {
|
|
||||||
testDbCollection.insertMany([
|
|
||||||
{ value1: 'test2' },
|
|
||||||
{ value1: 'test', value2: 3, value3: 'hi' }
|
|
||||||
]).then(() => { done() })
|
|
||||||
})
|
|
||||||
it('should find a specified doc', function (done) {
|
|
||||||
testDbCollection.find({'value3': {'$exists': true}}).then((resultArray) => {
|
|
||||||
console.log(resultArray)
|
|
||||||
should(resultArray[0].value3).equal('hi')
|
|
||||||
done()
|
|
||||||
}).catch(console.log)
|
|
||||||
})
|
|
||||||
it('should close the db Connection', function () {
|
|
||||||
testDb.close()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('mongodb', function () {
|
tap.test('should use a cursor', async () => {
|
||||||
it('should kill mongodb', function (done) {
|
const cursor = await Truck.getCursor({});
|
||||||
this.timeout(30000)
|
cursor.forEach(async truckArg => {
|
||||||
mongoChildProcess.stdout.on('data', function (data) {
|
console.log(truckArg.id);
|
||||||
if (/dbexit: rc: 0/.test(data)) {
|
});
|
||||||
done()
|
});
|
||||||
}
|
|
||||||
})
|
// =======================================
|
||||||
shelljs.exec('mongod --dbpath=./test/data --shutdown')
|
// close the database connection
|
||||||
mongoChildProcess.kill('SIGTERM')
|
// =======================================
|
||||||
})
|
tap.test('should drop the db and close the database connection', async (tools) => {
|
||||||
})
|
await testDb.mongoDb.dropDatabase();
|
||||||
|
await testDb.close();
|
||||||
|
try {
|
||||||
|
await mongod.stop();
|
||||||
|
} catch (e) {}
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.start({ throwOnError: true });
|
||||||
|
105
test/test.typescript.ts
Normal file
105
test/test.typescript.ts
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import { tap, expect } from '@pushrocks/tapbundle';
|
||||||
|
import { Qenv } from '@pushrocks/qenv';
|
||||||
|
|
||||||
|
const testQenv = new Qenv(process.cwd(), process.cwd() + '/.nogit/');
|
||||||
|
|
||||||
|
console.log(process.memoryUsage());
|
||||||
|
|
||||||
|
// the tested module
|
||||||
|
import * as smartdata from '../ts/index';
|
||||||
|
|
||||||
|
import * as mongoPlugin from 'mongodb-memory-server';
|
||||||
|
import { smartunique } from '../ts/smartdata.plugins';
|
||||||
|
|
||||||
|
// =======================================
|
||||||
|
// Connecting to the database server
|
||||||
|
// =======================================
|
||||||
|
|
||||||
|
let testDb: smartdata.SmartdataDb;
|
||||||
|
let smartdataOptions: smartdata.IMongoDescriptor;
|
||||||
|
let mongod: mongoPlugin.MongoMemoryServer;
|
||||||
|
|
||||||
|
const totalCars = 2000;
|
||||||
|
|
||||||
|
tap.skip.test('should create a testinstance as database', async () => {
|
||||||
|
mongod = await mongoPlugin.MongoMemoryServer.create();
|
||||||
|
console.log('created mongod instance');
|
||||||
|
console.log('mongod started');
|
||||||
|
smartdataOptions = {
|
||||||
|
mongoDbUrl: mongod.getUri(),
|
||||||
|
};
|
||||||
|
console.log(smartdataOptions);
|
||||||
|
testDb = new smartdata.SmartdataDb(smartdataOptions);
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('should connect to atlas', async (tools) => {
|
||||||
|
const databaseName = `test-smartdata-${smartunique.shortId()}`;
|
||||||
|
testDb = new smartdata.SmartdataDb({
|
||||||
|
mongoDbUrl: testQenv.getEnvVarOnDemand('MONGO_URL'),
|
||||||
|
mongoDbName: databaseName,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('should establish a connection to mongod', async () => {
|
||||||
|
await testDb.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
// =======================================
|
||||||
|
// The actual tests
|
||||||
|
// =======================================
|
||||||
|
|
||||||
|
// ------
|
||||||
|
// Collections
|
||||||
|
// ------
|
||||||
|
@smartdata.Manager()
|
||||||
|
class Car extends smartdata.SmartDataDbDoc<Car, Car> {
|
||||||
|
@smartdata.unI()
|
||||||
|
public index: string = smartunique.shortId();
|
||||||
|
|
||||||
|
@smartdata.svDb()
|
||||||
|
public color: string;
|
||||||
|
|
||||||
|
@smartdata.svDb()
|
||||||
|
public brand: string;
|
||||||
|
|
||||||
|
@smartdata.svDb()
|
||||||
|
deepData = {
|
||||||
|
sodeep: 'yes',
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(colorArg: string, brandArg: string) {
|
||||||
|
super();
|
||||||
|
this.color = colorArg;
|
||||||
|
this.brand = brandArg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const createCarClass = (dbArg: smartdata.SmartdataDb) => {
|
||||||
|
smartdata.setDefaultManagerForDoc({ db: dbArg }, Car);
|
||||||
|
return Car;
|
||||||
|
};
|
||||||
|
|
||||||
|
tap.test('should produce a car', async () => {
|
||||||
|
const CarClass = createCarClass(testDb);
|
||||||
|
const carInstance = new CarClass('red', 'Mercedes');
|
||||||
|
await carInstance.save();
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('should get a car', async () => {
|
||||||
|
const car = Car.getInstance({
|
||||||
|
color: 'red',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// =======================================
|
||||||
|
// close the database connection
|
||||||
|
// =======================================
|
||||||
|
tap.test('should close the database connection', async (tools) => {
|
||||||
|
await testDb.mongoDb.dropDatabase();
|
||||||
|
await testDb.close();
|
||||||
|
try {
|
||||||
|
await mongod.stop();
|
||||||
|
} catch (e) {}
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.start({ throwOnError: true });
|
@ -1,4 +1,6 @@
|
|||||||
import * as plugins from './smartdata.plugins'
|
export * from './smartdata.classes.db';
|
||||||
|
export * from './smartdata.classes.collection';
|
||||||
|
export * from './smartdata.classes.doc';
|
||||||
|
export * from './smartdata.classes.easystore';
|
||||||
|
|
||||||
export * from './smartdata.classes.dbcollection'
|
export { IMongoDescriptor } from './interfaces';
|
||||||
export * from './smartdata.classes.db'
|
|
||||||
|
1
ts/interfaces/index.ts
Normal file
1
ts/interfaces/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './mongodescriptor';
|
22
ts/interfaces/mongodescriptor.ts
Normal file
22
ts/interfaces/mongodescriptor.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
export interface IMongoDescriptor {
|
||||||
|
/**
|
||||||
|
* the URL to connect to
|
||||||
|
*/
|
||||||
|
mongoDbUrl: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the db to use for the project
|
||||||
|
*/
|
||||||
|
mongoDbName?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a username to use to connect to the database
|
||||||
|
*/
|
||||||
|
|
||||||
|
mongoDbUser?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* an optional password that will be replace <PASSWORD> in the connection string
|
||||||
|
*/
|
||||||
|
mongoDbPass?: string;
|
||||||
|
}
|
258
ts/smartdata.classes.collection.ts
Normal file
258
ts/smartdata.classes.collection.ts
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
import * as plugins from './smartdata.plugins';
|
||||||
|
import { SmartdataDb } from './smartdata.classes.db';
|
||||||
|
import { SmartdataDbCursor } from './smartdata.classes.cursor';
|
||||||
|
import { SmartDataDbDoc } from './smartdata.classes.doc';
|
||||||
|
import { CollectionFactory } from './smartdata.classes.collectionfactory';
|
||||||
|
|
||||||
|
export interface IFindOptions {
|
||||||
|
limit?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export interface IDocValidationFunc<T> {
|
||||||
|
(doc: T): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TDelayed<TDelayedArg> = () => TDelayedArg;
|
||||||
|
|
||||||
|
const collectionFactory = new CollectionFactory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a decorator that will tell the decorated class what dbTable to use
|
||||||
|
* @param dbArg
|
||||||
|
*/
|
||||||
|
export function Collection(dbArg: SmartdataDb | TDelayed<SmartdataDb>) {
|
||||||
|
return function classDecorator<T extends { new (...args: any[]): {} }>(constructor: T) {
|
||||||
|
return class extends constructor {
|
||||||
|
public static get collection() {
|
||||||
|
if (!(dbArg instanceof SmartdataDb)) {
|
||||||
|
dbArg = dbArg();
|
||||||
|
}
|
||||||
|
return collectionFactory.getCollection(constructor.name, dbArg);
|
||||||
|
}
|
||||||
|
public get collection() {
|
||||||
|
if (!(dbArg instanceof SmartdataDb)) {
|
||||||
|
dbArg = dbArg();
|
||||||
|
}
|
||||||
|
return collectionFactory.getCollection(constructor.name, dbArg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IManager {
|
||||||
|
db: SmartdataDb;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setDefaultManagerForDoc = <T>(managerArg: IManager, dbDocArg: T): T => {
|
||||||
|
(dbDocArg as any).prototype.defaultManager = managerArg;
|
||||||
|
return dbDocArg;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a decorator that will tell the decorated class what dbTable to use
|
||||||
|
* @param dbArg
|
||||||
|
*/
|
||||||
|
export function Manager<TManager extends IManager>(managerArg?: TManager | TDelayed<TManager>) {
|
||||||
|
return function classDecorator<T extends { new (...args: any[]): any }>(constructor: T) {
|
||||||
|
return class extends constructor {
|
||||||
|
public static get collection() {
|
||||||
|
let dbArg: SmartdataDb;
|
||||||
|
if (!managerArg) {
|
||||||
|
dbArg = this.prototype.defaultManager.db;
|
||||||
|
} else if (managerArg['db']) {
|
||||||
|
dbArg = (managerArg as TManager).db;
|
||||||
|
} else {
|
||||||
|
dbArg = (managerArg as TDelayed<TManager>)().db;
|
||||||
|
}
|
||||||
|
return collectionFactory.getCollection(constructor.name, dbArg);
|
||||||
|
}
|
||||||
|
public get collection() {
|
||||||
|
let dbArg: SmartdataDb;
|
||||||
|
if (!managerArg) {
|
||||||
|
//console.log(this.defaultManager.db);
|
||||||
|
//process.exit(0)
|
||||||
|
dbArg = this.defaultManager.db;
|
||||||
|
} else if (managerArg['db']) {
|
||||||
|
dbArg = (managerArg as TManager).db;
|
||||||
|
} else {
|
||||||
|
dbArg = (managerArg as TDelayed<TManager>)().db;
|
||||||
|
}
|
||||||
|
return collectionFactory.getCollection(constructor.name, dbArg);
|
||||||
|
}
|
||||||
|
public static get manager() {
|
||||||
|
let manager: TManager;
|
||||||
|
if (!managerArg) {
|
||||||
|
manager = this.prototype.defaultManager
|
||||||
|
} else if (managerArg['db']) {
|
||||||
|
manager = managerArg as TManager;
|
||||||
|
} else {
|
||||||
|
manager = (managerArg as TDelayed<TManager>)();
|
||||||
|
}
|
||||||
|
return manager;
|
||||||
|
}
|
||||||
|
public get manager() {
|
||||||
|
let manager: TManager;
|
||||||
|
if (!managerArg) {
|
||||||
|
manager = this.defaultManager
|
||||||
|
} else if (managerArg['db']) {
|
||||||
|
manager = managerArg as TManager;
|
||||||
|
} else {
|
||||||
|
manager = (managerArg as TDelayed<TManager>)();
|
||||||
|
}
|
||||||
|
return manager;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line: max-classes-per-file
|
||||||
|
export class SmartdataCollection<T> {
|
||||||
|
/**
|
||||||
|
* the collection that is used
|
||||||
|
*/
|
||||||
|
public mongoDbCollection: plugins.mongodb.Collection;
|
||||||
|
public objectValidation: IDocValidationFunc<T> = null;
|
||||||
|
public collectionName: string;
|
||||||
|
public smartdataDb: SmartdataDb;
|
||||||
|
public uniqueIndexes: string[] = [];
|
||||||
|
|
||||||
|
constructor(classNameArg: string, smartDataDbArg: SmartdataDb) {
|
||||||
|
// tell the collection where it belongs
|
||||||
|
this.collectionName = classNameArg;
|
||||||
|
this.smartdataDb = smartDataDbArg;
|
||||||
|
|
||||||
|
// tell the db class about it (important since Db uses different systems under the hood)
|
||||||
|
this.smartdataDb.addCollection(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* makes sure a collection exists within MongoDb that maps to the SmartdataCollection
|
||||||
|
*/
|
||||||
|
public async init() {
|
||||||
|
if (!this.mongoDbCollection) {
|
||||||
|
// connect this instance to a MongoDB collection
|
||||||
|
const availableMongoDbCollections = await this.smartdataDb.mongoDb.collections();
|
||||||
|
const wantedCollection = availableMongoDbCollections.find((collection) => {
|
||||||
|
return collection.collectionName === this.collectionName;
|
||||||
|
});
|
||||||
|
if (!wantedCollection) {
|
||||||
|
await this.smartdataDb.mongoDb.createCollection(this.collectionName);
|
||||||
|
console.log(`Successfully initiated Collection ${this.collectionName}`);
|
||||||
|
}
|
||||||
|
this.mongoDbCollection = this.smartdataDb.mongoDb.collection(this.collectionName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mark unique index
|
||||||
|
*/
|
||||||
|
public markUniqueIndexes(keyArrayArg: string[] = []) {
|
||||||
|
for (const key of keyArrayArg) {
|
||||||
|
if (!this.uniqueIndexes.includes(key)) {
|
||||||
|
this.mongoDbCollection.createIndex(key, {
|
||||||
|
unique: true,
|
||||||
|
});
|
||||||
|
// make sure we only call this once and not for every doc we create
|
||||||
|
this.uniqueIndexes.push(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* adds a validation function that all newly inserted and updated objects have to pass
|
||||||
|
*/
|
||||||
|
public addDocValidation(funcArg: IDocValidationFunc<T>) {
|
||||||
|
this.objectValidation = funcArg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* finds an object in the DbCollection
|
||||||
|
*/
|
||||||
|
public async findOne(filterObject: any): Promise<any> {
|
||||||
|
await this.init();
|
||||||
|
const cursor = this.mongoDbCollection.find(filterObject);
|
||||||
|
const result = await cursor.next();
|
||||||
|
cursor.close();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getCursor(filterObject: any): Promise<SmartdataDbCursor<any>> {
|
||||||
|
await this.init();
|
||||||
|
const cursor = this.mongoDbCollection.find(filterObject);
|
||||||
|
return new SmartdataDbCursor(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* finds an object in the DbCollection
|
||||||
|
*/
|
||||||
|
public async findAll(filterObject: any): Promise<any[]> {
|
||||||
|
await this.init();
|
||||||
|
const cursor = this.mongoDbCollection.find(filterObject);
|
||||||
|
const result = await cursor.toArray();
|
||||||
|
cursor.close();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create an object in the database
|
||||||
|
*/
|
||||||
|
public async insert(dbDocArg: T & SmartDataDbDoc<T, unknown>): Promise<any> {
|
||||||
|
await this.init();
|
||||||
|
await this.checkDoc(dbDocArg);
|
||||||
|
this.markUniqueIndexes(dbDocArg.uniqueIndexes);
|
||||||
|
const saveableObject = await dbDocArg.createSavableObject();
|
||||||
|
const result = await this.mongoDbCollection.insertOne(saveableObject);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* inserts object into the DbCollection
|
||||||
|
*/
|
||||||
|
public async update(dbDocArg: T & SmartDataDbDoc<T, unknown>): Promise<any> {
|
||||||
|
await this.init();
|
||||||
|
await this.checkDoc(dbDocArg);
|
||||||
|
const identifiableObject = await dbDocArg.createIdentifiableObject();
|
||||||
|
const saveableObject = await dbDocArg.createSavableObject();
|
||||||
|
const updateableObject: any = {};
|
||||||
|
for (const key of Object.keys(saveableObject)) {
|
||||||
|
if (identifiableObject[key]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
updateableObject[key] = saveableObject[key];
|
||||||
|
}
|
||||||
|
const result = await this.mongoDbCollection.updateOne(
|
||||||
|
identifiableObject,
|
||||||
|
{ $set: updateableObject },
|
||||||
|
{ upsert: true }
|
||||||
|
);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async delete(dbDocArg: T & SmartDataDbDoc<T, unknown>): Promise<any> {
|
||||||
|
await this.init();
|
||||||
|
await this.checkDoc(dbDocArg);
|
||||||
|
const identifiableObject = await dbDocArg.createIdentifiableObject();
|
||||||
|
await this.mongoDbCollection.deleteOne(identifiableObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* checks a Doc for constraints
|
||||||
|
* if this.objectValidation is not set it passes.
|
||||||
|
*/
|
||||||
|
private checkDoc(docArg: T): Promise<void> {
|
||||||
|
const done = plugins.smartq.defer<void>();
|
||||||
|
let validationResult = true;
|
||||||
|
if (this.objectValidation) {
|
||||||
|
validationResult = this.objectValidation(docArg);
|
||||||
|
}
|
||||||
|
if (validationResult) {
|
||||||
|
done.resolve();
|
||||||
|
} else {
|
||||||
|
done.reject('validation of object did not pass');
|
||||||
|
}
|
||||||
|
return done.promise;
|
||||||
|
}
|
||||||
|
}
|
19
ts/smartdata.classes.collectionfactory.ts
Normal file
19
ts/smartdata.classes.collectionfactory.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import * as plugins from './smartdata.plugins';
|
||||||
|
import { SmartdataCollection } from './smartdata.classes.collection';
|
||||||
|
import { SmartdataDb } from './smartdata.classes.db';
|
||||||
|
|
||||||
|
export class CollectionFactory {
|
||||||
|
public collections: { [key: string]: SmartdataCollection<any> } = {};
|
||||||
|
|
||||||
|
public getCollection = (nameArg: string, dbArg: SmartdataDb): SmartdataCollection<any> => {
|
||||||
|
if (!this.collections[nameArg]) {
|
||||||
|
this.collections[nameArg] = (() => {
|
||||||
|
if (dbArg instanceof SmartdataDb) {
|
||||||
|
// tslint:disable-next-line: no-string-literal
|
||||||
|
return new SmartdataCollection(nameArg, dbArg);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
return this.collections[nameArg];
|
||||||
|
};
|
||||||
|
}
|
39
ts/smartdata.classes.cursor.ts
Normal file
39
ts/smartdata.classes.cursor.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import * as plugins from './smartdata.plugins';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a wrapper for the native mongodb cursor. Exposes better
|
||||||
|
*/
|
||||||
|
export class SmartdataDbCursor<T = any> {
|
||||||
|
// STATIC
|
||||||
|
|
||||||
|
// INSTANCE
|
||||||
|
public mongodbCursor: plugins.mongodb.FindCursor<T>;
|
||||||
|
constructor(cursorArg: plugins.mongodb.FindCursor<T>) {
|
||||||
|
this.mongodbCursor = cursorArg
|
||||||
|
};
|
||||||
|
|
||||||
|
public async next(closeAtEnd = true) {
|
||||||
|
const result = await this.mongodbCursor.next();
|
||||||
|
if (!result && closeAtEnd) {
|
||||||
|
await this.close();
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
public async forEach(forEachFuncArg: (itemArg: T) => Promise<any>, closeCursorAtEnd = true) {
|
||||||
|
let currentValue: T;
|
||||||
|
do {
|
||||||
|
currentValue = await this.mongodbCursor.next();
|
||||||
|
if (currentValue) {
|
||||||
|
await forEachFuncArg(currentValue);
|
||||||
|
}
|
||||||
|
} while (currentValue);
|
||||||
|
if (closeCursorAtEnd) {
|
||||||
|
await this.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async close() {
|
||||||
|
await this.mongodbCursor.close();
|
||||||
|
}
|
||||||
|
}
|
@ -1,33 +1,84 @@
|
|||||||
import * as plugins from './smartdata.plugins'
|
import * as plugins from './smartdata.plugins';
|
||||||
|
import { ObjectMap } from '@pushrocks/lik';
|
||||||
|
|
||||||
export type TConnectionStatus = 'disconnected' | 'connected' | 'failed'
|
import { SmartdataCollection } from './smartdata.classes.collection';
|
||||||
|
import { EasyStore } from './smartdata.classes.easystore';
|
||||||
|
|
||||||
export class Db {
|
import { logger } from './smartdata.logging';
|
||||||
dbUrl: string
|
import { IMongoDescriptor } from './interfaces';
|
||||||
db: plugins.mongodb.Db
|
|
||||||
status: TConnectionStatus
|
|
||||||
|
|
||||||
constructor(dbUrl: string) {
|
/**
|
||||||
this.dbUrl = dbUrl
|
* interface - indicates the connection status of the db
|
||||||
|
*/
|
||||||
|
export type TConnectionStatus = 'initial' | 'disconnected' | 'connected' | 'failed';
|
||||||
|
|
||||||
|
export class SmartdataDb {
|
||||||
|
smartdataOptions: IMongoDescriptor;
|
||||||
|
mongoDbClient: plugins.mongodb.MongoClient;
|
||||||
|
mongoDb: plugins.mongodb.Db;
|
||||||
|
status: TConnectionStatus;
|
||||||
|
smartdataCollectionMap = new ObjectMap<SmartdataCollection<any>>();
|
||||||
|
|
||||||
|
constructor(smartdataOptions: IMongoDescriptor) {
|
||||||
|
this.smartdataOptions = smartdataOptions;
|
||||||
|
this.status = 'initial';
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(): plugins.q.Promise<any> {
|
// easystore
|
||||||
let done = plugins.q.defer()
|
public async createEasyStore(nameIdArg: string) {
|
||||||
plugins.mongodb.MongoClient.connect(this.dbUrl, (err, db) => {
|
const easyStore = new EasyStore(nameIdArg, this);
|
||||||
if (err) { console.log(err) }
|
return easyStore;
|
||||||
plugins.assert.equal(null, err)
|
|
||||||
this.db = db
|
|
||||||
plugins.beautylog.success(`connected to database at ${this.dbUrl}`)
|
|
||||||
done.resolve(this.db)
|
|
||||||
})
|
|
||||||
return done.promise
|
|
||||||
}
|
}
|
||||||
|
|
||||||
close(): plugins.q.Promise<any> {
|
// basic connection stuff ----------------------------------------------
|
||||||
let done = plugins.q.defer()
|
|
||||||
this.db.close()
|
/**
|
||||||
plugins.beautylog.ok(`disconnected to database at ${this.dbUrl}`)
|
* connects to the database that was specified during instance creation
|
||||||
done.resolve()
|
*/
|
||||||
return done.promise
|
public async init(): Promise<any> {
|
||||||
|
const finalConnectionUrl = this.smartdataOptions.mongoDbUrl
|
||||||
|
.replace('<USERNAME>', this.smartdataOptions.mongoDbUser)
|
||||||
|
.replace('<username>', this.smartdataOptions.mongoDbUser)
|
||||||
|
.replace('<USER>', this.smartdataOptions.mongoDbUser)
|
||||||
|
.replace('<user>', this.smartdataOptions.mongoDbUser)
|
||||||
|
.replace('<PASSWORD>', this.smartdataOptions.mongoDbPass)
|
||||||
|
.replace('<password>', this.smartdataOptions.mongoDbPass)
|
||||||
|
.replace('<DBNAME>', this.smartdataOptions.mongoDbName)
|
||||||
|
.replace('<dbname>', this.smartdataOptions.mongoDbName);
|
||||||
|
|
||||||
|
this.mongoDbClient = await plugins.mongodb.MongoClient.connect(finalConnectionUrl, {
|
||||||
|
maxPoolSize: 100,
|
||||||
|
maxIdleTimeMS: 10,
|
||||||
|
});
|
||||||
|
this.mongoDb = this.mongoDbClient.db(this.smartdataOptions.mongoDbName);
|
||||||
|
this.status = 'connected';
|
||||||
|
console.log(`Connected to database ${this.smartdataOptions.mongoDbName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* closes the connection to the databse
|
||||||
|
*/
|
||||||
|
public async close(): Promise<any> {
|
||||||
|
await this.mongoDbClient.close();
|
||||||
|
this.status = 'disconnected';
|
||||||
|
logger.log('info', `disconnected from database ${this.smartdataOptions.mongoDbName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle table to class distribution
|
||||||
|
|
||||||
|
public addCollection(SmartdataCollectionArg: SmartdataCollection<any>) {
|
||||||
|
this.smartdataCollectionMap.add(SmartdataCollectionArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a collection's name and returns a SmartdataCollection instance
|
||||||
|
* @param nameArg
|
||||||
|
* @returns DbTable
|
||||||
|
*/
|
||||||
|
public async getSmartdataCollectionByName<T>(nameArg: string): Promise<SmartdataCollection<T>> {
|
||||||
|
const resultCollection = await this.smartdataCollectionMap.find(async (dbTableArg) => {
|
||||||
|
return dbTableArg.collectionName === nameArg;
|
||||||
|
});
|
||||||
|
return resultCollection;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
import * as plugins from './smartdata.plugins'
|
|
||||||
import { Db } from './smartdata.classes.db'
|
|
||||||
|
|
||||||
export class DbCollection<T> {
|
|
||||||
collection: plugins.mongodb.Collection
|
|
||||||
constructor(nameArg: string, dbArg: Db) {
|
|
||||||
this.collection = dbArg.db.collection(nameArg)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* adds a validation function that all newly inserted and updated objects have to pass
|
|
||||||
*/
|
|
||||||
addObjectValidation(funcArg) { }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* finds an object in the DbCollection
|
|
||||||
*/
|
|
||||||
find(docMatchArg: T | any): plugins.q.Promise<T[]> {
|
|
||||||
let done = plugins.q.defer<T[]>()
|
|
||||||
this.collection.find(docMatchArg).toArray((err, docs) => {
|
|
||||||
if (err) { throw err }
|
|
||||||
done.resolve(docs)
|
|
||||||
})
|
|
||||||
return done.promise
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* inserts object into the DbCollection
|
|
||||||
*/
|
|
||||||
insertOne(docArg: T): plugins.q.Promise<void> {
|
|
||||||
let done = plugins.q.defer<void>()
|
|
||||||
this.checkDoc(docArg).then(() => {
|
|
||||||
this.collection.insertOne(docArg)
|
|
||||||
.then(() => { done.resolve() })
|
|
||||||
})
|
|
||||||
return done.promise
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* inserts many objects at once into the DbCollection
|
|
||||||
*/
|
|
||||||
insertMany(docArrayArg: T[]): plugins.q.Promise<void> {
|
|
||||||
let done = plugins.q.defer<void>()
|
|
||||||
let checkDocPromiseArray: plugins.q.Promise<void>[] = []
|
|
||||||
for (let docArg of docArrayArg){
|
|
||||||
checkDocPromiseArray.push(this.checkDoc(docArg))
|
|
||||||
}
|
|
||||||
plugins.q.all(checkDocPromiseArray).then(() => {
|
|
||||||
this.collection.insertMany(docArrayArg)
|
|
||||||
.then(() => { done.resolve() })
|
|
||||||
})
|
|
||||||
return done.promise
|
|
||||||
}
|
|
||||||
|
|
||||||
private checkDoc(doc: T): plugins.q.Promise<void> {
|
|
||||||
let done = plugins.q.defer<void>()
|
|
||||||
done.resolve()
|
|
||||||
return done.promise
|
|
||||||
}
|
|
||||||
}
|
|
253
ts/smartdata.classes.doc.ts
Normal file
253
ts/smartdata.classes.doc.ts
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
import * as plugins from './smartdata.plugins';
|
||||||
|
|
||||||
|
import { ObjectMap } from '@pushrocks/lik';
|
||||||
|
|
||||||
|
import { SmartdataDb } from './smartdata.classes.db';
|
||||||
|
import { SmartdataDbCursor } from './smartdata.classes.cursor';
|
||||||
|
import { IManager, SmartdataCollection } from './smartdata.classes.collection';
|
||||||
|
|
||||||
|
export type TDocCreation = 'db' | 'new' | 'mixed';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* saveable - saveable decorator to be used on class properties
|
||||||
|
*/
|
||||||
|
export function svDb() {
|
||||||
|
return (target: SmartDataDbDoc<unknown, unknown>, key: string) => {
|
||||||
|
console.log(`called svDb() on >${target.constructor.name}.${key}<`);
|
||||||
|
if (!target.saveableProperties) {
|
||||||
|
target.saveableProperties = [];
|
||||||
|
}
|
||||||
|
target.saveableProperties.push(key);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* unique index - decorator to mark a unique index
|
||||||
|
*/
|
||||||
|
export function unI() {
|
||||||
|
return (target: SmartDataDbDoc<unknown, unknown>, key: string) => {
|
||||||
|
console.log(`called unI on >>${target.constructor.name}.${key}<<`);
|
||||||
|
|
||||||
|
// mark the index as unique
|
||||||
|
if (!target.uniqueIndexes) {
|
||||||
|
target.uniqueIndexes = [];
|
||||||
|
}
|
||||||
|
target.uniqueIndexes.push(key);
|
||||||
|
|
||||||
|
// and also save it
|
||||||
|
if (!target.saveableProperties) {
|
||||||
|
target.saveableProperties = [];
|
||||||
|
}
|
||||||
|
target.saveableProperties.push(key);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const convertFilterForMongoDb = (filterArg: { [key: string]: any }) => {
|
||||||
|
const convertedFilter: { [key: string]: any } = {};
|
||||||
|
const convertFilterArgument = (keyPathArg2: string, filterArg2: any) => {
|
||||||
|
if (typeof filterArg2 === 'object') {
|
||||||
|
for (const key of Object.keys(filterArg2)) {
|
||||||
|
if (key.startsWith('$')) {
|
||||||
|
convertedFilter[keyPathArg2] = filterArg2;
|
||||||
|
return;
|
||||||
|
} else if (key.includes('.')) {
|
||||||
|
throw new Error('keys cannot contain dots');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const key of Object.keys(filterArg2)) {
|
||||||
|
convertFilterArgument(`${keyPathArg2}.${key}`, filterArg2[key]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
convertedFilter[keyPathArg2] = filterArg2;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for (const key of Object.keys(filterArg)) {
|
||||||
|
convertFilterArgument(key, filterArg[key]);
|
||||||
|
}
|
||||||
|
return convertedFilter;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class SmartDataDbDoc<T extends TImplements, TImplements, TManager extends IManager = any> {
|
||||||
|
/**
|
||||||
|
* the collection object an Doc belongs to
|
||||||
|
*/
|
||||||
|
public static collection: SmartdataCollection<any>;
|
||||||
|
public collection: SmartdataCollection<any>;
|
||||||
|
public static defaultManager;
|
||||||
|
public static manager;
|
||||||
|
public manager: TManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* how the Doc in memory was created, may prove useful later.
|
||||||
|
*/
|
||||||
|
public creationStatus: TDocCreation = 'new';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* unique indexes
|
||||||
|
*/
|
||||||
|
public uniqueIndexes: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* an array of saveable properties of a doc
|
||||||
|
*/
|
||||||
|
public saveableProperties: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* name
|
||||||
|
*/
|
||||||
|
public name: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* primary id in the database
|
||||||
|
*/
|
||||||
|
public dbDocUniqueId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* class constructor
|
||||||
|
*/
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
public static createInstanceFromMongoDbNativeDoc<T>(
|
||||||
|
this: plugins.tsclass.typeFest.Class<T>,
|
||||||
|
mongoDbNativeDocArg: any
|
||||||
|
): T {
|
||||||
|
const newInstance = new this();
|
||||||
|
(newInstance as any).creationStatus = 'db';
|
||||||
|
for (const key of Object.keys(mongoDbNativeDocArg)) {
|
||||||
|
newInstance[key] = mongoDbNativeDocArg[key];
|
||||||
|
}
|
||||||
|
return newInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gets all instances as array
|
||||||
|
* @param this
|
||||||
|
* @param filterArg
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public static async getInstances<T>(
|
||||||
|
this: plugins.tsclass.typeFest.Class<T>,
|
||||||
|
filterArg: plugins.tsclass.typeFest.PartialDeep<T>
|
||||||
|
): Promise<T[]> {
|
||||||
|
const foundDocs = await (this as any).collection.findAll(convertFilterForMongoDb(filterArg));
|
||||||
|
const returnArray = [];
|
||||||
|
for (const foundDoc of foundDocs) {
|
||||||
|
const newInstance: T = (this as any).createInstanceFromMongoDbNativeDoc(foundDoc);
|
||||||
|
returnArray.push(newInstance);
|
||||||
|
}
|
||||||
|
return returnArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gets the first matching instance
|
||||||
|
* @param this
|
||||||
|
* @param filterArg
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public static async getInstance<T>(
|
||||||
|
this: plugins.tsclass.typeFest.Class<T>,
|
||||||
|
filterArg: plugins.tsclass.typeFest.PartialDeep<T>
|
||||||
|
): Promise<T> {
|
||||||
|
const foundDoc = await (this as any).collection.findOne(convertFilterForMongoDb(filterArg));
|
||||||
|
if (foundDoc) {
|
||||||
|
const newInstance: T = (this as any).createInstanceFromMongoDbNativeDoc(foundDoc);
|
||||||
|
return newInstance;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get cursor
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public static async getCursor<T>(
|
||||||
|
this: plugins.tsclass.typeFest.Class<T>,
|
||||||
|
filterArg: plugins.tsclass.typeFest.PartialDeep<T>
|
||||||
|
) {
|
||||||
|
const cursor: SmartdataDbCursor<T> = await (this as any).collection.getCursor(convertFilterForMongoDb(filterArg));
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* run a function for all instances
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public static async forEach<T>(
|
||||||
|
this: plugins.tsclass.typeFest.Class<T>,
|
||||||
|
filterArg: plugins.tsclass.typeFest.PartialDeep<T>,
|
||||||
|
forEachFunction: (itemArg: T) => Promise<any>
|
||||||
|
) {
|
||||||
|
const cursor: SmartdataDbCursor<T> = await (this as any).getCursor(filterArg);
|
||||||
|
await cursor.forEach(forEachFunction)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* saves this instance but not any connected items
|
||||||
|
* may lead to data inconsistencies, but is faster
|
||||||
|
*/
|
||||||
|
public async save() {
|
||||||
|
// tslint:disable-next-line: no-this-assignment
|
||||||
|
const self: any = this;
|
||||||
|
let dbResult: any;
|
||||||
|
switch (this.creationStatus) {
|
||||||
|
case 'db':
|
||||||
|
dbResult = await this.collection.update(self);
|
||||||
|
break;
|
||||||
|
case 'new':
|
||||||
|
dbResult = await this.collection.insert(self);
|
||||||
|
this.creationStatus = 'db';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.error('neither new nor in db?');
|
||||||
|
}
|
||||||
|
return dbResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* deletes a document from the database
|
||||||
|
*/
|
||||||
|
public async delete() {
|
||||||
|
await this.collection.delete(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* also store any referenced objects to DB
|
||||||
|
* better for data consistency
|
||||||
|
*/
|
||||||
|
public saveDeep(savedMapArg: ObjectMap<SmartDataDbDoc<any, any>> = null) {
|
||||||
|
if (!savedMapArg) {
|
||||||
|
savedMapArg = new ObjectMap<SmartDataDbDoc<any, any>>();
|
||||||
|
}
|
||||||
|
savedMapArg.add(this);
|
||||||
|
this.save();
|
||||||
|
for (const propertyKey of Object.keys(this)) {
|
||||||
|
const property: any = this[propertyKey];
|
||||||
|
if (property instanceof SmartDataDbDoc && !savedMapArg.checkForObject(property)) {
|
||||||
|
property.saveDeep(savedMapArg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* creates a saveable object so the instance can be persisted as json in the database
|
||||||
|
*/
|
||||||
|
public async createSavableObject(): Promise<TImplements> {
|
||||||
|
const saveableObject: unknown = {}; // is not exposed to outside, so any is ok here
|
||||||
|
for (const propertyNameString of this.saveableProperties) {
|
||||||
|
saveableObject[propertyNameString] = this[propertyNameString];
|
||||||
|
}
|
||||||
|
return saveableObject as TImplements;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* creates an identifiable object for operations that require filtering
|
||||||
|
*/
|
||||||
|
public async createIdentifiableObject() {
|
||||||
|
const identifiableObject: any = {}; // is not exposed to outside, so any is ok here
|
||||||
|
for (const propertyNameString of this.uniqueIndexes) {
|
||||||
|
identifiableObject[propertyNameString] = this[propertyNameString];
|
||||||
|
}
|
||||||
|
return identifiableObject;
|
||||||
|
}
|
||||||
|
}
|
93
ts/smartdata.classes.easystore.ts
Normal file
93
ts/smartdata.classes.easystore.ts
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import * as plugins from './smartdata.plugins';
|
||||||
|
import { Collection } from './smartdata.classes.collection';
|
||||||
|
import { SmartdataDb } from './smartdata.classes.db';
|
||||||
|
import { SmartDataDbDoc, svDb, unI } from './smartdata.classes.doc';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EasyStore allows the storage of easy objects. It also allows easy sharing of the object between different instances
|
||||||
|
*/
|
||||||
|
export class EasyStore<T> {
|
||||||
|
// instance
|
||||||
|
public smartdataDbRef: SmartdataDb;
|
||||||
|
public nameId: string;
|
||||||
|
|
||||||
|
private easyStoreClass = (() => {
|
||||||
|
@Collection(() => this.smartdataDbRef)
|
||||||
|
class SmartdataEasyStore extends SmartDataDbDoc<SmartdataEasyStore, SmartdataEasyStore> {
|
||||||
|
@unI()
|
||||||
|
public nameId: string;
|
||||||
|
|
||||||
|
@svDb()
|
||||||
|
public data: Partial<T>;
|
||||||
|
}
|
||||||
|
return SmartdataEasyStore;
|
||||||
|
})();
|
||||||
|
|
||||||
|
constructor(nameIdArg: string, smnartdataDbRefArg: SmartdataDb) {
|
||||||
|
this.smartdataDbRef = smnartdataDbRefArg;
|
||||||
|
this.nameId = nameIdArg;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getEasyStore() {
|
||||||
|
let easyStore = await this.easyStoreClass.getInstance({
|
||||||
|
nameId: this.nameId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!easyStore) {
|
||||||
|
easyStore = new this.easyStoreClass();
|
||||||
|
easyStore.nameId = this.nameId;
|
||||||
|
easyStore.data = {};
|
||||||
|
await easyStore.save();
|
||||||
|
}
|
||||||
|
return easyStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* reads all keyValue pairs at once and returns them
|
||||||
|
*/
|
||||||
|
public async readAll() {
|
||||||
|
const easyStore = await this.getEasyStore();
|
||||||
|
return easyStore.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* reads a keyValueFile from disk
|
||||||
|
*/
|
||||||
|
public async readKey(keyArg: keyof T) {
|
||||||
|
const easyStore = await this.getEasyStore();
|
||||||
|
return easyStore.data[keyArg];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* writes a specific key to the keyValueStore
|
||||||
|
*/
|
||||||
|
public async writeKey(keyArg: keyof T, valueArg: any) {
|
||||||
|
const easyStore = await this.getEasyStore();
|
||||||
|
easyStore.data[keyArg] = valueArg;
|
||||||
|
await easyStore.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async deleteKey(keyArg: keyof T) {
|
||||||
|
const easyStore = await this.getEasyStore();
|
||||||
|
delete easyStore.data[keyArg];
|
||||||
|
await easyStore.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* writes all keyValue pairs in the object argument
|
||||||
|
*/
|
||||||
|
public async writeAll(keyValueObject: Partial<T>) {
|
||||||
|
const easyStore = await this.getEasyStore();
|
||||||
|
easyStore.data = { ...easyStore.data, ...keyValueObject };
|
||||||
|
await easyStore.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* wipes a key value store from disk
|
||||||
|
*/
|
||||||
|
public async wipe() {
|
||||||
|
const easyStore = await this.getEasyStore();
|
||||||
|
easyStore.data = {};
|
||||||
|
await easyStore.save();
|
||||||
|
}
|
||||||
|
}
|
3
ts/smartdata.logging.ts
Normal file
3
ts/smartdata.logging.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import * as plugins from './smartdata.plugins';
|
||||||
|
|
||||||
|
export const logger = new plugins.smartlog.ConsoleLog();
|
@ -1,5 +1,14 @@
|
|||||||
import 'typings-global'
|
// tsclass scope
|
||||||
export import assert = require('assert')
|
import * as tsclass from '@tsclass/tsclass';
|
||||||
export import beautylog = require('beautylog')
|
|
||||||
export import mongodb = require('mongodb')
|
export { tsclass };
|
||||||
export import q = require('q')
|
|
||||||
|
// @pushrocks scope
|
||||||
|
import * as smartlog from '@pushrocks/smartlog';
|
||||||
|
import * as lodash from 'lodash';
|
||||||
|
import * as mongodb from 'mongodb';
|
||||||
|
import * as smartq from '@pushrocks/smartpromise';
|
||||||
|
import * as smartstring from '@pushrocks/smartstring';
|
||||||
|
import * as smartunique from '@pushrocks/smartunique';
|
||||||
|
|
||||||
|
export { smartlog, lodash, smartq, mongodb, smartstring, smartunique };
|
||||||
|
7
tsconfig.json
Normal file
7
tsconfig.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"target": "es2017",
|
||||||
|
"module": "commonjs"
|
||||||
|
}
|
||||||
|
}
|
16
tslint.json
16
tslint.json
@ -1,3 +1,17 @@
|
|||||||
{
|
{
|
||||||
"extends": "tslint-config-standard"
|
"extends": ["tslint:latest", "tslint-config-prettier"],
|
||||||
|
"rules": {
|
||||||
|
"semicolon": [true, "always"],
|
||||||
|
"no-console": false,
|
||||||
|
"ordered-imports": false,
|
||||||
|
"object-literal-sort-keys": false,
|
||||||
|
"member-ordering": {
|
||||||
|
"options":{
|
||||||
|
"order": [
|
||||||
|
"static-method"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultSeverity": "warning"
|
||||||
}
|
}
|
Reference in New Issue
Block a user