From a970aeaab86a9d10b649045a11571c44d08eab61 Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Wed, 11 Mar 2020 14:53:55 +0100 Subject: [PATCH] disregard embed url params when indexing and checking indexed request urls --- src/app/core/data/request.service.spec.ts | 2 ++ src/app/core/data/request.service.ts | 35 ++++++++++---------- src/app/core/index/index.effects.ts | 3 +- src/app/core/index/index.selectors.spec.ts | 32 +++++++++++++++++++ src/app/core/index/index.selectors.ts | 37 ++++++++++++++++++++-- src/app/core/url-combiner/url-combiner.ts | 4 +-- 6 files changed, 92 insertions(+), 21 deletions(-) create mode 100644 src/app/core/index/index.selectors.spec.ts diff --git a/src/app/core/data/request.service.spec.ts b/src/app/core/data/request.service.spec.ts index 017721fdf9..23c27093e0 100644 --- a/src/app/core/data/request.service.spec.ts +++ b/src/app/core/data/request.service.spec.ts @@ -1,3 +1,4 @@ +import { NgZone } from '@angular/core'; import * as ngrx from '@ngrx/store'; import { ActionsSubject, Store } from '@ngrx/store'; import { cold, getTestScheduler, hot } from 'jasmine-marbles'; @@ -62,6 +63,7 @@ describe('RequestService', () => { objectCache, uuidService, store, + new NgZone({}), undefined ); serviceAsAny = service as any; diff --git a/src/app/core/data/request.service.ts b/src/app/core/data/request.service.ts index 1101c851ac..c63490d8e7 100644 --- a/src/app/core/data/request.service.ts +++ b/src/app/core/data/request.service.ts @@ -1,9 +1,9 @@ -import { Injectable } from '@angular/core'; +import { Injectable, NgZone } from '@angular/core'; import { HttpHeaders } from '@angular/common/http'; import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store'; import { Observable, race as observableRace } from 'rxjs'; -import { filter, map, mergeMap, switchMap, take } from 'rxjs/operators'; +import { filter, map, mergeMap, take } from 'rxjs/operators'; import { cloneDeep, remove } from 'lodash'; import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util'; import { CacheableObject } from '../cache/object-cache.reducer'; @@ -80,6 +80,7 @@ export class RequestService { constructor(private objectCache: ObjectCacheService, private uuidService: UUIDService, private store: Store, + private zone: NgZone, private indexStore: Store) { } @@ -147,21 +148,23 @@ export class RequestService { * @param {RestRequest} request The request to send out */ configure(request: RestRequest): void { - const isGetRequest = request.method === RestRequestMethod.GET; - if (!isGetRequest || request.forceBypassCache || !this.isCachedOrPending(request)) { - this.dispatchRequest(request); - if (isGetRequest) { - this.trackRequestsOnTheirWayToTheStore(request); - } - } else { - this.getByHref(request.href).pipe( - filter((entry) => hasValue(entry)), - take(1) - ).subscribe((entry) => { - return this.store.dispatch(new AddToIndexAction(IndexName.UUID_MAPPING, request.uuid, entry.request.uuid)) + this.zone.runOutsideAngular(() => { + const isGetRequest = request.method === RestRequestMethod.GET; + if (!isGetRequest || request.forceBypassCache || !this.isCachedOrPending(request)) { + this.dispatchRequest(request); + if (isGetRequest) { + this.trackRequestsOnTheirWayToTheStore(request); } - ) - } + } else { + this.getByHref(request.href).pipe( + filter((entry) => hasValue(entry)), + take(1) + ).subscribe((entry) => { + return this.store.dispatch(new AddToIndexAction(IndexName.UUID_MAPPING, request.uuid, entry.request.uuid)) + } + ) + } + }); } /** diff --git a/src/app/core/index/index.effects.ts b/src/app/core/index/index.effects.ts index c9f6eace8f..f885db1436 100644 --- a/src/app/core/index/index.effects.ts +++ b/src/app/core/index/index.effects.ts @@ -12,6 +12,7 @@ import { AddToIndexAction, RemoveFromIndexByValueAction } from './index.actions' import { hasValue } from '../../shared/empty.util'; import { IndexName } from './index.reducer'; import { RestRequestMethod } from '../data/rest-request-method'; +import { getUrlWithoutEmbedParams } from './index.selectors'; @Injectable() export class UUIDIndexEffects { @@ -47,7 +48,7 @@ export class UUIDIndexEffects { map((action: RequestConfigureAction) => { return new AddToIndexAction( IndexName.REQUEST, - action.payload.href, + getUrlWithoutEmbedParams(action.payload.href), action.payload.uuid ); }) diff --git a/src/app/core/index/index.selectors.spec.ts b/src/app/core/index/index.selectors.spec.ts new file mode 100644 index 0000000000..02cce4b7d6 --- /dev/null +++ b/src/app/core/index/index.selectors.spec.ts @@ -0,0 +1,32 @@ +import { getUrlWithoutEmbedParams } from './index.selectors'; + +describe(`index selectors`, () => { + + describe(`getUrlWithoutEmbedParams`, () => { + + it(`should return a url without its embed params`, () => { + const source = 'https://rest.api/resource?a=1&embed=2&b=3&embed=4/5&c=6&embed=7/8/9'; + const result = getUrlWithoutEmbedParams(source); + expect(result).toBe('https://rest.api/resource?a=1&b=3&c=6'); + }); + + it(`should return a url without embed params unmodified`, () => { + const source = 'https://rest.api/resource?a=1&b=3&c=6'; + const result = getUrlWithoutEmbedParams(source); + expect(result).toBe(source); + }); + + it(`should return a string that isn't a url unmodified`, () => { + const source = 'a=1&embed=2&b=3&embed=4/5&c=6&embed=7/8/9'; + const result = getUrlWithoutEmbedParams(source); + expect(result).toBe(source); + }); + + it(`should return undefined or null unmodified`, () => { + expect(getUrlWithoutEmbedParams(undefined)).toBe(undefined); + expect(getUrlWithoutEmbedParams(null)).toBe(null); + }); + + }); + +}); diff --git a/src/app/core/index/index.selectors.ts b/src/app/core/index/index.selectors.ts index de4adab09b..b23496c501 100644 --- a/src/app/core/index/index.selectors.ts +++ b/src/app/core/index/index.selectors.ts @@ -1,8 +1,41 @@ import { createSelector, MemoizedSelector } from '@ngrx/store'; -import { hasValue } from '../../shared/empty.util'; +import { hasValue, isNotEmpty } from '../../shared/empty.util'; import { CoreState } from '../core.reducers'; import { coreSelector } from '../core.selectors'; +import { URLCombiner } from '../url-combiner/url-combiner'; import { IndexName, IndexState, MetaIndexState } from './index.reducer'; +import * as parse from 'url-parse'; + +/** + * Return the given url without `embed` params. + * + * E.g. https://rest.api/resource?size=5&embed=subresource&rpp=3 + * becomes https://rest.api/resource?size=5&rpp=3 + * + * When you index a request url you don't want to include + * embed params because embedded data isn't relevant when + * you want to know + * + * @param url The url to use + */ +export const getUrlWithoutEmbedParams = (url: string): string => { + if (isNotEmpty(url)) { + const parsed = parse(url); + if (isNotEmpty(parsed.query)) { + const parts = parsed.query.split(/[?|&]/) + .filter((part: string) => isNotEmpty(part)) + .filter((part: string) => !part.startsWith('embed=')); + let args = ''; + if (isNotEmpty(parts)) { + args = `?${parts.join('&')}`; + } + url = new URLCombiner(parsed.origin, parsed.pathname, args).toString(); + return url; + } + } + + return url; +}; /** * Return the MetaIndexState based on the CoreSate @@ -74,7 +107,7 @@ export const selfLinkFromUuidSelector = export const uuidFromHrefSelector = (href: string): MemoizedSelector => createSelector( requestIndexSelector, - (state: IndexState) => hasValue(state) ? state[href] : undefined + (state: IndexState) => hasValue(state) ? state[getUrlWithoutEmbedParams(href)] : undefined ); /** diff --git a/src/app/core/url-combiner/url-combiner.ts b/src/app/core/url-combiner/url-combiner.ts index ae622ab976..e7468c6107 100644 --- a/src/app/core/url-combiner/url-combiner.ts +++ b/src/app/core/url-combiner/url-combiner.ts @@ -41,8 +41,8 @@ export class URLCombiner { // remove consecutive slashes url = url.replace(/([^:\s])\/+/g, '$1/'); - // remove trailing slash before parameters or hash - url = url.replace(/\/(\?|&|#[^!])/g, '$1'); + // remove trailing slash + url = url.replace(/\/($|\?|&|#[^!])/g, '$1'); // replace ? in parameters with & url = url.replace(/(\?.+)\?/g, '$1&');