Skip to content

Commit

Permalink
feat: design and data transform for forecast
Browse files Browse the repository at this point in the history
JIRA: F1-277
  • Loading branch information
hackerstanislav committed May 2, 2024
1 parent 886aa5d commit 64d1e05
Show file tree
Hide file tree
Showing 50 changed files with 928 additions and 145 deletions.
5 changes: 5 additions & 0 deletions libs/api-client-tiger/api/api-client-tiger.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -6819,6 +6819,8 @@ export interface ITigerClient {
explain: ReturnType<typeof tigerAfmExplainClientFactory>;
// (undocumented)
export: ReturnType<typeof tigerExportClientFactory>;
// @beta (undocumented)
forecast: ReturnType<typeof tigerForecastClientFactory>;
// (undocumented)
labelElements: ReturnType<typeof tigerLabelElementsClientFactory>;
// (undocumented)
Expand Down Expand Up @@ -14189,6 +14191,9 @@ export const tigerExecutionResultClientFactory: (axios: AxiosInstance) => Pick<A
// @public (undocumented)
export const tigerExportClientFactory: (axios: AxiosInstance) => ExportActionsApiInterface;

// @beta
export const tigerForecastClientFactory: (axios: AxiosInstance) => Pick<AfmActionsApiInterface, "forecast" | "forecastResult">;

// @public (undocumented)
export const tigerLabelElementsClientFactory: (axios: AxiosInstance) => Pick<AfmActionsApiInterface, "computeLabelElementsPost">;

Expand Down
8 changes: 8 additions & 0 deletions libs/api-client-tiger/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import {
import { tigerValidDescendantsClientFactory } from "./validDescendants.js";
import { tigerResultClientFactory, ResultActionsApiInterface } from "./result.js";
import { tigerUserManagementClientFactory } from "./userManagement.js";
import { tigerForecastClientFactory } from "./forecast.js";

export {
tigerExecutionClientFactory,
Expand All @@ -67,6 +68,7 @@ export {
tigerExportClientFactory,
tigerResultClientFactory,
tigerUserManagementClientFactory,
tigerForecastClientFactory,
MetadataConfiguration,
MetadataConfigurationParameters,
MetadataBaseApi,
Expand Down Expand Up @@ -109,6 +111,10 @@ export interface ITigerClient {
export: ReturnType<typeof tigerExportClientFactory>;
result: ReturnType<typeof tigerResultClientFactory>;
userManagement: ReturnType<typeof tigerUserManagementClientFactory>;
/**
* @beta
*/
forecast: ReturnType<typeof tigerForecastClientFactory>;

/**
* Updates tiger client to send the provided API TOKEN in `Authorization` header of all
Expand Down Expand Up @@ -140,6 +146,7 @@ export const tigerClientFactory = (axios: AxiosInstance): ITigerClient => {
const exportFactory = tigerExportClientFactory(axios);
const result = tigerResultClientFactory(axios);
const userManagement = tigerUserManagementClientFactory(axios);
const forecast = tigerForecastClientFactory(axios);

return {
axios,
Expand All @@ -161,5 +168,6 @@ export const tigerClientFactory = (axios: AxiosInstance): ITigerClient => {
setAxiosAuthorizationToken(axios, token);
},
export: exportFactory,
forecast,
};
};
11 changes: 11 additions & 0 deletions libs/api-client-tiger/src/forecast.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// (C) 2019-2024 GoodData Corporation
import { AxiosInstance } from "axios";
import { ActionsApi, ActionsApiInterface } from "./generated/afm-rest-api/index.js";

/**
* Tiger forecast client factory
* @beta
*/
export const tigerForecastClientFactory = (
axios: AxiosInstance,
): Pick<ActionsApiInterface, "forecast" | "forecastResult"> => new ActionsApi(undefined, "", axios);
2 changes: 1 addition & 1 deletion libs/api-client-tiger/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,10 +284,10 @@ export {
GdStorageFile,
GdStorageFileTypeEnum,
ImportCsvRequest,
ImportCsvResponse,
ImportCsvRequestTable,
ImportCsvRequestTableSource,
ImportCsvRequestTableSourceConfig,
ImportCsvResponse,
OrganizationCacheSettings,
OrganizationCacheUsage,
OrganizationCurrentCacheUsage,
Expand Down
6 changes: 6 additions & 0 deletions libs/sdk-backend-base/api/sdk-backend-base.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ import { IExportResult } from '@gooddata/sdk-backend-spi';
import { IFactMetadataObject } from '@gooddata/sdk-model';
import { IFilter } from '@gooddata/sdk-model';
import { IFilterContextDefinition } from '@gooddata/sdk-model';
import { IForecastConfig } from '@gooddata/sdk-backend-spi';
import { IForecastResult } from '@gooddata/sdk-backend-spi';
import { IGetDashboardOptions } from '@gooddata/sdk-backend-spi';
import { IGetDashboardPluginOptions } from '@gooddata/sdk-backend-spi';
import { IGetScheduledMailOptions } from '@gooddata/sdk-backend-spi';
Expand Down Expand Up @@ -136,6 +138,8 @@ export type AnalyticalBackendCallbacks = {
failedResultReadAll?: (error: any, executionId: string) => void;
successfulResultReadWindow?: (offset: number[], size: number[], dataView: IDataView, executionId: string) => void;
failedResultReadWindow?: (offset: number[], size: number[], error: any, executionId: string) => void;
successfulForecastResultReadAll?: (forecastResult: IForecastResult, executionId: string) => void;
failedForecastResultReadAll?: (error: any, executionId: string) => void;
};

// @public
Expand Down Expand Up @@ -398,6 +402,8 @@ export abstract class DecoratedExecutionResult implements IExecutionResult {
// (undocumented)
readAll(): Promise<IDataView>;
// (undocumented)
readForecastAll(config: IForecastConfig): Promise<IForecastResult>;
// (undocumented)
readWindow(offset: number[], size: number[]): Promise<IDataView>;
// (undocumented)
transform(): IPreparedExecution;
Expand Down
17 changes: 17 additions & 0 deletions libs/sdk-backend-base/src/cachingBackend/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {
IElementsQueryResult,
IExecutionFactory,
IExecutionResult,
IForecastConfig,
IForecastResult,
IPreparedExecution,
ISecuritySettingsService,
IUserWorkspaceSettings,
Expand Down Expand Up @@ -199,6 +201,8 @@ function windowKey(offset: number[], size: number[]): string {

class WithExecutionResultCaching extends DecoratedExecutionResult {
private allData: Promise<IDataView> | undefined;
private allForecastConfig: IForecastConfig | undefined;
private allForecastData: Promise<IForecastResult> | undefined;
private windows: LRUCache<string, Promise<IDataView>> | undefined;

constructor(
Expand All @@ -224,6 +228,19 @@ class WithExecutionResultCaching extends DecoratedExecutionResult {
return this.allData;
};

public readForecastAll = (config: IForecastConfig): Promise<IForecastResult> => {
// TODO: enable forecasting caching size configuration
if (!this.allForecastData || this.allForecastConfig !== config) {
this.allForecastConfig = config;
this.allForecastData = super.readForecastAll(config).catch((e) => {
this.allForecastData = undefined;
throw e;
});
}

return this.allForecastData;
};

public readWindow = (offset: number[], size: number[]): Promise<IDataView> => {
if (!this.windows) {
return super.readWindow(offset, size);
Expand Down
7 changes: 6 additions & 1 deletion libs/sdk-backend-base/src/customBackend/execution.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// (C) 2019-2023 GoodData Corporation
// (C) 2019-2024 GoodData Corporation

import { AbstractExecutionFactory } from "../toolkit/execution.js";
import {
Expand Down Expand Up @@ -27,6 +27,7 @@ import {
NotImplemented,
IExplainProvider,
ExplainType,
IForecastResult,
} from "@gooddata/sdk-backend-spi";
import isEqual from "lodash/isEqual.js";
import {
Expand Down Expand Up @@ -160,6 +161,10 @@ class CustomExecutionResult implements IExecutionResult {
});
};

public readForecastAll(): Promise<IForecastResult> {
throw new NotSupported("Forecasting is not supported by the custom backend.");
}

public readWindow = (offset: number[], size: number[]): Promise<IDataView> => {
return this.state.authApiCall((client) => {
if (!this.config.dataProvider) {
Expand Down
21 changes: 20 additions & 1 deletion libs/sdk-backend-base/src/decoratedBackend/execution.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// (C) 2019-2023 GoodData Corporation
// (C) 2019-2024 GoodData Corporation
import {
IDataView,
IForecastView,
IExecutionFactory,
IExecutionResult,
IExportConfig,
Expand All @@ -9,6 +10,8 @@ import {
ExplainConfig,
IExplainProvider,
ExplainType,
IForecastResult,
IForecastConfig,
} from "@gooddata/sdk-backend-spi";
import {
IAttributeOrMeasure,
Expand Down Expand Up @@ -171,6 +174,10 @@ export abstract class DecoratedExecutionResult implements IExecutionResult {
return this.decorated.readAll();
}

public readForecastAll(config: IForecastConfig): Promise<IForecastResult> {
return this.decorated.readForecastAll(config);
}

public readWindow(offset: number[], size: number[]): Promise<IDataView> {
return this.decorated.readWindow(offset, size);
}
Expand Down Expand Up @@ -205,9 +212,13 @@ export abstract class DecoratedDataView implements IDataView {
public definition: IExecutionDefinition;
public result: IExecutionResult;
public warnings?: IResultWarning[];
public forecastConfig?: IForecastConfig;
public forecastResult?: IForecastResult;

constructor(private readonly decorated: IDataView, result?: IExecutionResult) {
this.result = result ?? decorated.result;
this.forecastConfig = decorated.forecastConfig;
this.forecastResult = decorated.forecastResult;

this.count = decorated.count;
this.data = decorated.data;
Expand All @@ -227,4 +238,12 @@ export abstract class DecoratedDataView implements IDataView {
public fingerprint(): string {
return this.decorated.fingerprint();
}

public withForecast(config: IForecastConfig, result: IForecastResult): IDataView {
return this.decorated.withForecast(config, result);
}

public forecast(): IForecastView {
return this.decorated.forecast();
}
}
17 changes: 17 additions & 0 deletions libs/sdk-backend-base/src/dummyBackend/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ import {
IWorkspaceExportDefinitionsService,
IDataFiltersService,
IWorkspaceLogicalModelService,
IForecastResult,
IForecastConfig,
IForecastView,
} from "@gooddata/sdk-backend-spi";
import {
defFingerprint,
Expand Down Expand Up @@ -256,6 +259,17 @@ export function dummyDataView(
equals(other: IDataView): boolean {
return fp === other.fingerprint();
},
forecast(): IForecastView {
return {
headerItems: [],
prediction: [],
low: [],
high: [],
};
},
withForecast(_config: IForecastConfig, _result: IForecastResult): IDataView {
throw new NotSupported("not supported");
},
};
}

Expand Down Expand Up @@ -385,6 +399,9 @@ function dummyExecutionResult(
readWindow(_1: number[], _2: number[]): Promise<IDataView> {
return dummyRead();
},
readForecastAll(): Promise<IForecastResult> {
throw new NotSupported("Forecasting is not supported in dummy backend.");
},
fingerprint(): string {
return fp;
},
Expand Down
38 changes: 37 additions & 1 deletion libs/sdk-backend-base/src/eventingBackend/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
// (C) 2007-2021 GoodData Corporation
// (C) 2007-2024 GoodData Corporation
import isEmpty from "lodash/isEmpty.js";
import { v4 as uuid } from "uuid";
import {
IAnalyticalBackend,
IDataView,
IExecutionResult,
IForecastResult,
IPreparedExecution,
IForecastConfig,
} from "@gooddata/sdk-backend-spi";
import { IExecutionDefinition } from "@gooddata/sdk-model";

Expand Down Expand Up @@ -75,6 +77,24 @@ class WithExecutionResultEventing extends DecoratedExecutionResult {
});
};

public readForecastAll(config: IForecastConfig): Promise<IForecastResult> {
const { successfulForecastResultReadAll, failedForecastResultReadAll } = this.callbacks;

const promisedDataView = super.readForecastAll(config);

return promisedDataView
.then((res) => {
successfulForecastResultReadAll?.(res, this.executionId);

return res;
})
.catch((e) => {
failedForecastResultReadAll?.(e, this.executionId);

throw e;
});
}

public readWindow = (offset: number[], size: number[]): Promise<IDataView> => {
const { successfulResultReadWindow, failedResultReadWindow } = this.callbacks;

Expand Down Expand Up @@ -166,6 +186,22 @@ export type AnalyticalBackendCallbacks = {
* @param executionId - unique ID assigned to each execution that can be used to correlate individual events that "belong" to the same execution
*/
failedResultReadWindow?: (offset: number[], size: number[], error: any, executionId: string) => void;

/**
* Called success when forecast results read
*
* @param forecastResult - forecast result
* @param executionId - unique ID assigned to each execution that can be used to correlate individual events that "belong" to the same execution
*/
successfulForecastResultReadAll?: (forecastResult: IForecastResult, executionId: string) => void;

/**
* Called when forecast results read failed
*
* @param error - error from the underlying backend, contractually this should be an instance of AnalyticalBackendError
* @param executionId - unique ID assigned to each execution that can be used to correlate individual events that "belong" to the same execution
*/
failedForecastResultReadAll?: (error: any, executionId: string) => void;
};

/**
Expand Down
Loading

0 comments on commit 64d1e05

Please sign in to comment.