From 537baadd7cfd8c913bb1de126bf32630ecbb324d Mon Sep 17 00:00:00 2001 From: ci Date: Sun, 29 May 2022 06:57:15 +0000 Subject: [PATCH] release(workspace): v5.2.0 release added collection group support to dbx-firebase components feat: added firestore collection group support docs: setup-project docs improvement --- .circleci/config.yml | 3 +- .husky/{prettier => pre-commit} | 0 .prettierrc | 2 +- CHANGELOG.md | 10 ++ apps/demo-api/CHANGELOG.md | 4 + apps/demo-api/project.json | 44 +++-- apps/demo-api/src/test/fixture.ts | 20 +-- apps/demo-e2e/CHANGELOG.md | 4 + apps/demo/CHANGELOG.md | 4 + components/demo-components/CHANGELOG.md | 9 + components/demo-components/package.json | 2 +- ...stbook.entry.collection.store.directive.ts | 8 +- .../store/guestbook.entry.collection.store.ts | 2 +- components/demo-firebase/CHANGELOG.md | 9 + components/demo-firebase/package.json | 2 +- .../demo-firebase/src/lib/collection.ts | 4 +- .../src/lib/guestbook/guestbook.ts | 17 +- firebase.json | 18 +- firestore.rules | 11 ++ make-env.js | 7 +- package.json | 2 +- packages/browser/CHANGELOG.md | 4 + packages/browser/package.json | 2 +- packages/date/CHANGELOG.md | 4 + packages/date/package.json | 2 +- packages/dbx-analytics/CHANGELOG.md | 4 + packages/dbx-analytics/package.json | 2 +- packages/dbx-core/CHANGELOG.md | 4 + packages/dbx-core/package.json | 2 +- packages/dbx-firebase/CHANGELOG.md | 9 + packages/dbx-firebase/package.json | 2 +- .../lib/auth/appcheck/appcheck.interceptor.ts | 59 +++--- .../src/lib/auth/appcheck/appcheck.ts | 9 +- .../src/lib/auth/login/login.service.ts | 6 +- .../dbx-firebase/src/lib/firebase/appcheck.ts | 4 +- .../src/lib/firebase/firebase.module.ts | 15 +- .../loader/collection.loader.instance.ts | 14 +- .../dbx-firebase/src/lib/model/store/index.ts | 1 + .../model/store/store.collection.directive.ts | 2 +- .../src/lib/model/store/store.collection.ts | 14 +- .../store/store.subcollection.directive.ts | 41 +++++ .../store/store.subcollection.document.ts | 40 +++-- .../model/store/store.subcollection.rxjs.ts | 41 +---- .../model/store/store.subcollection.spec.ts | 22 +++ .../lib/model/store/store.subcollection.ts | 87 +++++++-- packages/dbx-form/CHANGELOG.md | 4 + packages/dbx-form/package.json | 2 +- .../selection/searchable/searchable.field.ts | 8 +- packages/dbx-web/CHANGELOG.md | 4 + packages/dbx-web/package.json | 2 +- .../progress/spinner.button.component.html | 15 +- packages/firebase-server/CHANGELOG.md | 9 + packages/firebase-server/package.json | 2 +- .../src/lib/firestore/driver.accessor.ts | 3 +- packages/firebase-server/src/lib/nest/app.ts | 2 +- .../nest/middleware/appcheck.middleware.ts | 15 +- .../lib/nest/middleware/appcheck.module.ts | 2 - .../src/lib/nest/middleware/webhook.ts | 2 - packages/firebase-server/test/CHANGELOG.md | 4 + packages/firebase-server/test/package.json | 2 +- .../src/lib/firebase/firebase.admin.auth.ts | 28 +-- .../lib/firebase/firebase.admin.collection.ts | 22 +-- .../firebase/firebase.admin.nest.function.ts | 41 ++--- .../src/lib/firebase/firebase.admin.nest.ts | 22 +-- packages/firebase/CHANGELOG.md | 10 ++ packages/firebase/package.json | 2 +- .../lib/client/firestore/driver.accessor.ts | 5 +- .../src/lib/client/firestore/driver.query.ts | 13 +- .../lib/common/firestore/accessor/document.ts | 169 ++++++++++++------ .../firestore/accessor/document.utility.ts | 10 +- .../firestore/collection/collection.group.ts | 51 ++++++ .../firestore/collection/collection.query.ts | 4 +- .../common/firestore/collection/collection.ts | 55 ++++-- .../lib/common/firestore/collection/index.ts | 1 + .../firestore/collection/subcollection.ts | 1 + .../src/lib/common/firestore/context.ts | 20 ++- .../lib/common/firestore/driver/accessor.ts | 4 +- .../lib/common/firestore/query/iterator.ts | 10 +- .../src/lib/common/firestore/query/query.ts | 8 +- .../src/lib/common/firestore/reference.ts | 9 +- .../src/lib/common/firestore/types.ts | 7 + packages/firebase/test/CHANGELOG.md | 10 ++ packages/firebase/test/package.json | 2 +- .../lib/common/firestore.mock.item.fixture.ts | 4 + .../src/lib/common/firestore.mock.item.ts | 21 ++- .../firebase/test/src/lib/common/firestore.ts | 34 +++- .../test/src/lib/common/test.driver.query.ts | 159 ++++++++++++++-- packages/model/CHANGELOG.md | 4 + packages/model/package.json | 2 +- packages/nestjs/CHANGELOG.md | 4 + packages/nestjs/package.json | 2 +- .../nestjs/src/lib/middlewares/webhook.ts | 10 +- packages/nestjs/stripe/CHANGELOG.md | 4 + packages/nestjs/stripe/package.json | 2 +- packages/rxjs/CHANGELOG.md | 4 + packages/rxjs/package.json | 2 +- packages/util/CHANGELOG.md | 4 + packages/util/package.json | 2 +- packages/util/src/lib/value/url.spec.ts | 2 - packages/util/src/lib/value/url.ts | 8 +- packages/util/test/CHANGELOG.md | 4 + packages/util/test/package.json | 2 +- project.json | 6 + setup/README.md | 52 ++++++ setup/setup-project.sh | 9 +- setup/templates/.circleci/config.yml | 14 +- start-merge-in-main.sh | 6 +- 107 files changed, 1035 insertions(+), 478 deletions(-) rename .husky/{prettier => pre-commit} (100%) create mode 100644 packages/dbx-firebase/src/lib/model/store/store.subcollection.directive.ts create mode 100644 packages/firebase/src/lib/common/firestore/collection/collection.group.ts diff --git a/.circleci/config.yml b/.circleci/config.yml index ab2fc1f04..1582c5a9d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -158,7 +158,7 @@ jobs: - run: # we grab the relevant tag names before swapping to main name: "setup env vars for git changes" - command: echo -e "export DEV_TAG_ID=$(git describe --abbrev=0 HEAD^)\nTAG_ID=$(git describe --abbrev=0)" >> $BASH_ENV + command: echo -e "export DEV_TAG_ID=$(git describe --tags --abbrev=0 HEAD^)\nTAG_ID=$(git describe --tags --abbrev=0)" >> $BASH_ENV - run: # copy the commit message before we swap branches, or it will include unrelated changes. Move tag to the main branch after making the merge commit into main name: merge release into main and use the release commit message, move tag from release to main @@ -169,6 +169,7 @@ jobs: - run: name: delete release branch command: git push origin --delete release + # Deploys rules and configured projects to Firebase. deploy-to-firebase: <<: *defaults steps: diff --git a/.husky/prettier b/.husky/pre-commit similarity index 100% rename from .husky/prettier rename to .husky/pre-commit diff --git a/.prettierrc b/.prettierrc index 5ccac64c3..eec7c9704 100644 --- a/.prettierrc +++ b/.prettierrc @@ -10,5 +10,5 @@ "tabWidth": 2, "htmlWhitespaceSensitivity": "ignore", "endOfLine": "lf", - "printWidth": 400 + "printWidth": 500 } diff --git a/CHANGELOG.md b/CHANGELOG.md index 31535486f..ff03a0d1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver). +# [5.2.0](https://github.com/dereekb/dbx-components/compare/v5.1.0-dev...v5.2.0) (2022-05-29) + + +### Features + +* added collection group support to dbx-firebase components ([9f746c1](https://github.com/dereekb/dbx-components/commit/9f746c12a0e219970dcde12d920f1ef540514ce9)) +* added firestore collection group support ([3b4c4cf](https://github.com/dereekb/dbx-components/commit/3b4c4cfa1dd860604c347ade69acdc2fea1063f8)) + + + # [5.1.0](https://github.com/dereekb/dbx-components/compare/v5.0.1-dev...v5.1.0) (2022-05-27) diff --git a/apps/demo-api/CHANGELOG.md b/apps/demo-api/CHANGELOG.md index 3325c7bfd..490d043a1 100644 --- a/apps/demo-api/CHANGELOG.md +++ b/apps/demo-api/CHANGELOG.md @@ -2,6 +2,10 @@ This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver). +# [5.2.0](https://github.com/dereekb/dbx-components/compare/v5.1.0-dev...v5.2.0) (2022-05-29) + + + # [5.1.0](https://github.com/dereekb/dbx-components/compare/v5.0.1-dev...v5.1.0) (2022-05-27) diff --git a/apps/demo-api/project.json b/apps/demo-api/project.json index 0b2f2c7f0..2867d7545 100644 --- a/apps/demo-api/project.json +++ b/apps/demo-api/project.json @@ -12,10 +12,12 @@ "build-base": { "executor": "@nrwl/node:webpack", "outputs": ["{options.outputPath}"], - "dependsOn": [{ - "target": "build", - "projects": "dependencies" - }], + "dependsOn": [ + { + "target": "build", + "projects": "dependencies" + } + ], "options": { "outputPath": "dist/apps/demo-api", "main": "apps/demo-api/src/main.ts", @@ -29,10 +31,12 @@ "extractLicenses": false, "sourceMap": false, "inspect": false, - "fileReplacements": [{ - "replace": "apps/demo-api/src/environments/environment.ts", - "with": "apps/demo-api/src/environments/environment.prod.ts" - }] + "fileReplacements": [ + { + "replace": "apps/demo-api/src/environments/environment.ts", + "with": "apps/demo-api/src/environments/environment.prod.ts" + } + ] } } }, @@ -51,7 +55,8 @@ "serve": { "executor": "@nrwl/workspace:run-commands", "options": { - "commands": [{ + "commands": [ + { "command": "npx nx build-base demo-api --watch" }, { @@ -76,10 +81,7 @@ }, "run-tests": { "executor": "@nrwl/jest:jest", - "outputs": [ - "coverage/apps/demo-api", - "./.reports/jest/demo-api.junit.xml" - ], + "outputs": ["coverage/apps/demo-api", "./.reports/jest/demo-api.junit.xml"], "options": { "jestConfig": "apps/demo-api/jest.config.ts", "passWithNoTests": true @@ -99,14 +101,18 @@ }, "ci-deploy": { "executor": "@nrwl/workspace:run-commands", - "dependsOn": [{ - "target": "build", - "projects": "self" - }], + "dependsOn": [ + { + "target": "build", + "projects": "self" + } + ], "options": { - "commands": [{ + "commands": [ + { "command": "npx nx run demo-api:build-base:production" - }, { + }, + { "command": "npx nx make-env demo-api" }, { diff --git a/apps/demo-api/src/test/fixture.ts b/apps/demo-api/src/test/fixture.ts index ed5028d21..b9b38daba 100644 --- a/apps/demo-api/src/test/fixture.ts +++ b/apps/demo-api/src/test/fixture.ts @@ -131,15 +131,7 @@ export class DemoApiGuestbookTestContextFixture extends ModelTestContextInstance> {} export const demoGuestbookContextFactory = () => - modelTestContextFactory< - Guestbook, - GuestbookDocument, - DemoApiGuestbookTestContextParams, - DemoApiFunctionContextFixtureInstance, - DemoApiFunctionContextFixture, - DemoApiGuestbookTestContextInstance, - DemoApiGuestbookTestContextFixture - >({ + modelTestContextFactory, DemoApiFunctionContextFixture, DemoApiGuestbookTestContextInstance, DemoApiGuestbookTestContextFixture>({ makeFixture: (f) => new DemoApiGuestbookTestContextFixture(f), getCollection: (fi) => fi.demoFirestoreCollections.guestbookFirestoreCollection, makeInstance: (delegate, ref, testInstance) => new DemoApiGuestbookTestContextInstance(delegate, ref, testInstance), @@ -169,15 +161,7 @@ export class DemoApiGuestbookEntryTestContextFixture extends ModelTestContextInstance> {} export const demoGuestbookEntryContextFactory = () => - modelTestContextFactory< - GuestbookEntry, - GuestbookEntryDocument, - DemoApiGuestbookEntryTestContextParams, - DemoApiFunctionContextFixtureInstance, - DemoApiFunctionContextFixture, - DemoApiGuestbookEntryTestContextInstance, - DemoApiGuestbookEntryTestContextFixture - >({ + modelTestContextFactory, DemoApiFunctionContextFixture, DemoApiGuestbookEntryTestContextInstance, DemoApiGuestbookEntryTestContextFixture>({ makeFixture: (f) => new DemoApiGuestbookEntryTestContextFixture(f), getCollection: (fi, params) => fi.demoFirestoreCollections.guestbookEntryCollectionFactory(params.g.document), makeInstance: (delegate, ref, testInstance) => new DemoApiGuestbookEntryTestContextInstance(delegate, ref, testInstance), diff --git a/apps/demo-e2e/CHANGELOG.md b/apps/demo-e2e/CHANGELOG.md index 4579ad0d8..8c79ebe88 100644 --- a/apps/demo-e2e/CHANGELOG.md +++ b/apps/demo-e2e/CHANGELOG.md @@ -2,6 +2,10 @@ This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver). +# [5.2.0](https://github.com/dereekb/dbx-components/compare/v5.1.0-dev...v5.2.0) (2022-05-29) + + + # [5.1.0](https://github.com/dereekb/dbx-components/compare/v5.0.1-dev...v5.1.0) (2022-05-27) diff --git a/apps/demo/CHANGELOG.md b/apps/demo/CHANGELOG.md index 6def38388..c13a982e9 100644 --- a/apps/demo/CHANGELOG.md +++ b/apps/demo/CHANGELOG.md @@ -2,6 +2,10 @@ This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver). +# [5.2.0](https://github.com/dereekb/dbx-components/compare/v5.1.0-dev...v5.2.0) (2022-05-29) + + + # [5.1.0](https://github.com/dereekb/dbx-components/compare/v5.0.1-dev...v5.1.0) (2022-05-27) diff --git a/components/demo-components/CHANGELOG.md b/components/demo-components/CHANGELOG.md index 4353abce5..70e4128e4 100644 --- a/components/demo-components/CHANGELOG.md +++ b/components/demo-components/CHANGELOG.md @@ -2,6 +2,15 @@ This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver). +# [5.2.0](https://github.com/dereekb/dbx-components/compare/v5.1.0-dev...v5.2.0) (2022-05-29) + + +### Features + +* added collection group support to dbx-firebase components ([9f746c1](https://github.com/dereekb/dbx-components/commit/9f746c12a0e219970dcde12d920f1ef540514ce9)) + + + # [5.1.0](https://github.com/dereekb/dbx-components/compare/v5.0.1-dev...v5.1.0) (2022-05-27) diff --git a/components/demo-components/package.json b/components/demo-components/package.json index e6898c27e..f3473a88c 100644 --- a/components/demo-components/package.json +++ b/components/demo-components/package.json @@ -1,6 +1,6 @@ { "name": "@dereekb/demo-components", - "version": "5.1.0", + "version": "5.2.0", "peerDependencies": { "@angular/common": "^13.3.0", "@angular/core": "^13.3.0" diff --git a/components/demo-components/src/lib/modules/guestbook/store/guestbook.entry.collection.store.directive.ts b/components/demo-components/src/lib/modules/guestbook/store/guestbook.entry.collection.store.directive.ts index 62f8ace7a..d0a276737 100644 --- a/components/demo-components/src/lib/modules/guestbook/store/guestbook.entry.collection.store.directive.ts +++ b/components/demo-components/src/lib/modules/guestbook/store/guestbook.entry.collection.store.directive.ts @@ -1,13 +1,13 @@ import { Directive } from '@angular/core'; -import { DbxFirebaseCollectionStoreDirective, provideDbxFirebaseCollectionStoreDirective } from '@dereekb/dbx-firebase'; -import { GuestbookEntry, GuestbookEntryDocument } from '@dereekb/demo-firebase'; +import { DbxFirebaseCollectionWithParentStoreDirective, provideDbxFirebaseCollectionWithParentStoreDirective } from '@dereekb/dbx-firebase'; +import { Guestbook, GuestbookDocument, GuestbookEntry, GuestbookEntryDocument } from '@dereekb/demo-firebase'; import { GuestbookEntryCollectionStore } from './guestbook.entry.collection.store'; @Directive({ selector: '[demoGuestbookEntryCollection]', - providers: provideDbxFirebaseCollectionStoreDirective(DemoGuestbookEntryCollectionStoreDirective, GuestbookEntryCollectionStore) + providers: provideDbxFirebaseCollectionWithParentStoreDirective(DemoGuestbookEntryCollectionStoreDirective, GuestbookEntryCollectionStore) }) -export class DemoGuestbookEntryCollectionStoreDirective extends DbxFirebaseCollectionStoreDirective { +export class DemoGuestbookEntryCollectionStoreDirective extends DbxFirebaseCollectionWithParentStoreDirective { constructor(store: GuestbookEntryCollectionStore) { super(store); } diff --git a/components/demo-components/src/lib/modules/guestbook/store/guestbook.entry.collection.store.ts b/components/demo-components/src/lib/modules/guestbook/store/guestbook.entry.collection.store.ts index 6b00ed8ab..5e5246884 100644 --- a/components/demo-components/src/lib/modules/guestbook/store/guestbook.entry.collection.store.ts +++ b/components/demo-components/src/lib/modules/guestbook/store/guestbook.entry.collection.store.ts @@ -6,7 +6,7 @@ import { GuestbookDocumentStore } from './guestbook.document.store'; @Injectable() export class GuestbookEntryCollectionStore extends AbstractDbxFirebaseCollectionWithParentStore { constructor(collections: DemoFirestoreCollections, @Optional() parent: GuestbookDocumentStore) { - super({ collectionFactory: collections.guestbookEntryCollectionFactory }); + super({ collectionFactory: collections.guestbookEntryCollectionFactory, collectionGroup: collections.guestbookEntryCollectionGroup }); this.setConstraints(publishedGuestbookEntry()); // todo: replace with filter if (parent) { diff --git a/components/demo-firebase/CHANGELOG.md b/components/demo-firebase/CHANGELOG.md index d3fb7b974..d76ad154f 100644 --- a/components/demo-firebase/CHANGELOG.md +++ b/components/demo-firebase/CHANGELOG.md @@ -2,6 +2,15 @@ This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver). +# [5.2.0](https://github.com/dereekb/dbx-components/compare/v5.1.0-dev...v5.2.0) (2022-05-29) + + +### Features + +* added firestore collection group support ([3b4c4cf](https://github.com/dereekb/dbx-components/commit/3b4c4cfa1dd860604c347ade69acdc2fea1063f8)) + + + # [5.1.0](https://github.com/dereekb/dbx-components/compare/v5.0.1-dev...v5.1.0) (2022-05-27) diff --git a/components/demo-firebase/package.json b/components/demo-firebase/package.json index 4429c72a5..57f3a45ae 100644 --- a/components/demo-firebase/package.json +++ b/components/demo-firebase/package.json @@ -1,5 +1,5 @@ { "name": "@dereekb/demo-firebase", - "version": "5.1.0", + "version": "5.2.0", "type": "commonjs" } \ No newline at end of file diff --git a/components/demo-firebase/src/lib/collection.ts b/components/demo-firebase/src/lib/collection.ts index c0955309a..492796b8c 100644 --- a/components/demo-firebase/src/lib/collection.ts +++ b/components/demo-firebase/src/lib/collection.ts @@ -1,9 +1,10 @@ import { FirestoreContext } from '@dereekb/firebase'; -import { guestbookEntryFirestoreCollectionFactory, GuestbookEntryFirestoreCollectionFactory, guestbookFirestoreCollection, GuestbookFirestoreCollection, GuestbookFirestoreCollections } from './guestbook'; +import { guestbookEntryFirestoreCollectionFactory, GuestbookEntryFirestoreCollectionFactory, guestbookEntryFirestoreCollectionGroup, GuestbookEntryFirestoreCollectionGroup, guestbookFirestoreCollection, GuestbookFirestoreCollection, GuestbookFirestoreCollections } from './guestbook'; import { profileFirestoreCollection, ProfileFirestoreCollection, ProfileFirestoreCollections, profilePrivateDataFirestoreCollectionFactory, ProfilePrivateDataFirestoreCollectionFactory } from './profile/profile'; export abstract class DemoFirestoreCollections implements ProfileFirestoreCollections, GuestbookFirestoreCollections { abstract readonly guestbookFirestoreCollection: GuestbookFirestoreCollection; + abstract readonly guestbookEntryCollectionGroup: GuestbookEntryFirestoreCollectionGroup; abstract readonly guestbookEntryCollectionFactory: GuestbookEntryFirestoreCollectionFactory; abstract readonly profileFirestoreCollection: ProfileFirestoreCollection; abstract readonly profilePrivateDataCollectionFactory: ProfilePrivateDataFirestoreCollectionFactory; @@ -12,6 +13,7 @@ export abstract class DemoFirestoreCollections implements ProfileFirestoreCollec export function makeDemoFirestoreCollections(firestoreContext: FirestoreContext): DemoFirestoreCollections { return { guestbookFirestoreCollection: guestbookFirestoreCollection(firestoreContext), + guestbookEntryCollectionGroup: guestbookEntryFirestoreCollectionGroup(firestoreContext), guestbookEntryCollectionFactory: guestbookEntryFirestoreCollectionFactory(firestoreContext), profileFirestoreCollection: profileFirestoreCollection(firestoreContext), profilePrivateDataCollectionFactory: profilePrivateDataFirestoreCollectionFactory(firestoreContext) diff --git a/components/demo-firebase/src/lib/guestbook/guestbook.ts b/components/demo-firebase/src/lib/guestbook/guestbook.ts index 933a9e5ba..1ddc720c0 100644 --- a/components/demo-firebase/src/lib/guestbook/guestbook.ts +++ b/components/demo-firebase/src/lib/guestbook/guestbook.ts @@ -1,4 +1,4 @@ -import { CollectionReference, AbstractFirestoreDocument, snapshotConverterFunctions, firestoreString, firestoreDate, FirestoreCollection, UserRelatedById, DocumentReferenceRef, FirestoreContext, FirestoreCollectionWithParent, firestoreBoolean, DocumentDataWithId, AbstractFirestoreDocumentWithParent, optionalFirestoreDate } from '@dereekb/firebase'; +import { CollectionReference, AbstractFirestoreDocument, snapshotConverterFunctions, firestoreString, firestoreDate, FirestoreCollection, UserRelatedById, DocumentReferenceRef, FirestoreContext, FirestoreCollectionWithParent, firestoreBoolean, DocumentDataWithId, AbstractFirestoreDocumentWithParent, optionalFirestoreDate, DocumentReference, FirestoreCollectionGroup, CollectionGroup } from '@dereekb/firebase'; import { Maybe } from '@dereekb/util'; export interface GuestbookFirestoreCollections { @@ -122,3 +122,18 @@ export function guestbookEntryFirestoreCollectionFactory(firestoreContext: Fires }); }; } + +export function guestbookEntryCollectionReference(context: FirestoreContext): CollectionGroup { + return context.collectionGroup(guestbookCollectionGuestbookEntryCollectionPath).withConverter(guestbookEntryConverter); +} + +export type GuestbookEntryFirestoreCollectionGroup = FirestoreCollectionGroup; + +export function guestbookEntryFirestoreCollectionGroup(firestoreContext: FirestoreContext): GuestbookEntryFirestoreCollectionGroup { + return firestoreContext.firestoreCollectionGroup({ + itemsPerPage: 50, + queryLike: guestbookEntryCollectionReference(firestoreContext), + makeDocument: (accessor, documentAccessor) => new GuestbookEntryDocument(undefined, accessor, documentAccessor), + firestoreContext + }); +} diff --git a/firebase.json b/firebase.json index c723fb02b..7b8ff8a56 100644 --- a/firebase.json +++ b/firebase.json @@ -3,13 +3,17 @@ "site": "dereekb-components", "public": "dist/apps/demo", "ignore": ["firebase.json", "**/.*", "**/node_modules/**"], - "headers": [{ - "source": "*.[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f].+(css|js)", - "headers": [{ - "key": "Cache-Control", - "value": "public,max-age=31536000,immutable" - }] - }], + "headers": [ + { + "source": "*.[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f].+(css|js)", + "headers": [ + { + "key": "Cache-Control", + "value": "public,max-age=31536000,immutable" + } + ] + } + ], "rewrites": [ { "source": "/api/**", diff --git a/firestore.rules b/firestore.rules index 6cecf4747..542e6e964 100644 --- a/firestore.rules +++ b/firestore.rules @@ -20,6 +20,17 @@ service cloud.firestore { } } + // collection group example + // https://firebase.google.com/docs/firestore/security/rules-query#collection_group_queries_and_security_rules + match /{path=**}/entry/{entry} { + + // Can read any entry that is owned or published. + allow get: if resourceIsPublished() || resourceIsOwnedByAuthUserId(); + + // Can only query if the guestbook entry is published. + allow list: if resourceIsPublished(); + } + function resourceIsPublished() { return (resource.data.published != false); } diff --git a/make-env.js b/make-env.js index 51a3cb404..f4978923a 100644 --- a/make-env.js +++ b/make-env.js @@ -6,14 +6,11 @@ * node make-env.js */ const fs = require('fs'); -const { - parse, - stringify -} = require('envfile'); +const { parse, stringify } = require('envfile'); // NOTE: If run within nx, remember that nx adds all variables within .env to the environment and, thus, process.env. // Variables that are within bash take prority. -const templateFilePath = '.env'; +const templateFilePath = '.env'; const templateEnv = fs.readFileSync(templateFilePath).toString(); const template = parse(templateEnv); diff --git a/package.json b/package.json index 5c1d60482..21692fa70 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@dereekb/dbx-components", - "version": "5.1.0", + "version": "5.2.0", "license": "MIT", "scripts": { "postinstall": "ngcc --properties es2015 browser module main", diff --git a/packages/browser/CHANGELOG.md b/packages/browser/CHANGELOG.md index c987a53fb..c5f7dbe14 100644 --- a/packages/browser/CHANGELOG.md +++ b/packages/browser/CHANGELOG.md @@ -2,6 +2,10 @@ This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver). +# [5.2.0](https://github.com/dereekb/dbx-components/compare/v5.1.0-dev...v5.2.0) (2022-05-29) + + + # [5.1.0](https://github.com/dereekb/dbx-components/compare/v5.0.1-dev...v5.1.0) (2022-05-27) diff --git a/packages/browser/package.json b/packages/browser/package.json index a24442348..022666f49 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -1,5 +1,5 @@ { "name": "@dereekb/browser", - "version": "5.1.0", + "version": "5.2.0", "type": "commonjs" } \ No newline at end of file diff --git a/packages/date/CHANGELOG.md b/packages/date/CHANGELOG.md index c7851255f..05c44e725 100644 --- a/packages/date/CHANGELOG.md +++ b/packages/date/CHANGELOG.md @@ -2,6 +2,10 @@ This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver). +# [5.2.0](https://github.com/dereekb/dbx-components/compare/v5.1.0-dev...v5.2.0) (2022-05-29) + + + # [5.1.0](https://github.com/dereekb/dbx-components/compare/v5.0.1-dev...v5.1.0) (2022-05-27) diff --git a/packages/date/package.json b/packages/date/package.json index e28fd6aa0..7c690adf8 100644 --- a/packages/date/package.json +++ b/packages/date/package.json @@ -1,5 +1,5 @@ { "name": "@dereekb/date", - "version": "5.1.0", + "version": "5.2.0", "type": "commonjs" } \ No newline at end of file diff --git a/packages/dbx-analytics/CHANGELOG.md b/packages/dbx-analytics/CHANGELOG.md index 587ba173b..3fa1a2bd7 100644 --- a/packages/dbx-analytics/CHANGELOG.md +++ b/packages/dbx-analytics/CHANGELOG.md @@ -2,6 +2,10 @@ This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver). +# [5.2.0](https://github.com/dereekb/dbx-components/compare/v5.1.0-dev...v5.2.0) (2022-05-29) + + + # [5.1.0](https://github.com/dereekb/dbx-components/compare/v5.0.1-dev...v5.1.0) (2022-05-27) diff --git a/packages/dbx-analytics/package.json b/packages/dbx-analytics/package.json index 2b62fe7cf..a227d1d86 100644 --- a/packages/dbx-analytics/package.json +++ b/packages/dbx-analytics/package.json @@ -1,6 +1,6 @@ { "name": "@dereekb/dbx-analytics", - "version": "5.1.0", + "version": "5.2.0", "peerDependencies": { "@angular/common": "^13.1.0", "@angular/core": "^13.1.0" diff --git a/packages/dbx-core/CHANGELOG.md b/packages/dbx-core/CHANGELOG.md index 505b4d34a..a7a7eca90 100644 --- a/packages/dbx-core/CHANGELOG.md +++ b/packages/dbx-core/CHANGELOG.md @@ -2,6 +2,10 @@ This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver). +# [5.2.0](https://github.com/dereekb/dbx-components/compare/v5.1.0-dev...v5.2.0) (2022-05-29) + + + # [5.1.0](https://github.com/dereekb/dbx-components/compare/v5.0.1-dev...v5.1.0) (2022-05-27) diff --git a/packages/dbx-core/package.json b/packages/dbx-core/package.json index 802c4629e..030f4d289 100644 --- a/packages/dbx-core/package.json +++ b/packages/dbx-core/package.json @@ -1,6 +1,6 @@ { "name": "@dereekb/dbx-core", - "version": "5.1.0", + "version": "5.2.0", "peerDependencies": { "@angular/common": "^13.0.0", "@angular/core": "^13.0.0" diff --git a/packages/dbx-firebase/CHANGELOG.md b/packages/dbx-firebase/CHANGELOG.md index 802993218..dab896513 100644 --- a/packages/dbx-firebase/CHANGELOG.md +++ b/packages/dbx-firebase/CHANGELOG.md @@ -2,6 +2,15 @@ This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver). +# [5.2.0](https://github.com/dereekb/dbx-components/compare/v5.1.0-dev...v5.2.0) (2022-05-29) + + +### Features + +* added collection group support to dbx-firebase components ([9f746c1](https://github.com/dereekb/dbx-components/commit/9f746c12a0e219970dcde12d920f1ef540514ce9)) + + + # [5.1.0](https://github.com/dereekb/dbx-components/compare/v5.0.1-dev...v5.1.0) (2022-05-27) diff --git a/packages/dbx-firebase/package.json b/packages/dbx-firebase/package.json index ebe296597..3e34a47f7 100644 --- a/packages/dbx-firebase/package.json +++ b/packages/dbx-firebase/package.json @@ -1,6 +1,6 @@ { "name": "@dereekb/dbx-firebase", - "version": "5.1.0", + "version": "5.2.0", "peerDependencies": { "@angular/common": "^13.0.0", "@angular/core": "^13.0.0" diff --git a/packages/dbx-firebase/src/lib/auth/appcheck/appcheck.interceptor.ts b/packages/dbx-firebase/src/lib/auth/appcheck/appcheck.interceptor.ts index e194331c2..9a7d42bb0 100644 --- a/packages/dbx-firebase/src/lib/auth/appcheck/appcheck.interceptor.ts +++ b/packages/dbx-firebase/src/lib/auth/appcheck/appcheck.interceptor.ts @@ -1,16 +1,8 @@ import { urlWithoutParameters } from '@dereekb/util'; import { DBX_FIREBASE_OPTIONS_TOKEN, DbxFirebaseOptions } from '../../firebase/options'; -import { - HttpInterceptor, HttpRequest, HttpHandler, HttpEvent -} from '@angular/common/http'; +import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; import { getToken } from 'firebase/app-check'; -import { - Observable, - switchMap, - first, - map, - from, -} from 'rxjs'; +import { Observable, switchMap, first, map, from } from 'rxjs'; import { Inject, Injectable } from '@angular/core'; import { AppCheck } from '@angular/fire/app-check'; @@ -24,21 +16,17 @@ interface EnabledAppCheckRoute { */ @Injectable() export class DbxFirebaseAppCheckHttpInterceptor implements HttpInterceptor { - private _isEnabled: boolean; private _appCheckRoutes: EnabledAppCheckRoute[]; - constructor( - @Inject(DBX_FIREBASE_OPTIONS_TOKEN) private dbxFirebaseOptions: DbxFirebaseOptions, - private appCheck: AppCheck - ) { + constructor(@Inject(DBX_FIREBASE_OPTIONS_TOKEN) private dbxFirebaseOptions: DbxFirebaseOptions, private appCheck: AppCheck) { let routes: EnabledAppCheckRoute[] = []; if (appCheck != null) { routes = (this.dbxFirebaseOptions.appCheck?.appCheckRoutes ?? ['/api/*']).map((route) => { const wildcardIndex = route.indexOf('*'); - const isWildcard = (wildcardIndex === route.length - 1); - const match = (isWildcard) ? route.substring(0, wildcardIndex) : route; + const isWildcard = wildcardIndex === route.length - 1; + const match = isWildcard ? route.substring(0, wildcardIndex) : route; return { isWildcard, @@ -51,11 +39,7 @@ export class DbxFirebaseAppCheckHttpInterceptor implements HttpInterceptor { this._isEnabled = routes.length > 0; } - intercept( - req: HttpRequest, - next: HttpHandler - ): Observable> { - + intercept(req: HttpRequest, next: HttpHandler): Observable> { let obs: Observable>; if (this._isEnabled) { @@ -64,20 +48,20 @@ export class DbxFirebaseAppCheckHttpInterceptor implements HttpInterceptor { let nextEvent: Observable>; if (isMatch) { - nextEvent = from(getToken(this.appCheck).then((appCheckTokenResponse) => { - const token = appCheckTokenResponse.token; - let nextRequest: HttpRequest = req; - - if (token) { - nextRequest = req.clone({ - headers: req.headers.set('X-Firebase-AppCheck', token) - }); - } - - return nextRequest; - })).pipe( - switchMap((nextRequest) => next.handle(nextRequest)) - ); + nextEvent = from( + getToken(this.appCheck).then((appCheckTokenResponse) => { + const token = appCheckTokenResponse.token; + let nextRequest: HttpRequest = req; + + if (token) { + nextRequest = req.clone({ + headers: req.headers.set('X-Firebase-AppCheck', token) + }); + } + + return nextRequest; + }) + ).pipe(switchMap((nextRequest) => next.handle(nextRequest))); } else { nextEvent = next.handle(req); } @@ -105,8 +89,7 @@ export class DbxFirebaseAppCheckHttpInterceptor implements HttpInterceptor { return from(this._appCheckRoutes).pipe( first((route) => isEnabledRouteMatch(route), false), - map(x => Boolean(x)) + map((x) => Boolean(x)) ); } - } diff --git a/packages/dbx-firebase/src/lib/auth/appcheck/appcheck.ts b/packages/dbx-firebase/src/lib/auth/appcheck/appcheck.ts index d8a22238f..dcc7ad50c 100644 --- a/packages/dbx-firebase/src/lib/auth/appcheck/appcheck.ts +++ b/packages/dbx-firebase/src/lib/auth/appcheck/appcheck.ts @@ -1,11 +1,10 @@ - /** * Enables debug token generation for AppCheck by setting FIREBASE_APPCHECK_DEBUG_TOKEN in the browser's self/window. - * + * * https://firebase.google.com/docs/app-check/web/debug-provider - * - * @param enable + * + * @param enable */ - export function enableAppCheckDebugTokenGeneration(enable = true) { +export function enableAppCheckDebugTokenGeneration(enable = true) { (self as unknown as { FIREBASE_APPCHECK_DEBUG_TOKEN: boolean }).FIREBASE_APPCHECK_DEBUG_TOKEN = enable; } diff --git a/packages/dbx-firebase/src/lib/auth/login/login.service.ts b/packages/dbx-firebase/src/lib/auth/login/login.service.ts index 548228ae0..20f43d233 100644 --- a/packages/dbx-firebase/src/lib/auth/login/login.service.ts +++ b/packages/dbx-firebase/src/lib/auth/login/login.service.ts @@ -81,11 +81,7 @@ export class DbxFirebaseAuthLoginService { private _assets = new Map(); private _enabled = new Set(); - constructor( - @Optional() @Inject(DEFAULT_FIREBASE_AUTH_LOGIN_PROVIDERS_TOKEN) defaultProviders: DbxFirebaseAuthLoginProvider[], - @Optional() @Inject(DEFAULT_FIREBASE_AUTH_LOGIN_PASSWORD_CONFIG_TOKEN) passwordConfig: DbxFirebaseAuthLoginPasswordConfig, - @Optional() @Inject(DEFAULT_FIREBASE_AUTH_LOGIN_TERMS_COMPONENT_CLASS_TOKEN) readonly termsComponentClass: Type = DbxFirebaseLoginTermsSimpleComponent - ) { + constructor(@Optional() @Inject(DEFAULT_FIREBASE_AUTH_LOGIN_PROVIDERS_TOKEN) defaultProviders: DbxFirebaseAuthLoginProvider[], @Optional() @Inject(DEFAULT_FIREBASE_AUTH_LOGIN_PASSWORD_CONFIG_TOKEN) passwordConfig: DbxFirebaseAuthLoginPasswordConfig, @Optional() @Inject(DEFAULT_FIREBASE_AUTH_LOGIN_TERMS_COMPONENT_CLASS_TOKEN) readonly termsComponentClass: Type = DbxFirebaseLoginTermsSimpleComponent) { if (defaultProviders) { defaultProviders.forEach((x) => this.register(x, false)); } diff --git a/packages/dbx-firebase/src/lib/firebase/appcheck.ts b/packages/dbx-firebase/src/lib/firebase/appcheck.ts index ad74b69b6..972cd35fb 100644 --- a/packages/dbx-firebase/src/lib/firebase/appcheck.ts +++ b/packages/dbx-firebase/src/lib/firebase/appcheck.ts @@ -5,7 +5,7 @@ export interface DbxFirebaseAppCheckConfig extends Partial { return { ngModule: DbxFirebaseDefaultFirebaseProvidersModule, @@ -178,5 +176,4 @@ export class DbxFirebaseDefaultFirebaseProvidersModule { ] }; } - } diff --git a/packages/dbx-firebase/src/lib/model/loader/collection.loader.instance.ts b/packages/dbx-firebase/src/lib/model/loader/collection.loader.instance.ts index 66a7ef625..23155d953 100644 --- a/packages/dbx-firebase/src/lib/model/loader/collection.loader.instance.ts +++ b/packages/dbx-firebase/src/lib/model/loader/collection.loader.instance.ts @@ -1,11 +1,11 @@ import { PageListLoadingState, cleanupDestroyable, filterMaybe, useFirst, SubscriptionObject, accumulatorFlattenPageListLoadingState } from '@dereekb/rxjs'; import { BehaviorSubject, combineLatest, map, shareReplay, distinctUntilChanged, Subject, throttleTime, switchMap, Observable, tap, startWith, NEVER } from 'rxjs'; -import { FirebaseQueryItemAccumulator, firebaseQueryItemAccumulator, FirestoreCollection, FirestoreDocument, FirestoreItemPageIterationInstance, FirestoreItemPageIteratorFilter, FirestoreQueryConstraint, IterationQueryDocChangeWatcher, iterationQueryDocChangeWatcher } from '@dereekb/firebase'; +import { FirebaseQueryItemAccumulator, firebaseQueryItemAccumulator, FirestoreCollectionLike, FirestoreDocument, FirestoreItemPageIterationInstance, FirestoreItemPageIteratorFilter, FirestoreQueryConstraint, IterationQueryDocChangeWatcher, iterationQueryDocChangeWatcher } from '@dereekb/firebase'; import { ArrayOrValue, Destroyable, Initialized, Maybe } from '@dereekb/util'; import { DbxFirebaseCollectionLoader } from './collection.loader'; export interface DbxFirebaseCollectionLoaderInstanceInitConfig = FirestoreDocument> { - collection?: Maybe>; + collection?: Maybe>; maxPages?: Maybe; itemsPerPage?: Maybe; constraints?: Maybe>; @@ -21,7 +21,7 @@ export interface DbxFirebaseCollectionLoaderInstanceData = FirestoreDocument> implements DbxFirebaseCollectionLoader, DbxFirebaseCollectionLoaderInstanceData, Initialized, Destroyable { - protected readonly _collection = new BehaviorSubject>>(this._initConfig?.collection); + protected readonly _collection = new BehaviorSubject>>(this._initConfig?.collection); protected readonly _maxPages = new BehaviorSubject>(this._initConfig?.maxPages); protected readonly _itemsPerPage = new BehaviorSubject>(this._initConfig?.itemsPerPage); @@ -128,11 +128,11 @@ export class DbxFirebaseCollectionLoaderInstance> { + get collection(): Maybe> { return this._collection.value; } - set collection(collection: Maybe>) { + set collection(collection: Maybe>) { this._collection.next(collection); } @@ -149,7 +149,7 @@ export class DbxFirebaseCollectionLoaderInstance>) { + setCollection(firestoreCollection: Maybe>) { this.collection = firestoreCollection; } } @@ -158,6 +158,6 @@ export function dbxFirebaseCollectionLoaderInstance(config); } -export function dbxFirebaseCollectionLoaderInstanceWithCollection = FirestoreDocument>(collection: Maybe>): DbxFirebaseCollectionLoaderInstance { +export function dbxFirebaseCollectionLoaderInstanceWithCollection = FirestoreDocument>(collection: Maybe>): DbxFirebaseCollectionLoaderInstance { return new DbxFirebaseCollectionLoaderInstance({ collection }); } diff --git a/packages/dbx-firebase/src/lib/model/store/index.ts b/packages/dbx-firebase/src/lib/model/store/index.ts index eb8cb211a..084367eae 100644 --- a/packages/dbx-firebase/src/lib/model/store/index.ts +++ b/packages/dbx-firebase/src/lib/model/store/index.ts @@ -10,4 +10,5 @@ export * from './store.document.directive'; export * from './store.document.router.directive'; export * from './store.subcollection'; export * from './store.subcollection.document'; +export * from './store.subcollection.directive'; export * from './store.subcollection.rxjs'; diff --git a/packages/dbx-firebase/src/lib/model/store/store.collection.directive.ts b/packages/dbx-firebase/src/lib/model/store/store.collection.directive.ts index c224618b4..4debc71ba 100644 --- a/packages/dbx-firebase/src/lib/model/store/store.collection.directive.ts +++ b/packages/dbx-firebase/src/lib/model/store/store.collection.directive.ts @@ -52,7 +52,7 @@ export abstract class DbxFirebaseCollectionStoreDirective>(sourceType: Type): Provider[]; -export function provideDbxFirebaseCollectionStoreDirective, C extends DbxFirebaseCollectionStoreDirective = DbxFirebaseCollectionStoreDirective>(sourceType: Type, storeType: Type): Provider[]; +export function provideDbxFirebaseCollectionStoreDirective, C extends DbxFirebaseCollectionStoreDirective = DbxFirebaseCollectionStoreDirective>(sourceType: Type, storeType?: Type): Provider[]; export function provideDbxFirebaseCollectionStoreDirective, C extends DbxFirebaseCollectionStoreDirective = DbxFirebaseCollectionStoreDirective>(sourceType: Type, storeType?: Type): Provider[] { const providers: Provider[] = [ { diff --git a/packages/dbx-firebase/src/lib/model/store/store.collection.ts b/packages/dbx-firebase/src/lib/model/store/store.collection.ts index ee7559ac0..413a26ea7 100644 --- a/packages/dbx-firebase/src/lib/model/store/store.collection.ts +++ b/packages/dbx-firebase/src/lib/model/store/store.collection.ts @@ -1,13 +1,13 @@ import { Injectable } from '@angular/core'; import { Observable, shareReplay, distinctUntilChanged, Subscription, exhaustMap, first, map, switchMap, tap } from 'rxjs'; -import { FirebaseQueryItemAccumulator, FirestoreCollection, FirestoreDocument, FirestoreItemPageIterationInstance, FirestoreQueryConstraint, IterationQueryDocChangeWatcher } from '@dereekb/firebase'; +import { FirebaseQueryItemAccumulator, FirestoreCollectionLike, FirestoreDocument, FirestoreItemPageIterationInstance, FirestoreQueryConstraint, IterationQueryDocChangeWatcher } from '@dereekb/firebase'; import { ObservableOrValue, cleanupDestroyable, PageListLoadingState, filterMaybe } from '@dereekb/rxjs'; import { ArrayOrValue, Maybe } from '@dereekb/util'; import { LockSetComponentStore } from '@dereekb/dbx-core'; import { DbxFirebaseCollectionLoaderInstance, dbxFirebaseCollectionLoaderInstance, DbxFirebaseCollectionLoaderInstanceData } from '../loader/collection.loader.instance'; export interface DbxFirebaseCollectionStore = FirestoreDocument> extends DbxFirebaseCollectionLoaderInstanceData { - readonly firestoreCollection$: Observable>>; + readonly firestoreCollection$: Observable>>; readonly loader$: Observable>; readonly firestoreIteration$: Observable>; @@ -21,11 +21,11 @@ export interface DbxFirebaseCollectionStore = next(observableOrValue: ObservableOrValue): void; restart(observableOrValue: ObservableOrValue): void; - readonly setFirestoreCollection: (() => void) | ((observableOrValue: ObservableOrValue>>) => Subscription); + readonly setFirestoreCollection: (() => void) | ((observableOrValue: ObservableOrValue>>) => Subscription); } export interface DbxFirebaseCollectionStoreContextState = FirestoreDocument> { - readonly firestoreCollection?: Maybe>; + readonly firestoreCollection?: Maybe>; readonly maxPages?: Maybe; readonly itemsPerPage?: Maybe; readonly constraints?: Maybe>; @@ -72,13 +72,13 @@ export class AbstractDbxFirebaseCollectionStore>> = this.state$.pipe( + readonly currentFirestoreCollection$: Observable>> = this.state$.pipe( map((x) => x.firestoreCollection), distinctUntilChanged(), shareReplay(1) ); - readonly firestoreCollection$: Observable> = this.currentFirestoreCollection$.pipe(filterMaybe()); + readonly firestoreCollection$: Observable> = this.currentFirestoreCollection$.pipe(filterMaybe()); readonly loader$: Observable> = this.currentFirestoreCollection$.pipe( switchMap((collection) => @@ -104,5 +104,5 @@ export class AbstractDbxFirebaseCollectionStore> = this.loader$.pipe(switchMap((x) => x.accumulator$)); readonly pageLoadingState$: Observable> = this.loader$.pipe(switchMap((x) => x.pageLoadingState$)); - readonly setFirestoreCollection = this.updater((state, firestoreCollection: FirestoreCollection | null | undefined) => ({ ...state, firestoreCollection })); + readonly setFirestoreCollection = this.updater((state, firestoreCollection: FirestoreCollectionLike | null | undefined) => ({ ...state, firestoreCollection })); } diff --git a/packages/dbx-firebase/src/lib/model/store/store.subcollection.directive.ts b/packages/dbx-firebase/src/lib/model/store/store.subcollection.directive.ts new file mode 100644 index 000000000..274cf02fe --- /dev/null +++ b/packages/dbx-firebase/src/lib/model/store/store.subcollection.directive.ts @@ -0,0 +1,41 @@ +import { Directive, forwardRef, Input, Provider, Type } from '@angular/core'; +import { FirestoreDocument } from '@dereekb/firebase'; +import { Maybe } from '@dereekb/util'; +import { DbxFirebaseCollectionStoreDirective, provideDbxFirebaseCollectionStoreDirective } from './store.collection.directive'; +import { DbxFirebaseCollectionWithParentStore, DbxFirebaseComponentStoreWithParentSourceMode } from './store.subcollection'; + +/** + * Abstract directive that contains a DbxFirebaseCollectionWithParentStore and provides an interface for communicating with other directives. + */ +@Directive() +export abstract class DbxFirebaseCollectionWithParentStoreDirective = FirestoreDocument, PD extends FirestoreDocument = FirestoreDocument, S extends DbxFirebaseCollectionWithParentStore = DbxFirebaseCollectionWithParentStore> extends DbxFirebaseCollectionStoreDirective { + // MARK: Inputs + @Input() + set sourceMode(sourceMode: Maybe) { + this.store.setSourceMode(sourceMode); + } +} + +/* eslint-disable @typescript-eslint/no-explicit-any */ +// The use of any here does not degrade the type-safety; we want to simply match that the Store type S is used in the Directive type C to provide it. + +/** + * Configures providers for a DbxFirebaseCollectionWithParentStoreDirective. + * + * Can optionally also provide the actual store type to include in the providers array so it is instantiated by Angular. + * + * @param sourceType + */ +export function provideDbxFirebaseCollectionWithParentStoreDirective>(sourceType: Type): Provider[]; +export function provideDbxFirebaseCollectionWithParentStoreDirective, C extends DbxFirebaseCollectionWithParentStoreDirective = DbxFirebaseCollectionWithParentStoreDirective>(sourceType: Type, storeType?: Type): Provider[]; +export function provideDbxFirebaseCollectionWithParentStoreDirective, C extends DbxFirebaseCollectionWithParentStoreDirective = DbxFirebaseCollectionWithParentStoreDirective>(sourceType: Type, storeType?: Type): Provider[] { + const providers: Provider[] = [ + ...provideDbxFirebaseCollectionStoreDirective(sourceType, storeType), + { + provide: DbxFirebaseCollectionWithParentStoreDirective, + useExisting: forwardRef(() => sourceType) + } + ]; + + return providers; +} diff --git a/packages/dbx-firebase/src/lib/model/store/store.subcollection.document.ts b/packages/dbx-firebase/src/lib/model/store/store.subcollection.document.ts index 8997c70ab..fd5641976 100644 --- a/packages/dbx-firebase/src/lib/model/store/store.subcollection.document.ts +++ b/packages/dbx-firebase/src/lib/model/store/store.subcollection.document.ts @@ -1,26 +1,46 @@ import { filterMaybe } from '@dereekb/rxjs'; import { Injectable } from '@angular/core'; -import { Observable, shareReplay, distinctUntilChanged, map } from 'rxjs'; -import { FirestoreDocument, FirestoreCollectionWithParentFactory } from '@dereekb/firebase'; +import { Observable, shareReplay, distinctUntilChanged, map, NEVER, switchMap, tap, Subscription } from 'rxjs'; +import { FirestoreDocument, FirestoreCollectionWithParentFactory, FirestoreCollection } from '@dereekb/firebase'; import { Maybe } from '@dereekb/util'; import { AbstractDbxFirebaseDocumentStore, DbxFirebaseDocumentStore, DbxFirebaseDocumentStoreContextState } from './store.document'; -import { DbxFirebaseComponentStoreWithParentSetParentEffectFunction, setParentEffect, DbxFirebaseComponentStoreWithParentSetParentStoreEffectFunction, setParentStoreEffect, DbxFirebaseComponentStoreWithParent, DbxFirebaseComponentStoreWithParentContextState } from './store.subcollection.rxjs'; - -export interface DbxFirebaseDocumentWithParentStore = FirestoreDocument, PD extends FirestoreDocument = FirestoreDocument> extends DbxFirebaseDocumentStore, DbxFirebaseComponentStoreWithParent {} +import { DbxFirebaseComponentStoreWithParentSetParentStoreEffectFunction, setParentStoreEffect, DbxFirebaseComponentStoreWithParent, DbxFirebaseComponentStoreWithParentContextState, DbxFirebaseComponentStoreWithParentSetParentEffectFunction } from './store.subcollection.rxjs'; export interface DbxFirebaseDocumentWithParentStoreContextState = FirestoreDocument, PD extends FirestoreDocument = FirestoreDocument> extends DbxFirebaseDocumentStoreContextState, DbxFirebaseComponentStoreWithParentContextState {} +export interface DbxFirebaseDocumentWithParentStore = FirestoreDocument, PD extends FirestoreDocument = FirestoreDocument> extends DbxFirebaseDocumentStore, DbxFirebaseComponentStoreWithParent> {} + /** * Abstract DbxFirebaseDocumentStore that has a parent document from which is derives it's FiresbaseCollection from. */ @Injectable() -export class AbstractDbxFirebaseDocumentWithParentStore = FirestoreDocument, PD extends FirestoreDocument = FirestoreDocument, C extends DbxFirebaseDocumentWithParentStoreContextState = DbxFirebaseDocumentWithParentStoreContextState> - extends AbstractDbxFirebaseDocumentStore - implements DbxFirebaseDocumentWithParentStore -{ +export class AbstractDbxFirebaseDocumentWithParentStore = FirestoreDocument, PD extends FirestoreDocument = FirestoreDocument, C extends DbxFirebaseDocumentWithParentStoreContextState = DbxFirebaseDocumentWithParentStoreContextState> extends AbstractDbxFirebaseDocumentStore implements DbxFirebaseDocumentWithParentStore { // MARK: Effects - readonly setParent: DbxFirebaseComponentStoreWithParentSetParentEffectFunction = setParentEffect(this); readonly setParentStore: DbxFirebaseComponentStoreWithParentSetParentStoreEffectFunction = setParentStoreEffect(this); + readonly setParent: DbxFirebaseComponentStoreWithParentSetParentEffectFunction = this.effect((input: Observable>) => { + return input.pipe( + switchMap((parent) => { + this._setParentDocument(parent); + + if (parent) { + return this.collectionFactory$.pipe( + tap((collectionFactory) => { + const collection = collectionFactory(parent); + this.setFirestoreCollection(collection); + }) + ); + } else { + // clear the current collection + this.setFirestoreCollection(undefined); + + // do nothing until a parent is returned. + return NEVER; + } + }) + ); + }); + + readonly _setParent = this.setParent; // MARK: Accessors readonly currentParent$: Observable> = this.state$.pipe( diff --git a/packages/dbx-firebase/src/lib/model/store/store.subcollection.rxjs.ts b/packages/dbx-firebase/src/lib/model/store/store.subcollection.rxjs.ts index 188bd6bb6..adde2f2d4 100644 --- a/packages/dbx-firebase/src/lib/model/store/store.subcollection.rxjs.ts +++ b/packages/dbx-firebase/src/lib/model/store/store.subcollection.rxjs.ts @@ -1,9 +1,9 @@ import { LockSetComponentStore } from '@dereekb/dbx-core'; -import { FirestoreCollection, FirestoreCollectionWithParentFactory, FirestoreDocument } from '@dereekb/firebase'; +import { FirestoreCollectionGroup, FirestoreCollectionLike, FirestoreCollectionWithParentFactory, FirestoreDocument } from '@dereekb/firebase'; import { cleanup, ObservableOrValue } from '@dereekb/rxjs'; import { Maybe } from '@dereekb/util'; import { ComponentStore } from '@ngrx/component-store'; -import { map, Observable, Subscription, NEVER, switchMap, tap } from 'rxjs'; +import { map, NEVER, Observable, Subscription, switchMap, tap } from 'rxjs'; import { DbxFirebaseDocumentStore } from './store.document'; export interface DbxFirebaseComponentStoreWithParentContextState = FirestoreDocument, PD extends FirestoreDocument = FirestoreDocument> { @@ -11,26 +11,28 @@ export interface DbxFirebaseComponentStoreWithParentContextState>; } +export type DbxFirebaseComponentStoreSetParentEffectFunction = (parent: Observable>) => Subscription; export type DbxFirebaseComponentStoreWithParentSetParentEffectFunction = (observableOrValue: ObservableOrValue>) => Subscription; export type DbxFirebaseComponentStoreWithParentSetParentStoreEffectFunction = FirestoreDocument> = (observableOrValue: ObservableOrValue>) => Subscription; -export interface DbxFirebaseComponentStoreWithParent = FirestoreDocument, PD extends FirestoreDocument = FirestoreDocument> extends LockSetComponentStore, Pick>, 'effect'> { +export interface DbxFirebaseComponentStoreWithParent = FirestoreDocument, PD extends FirestoreDocument = FirestoreDocument, A extends FirestoreCollectionLike = FirestoreCollectionLike> extends LockSetComponentStore, Pick>, 'effect'> { readonly currentParent$: Observable>; readonly parent$: Observable; + readonly currentCollectionFactory$: Observable>>; readonly collectionFactory$: Observable>; - readonly setParent: DbxFirebaseComponentStoreWithParentSetParentEffectFunction; + readonly _setParent: DbxFirebaseComponentStoreSetParentEffectFunction; readonly _setParentDocument: (() => void) | ((observableOrValue: ObservableOrValue>) => Subscription); - readonly setFirestoreCollection: (() => void) | ((observableOrValue: ObservableOrValue>>) => Subscription); + readonly setFirestoreCollection: (() => void) | ((observableOrValue: ObservableOrValue>) => Subscription); } -export function setParentStoreEffect = FirestoreDocument, PD extends FirestoreDocument = FirestoreDocument>(store: DbxFirebaseComponentStoreWithParent): DbxFirebaseComponentStoreWithParentSetParentStoreEffectFunction { +export function setParentStoreEffect = FirestoreDocument, PD extends FirestoreDocument = FirestoreDocument, A extends FirestoreCollectionLike = FirestoreCollectionLike>(store: DbxFirebaseComponentStoreWithParent): DbxFirebaseComponentStoreWithParentSetParentStoreEffectFunction { return store.effect((input: Observable>>) => { return input.pipe( map((parentStore) => { let result: Maybe; if (parentStore) { - result = store.setParent(parentStore.currentDocument$) as Subscription; + result = store._setParent(parentStore.currentDocument$) as Subscription; } else { result = undefined; } @@ -48,28 +50,3 @@ export function setParentStoreEffect = Fir ); }); } - -export function setParentEffect = FirestoreDocument, PD extends FirestoreDocument = FirestoreDocument>(store: DbxFirebaseComponentStoreWithParent): DbxFirebaseComponentStoreWithParentSetParentEffectFunction { - return store.effect((input: Observable>) => { - return input.pipe( - switchMap((parent) => { - store._setParentDocument(parent); - - if (parent) { - return store.collectionFactory$.pipe( - tap((collectionFactory) => { - const collection = collectionFactory(parent); - store.setFirestoreCollection(collection); - }) - ); - } else { - // clear the current collection - store.setFirestoreCollection(undefined); - - // do nothing until a parent is returned. - return NEVER; - } - }) - ); - }); -} diff --git a/packages/dbx-firebase/src/lib/model/store/store.subcollection.spec.ts b/packages/dbx-firebase/src/lib/model/store/store.subcollection.spec.ts index 2aa900d61..ebbfe0591 100644 --- a/packages/dbx-firebase/src/lib/model/store/store.subcollection.spec.ts +++ b/packages/dbx-firebase/src/lib/model/store/store.subcollection.spec.ts @@ -64,6 +64,28 @@ describe('AbstractDbxFirebaseCollectionWithParentStore', () => { }); }); + describe('with collection group', () => { + beforeEach(() => { + store.setCollectionGroup(f.instance.mockItemSubItemCollectionGroup); + store.setSourceMode('group'); + }); + + it('should not load while a collection group is not set.', (done) => { + store.setCollectionGroup(undefined); + sub.subscription = store.firestoreIteration$.pipe(timeout({ first: 500, with: () => of(false) }), first()).subscribe((result) => { + expect(result).toBe(false); + done(); + }); + }); + + it('should provide a firestoreIteration$', (done) => { + sub.subscription = store.firestoreIteration$.pipe(first()).subscribe((result) => { + expect(result).toBeDefined(); + done(); + }); + }); + }); + describe('loader$', () => { describe('with parent loaded', () => { beforeEach(() => { diff --git a/packages/dbx-firebase/src/lib/model/store/store.subcollection.ts b/packages/dbx-firebase/src/lib/model/store/store.subcollection.ts index 361ac8d4b..ffb4ed141 100644 --- a/packages/dbx-firebase/src/lib/model/store/store.subcollection.ts +++ b/packages/dbx-firebase/src/lib/model/store/store.subcollection.ts @@ -1,26 +1,75 @@ -import { filterMaybe } from '@dereekb/rxjs'; -import { Injectable } from '@angular/core'; -import { Observable, shareReplay, distinctUntilChanged, map } from 'rxjs'; -import { FirestoreCollectionWithParentFactory, FirestoreDocument } from '@dereekb/firebase'; import { Maybe } from '@dereekb/util'; +import { Inject, Injectable, Optional } from '@angular/core'; +import { filterMaybe, ObservableOrValue } from '@dereekb/rxjs'; +import { Observable, shareReplay, distinctUntilChanged, map, Subscription, NEVER, switchMap, tap } from 'rxjs'; +import { FirestoreCollectionGroup, FirestoreCollectionWithParentFactory, FirestoreDocument } from '@dereekb/firebase'; import { AbstractDbxFirebaseCollectionStore, DbxFirebaseCollectionStore, DbxFirebaseCollectionStoreContextState } from './store.collection'; -import { DbxFirebaseComponentStoreWithParent, DbxFirebaseComponentStoreWithParentContextState, DbxFirebaseComponentStoreWithParentSetParentEffectFunction, DbxFirebaseComponentStoreWithParentSetParentStoreEffectFunction, setParentEffect, setParentStoreEffect } from './store.subcollection.rxjs'; +import { DbxFirebaseComponentStoreSetParentEffectFunction, DbxFirebaseComponentStoreWithParent, DbxFirebaseComponentStoreWithParentContextState, DbxFirebaseComponentStoreWithParentSetParentStoreEffectFunction, setParentStoreEffect } from './store.subcollection.rxjs'; -export interface DbxFirebaseCollectionWithParentStore = FirestoreDocument, PD extends FirestoreDocument = FirestoreDocument> extends DbxFirebaseCollectionStore, DbxFirebaseComponentStoreWithParent {} +/** + * Whether or not to load values from a parent or a group. + */ +export type DbxFirebaseComponentStoreWithParentSourceMode = 'parent' | 'group'; + +export interface DbxFirebaseCollectionWithParentStoreContextState = FirestoreDocument, PD extends FirestoreDocument = FirestoreDocument> extends DbxFirebaseCollectionStoreContextState, DbxFirebaseComponentStoreWithParentContextState { + readonly mode?: Maybe; + readonly collectionGroup?: Maybe>; +} -export interface DbxFirebaseCollectionWithParentStoreContextState = FirestoreDocument, PD extends FirestoreDocument = FirestoreDocument> extends DbxFirebaseCollectionStoreContextState, DbxFirebaseComponentStoreWithParentContextState {} +export type DbxFirebaseComponentStoreWithParentSetParentSourceModeFunction = (observableOrValue: ObservableOrValue>) => Subscription; + +export interface DbxFirebaseCollectionWithParentStore = FirestoreDocument, PD extends FirestoreDocument = FirestoreDocument> extends DbxFirebaseCollectionStore, DbxFirebaseComponentStoreWithParent { + readonly setSourceMode: DbxFirebaseComponentStoreWithParentSetParentSourceModeFunction; + readonly setCollectionGroup: (observableOrValue: FirestoreCollectionGroup | Observable>) => Subscription; + readonly currentCollectionGroup$: Observable>>; + readonly collectionGroup$: Observable>; +} /** * Abstract DbxFirebaseCollectionStore that has a parent document from which is derives it's FiresbaseCollection from. */ @Injectable() -export class AbstractDbxFirebaseCollectionWithParentStore = FirestoreDocument, PD extends FirestoreDocument = FirestoreDocument, C extends DbxFirebaseCollectionWithParentStoreContextState = DbxFirebaseCollectionWithParentStoreContextState> - extends AbstractDbxFirebaseCollectionStore - implements DbxFirebaseCollectionWithParentStore -{ +export class AbstractDbxFirebaseCollectionWithParentStore = FirestoreDocument, PD extends FirestoreDocument = FirestoreDocument, C extends DbxFirebaseCollectionWithParentStoreContextState = DbxFirebaseCollectionWithParentStoreContextState> extends AbstractDbxFirebaseCollectionStore implements DbxFirebaseCollectionWithParentStore { + constructor(@Inject(null) @Optional() state: C, @Inject(null) @Optional() defaultSourceMode?: DbxFirebaseComponentStoreWithParentSourceMode) { + super(state); + this.setSourceMode(defaultSourceMode || 'parent'); + } + // MARK: Effects - readonly setParent: DbxFirebaseComponentStoreWithParentSetParentEffectFunction = setParentEffect(this); readonly setParentStore: DbxFirebaseComponentStoreWithParentSetParentStoreEffectFunction = setParentStoreEffect(this); + readonly setSourceMode: DbxFirebaseComponentStoreWithParentSetParentSourceModeFunction = this.effect((input: Observable>) => { + return input.pipe( + distinctUntilChanged(), + switchMap((inputMode) => { + const mode = inputMode?.toLowerCase() ?? 'parent'; // default to parent mode + + if (mode === 'group') { + return this.currentCollectionGroup$.pipe( + tap((collectionGroup) => { + this.setFirestoreCollection(collectionGroup); + }) + ); + } else { + // parent document collection + return this.currentParent$.pipe( + switchMap((parent) => { + if (parent) { + return this.collectionFactory$.pipe( + tap((collectionFactory) => { + const collection = collectionFactory(parent); + this.setFirestoreCollection(collection); + }) + ); + } else { + this.setFirestoreCollection(undefined); + return NEVER; + } + }) + ); + } + }) + ); + }); // MARK: Accessors readonly currentParent$: Observable> = this.state$.pipe( @@ -39,14 +88,28 @@ export class AbstractDbxFirebaseCollectionWithParentStore> = this.currentCollectionFactory$.pipe(filterMaybe()); + readonly currentCollectionGroup$: Observable>> = this.state$.pipe( + map((x) => x.collectionGroup), + distinctUntilChanged(), + shareReplay(1) + ); + + readonly collectionGroup$: Observable> = this.currentCollectionGroup$.pipe(filterMaybe()); + // MARK: State Changes /** * Sets the collection factory function to use. */ readonly setCollectionFactory = this.updater((state, collectionFactory: FirestoreCollectionWithParentFactory) => ({ ...state, collectionFactory })); + /** + * Sets the collection group to use. + */ + readonly setCollectionGroup = this.updater((state, collectionGroup: Maybe>) => ({ ...state, collectionGroup })) as (observableOrValue: Maybe> | Observable>>) => Subscription; + /** * Sets the parent on the current state. */ readonly _setParentDocument = this.updater((state, parent: Maybe) => ({ ...state, parent })); + readonly _setParent = this._setParentDocument as DbxFirebaseComponentStoreSetParentEffectFunction; } diff --git a/packages/dbx-form/CHANGELOG.md b/packages/dbx-form/CHANGELOG.md index 19ee1622a..e1b6d4131 100644 --- a/packages/dbx-form/CHANGELOG.md +++ b/packages/dbx-form/CHANGELOG.md @@ -2,6 +2,10 @@ This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver). +# [5.2.0](https://github.com/dereekb/dbx-components/compare/v5.1.0-dev...v5.2.0) (2022-05-29) + + + # [5.1.0](https://github.com/dereekb/dbx-components/compare/v5.0.1-dev...v5.1.0) (2022-05-27) diff --git a/packages/dbx-form/package.json b/packages/dbx-form/package.json index 8d52df9ec..184fa5ea7 100644 --- a/packages/dbx-form/package.json +++ b/packages/dbx-form/package.json @@ -1,6 +1,6 @@ { "name": "@dereekb/dbx-form", - "version": "5.1.0", + "version": "5.2.0", "peerDependencies": { "@angular/common": "^13.0.0", "@angular/core": "^13.0.0" diff --git a/packages/dbx-form/src/lib/formly/field/selection/searchable/searchable.field.ts b/packages/dbx-form/src/lib/formly/field/selection/searchable/searchable.field.ts index d4c8036fc..387749dd5 100644 --- a/packages/dbx-form/src/lib/formly/field/selection/searchable/searchable.field.ts +++ b/packages/dbx-form/src/lib/formly/field/selection/searchable/searchable.field.ts @@ -12,13 +12,7 @@ import { SearchableTextValueFieldsFieldConfig } from './searchable.text.field.co * @param param0 * @returns */ -export function makeMetaFilterSearchableFieldValueDisplayFn({ - loadMetaForValues, - makeDisplayForValues -}: { - loadMetaForValues: (values: SearchableValueFieldValue[]) => Observable[]>; - makeDisplayForValues: (values: SearchableValueFieldValue[]) => Observable[]>; -}): SearchableValueFieldDisplayFn { +export function makeMetaFilterSearchableFieldValueDisplayFn({ loadMetaForValues, makeDisplayForValues }: { loadMetaForValues: (values: SearchableValueFieldValue[]) => Observable[]>; makeDisplayForValues: (values: SearchableValueFieldValue[]) => Observable[]> }): SearchableValueFieldDisplayFn { return (values: SearchableValueFieldValue[]) => { const { included: loaded, excluded: needLoading } = separateValues(values, (x) => Boolean(x.meta)); let allValues: Observable[]>; diff --git a/packages/dbx-web/CHANGELOG.md b/packages/dbx-web/CHANGELOG.md index 66d3d364d..baa849499 100644 --- a/packages/dbx-web/CHANGELOG.md +++ b/packages/dbx-web/CHANGELOG.md @@ -2,6 +2,10 @@ This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver). +# [5.2.0](https://github.com/dereekb/dbx-components/compare/v5.1.0-dev...v5.2.0) (2022-05-29) + + + # [5.1.0](https://github.com/dereekb/dbx-components/compare/v5.0.1-dev...v5.1.0) (2022-05-27) diff --git a/packages/dbx-web/package.json b/packages/dbx-web/package.json index 09586fce1..fa433ef27 100644 --- a/packages/dbx-web/package.json +++ b/packages/dbx-web/package.json @@ -1,6 +1,6 @@ { "name": "@dereekb/dbx-web", - "version": "5.1.0", + "version": "5.2.0", "peerDependencies": { "@angular/common": "^13.0.0", "@angular/core": "^13.0.0" diff --git a/packages/dbx-web/src/lib/button/progress/spinner.button.component.html b/packages/dbx-web/src/lib/button/progress/spinner.button.component.html index 30788b9ac..a09d080de 100644 --- a/packages/dbx-web/src/lib/button/progress/spinner.button.component.html +++ b/packages/dbx-web/src/lib/button/progress/spinner.button.component.html @@ -1,17 +1,4 @@ -