Skip to content

Commit

Permalink
feat: added FirebaseStorageContext
Browse files Browse the repository at this point in the history
  • Loading branch information
dereekb committed Jul 1, 2022
1 parent 8c902ab commit 5a30d46
Show file tree
Hide file tree
Showing 46 changed files with 390 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export function docRefForPath<T>(start: DocRefForPathInput, path?: string, pathS
return doc as DocumentReference<T>;
}

export function firestoreClientAccessorDriver(): FirestoreAccessorDriver {
export function googleCloudFirestoreAccessorDriver(): FirestoreAccessorDriver {
return {
doc: <T>(collection: CollectionReference<T>, path?: string, ...pathSegments: string[]) => docRefForPath(collection as GoogleCloudCollectionReference, path, pathSegments) as DocumentReference<T>,
docAtPath: <T>(firestore: Firestore, fullPath: string) => (firestore as GoogleCloudFirestore).doc(fullPath) as DocumentReference<T>,
Expand Down
2 changes: 1 addition & 1 deletion packages/firebase-server/src/lib/firestore/driver.query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export function firestoreClientQueryConstraintFunctionsDriver(): FirestoreQueryC
});
}

export function firestoreClientQueryDriver(): FirestoreQueryDriver {
export function googleCloudFirestoreQueryDriver(): FirestoreQueryDriver {
return {
...firestoreClientQueryConstraintFunctionsDriver(),
getDocs<T>(query: Query<T>, transaction?: Transaction): Promise<QuerySnapshot<T>> {
Expand Down
8 changes: 4 additions & 4 deletions packages/firebase-server/src/lib/firestore/driver.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { FirestoreDrivers } from '@dereekb/firebase';
import { firestoreClientAccessorDriver } from './driver.accessor';
import { firestoreClientQueryDriver } from './driver.query';
import { googleCloudFirestoreAccessorDriver } from './driver.accessor';
import { googleCloudFirestoreQueryDriver } from './driver.query';

export type GoogleCloudFirestoreDrivers = FirestoreDrivers;

export function googleCloudFirestoreDrivers(): GoogleCloudFirestoreDrivers {
return {
driverIdentifier: '@google-cloud/firestore',
driverType: 'production',
firestoreAccessorDriver: firestoreClientAccessorDriver(),
firestoreQueryDriver: firestoreClientQueryDriver()
firestoreAccessorDriver: googleCloudFirestoreAccessorDriver(),
firestoreQueryDriver: googleCloudFirestoreQueryDriver()
};
}
2 changes: 1 addition & 1 deletion packages/firebase-server/src/lib/firestore/firestore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ import { FirestoreContextFactory, firestoreContextFactory } from '@dereekb/fireb
import { googleCloudFirestoreDrivers } from './driver';

/**
* Creates a FirestoreContextFactory that uses the @firebase/firebase package.
* Creates a FirestoreContextFactory that uses the @'@google-cloud/firestore package.
*/
export const googleCloudFirestoreContextFactory: FirestoreContextFactory = firestoreContextFactory(googleCloudFirestoreDrivers());
1 change: 1 addition & 0 deletions packages/firebase-server/src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * from './firebase';
export * from './firestore';
export * from './function';
export * from './nest';
export * from './storage';
13 changes: 13 additions & 0 deletions packages/firebase-server/src/lib/storage/driver.accessor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { FirebaseStorage, FirebaseStorageAccessorDriver, FirebaseStorageAccessorDriverGetDownloadUrlFunction, GoogleCloudStorageFilePath, StorageFilePath } from '@dereekb/firebase';
import { SlashPathFolder } from '@dereekb/util';
import { Storage as GoogleCloudStorage } from '@google-cloud/storage';

export function googleCloudStorageFileForStorageFilePath(storage: GoogleCloudStorage, path: StorageFilePath) {
return storage.bucket(path.bucketId).file(path.pathString);
}

export function googleCloudStorageFirebaseStorageAccessorDriver(): FirebaseStorageAccessorDriver {
return {
getDownloadUrl: async (storage: FirebaseStorage, path: StorageFilePath) => googleCloudStorageFileForStorageFilePath(storage as GoogleCloudStorage, path).publicUrl()
};
}
12 changes: 12 additions & 0 deletions packages/firebase-server/src/lib/storage/driver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { FirebaseStorageDrivers, FirestoreDrivers } from '@dereekb/firebase';
import { googleCloudStorageFirebaseStorageAccessorDriver } from './driver.accessor';

export type GoogleCloudFirebaseStorageDrivers = FirebaseStorageDrivers;

export function googleCloudFirebaseStorageDrivers(): GoogleCloudFirebaseStorageDrivers {
return {
driverIdentifier: '@google-cloud/storage',
driverType: 'production',
storageAccessorDriver: googleCloudStorageFirebaseStorageAccessorDriver()
};
}
4 changes: 4 additions & 0 deletions packages/firebase-server/src/lib/storage/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './driver';
export * from './driver.accessor';
export * from './storage.nest';
export * from './storage';
47 changes: 47 additions & 0 deletions packages/firebase-server/src/lib/storage/storage.nest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import * as admin from 'firebase-admin';
import { InjectionToken, Module, ModuleMetadata, Provider } from '@nestjs/common';
import { ClassLikeType } from '@dereekb/util';
import { FIREBASE_APP_TOKEN } from '../firebase/firebase.nest';
import { googleCloudFirebaseStorageContextFactory } from './storage';

// MARK: Tokens
/**
* Token to access the Storage.
*/
export const FIREBASE_STORAGE_TOKEN: InjectionToken = 'FIREBASE_STORAGE_TOKEN';

/**
* Token to access the root StorageContext for a server.
*/
export const FIREBASE_STORAGE_CONTEXT_TOKEN: InjectionToken = 'FIREBASE_STORAGE_CONTEXT_TOKEN';

/**
* Nest provider module for Firebase that provides a firestore, etc. from the firestore token.
*/
@Module({
providers: [
{
provide: FIREBASE_STORAGE_TOKEN,
useFactory: (app: admin.app.App) => app.storage(),
inject: [FIREBASE_APP_TOKEN]
}
],
exports: [FIREBASE_STORAGE_TOKEN]
})
export class FirebaseServerStorageModule {}

/**
* Nest provider module for firebase that includes the FirebaseServerStorageModule and provides a value for STORAGE_CONTEXT_TOKEN using the googleCloudStorageContextFactory.
*/
@Module({
imports: [FirebaseServerStorageModule],
providers: [
{
provide: FIREBASE_STORAGE_CONTEXT_TOKEN,
useFactory: googleCloudFirebaseStorageContextFactory,
inject: [FIREBASE_STORAGE_TOKEN]
}
],
exports: [FirebaseServerStorageModule, FIREBASE_STORAGE_CONTEXT_TOKEN]
})
export class FirebaseServerStorageContextModule {}
7 changes: 7 additions & 0 deletions packages/firebase-server/src/lib/storage/storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { FirebaseStorageContextFactory, firebaseStorageContextFactory, FirestoreContextFactory, firestoreContextFactory } from '@dereekb/firebase';
import { googleCloudFirebaseStorageDrivers } from './driver';

/**
* Creates a FirestoreContextFactory that uses the @google-cloud/storage package.
*/
export const googleCloudFirebaseStorageContextFactory: FirebaseStorageContextFactory = firebaseStorageContextFactory(googleCloudFirebaseStorageDrivers());
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Firestore } from '@google-cloud/firestore';
import { Auth } from 'firebase-admin/lib/auth/auth';
import { JestTestFirestoreContextFactory, makeTestingFirestoreDrivers, TestFirestoreContext, TestFirestoreContextFixture, TestFirestoreInstance } from '@dereekb/firebase/test';
import { AbstractJestTestContextFixture, JestBuildTestsWithContextFunction, jestTestContextBuilder, JestTestContextFactory, JestTestContextFixture, useJestContextFixture } from '@dereekb/util/test';
import { googleCloudFirestoreDrivers } from '@dereekb/firebase-server';
import { googleCloudFirebaseStorageDrivers } from '@dereekb/firebase-server';
import { GoogleCloudTestFirestoreInstance } from '../firestore/firestore';
import { generateNewProjectId, isAdminEnvironmentInitialized } from './firebase';
import { cachedGetter } from '@dereekb/util';
Expand Down Expand Up @@ -49,7 +49,7 @@ export class FirebaseAdminTestContextFixture extends AbstractJestTestContextFixt
// MARK: FirebaseAdminTestBuilder
export class FirebaseAdminTestContextInstance implements FirebaseAdminTestContext {
readonly getTestFirestoreInstance = cachedGetter(() => {
const drivers = makeTestingFirestoreDrivers(googleCloudFirestoreDrivers());
const drivers = makeTestingFirestoreDrivers(googleCloudFirebaseStorageDrivers());
return new GoogleCloudTestFirestoreInstance(drivers, this.firestore);
});

Expand Down
4 changes: 2 additions & 2 deletions packages/firebase-server/test/src/lib/firestore/firestore.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Firestore } from '@google-cloud/firestore';
import { TestFirestoreContext, TestFirestoreInstance, TestFirestoreContextFixture, TestingFirestoreDrivers, makeTestingFirestoreDrivers } from '@dereekb/firebase/test';
import { jestTestContextBuilder } from '@dereekb/util/test';
import { googleCloudFirestoreDrivers } from '@dereekb/firebase-server';
import { googleCloudFirebaseStorageDrivers } from '@dereekb/firebase-server';
import { firestoreContextFactory } from '@dereekb/firebase';

export interface GoogleCloudTestFirestoreConfig {
Expand Down Expand Up @@ -47,7 +47,7 @@ export const googleCloudTestFirestoreBuilder = jestTestContextBuilder<GoogleClou
},
buildFixture: () => new GoogleCloudTestFirestoreContextFixture(),
setupInstance: async (config) => {
const drivers = makeTestingFirestoreDrivers(googleCloudFirestoreDrivers());
const drivers = makeTestingFirestoreDrivers(googleCloudFirebaseStorageDrivers());

const projectId = `firebase-server-test-${new Date().getTime()}-${COUNTER++}`;
const firestore = new Firestore({
Expand Down
1 change: 1 addition & 0 deletions packages/firebase/src/lib/client/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './firestore';
export * from './function';
export * from './storage';
15 changes: 15 additions & 0 deletions packages/firebase/src/lib/client/storage/driver.accessor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { FirebaseStorageAccessorDriver, FirebaseStorageAccessorDriverGetDownloadUrlFunction } from '../../common/storage/driver/accessor';
import { getDownloadURL, FirebaseStorage as ClientFirebaseStorage, ref } from '@firebase/storage';
import { firebaseStorageFilePathFromStorageFilePath, GoogleCloudStorageFilePath, StorageBucketId, StorageBucketIdRef, StorageFilePath } from '../../common/storage/storage';
import { SlashPathFolder } from '@dereekb/util';
import { FirebaseStorage } from '../../common/storage/types';

export function firebaseStorageRefForStorageFilePath(storage: ClientFirebaseStorage, path: StorageFilePath) {
return ref(storage, firebaseStorageFilePathFromStorageFilePath(path));
}

export function firebaseStorageClientAccessorDriver(): FirebaseStorageAccessorDriver {
return {
getDownloadUrl: (storage: FirebaseStorage, path: StorageFilePath) => getDownloadURL(firebaseStorageRefForStorageFilePath(storage as ClientFirebaseStorage, path))
};
}
12 changes: 12 additions & 0 deletions packages/firebase/src/lib/client/storage/driver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { FirebaseStorageDrivers } from '../../common/storage/driver/driver';
import { firebaseStorageClientAccessorDriver } from './driver.accessor';

export type FirebaseStorageClientDrivers = FirebaseStorageDrivers;

export function firebaseStorageClientDrivers(): FirebaseStorageClientDrivers {
return {
driverIdentifier: '@firebase/storage',
driverType: 'production',
storageAccessorDriver: firebaseStorageClientAccessorDriver()
};
}
3 changes: 3 additions & 0 deletions packages/firebase/src/lib/client/storage/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './driver';
export * from './driver.accessor';
export * from './storage';
7 changes: 7 additions & 0 deletions packages/firebase/src/lib/client/storage/storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { FirebaseStorageContextFactory, firebaseStorageContextFactory } from '../../common/storage/context';
import { firebaseStorageClientDrivers } from './driver';

/**
* Creates a FirebaseStorageContextFactory that uses the client @firebase/storage package.
*/
export const clientFirebaseStorageContextFactory: FirebaseStorageContextFactory = firebaseStorageContextFactory(firebaseStorageClientDrivers());
2 changes: 1 addition & 1 deletion packages/firebase/src/lib/common/firestore/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { DocumentReference, CollectionReference, DocumentData, Firestore, Collec
import { QueryLikeReferenceRef } from './reference';

/**
* A @dereekb/firestore FirestoreContext. Wraps the main Firestore context and the drivers, as well as utility/convenience functions.
* A @dereekb/firebase FirestoreContext. Wraps the main Firestore context and the drivers, as well as utility/convenience functions.
*/
export interface FirestoreContext<F extends Firestore = Firestore> extends RunTransactionFactoryReference, WriteBatchFactoryReference {
readonly firestore: F;
Expand Down
1 change: 1 addition & 0 deletions packages/firebase/src/lib/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './auth';
export * from './firestore';
export * from './function';
export * from './model';
export * from './storage';
1 change: 1 addition & 0 deletions packages/firebase/src/lib/common/storage/accessor/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './path.model';
37 changes: 37 additions & 0 deletions packages/firebase/src/lib/common/storage/accessor/path.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { SlashPath, slashPathFactory, SlashPathFolder, FactoryWithRequiredInput } from '@dereekb/util';
import { readFirestoreModelKey, ReadFirestoreModelKeyInput } from '../../firestore/collection/collection';

export const BASE_MODEL_STORAGE_FILE_PATH: SlashPathFolder = '/model/';

/**
* Shared and configured slashPathFactory configuration for the model storage file path.
*/
export const MODEL_STORAGE_FILE_SLASH_PATH_FACTORY = slashPathFactory({ startType: 'absolute', basePath: BASE_MODEL_STORAGE_FILE_PATH });

export interface ModelStorageSlashPathFactoryConfig {
/**
* Additional base path to provide.
*
* This value is merged with the BASE_MODEL_STORAGE_FILE_PATH (/model/) base path configured for all ModelStorageSlashPathFactory values
*/
basePath?: string;
}

/**
* Factory for SlashPath values using input ReadFirestoreModelKeyInput values.
*/
export type ModelStorageSlashPathFactory<T extends object = object> = FactoryWithRequiredInput<SlashPath, ReadFirestoreModelKeyInput<T>>;

/**
* Creates a ModelStorageSlashPathFactory.
*
* @param config
* @returns
*/
export function modelStorageSlashPathFactory<T extends object = object>(config?: ModelStorageSlashPathFactoryConfig): ModelStorageSlashPathFactory<T> {
const { basePath = '' } = config ?? {};
return (input: ReadFirestoreModelKeyInput) => {
const key = readFirestoreModelKey(input, true);
return MODEL_STORAGE_FILE_SLASH_PATH_FACTORY([basePath, key]);
};
}
32 changes: 32 additions & 0 deletions packages/firebase/src/lib/common/storage/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { FirebaseStorageDrivers } from './driver/driver';
import { FirebaseStorage } from './types';

/**
* A @dereekb/firebase FirebaseStorageContext. Wraps the main FirebaseStorage context and the drivers, as well as utility/convenience functions.
*/
export interface FirebaseStorageContext<F extends FirebaseStorage = FirebaseStorage> {
readonly storage: F;
readonly drivers: FirebaseStorageDrivers;
}

/**
* Factory function for generating a FirebaseStorageContext given the input FirebaseStorage.
*/
export type FirebaseStorageContextFactory<F extends FirebaseStorage = FirebaseStorage> = (firebaseStorage: F) => FirebaseStorageContext;

/**
* Creates a new FirebaseStorageContextFactory given the input FirebaseStorageDrivers.
*
* @param drivers
* @returns
*/
export function firebaseStorageContextFactory<F extends FirebaseStorage = FirebaseStorage>(drivers: FirebaseStorageDrivers): FirebaseStorageContextFactory<F> {
return (firebaseStorage: F) => {
const context: FirebaseStorageContext<F> = {
storage: firebaseStorage,
drivers
};

return context;
};
}
18 changes: 18 additions & 0 deletions packages/firebase/src/lib/common/storage/driver/accessor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { GoogleCloudStorageFilePath, StorageFilePath } from '../storage';
import { FirebaseStorage, StorageDownloadUrl } from '../types';

export type FirebaseStorageAccessorDriverGetDownloadUrlFunction = (storage: FirebaseStorage, path: StorageFilePath) => Promise<StorageDownloadUrl>;

/**
* A driver to use for storage functionality.
*/
export interface FirebaseStorageAccessorDriver {
readonly getDownloadUrl: FirebaseStorageAccessorDriverGetDownloadUrlFunction;
}

/**
* Ref to a StorageAccessorDriver.
*/
export interface FirebaseStorageAccessorDriverRef {
storageAccessorDriver: FirebaseStorageAccessorDriver;
}
12 changes: 12 additions & 0 deletions packages/firebase/src/lib/common/storage/driver/driver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { FirebaseStorageAccessorDriverRef } from './accessor';

export type FirebaseStorageDriverIdentifier = string;
export type FirebaseStorageDriverType = 'production' | 'testing';

/**
* Implements all FirebaseStorage related driver reference interfaces.
*/
export interface FirebaseStorageDrivers extends FirebaseStorageAccessorDriverRef {
driverIdentifier?: FirebaseStorageDriverIdentifier;
driverType: FirebaseStorageDriverType;
}
2 changes: 2 additions & 0 deletions packages/firebase/src/lib/common/storage/driver/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './accessor';
export * from './driver';
5 changes: 5 additions & 0 deletions packages/firebase/src/lib/common/storage/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from './accessor';
export * from './driver';
export * from './context';
export * from './storage';
export * from './types';
Loading

0 comments on commit 5a30d46

Please sign in to comment.