Skip to content

Commit

Permalink
feat: updated firestoreModelKey()
Browse files Browse the repository at this point in the history
- added FirestoreModelIdentityWithParent
- renamed firestoreModelKey/firestoreModelId snapshot functions to firestoreModelKeyString/firestoreModelIdString
  • Loading branch information
dereekb committed Jun 14, 2022
1 parent c597eb9 commit 1459a15
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export function guestbookFirestoreCollection(firestoreContext: FirestoreContext)
}

// MARK: Guestbook Entry
export const guestbookEntryIdentity = firestoreModelIdentity('guestbookEntry', 'gbe');
export const guestbookEntryIdentity = firestoreModelIdentity(guestbookIdentity, 'guestbookEntry', 'gbe');

export interface GuestbookEntry extends UserRelated, UserRelatedById {
/**
Expand Down
2 changes: 1 addition & 1 deletion components/demo-firebase/src/lib/models/profile/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export function profileFirestoreCollection(firestoreContext: FirestoreContext):
}

// MARK: Profile Private Data
export const profilePrivateDataIdentity = firestoreModelIdentity('profilePrivate', 'prp');
export const profilePrivateDataIdentity = firestoreModelIdentity(profileIdentity, 'profilePrivate', 'prp');

export interface ProfilePrivateData {
/**
Expand Down
5 changes: 3 additions & 2 deletions packages/firebase-server/src/lib/nest/model/model.param.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { FirestoreModelKey, FirestoreModelKeyRef } from '@dereekb/firebase';
import { Expose } from 'class-transformer';
import { IsString } from 'class-validator';
import { IsNotEmpty, IsString } from 'class-validator';

/**
* Simple annotated params that
* Simple annotated params that implements FirestoreModelKeyRef.
*/
export class TargetModelParams implements FirestoreModelKeyRef {
@Expose()
@IsNotEmpty()
@IsString()
key!: FirestoreModelKey;
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { firestoreModelIdentity } from './collection';
import { firestoreModelIdentity, firestoreModelKey } from './collection';

describe('firestoreModelIdentity()', () => {
const testName = 'testNameWithPieces';
Expand Down Expand Up @@ -28,4 +28,49 @@ describe('firestoreModelIdentity()', () => {
expect(identity.collection === testCollectionName).toBe(true);
});
});

describe('with a parent', () => {
const parent = firestoreModelIdentity('parent');

describe('with only a model name', () => {
it('should generate a default collection name', () => {
const identity = firestoreModelIdentity(parent, testName);
expect(identity.collection).toBe(testName.toLowerCase());
expect(identity.parent).toBe(parent);
});

it('should compile', () => {
const identity = firestoreModelIdentity(parent, testName);
expect(identity.collection === 'testnamewithpieces').toBe(true);
expect(identity.parent).toBe(parent);
});
});

describe('with a model and collection name', () => {
const testCollectionName = 'tnwp';

it('should generate a default collection name', () => {
const identity = firestoreModelIdentity(parent, testName, testCollectionName);
expect(identity.collection).toBe(testCollectionName);
expect(identity.parent).toBe(parent);
});

it('should compile', () => {
const identity = firestoreModelIdentity(parent, testName, testCollectionName);
expect(identity.collection === testCollectionName).toBe(true);
expect(identity.parent).toBe(parent);
});
});
});
});

