Skip to content

Commit

Permalink
refactor: use ngrx/entity in quoting feature (#25)
Browse files Browse the repository at this point in the history
  • Loading branch information
fmalcher authored and shauke committed Dec 10, 2019
1 parent 0d75e7c commit 70102d9
Show file tree
Hide file tree
Showing 12 changed files with 125 additions and 108 deletions.
12 changes: 6 additions & 6 deletions src/app/extensions/quoting/facades/quoting.facade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
getCurrentQuotes,
getQuoteError,
getQuoteLoading,
getSelectedQuote,
getSelectedQuoteWithProducts,
} from '../store/quote';
import {
AddBasketToQuoteRequest,
Expand All @@ -30,11 +30,11 @@ import {
UpdateQuoteRequest,
UpdateQuoteRequestItems,
UpdateSubmitQuoteRequest,
getActiveQuoteRequest,
getActiveQuoteRequestWithProducts,
getCurrentQuoteRequests,
getQuoteRequestError,
getQuoteRequestLoading,
getSelectedQuoteRequest,
getSelectedQuoteRequestWithProducts,
} from '../store/quote-request';

const getQuotesAndQuoteRequests = createSelector(
Expand All @@ -49,7 +49,7 @@ export class QuotingFacade {
constructor(private store: Store<{}>) {}

// QUOTE
quote$ = this.store.pipe(select(getSelectedQuote));
quote$ = this.store.pipe(select(getSelectedQuoteWithProducts));
quoteLoading$ = this.store.pipe(select(getQuoteLoading));
quoteError$ = this.store.pipe(select(getQuoteError));

Expand All @@ -75,10 +75,10 @@ export class QuotingFacade {
}

// QUOTE REQUEST
quoteRequest$ = this.store.pipe(select(getSelectedQuoteRequest));
quoteRequest$ = this.store.pipe(select(getSelectedQuoteRequestWithProducts));
quoteRequestLoading$ = this.store.pipe(select(getQuoteRequestLoading));
quoteRequestError$ = this.store.pipe(select(getQuoteRequestError));
activeQuoteRequest$ = this.store.pipe(select(getActiveQuoteRequest));
activeQuoteRequest$ = this.store.pipe(select(getActiveQuoteRequestWithProducts));

quoteRequests$() {
this.loadQuoteRequests();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import { Injectable } from '@angular/core';
import { Store, select } from '@ngrx/store';
import { Observable, combineLatest, of, throwError } from 'rxjs';
import { concatMap, filter, map, mapTo, shareReplay, take } from 'rxjs/operators';
import { concatMap, map, mapTo, shareReplay, take } from 'rxjs/operators';

import { LineItemUpdate } from 'ish-core/models/line-item-update/line-item-update.model';
import { Link } from 'ish-core/models/link/link.model';
import { ApiService, resolveLinks, unpackEnvelope } from 'ish-core/services/api/api.service';
import { getLoggedInCustomer, getLoggedInUser } from 'ish-core/store/user';
import { whenFalsy } from 'ish-core/utils/operators';

import { QuoteLineItemResult } from '../../models/quote-line-item-result/quote-line-item-result.model';
import { QuoteRequestItemData } from '../../models/quote-request-item/quote-request-item.interface';
import { QuoteRequestItemMapper } from '../../models/quote-request-item/quote-request-item.mapper';
import { QuoteRequestItem } from '../../models/quote-request-item/quote-request-item.model';
import { QuoteRequestData } from '../../models/quote-request/quote-request.interface';
import { QuoteRequest } from '../../models/quote-request/quote-request.model';
import { getActiveQuoteRequest } from '../../store/quote-request';
import { getActiveQuoteRequestWithProducts } from '../../store/quote-request';

/**
* The Quote Request Service handles the interaction with the 'quoteRequest' related REST API.
Expand Down Expand Up @@ -46,8 +47,8 @@ export class QuoteRequestService {
// rebuild the stream everytime the selected id switches back to undefined
store
.pipe(
select(getActiveQuoteRequest),
filter(x => !x)
select(getActiveQuoteRequestWithProducts),
whenFalsy()
)
.subscribe(() => this.buildActiveQuoteRequestStream());

Expand Down Expand Up @@ -252,7 +253,7 @@ export class QuoteRequestService {
* selects or creates editable quote request
*/
private buildActiveQuoteRequestStream() {
this.quoteRequest$ = this.store.pipe(select(getActiveQuoteRequest)).pipe(
this.quoteRequest$ = this.store.pipe(select(getActiveQuoteRequestWithProducts)).pipe(
take(1),
concatMap(quoteRequest => (quoteRequest ? of(quoteRequest.id) : this.addQuoteRequest())),
shareReplay(1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ import { QuoteRequestService } from '../../services/quote-request/quote-request.
import { QuoteActionTypes } from '../quote/quote.actions';

import * as actions from './quote-request.actions';
import { getCurrentQuoteRequests, getSelectedQuoteRequest, getSelectedQuoteRequestId } from './quote-request.selectors';
import {
getCurrentQuoteRequests,
getSelectedQuoteRequestId,
getSelectedQuoteRequestWithProducts,
} from './quote-request.selectors';

@Injectable()
export class QuoteRequestEffects {
Expand Down Expand Up @@ -149,7 +153,7 @@ export class QuoteRequestEffects {
@Effect()
createQuoteRequestFromQuoteRequest$ = this.actions$.pipe(
ofType(actions.QuoteRequestActionTypes.CreateQuoteRequestFromQuoteRequest),
withLatestFrom(this.store.pipe(select(getSelectedQuoteRequest))),
withLatestFrom(this.store.pipe(select(getSelectedQuoteRequestWithProducts))),
concatMap(([, currentQuoteRequest]) =>
this.quoteRequestService.createQuoteRequestFromQuoteRequest(currentQuoteRequest).pipe(
map(quoteLineItemResult => new actions.CreateQuoteRequestFromQuoteRequestSuccess({ quoteLineItemResult })),
Expand Down Expand Up @@ -250,7 +254,7 @@ export class QuoteRequestEffects {
updateQuoteRequestItems$ = this.actions$.pipe(
ofType<actions.UpdateQuoteRequestItems>(actions.QuoteRequestActionTypes.UpdateQuoteRequestItems),
mapToPayloadProperty('lineItemUpdates'),
withLatestFrom(this.store.pipe(select(getSelectedQuoteRequest))),
withLatestFrom(this.store.pipe(select(getSelectedQuoteRequestWithProducts))),
map(([lineItemUpdates, selectedQuoteRequest]) => ({
quoteRequestId: selectedQuoteRequest.id,
updatedItems: this.filterQuoteRequestsForChanges(lineItemUpdates, selectedQuoteRequest),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ describe('Quote Request Reducer', () => {
const action = new fromActions.LoadQuoteRequestsSuccess({ quoteRequests });
const state = quoteRequestReducer(initialState, action);

expect(state.quoteRequests).toEqual(quoteRequests);
expect(state.ids).toEqual(['test']);
expect(state.entities).toEqual({ test: quoteRequests[0] });
expect(state.loading).toBeFalse();
});
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { EntityState, createEntityAdapter } from '@ngrx/entity';

import { HttpError } from 'ish-core/models/http-error/http-error.model';
import { UserAction, UserActionTypes } from 'ish-core/store/user';

Expand All @@ -6,21 +8,21 @@ import { QuoteRequestData } from '../../models/quote-request/quote-request.inter

import { QuoteAction, QuoteRequestActionTypes } from './quote-request.actions';

export interface QuoteRequestState {
quoteRequests: QuoteRequestData[];
export const quoteRequestAdapter = createEntityAdapter<QuoteRequestData>();

export interface QuoteRequestState extends EntityState<QuoteRequestData> {
quoteRequestItems: QuoteRequestItem[];
loading: boolean;
error: HttpError;
selected: string;
}

export const initialState: QuoteRequestState = {
quoteRequests: [],
export const initialState: QuoteRequestState = quoteRequestAdapter.getInitialState({
quoteRequestItems: [],
loading: false,
error: undefined,
selected: undefined,
};
});

export function quoteRequestReducer(state = initialState, action: QuoteAction | UserAction): QuoteRequestState {
switch (action.type) {
Expand Down Expand Up @@ -76,9 +78,12 @@ export function quoteRequestReducer(state = initialState, action: QuoteAction |
case QuoteRequestActionTypes.LoadQuoteRequestsSuccess: {
const quoteRequests = action.payload.quoteRequests;

if (!state) {
return;
}

return {
...state,
quoteRequests,
...quoteRequestAdapter.addAll(quoteRequests, state),
loading: false,
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ import {
} from './quote-request.actions';
import {
getActiveQuoteRequest,
getActiveQuoteRequestWithProducts,
getCurrentQuoteRequests,
getQuoteRequestError,
getQuoteRequestItemsWithProducts,
getQuoteRequestLoading,
getQuoteRequstItems,
getSelectedQuoteRequest,
getSelectedQuoteRequestId,
getSelectedQuoteRequestWithProducts,
} from './quote-request.selectors';

describe('Quote Request Selectors', () => {
Expand Down Expand Up @@ -79,7 +80,7 @@ describe('Quote Request Selectors', () => {
};

expect(getSelectedQuoteRequestId(store$.state)).toEqual('test');
expect(getSelectedQuoteRequest(store$.state)).toEqual(expected);
expect(getSelectedQuoteRequestWithProducts(store$.state)).toEqual(expected);
});
});

Expand Down Expand Up @@ -124,14 +125,14 @@ describe('Quote Request Selectors', () => {
store$.dispatch(new LoadQuoteRequestItemsSuccess({ quoteRequestItems }));

expect(getQuoteRequestLoading(store$.state)).toBeFalse();
expect(getQuoteRequstItems(store$.state)).toEqual(quoteRequestItems);
expect(getQuoteRequestItemsWithProducts(store$.state)).toEqual(quoteRequestItems);
expect(getActiveQuoteRequest(store$.state)).toBeUndefined();
});

it('should set loading to false and set error state', () => {
store$.dispatch(new LoadQuoteRequestItemsFail({ error: { message: 'invalid' } as HttpError }));
expect(getQuoteRequestLoading(store$.state)).toBeFalse();
expect(getQuoteRequstItems(store$.state)).toBeEmpty();
expect(getQuoteRequestItemsWithProducts(store$.state)).toBeEmpty();
expect(getQuoteRequestError(store$.state)).toEqual({ message: 'invalid' });
});
});
Expand All @@ -149,7 +150,7 @@ describe('Quote Request Selectors', () => {
});

it('should have a product on the active quote request', () => {
const activeQuoteRequest = getActiveQuoteRequest(store$.state);
const activeQuoteRequest = getActiveQuoteRequestWithProducts(store$.state);
expect(activeQuoteRequest).toBeTruthy();
const items = activeQuoteRequest.items;
expect(items).toHaveLength(1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,79 +4,69 @@ import { isEqual } from 'lodash-es';
import { getProductEntities } from 'ish-core/store/shopping/products';

import { QuoteRequestHelper } from '../../models/quote-request/quote-request.helper';
import { QuoteRequestData } from '../../models/quote-request/quote-request.interface';
import { getQuotingState } from '../quoting-store';

import { initialState } from './quote-request.reducer';
import { initialState, quoteRequestAdapter } from './quote-request.reducer';

const getQuoteRequestState = createSelector(
getQuotingState,
state => (state ? state.quoteRequest : initialState)
);

const { selectAll, selectEntities } = quoteRequestAdapter.getSelectors(getQuoteRequestState);

export const getSelectedQuoteRequestId = createSelector(
getQuoteRequestState,
state => state.selected
);

export const getSelectedQuoteRequest = createSelector(
selectEntities,
getSelectedQuoteRequestId,
(entities, id) => id && addStateToQuoteRequest(entities[id])
);

export const getCurrentQuoteRequests = createSelector(
getQuoteRequestState,
state =>
state.quoteRequests.map(item => ({
...item,
state: QuoteRequestHelper.getQuoteRequestState(item),
}))
selectAll,
quoteRequests => quoteRequests.map(addStateToQuoteRequest)
);

export const getQuoteRequstItems = createSelector(
export const getQuoteRequestItems = createSelector(
getQuoteRequestState,
state => state.quoteRequestItems
);

export const getActiveQuoteRequest = createSelector(
createSelector(
getCurrentQuoteRequests,
quoteRequests => quoteRequests.filter(item => item.editable).pop() || undefined
),
getQuoteRequstItems,
export const getQuoteRequestItemsWithProducts = createSelector(
getQuoteRequestItems,
getProductEntities,
(quoteRequest, quoteRequestItems, products) =>
!quoteRequest
? undefined
: {
...quoteRequest,
state: QuoteRequestHelper.getQuoteRequestState(quoteRequest),
items: quoteRequestItems.map(item => ({
...item,
product: item.productSKU ? products[item.productSKU] : undefined,
})),
}
(items, products) =>
items.map(item => ({
...item,
product: item.productSKU ? products[item.productSKU] : undefined,
}))
);

export const getActiveQuoteRequest = createSelector(
getCurrentQuoteRequests,
quoteRequests => {
const quoteRequest = quoteRequests.reverse().find(item => item.editable);
return addStateToQuoteRequest(quoteRequest);
}
);

export const getActiveQuoteRequestWithProducts = createSelector(
getActiveQuoteRequest,
getQuoteRequestItemsWithProducts,
(quoteRequest, items) => quoteRequest && { ...quoteRequest, items }
);

/**
* Select the selected quote request with the appended line item and product data if available.
*/
export const getSelectedQuoteRequest = createSelectorFactory(projector =>
export const getSelectedQuoteRequestWithProducts = createSelectorFactory(projector =>
defaultMemoize(projector, undefined, isEqual)
)(
createSelector(
getCurrentQuoteRequests,
getSelectedQuoteRequestId,
(items, id) => items.filter(item => item.id === id).pop()
),
getQuoteRequstItems,
getProductEntities,
(quote, quoteRequestItems, products) =>
!quote
? undefined
: {
...quote,
state: QuoteRequestHelper.getQuoteRequestState(quote),
items: quoteRequestItems.map(item => ({
...item,
product: item.productSKU ? products[item.productSKU] : undefined,
})),
}
);
)(getSelectedQuoteRequest, getQuoteRequestItemsWithProducts, (quote, items) => quote && { ...quote, items });

export const getQuoteRequestLoading = createSelector(
getQuoteRequestState,
Expand All @@ -87,3 +77,12 @@ export const getQuoteRequestError = createSelector(
getQuoteRequestState,
state => state.error
);

function addStateToQuoteRequest(quote: QuoteRequestData) {
return (
quote && {
...quote,
state: QuoteRequestHelper.getQuoteRequestState(quote),
}
);
}
4 changes: 2 additions & 2 deletions src/app/extensions/quoting/store/quote/quote.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { QuoteService } from '../../services/quote/quote.service';
import { QuoteRequestActionTypes } from '../quote-request';

import * as actions from './quote.actions';
import { getSelectedQuote, getSelectedQuoteId } from './quote.selectors';
import { getSelectedQuoteId, getSelectedQuoteWithProducts } from './quote.selectors';

@Injectable()
export class QuoteEffects {
Expand Down Expand Up @@ -81,7 +81,7 @@ export class QuoteEffects {
@Effect()
createQuoteRequestFromQuote$ = this.actions$.pipe(
ofType(actions.QuoteActionTypes.CreateQuoteRequestFromQuote),
withLatestFrom(this.store.pipe(select(getSelectedQuote))),
withLatestFrom(this.store.pipe(select(getSelectedQuoteWithProducts))),
concatMap(([, currentQuoteRequest]) =>
this.quoteService.createQuoteRequestFromQuote(currentQuoteRequest).pipe(
map(quoteLineItemRequest => new actions.CreateQuoteRequestFromQuoteSuccess({ quoteLineItemRequest })),
Expand Down
3 changes: 2 additions & 1 deletion src/app/extensions/quoting/store/quote/quote.reducer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ describe('Quote Reducer', () => {
const action = new fromActions.LoadQuotesSuccess(quotes);
const state = quoteReducer(initialState, action);

expect(state.quotes).toEqual(quotes.quotes);
expect(state.ids).toEqual(['test']);
expect(state.entities).toEqual({ test: quotes.quotes[0] });
expect(state.loading).toBeFalse();
});
});
Expand Down
Loading

0 comments on commit 70102d9

Please sign in to comment.