Skip to content

Commit

Permalink
feat: added specifier for crud functions
Browse files Browse the repository at this point in the history
- Can now add multiple mapped functions to a create, update, or delete function mapping
- update demo to reflect new usage
  • Loading branch information
dereekb committed Jul 5, 2022
1 parent f543433 commit 39e366e
Show file tree
Hide file tree
Showing 16 changed files with 282 additions and 48 deletions.
9 changes: 6 additions & 3 deletions apps/demo-api/src/app/function/model/crud.functions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createGuestbook } from './../guestbook/guestbook.crud';
import { updateProfile } from '../profile/profile.update';
import { updateProfile, updateProfileUsername } from '../profile/profile.update';
import { updateGuestbookEntry } from '../guestbook/guestbookentry.update';
import { inAuthContext, onCallCreateModel, onCallDeleteModel, onCallUpdateModel } from '@dereekb/firebase-server';
import { inAuthContext, onCallCreateModel, onCallDeleteModel, onCallUpdateModel, onCallSpecifierHandler } from '@dereekb/firebase-server';
import { DemoOnCallCreateModelMap, DemoOnCallDeleteModelMap, DemoOnCallUpdateModelMap, onCallWithDemoNestContext } from '../function';

// MARK: Create
Expand All @@ -13,7 +13,10 @@ export const demoCreateModel = onCallWithDemoNestContext(inAuthContext(onCallCre
// MARK: Update
export const demoUpdateModelMap: DemoOnCallUpdateModelMap = {
guestbookEntry: updateGuestbookEntry,
profile: updateProfile
profile: onCallSpecifierHandler({
_: updateProfile,
username: updateProfileUsername
})
};
export const demoUpdateModel = onCallWithDemoNestContext(inAuthContext(onCallUpdateModel(demoUpdateModelMap)));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { onCallWithDemoNestContext } from '../function';
import { userHasNoProfileError } from '../../common';
import { profileForUserRequest } from './profile.util';

/**
* @deprecated use updateProfileUsername instead.
*/
export const profileSetUsername = onCallWithDemoNestContext<SetProfileUsernameParams>(
inAuthContext(async (request) => {
const { nest, auth, data } = request;
Expand Down
16 changes: 15 additions & 1 deletion apps/demo-api/src/app/function/profile/profile.update.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
import { ProfileDocument, UpdateProfileParams } from '@dereekb/demo-firebase';
import { ProfileDocument, SetProfileUsernameParams, UpdateProfileParams } from '@dereekb/demo-firebase';
import { DemoUpdateModelfunction } from '../function';
import { profileForUserRequest } from './profile.util';
import { userHasNoProfileError } from '../../common';

export const updateProfile: DemoUpdateModelfunction<UpdateProfileParams> = async (request) => {
const { nest, auth, data } = request;
const updateProfile = await nest.profileActions.updateProfile(data);
const profileDocument: ProfileDocument = await profileForUserRequest(request);
await updateProfile(profileDocument);
};

export const updateProfileUsername: DemoUpdateModelfunction<SetProfileUsernameParams> = async (request) => {
const { nest, auth, data } = request;
const setProfileUsername = await nest.profileActions.setProfileUsername(data);
const profileDocument: ProfileDocument = await profileForUserRequest(request);
const exists = await profileDocument.accessor.exists();

if (!exists) {
throw userHasNoProfileError(auth.uid);
}

await setProfileUsername(profileDocument);
};
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export class DemoProfileViewComponent implements OnInit, OnDestroy {
};

handleChangeUsername: HandleActionWithContext<DemoProfileUsernameFormValue> = (form, context) => {
context.startWorkingWithLoadingStateObservable(this.profileDocumentStore.setProfileUsername(form));
context.startWorkingWithLoadingStateObservable(this.profileDocumentStore.updateProfileUsername(form));
};

handleUpdateProfile: HandleActionWithContext<DemoProfileFormValue> = (form, context) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ export class ProfileDocumentStore extends AbstractDbxFirebaseDocumentStore<Profi
super({ firestoreCollection: collections.profileCollection });
}

setProfileUsername(params: SetProfileUsernameParams): Observable<LoadingState<void>> {
return loadingStateFromObs(from(this.profileFunctions[profileSetUsernameKey](params)));
}

readonly updateProfile = firebaseDocumentStoreUpdateFunction(this, this.profileFunctions.profile.updateProfile);
readonly updateProfile = firebaseDocumentStoreUpdateFunction(this, this.profileFunctions.profile.updateProfile.updateProfile);
readonly updateProfileUsername = firebaseDocumentStoreUpdateFunction(this, this.profileFunctions.profile.updateProfile.updateProfileUsername);
}
12 changes: 9 additions & 3 deletions components/demo-firebase/src/lib/models/profile/profile.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,17 @@ export const profileFunctionTypeConfigMap: FirebaseFunctionTypeConfigMap<Profile

export type ProfileModelCrudFunctionsConfig = {
profile: {
update: UpdateProfileParams;
update: {
_: UpdateProfileParams;
username: SetProfileUsernameParams;
};
delete: UpdateProfileParams;
};
profilePrivate: null;
};

export const profileModelCrudFunctionsConfig: ModelFirebaseCrudFunctionConfigMap<ProfileModelCrudFunctionsConfig, ProfileTypes> = {
profile: ['update', 'delete']
profile: ['update:_,username', 'delete']
};

/**
Expand All @@ -73,7 +76,10 @@ export const profileFunctionMap = modelFirebaseFunctionMapFactory(profileFunctio
export abstract class ProfileFunctions implements ModelFirebaseFunctionMap<ProfileFunctionTypeMap, ProfileModelCrudFunctionsConfig> {
abstract [profileSetUsernameKey]: FirebaseFunctionMapFunction<ProfileFunctionTypeMap, 'profileSetUsername'>;
abstract profile: {
updateProfile: ModelFirebaseCrudFunction<UpdateProfileParams>;
updateProfile: {
updateProfile: ModelFirebaseCrudFunction<UpdateProfileParams>;
updateProfileUsername: ModelFirebaseCrudFunction<SetProfileUsernameParams>;
};
deleteProfile: ModelFirebaseCrudFunction<UpdateProfileParams>;
};
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { PromiseOrValue, serverError } from '@dereekb/util';
import { FirestoreModelType, FirestoreModelIdentity, FirestoreModelTypes, OnCallCreateModelParams, OnCallCreateModelResult } from '@dereekb/firebase';
import { FirestoreModelType, FirestoreModelIdentity, FirestoreModelTypes, OnCallCreateModelParams, OnCallCreateModelResult, ModelFirebaseCrudFunctionSpecifierRef } from '@dereekb/firebase';
import { badRequestError } from '../../function';
import { OnCallWithAuthorizedNestContext } from '../function/call';
import { NestContextCallableRequestWithAuth } from '../function/nest';

// MARK: Function
export type OnCallCreateModelFunction<N, I = unknown, O extends OnCallCreateModelResult = OnCallCreateModelResult> = (request: NestContextCallableRequestWithAuth<N, I>) => PromiseOrValue<O>;
export type OnCallCreateModelFunction<N, I = unknown, O extends OnCallCreateModelResult = OnCallCreateModelResult> = (request: NestContextCallableRequestWithAuth<N, I> & ModelFirebaseCrudFunctionSpecifierRef) => PromiseOrValue<O>;

export type OnCallCreateModelMap<N, T extends FirestoreModelIdentity = FirestoreModelIdentity> = {
[K in FirestoreModelTypes<T>]?: OnCallCreateModelFunction<N, any, OnCallCreateModelResult>;
Expand All @@ -25,6 +25,7 @@ export function onCallCreateModel<N>(map: OnCallCreateModelMap<N>): OnCallWithAu
if (createFn) {
return createFn({
...request,
specifier: request.data.specifier,
data: request.data.data
});
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { PromiseOrValue, serverError } from '@dereekb/util';
import { FirestoreModelType, FirestoreModelIdentity, FirestoreModelTypes, OnCallDeleteModelParams } from '@dereekb/firebase';
import { FirestoreModelType, FirestoreModelIdentity, FirestoreModelTypes, OnCallDeleteModelParams, ModelFirebaseCrudFunctionSpecifierRef } from '@dereekb/firebase';
import { badRequestError } from '../../function';
import { NestContextCallableRequestWithAuth } from '../function/nest';
import { OnCallWithAuthorizedNestContext } from '../function/call';

// MARK: Function
export type OnCallDeleteModelFunction<N, I = unknown, O = void> = (request: NestContextCallableRequestWithAuth<N, I>) => PromiseOrValue<O>;
export type OnCallDeleteModelFunction<N, I = unknown, O = void> = (request: NestContextCallableRequestWithAuth<N, I> & ModelFirebaseCrudFunctionSpecifierRef) => PromiseOrValue<O>;

export type OnCallDeleteModelMap<N, T extends FirestoreModelIdentity = FirestoreModelIdentity> = {
[K in FirestoreModelTypes<T>]?: OnCallDeleteModelFunction<N, any, any>;
Expand All @@ -25,6 +25,7 @@ export function onCallDeleteModel<N>(map: OnCallDeleteModelMap<N>): OnCallWithAu
if (deleteFn) {
return deleteFn({
...request,
specifier: request.data.specifier,
data: request.data.data
});
} else {
Expand Down
1 change: 1 addition & 0 deletions packages/firebase-server/src/lib/nest/model/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './permission.error';
export * from './specifier.function';
export * from './create.model.function';
export * from './update.model.function';
export * from './delete.model.function';
44 changes: 44 additions & 0 deletions packages/firebase-server/src/lib/nest/model/specifier.function.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { ModelFirebaseCrudFunctionSpecifier, ModelFirebaseCrudFunctionSpecifierRef, MODEL_FUNCTION_FIREBASE_CRUD_FUNCTION_SPECIFIER_DEFAULT } from '@dereekb/firebase';
import { objectToMap, PromiseOrValue, serverError } from '@dereekb/util';
import { NestContextCallableRequestWithAuth } from '../function/nest';
import { badRequestError } from '../../function';

export type OnCallSpecifierHandlerNestContextRequest<N, I = unknown> = NestContextCallableRequestWithAuth<N, I> & ModelFirebaseCrudFunctionSpecifierRef;
export type OnCallSpecifierHandlerFunction<N, I = unknown, O = void> = (request: OnCallSpecifierHandlerNestContextRequest<N, I>) => PromiseOrValue<O>;

// TODO: Add typings to ensure all expected function keys are present here.
export type OnCallSpecifierHandlerConfig<N> = {
/**
* The default handler function.
*/
_: OnCallSpecifierHandlerFunction<N, any, any>;
[key: string]: OnCallSpecifierHandlerFunction<N, any, any>;
};

export function onCallSpecifierHandler<N>(config: OnCallSpecifierHandlerConfig<N>): OnCallSpecifierHandlerFunction<N> {
const map = objectToMap(config);

return async (request) => {
const { specifier = MODEL_FUNCTION_FIREBASE_CRUD_FUNCTION_SPECIFIER_DEFAULT } = request;
const handler = map.get(specifier);

if (handler != null) {
return await handler(request);
} else {
throw unknownModelCrudFunctionSpecifierError(specifier);
}
};
}

export function unknownModelCrudFunctionSpecifierError(specifier: ModelFirebaseCrudFunctionSpecifier) {
return badRequestError(
serverError({
status: 400,
code: 'UNKNOWN_SPECIFIER_ERROR',
message: 'Invalid/unknown specifier for this function.',
data: {
specifier
}
})
);
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { PromiseOrValue, serverError } from '@dereekb/util';
import { FirestoreModelType, FirestoreModelIdentity, FirestoreModelTypes, OnCallUpdateModelParams } from '@dereekb/firebase';
import { FirestoreModelType, FirestoreModelIdentity, FirestoreModelTypes, OnCallUpdateModelParams, ModelFirebaseCrudFunctionSpecifierRef } from '@dereekb/firebase';
import { badRequestError } from '../../function';
import { OnCallWithAuthorizedNestContext } from '../function/call';
import { NestContextCallableRequestWithAuth } from '../function/nest';

// MARK: Function
export type OnCallUpdateModelFunction<N, I = unknown, O = void> = (request: NestContextCallableRequestWithAuth<N, I>) => PromiseOrValue<O>;
export type OnCallUpdateModelFunction<N, I = unknown, O = void> = (request: NestContextCallableRequestWithAuth<N, I> & ModelFirebaseCrudFunctionSpecifierRef) => PromiseOrValue<O>;

export type OnCallUpdateModelMap<N, T extends FirestoreModelIdentity = FirestoreModelIdentity> = {
[K in FirestoreModelTypes<T>]?: OnCallUpdateModelFunction<N, any, any>;
Expand All @@ -25,6 +25,7 @@ export function onCallUpdateModel<N>(map: OnCallUpdateModelMap<N>): OnCallWithAu
if (updateFn) {
return updateFn({
...request,
specifier: request.data.specifier,
data: request.data.data
});
} else {
Expand Down
Loading

0 comments on commit 39e366e

Please sign in to comment.