describe('firestoreModelKey', () => {
const identity = firestoreModelIdentity('identity', 'i');

it('should create a FirestoreModelKey for the input identity and FirestoreModelKey', () => {
const key = 'hello';
const result = firestoreModelKey(identity, key);

expect(result).toBe(`i/${key}`);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,32 @@ export type FirestoreModelName = ModelTypeString;
*/
export type FirestoreCollectionName = string;

export type FirestoreModelIdentityType = 'root' | 'nested';

/**
* A firestore model's identity
*/
export type FirestoreModelIdentity<M extends FirestoreModelName = FirestoreModelName, C extends FirestoreCollectionName = FirestoreCollectionName> = {
readonly type: FirestoreModelIdentityType;
readonly model: M;
readonly collection: C;
};

/**
* A root-level FirestoreModelIdentity
*/
export type RootFirestoreModelIdentity<M extends FirestoreModelName = FirestoreModelName, C extends FirestoreCollectionName = FirestoreCollectionName> = FirestoreModelIdentity<M, C> & {
readonly type: 'root';
};

/**
* A nested FirestoreModelIdentity with a parent.
*/
export type FirestoreModelIdentityWithParent<P extends FirestoreModelIdentity<string, string>, M extends FirestoreModelName = FirestoreModelName, C extends FirestoreCollectionName = FirestoreCollectionName> = FirestoreModelIdentity<M, C> & {
readonly type: 'nested';
readonly parent: P;
};

/**
* A default collection name derived from the model name.
*/
Expand All @@ -55,13 +73,25 @@ export type FirestoreModelNames<I extends FirestoreModelIdentity> = I extends Fi
* @param modelName
* @returns
*/
export function firestoreModelIdentity<M extends FirestoreModelName>(modelName: M): FirestoreModelIdentity<M, FirestoreModelDefaultCollectionName<M>>;
export function firestoreModelIdentity<M extends FirestoreModelName, C extends FirestoreCollectionName = FirestoreCollectionName>(modelName: M, collectionName: C): FirestoreModelIdentity<M, C>;
export function firestoreModelIdentity<M extends FirestoreModelName, C extends FirestoreCollectionName = FirestoreCollectionName>(modelName: M, collectionName?: C): FirestoreModelIdentity<M, C> {
return {
collection: collectionName ?? (modelName.toLowerCase() as C),
model: modelName
};
export function firestoreModelIdentity<M extends FirestoreModelName>(modelName: M): RootFirestoreModelIdentity<M, FirestoreModelDefaultCollectionName<M>>;
export function firestoreModelIdentity<P extends FirestoreModelIdentity<string, string>, M extends FirestoreModelName>(parent: P, modelName: M): FirestoreModelIdentityWithParent<P, M, FirestoreModelDefaultCollectionName<M>>;
export function firestoreModelIdentity<M extends FirestoreModelName, C extends FirestoreCollectionName = FirestoreCollectionName>(modelName: M, collectionName: C): RootFirestoreModelIdentity<M, C>;
export function firestoreModelIdentity<P extends FirestoreModelIdentity<string, string>, M extends FirestoreModelName, C extends FirestoreCollectionName = FirestoreCollectionName>(parent: P, modelName: M, collectionName: C): FirestoreModelIdentityWithParent<P, M, C>;
export function firestoreModelIdentity<P extends FirestoreModelIdentity<string, string>, M extends FirestoreModelName, C extends FirestoreCollectionName = FirestoreCollectionName>(parentOrModelName: P | M, collectionNameOrModelName?: M | C, collectionName?: C): FirestoreModelIdentityWithParent<P, M, C> | RootFirestoreModelIdentity<M, C> {
if (typeof parentOrModelName === 'object') {
return {
type: 'nested',
parent: parentOrModelName as P,
collection: (collectionName as C) ?? ((collectionNameOrModelName as M).toLowerCase() as C),
model: collectionNameOrModelName as M
};
} else {
return {
type: 'root',
collection: (collectionNameOrModelName as C) ?? (parentOrModelName.toLowerCase() as C),
model: parentOrModelName
};
}
}

/**
Expand Down Expand Up @@ -124,6 +154,18 @@ export interface FirestoreModelIdRef {
* collection/12345/subcollection/67890
*/
export type FirestoreModelKey = ModelKey;
export type FirestoreIdentityModelKey<I extends RootFirestoreModelIdentity, K extends FirestoreModelId = FirestoreModelId> = I extends RootFirestoreModelIdentity<infer M, infer C> ? `${C}/${K}` : never;

/**
* Creates a firestoreModelKey for root identities.
*
* @param identity
* @param id
* @returns
*/
export function firestoreModelKey<I extends RootFirestoreModelIdentity, K extends FirestoreModelId = FirestoreModelId>(identity: I, id: K): FirestoreIdentityModelKey<I, K> {
return `${identity.collection}/${id}` as FirestoreIdentityModelKey<I, K>;
}

/**
* Reference to a FirestoreModelKey
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,8 @@ export function optionalFirestoreUID() {
return optionalFirestoreString();
}

export const firestoreModelKey = firestoreString();
export const firestoreModelId = firestoreString();
export const firestoreModelKeyString = firestoreString();
export const firestoreModelIdString = firestoreString();

export type FirestoreDateFieldConfig = DefaultMapConfiguredFirestoreFieldConfig<Date, string> & {
saveDefaultAsNow?: boolean;
Expand Down
8 changes: 4 additions & 4 deletions packages/firebase/test/src/lib/common/firestore.mock.item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export function mockItemFirestoreCollection(firestoreContext: FirestoreContext):
}

// MARK: MockItemPrivate
export const mockItemPrivateIdentity = firestoreModelIdentity('mockItemPrivate', 'mip');
export const mockItemPrivateIdentity = firestoreModelIdentity(mockItemIdentity, 'mockItemPrivate', 'mip');

/**
* Private data for each MockItem.
Expand Down Expand Up @@ -182,7 +182,7 @@ export function mockItemPrivateFirestoreCollectionGroup(firestoreContext: Firest
}

// MARK: MockItemUser
export const mockItemUserIdentity = firestoreModelIdentity('mockItemUser', 'miu');
export const mockItemUserIdentity = firestoreModelIdentity(mockItemIdentity, 'mockItemUser', 'miu');

/**
* An item associated per user to this item.
Expand Down Expand Up @@ -269,7 +269,7 @@ export function mockItemUserFirestoreCollectionGroup(firestoreContext: Firestore
}

// MARK: MockItemSubItem
export const mockItemSubItemIdentity = firestoreModelIdentity('mockItemSub', 'misi');
export const mockItemSubItemIdentity = firestoreModelIdentity(mockItemIdentity, 'mockItemSub', 'misi');

/**
* Data for a sub item in our firestore collection.
Expand Down Expand Up @@ -341,7 +341,7 @@ export function mockItemSubItemFirestoreCollectionGroup(firestoreContext: Firest
}

// MARK: Sub-Sub Item
export const mockItemSubItemDeepIdentity = firestoreModelIdentity('mockItemSubItemDeep', 'misid');
export const mockItemSubItemDeepIdentity = firestoreModelIdentity(mockItemSubItemIdentity, 'mockItemSubItemDeep', 'misid');

/**
* Data for a sub item in our firestore collection.
Expand Down

0 comments on commit 1459a15

Please sign in to comment.