@typeheim/orm-on-fire
Advanced tools
Comparing version 0.0.0-beta-4 to 0.0.1
export * from './src/Contracts'; | ||
export * from './src/Data'; | ||
export * from './src/Data/EntityPromise'; | ||
export * from './src/Data/EntityStream'; | ||
export * from './src/Decorators'; | ||
export * from './src/Exceptions'; | ||
export * from './src/Model'; | ||
export * from './src/Persistence/FirestoreConnection'; | ||
export * from './src/operators'; | ||
export * from './src/singletons'; | ||
export { NullCollection } from './src/Model/NullCollection'; | ||
export { TextIndex } from './src/Functions/TextIndexTrigger'; | ||
export { OrmOnFire, } from './src/singletons'; |
13
index.js
@@ -10,12 +10,21 @@ "use strict"; | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.OrmOnFire = exports.TextIndex = exports.NullCollection = void 0; | ||
__exportStar(require("./src/Contracts"), exports); | ||
__exportStar(require("./src/Data"), exports); | ||
__exportStar(require("./src/Data/EntityPromise"), exports); | ||
__exportStar(require("./src/Data/EntityStream"), exports); | ||
__exportStar(require("./src/Decorators"), exports); | ||
__exportStar(require("./src/Exceptions"), exports); | ||
__exportStar(require("./src/Model"), exports); | ||
__exportStar(require("./src/Persistence/FirestoreConnection"), exports); | ||
__exportStar(require("./src/operators"), exports); | ||
__exportStar(require("./src/singletons"), exports); | ||
var NullCollection_1 = require("./src/Model/NullCollection"); | ||
Object.defineProperty(exports, "NullCollection", { enumerable: true, get: function () { return NullCollection_1.NullCollection; } }); | ||
var TextIndexTrigger_1 = require("./src/Functions/TextIndexTrigger"); | ||
Object.defineProperty(exports, "TextIndex", { enumerable: true, get: function () { return TextIndexTrigger_1.TextIndex; } }); | ||
var singletons_1 = require("./src/singletons"); | ||
Object.defineProperty(exports, "OrmOnFire", { enumerable: true, get: function () { return singletons_1.OrmOnFire; } }); | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "@typeheim/orm-on-fire", | ||
"version": "0.0.0-beta-4", | ||
"version": "0.0.1", | ||
"description": "Firestore ORM", | ||
@@ -39,7 +39,10 @@ "keywords": [ | ||
"dependencies": { | ||
"@typeheim/fire-rx": "^0.0.0-beta-4", | ||
"reflect-metadata": "^0.1.13", | ||
"rxjs": "^6.5.5" | ||
"@typeheim/fire-rx": "^0.1.0", | ||
"reflect-metadata": "^0.1.13" | ||
}, | ||
"gitHead": "088342861eeab00c824ce8b3d9834f95004070f6" | ||
"devDependencies": { | ||
"firebase": "^8.2.1", | ||
"rxjs": "^7.2.0" | ||
}, | ||
"gitHead": "5d825d03f1016b245b76e10d757b80d0a9a223ce" | ||
} |
186
README.md
@@ -1,37 +0,103 @@ | ||
# ORM On Fire | ||
Firestore ORM | ||
<p align="center"> | ||
<img style="max-width: 100%" width="1200" src="https://raw.githubusercontent.com/typeheim/fire-legion/46726290060f4631bb0fb10017bdf7954f7e21d9/packages/orm-on-fire/docs/orm-on-fire-logo.svg"> | ||
</p> | ||
<p> | ||
<a href="https://www.npmjs.com/package/@typeheim/orm-on-fire" target="_blank"><img src="https://img.shields.io/npm/v/@typeheim/orm-on-fire.svg" alt="NPM Version" /></a> | ||
<a href="https://app.buddy.works/typeheim/fire-legion/pipelines/pipeline/300564" target="_blank"><img src="https://app.buddy.works/typeheim/fire-legion/pipelines/pipeline/300564/badge.svg?token=aad32357cefae9d70b31d8b440fdf3f3d5d2a244a0412ff42ac294abbfc508f5" alt="Build Status" /></a> | ||
<a href="https://www.npmjs.com/package/@typeheim/orm-on-fire" target="_blank"><img src="https://img.shields.io/npm/l/@typeheim/orm-on-fire.svg" alt="Package License" /></a> | ||
<a href="https://discord.gg/dmMznp9" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a> | ||
</p> | ||
Sample: | ||
ORMOnFire is a powerful Firestore ORM. | ||
## Installation | ||
Install package | ||
```shell | ||
# in backend | ||
yarn add firebase firebase-admin firebase-tools | ||
# in frontend | ||
yarn add firebase | ||
yarn add @typeheim/orm-on-fire | ||
# or | ||
npm -i @typeheim/orm-on-fire | ||
``` | ||
Setup ORMOnFire driver: | ||
```typescript | ||
import { Entity, Collection, CollectionRef, ID, Field, Repo } from '@typeheim/orm-on-fire' | ||
// sample for Node.JS | ||
FirebaseAdmin.initializeApp({ | ||
credential: FirebaseAdmin.credential.cert('my.key.json'), | ||
databaseURL: "https://my-db.firebaseio.com", | ||
}) | ||
OrmOnFire.driver = FirebaseAdmin.firestore() | ||
``` | ||
## Easy entity declaration | ||
@Entity() | ||
To define entity you need to use `@Entity` or `@Agregate` decorators for simple and nested collections. Note both | ||
decorators will transform class name to kebab case - lowercase and split words with hyphens, like `UserFiles` < | ||
=> `user-files`. | ||
Then, each document field must be decorated with `@Field`, `@MapField`, `@CreatedDateField`, `@UpdatedDateField` | ||
or `@DocRef`(for document references) decorators. Sub-collections can be referenced by `@CollectionRef` decorator. | ||
```typescript | ||
import { | ||
Agregate, | ||
Entity, | ||
Collection, | ||
CollectionRef, | ||
ID, | ||
Field, | ||
CreatedDateField, | ||
UpdatedDateField, | ||
MapField | ||
} from '@typeheim/orm-on-fire' | ||
import { CreatedDateField } from './Entity' | ||
@Agregate() | ||
export class User { | ||
@ID() | ||
id : string | ||
@ID() id: string | ||
@Field() | ||
firstName: string | ||
@Field() firstName: string | ||
@Field() | ||
lastName: string | ||
@Field() lastName: string | ||
@Collection() | ||
files: Collection<UserFile> | ||
@Field() status: string | ||
@CollectionRef(UserFile) files: Collection<UserFile> | ||
} | ||
@Entity({collection: 'user-files'}) | ||
@Entity({ collection: 'user-files' }) | ||
export class UserFile { | ||
@ID() | ||
id : string | ||
@ID() id: string | ||
@Field() | ||
name: string | ||
@Field() name: string | ||
@MapField() properties: FileProperties | ||
@CreatedDateField() createdAt: Date | ||
@UpdatedDateField() createdAt: Date | ||
} | ||
class FileProperties { | ||
type: "image" | "doc" | ||
} | ||
``` | ||
## Simple data fetching | ||
```typescript | ||
import { Collection } from '@typeheim/orm-on-fire' | ||
// with promise-like interface | ||
let markus = await Repo.of(User).one('markus').get() | ||
let markus = await Collection.of(User).one('markus').get() | ||
// with Rx interface | ||
Repo.of(User).one('tom').get().subscribe((tom: User) => { | ||
Collection.of(User).one('tom').get().subscribe((tom: User) => { | ||
tom.files.forEach((file: UserFile) => { | ||
@@ -41,3 +107,83 @@ // some cool stuff | ||
}) | ||
``` | ||
## Powerful filtering | ||
### Using firestore operators | ||
```typescript | ||
import { Collection } from '@typeheim/orm-on-fire' | ||
const UsersCollection = Collection.of(User) | ||
// Search using regular Firesotre operators | ||
let activeUsers = await UsersCollection.all().filter(user => user.status.equal('active')).get() | ||
let notActiveUsers = await UsersCollection.all().filter(user => user.status.notEqual('active')).get() | ||
let adultUsers = await UsersCollection.all().filter(user => user.age.greaterThan(18)).get() | ||
``` | ||
### Test index search | ||
To use text index search you first need to add text index hook Firebase function to your Firebase functions list for | ||
each colelction you want to be idndexed | ||
```typescript | ||
import { TextIndex } from '@typeheim/orm-on-fire' | ||
import * as functions from 'firebase-functions' | ||
import * as FirebaseAdmin from 'firebase-admin' | ||
FirebaseAdmin.initializeApp() | ||
export const generateUserIndex = TextIndex(functions, FirebaseAdmin).forCollection('users') | ||
.fields(['name', 'text']) | ||
.buildTrigger() | ||
``` | ||
Official functions deployment | ||
guide: [Get started: write, test, and deploy your first functions](https://firebase.google.com/docs/functions/get-started) | ||
Once you deploy hooks, you can use index search as below: | ||
```typescript | ||
// Note: text index search is case-insensitive | ||
let usersStartsWithAlex = await UsersCollection.all().useIndex(user => user.firstName.startsWith('Alex')).get() | ||
let usersEndsWithLex = await UsersCollection.all().useIndex(user => user.firstName.endsWith('lex')).get() | ||
``` | ||
NOTE: for now text index won't work with collection group queries. Support coming in next releases. | ||
## Filter scopes: | ||
Commonly used filer conditions can be organized in named filter scopes for easy code reuse: | ||
```typescript | ||
class UserScope { | ||
static active() { | ||
return (user: EntityFilter<User>) => { | ||
user.status.equal(1) | ||
} | ||
} | ||
} | ||
// fetch all active users | ||
let activeUsers = await UsersCollection.all().filter(UserScope.active()).get() | ||
``` | ||
## Sub-collection queries: | ||
For nested collections you don't need to fetch each document separately and can access required collection under | ||
specific document ID: | ||
```typescript | ||
// fetch all PDF files from user suwth id "userId" | ||
let userFiles = await UsersCollection.one('userId').collecction(UserFile).filter(UserFile.pdf()).get() | ||
``` | ||
## Group collection queries: | ||
ORMOnFire support easy declaration | ||
of [collection groups](https://firebase.googleblog.com/2019/06/understanding-collection-group-queries.html) the same way | ||
as for regular collections. | ||
```typescript | ||
// fetch all file attachments that exist in any collection and sub-collection | ||
let attechments = await Collection.groupOf(Attachment).all().filter(Attachment.file()).get() | ||
``` |
@@ -12,2 +12,8 @@ export interface EntityMetadata { | ||
name: string; | ||
isText?: boolean; | ||
isDate?: boolean; | ||
isMap?: boolean; | ||
constructor?: any; | ||
updateOnSave?: boolean; | ||
generateOnCreate?: boolean; | ||
} | ||
@@ -14,0 +20,0 @@ export interface CollectionRefMetadata { |
@@ -5,1 +5,2 @@ export * from './EntityMetadata'; | ||
export * from './Types'; | ||
export * from './Query'; |
@@ -10,3 +10,3 @@ "use strict"; | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
@@ -18,2 +18,3 @@ Object.defineProperty(exports, "__esModule", { value: true }); | ||
__exportStar(require("./Types"), exports); | ||
__exportStar(require("./Query"), exports); | ||
//# sourceMappingURL=index.js.map |
@@ -1,4 +0,5 @@ | ||
import { GenericRepository } from '../Model/GenericRepository'; | ||
import { WriteResult } from '@google-cloud/firestore'; | ||
import { FireReplaySubject } from '@typeheim/fire-rx'; | ||
import { EntityPersister } from '../Model/EntityPersister'; | ||
import { ReactivePromise } from '@typeheim/fire-rx'; | ||
import { DocReference } from '../Persistence/DocReference'; | ||
import { MutationTracker } from '../Persistence/EntityManager'; | ||
export interface Model { | ||
@@ -11,6 +12,9 @@ id?: string; | ||
interface OrmMetadata { | ||
repository?: GenericRepository<any>; | ||
save?(): FireReplaySubject<WriteResult>; | ||
remove?(): FireReplaySubject<WriteResult>; | ||
isNew: boolean; | ||
mutation?: MutationTracker; | ||
repository?: EntityPersister<any>; | ||
docRef?: DocReference; | ||
save?(): ReactivePromise<void>; | ||
remove?(): ReactivePromise<void>; | ||
} | ||
export {}; |
@@ -0,1 +1,3 @@ | ||
import { FireFilter } from './Query'; | ||
import { IndexFilter } from '../Persistence/IndexFilter'; | ||
export declare enum ChangeType { | ||
@@ -6,1 +8,9 @@ Added = "added", | ||
} | ||
export declare type EntityFilter<Entity> = { | ||
[Key in keyof Entity]?: FireFilter<Entity>; | ||
}; | ||
export declare type EntityIndex<Entity> = { | ||
[Key in keyof Entity]?: IndexFilter<Entity>; | ||
}; | ||
export declare type FilterFunction<Entity> = (filter: EntityFilter<Entity>) => void; | ||
export declare type IndexFunction<Entity> = (filter: EntityIndex<Entity>) => void; |
@@ -10,3 +10,3 @@ "use strict"; | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
@@ -13,0 +13,0 @@ Object.defineProperty(exports, "__esModule", { value: true }); |
@@ -6,4 +6,11 @@ import 'reflect-metadata'; | ||
export declare function Field(metadata?: PropertyMetadata): PropertyDecorator; | ||
/** | ||
* @deprecated | ||
*/ | ||
export declare function SearchField(metadata?: PropertyMetadata): PropertyDecorator; | ||
export declare function CreatedDateField(metadata?: PropertyMetadata): PropertyDecorator; | ||
export declare function MapField(constructor?: any, metadata?: PropertyMetadata): PropertyDecorator; | ||
export declare function UpdatedDateField(metadata?: PropertyMetadata): PropertyDecorator; | ||
export declare function ID(): PropertyDecorator; | ||
export declare function DocRef(entity: any): PropertyDecorator; | ||
export declare function CollectionRef(entity: any): PropertyDecorator; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.CollectionRef = exports.DocRef = exports.ID = exports.Field = exports.Entity = exports.Aggregate = void 0; | ||
exports.CollectionRef = exports.DocRef = exports.ID = exports.UpdatedDateField = exports.MapField = exports.CreatedDateField = exports.SearchField = exports.Field = exports.Entity = exports.Aggregate = void 0; | ||
require("reflect-metadata"); | ||
const singletons_1 = require("../singletons"); | ||
const Reference_1 = require("../Model/Reference"); | ||
function Aggregate(metadata) { | ||
@@ -26,14 +27,87 @@ return Entity(metadata); | ||
return (target, propertyKey) => { | ||
if (!metadata) { | ||
addFieldMetadata(target, metadata, propertyKey); | ||
}; | ||
} | ||
exports.Field = Field; | ||
/** | ||
* @deprecated | ||
*/ | ||
function SearchField(metadata) { | ||
return (target, propertyKey) => { | ||
if (metadata) { | ||
metadata.isText = true; | ||
} | ||
else { | ||
metadata = { | ||
name: propertyKey.toString() | ||
name: '', | ||
isText: true, | ||
}; | ||
} | ||
else if (!metadata.name) { | ||
metadata.name = propertyKey.toString(); | ||
addFieldMetadata(target, metadata, propertyKey); | ||
}; | ||
} | ||
exports.SearchField = SearchField; | ||
function CreatedDateField(metadata) { | ||
return (target, propertyKey) => { | ||
if (metadata) { | ||
metadata.isDate = true; | ||
metadata.generateOnCreate = true; | ||
} | ||
singletons_1.Metadata.entity(target).addField(metadata); | ||
else { | ||
metadata = { | ||
name: '', | ||
isDate: true, | ||
generateOnCreate: true, | ||
}; | ||
} | ||
addFieldMetadata(target, metadata, propertyKey); | ||
}; | ||
} | ||
exports.Field = Field; | ||
exports.CreatedDateField = CreatedDateField; | ||
function MapField(constructor, metadata) { | ||
return (target, propertyKey) => { | ||
let reflectionType = Reflect.getMetadata("design:type", target, propertyKey); | ||
if (metadata) { | ||
metadata.isMap = true; | ||
metadata.constructor = constructor !== null && constructor !== void 0 ? constructor : reflectionType; | ||
} | ||
else { | ||
metadata = { | ||
name: '', | ||
isMap: true, | ||
constructor: constructor !== null && constructor !== void 0 ? constructor : reflectionType | ||
}; | ||
} | ||
addFieldMetadata(target, metadata, propertyKey); | ||
}; | ||
} | ||
exports.MapField = MapField; | ||
function UpdatedDateField(metadata) { | ||
return (target, propertyKey) => { | ||
if (metadata) { | ||
metadata.isDate = true; | ||
metadata.updateOnSave = true; | ||
} | ||
else { | ||
metadata = { | ||
name: '', | ||
isDate: true, | ||
updateOnSave: true, | ||
}; | ||
} | ||
addFieldMetadata(target, metadata, propertyKey); | ||
}; | ||
} | ||
exports.UpdatedDateField = UpdatedDateField; | ||
function addFieldMetadata(target, metadata, propertyKey) { | ||
if (!metadata) { | ||
metadata = { | ||
name: propertyKey.toString(), | ||
}; | ||
} | ||
else if (!metadata.name || metadata.name.length == 0) { | ||
metadata.name = propertyKey.toString(); | ||
} | ||
singletons_1.Metadata.entity(target.constructor).addField(metadata); | ||
} | ||
function ID() { | ||
@@ -47,6 +121,12 @@ return (target, propertyKey) => { | ||
return (target, propertyKey) => { | ||
singletons_1.Metadata.entity(target).addDocRef({ | ||
singletons_1.Metadata.entity(target.constructor).addDocRef({ | ||
fieldName: propertyKey.toString(), | ||
entity: entity | ||
entity: entity, | ||
}); | ||
Object.defineProperty(target, propertyKey, { | ||
value: new Reference_1.Reference(entity, target), | ||
writable: true, | ||
enumerable: true, | ||
configurable: true, | ||
}); | ||
}; | ||
@@ -57,6 +137,12 @@ } | ||
return (target, propertyKey) => { | ||
singletons_1.Metadata.entity(target).addCollectionRef({ | ||
singletons_1.Metadata.entity(target.constructor).addCollectionRef({ | ||
fieldName: propertyKey.toString(), | ||
entity: entity | ||
entity: entity, | ||
}); | ||
Object.defineProperty(target, propertyKey, { | ||
value: singletons_1.CollectionFactory.createNullCollection(), | ||
writable: true, | ||
enumerable: true, | ||
configurable: true, | ||
}); | ||
}; | ||
@@ -63,0 +149,0 @@ } |
@@ -10,3 +10,3 @@ "use strict"; | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
@@ -13,0 +13,0 @@ Object.defineProperty(exports, "__esModule", { value: true }); |
export * from './InvalidOptionsException'; | ||
export * from './EntityNotSavedException'; |
@@ -10,6 +10,7 @@ "use strict"; | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
__exportStar(require("./InvalidOptionsException"), exports); | ||
__exportStar(require("./EntityNotSavedException"), exports); | ||
//# sourceMappingURL=index.js.map |
import { CollectionRefMetadata, DocRefMetadata, EntityMetadata, PropertyMetadata } from '../Contracts/EntityMetadata'; | ||
export declare class MetadataStorage { | ||
protected metadataMap: MetadataMap; | ||
protected metadataMap: Map<any, EntityMetadata>; | ||
entity(target: any): { | ||
@@ -20,5 +20,1 @@ add: (metadata: EntityMetadata) => void; | ||
} | ||
interface MetadataMap { | ||
[key: string]: EntityMetadata; | ||
} | ||
export {}; |
@@ -6,3 +6,3 @@ "use strict"; | ||
constructor() { | ||
this.metadataMap = {}; | ||
this.metadataMap = new Map(); | ||
} | ||
@@ -12,23 +12,23 @@ entity(target) { | ||
add: (metadata) => { | ||
this.add(target.prototype.constructor.name, metadata); | ||
this.add(target, metadata); | ||
}, | ||
addField: (fieldMetadata) => { | ||
this.addField(target.constructor.name, fieldMetadata); | ||
this.addField(target, fieldMetadata); | ||
}, | ||
addCollectionRef: (subCollectionMetadata) => { | ||
this.addCollectionRef(target.constructor.name, subCollectionMetadata); | ||
this.addCollectionRef(target, subCollectionMetadata); | ||
}, | ||
addDocRef: (refMetadata) => { | ||
this.addDocRef(target.constructor.name, refMetadata); | ||
this.addDocRef(target, refMetadata); | ||
}, | ||
setRepository: (repository) => { | ||
this.setRepository(target.constructor.name, repository); | ||
this.setRepository(target, repository); | ||
}, | ||
get: () => { | ||
return this.get(target.prototype.constructor.name); | ||
} | ||
return this.get(target); | ||
}, | ||
}; | ||
} | ||
get(target) { | ||
return this.metadataMap[target]; | ||
return this.metadataMap.get(target); | ||
} | ||
@@ -38,6 +38,6 @@ add(target, metadata) { | ||
if (metadata.collection) { | ||
this.metadataMap[target].collection = metadata.collection; | ||
this.metadataMap.get(target).collection = metadata.collection; | ||
} | ||
if (metadata.fields) { | ||
this.metadataMap[target].fields = metadata.fields; | ||
this.metadataMap.get(target).fields = metadata.fields; | ||
} | ||
@@ -47,19 +47,19 @@ } | ||
this.ensureMetadataExistForTarget(target); | ||
this.metadataMap[target].fields.push(metadata); | ||
this.metadataMap.get(target).fields.push(metadata); | ||
} | ||
addCollectionRef(target, metadata) { | ||
this.ensureMetadataExistForTarget(target); | ||
this.metadataMap[target].collectionRefs.push(metadata); | ||
this.metadataMap.get(target).collectionRefs.push(metadata); | ||
} | ||
addDocRef(target, metadata) { | ||
this.ensureMetadataExistForTarget(target); | ||
this.metadataMap[target].docRefs[metadata.fieldName] = metadata; | ||
this.metadataMap.get(target).docRefs[metadata.fieldName] = metadata; | ||
} | ||
setRepository(target, repository) { | ||
this.ensureMetadataExistForTarget(target); | ||
this.metadataMap[target].repository = repository; | ||
this.metadataMap.get(target).repository = repository; | ||
} | ||
ensureMetadataExistForTarget(target) { | ||
if (!this.metadataMap.hasOwnProperty(target)) { | ||
this.metadataMap[target] = { | ||
if (!this.metadataMap.has(target)) { | ||
this.metadataMap.set(target, { | ||
collection: '', | ||
@@ -69,4 +69,4 @@ repository: null, | ||
collectionRefs: [], | ||
docRefs: {} | ||
}; | ||
docRefs: {}, | ||
}); | ||
} | ||
@@ -73,0 +73,0 @@ } |
@@ -1,12 +0,24 @@ | ||
import { GenericRepository } from './GenericRepository'; | ||
import { FireReplaySubject } from '@typeheim/fire-rx'; | ||
import { EntityPersister } from './EntityPersister'; | ||
import { AsyncStream, ReactivePromise, StatefulSubject } from '@typeheim/fire-rx'; | ||
import { ChangedEntities } from '../Data/ChangedEntities'; | ||
import { EntityQuery } from '../Persistence/EntityQuery'; | ||
import { EntityType, FilterFunction } from '../Contracts'; | ||
import { CollectionQuery } from '../Persistence/CollectionQuery'; | ||
import { QueryFactory } from '../Persistence/QueryFactory'; | ||
import { EntityStream } from '../Data/EntityStream'; | ||
export declare class Collection<Entity> { | ||
protected repository: GenericRepository<Entity>; | ||
constructor(repository: GenericRepository<Entity>); | ||
get(): FireReplaySubject<Entity[]>; | ||
changesStream(): FireReplaySubject<ChangedEntities<Entity>>; | ||
dataStream(): FireReplaySubject<Entity[]>; | ||
forEach(callback: ((value: Entity) => void)): FireReplaySubject<Entity[]>; | ||
delete(): FireReplaySubject<unknown>; | ||
protected queryFactory: QueryFactory<Entity>; | ||
protected persister: EntityPersister<Entity>; | ||
constructor(queryFactory: QueryFactory<Entity>, persister: EntityPersister<Entity>); | ||
static of<Entity>(entity: EntityType<Entity>): Collection<Entity>; | ||
static groupOf<Entity>(entity: EntityType<Entity>): Collection<Entity>; | ||
all(): CollectionQuery<Entity>; | ||
one(id: string): EntityQuery<Entity>; | ||
new(id?: string): ReactivePromise<Entity>; | ||
save(entity: Entity): ReactivePromise<void>; | ||
remove(entity: Entity): ReactivePromise<void>; | ||
filter(filterFunction: FilterFunction<Entity>): CollectionQuery<Entity>; | ||
changes(): EntityStream<ChangedEntities<Entity>>; | ||
forEach(callback: ((value: Entity) => void)): AsyncStream<any>; | ||
clean(): StatefulSubject<unknown>; | ||
} |
@@ -5,29 +5,48 @@ "use strict"; | ||
const fire_rx_1 = require("@typeheim/fire-rx"); | ||
const singletons_1 = require("../singletons"); | ||
const operators_1 = require("rxjs/operators"); | ||
class Collection { | ||
constructor(repository) { | ||
this.repository = repository; | ||
constructor(queryFactory, persister) { | ||
this.queryFactory = queryFactory; | ||
this.persister = persister; | ||
} | ||
get() { | ||
return this.repository.all().get(); | ||
static of(entity) { | ||
return singletons_1.InternalCollectionsMap.of(entity); | ||
} | ||
changesStream() { | ||
return this.repository.all().changesStream(); | ||
static groupOf(entity) { | ||
return singletons_1.InternalCollectionsMap.groupOf(entity); | ||
} | ||
dataStream() { | ||
return this.repository.all().dataStream(); | ||
all() { | ||
return this.queryFactory.createCollectionQuery(); | ||
} | ||
one(id) { | ||
return this.queryFactory.createEntityQuery(id); | ||
} | ||
new(id) { | ||
return this.persister.new(id); | ||
} | ||
save(entity) { | ||
return this.persister.save(entity); | ||
} | ||
remove(entity) { | ||
return this.persister.remove(entity); | ||
} | ||
filter(filterFunction) { | ||
return this.queryFactory.createCollectionQuery().filter(filterFunction); | ||
} | ||
changes() { | ||
return this.queryFactory.createCollectionQuery().changes(); | ||
} | ||
forEach(callback) { | ||
let subject = this.get(); | ||
subject.subscribe(entities => { | ||
return new fire_rx_1.AsyncStream(this.all().get().pipe(operators_1.map(entities => { | ||
entities.forEach(entity => { | ||
callback(entity); | ||
}); | ||
}); | ||
return subject; | ||
}))); | ||
} | ||
delete() { | ||
let subject = new fire_rx_1.FireReplaySubject(1); | ||
this.get().subscribe((entities) => { | ||
clean() { | ||
let subject = new fire_rx_1.StatefulSubject(1); | ||
this.all().get().subscribe((entities) => { | ||
entities.forEach(entity => { | ||
this.repository.remove(entity); | ||
this.persister.remove(entity); | ||
}); | ||
@@ -34,0 +53,0 @@ subject.next(true); |
export * from './Collection'; | ||
export * from './Reference'; | ||
export * from './GenericRepository'; | ||
export * from './EntityPersister'; |
@@ -10,3 +10,3 @@ "use strict"; | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
@@ -16,3 +16,3 @@ Object.defineProperty(exports, "__esModule", { value: true }); | ||
__exportStar(require("./Reference"), exports); | ||
__exportStar(require("./GenericRepository"), exports); | ||
__exportStar(require("./EntityPersister"), exports); | ||
//# sourceMappingURL=index.js.map |
import { EntityManager } from '../Persistence/EntityManager'; | ||
import { FireReplaySubject } from '@typeheim/fire-rx'; | ||
import { ReactivePromise } from '@typeheim/fire-rx'; | ||
import { DocReference } from '../Persistence/DocReference'; | ||
import { Model } from '../Contracts'; | ||
import { EntityPromise } from '../Data/EntityPromise'; | ||
import { EntityStream } from '../Data/EntityStream'; | ||
export declare class Reference<Entity> { | ||
protected entityConstructor: any; | ||
protected owner: Model; | ||
protected _entityBuilder: EntityManager<Entity>; | ||
protected docRef: DocReference; | ||
protected _entityBuilder: EntityManager<Entity>; | ||
constructor(entityConstructor: any, docRef: DocReference); | ||
get(): FireReplaySubject<Entity>; | ||
stream(): FireReplaySubject<Entity>; | ||
constructor(entityConstructor: any, owner: Model); | ||
link(reference: Entity | Model): ReactivePromise<void>; | ||
get(): EntityPromise<Entity>; | ||
stream(): EntityStream<Entity>; | ||
protected get entityBuilder(): EntityManager<Entity>; | ||
___attachDockRef(docRef: DocReference): void; | ||
get ___docReference(): DocReference; | ||
} |
@@ -7,7 +7,24 @@ "use strict"; | ||
const EntityQuery_1 = require("../Persistence/EntityQuery"); | ||
const fire_rx_1 = require("@typeheim/fire-rx"); | ||
const CollectionReference_1 = require("../Persistence/CollectionReference"); | ||
const operators_1 = require("../operators"); | ||
class Reference { | ||
constructor(entityConstructor, docRef) { | ||
constructor(entityConstructor, owner) { | ||
this.entityConstructor = entityConstructor; | ||
this.docRef = docRef; | ||
this.owner = owner; | ||
} | ||
link(reference) { | ||
var _a, _b, _c, _d; | ||
// @ts-ignore | ||
this.docRef = (_a = reference === null || reference === void 0 ? void 0 : reference.__ormOnFire) === null || _a === void 0 ? void 0 : _a.docRef; | ||
let result; | ||
if (((_b = this.owner) === null || _b === void 0 ? void 0 : _b.__ormOnFire) === undefined || ((_d = (_c = this.owner) === null || _c === void 0 ? void 0 : _c.__ormOnFire) === null || _d === void 0 ? void 0 : _d.isNew)) { | ||
result = new fire_rx_1.ReactivePromise(); | ||
result.resolve(); | ||
} | ||
else { | ||
result = operators_1.save(this.owner); | ||
} | ||
return result; | ||
} | ||
get() { | ||
@@ -20,9 +37,19 @@ return new EntityQuery_1.EntityQuery(this.docRef, this.entityBuilder).get(); | ||
get entityBuilder() { | ||
var _a, _b; | ||
if (!this._entityBuilder) { | ||
this._entityBuilder = new EntityManager_1.EntityManager(singletons_1.Metadata.entity(this.entityConstructor).get(), singletons_1.Repo.of(this.entityConstructor), this.entityConstructor); | ||
// @todo move outside of Reference | ||
let metadata = singletons_1.Metadata.entity(this.entityConstructor).get(); | ||
let collectionPath = `${(_b = (_a = this.owner) === null || _a === void 0 ? void 0 : _a.__ormOnFire) === null || _b === void 0 ? void 0 : _b.docRef.nativeRef.path}/${metadata.collection}`; | ||
this._entityBuilder = new EntityManager_1.EntityManager(metadata, this.entityConstructor, new CollectionReference_1.CollectionReference(this.entityConstructor, collectionPath)); | ||
} | ||
return this._entityBuilder; | ||
} | ||
___attachDockRef(docRef) { | ||
this.docRef = docRef; | ||
} | ||
get ___docReference() { | ||
return this.docRef; | ||
} | ||
} | ||
exports.Reference = Reference; | ||
//# sourceMappingURL=Reference.js.map |
import { Model } from './Contracts/Model'; | ||
export declare function save(model: Model): import("../../fire-rx").FireReplaySubject<FirebaseFirestore.WriteResult>; | ||
export declare function remove(model: Model): import("../../fire-rx").FireReplaySubject<FirebaseFirestore.WriteResult>; | ||
export declare function save(model: Model | any): import("@typeheim/fire-rx").ReactivePromise<void>; | ||
export declare function remove(model: Model | any): import("@typeheim/fire-rx").ReactivePromise<void>; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.remove = exports.save = void 0; | ||
const singletons_1 = require("./singletons"); | ||
function save(model) { | ||
return model.__ormOnFire.repository.save(model); | ||
return singletons_1.InternalCollectionsMap.of(model.constructor).save(model); | ||
} | ||
exports.save = save; | ||
function remove(model) { | ||
return model.__ormOnFire.repository.remove(model); | ||
return singletons_1.InternalCollectionsMap.of(model.constructor).remove(model); | ||
} | ||
exports.remove = remove; | ||
//# sourceMappingURL=operators.js.map |
import { EntityManager } from './EntityManager'; | ||
import { FireReplaySubject } from '@typeheim/fire-rx'; | ||
import { ChangedEntities } from '../Data/ChangedEntities'; | ||
import { CollectionReference } from './CollectionReference'; | ||
export declare class CollectionQuery<Entity> { | ||
import { FilterFunction, IndexFunction, PropertyMetadata } from '../../index'; | ||
import { QueryState, SortOrder } from '../Contracts/Query'; | ||
import { EntityStream } from '../Data/EntityStream'; | ||
import { EntityPromise } from '../Data/EntityPromise'; | ||
export declare class CollectionQuery<Entity, FetchType = Entity[]> { | ||
protected collectionReference: CollectionReference; | ||
protected entityBuilder: EntityManager<Entity>; | ||
constructor(collectionReference: CollectionReference, entityBuilder: EntityManager<Entity>); | ||
get(): FireReplaySubject<Entity[]>; | ||
changesStream(): FireReplaySubject<ChangedEntities<Entity>>; | ||
dataStream(): FireReplaySubject<Entity[]>; | ||
protected entityManager: EntityManager<Entity>; | ||
protected filterFields: PropertyMetadata[]; | ||
protected queryState: QueryState; | ||
constructor(collectionReference: CollectionReference, entityManager: EntityManager<Entity>, filterFields: PropertyMetadata[]); | ||
exclude(id: string): any; | ||
exclude(ids: string[]): any; | ||
map<T = Entity, R = any>(operator: ((value: Entity[]) => R)): CollectionQuery<T, R>; | ||
debounceUpdates(dueTime: number): this; | ||
useIndex(indexFunction: IndexFunction<Entity>): this; | ||
asIds(): CollectionQuery<Entity, string[]>; | ||
filter(filterFunction: FilterFunction<Entity>): this; | ||
limit(limit: number): this; | ||
orderBy(field: string, sortOrder?: SortOrder): this; | ||
startAt(position: Entity | any): this; | ||
startAfter(position: Entity | any): this; | ||
endAt(position: Entity | any): this; | ||
endBefore(position: Entity | any): this; | ||
get(): EntityPromise<FetchType>; | ||
changes(): EntityStream<ChangedEntities<Entity>>; | ||
stream(): EntityStream<FetchType>; | ||
} |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.CollectionQuery = void 0; | ||
const fire_rx_1 = require("@typeheim/fire-rx"); | ||
const ChangedEntities_1 = require("../Data/ChangedEntities"); | ||
const FieldFilter_1 = require("./FieldFilter"); | ||
const Query_1 = require("../Contracts/Query"); | ||
const EntityStream_1 = require("../Data/EntityStream"); | ||
const operators_1 = require("rxjs/operators"); | ||
const EntityPromise_1 = require("../Data/EntityPromise"); | ||
const IndexFilter_1 = require("./IndexFilter"); | ||
class CollectionQuery { | ||
constructor(collectionReference, entityBuilder) { | ||
constructor(collectionReference, entityManager, filterFields) { | ||
this.collectionReference = collectionReference; | ||
this.entityBuilder = entityBuilder; | ||
this.entityManager = entityManager; | ||
this.filterFields = filterFields; | ||
this.queryState = { | ||
conditions: [], | ||
indexes: [], | ||
limit: -1, | ||
asIds: false, | ||
orderBy: [], | ||
exclude: [], | ||
}; | ||
} | ||
exclude(ids) { | ||
if (typeof ids == 'string') { | ||
this.queryState.exclude.push(ids); | ||
} | ||
else { | ||
this.queryState.exclude = ids; | ||
} | ||
return this; | ||
} | ||
map(operator) { | ||
this.queryState.map = operator; | ||
return this; | ||
} | ||
debounceUpdates(dueTime) { | ||
this.queryState.debounceUpdatesTime = dueTime; | ||
return this; | ||
} | ||
useIndex(indexFunction) { | ||
let filter = {}; | ||
this.filterFields.forEach(field => { | ||
filter[field.name] = new IndexFilter_1.IndexFilter(this.queryState, field.name); | ||
}); | ||
indexFunction(filter); | ||
return this; | ||
} | ||
asIds() { | ||
this.queryState.asIds = true; | ||
return this; | ||
} | ||
filter(filterFunction) { | ||
let filter = {}; | ||
filter['id'] = new FieldFilter_1.FieldFilter(this.queryState, 'id'); | ||
this.filterFields.forEach(field => { | ||
filter[field.name] = new FieldFilter_1.FieldFilter(this.queryState, field.name); | ||
}); | ||
filterFunction(filter); | ||
return this; | ||
} | ||
limit(limit) { | ||
this.queryState.limit = limit; | ||
return this; | ||
} | ||
orderBy(field, sortOrder = Query_1.SortOrder.Ascending) { | ||
this.queryState.orderBy.push({ | ||
field, | ||
sortOrder, | ||
}); | ||
return this; | ||
} | ||
startAt(position) { | ||
this.queryState.startAt = position; | ||
return this; | ||
} | ||
startAfter(position) { | ||
this.queryState.startAfter = position; | ||
return this; | ||
} | ||
endAt(position) { | ||
this.queryState.endAt = position; | ||
return this; | ||
} | ||
endBefore(position) { | ||
this.queryState.endBefore = position; | ||
return this; | ||
} | ||
get() { | ||
let subject = new fire_rx_1.FireReplaySubject(1); | ||
this.collectionReference.get().then((querySnapshot) => { | ||
let entitiesPromise = new EntityPromise_1.EntityPromise(); | ||
this.collectionReference.get(this.queryState).then((querySnapshot) => { | ||
var _a; | ||
let entities = []; | ||
querySnapshot.forEach((docSnapshot) => { | ||
entities.push(this.entityBuilder.fromSnapshot(docSnapshot)); | ||
}); | ||
subject.next(entities); | ||
subject.complete(); | ||
}); | ||
return subject; | ||
let docs; | ||
if (this.queryState.exclude) { | ||
docs = querySnapshot.docs.filter(doc => !this.queryState.exclude.includes(doc.id)); | ||
} | ||
else { | ||
docs = querySnapshot; | ||
} | ||
if ((_a = this.queryState) === null || _a === void 0 ? void 0 : _a.asIds) { | ||
docs.forEach((docSnapshot) => { | ||
entities.push(docSnapshot.id); | ||
}); | ||
} | ||
else { | ||
docs.forEach((docSnapshot) => { | ||
entities.push(this.entityManager.fromSnapshot(docSnapshot)); | ||
}); | ||
} | ||
if (this.queryState.map) { | ||
entities = this.queryState.map(entities); | ||
} | ||
entitiesPromise.resolve(entities); | ||
}).catch(error => entitiesPromise.reject(error)); | ||
return entitiesPromise; | ||
} | ||
changesStream() { | ||
let subject = new fire_rx_1.FireReplaySubject(1); | ||
this.collectionReference.snapshot().subscribe((querySnapshot) => { | ||
changes() { | ||
let snapshotStream = this.collectionReference.snapshot(this.queryState); | ||
let dataStream = this.queryState.debounceUpdatesTime ? snapshotStream.pipe(operators_1.debounceTime(this.queryState.debounceUpdatesTime)) : snapshotStream; | ||
let source = dataStream.pipe(operators_1.map((querySnapshot) => { | ||
var _a; | ||
let entityChanges = []; | ||
querySnapshot.docChanges().forEach((docSnapshot) => { | ||
let entity = this.entityBuilder.fromSnapshot(docSnapshot.doc); | ||
if (entity) { | ||
entityChanges.push({ | ||
type: docSnapshot.type, | ||
entity | ||
}); | ||
querySnapshot.docChanges().forEach((change) => { | ||
var _a; | ||
if (((_a = this.queryState) === null || _a === void 0 ? void 0 : _a.asIds) && change.doc.exists) { | ||
entityChanges.push(change.doc.id); | ||
} | ||
else { | ||
let entity = this.entityManager.fromSnapshot(change.doc); | ||
if (entity) { | ||
entityChanges.push({ | ||
type: change.type, | ||
entity, | ||
}); | ||
} | ||
} | ||
}); | ||
subject.next(new ChangedEntities_1.ChangedEntities(entityChanges)); | ||
}); | ||
return subject; | ||
if ((_a = this.queryState) === null || _a === void 0 ? void 0 : _a.map) { | ||
entityChanges = this.queryState.map(entityChanges); | ||
} | ||
return new ChangedEntities_1.ChangedEntities(entityChanges); | ||
})); | ||
return new EntityStream_1.EntityStream(source, snapshotStream); | ||
} | ||
dataStream() { | ||
let subject = new fire_rx_1.FireReplaySubject(1); | ||
this.collectionReference.snapshot().subscribe((querySnapshot) => { | ||
stream() { | ||
let snapshotStream = this.collectionReference.snapshot(this.queryState); | ||
let dataStream = this.queryState.debounceUpdatesTime ? snapshotStream.pipe(operators_1.debounceTime(this.queryState.debounceUpdatesTime)) : snapshotStream; | ||
let source = dataStream.pipe(operators_1.map((querySnapshot) => { | ||
let entities = []; | ||
querySnapshot.forEach((docSnapshot) => { | ||
let entity = this.entityBuilder.fromSnapshot(docSnapshot); | ||
if (entity) { | ||
entities.push(entity); | ||
var _a; | ||
if (((_a = this.queryState) === null || _a === void 0 ? void 0 : _a.asIds) && docSnapshot.exists) { | ||
entities.push(docSnapshot.id); | ||
} | ||
else { | ||
let entity = this.entityManager.fromSnapshot(docSnapshot); | ||
if (entity) { | ||
entities.push(entity); | ||
} | ||
} | ||
}); | ||
subject.next(entities); | ||
}); | ||
return subject; | ||
if (this.queryState.map) { | ||
entities = this.queryState.map(entities); | ||
} | ||
return entities; | ||
})); | ||
return new EntityStream_1.EntityStream(source, snapshotStream); | ||
} | ||
@@ -54,0 +171,0 @@ } |
import { FirestoreConnection } from './FirestoreConnection'; | ||
import { FireReplaySubject } from '@typeheim/fire-rx'; | ||
import { QuerySnapshot } from '@google-cloud/firestore'; | ||
import { ReactivePromise, StatefulSubject } from '@typeheim/fire-rx'; | ||
import { DocReference } from './DocReference'; | ||
import { QueryState } from '../Contracts/Query'; | ||
import * as types from '@firebase/firestore-types'; | ||
import QuerySnapshot = types.QuerySnapshot; | ||
import Query = types.Query; | ||
export declare enum CollectionRefType { | ||
Basic = 0, | ||
Group = 1 | ||
} | ||
export declare class CollectionReference { | ||
protected connection: FirestoreConnection; | ||
protected collectionPath: string; | ||
constructor(connection: FirestoreConnection, collectionPath: string); | ||
get(): FireReplaySubject<QuerySnapshot>; | ||
snapshot(): FireReplaySubject<QuerySnapshot>; | ||
protected type: CollectionRefType; | ||
constructor(connection: FirestoreConnection, collectionPath: string, type?: CollectionRefType); | ||
get(queryState?: QueryState): ReactivePromise<QuerySnapshot>; | ||
snapshot(queryState?: QueryState): StatefulSubject<QuerySnapshot>; | ||
protected fetchIndex(queryState: QueryState): any; | ||
protected buildQuery(queryState?: QueryState): Query; | ||
buildNativeCollection(): types.Query<types.DocumentData>; | ||
buildNativeIndexCollection(): types.CollectionReference<types.DocumentData>; | ||
doc(docPath?: string): DocReference; | ||
} |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.CollectionReference = void 0; | ||
exports.CollectionReference = exports.CollectionRefType = void 0; | ||
const fire_rx_1 = require("@typeheim/fire-rx"); | ||
const DocReference_1 = require("./DocReference"); | ||
const Query_1 = require("../Contracts/Query"); | ||
var CollectionRefType; | ||
(function (CollectionRefType) { | ||
CollectionRefType[CollectionRefType["Basic"] = 0] = "Basic"; | ||
CollectionRefType[CollectionRefType["Group"] = 1] = "Group"; | ||
})(CollectionRefType = exports.CollectionRefType || (exports.CollectionRefType = {})); | ||
class CollectionReference { | ||
constructor(connection, collectionPath) { | ||
constructor(connection, collectionPath, type = CollectionRefType.Basic) { | ||
this.connection = connection; | ||
this.collectionPath = collectionPath; | ||
this.type = type; | ||
} | ||
get() { | ||
let subject = new fire_rx_1.FireReplaySubject(); | ||
get(queryState) { | ||
let promise = new fire_rx_1.ReactivePromise(); | ||
this.connection.isInitialized.then((isInitialized) => { | ||
var _a; | ||
if (isInitialized) { | ||
this.connection.driver.collection(this.collectionPath).get().then((snapshot) => { | ||
subject.next(snapshot); | ||
subject.complete(); | ||
}); | ||
if (((_a = queryState === null || queryState === void 0 ? void 0 : queryState.indexes) === null || _a === void 0 ? void 0 : _a.length) > 0) { | ||
this.fetchIndex(queryState).then((snapshot) => { | ||
let ids = snapshot.docs.map(snapshot => snapshot.id); | ||
this.buildQuery(queryState).where('__name__', 'in', ids).get().then((snapshot) => { | ||
promise.resolve(snapshot); | ||
}).catch(error => promise.reject(error)); | ||
}).catch(error => promise.reject(error)); | ||
} | ||
else { | ||
this.buildQuery(queryState).get().then((snapshot) => { | ||
promise.resolve(snapshot); | ||
}).catch(error => promise.reject(error)); | ||
} | ||
} | ||
}); | ||
return subject; | ||
return promise; | ||
} | ||
snapshot() { | ||
let subject = new fire_rx_1.FireReplaySubject(); | ||
snapshot(queryState) { | ||
let subject = new fire_rx_1.StatefulSubject(); | ||
this.connection.isInitialized.then((isInitialized) => { | ||
var _a; | ||
if (isInitialized) { | ||
this.connection.driver.collection(this.collectionPath).onSnapshot(snapshot => { | ||
subject.next(snapshot); | ||
}); | ||
if (((_a = queryState === null || queryState === void 0 ? void 0 : queryState.indexes) === null || _a === void 0 ? void 0 : _a.length) > 0) { | ||
this.fetchIndex(queryState).get().then((snapshot) => { | ||
let ids = snapshot.docs.map(snapshot => snapshot.id); | ||
this.buildQuery(queryState).where('__name__', 'in', ids).onSnapshot(snapshot => subject.next(snapshot), error => subject.error(error), () => subject.complete()); | ||
}).catch(error => subject.error(error)); | ||
} | ||
else { | ||
this.buildQuery(queryState).onSnapshot(snapshot => subject.next(snapshot), error => subject.error(error), () => subject.complete()); | ||
} | ||
} | ||
@@ -34,2 +58,76 @@ }); | ||
} | ||
fetchIndex(queryState) { | ||
let collection = this.buildNativeIndexCollection(); | ||
let query = collection; | ||
queryState.indexes.forEach(index => { | ||
query = collection.where(index.fieldName, index.operator, index.compareValue); | ||
}); | ||
return query.get(); | ||
} | ||
buildQuery(queryState) { | ||
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x; | ||
let collection = this.buildNativeCollection(); | ||
let query = collection; | ||
if (queryState) { | ||
if (((_a = queryState.conditions) === null || _a === void 0 ? void 0 : _a.length) > 0) { | ||
queryState.conditions.forEach(condition => { | ||
query = query.where(condition.fieldName, condition.operator, condition.compareValue); | ||
}); | ||
} | ||
if (((_b = queryState.orderBy) === null || _b === void 0 ? void 0 : _b.length) > 0) { | ||
queryState.orderBy.forEach(orderCondition => { | ||
if (orderCondition.sortOrder === Query_1.SortOrder.Descending) { | ||
query = query.orderBy(orderCondition.field, 'desc'); | ||
} | ||
else { | ||
query = query.orderBy(orderCondition.field); | ||
} | ||
}); | ||
} | ||
if (queryState.limit > 0) { | ||
query = query.limit(queryState.limit); | ||
} | ||
if (queryState.startAt !== undefined) { | ||
if ((_d = (_c = queryState.startAt) === null || _c === void 0 ? void 0 : _c.__ormOnFire) === null || _d === void 0 ? void 0 : _d.docRef) { | ||
query = query.startAt((_g = (_f = (_e = queryState.startAt) === null || _e === void 0 ? void 0 : _e.__ormOnFire) === null || _f === void 0 ? void 0 : _f.docRef) === null || _g === void 0 ? void 0 : _g.nativeRef); | ||
} | ||
else { | ||
query = query.startAt(queryState.startAt); | ||
} | ||
} | ||
if (queryState.startAfter !== undefined) { | ||
if ((_j = (_h = queryState.startAfter) === null || _h === void 0 ? void 0 : _h.__ormOnFire) === null || _j === void 0 ? void 0 : _j.docRef) { | ||
query = query.startAfter((_m = (_l = (_k = queryState.startAfter) === null || _k === void 0 ? void 0 : _k.__ormOnFire) === null || _l === void 0 ? void 0 : _l.docRef) === null || _m === void 0 ? void 0 : _m.nativeRef); | ||
} | ||
else { | ||
query = query.startAfter(queryState.startAfter); | ||
} | ||
} | ||
if (queryState.endAt !== undefined) { | ||
if ((_p = (_o = queryState.endAt) === null || _o === void 0 ? void 0 : _o.__ormOnFire) === null || _p === void 0 ? void 0 : _p.docRef) { | ||
query = query.endAt((_s = (_r = (_q = queryState.endAt) === null || _q === void 0 ? void 0 : _q.__ormOnFire) === null || _r === void 0 ? void 0 : _r.docRef) === null || _s === void 0 ? void 0 : _s.nativeRef); | ||
} | ||
else { | ||
query = query.endAt(queryState.endAt); | ||
} | ||
} | ||
if (queryState.endBefore !== undefined) { | ||
if ((_u = (_t = queryState.endBefore) === null || _t === void 0 ? void 0 : _t.__ormOnFire) === null || _u === void 0 ? void 0 : _u.docRef) { | ||
query = query.endBefore((_x = (_w = (_v = queryState.endBefore) === null || _v === void 0 ? void 0 : _v.__ormOnFire) === null || _w === void 0 ? void 0 : _w.docRef) === null || _x === void 0 ? void 0 : _x.nativeRef); | ||
} | ||
else { | ||
query = query.endBefore(queryState.endBefore); | ||
} | ||
} | ||
} | ||
return query; | ||
} | ||
buildNativeCollection() { | ||
return this.type === CollectionRefType.Basic ? | ||
this.connection.driver.collection(this.collectionPath) : | ||
this.connection.driver.collectionGroup(this.collectionPath); | ||
} | ||
buildNativeIndexCollection() { | ||
return this.connection.driver.collection(`of-metadata/${this.collectionPath}/indexes`); | ||
} | ||
doc(docPath) { | ||
@@ -36,0 +134,0 @@ return new DocReference_1.DocReference(this.connection, docPath, this.collectionPath); |
@@ -1,4 +0,3 @@ | ||
import { WriteResult } from '@google-cloud/firestore'; | ||
import { EntityManager } from './EntityManager'; | ||
import { FireReplaySubject } from '@typeheim/fire-rx'; | ||
import { ReactivePromise } from '@typeheim/fire-rx'; | ||
import { CollectionReference } from './CollectionReference'; | ||
@@ -9,3 +8,3 @@ export declare class DocInitializer<Entity> { | ||
constructor(entity: Entity, entityManager: EntityManager<Entity>); | ||
addTo(collectionRef: CollectionReference): FireReplaySubject<WriteResult>; | ||
addTo(collectionRef: CollectionReference): ReactivePromise<boolean>; | ||
} |
@@ -13,11 +13,16 @@ "use strict"; | ||
let data = this.entityManager.extractDataFromEntity(this.entity); | ||
// type safety check to ensure dock path will be a string | ||
if (this.entity['id'] && typeof this.entity['id'] !== 'string') { | ||
this.entity['id'] = '' + this.entity['id']; | ||
} | ||
let documentReference = this.entity['id'] ? collectionRef.doc(this.entity['id']) : collectionRef.doc(); | ||
let subject = new fire_rx_1.FireReplaySubject(1); | ||
documentReference.set(data).then((result) => { | ||
let promise = new fire_rx_1.ReactivePromise(); | ||
documentReference.set(data).then(() => { | ||
this.entity['id'] = documentReference.nativeRef.id; | ||
this.entityManager.attachOrmMetadataToEntity(this.entity, documentReference.nativeRef); | ||
subject.next(result); | ||
subject.complete(); | ||
this.entityManager.refreshNewEntity(this.entity, documentReference.nativeRef); | ||
promise.resolve(true); | ||
}).catch(error => { | ||
promise.reject(error); | ||
}); | ||
return subject; | ||
return promise; | ||
} | ||
@@ -24,0 +29,0 @@ } |
@@ -1,8 +0,10 @@ | ||
import { DocumentReference, WriteResult } from '@google-cloud/firestore'; | ||
import { FireReplaySubject } from '@typeheim/fire-rx'; | ||
import { ReactivePromise } from '@typeheim/fire-rx'; | ||
import * as types from '@firebase/firestore-types'; | ||
import { MutationTracker } from './EntityManager'; | ||
import DocumentReference = types.DocumentReference; | ||
export declare class DocPersistenceManager<Entity> { | ||
protected docReference: DocumentReference; | ||
constructor(docReference: DocumentReference); | ||
update(dataToSave: any): FireReplaySubject<WriteResult>; | ||
remove(): FireReplaySubject<WriteResult>; | ||
update(dataToSave: any, mutationTracker: MutationTracker): ReactivePromise<boolean>; | ||
remove(): ReactivePromise<boolean>; | ||
} |
@@ -9,17 +9,24 @@ "use strict"; | ||
} | ||
update(dataToSave) { | ||
let subject = new fire_rx_1.FireReplaySubject(1); | ||
this.docReference.update(dataToSave).then((writeResult) => { | ||
subject.next(writeResult); | ||
subject.complete(); | ||
update(dataToSave, mutationTracker) { | ||
let promise = new fire_rx_1.ReactivePromise(); | ||
if (dataToSave && Object.keys(dataToSave).length === 0) { | ||
promise.resolve(true); | ||
return promise; | ||
} | ||
this.docReference.update(dataToSave).then(() => { | ||
mutationTracker.refreshEntity(); | ||
promise.resolve(true); | ||
}).catch(error => { | ||
promise.reject(error); | ||
}); | ||
return subject; | ||
return promise; | ||
} | ||
remove() { | ||
let subject = new fire_rx_1.FireReplaySubject(1); | ||
this.docReference.delete().then((writeResult) => { | ||
subject.next(writeResult); | ||
subject.complete(); | ||
let promise = new fire_rx_1.ReactivePromise(); | ||
this.docReference.delete().then(() => { | ||
promise.resolve(true); | ||
}).catch(error => { | ||
promise.reject(error); | ||
}); | ||
return subject; | ||
return promise; | ||
} | ||
@@ -26,0 +33,0 @@ } |
import { FirestoreConnection } from './FirestoreConnection'; | ||
import { FireReplaySubject } from '@typeheim/fire-rx'; | ||
import { DocumentReference, DocumentSnapshot, WriteResult } from '@google-cloud/firestore'; | ||
import { ReactivePromise, StatefulSubject } from '@typeheim/fire-rx'; | ||
import * as types from '@firebase/firestore-types'; | ||
import DocumentReference = types.DocumentReference; | ||
import DocumentSnapshot = types.DocumentSnapshot; | ||
export declare class DocReference { | ||
@@ -11,8 +13,9 @@ protected connection: FirestoreConnection; | ||
static fromNativeRef(docRef: DocumentReference): DocReference; | ||
get(): FireReplaySubject<DocumentSnapshot>; | ||
set(data: any): FireReplaySubject<WriteResult>; | ||
update(data: any): FireReplaySubject<WriteResult>; | ||
delete(): FireReplaySubject<WriteResult>; | ||
snapshot(): FireReplaySubject<DocumentSnapshot>; | ||
get nativeRef(): DocumentReference<FirebaseFirestore.DocumentData>; | ||
get(): StatefulSubject<DocumentSnapshot>; | ||
set(data: any): ReactivePromise<boolean>; | ||
update(data: any): ReactivePromise<boolean>; | ||
delete(): ReactivePromise<boolean>; | ||
snapshot(): StatefulSubject<DocumentSnapshot>; | ||
get nativeRef(): types.DocumentReference<types.DocumentData>; | ||
get path(): string; | ||
} |
@@ -18,3 +18,3 @@ "use strict"; | ||
get() { | ||
let subject = new fire_rx_1.FireReplaySubject(); | ||
let subject = new fire_rx_1.StatefulSubject(); | ||
this.connection.isInitialized.then((isInitialized) => { | ||
@@ -25,52 +25,52 @@ if (isInitialized) { | ||
subject.complete(); | ||
}); | ||
}).catch(error => subject.error(error)); | ||
} | ||
}); | ||
}).catch(error => subject.error(error)); | ||
return subject; | ||
} | ||
set(data) { | ||
let subject = new fire_rx_1.FireReplaySubject(); | ||
let promise = new fire_rx_1.ReactivePromise(); | ||
this.connection.isInitialized.then((isInitialized) => { | ||
if (isInitialized) { | ||
this.nativeRef.set(data).then((result) => { | ||
subject.next(result); | ||
subject.complete(); | ||
}); | ||
this.nativeRef.set(data) | ||
.then(() => promise.resolve(true)) | ||
.catch(() => promise.resolve(false)); | ||
} | ||
}); | ||
return subject; | ||
}).catch(error => promise.reject(error)); | ||
return promise; | ||
} | ||
update(data) { | ||
let subject = new fire_rx_1.FireReplaySubject(); | ||
let promise = new fire_rx_1.ReactivePromise(); | ||
this.connection.isInitialized.then((isInitialized) => { | ||
if (isInitialized) { | ||
this.nativeRef.update(data).then((result) => { | ||
subject.next(result); | ||
subject.complete(); | ||
}); | ||
this.nativeRef.update(data) | ||
.then(() => promise.resolve(true)) | ||
.catch(() => promise.resolve(false)); | ||
} | ||
}); | ||
return subject; | ||
}).catch(error => promise.reject(error)); | ||
return promise; | ||
} | ||
delete() { | ||
let subject = new fire_rx_1.FireReplaySubject(); | ||
let promise = new fire_rx_1.ReactivePromise(); | ||
this.connection.isInitialized.then((isInitialized) => { | ||
if (isInitialized) { | ||
this.nativeRef.delete().then((result) => { | ||
subject.next(result); | ||
subject.complete(); | ||
}); | ||
this.nativeRef.delete() | ||
.then(() => promise.resolve(true)) | ||
.catch(() => promise.resolve(false)); | ||
} | ||
}); | ||
return subject; | ||
}).catch(error => promise.reject(error)); | ||
return promise; | ||
} | ||
snapshot() { | ||
let subject = new fire_rx_1.FireReplaySubject(); | ||
let subject = new fire_rx_1.StatefulSubject(); | ||
this.connection.isInitialized.then((isInitialized) => { | ||
if (isInitialized) { | ||
this.nativeRef.onSnapshot((snapshot) => { | ||
let unsubscribeSnapshot = this.nativeRef.onSnapshot((snapshot) => { | ||
subject.next(snapshot); | ||
}); | ||
subject.subscribe({ | ||
complete: () => unsubscribeSnapshot(), | ||
}); | ||
} | ||
}); | ||
}).catch(error => subject.error(error)); | ||
return subject; | ||
@@ -86,4 +86,7 @@ } | ||
} | ||
get path() { | ||
return this.collectionPath ? `${this.collectionPath}/${this.docPath}` : this.docPath; | ||
} | ||
} | ||
exports.DocReference = DocReference; | ||
//# sourceMappingURL=DocReference.js.map |
@@ -1,18 +0,32 @@ | ||
import { EntityMetadata } from '../Contracts/EntityMetadata'; | ||
import { DocumentReference, DocumentSnapshot } from '@google-cloud/firestore'; | ||
import { GenericRepository } from '../Model/GenericRepository'; | ||
import { EntityType } from '../Contracts/EntityType'; | ||
import * as types from '@firebase/firestore-types'; | ||
import { EntityMetadata, PropertyMetadata } from '../Contracts/EntityMetadata'; | ||
import { CollectionReference } from './CollectionReference'; | ||
import { Model } from '../Contracts/Model'; | ||
import DocumentSnapshot = types.DocumentSnapshot; | ||
import DocumentReference = types.DocumentReference; | ||
export declare class EntityManager<Entity> { | ||
protected metadata: EntityMetadata; | ||
protected repository: GenericRepository<Entity>; | ||
protected entityConstructor: any; | ||
constructor(metadata: EntityMetadata, repository: GenericRepository<Entity>, entityConstructor: any); | ||
protected collectionReference: CollectionReference; | ||
constructor(metadata: EntityMetadata, entityConstructor: any, collectionReference: CollectionReference); | ||
fromSnapshot(docSnapshot: DocumentSnapshot): Entity; | ||
createEntity(collectionRef: CollectionReference): any; | ||
protected createTextIndex(text: string): string[]; | ||
protected createReverseTextIndex(text: string): string[]; | ||
createEntity(): any; | ||
protected attachSubCollectionsToEntity(entity: Entity, docReference: DocumentReference): void; | ||
protected createRepositoryForSubEntity<Entity>(entity: EntityType<Entity>, docReference: DocumentReference): any; | ||
extractDataFromEntity(entity: Entity): {}; | ||
protected attachRefsToEntity(entity: Entity, data: any): void; | ||
extractDataFromEntity(entity: Model): {}; | ||
refreshNewEntity(entity: Entity, docReference: DocumentReference): void; | ||
attachOrmMetadataToEntity(entity: Entity, docReference: DocumentReference): void; | ||
attachMetadataToNewEntity(entity: Entity, collectionRef: CollectionReference): void; | ||
attachMetadataToNewEntity(entity: Entity): void; | ||
protected createMutationTracker(entity: Entity): MutationTracker; | ||
} | ||
export declare class MutationTracker { | ||
protected copy: {}; | ||
protected fields: PropertyMetadata[]; | ||
protected entity: any; | ||
constructor(entity: any, fields: PropertyMetadata[]); | ||
refreshEntity(): void; | ||
getChanges(entity: any): {}; | ||
protected deepCopy(inObject: any): any; | ||
} |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.EntityManager = void 0; | ||
const GenericRepository_1 = require("../Model/GenericRepository"); | ||
const singletons_1 = require("../singletons"); | ||
const Collection_1 = require("../Model/Collection"); | ||
exports.MutationTracker = exports.EntityManager = void 0; | ||
const Reference_1 = require("../Model/Reference"); | ||
const DocInitializer_1 = require("./DocInitializer"); | ||
const DocPersistenceManager_1 = require("./DocPersistenceManager"); | ||
const CollectionReference_1 = require("./CollectionReference"); | ||
const DocReference_1 = require("./DocReference"); | ||
const singletons_1 = require("../singletons"); | ||
// import FieldValue = types.FieldValue - somehow fails TS | ||
class EntityManager { | ||
constructor(metadata, repository, entityConstructor) { | ||
constructor(metadata, entityConstructor, collectionReference) { | ||
this.metadata = metadata; | ||
this.repository = repository; | ||
this.entityConstructor = entityConstructor; | ||
this.collectionReference = collectionReference; | ||
} | ||
@@ -26,12 +24,30 @@ fromSnapshot(docSnapshot) { | ||
entity['id'] = docSnapshot.id; | ||
for (let field in data) { | ||
if (this.metadata.docRefs[field]) { | ||
entity[field] = new Reference_1.Reference(this.metadata.docRefs[field].entity, DocReference_1.DocReference.fromNativeRef(data[field])); | ||
this.metadata.fields.forEach(field => { | ||
var _a, _b, _c, _d, _e, _f; | ||
if (data[field.name] === undefined) { | ||
return; | ||
} | ||
else if ((((_a = data[field.name]) === null || _a === void 0 ? void 0 : _a.toDate) !== undefined && typeof ((_b = data[field.name]) === null || _b === void 0 ? void 0 : _b.toDate) === 'function') || (typeof data[field.name] === 'object' && ((_d = (_c = data[field.name]) === null || _c === void 0 ? void 0 : _c.constructor) === null || _d === void 0 ? void 0 : _d.name) === 'Timestamp')) { | ||
entity[field.name] = (_e = data[field.name]) === null || _e === void 0 ? void 0 : _e.toDate(); | ||
} | ||
else if ((field === null || field === void 0 ? void 0 : field.isDate) && data[field.name] && ((_f = data[field.name]) === null || _f === void 0 ? void 0 : _f.length) > 0) { | ||
try { | ||
entity[field.name] = new Date(data[field.name]); | ||
} | ||
catch (error) { | ||
console.error(error); | ||
} | ||
} | ||
else if (typeof data[field.name] === 'object' && (field === null || field === void 0 ? void 0 : field.isMap) && (typeof (field === null || field === void 0 ? void 0 : field.constructor) === 'object' || typeof (field === null || field === void 0 ? void 0 : field.constructor) === 'function')) { | ||
if (Array.isArray(data[field.name])) { | ||
entity[field.name] = data[field.name].map(obj => Object.assign(new field.constructor(), obj)); | ||
} | ||
else { | ||
entity[field.name] = Object.assign(new field.constructor(), data[field.name]); | ||
} | ||
} | ||
else { | ||
entity[field] = data[field]; | ||
entity[field.name] = data[field.name]; | ||
} | ||
} | ||
this.attachOrmMetadataToEntity(entity, docSnapshot.ref); | ||
this.attachSubCollectionsToEntity(entity, docSnapshot.ref); | ||
}); | ||
if (!entity['toJSON']) { | ||
@@ -46,10 +62,35 @@ //@todo need to add support for passing additional properties | ||
}); | ||
return JSON.stringify(jsonData); | ||
return jsonData; | ||
}; | ||
} | ||
this.attachOrmMetadataToEntity(entity, docSnapshot.ref); | ||
this.attachSubCollectionsToEntity(entity, docSnapshot.ref); | ||
this.attachRefsToEntity(entity, data); | ||
if (typeof entity.init === 'function') { | ||
// @todo handle promises | ||
entity.init(); | ||
} | ||
return entity; | ||
} | ||
createEntity(collectionRef) { | ||
createTextIndex(text) { | ||
const arrName = []; | ||
let curName = ''; | ||
text.split('').forEach(letter => { | ||
curName += letter.toLowerCase(); | ||
arrName.push(curName); | ||
}); | ||
return arrName; | ||
} | ||
createReverseTextIndex(text) { | ||
const arrName = []; | ||
let curName = ''; | ||
text.split('').reverse().forEach(letter => { | ||
curName += letter.toLowerCase(); | ||
arrName.push(curName); | ||
}); | ||
return arrName; | ||
} | ||
createEntity() { | ||
let entity = new this.entityConstructor(); | ||
this.attachMetadataToNewEntity(entity, collectionRef); | ||
this.attachMetadataToNewEntity(entity); | ||
return entity; | ||
@@ -63,43 +104,88 @@ } | ||
subCollectionsMetadata.forEach(subCollection => { | ||
entity[subCollection.fieldName] = new Collection_1.Collection(this.createRepositoryForSubEntity(subCollection.entity, docReference)); | ||
entity[subCollection.fieldName] = singletons_1.CollectionFactory.createWithRef(subCollection.entity, docReference); | ||
}); | ||
} | ||
createRepositoryForSubEntity(entity, docReference) { | ||
const metadata = singletons_1.Metadata.entity(entity).get(); | ||
if (metadata.repository) { | ||
// @ts-ignore | ||
return new metadata.repository(metadata, entity, docReference.collection(metadata.collection)); | ||
attachRefsToEntity(entity, data) { | ||
let docRefs = this.metadata.docRefs; | ||
for (let fieldName in docRefs) { | ||
entity[fieldName] = new Reference_1.Reference(docRefs[fieldName].entity, entity); | ||
if (data[fieldName] !== undefined) { | ||
entity[fieldName].___attachDockRef(DocReference_1.DocReference.fromNativeRef(data[fieldName])); | ||
} | ||
} | ||
else { | ||
return new GenericRepository_1.GenericRepository(metadata, entity, new CollectionReference_1.CollectionReference(singletons_1.OrmOnFire, `${docReference.path}/${metadata.collection}`)); | ||
} | ||
} | ||
extractDataFromEntity(entity) { | ||
var _a, _b, _c, _d; | ||
const fields = this.metadata.fields; | ||
let dataToSave = {}; | ||
let changes = null; | ||
if (!(((_a = entity === null || entity === void 0 ? void 0 : entity.__ormOnFire) === null || _a === void 0 ? void 0 : _a.isNew) || entity.__ormOnFire === undefined)) { | ||
// not new entities require mutation check | ||
changes = (_c = (_b = entity === null || entity === void 0 ? void 0 : entity.__ormOnFire) === null || _b === void 0 ? void 0 : _b.mutation) === null || _c === void 0 ? void 0 : _c.getChanges(entity); | ||
} | ||
fields.forEach(field => { | ||
if (entity[field.name] !== undefined) { | ||
dataToSave[field.name] = entity[field.name]; | ||
var _a, _b; | ||
if ((field === null || field === void 0 ? void 0 : field.isDate) && (field === null || field === void 0 ? void 0 : field.updateOnSave)) { | ||
let date = new Date(); | ||
entity[field.name] = date; | ||
dataToSave[field.name] = date; | ||
} | ||
else if ((field === null || field === void 0 ? void 0 : field.isDate) && (field === null || field === void 0 ? void 0 : field.generateOnCreate) && (((_a = entity === null || entity === void 0 ? void 0 : entity.__ormOnFire) === null || _a === void 0 ? void 0 : _a.isNew) || entity.__ormOnFire === undefined)) { | ||
let date = new Date(); | ||
entity[field.name] = date; | ||
dataToSave[field.name] = date; | ||
} | ||
if (changes && field.name in changes) { | ||
dataToSave[field.name] = changes[field.name]; | ||
} | ||
else if ((((_b = entity === null || entity === void 0 ? void 0 : entity.__ormOnFire) === null || _b === void 0 ? void 0 : _b.isNew) || entity.__ormOnFire === undefined) && entity[field.name] !== undefined) { | ||
// this condition required only for new entities | ||
if ((field === null || field === void 0 ? void 0 : field.isMap) && Array.isArray(entity[field.name])) { | ||
dataToSave[field.name] = entity[field.name].map(obj => { | ||
return Object.assign({}, obj); | ||
}); | ||
} | ||
else if (field === null || field === void 0 ? void 0 : field.isMap) { | ||
dataToSave[field.name] = Object.assign({}, entity[field.name]); | ||
} | ||
else { | ||
dataToSave[field.name] = entity[field.name]; | ||
} | ||
} | ||
}); | ||
const docRefs = this.metadata.docRefs; | ||
for (let fieldName in docRefs) { | ||
let docRef = (_d = entity[fieldName]) === null || _d === void 0 ? void 0 : _d.___docReference; | ||
if (docRef) { | ||
dataToSave[fieldName] = docRef.nativeRef; | ||
} | ||
} | ||
return dataToSave; | ||
} | ||
refreshNewEntity(entity, docReference) { | ||
this.attachOrmMetadataToEntity(entity, docReference); | ||
this.attachSubCollectionsToEntity(entity, docReference); | ||
} | ||
attachOrmMetadataToEntity(entity, docReference) { | ||
let persistenceManager = new DocPersistenceManager_1.DocPersistenceManager(docReference); | ||
entity['__ormOnFire'] = { | ||
repository: this.repository, | ||
isNew: false, | ||
docRef: DocReference_1.DocReference.fromNativeRef(docReference), | ||
mutation: this.createMutationTracker(entity), | ||
save: () => { | ||
return persistenceManager.update(this.extractDataFromEntity(entity)); | ||
return persistenceManager.update(this.extractDataFromEntity(entity), entity['__ormOnFire'].mutation); | ||
}, | ||
remove: () => { | ||
return persistenceManager.remove(); | ||
} | ||
}, | ||
}; | ||
} | ||
attachMetadataToNewEntity(entity, collectionRef) { | ||
attachMetadataToNewEntity(entity) { | ||
let docInitializer = new DocInitializer_1.DocInitializer(entity, this); | ||
entity['__ormOnFire'] = { | ||
repository: this.repository, | ||
isNew: true, | ||
docRef: null, | ||
mutation: this.createMutationTracker(entity), | ||
save: () => { | ||
return docInitializer.addTo(collectionRef); | ||
return docInitializer.addTo(this.collectionReference); | ||
}, | ||
@@ -109,7 +195,108 @@ remove: () => { | ||
return false; | ||
} | ||
}, | ||
}; | ||
} | ||
createMutationTracker(entity) { | ||
return new MutationTracker(entity, this.metadata.fields); | ||
} | ||
} | ||
exports.EntityManager = EntityManager; | ||
// @todo - change mutation tracker to use snapshot as source of original data | ||
class MutationTracker { | ||
constructor(entity, fields) { | ||
this.copy = {}; | ||
this.fields = fields; | ||
this.entity = entity; | ||
this.refreshEntity(); | ||
} | ||
refreshEntity() { | ||
let entity = this.entity; | ||
let fields = this.fields; | ||
this.copy = {}; | ||
fields.forEach(field => { | ||
if (entity[field.name] === undefined) { | ||
return; | ||
} | ||
if (Array.isArray(entity[field.name])) { | ||
// this.copy[field.name] = entity[field.name]?.slice() | ||
this.copy[field.name] = this.deepCopy(entity[field.name]); | ||
} | ||
else if (field.isMap) { | ||
this.copy[field.name] = Object.assign({}, entity[field.name]); | ||
} | ||
else if (typeof entity[field.name] === 'object' && entity[field.name] instanceof Date) { | ||
this.copy[field.name] = new Date(entity[field.name].getTime()); | ||
} | ||
else if (typeof entity[field.name] === 'object') { | ||
this.copy[field.name] = Object.assign({}, entity[field.name]); | ||
} | ||
else { | ||
this.copy[field.name] = entity[field.name]; | ||
} | ||
}); | ||
} | ||
getChanges(entity) { | ||
let changes = {}; | ||
this.fields.forEach(field => { | ||
var _a, _b; | ||
// @todo - half of checks disabled because of instability. Need further tetsing | ||
if (entity[field.name] === undefined) { | ||
if (this.copy[field.name] !== undefined) { | ||
// changes[field.name] = FieldValue.delete() - somehow fails TS | ||
changes[field.name] = undefined; | ||
} | ||
else { | ||
return; | ||
} | ||
} | ||
else if (Array.isArray(entity[field.name])) { // array should be checked explicitly | ||
changes[field.name] = entity[field.name]; | ||
// if (JSON.stringify(entity[field.name]) !== JSON.stringify(this.copy[field.name])) { | ||
// changes[field.name] = entity[field.name] | ||
// } | ||
} | ||
else if (typeof entity[field.name] === 'object' && entity[field.name] !== null) { | ||
// dates must be compared by time | ||
// if (entity[field.name] instanceof Date && entity[field.name]?.getTime() !== this.copy[field.name]?.getTime()) { | ||
// changes[field.name] = entity[field.name] | ||
// } else if (JSON.stringify(entity[field.name]) !== JSON.stringify(this.copy[field.name])) { | ||
// // objects need deep equality compare | ||
// changes[field.name] = { ...entity[field.name] } | ||
// } | ||
//dates must be compared by time | ||
if (entity[field.name] instanceof Date) { | ||
if (this.copy[field.name] && ((_a = entity[field.name]) === null || _a === void 0 ? void 0 : _a.getTime()) !== ((_b = this.copy[field.name]) === null || _b === void 0 ? void 0 : _b.getTime())) { | ||
changes[field.name] = entity[field.name]; | ||
} | ||
else if (!this.copy[field.name]) { | ||
changes[field.name] = entity[field.name]; | ||
} | ||
} | ||
else { | ||
// objects need deep equality compare | ||
changes[field.name] = Object.assign({}, entity[field.name]); | ||
} | ||
} | ||
else if (entity[field.name] !== this.copy[field.name]) { | ||
changes[field.name] = entity[field.name]; | ||
} | ||
}); | ||
return changes; | ||
} | ||
deepCopy(inObject) { | ||
let outObject, value, key; | ||
if (typeof inObject !== 'object' || inObject === null) { | ||
return inObject; // Return the value if inObject is not an object | ||
} | ||
// Create an array or object to hold the values | ||
outObject = Array.isArray(inObject) ? [] : {}; | ||
for (key in inObject) { | ||
value = inObject[key]; | ||
// Recursively (deep) copy for nested objects, including arrays | ||
outObject[key] = this.deepCopy(value); | ||
} | ||
return outObject; | ||
} | ||
} | ||
exports.MutationTracker = MutationTracker; | ||
//# sourceMappingURL=EntityManager.js.map |
@@ -1,4 +0,6 @@ | ||
import { FireReplaySubject } from '@typeheim/fire-rx'; | ||
import { EntityManager } from './EntityManager'; | ||
import { DocReference } from './DocReference'; | ||
import { EntityStream } from '../Data/EntityStream'; | ||
import { EntityPromise } from '../Data/EntityPromise'; | ||
import { EntityType, Collection } from '@typeheim/orm-on-fire'; | ||
export declare class EntityQuery<Entity> { | ||
@@ -8,4 +10,5 @@ protected docReference: DocReference; | ||
constructor(docReference: DocReference, entityBuilder: EntityManager<Entity>); | ||
get(): FireReplaySubject<Entity>; | ||
stream(): FireReplaySubject<Entity>; | ||
get(): EntityPromise<Entity>; | ||
collection<RefEntity>(entity: EntityType<RefEntity>): Collection<RefEntity>; | ||
stream(): EntityStream<Entity>; | ||
} |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.EntityQuery = void 0; | ||
const fire_rx_1 = require("@typeheim/fire-rx"); | ||
const EntityStream_1 = require("../Data/EntityStream"); | ||
const EntityPromise_1 = require("../Data/EntityPromise"); | ||
const operators_1 = require("rxjs/operators"); | ||
const singletons_1 = require("../singletons"); | ||
class EntityQuery { | ||
@@ -11,15 +14,25 @@ constructor(docReference, entityBuilder) { | ||
get() { | ||
let subject = new fire_rx_1.FireReplaySubject(1); | ||
this.docReference.get().subscribe((docSnapshot) => { | ||
subject.next(this.entityBuilder.fromSnapshot(docSnapshot)); | ||
subject.complete(); | ||
}); | ||
return subject; | ||
let promise = new EntityPromise_1.EntityPromise(); | ||
if (this.docReference) { | ||
this.docReference.get().subscribe({ | ||
next: (docSnapshot) => { | ||
promise.resolve(this.entityBuilder.fromSnapshot(docSnapshot)); | ||
}, | ||
error: error => promise.reject(error), | ||
}); | ||
} | ||
else { | ||
promise.resolve(null); | ||
} | ||
return promise; | ||
} | ||
collection(entity) { | ||
return singletons_1.CollectionFactory.createFromBasePath(entity, this.docReference.path); | ||
} | ||
stream() { | ||
let subject = new fire_rx_1.FireReplaySubject(1); | ||
this.docReference.snapshot().subscribe((docSnapshot) => { | ||
subject.next(this.entityBuilder.fromSnapshot(docSnapshot)); | ||
}); | ||
return subject; | ||
let snapshotStream = this.docReference.snapshot(); | ||
let source = snapshotStream.pipe(operators_1.map((docSnapshot) => { | ||
return this.entityBuilder.fromSnapshot(docSnapshot); | ||
})); | ||
return new EntityStream_1.EntityStream(source, snapshotStream); | ||
} | ||
@@ -26,0 +39,0 @@ } |
@@ -1,13 +0,18 @@ | ||
import { Firestore } from '@google-cloud/firestore'; | ||
import { FireReplaySubject } from '@typeheim/fire-rx'; | ||
import { ReactivePromise } from '@typeheim/fire-rx'; | ||
import { CollectionReference } from './CollectionReference'; | ||
import { DocReference } from './DocReference'; | ||
import * as FirestoreTypes from '@firebase/firestore-types'; | ||
import Firestore = FirestoreTypes.FirebaseFirestore; | ||
export declare class FirestoreConnection { | ||
/** | ||
* @type {Firestore} | ||
*/ | ||
protected _driver: Firestore; | ||
protected _isInitializedSubject: FireReplaySubject<boolean>; | ||
set driver(driver: Firestore); | ||
get driver(): Firestore; | ||
get isInitialized(): FireReplaySubject<boolean>; | ||
collectionReference(collectionPath: string): CollectionReference; | ||
docReference(docPath?: string): DocReference; | ||
protected _isInitializedSubject: ReactivePromise<boolean>; | ||
set driver(driver: FirestoreTypes.FirebaseFirestore); | ||
get driver(): FirestoreTypes.FirebaseFirestore; | ||
get isInitialized(): ReactivePromise<boolean>; | ||
collectionRef(collectionPath: string): CollectionReference; | ||
collectionGroupRef(collectionPath: string): CollectionReference; | ||
docRef(docPath?: string): DocReference; | ||
} |
@@ -9,7 +9,7 @@ "use strict"; | ||
constructor() { | ||
this._isInitializedSubject = new fire_rx_1.FireReplaySubject(1); | ||
this._isInitializedSubject = new fire_rx_1.ReactivePromise(); | ||
} | ||
set driver(driver) { | ||
this._driver = driver; | ||
this.isInitialized.next(true); | ||
this.isInitialized.resolve(true); | ||
} | ||
@@ -22,6 +22,9 @@ get driver() { | ||
} | ||
collectionReference(collectionPath) { | ||
collectionRef(collectionPath) { | ||
return new CollectionReference_1.CollectionReference(this, collectionPath); | ||
} | ||
docReference(docPath) { | ||
collectionGroupRef(collectionPath) { | ||
return new CollectionReference_1.CollectionReference(this, collectionPath, CollectionReference_1.CollectionRefType.Group); | ||
} | ||
docRef(docPath) { | ||
return new DocReference_1.DocReference(this, docPath); | ||
@@ -28,0 +31,0 @@ } |
@@ -1,6 +0,8 @@ | ||
import { RepositoryMap } from './Model/RepositoryMap'; | ||
import { CollectionMap } from './Model/CollectionMap'; | ||
import { FirestoreConnection } from './Persistence/FirestoreConnection'; | ||
import { MetadataStorage } from './Metadata/MetadataStorage'; | ||
import { Factory } from './Model/CollectionFactory'; | ||
export declare const Metadata: MetadataStorage; | ||
export declare const OrmOnFire: FirestoreConnection; | ||
export declare const Repo: RepositoryMap; | ||
export declare const CollectionFactory: Factory; | ||
export declare const InternalCollectionsMap: CollectionMap; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Repo = exports.OrmOnFire = exports.Metadata = void 0; | ||
const RepositoryMap_1 = require("./Model/RepositoryMap"); | ||
exports.InternalCollectionsMap = exports.CollectionFactory = exports.OrmOnFire = exports.Metadata = void 0; | ||
const CollectionMap_1 = require("./Model/CollectionMap"); | ||
const FirestoreConnection_1 = require("./Persistence/FirestoreConnection"); | ||
const MetadataStorage_1 = require("./Metadata/MetadataStorage"); | ||
const CollectionFactory_1 = require("./Model/CollectionFactory"); | ||
exports.Metadata = new MetadataStorage_1.MetadataStorage(); | ||
exports.OrmOnFire = new FirestoreConnection_1.FirestoreConnection(); | ||
exports.Repo = new RepositoryMap_1.RepositoryMap(exports.OrmOnFire); | ||
exports.CollectionFactory = new CollectionFactory_1.Factory(exports.OrmOnFire, exports.Metadata); | ||
exports.InternalCollectionsMap = new CollectionMap_1.CollectionMap(exports.CollectionFactory); | ||
//# sourceMappingURL=singletons.js.map |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
161377
2
129
2307
189
2
1
+ Added@typeheim/fire-rx@0.1.0(transitive)
- Removedrxjs@^6.5.5
- Removed@typeheim/fire-rx@0.0.0-beta-4(transitive)
- Removedrxjs@6.6.7(transitive)
- Removedtslib@1.14.1(transitive)
Updated@typeheim/fire-rx@^0.1.0