Skip to content

Commit

Permalink
fix(graphql): disable pagination should remove any page info from query
Browse files Browse the repository at this point in the history
- renamed previous `GraphqlResult` to `GraphqlPaginatedResult` and use `GraphqlResult` without Pagination info
  • Loading branch information
ghiscoding-SE committed Jan 6, 2020
1 parent c76474d commit 63190c8
Show file tree
Hide file tree
Showing 13 changed files with 166 additions and 56 deletions.
9 changes: 5 additions & 4 deletions src/app/examples/grid-graphql.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
Filters,
Formatters,
GraphqlResult,
GraphqlPaginatedResult,
GraphqlService,
GraphqlServiceOption,
GridOption,
Expand Down Expand Up @@ -180,7 +181,7 @@ export class GridGraphqlComponent implements OnInit, OnDestroy {
// onInit: (query) => this.getCustomerApiCall(query)
preProcess: () => this.displaySpinner(true),
process: (query) => this.getCustomerApiCall(query),
postProcess: (result: GraphqlResult) => {
postProcess: (result: GraphqlResult | GraphqlPaginatedResult) => {
this.metrics = result.metrics;
this.displaySpinner(false);
}
Expand Down Expand Up @@ -220,12 +221,12 @@ export class GridGraphqlComponent implements OnInit, OnDestroy {
}

/**
* Calling your GraphQL backend server should always return a Promise or Observable of type GraphqlResult
* Calling your GraphQL backend server should always return a Promise or Observable of type GraphqlPaginatedResult (or GraphqlResult)
*
* @param query
* @return Promise<GraphqlResult> | Observable<GraphqlResult>
* @return Promise<GraphqlPaginatedResult> | Observable<GraphqlPaginatedResult>
*/
getCustomerApiCall(query: string): Promise<GraphqlResult> {
getCustomerApiCall(query: string): Promise<GraphqlResult | GraphqlPaginatedResult> {
// in your case, you will call your WebAPI function (wich needs to return a Promise)
// for the demo purpose, we will call a mock WebAPI function
const mockedResult = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -588,7 +588,7 @@ describe('Angular-Slickgrid Custom Component instantiated via Constructor', () =
expect(component.gridOptions.backendServiceApi.internalPostProcess).toEqual(expect.any(Function));
});

it('should execute the "internalPostProcess" callback method that was created by "createBackendApiInternalPostProcessCallback"', () => {
it('should execute the "internalPostProcess" callback method that was created by "createBackendApiInternalPostProcessCallback" with Pagination', () => {
jest.spyOn(component.gridOptions.backendServiceApi.service, 'getDatasetName').mockReturnValue('users');
const spy = jest.spyOn(component, 'refreshGridData');

Expand All @@ -599,7 +599,20 @@ describe('Angular-Slickgrid Custom Component instantiated via Constructor', () =
expect(component.gridOptions.backendServiceApi.internalPostProcess).toEqual(expect.any(Function));
});

it('should execute the "internalPostProcess" callback method that was created by "createBackendApiInternalPostProcessCallback" without Pagination (when disabled)', () => {
component.gridOptions.enablePagination = false;
jest.spyOn(component.gridOptions.backendServiceApi.service, 'getDatasetName').mockReturnValue('users');
const spy = jest.spyOn(component, 'refreshGridData');

component.ngAfterViewInit();
component.gridOptions.backendServiceApi.internalPostProcess({ data: { users: [{ firstName: 'John' }] } });

expect(spy).toHaveBeenCalled();
expect(component.gridOptions.backendServiceApi.internalPostProcess).toEqual(expect.any(Function));
});

xit('should execute the "internalPostProcess" callback method but return an empty dataset when dataset name does not match "getDatasetName"', () => {
component.gridOptions.enablePagination = true;
jest.spyOn(component.gridOptions.backendServiceApi.service, 'getDatasetName').mockReturnValue('users');
const spy = jest.spyOn(component, 'refreshGridData');

Expand Down Expand Up @@ -668,7 +681,7 @@ describe('Angular-Slickgrid Custom Component instantiated via Constructor', () =
expect(refreshSpy).toHaveBeenCalledWith(mockData);
});

it('should execute the process method on initialization when "executeProcessCommandOnInit" is set as a backend service options with a Promise', (done) => {
it('should execute the process method on initialization when "executeProcessCommandOnInit" is set as a backend service options with a Promise and Pagination enabled', (done) => {
const now = new Date();
const query = `query { users (first:20,offset:0) { totalCount, nodes { id,name,gender,company } } }`;
const processResult = {
Expand All @@ -690,7 +703,7 @@ describe('Angular-Slickgrid Custom Component instantiated via Constructor', () =
}, 5);
});

it('should execute the process method on initialization when "executeProcessCommandOnInit" is set as a backend service options with an Observable', (done) => {
it('should execute the process method on initialization when "executeProcessCommandOnInit" is set as a backend service options with an Observable and Pagination enabled', (done) => {
const now = new Date();
const query = `query { users (first:20,offset:0) { totalCount, nodes { id,name,gender,company } } }`;
const processResult = {
Expand All @@ -711,6 +724,27 @@ describe('Angular-Slickgrid Custom Component instantiated via Constructor', () =
}, 5);
});

it('should execute the process method on initialization when "executeProcessCommandOnInit" is set as a backend service options with an Observable but without Pagination (when disabled)', (done) => {
const now = new Date();
const query = `query { users (first:20,offset:0) { totalCount, nodes { id,name,gender,company } } }`;
const processResult = {
data: { users: [] },
metrics: { startTime: now, endTime: now, executionTime: 0, totalItemCount: 0 }
};
const processSpy = jest.spyOn(component.gridOptions.backendServiceApi, 'process').mockReturnValue(of(processResult));
jest.spyOn(component.gridOptions.backendServiceApi.service, 'buildQuery').mockReturnValue(query);

component.gridOptions.backendServiceApi.service.options = { executeProcessCommandOnInit: true };
component.ngAfterViewInit();

expect(processSpy).toHaveBeenCalled();

setTimeout(() => {
expect(mockExecuteBackendProcess).toHaveBeenCalledWith(expect.toBeDate(), processResult, component.gridOptions.backendServiceApi, 0);
done();
}, 5);
});

it('should throw an error when the process method on initialization when "executeProcessCommandOnInit" is set as a backend service options from a Promise', (done) => {
const mockError = { error: '404' };
const query = `query { users (first:20,offset:0) { totalCount, nodes { id,name,gender,company } } }`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
BackendServiceOption,
Column,
ExtensionName,
GraphqlPaginatedResult,
GraphqlResult,
GridOption,
GridStateChange,
Expand Down Expand Up @@ -275,12 +276,13 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy, OnIn
if (backendApi && backendApi.service) {
// internalPostProcess only works (for now) with a GraphQL Service, so make sure it is of that type
if (backendApi.service instanceof GraphqlService || typeof backendApi.service.getDatasetName === 'function') {
backendApi.internalPostProcess = (processResult: GraphqlResult) => {
backendApi.internalPostProcess = (processResult: GraphqlResult | GraphqlPaginatedResult) => {
const datasetName = (backendApi && backendApi.service && typeof backendApi.service.getDatasetName === 'function') ? backendApi.service.getDatasetName() : '';
this._dataset = [];
if (processResult && processResult.data && processResult.data[datasetName]) {
this._dataset = processResult.data[datasetName].nodes;
this.refreshGridData(this._dataset, processResult.data[datasetName].totalCount);
this._dataset = processResult.data[datasetName].hasOwnProperty('nodes') ? (processResult as GraphqlPaginatedResult).data[datasetName].nodes : (processResult as GraphqlResult).data[datasetName];
const totalCount = processResult.data[datasetName].hasOwnProperty('totalCount') ? (processResult as GraphqlPaginatedResult).data[datasetName].totalCount : (processResult as GraphqlResult).data[datasetName].length;
this.refreshGridData(this._dataset, totalCount || 0);
}
};
}
Expand Down Expand Up @@ -545,11 +547,11 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy, OnIn
// the processes can be Promises or Observables (like Angular HttpClient)
const totalItems = this.gridOptions && this.gridOptions.pagination && this.gridOptions.pagination.totalItems;
if (process instanceof Promise && process.then) {
process.then((processResult: GraphqlResult | any) => executeBackendProcessesCallback(startTime, processResult, backendApi, totalItems))
process.then((processResult: GraphqlResult | GraphqlPaginatedResult | any) => executeBackendProcessesCallback(startTime, processResult, backendApi, totalItems))
.catch((error: any) => onBackendError(error, backendApi));
} else if (isObservable(process)) {
process.subscribe(
(processResult: GraphqlResult | any) => executeBackendProcessesCallback(startTime, processResult, backendApi, totalItems),
(processResult: GraphqlResult | GraphqlPaginatedResult | any) => executeBackendProcessesCallback(startTime, processResult, backendApi, totalItems),
(error: any) => onBackendError(error, backendApi)
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import { Observable } from 'rxjs';

import { BackendService } from './index';
import { BackendServiceOption } from './backendServiceOption.interface';
import { Observable } from 'rxjs';
import { GraphqlResult } from './graphqlResult.interface';
import { GraphqlPaginatedResult } from './graphqlPaginatedResult.interface';

export interface BackendEventChanged {
options?: BackendServiceOption;

/** On init (or on page load), what action to perform? */
onInit?: (query: string) => Promise<GraphqlResult | any> | Observable<GraphqlResult | any>;
onInit?: (query: string) => Promise<GraphqlResult | GraphqlPaginatedResult | any> | Observable<GraphqlResult | GraphqlPaginatedResult | any>;

/** Before executing the query, what action to perform? For example, start a spinner */
preProcess?: () => void;

/** On Processing, we get the query back from the service, and we need to provide a Promise/Observable. For example: this.http.get(myGraphqlUrl) */
process: (query: string) => Promise<GraphqlResult | any> | Observable<GraphqlResult | any>;
process: (query: string) => Promise<GraphqlResult | GraphqlPaginatedResult | any> | Observable<GraphqlResult | GraphqlPaginatedResult | any>;


/** After executing the query, what action to perform? For example, stop the spinner */
Expand All @@ -29,5 +31,5 @@ export interface BackendEventChanged {
* INTERNAL USAGE ONLY by Angular-Slickgrid
* This internal process will be run just before postProcess and is meant to refresh the Dataset & Pagination after a GraphQL call
*/
internalPostProcess?: (result: GraphqlResult) => void;
internalPostProcess?: (result: GraphqlResult | GraphqlPaginatedResult) => void;
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { Observable } from 'rxjs';

import { OdataOption } from './odataOption.interface';
import { GraphqlResult } from './graphqlResult.interface';
import { BackendService } from './backendService.interface';
import { GraphqlResult } from './graphqlResult.interface';
import { GraphqlPaginatedResult } from './graphqlPaginatedResult.interface';
import { GraphqlServiceOption } from './graphqlServiceOption.interface';
import { Observable } from 'rxjs';

export interface BackendServiceApi {
/** Backend Service Options */
Expand All @@ -15,16 +17,16 @@ export interface BackendServiceApi {
onError?: (e) => void;

/** On init (or on page load), what action to perform? */
onInit?: (query: string) => Promise<GraphqlResult | any> | Observable<GraphqlResult | any>;
onInit?: (query: string) => Promise<GraphqlResult | GraphqlPaginatedResult | any> | Observable<GraphqlResult | GraphqlPaginatedResult | any>;

/** Before executing the query, what action to perform? For example, start a spinner */
preProcess?: () => void;

/** On Processing, we get the query back from the service, and we need to provide a Promise/Observable. For example: this.http.get(myGraphqlUrl) */
process: (query: string) => Promise<GraphqlResult | any> | Observable<GraphqlResult | any>;
process: (query: string) => Promise<GraphqlResult | GraphqlPaginatedResult | any> | Observable<GraphqlResult | GraphqlPaginatedResult | any>;

/** After executing the query, what action to perform? For example, stop the spinner */
postProcess?: (response: GraphqlResult | any) => void;
postProcess?: (response: GraphqlResult | GraphqlPaginatedResult | any) => void;

/** How long to wait until we start querying backend to avoid sending too many requests to backend server. Default to 750ms */
filterTypingDebounce?: number;
Expand All @@ -33,5 +35,5 @@ export interface BackendServiceApi {
* INTERNAL USAGE ONLY by Angular-Slickgrid
* This internal process will be run just before postProcess and is meant to refresh the Dataset & Pagination after a GraphQL call
*/
internalPostProcess?: (result: GraphqlResult) => void;
internalPostProcess?: (result: GraphqlResult | GraphqlPaginatedResult) => void;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Metrics } from './metrics.interface';
import { Statistic } from './statistic.interface';

export interface GraphqlPaginatedResult {
data: {
[datasetName: string]: {
nodes: any[];
pageInfo: {
hasNextPage: boolean;
};
totalCount: number;
}
};

/** Some metrics of the last executed query (startTime, endTime, executionTime, itemCount, totalItemCount) */
metrics?: Metrics;

/** @deprecated please use "metrics" instead */
statistics?: Statistic;
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
import { DelimiterType } from './delimiterType.enum';
import { Metrics } from './metrics.interface';
import { Statistic } from './statistic.interface';

export interface GraphqlResult {
data: {
[datasetName: string]: {
nodes: any[];
pageInfo: {
hasNextPage: boolean;
};
totalCount: number;
}
[datasetName: string]: any[];
};

/** Some metrics of the last executed query (startTime, endTime, executionTime, itemCount, totalItemCount) */
metrics?: Metrics;

/** @deprecated please use "metrics" instead */
Expand Down
1 change: 1 addition & 0 deletions src/app/modules/angular-slickgrid/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export * from './formatterResultObject.interface';
export * from './graphqlCursorPaginationOption.interface';
export * from './graphqlDatasetFilter.interface';
export * from './graphqlFilteringOption.interface';
export * from './graphqlPaginatedResult.interface';
export * from './graphqlPaginationOption.interface';
export * from './graphqlResult.interface';
export * from './graphqlServiceOption.interface';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ describe('backend-utilities', () => {
executeSpy = jest.spyOn(main, 'executeBackendCallback');
});

it('should call "executeBackendCallback" after calling the "refreshBackendDataset" method', () => {
it('should call "executeBackendCallback" after calling the "refreshBackendDataset" method with Pagination', () => {
const query = `query { users (first:20,offset:0) { totalCount, nodes { id,name,gender,company } } }`;
const querySpy = jest.spyOn(gridOptionMock.backendServiceApi.service, 'buildQuery').mockReturnValue(query);

Expand All @@ -105,8 +105,20 @@ describe('backend-utilities', () => {
expect(executeSpy).toHaveBeenCalledWith(gridOptionMock.backendServiceApi, query, null, expect.toBeDate(), gridOptionMock.pagination.totalItems);
});

it('should call "executeBackendCallback" after calling the "refreshBackendDataset" method without Pagination (when disabled)', () => {
gridOptionMock.enablePagination = false;
const query = `query { users { id,name,gender,company } }`;
const querySpy = jest.spyOn(gridOptionMock.backendServiceApi.service, 'buildQuery').mockReturnValue(query);

refreshBackendDataset(gridOptionMock);

expect(querySpy).toHaveBeenCalled();
expect(executeSpy).toHaveBeenCalledWith(gridOptionMock.backendServiceApi, query, null, expect.toBeDate(), gridOptionMock.pagination.totalItems);
});

it('should throw an error when backendServiceApi is undefined', (done) => {
try {
gridOptionMock.enablePagination = true;
gridOptionMock.backendServiceApi = undefined;
refreshBackendDataset(undefined);
} catch (e) {
Expand Down

0 comments on commit 63190c8

Please sign in to comment.