Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions spa/src/app/_ngrx/app.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { MatrixEffects } from "../files/_ngrx/matrix/matrix.effects";
import { ProjectEffects } from "../files/_ngrx/project/project.effects";
import { ProjectEditsEffects } from "../files/_ngrx/project-edits/project-edits.effects";
import { ReleaseEffects } from "../files/_ngrx/release/release.effects";
import { SearchEffects } from "../files/_ngrx/search/search.effects";
import { SystemEffects } from "../system/_ngrx/system.effects";
import { TableEffects } from "../files/_ngrx/table/table.effects";
import { TerraEffects } from "../files/_ngrx/terra/terra.effects";
Expand All @@ -32,6 +33,7 @@ export const AppEffects = [
ProjectEffects,
ProjectEditsEffects,
ReleaseEffects,
SearchEffects,
SystemEffects,
TableEffects,
TerraEffects,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ export class AnalysisProtocolEffects {
withLatestFrom(this.store.pipe(select(selectPreviousQuery), take(1)))
)),
tap(([action, queryWhenActionTriggered]) => {
this.gtmService.trackEvent((action as ViewAnalysisProtocolAction).asEvent(queryWhenActionTriggered));
this.gtmService.trackEvent((action as ViewAnalysisProtocolAction).asEvent({
currentQuery: queryWhenActionTriggered
}));
})
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ export class ViewAnalysisProtocolAction implements Action, TrackingAction {
/**
* Return the clear action as a GA event.
*
* @param {string} currentQuery
* @param {{[key: string]: any}} dimensions
* @returns {GAEvent}
*/
public asEvent(currentQuery: string): GAEvent {
public asEvent({currentQuery}): GAEvent {

return {
category: GACategory.PORTAL_LINK,
Expand Down
3 changes: 2 additions & 1 deletion spa/src/app/files/_ngrx/analytics/tracking.action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ export interface TrackingAction extends Action {
/**
* Return action in the format of a GA event.
*
* @param {{[key: string]: any}} dimensions
* @returns {string}
*/
asEvent(currentQuery?: string): GAEvent;
asEvent(dimensions?: {[key: string]: any}): GAEvent;
}
7 changes: 5 additions & 2 deletions spa/src/app/files/_ngrx/entity/entity.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ import { concatMap, withLatestFrom } from "rxjs/operators";
// App dependencies
import { AppState } from "../../../_ngrx/app.state";
import { SelectEntityAction } from "./select-entity.action";
import { InitEntityStateAction, NoOpAction } from "../facet/file-facet-list.actions";
import { selectTableQueryParams } from "../file.selectors";
import { GTMService } from "../../../shared/analytics/gtm.service";
import { getSelectedTable } from "../table/table.state";
import { BackToEntityAction } from "./back-to-entity.action";
import { selectPreviousQuery } from "../search/search.selectors";
import { InitEntityStateAction } from "./init-entity-state.action";
import { NoOpAction } from "../facet/no-op.action";

@Injectable()
export class EntityEffects {
Expand Down Expand Up @@ -55,7 +56,9 @@ export class EntityEffects {
map(([action, tableQueryParams, queryWhenActionTriggered]) => {

// Track change of tab
this.gtmService.trackEvent((action as SelectEntityAction).asEvent(queryWhenActionTriggered));
this.gtmService.trackEvent((action as SelectEntityAction).asEvent({
currentQuery: queryWhenActionTriggered
}));

// Return cached table, if available
if ( getSelectedTable(tableQueryParams.tableState).data.length ) {
Expand Down
15 changes: 15 additions & 0 deletions spa/src/app/files/_ngrx/entity/init-entity-state.action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* Human Cell Atlas
* https://www.humancellatlas.org/
*
* Action dispatched when entity state is not yet cached and needs to be initialized.
*/

// App dependencies
import { Action } from "@ngrx/store";

export class InitEntityStateAction implements Action {

public static ACTION_TYPE = "FILE.FACET.INIT_ENTITY_STATE";
public readonly type = InitEntityStateAction.ACTION_TYPE;
}
6 changes: 3 additions & 3 deletions spa/src/app/files/_ngrx/entity/select-entity.action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ export class SelectEntityAction implements Action, TrackingAction {
/**
* Return the cleared age range action as a GA event.
*
* @param {string} currentQuery
* @param {{[key: string]: any}} dimensions
* @returns {GAEvent}
*/
public asEvent(currentQuery: string): GAEvent {
public asEvent({currentQuery}): GAEvent {

return {
category: GACategory.ENTITY,
action: this.getTrackingAction(),
label: this.entityKey,
label: this.entityKey.charAt(0).toUpperCase() + this.entityKey.slice(1), // Capitalize first letter of entity
dimensions: {
[GADimension.CURRENT_QUERY]: currentQuery,
}
Expand Down
4 changes: 1 addition & 3 deletions spa/src/app/files/_ngrx/facet/facet.reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,11 @@ import { Action } from "@ngrx/store";

// App dependencies
import { ClearIsMatrixSupportedAction } from "./clear-is-matrix-supported.action";
import { InitEntityStateAction } from "../entity/init-entity-state.action";
import { FetchFacetsSuccessAction } from "./fetch-facets-success-action.action";
import { FetchIsMatrixSupportedRequestAction } from "./fetch-is-matrix-supported-request.action";
import { FetchIsMatrixSupportedSuccessAction } from "./fetch-is-matrix-supported-success.action";
import { FacetState } from "./facet.state";
import {
InitEntityStateAction
} from "./file-facet-list.actions";
import { SelectFileFacetTermAction } from "../search/select-file-facet-term.action";
import { ClearSelectedTermsAction } from "../search/clear-selected-terms.action";
import { SetViewStateAction } from "./set-view-state.action";
Expand Down
22 changes: 22 additions & 0 deletions spa/src/app/files/_ngrx/facet/fetch-file-facets-request.action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Human Cell Atlas
* https://www.humancellatlas.org/
*
* Action dispatched when file fileFacets are to be updated. This can be on load of app, select or clear of fileFacets, or
* select of project.
*/

// Core dependencies
import { Action } from "@ngrx/store";

export class FetchFileFacetsRequestAction implements Action {

public static ACTION_TYPE = "FILE.FACET.FETCH_REQUEST";
public readonly type = FetchFileFacetsRequestAction.ACTION_TYPE;

/**
* @param {boolean} updateTableData - True if table data is to be updated. This is false on select of project as we
* want table data and state (eg pagination) to remain unchanged on select of project.
*/
constructor(public readonly updateTableData: boolean) {}
}
44 changes: 0 additions & 44 deletions spa/src/app/files/_ngrx/facet/file-facet-list.actions.ts

This file was deleted.

14 changes: 14 additions & 0 deletions spa/src/app/files/_ngrx/facet/no-op.action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Human Cell Atlas
* https://www.humancellatlas.org/
*
* Noop action.
*/

// Core dependencies
import { Action } from "@ngrx/store";

export class NoOpAction implements Action {
public static ACTION_TYPE = "FILE.FACET.NOOP";
public readonly type = NoOpAction.ACTION_TYPE;
}
8 changes: 7 additions & 1 deletion spa/src/app/files/_ngrx/facet/set-view-state.action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,18 @@

// Core dependencies
import { Action } from "@ngrx/store";
import { QueryStringSearchTerm } from "../../search/url/query-string-search-term.model";

// App dependencies
import { QueryStringSearchTerm } from "../../search/url/query-string-search-term.model";

export class SetViewStateAction implements Action {

public static ACTION_TYPE = "FILE.FACET.SET_VIEW_STATE";
public readonly type = SetViewStateAction.ACTION_TYPE;

/**
* @param {string} selectedEntity
* @param {QueryStringSearchTerm[]} selectedSearchTerms
*/
constructor(public selectedEntity: string, public selectedSearchTerms: QueryStringSearchTerm[]) {}
}
12 changes: 6 additions & 6 deletions spa/src/app/files/_ngrx/file.effects.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { MockStore, provideMockStore } from "@ngrx/store/testing";
import { Observable, of } from "rxjs";

// App dependencies
import { FetchFacetsSuccessAction } from "./facet/fetch-facets-success-action.action";
import { FetchFileFacetsRequestAction } from "./facet/fetch-file-facets-request.action";
import { FileEffects } from "./file.effects";
import { FileState } from "./file.state";
import {
Expand All @@ -22,8 +24,6 @@ import {
DEFAULT_SAMPLES_STATE, FILES_STATE_WITH_SEARCH_TERM,
PROJECTS_STATE_WITH_PROJECT_SEARCH_TERM, SAMPLES_STATE_WITH_SEARCH_TERM
} from "./file.state.mock";
import { FetchFileFacetsRequestAction } from "./facet/file-facet-list.actions";
import { FetchFacetsSuccessAction } from "./facet/fetch-facets-success-action.action";
import { SearchTermsUpdatedAction } from "./search/search-terms-updated.action";
import { GTMService } from "../../shared/analytics/gtm.service";
import { DEFAULT_PROJECTS_ENTITY_SEARCH_RESULTS } from "../shared/entity-search-results.mock";
Expand Down Expand Up @@ -84,7 +84,7 @@ describe("File Effects", () => {
actions = hot("--a-", {
a: new FetchFileFacetsRequestAction(false)
});

const expected = cold("--(bcd)", {
b: new FetchFacetsSuccessAction(DEFAULT_PROJECTS_ENTITY_SEARCH_RESULTS.facets),
c: new SearchTermsUpdatedAction([]),
Expand Down Expand Up @@ -114,15 +114,15 @@ describe("File Effects", () => {

expect(effects.fetchFacets$).toBeObservable(expected);
});

/**
* A new request to fetch table data should be returned when viewing the projects tab and there is currently a
* selected project.
*/
it("fetchFacets$ - projects tab - should request table data when a project search term is selected", () => {

// Update search state to include a selected project search term
store.setState(PROJECTS_STATE_WITH_PROJECT_SEARCH_TERM);
store.setState(PROJECTS_STATE_WITH_PROJECT_SEARCH_TERM);

// Dispatch the fetch file facets action
actions = hot("--a-", {
Expand All @@ -137,7 +137,7 @@ describe("File Effects", () => {

expect(effects.fetchFacets$).toBeObservable(expected);
});

/**
* Full table model (data, pagination and term counts) should be updated when fetching facets if the "update table
* data" flag is set to true and the current tab is samples.
Expand Down
59 changes: 35 additions & 24 deletions spa/src/app/files/_ngrx/file.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,31 @@ import { Observable, of } from "rxjs";
import { concatMap, map, mergeMap, switchMap, take, withLatestFrom } from "rxjs/operators";

// App dependencies
import { TrackingAction } from "./analytics/tracking.action";
import { InitEntityStateAction } from "./entity/init-entity-state.action";
import { FetchFacetsSuccessAction } from "./facet/fetch-facets-success-action.action";
import { FetchFileFacetsRequestAction } from "./facet/fetch-file-facets-request.action";
import { FetchIsMatrixSupportedRequestAction } from "./facet/fetch-is-matrix-supported-request.action";
import { FetchIsMatrixSupportedSuccessAction } from "./facet/fetch-is-matrix-supported-success.action";
import { FileFacetName } from "../facet/file-facet/file-facet-name.model";
import { SetViewStateAction } from "./facet/set-view-state.action";
import { FetchFileFacetsRequestAction, InitEntityStateAction, NoOpAction } from "./facet/file-facet-list.actions";
import { selectTableQueryParams } from "./file.selectors";
import { FileSummary } from "../file-summary/file-summary";
import { FetchFileSummaryRequestAction, FetchFileSummarySuccessAction } from "./file-summary/file-summary.actions";
import { AppState } from "../../_ngrx/app.state";
import { ClearSelectedTermsAction } from "./search/clear-selected-terms.action";
import { SelectFileFacetTermAction } from "./search/select-file-facet-term.action";
import { selectPreviousQuery, selectSelectedSearchTerms } from "./search/search.selectors";
import { selectCurrentQuery, selectSelectedSearchTerms } from "./search/search.selectors";
import { SearchTermsUpdatedAction } from "./search/search-terms-updated.action";
import { SearchTerm } from "../search/search-term.model";
import { SelectFacetAgeRangeAction } from "./search/select-facet-age-range.action";
import { ClearSelectedAgeRangeAction } from "./search/clear-selected-age-range.action";
import { GTMService } from "../../shared/analytics/gtm.service";
import { EntityName } from "../shared/entity-name.model";
import { FilesService } from "../shared/files.service";
import { GAAction } from "../../shared/analytics/ga-action.model";
import { GACategory } from "../../shared/analytics/ga-category.model";
import { GADimension } from "../../shared/analytics/ga-dimension.model";
import { GAIndex } from "../../shared/analytics/ga-index.model";
import { FetchTableDataRequestAction } from "./table/fetch-table-data-request.action";
import { FetchTableModelSuccessAction } from "./table/fetch-table-model-success.action";
import { DEFAULT_TABLE_PARAMS } from "../table/pagination/table-params.model";
Expand Down Expand Up @@ -72,15 +76,7 @@ export class FileEffects {
SelectFileFacetTermAction.ACTION_TYPE, // Selecting facet term eg file type "matrix"
SelectFacetAgeRangeAction.ACTION_TYPE // Setting age range
),
concatMap(action => of(action).pipe(
withLatestFrom(this.store.pipe(select(selectPreviousQuery), take(1)))
)),
mergeMap(([action, queryWhenActionTriggered]) => {

// If this action is a tracking action, send tracking event.
if ( (action as TrackingAction).asEvent ) {
this.gtmService.trackEvent((action as TrackingAction).asEvent(queryWhenActionTriggered));
}
mergeMap(() => {

// Return an array of actions that need to be dispatched - we need to (re-)request summary and facet
// (including table) data.
Expand All @@ -95,22 +91,20 @@ export class FileEffects {

/**
* Fetch data to populate facet menus, facet summary and potentially table data. If we are currently on the projects
* tab with a selected project, an additional call to populate the table is called.
* tab with a selected project, an additional call to populate the table is called. Track any cases where the result
* set is empty.
*/
@Effect()
fetchFacets$: Observable<Action> = this.actions$
.pipe(
ofType(FetchFileFacetsRequestAction.ACTION_TYPE),
switchMap((action) =>
this.store.pipe(
select(selectTableQueryParams),
take(1),
map((tableQueryParams) => {
return {action, tableQueryParams};
})
concatMap(action => of(action).pipe(
withLatestFrom(
this.store.pipe(select(selectTableQueryParams), take(1)),
this.store.pipe(select(selectCurrentQuery), take(1))
)
),
switchMap(({action, tableQueryParams}) => {
)),
switchMap(([action, tableQueryParams, currentQuery]) => {

const selectedSearchTermsBySearchKey = tableQueryParams.selectedSearchTermsBySearchKey;
const selectedEntity = tableQueryParams.tableState.selectedEntity;
Expand All @@ -125,11 +119,28 @@ export class FileEffects {
return this.fileService.fetchEntitySearchResults(selectedSearchTermsBySearchKey, tableParams, selectedEntity)
.pipe(
map((entitySearchResults) => {
return {action, entitySearchResults, tableQueryParams};
return {action, entitySearchResults, currentQuery, tableQueryParams};
})
);
}),
mergeMap(({action, tableQueryParams, entitySearchResults}) => {
mergeMap(({action, entitySearchResults, currentQuery, tableQueryParams}) => {

// Track empty search results, using the tracking event triggered from the original action as a base
const emptyResultSet = entitySearchResults.tableModel.data.length === 0;
if ( emptyResultSet ) {

const selectedEntity = tableQueryParams.tableState.selectedEntity;
const index = GAIndex[selectedEntity.toUpperCase()];
this.gtmService.trackEvent({
category: GACategory.SEARCH,
action: GAAction.EXCEPTION,
label: "Empty Result Set",
dimensions: {
[GADimension.CURRENT_QUERY]: currentQuery,
[GADimension.INDEX]: index
}
});
}

// Set up fetch success action
const fetchSuccessAction = new FetchFacetsSuccessAction(entitySearchResults.facets);
Expand Down
Loading