Compare commits
99 Commits
Author | SHA1 | Date | |
---|---|---|---|
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 |
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,5 +1,4 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
test/data
|
|
||||||
pages/
|
|
||||||
coverage/
|
|
||||||
public/
|
public/
|
||||||
|
coverage/
|
||||||
|
.nogit/
|
||||||
|
145
.gitlab-ci.yml
145
.gitlab-ci.yml
@ -1,43 +1,150 @@
|
|||||||
image: hosttoday/ht-docker-node:npmts
|
# gitzone standard
|
||||||
|
image: hosttoday/ht-docker-node:npmci
|
||||||
|
|
||||||
|
cache:
|
||||||
|
paths:
|
||||||
|
- .npmci_cache/
|
||||||
|
key: "$CI_BUILD_STAGE"
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
|
- security
|
||||||
- test
|
- test
|
||||||
- release
|
- release
|
||||||
|
- metadata
|
||||||
|
|
||||||
before_script:
|
# ====================
|
||||||
- apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv EA312927
|
# security stage
|
||||||
- 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
|
# ====================
|
||||||
- apt-get update
|
mirror:
|
||||||
- apt-get install -y mongodb-org
|
stage: security
|
||||||
|
|
||||||
testLEGACY:
|
|
||||||
stage: test
|
|
||||||
script:
|
script:
|
||||||
- npmci test legacy
|
- npmci git mirror
|
||||||
tags:
|
tags:
|
||||||
- docker
|
- docker
|
||||||
|
- notpriv
|
||||||
|
|
||||||
|
snyk:
|
||||||
|
stage: security
|
||||||
|
script:
|
||||||
|
- npmci npm prepare
|
||||||
|
- npmci command npm install -g snyk
|
||||||
|
- npmci command npm install --ignore-scripts
|
||||||
|
- npmci command snyk test
|
||||||
|
tags:
|
||||||
|
- docker
|
||||||
|
- notpriv
|
||||||
|
|
||||||
|
sast:
|
||||||
|
stage: security
|
||||||
|
image: registry.gitlab.com/hosttoday/ht-docker-dbase:npmci
|
||||||
|
variables:
|
||||||
|
DOCKER_DRIVER: overlay2
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
|
services:
|
||||||
|
- docker:stable-dind
|
||||||
|
script:
|
||||||
|
- npmci npm prepare
|
||||||
|
- npmci npm install
|
||||||
|
- npmci command npm run build
|
||||||
|
- export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
|
||||||
|
- docker run
|
||||||
|
--env SAST_CONFIDENCE_LEVEL="${SAST_CONFIDENCE_LEVEL:-3}"
|
||||||
|
--volume "$PWD:/code"
|
||||||
|
--volume /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
"registry.gitlab.com/gitlab-org/security-products/sast:$SP_VERSION" /app/bin/run /code
|
||||||
|
artifacts:
|
||||||
|
reports:
|
||||||
|
sast: gl-sast-report.json
|
||||||
|
tags:
|
||||||
|
- docker
|
||||||
|
- priv
|
||||||
|
|
||||||
|
# ====================
|
||||||
|
# test stage
|
||||||
|
# ====================
|
||||||
|
|
||||||
testLTS:
|
testLTS:
|
||||||
stage: test
|
stage: test
|
||||||
script:
|
script:
|
||||||
- npmci test lts
|
- npmci npm prepare
|
||||||
|
- npmci node install lts
|
||||||
|
- npmci npm install
|
||||||
|
- npmci npm test
|
||||||
|
coverage: /\d+.?\d+?\%\s*coverage/
|
||||||
tags:
|
tags:
|
||||||
- docker
|
- docker
|
||||||
|
- notpriv
|
||||||
|
|
||||||
testSTABLE:
|
testSTABLE:
|
||||||
stage: test
|
stage: test
|
||||||
script:
|
script:
|
||||||
- npmci test stable
|
- npmci npm prepare
|
||||||
|
- npmci node install stable
|
||||||
|
- npmci npm install
|
||||||
|
- npmci npm test
|
||||||
|
coverage: /\d+.?\d+?\%\s*coverage/
|
||||||
tags:
|
tags:
|
||||||
- docker
|
- docker
|
||||||
|
- notpriv
|
||||||
|
|
||||||
release:
|
release:
|
||||||
stage: release
|
stage: release
|
||||||
environment: npm_registry
|
|
||||||
script:
|
script:
|
||||||
- npmci publish
|
- npmci node install stable
|
||||||
|
- npmci npm publish
|
||||||
|
only:
|
||||||
|
- tags
|
||||||
|
tags:
|
||||||
|
- docker
|
||||||
|
- notpriv
|
||||||
|
|
||||||
|
# ====================
|
||||||
|
# metadata stage
|
||||||
|
# ====================
|
||||||
|
codequality:
|
||||||
|
stage: metadata
|
||||||
|
image: docker:stable
|
||||||
|
allow_failure: true
|
||||||
|
services:
|
||||||
|
- docker:stable-dind
|
||||||
|
script:
|
||||||
|
- export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
|
||||||
|
- docker run
|
||||||
|
--env SOURCE_CODE="$PWD"
|
||||||
|
--volume "$PWD":/code
|
||||||
|
--volume /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
"registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code
|
||||||
|
artifacts:
|
||||||
|
paths: [codeclimate.json]
|
||||||
|
tags:
|
||||||
|
- docker
|
||||||
|
- priv
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
stage: metadata
|
||||||
|
script:
|
||||||
|
- npmci trigger
|
||||||
|
only:
|
||||||
|
- tags
|
||||||
|
tags:
|
||||||
|
- docker
|
||||||
|
- notpriv
|
||||||
|
|
||||||
|
pages:
|
||||||
|
image: hosttoday/ht-docker-node:npmci
|
||||||
|
stage: metadata
|
||||||
|
script:
|
||||||
|
- npmci command npm install -g typedoc typescript
|
||||||
|
- npmci npm prepare
|
||||||
|
- npmci npm install
|
||||||
|
- npmci command typedoc --module "commonjs" --target "ES2016" --out public/ ts/
|
||||||
|
tags:
|
||||||
|
- docker
|
||||||
|
- notpriv
|
||||||
only:
|
only:
|
||||||
- tags
|
- tags
|
||||||
tags:
|
artifacts:
|
||||||
- docker
|
expire_in: 1 week
|
||||||
|
paths:
|
||||||
|
- public
|
||||||
|
allow_failure: true
|
||||||
|
138
README.md
138
README.md
@ -1,89 +1,129 @@
|
|||||||
# smartdata
|
# @pushrocks/smartdata
|
||||||
|
|
||||||
> Note: Still in Beta
|
do more with data and RethinkDB
|
||||||
|
|
||||||
smartdata is a ODM that adheres to TypeScript practices and uses classes to organize data.
|
## Availabililty
|
||||||
It uses MongoDB as persistent storage.
|
|
||||||
|
[](https://www.npmjs.com/package/smartdata)
|
||||||
|
[](https://GitLab.com/pushrocks/smartdata)
|
||||||
|
[](https://github.com/pushrocks/smartdata)
|
||||||
|
[](https://pushrocks.gitlab.io/smartdata/)
|
||||||
|
|
||||||
|
## Status for master
|
||||||
|
|
||||||
|
[](https://GitLab.com/pushrocks/smartdata/commits/master)
|
||||||
|
[](https://GitLab.com/pushrocks/smartdata/commits/master)
|
||||||
|
[](https://www.npmjs.com/package/smartdata)
|
||||||
|
[](https://david-dm.org/pushrocks/smartdata)
|
||||||
|
[](https://www.bithound.io/github/pushrocks/smartdata/master/dependencies/npm)
|
||||||
|
[](https://www.bithound.io/github/pushrocks/smartdata)
|
||||||
|
[](https://snyk.io/test/npm/smartdata)
|
||||||
|
[](https://nodejs.org/dist/latest-v6.x/docs/api/)
|
||||||
|
[](https://nodejs.org/dist/latest-v6.x/docs/api/)
|
||||||
|
[](http://standardjs.com/)
|
||||||
|
|
||||||
|
## 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
|
## 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
|
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.
|
||||||
|
|
||||||
MongoDB term | smartdata class
|
How RethinkDB's terms map to the ones of smartdata:
|
||||||
--- | ---
|
|
||||||
Database | smartdata.Db
|
| MongoDb term | smartdata class |
|
||||||
Collection | smartdata.DbCollection
|
| ------------ | ----------------------------- |
|
||||||
Document | smartdata.DbDoc
|
| Database | smartdata.SmartdataDb |
|
||||||
|
| Collection | smartdata.SmartdataCollection |
|
||||||
|
| Document | smartdata.SmartadataDoc |
|
||||||
|
|
||||||
### class Db
|
### class Db
|
||||||
|
|
||||||
represents a Database. Naturally it has .connect() etc. methods on it.
|
represents a Database. Naturally it has .connect() etc. methods on it.
|
||||||
Since it is a class you can have multiple DBs defined.
|
|
||||||
```typescript
|
```typescript
|
||||||
import * as smartdata from 'smartdata'
|
import * as smartdata from 'smartdata';
|
||||||
|
|
||||||
let myDb1 = new smartdata.Db('someConnectionUrl')
|
const smartdataDb = new smartdata.SmartdataDb({
|
||||||
let myDb2 = new smartdata.Db('someConnectionUrl')
|
mongoDbUrl: '//someurl',
|
||||||
|
mongoDbName: 'myDatabase',
|
||||||
|
mongoDbPass: 'mypassword'
|
||||||
|
});
|
||||||
|
|
||||||
myDb1.connect()
|
smartdataDb.connect();
|
||||||
myDb2.connect()
|
|
||||||
|
|
||||||
// continues in next block...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### class DbCollection
|
### class DbCollection
|
||||||
|
|
||||||
represents a collection of objects.
|
represents a collection of objects.
|
||||||
A collection is defined by the object class (that is extending smartdata.dbdoc) it respresents
|
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
|
So to get to get access to a specific collection you document
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// continues from the block before...
|
// continues from the block before...
|
||||||
|
|
||||||
class myObject extends smartdata.DbDoc { // read the next block about DbDoc
|
@smartdata.Collection(smartdataDb)
|
||||||
property1:string
|
class MyObject extends smartdata.DbDoc<MyObject> {
|
||||||
property2:number
|
// read the next block about DbDoc
|
||||||
constructor(optionsArg:{
|
@smartdata.svDb() property1: string; // @smartdata.svDb() marks the property for db save
|
||||||
queryArg?:any,
|
property2: number; // this one is not marked, so it won't be save upon calling this.save()
|
||||||
dataArg?:{
|
constructor(optionsArg: { property1: string; property2: number }) {
|
||||||
property1:string,
|
super();
|
||||||
property2:number
|
}
|
||||||
}
|
}
|
||||||
}) {
|
|
||||||
super(this,optionsArg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let myCollection = myDb1.getCollection(myObject)
|
|
||||||
```
|
|
||||||
|
|
||||||
> Alert: You NEVER instantiate a collection.
|
// start to instantiate instances of classes from scratch or database
|
||||||
This is done for you!!!
|
|
||||||
|
let localObject = new MyObject({
|
||||||
|
property1: 'hi',
|
||||||
|
property2: 2
|
||||||
|
});
|
||||||
|
localObject.save(); // saves the object to the database
|
||||||
|
|
||||||
|
// start retrieving instances
|
||||||
|
|
||||||
|
MyObject.getInstance<MyObject>({
|
||||||
|
property: 'hi'
|
||||||
|
}); // outputs a new instance of MyObject with the values from db assigned
|
||||||
|
```
|
||||||
|
|
||||||
### class DbDoc
|
### class DbDoc
|
||||||
|
|
||||||
represents a individual document in a collection
|
represents a individual document in a collection
|
||||||
and thereby is ideally suited to extend the class you want to actually store.
|
and thereby is ideally suited to extend the class you want to actually store.
|
||||||
|
|
||||||
|
**sStore** instances of classes to Db:
|
||||||
DbDoc extends your class with the following methods:
|
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.
|
- `.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()`.
|
- `.saveDeep()` does the same like `.save()`.
|
||||||
In addition it will look for properties that reference an object
|
In addition it will look for properties that reference an object
|
||||||
that extends DbDoc as well and call .saveDeep() on them as well.
|
that extends DbDoc as well and call .saveDeep() on them as well.
|
||||||
Loops are prevented
|
Loops are prevented
|
||||||
|
|
||||||
So now we can **store** instances of classes to Db...
|
**Get** a new class instance from a Doc in the DB:
|
||||||
How do we **get** a new class instance from a Doc in the DB?
|
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. Yes, let that sink in a minute :)
|
||||||
Easy! Take a look at the constructor. When you specify `optionsArg.queryArg`
|
|
||||||
smartdata will fill in the data from the database!
|
So you can just call `.getInstance({ /* filter props here */ })`.
|
||||||
But when you specify a `optionsArg.dataArg` instead
|
|
||||||
the data for the class is taken from there :)
|
|
||||||
|
|
||||||
## TypeScript
|
## TypeScript
|
||||||
|
|
||||||
How does TypeScript play into this?
|
How does TypeScript play into this?
|
||||||
Since you define your classes in TapeScript and types flow through smartdata in a generic way
|
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.
|
you should get all the Intellisense and type checking you love when using smartdata.
|
||||||
smartdata itself also bundles typings.
|
smartdata itself also bundles typings.
|
||||||
So you don't need to install any additional types whenimporting smartdata.
|
So you don't need to install any additional types when importing smartdata.
|
||||||
|
|
||||||
[](https://push.rocks)
|
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.html)
|
||||||
|
|
||||||
|
[](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)
|
|
12
npmextra.json
Normal file
12
npmextra.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"npmdocker": {
|
||||||
|
"baseImage": "hosttoday/ht-docker-node:mongo",
|
||||||
|
"command": "npmci test stable",
|
||||||
|
"dockerSock": false
|
||||||
|
},
|
||||||
|
"npmci": {
|
||||||
|
"npmGlobalTools": [],
|
||||||
|
"npmAccessLevel": "public",
|
||||||
|
"npmRegistryUrl": "registry.npmjs.org"
|
||||||
|
}
|
||||||
|
}
|
2260
package-lock.json
generated
Normal file
2260
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
41
package.json
41
package.json
@ -1,12 +1,14 @@
|
|||||||
{
|
{
|
||||||
"name": "smartdata",
|
"name": "@pushrocks/smartdata",
|
||||||
"version": "1.0.13",
|
"version": "3.1.13",
|
||||||
|
"private": false,
|
||||||
"description": "do more with data",
|
"description": "do more with data",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"typings": "dist/index.d.ts",
|
"typings": "dist/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)"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -19,19 +21,26 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://gitlab.com/pushrocks/smartdata#README",
|
"homepage": "https://gitlab.com/pushrocks/smartdata#README",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/mongodb": "^2.1.32",
|
"@pushrocks/lik": "^3.0.4",
|
||||||
"@types/q": "0.0.30",
|
"@pushrocks/smartlog": "^2.0.9",
|
||||||
"beautylog": "^5.0.23",
|
"@pushrocks/smartpromise": "^2.0.5",
|
||||||
"mongodb": "^2.2.9",
|
"@pushrocks/smartstring": "^3.0.5",
|
||||||
"q": "^1.4.1",
|
"@types/lodash": "^4.14.119",
|
||||||
"typings-global": "^1.0.14"
|
"@types/mongodb": "^3.1.18",
|
||||||
|
"lodash": "^4.17.11",
|
||||||
|
"mongodb": "^3.1.10",
|
||||||
|
"runtime-type-checks": "0.0.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/shelljs": "^0.3.30",
|
"@gitzone/tsbuild": "^2.1.4",
|
||||||
"@types/should": "^8.1.29",
|
"@gitzone/tstest": "^1.0.18",
|
||||||
"shelljs": "^0.7.4",
|
"@pushrocks/qenv": "^3.0.2",
|
||||||
"should": "^11.1.0",
|
"@pushrocks/tapbundle": "^3.0.7",
|
||||||
"smartstring": "^2.0.17",
|
"@types/node": "^10.12.18",
|
||||||
"typings-test": "^1.0.3"
|
"@types/shortid": "0.0.29",
|
||||||
|
"mongodb-memory-server": "^2.9.1",
|
||||||
|
"shortid": "^2.2.14",
|
||||||
|
"tslint": "^5.12.0",
|
||||||
|
"tslint-config-prettier": "^1.17.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1
test/test.d.ts
vendored
1
test/test.d.ts
vendored
@ -1 +0,0 @@
|
|||||||
import 'typings-test';
|
|
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=
|
|
138
test/test.ts
138
test/test.ts
@ -1,93 +1,65 @@
|
|||||||
import 'typings-test'
|
import { tap, expect } from '@pushrocks/tapbundle';
|
||||||
|
import * as smartpromise from '@pushrocks/smartpromise';
|
||||||
|
import { Qenv } from '@pushrocks/qenv';
|
||||||
|
|
||||||
import * as shelljs from 'shelljs'
|
let testQenv = new Qenv(process.cwd(), process.cwd() + '/.nogit/');
|
||||||
import * as should from 'should'
|
|
||||||
import * as smartstring from 'smartstring'
|
|
||||||
|
|
||||||
// the tested module
|
// the tested module
|
||||||
import * as smartdata from '../dist/index'
|
import * as smartdata from '../ts/index';
|
||||||
|
import { smartstring } from '../ts/smartdata.plugins';
|
||||||
|
import * as shortid from 'shortid';
|
||||||
|
|
||||||
let mongoChildProcess
|
// =======================================
|
||||||
let testDb: smartdata.Db
|
// Connecting to the database server
|
||||||
|
// =======================================
|
||||||
|
|
||||||
interface ITestObject1 {
|
let testDb = new smartdata.SmartdataDb({
|
||||||
value1: string
|
mongoDbName: process.env.MONGO_DBNAME,
|
||||||
value2?: number
|
mongoDbUrl: process.env.MONGO_URL,
|
||||||
value3?: string
|
mongoDbPass: process.env.MONGO_PASS
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('should establish a connection to the rethink Db cluster', async () => {
|
||||||
|
await testDb.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
// =======================================
|
||||||
|
// The actual tests
|
||||||
|
// =======================================
|
||||||
|
|
||||||
|
// ------
|
||||||
|
// Collections
|
||||||
|
// ------
|
||||||
|
|
||||||
|
@smartdata.Collection(testDb)
|
||||||
|
class Car extends smartdata.SmartDataDbDoc<Car> {
|
||||||
|
@smartdata.unI() index: string = shortid();
|
||||||
|
@smartdata.svDb() color: string;
|
||||||
|
@smartdata.svDb() brand: string;
|
||||||
|
constructor(colorArg: string, brandArg: string) {
|
||||||
|
super();
|
||||||
|
this.color = colorArg;
|
||||||
|
this.brand = brandArg;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let testDbCollection: smartdata.DbCollection<ITestObject1>
|
tap.test('should save the car to the db', async () => {
|
||||||
|
const myCar = new Car('red', 'Volvo');
|
||||||
|
await myCar.save();
|
||||||
|
});
|
||||||
|
|
||||||
class testCar {
|
tap.test('expect to get instance of Car', async () => {
|
||||||
color: string
|
let myCars = await Car.getInstances<Car>({
|
||||||
constructor(colorArg:string) {
|
brand: 'Volvo'
|
||||||
this.color = colorArg
|
});
|
||||||
}
|
expect(myCars[0].color).to.equal('red');
|
||||||
}
|
});
|
||||||
|
|
||||||
|
// =======================================
|
||||||
|
// close the database connection
|
||||||
|
// =======================================
|
||||||
|
tap.test('should close the database connection', async tools => {
|
||||||
|
await testDb.close();
|
||||||
|
});
|
||||||
|
|
||||||
describe('mongodb', function () {
|
tap.start({ throwOnError: true });
|
||||||
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<ITestObject1>('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')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
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.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';
|
5
ts/interfaces/mongodescriptor.ts
Normal file
5
ts/interfaces/mongodescriptor.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export interface IMongoDescriptor {
|
||||||
|
mongoDbName: string;
|
||||||
|
mongoDbUrl: string;
|
||||||
|
mongoDbPass: string;
|
||||||
|
}
|
151
ts/smartdata.classes.collection.ts
Normal file
151
ts/smartdata.classes.collection.ts
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
import * as plugins from './smartdata.plugins';
|
||||||
|
import { SmartdataDb } from './smartdata.classes.db';
|
||||||
|
import { SmartDataDbDoc } from './smartdata.classes.doc';
|
||||||
|
|
||||||
|
export interface IFindOptions {
|
||||||
|
limit?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export interface IDocValidationFunc<T> {
|
||||||
|
(doc: T): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TDelayedDbCreation = () => SmartdataDb;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a decorator that will tell the decorated class what dbTable to use
|
||||||
|
* @param dbArg
|
||||||
|
*/
|
||||||
|
export function Collection(dbArg: SmartdataDb | TDelayedDbCreation) {
|
||||||
|
return function(constructor) {
|
||||||
|
if (dbArg instanceof SmartdataDb) {
|
||||||
|
// tslint:disable-next-line: no-string-literal
|
||||||
|
constructor['smartdataCollection'] = new SmartdataCollection(constructor, dbArg);
|
||||||
|
} else {
|
||||||
|
constructor['smartdataDelayedCollection'] = () => {
|
||||||
|
return new SmartdataCollection(constructor, dbArg());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SmartdataCollection<T> {
|
||||||
|
/**
|
||||||
|
* the collection that is used
|
||||||
|
*/
|
||||||
|
mongoDbCollection: plugins.mongodb.Collection;
|
||||||
|
objectValidation: IDocValidationFunc<T> = null;
|
||||||
|
collectionName: string;
|
||||||
|
smartdataDb: SmartdataDb;
|
||||||
|
uniqueIndexes: string[] = [];
|
||||||
|
|
||||||
|
constructor(collectedClassArg: T & SmartDataDbDoc<T>, smartDataDbArg: SmartdataDb) {
|
||||||
|
// tell the collection where it belongs
|
||||||
|
this.collectionName = collectedClassArg.name;
|
||||||
|
this.smartdataDb = smartDataDbArg;
|
||||||
|
|
||||||
|
// tell the db class about it (important since Db uses different systems under the hood)
|
||||||
|
this.smartdataDb.addTable(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* makes sure a collection exists within MongoDb that maps to the SmartdataCollection
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
this.mongoDbCollection = await this.smartdataDb.mongoDb.collection(this.collectionName);
|
||||||
|
console.log(`Successfully initiated Collection ${this.collectionName}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mark unique index
|
||||||
|
*/
|
||||||
|
markUniqueIndexes(keyArrayArg: string[] = []) {
|
||||||
|
for (let 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
|
||||||
|
*/
|
||||||
|
addDocValidation(funcArg: IDocValidationFunc<T>) {
|
||||||
|
this.objectValidation = funcArg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* finds an object in the DbCollection
|
||||||
|
*/
|
||||||
|
async find(filterObject: any): Promise<any> {
|
||||||
|
await this.init();
|
||||||
|
const result = await this.mongoDbCollection.find(filterObject).toArray();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create an object in the database
|
||||||
|
*/
|
||||||
|
async insert(dbDocArg: T & SmartDataDbDoc<T>): 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>): Promise<any> {
|
||||||
|
await this.init();
|
||||||
|
await this.checkDoc(dbDocArg);
|
||||||
|
const identifiableObject = await dbDocArg.createIdentifiableObject();
|
||||||
|
const saveableObject = await dbDocArg.createSavableObject();
|
||||||
|
this.mongoDbCollection.updateOne(identifiableObject, saveableObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async delete (dbDocArg: T & SmartDataDbDoc<T>): Promise<any> {
|
||||||
|
await this.init();
|
||||||
|
await this.checkDoc(dbDocArg);
|
||||||
|
const identifiableObject = await dbDocArg.createIdentifiableObject();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -1,33 +1,96 @@
|
|||||||
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';
|
||||||
|
|
||||||
export class Db {
|
import * as mongoHelpers from './smartdata.mongohelpers';
|
||||||
dbUrl: string
|
|
||||||
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';
|
||||||
|
|
||||||
connect(): plugins.q.Promise<any> {
|
export interface ISmartdataOptions {
|
||||||
let done = plugins.q.defer()
|
/**
|
||||||
plugins.mongodb.MongoClient.connect(this.dbUrl, (err, db) => {
|
* the URL to connect to
|
||||||
if (err) { console.log(err) }
|
*/
|
||||||
plugins.assert.equal(null, err)
|
mongoDbUrl: string;
|
||||||
this.db = db
|
|
||||||
plugins.beautylog.success(`connected to database at ${this.dbUrl}`)
|
|
||||||
done.resolve(this.db)
|
|
||||||
})
|
|
||||||
return done.promise
|
|
||||||
}
|
|
||||||
|
|
||||||
close(): plugins.q.Promise<any> {
|
/**
|
||||||
let done = plugins.q.defer()
|
* the db to use for the project
|
||||||
this.db.close()
|
*/
|
||||||
plugins.beautylog.ok(`disconnected to database at ${this.dbUrl}`)
|
mongoDbName: string;
|
||||||
done.resolve()
|
|
||||||
return done.promise
|
/**
|
||||||
}
|
* an optional password that will be replace <PASSWORD> in the connection string
|
||||||
|
*/
|
||||||
|
mongoDbPass?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SmartdataDb {
|
||||||
|
smartdataOptions: ISmartdataOptions;
|
||||||
|
mongoDbClient: plugins.mongodb.MongoClient;
|
||||||
|
mongoDb: plugins.mongodb.Db;
|
||||||
|
status: TConnectionStatus;
|
||||||
|
smartdataCollectionMap = new Objectmap<SmartdataCollection<any>>();
|
||||||
|
|
||||||
|
constructor(smartdataOptions: ISmartdataOptions) {
|
||||||
|
this.smartdataOptions = smartdataOptions;
|
||||||
|
this.status = 'initial';
|
||||||
|
}
|
||||||
|
|
||||||
|
// basic connection stuff ----------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* connects to the database that was specified during instance creation
|
||||||
|
*/
|
||||||
|
async init(): Promise<any> {
|
||||||
|
let finalConnectionUrl = this.smartdataOptions.mongoDbUrl;
|
||||||
|
if (this.smartdataOptions.mongoDbPass) {
|
||||||
|
finalConnectionUrl = mongoHelpers.addPassword(
|
||||||
|
this.smartdataOptions.mongoDbUrl,
|
||||||
|
this.smartdataOptions.mongoDbPass
|
||||||
|
);
|
||||||
|
}
|
||||||
|
console.log(finalConnectionUrl);
|
||||||
|
this.mongoDbClient = await plugins.mongodb.MongoClient.connect(
|
||||||
|
finalConnectionUrl,
|
||||||
|
{
|
||||||
|
useNewUrlParser: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
async close(): Promise<any> {
|
||||||
|
await this.mongoDbClient.close();
|
||||||
|
this.status = 'disconnected';
|
||||||
|
plugins.smartlog.defaultLogger.log(
|
||||||
|
'info',
|
||||||
|
`disconnected from database ${this.smartdataOptions.mongoDbName}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle table to class distribution
|
||||||
|
|
||||||
|
addTable(SmartdataCollectionArg: SmartdataCollection<any>) {
|
||||||
|
this.smartdataCollectionMap.add(SmartdataCollectionArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a collection's name and returns a SmartdataCollection instance
|
||||||
|
* @param nameArg
|
||||||
|
* @returns DbTable
|
||||||
|
*/
|
||||||
|
async getSmartdataCollectionByName<T>(nameArg: string): Promise<SmartdataCollection<T>> {
|
||||||
|
let resultCollection = this.smartdataCollectionMap.find(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
|
|
||||||
}
|
|
||||||
}
|
|
188
ts/smartdata.classes.doc.ts
Normal file
188
ts/smartdata.classes.doc.ts
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
import * as plugins from './smartdata.plugins';
|
||||||
|
|
||||||
|
import { Objectmap } from '@pushrocks/lik';
|
||||||
|
|
||||||
|
import { SmartdataDb } from './smartdata.classes.db';
|
||||||
|
import { 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<any>, key: string) => {
|
||||||
|
console.log(`called svDb() on ${key}`);
|
||||||
|
if (!target.saveableProperties) {
|
||||||
|
target.saveableProperties = [];
|
||||||
|
}
|
||||||
|
target.saveableProperties.push(key);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* unique index - decorator to mark a unique index
|
||||||
|
*/
|
||||||
|
export function unI() {
|
||||||
|
return (target: SmartDataDbDoc<any>, key: string) => {
|
||||||
|
console.log('called unI');
|
||||||
|
|
||||||
|
// 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 class SmartDataDbDoc<T> {
|
||||||
|
/**
|
||||||
|
* the collection object an Doc belongs to
|
||||||
|
*/
|
||||||
|
collection: SmartdataCollection<T>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* how the Doc in memory was created, may prove useful later.
|
||||||
|
*/
|
||||||
|
creationStatus: TDocCreation = 'new';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* unique indexes
|
||||||
|
*/
|
||||||
|
uniqueIndexes: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* an array of saveable properties of a doc
|
||||||
|
*/
|
||||||
|
saveableProperties: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* name
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* primary id in the database
|
||||||
|
*/
|
||||||
|
dbDocUniqueId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* class constructor
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
this.name = this.constructor['name'];
|
||||||
|
if(this.constructor['smartdataCollection']) {
|
||||||
|
// tslint:disable-next-line: no-string-literal
|
||||||
|
this.collection = this.constructor['smartdataCollection'];
|
||||||
|
// tslint:disable-next-line: no-string-literal
|
||||||
|
} else if (typeof this.constructor['smartdataDelayedCollection'] === 'function') {
|
||||||
|
// tslint:disable-next-line: no-string-literal
|
||||||
|
this.collection = this.constructor['smartdataDelayedCollection']();
|
||||||
|
} else {
|
||||||
|
console.error('Could not determine collection for DbDoc');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getInstances<T>(filterArg): Promise<T[]> {
|
||||||
|
let self: any = this; // fool typesystem
|
||||||
|
let referenceMongoDBCollection: SmartdataCollection<T>;
|
||||||
|
|
||||||
|
if (self.smartdataCollection) {
|
||||||
|
referenceMongoDBCollection = self.smartdataCollection;
|
||||||
|
} else if (self.smartdataDelayedCollection) {
|
||||||
|
referenceMongoDBCollection = self.smartdataDelayedCollection();
|
||||||
|
};
|
||||||
|
const foundDocs = await referenceMongoDBCollection.find(filterArg);
|
||||||
|
const returnArray = [];
|
||||||
|
for (let item of foundDocs) {
|
||||||
|
let newInstance = new this();
|
||||||
|
for (let key in item) {
|
||||||
|
if (key !== 'id') {
|
||||||
|
newInstance[key] = item[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
returnArray.push(newInstance);
|
||||||
|
}
|
||||||
|
return returnArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getInstance<T>(filterArg): Promise<T> {
|
||||||
|
let result = await this.getInstances<T>(filterArg);
|
||||||
|
if (result && result.length > 0) {
|
||||||
|
return result[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* saves this instance but not any connected items
|
||||||
|
* may lead to data inconsistencies, but is faster
|
||||||
|
*/
|
||||||
|
async save() {
|
||||||
|
// tslint:disable-next-line: no-this-assignment
|
||||||
|
let self: any = this;
|
||||||
|
switch (this.creationStatus) {
|
||||||
|
case 'db':
|
||||||
|
await this.collection.update(self);
|
||||||
|
break;
|
||||||
|
case 'new':
|
||||||
|
let writeResult = await this.collection.insert(self);
|
||||||
|
this.creationStatus = 'db';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.error('neither new nor in db?');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* deletes a document from the database
|
||||||
|
*/
|
||||||
|
async delete() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* also store any referenced objects to DB
|
||||||
|
* better for data consistency
|
||||||
|
*/
|
||||||
|
saveDeep(savedMapArg: Objectmap<SmartDataDbDoc<any>> = null) {
|
||||||
|
if (!savedMapArg) {
|
||||||
|
savedMapArg = new Objectmap<SmartDataDbDoc<any>>();
|
||||||
|
}
|
||||||
|
savedMapArg.add(this);
|
||||||
|
this.save();
|
||||||
|
for (let propertyKey in this) {
|
||||||
|
let 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() {
|
||||||
|
const saveableObject: any = {}; // is not exposed to outside, so any is ok here
|
||||||
|
for (const propertyNameString of this.saveableProperties) {
|
||||||
|
saveableObject[propertyNameString] = this[propertyNameString];
|
||||||
|
}
|
||||||
|
return saveableObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
3
ts/smartdata.mongohelpers.ts
Normal file
3
ts/smartdata.mongohelpers.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export const addPassword = (mongoUrlArg: string, passwordArg: string): string => {
|
||||||
|
return mongoUrlArg.replace('<PASSWORD>', passwordArg);
|
||||||
|
};
|
@ -1,5 +1,8 @@
|
|||||||
import 'typings-global'
|
import * as assert from 'assert';
|
||||||
export import assert = require('assert')
|
import * as smartlog from '@pushrocks/smartlog';
|
||||||
export import beautylog = require('beautylog')
|
import * as lodash from 'lodash';
|
||||||
export import mongodb = require('mongodb')
|
import * as mongodb from 'mongodb';
|
||||||
export import q = require('q')
|
import * as smartq from '@pushrocks/smartpromise';
|
||||||
|
import * as smartstring from '@pushrocks/smartstring';
|
||||||
|
|
||||||
|
export { assert, smartlog, lodash, smartq, mongodb, smartstring };
|
||||||
|
7
tsconfig.json
Normal file
7
tsconfig.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"target": "es2017",
|
||||||
|
"module": "commonjs"
|
||||||
|
}
|
||||||
|
}
|
18
tslint.json
18
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