Skip to content

Commit

Permalink
FEATURE: RAIL-1016 Export executeAfm paging functions
Browse files Browse the repository at this point in the history
  • Loading branch information
David Ocetnik committed Aug 1, 2018
1 parent 9e09ffd commit 81740bb
Show file tree
Hide file tree
Showing 9 changed files with 283 additions and 180 deletions.
2 changes: 1 addition & 1 deletion src/DataLayer/adapters/ExecuteAfmAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export class ExecuteAfmAdapter implements IAdapter<Execution.IExecutionResponses
return this.sdk.execution.getExecutionResponse(this.projectId, execution);
};

const resultFactory = this.sdk.execution.fetchExecutionResult;
const resultFactory = this.sdk.execution.getPartialExecutionResult;

const dataSource = new DataSource<Execution.IExecutionResponses>(
execFactory,
Expand Down
14 changes: 7 additions & 7 deletions src/DataLayer/dataSources/DataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ export class DataSource<T> implements IDataSource<T> {
private afm: AFM.IAfm,
private fingerprint?: string,
private responseFactory?: (resultSpec: AFM.IResultSpec) => Promise<Execution.IExecutionResponse>,
private resultFactory?:
(executionResultUri: string, offset: number[], limit: number[]) => Promise<Execution.IExecutionResult>
private resultFactory?: (executionResultUri: string, limit: number[], offset: number[]) =>
Promise<Execution.IExecutionResult | null>
) {
this.executionPromises = {};
}
Expand All @@ -32,8 +32,8 @@ export class DataSource<T> implements IDataSource<T> {

public getPage(
resultSpec: AFM.IResultSpec,
offset: number[] = [],
limit: number[] = []
limit: number[] = [],
offset: number[] = []
): Promise<Execution.IExecutionResponses> {
const resultSpecFingerprint = stringify(resultSpec);
if (!this.responseFactory) {
Expand Down Expand Up @@ -62,10 +62,10 @@ export class DataSource<T> implements IDataSource<T> {

return this.resultFactory(
executionResponse.links.executionResult,
safeOffset,
safeLimit
safeLimit,
safeOffset
).then(
(executionResult: Execution.IExecutionResult) => ({
(executionResult: Execution.IExecutionResult | null) => ({
executionResult,
executionResponse
})
Expand Down
32 changes: 16 additions & 16 deletions src/DataLayer/dataSources/tests/DataSource.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ describe('DataSource', () => {

const dataSource = new DataSource(execFactory, afm, 'finger', responseFactory, resultFactory);
expect(() => {
dataSource.getPage(resultSpec, [0, 0], [DEFAULT_LIMIT, DEFAULT_LIMIT]);
dataSource.getPage(resultSpec, [DEFAULT_LIMIT, DEFAULT_LIMIT], [0, 0]);
}).toThrow();
});

Expand All @@ -90,18 +90,18 @@ describe('DataSource', () => {

const dataSource = new DataSource(execFactory, afm, 'finger', responseFactory, resultFactory);
try {
await dataSource.getPage(resultSpec, [0, 0], [DEFAULT_LIMIT, DEFAULT_LIMIT]);
await dataSource.getPage(resultSpec, [DEFAULT_LIMIT, DEFAULT_LIMIT], [0, 0]);
} catch (e) {
expect(e).toBeInstanceOf(Error);
}
});

it('should use responseFactory+resultFactory', async () => {
const { responseFactory, resultFactory, dataSource } = createDataSource();
const responses = await dataSource.getPage(resultSpec, [0, 0], [DEFAULT_LIMIT, DEFAULT_LIMIT]);
const responses = await dataSource.getPage(resultSpec, [DEFAULT_LIMIT, DEFAULT_LIMIT], [0, 0]);

expect(responseFactory).toBeCalledWith(resultSpec);
expect(resultFactory).toBeCalledWith('url1', [0, 0], [DEFAULT_LIMIT, DEFAULT_LIMIT]);
expect(resultFactory).toBeCalledWith('url1', [DEFAULT_LIMIT, DEFAULT_LIMIT], [0, 0]);
expect(responses).toEqual({
executionResponse: { links: { executionResult: 'url1' } },
executionResult: { data: [] }
Expand All @@ -110,7 +110,7 @@ describe('DataSource', () => {

it('should cache first response', async () => {
const { responseFactory, resultFactory, dataSource } = createDataSource();
await dataSource.getPage(resultSpec, [0, 0], [DEFAULT_LIMIT, DEFAULT_LIMIT]);
await dataSource.getPage(resultSpec, [DEFAULT_LIMIT, DEFAULT_LIMIT], [0, 0]);
const responses2 = await dataSource.getPage(
resultSpec,
[DEFAULT_LIMIT, DEFAULT_LIMIT],
Expand All @@ -121,8 +121,8 @@ describe('DataSource', () => {
expect(resultFactory).toHaveBeenCalledTimes(2);
expect(resultFactory.mock.calls[0]).toEqual([
'url1',
[0, 0],
[DEFAULT_LIMIT, DEFAULT_LIMIT]
[DEFAULT_LIMIT, DEFAULT_LIMIT],
[0, 0]
]);
expect(resultFactory.mock.calls[1]).toEqual([
'url1',
Expand All @@ -138,16 +138,16 @@ describe('DataSource', () => {
it('should be able to handle two different result spec requests', async () => {
const resultSpec2: AFM.IResultSpec = { dimensions: [ ] };
const { responseFactory, resultFactory, dataSource } = createDataSource();
await dataSource.getPage(resultSpec, [0, 0], [DEFAULT_LIMIT, DEFAULT_LIMIT]);
await dataSource.getPage(resultSpec, [DEFAULT_LIMIT, DEFAULT_LIMIT], [0, 0]);
await dataSource.getPage(resultSpec, [DEFAULT_LIMIT, DEFAULT_LIMIT], [10, 10]);
const responsesRS2 = await dataSource.getPage(resultSpec2, [0, 0], [DEFAULT_LIMIT, DEFAULT_LIMIT]);
const responsesRS2 = await dataSource.getPage(resultSpec2, [DEFAULT_LIMIT, DEFAULT_LIMIT], [0, 0]);

expect(responseFactory).toHaveBeenCalledTimes(2);
expect(resultFactory).toHaveBeenCalledTimes(3);
expect(resultFactory.mock.calls[2]).toEqual([
'urlRS2',
[0, 0],
[DEFAULT_LIMIT, DEFAULT_LIMIT]
[DEFAULT_LIMIT, DEFAULT_LIMIT],
[0, 0]
]);
expect(responsesRS2).toEqual({
executionResponse: { links: { executionResult: 'urlRS2' } },
Expand All @@ -161,10 +161,10 @@ describe('DataSource', () => {

const { responseFactory, resultFactory, dataSource } = createDataSource();
const overLimit = [DEFAULT_LIMIT + 2, DEFAULT_LIMIT + 2];
const responses = await dataSource.getPage(resultSpec, [0, 0], overLimit);
const responses = await dataSource.getPage(resultSpec, overLimit, [0, 0]);

expect(responseFactory).toBeCalledWith(resultSpec);
expect(resultFactory).toBeCalledWith('url1', [0, 0], [DEFAULT_LIMIT, DEFAULT_LIMIT]);
expect(resultFactory).toBeCalledWith('url1', [DEFAULT_LIMIT, DEFAULT_LIMIT], [0, 0]);
expect(responses).toEqual({
executionResponse: { links: { executionResult: 'url1' } },
executionResult: { data: [] }
Expand All @@ -176,14 +176,14 @@ describe('DataSource', () => {

it('should work with a single dimension', async () => {
const { responseFactory, resultFactory, dataSource } = createDataSource();
const responses = await dataSource.getPage(resultSpec, [0], [DEFAULT_LIMIT]);
const responses = await dataSource.getPage(resultSpec, [DEFAULT_LIMIT], [0]);

expect(responseFactory).toHaveBeenCalledTimes(1);
expect(resultFactory).toHaveBeenCalledTimes(1);
expect(resultFactory).toHaveBeenCalledWith(
'url1',
[0],
[DEFAULT_LIMIT]
[DEFAULT_LIMIT],
[0]
);
expect(responses).toEqual({
executionResponse: { links: { executionResult: 'url1' } },
Expand Down
2 changes: 1 addition & 1 deletion src/DataLayer/interfaces/DataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ export interface IDataSource<T> {
getData(resultSpec: AFM.IResultSpec): Promise<T>;
getAfm(): AFM.IAfm;
getFingerprint(): string;
getPage(resultSpec: AFM.IResultSpec, offset: number[], limit: number[]): Promise<Execution.IExecutionResponses>;
getPage(resultSpec: AFM.IResultSpec, limit: number[], offset: number[]): Promise<Execution.IExecutionResponses>;
}
4 changes: 2 additions & 2 deletions src/DataLayer/utils/DummyDataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,9 @@ export class DummyDataSource<T> implements IDataSource<T> {
public getPage(
resultSpec: AFM.IResultSpec,
// tslint:disable-next-line:variable-name
_offset: number[],
_limit: number[],
// tslint:disable-next-line:variable-name
_limit: number[]
_offset: number[]
): Promise<Execution.IExecutionResponses> {
this.resultSpec = resultSpec;

Expand Down
6 changes: 4 additions & 2 deletions src/execution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import { MetadataModule } from './metadata';
export class ExecutionModule {
public readonly executeAfm: ExecuteAfmModule['executeAfm'];
public readonly getExecutionResponse: ExecuteAfmModule['getExecutionResponse'];
public readonly fetchExecutionResult: ExecuteAfmModule['fetchExecutionResult'];
public readonly getPartialExecutionResult: ExecuteAfmModule['getPartialExecutionResult'];
public readonly getExecutionResult: ExecuteAfmModule['getExecutionResult'];
private readonly executeAfmModule: ExecuteAfmModule;
private readonly xhr: XhrModule;
private readonly md: MetadataModule;
Expand All @@ -24,7 +25,8 @@ export class ExecutionModule {
this.executeAfmModule = new ExecuteAfmModule(xhr);
this.executeAfm = this.executeAfmModule.executeAfm.bind(this.executeAfmModule);
this.getExecutionResponse = this.executeAfmModule.getExecutionResponse.bind(this.executeAfmModule);
this.fetchExecutionResult = this.executeAfmModule.fetchExecutionResult.bind(this.executeAfmModule);
this.getPartialExecutionResult = this.executeAfmModule.getPartialExecutionResult.bind(this.executeAfmModule);
this.getExecutionResult = this.executeAfmModule.getExecutionResult.bind(this.executeAfmModule);
this.xhr = xhr;
this.md = md;
}
Expand Down
64 changes: 53 additions & 11 deletions src/execution/execute-afm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,25 @@ export class ExecuteAfmModule {
.then(unwrapExecutionResponse);
}

public fetchExecutionResult(executionResultUri: string, offset: number[], limit: number[])
: Promise<Execution.IExecutionResult> {
const uri = replaceLimitAndOffsetInUri(executionResultUri, limit, offset);
/**
* Get one page of Result from Execution (with requested limit and offset)
*
* @method getPartialExecutionResult
* @param {string} executionResultUri
* @param {number[]} limit - limit for each dimension
* @param {number[]} offset - offset for each dimension
*
* @returns {Promise<Execution.IExecutionResult | null>}
* Promise with `executionResult` or `null` (null means empty response - HTTP 204)
* See https://github.com/gooddata/gooddata-typings/blob/v2.1.0/src/Execution.ts#L88
*/
public getPartialExecutionResult(executionResultUri: string, limit: number[], offset: number[])
: Promise<Execution.IExecutionResult | null> {
const executionResultUriQueryPart = getExecutionResultUriQueryPart(executionResultUri);
const numOfDimensions = Number(qs.parse(executionResultUriQueryPart).dimensions);
validateNumOfDimensions(numOfDimensions);

return this.xhr.get(uri)
.then(apiResponse => apiResponse.getData())
.then(unwrapExecutionResult);
return this.getPage(executionResultUri, limit, offset);
}

/**
Expand All @@ -71,9 +83,9 @@ export class ExecuteAfmModule {
* Promise with `executionResult` or `null` (null means empty response - HTTP 204)
* See https://github.com/gooddata/gooddata-typings/blob/v2.1.0/src/Execution.ts#L88
*/
private getExecutionResult(executionResultUri: string)
public getExecutionResult(executionResultUri: string)
: Promise<Execution.IExecutionResult | null> {
const executionResultUriQueryPart = executionResultUri.split(/\?(.+)/)[1];
const executionResultUriQueryPart = getExecutionResultUriQueryPart(executionResultUri);
const numOfDimensions = Number(qs.parse(executionResultUriQueryPart).dimensions);
validateNumOfDimensions(numOfDimensions);

Expand All @@ -83,18 +95,33 @@ export class ExecuteAfmModule {
return this.getAllPages(executionResultUri, limit, offset);
}

private getPage(
executionResultUri: string,
limit: number[],
offset: number[]
): Promise<Execution.IExecutionResult | null> {
return this.fetchExecutionResult(executionResultUri, limit, offset)
.then((executionResultWrapper: Execution.IExecutionResultWrapper | null) => {
return executionResultWrapper
? unwrapExecutionResult(executionResultWrapper)
: null;
});
}

private getAllPages(
executionResultUri: string,
limit: number[],
offset: number[],
prevExecutionResult?: Execution.IExecutionResult
): Promise<Execution.IExecutionResult | null> {
return this.fetchExecutionResult(executionResultUri, offset, limit)
.then((executionResult: Execution.IExecutionResult | null) => {
if (!executionResult) {
return this.fetchExecutionResult(executionResultUri, limit, offset)
.then((executionResultWrapper: Execution.IExecutionResultWrapper | null) => {
if (!executionResultWrapper) {
return null;
}

const executionResult = unwrapExecutionResult(executionResultWrapper);

const newExecutionResult = prevExecutionResult
? mergePage(prevExecutionResult, executionResult)
: executionResult;
Expand All @@ -108,6 +135,21 @@ export class ExecuteAfmModule {
: newExecutionResult;
});
}

private fetchExecutionResult(executionResultUri: string, limit: number[], offset: number[])
: Promise<Execution.IExecutionResultWrapper | null> {
const uri = replaceLimitAndOffsetInUri(executionResultUri, limit, offset);

return this.xhr.get(uri).then(
apiResponse => apiResponse.response.status === 204
? null
: apiResponse.getData()
);
}
}

function getExecutionResultUriQueryPart(executionResultUri: string): string {
return executionResultUri.split(/\?(.+)/)[1];
}

function unwrapExecutionResponse(executionResponseWrapper: Execution.IExecutionResponseWrapper)
Expand Down
8 changes: 5 additions & 3 deletions src/execution/experimental-executions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -511,9 +511,11 @@ export class ExperimentalExecutionsModule {
*/
public getData(projectId: string, columns: any[], executionConfiguration: any = {}, settings: any = {}) {

// tslint:disable-next-line:no-console
console.warn('ExperimentalExecutionsModule is deprecated and is no longer being maintained. ' +
'Please migrate to the ExecuteAfmModule.');
if (process.env.NODE_ENV !== 'test') {
// tslint:disable-next-line:no-console
console.warn('ExperimentalExecutionsModule is deprecated and is no longer being maintained. ' +
'Please migrate to the ExecuteAfmModule.');
}

const executedReport: any = {
isLoaded: false
Expand Down
Loading

0 comments on commit 81740bb

Please sign in to comment.