@gooddata/react-components
Version:
GoodData.UI - A powerful JavaScript library for building analytical applications
170 lines (147 loc) • 6.43 kB
text/typescript
// (C) 2007-2020 GoodData Corporation
import { Execution, VisualizationObject } from "@gooddata/typings";
import { ApiResponseError } from "@gooddata/gooddata-js";
import { IntlShape } from "react-intl";
import { ErrorStates, ErrorCodes } from "../constants/errorStates";
import get = require("lodash/get");
import includes = require("lodash/includes");
import * as HttpStatusCodes from "http-status-codes";
import { RuntimeError } from "../errors/RuntimeError";
import { unwrap } from "./utils";
function getJSONFromText(data: string): object {
try {
return JSON.parse(data);
} catch (e) {
return null;
}
}
export function isApiResponseError(error: TypeError | ApiResponseError): error is ApiResponseError {
return error && (error as ApiResponseError).response !== undefined;
}
export interface IErrorMap {
[key: string]: {
icon?: string;
message: string;
description: string;
};
}
export function generateErrorMap(intl: IntlShape): IErrorMap {
const errorMap = {
[ErrorStates.DATA_TOO_LARGE_TO_DISPLAY]: {
icon: "icon-cloud-rain",
message: intl.formatMessage({ id: "visualization.ErrorMessageDataTooLarge" }),
description: intl.formatMessage({ id: "visualization.ErrorDescriptionDataTooLarge" }),
},
[ErrorStates.NOT_FOUND]: {
message: intl.formatMessage({ id: "visualization.ErrorMessageNotFound" }),
description: intl.formatMessage({ id: "visualization.ErrorDescriptionNotFound" }),
},
[ErrorStates.UNAUTHORIZED]: {
message: intl.formatMessage({ id: "visualization.ErrorMessageUnauthorized" }),
description: intl.formatMessage({ id: "visualization.ErrorDescriptionUnauthorized" }),
},
[ErrorStates.NO_DATA]: {
icon: "icon-filter",
message: intl.formatMessage({ id: "visualization.ErrorMessageNoData" }),
description: intl.formatMessage({ id: "visualization.ErrorDescriptionNoData" }),
},
[ErrorStates.UNKNOWN_ERROR]: {
message: intl.formatMessage({ id: "visualization.ErrorMessageGeneric" }),
description: intl.formatMessage({ id: "visualization.ErrorDescriptionGeneric" }),
},
[ErrorStates.GEO_MAPBOX_TOKEN_MISSING]: {
message: intl.formatMessage({ id: "visualization.ErrorMessageGeneric" }),
description: intl.formatMessage({ id: "visualization.ErrorDescriptionMissingMapboxToken" }),
},
};
return errorMap;
}
// CAREFUL: error can also be of different type than listed
export function convertErrors(error: ApiResponseError | TypeError): RuntimeError {
if (!isApiResponseError(error)) {
return new RuntimeError(error.message, error);
}
const errorCode: number = error.response.status;
switch (errorCode) {
case HttpStatusCodes.NO_CONTENT:
return new RuntimeError(ErrorStates.NO_DATA, error);
case HttpStatusCodes.REQUEST_TOO_LONG:
return new RuntimeError(ErrorStates.DATA_TOO_LARGE_TO_COMPUTE, error);
case HttpStatusCodes.BAD_REQUEST:
const message = get(getJSONFromText(error.responseBody), "error.message", "");
if (includes(message, "Attempt to execute protected report unsafely")) {
return new RuntimeError(ErrorStates.PROTECTED_REPORT, error);
} else {
return new RuntimeError(ErrorStates.BAD_REQUEST, error);
}
case HttpStatusCodes.NOT_FOUND:
return new RuntimeError(ErrorStates.NOT_FOUND, error);
case HttpStatusCodes.UNAUTHORIZED:
return new RuntimeError(ErrorStates.UNAUTHORIZED, error);
case ErrorCodes.EMPTY_AFM:
return new RuntimeError(ErrorStates.EMPTY_AFM);
case ErrorCodes.INVALID_BUCKETS:
return new RuntimeError(ErrorStates.INVALID_BUCKETS);
default:
return new RuntimeError(ErrorStates.UNKNOWN_ERROR);
}
}
/** @deprecated */
function isNullExecutionResult(responses: Execution.IExecutionResponses): boolean {
return responses.executionResult === null;
}
function hasEmptyData(responses: Execution.IExecutionResponses): boolean {
return responses.executionResult.data.length === 0;
}
function hasMissingHeaderItems(responses: Execution.IExecutionResponses): boolean {
return !responses.executionResult.headerItems;
}
function isEmptyDataResult(responses: Execution.IExecutionResponses): boolean {
return hasEmptyData(responses) && hasMissingHeaderItems(responses);
}
/**
* isEmptyResult
* is a function that returns true if the execution result is empty (no data points) and false otherwise.
* @param responses:Execution.IExecutionResponses - object with execution response and result
* @return boolean
* @internal
*/
export function isEmptyResult(responses: Execution.IExecutionResponses): boolean {
return isNullExecutionResult(responses) || isEmptyDataResult(responses);
}
export function checkEmptyResult(responses: Execution.IExecutionResponses) {
if (isEmptyResult(responses)) {
throw {
name: "EmptyResultError",
response: {
status: HttpStatusCodes.NO_CONTENT,
json: () => Promise.resolve(responses),
text: () => Promise.resolve(null),
},
};
}
return responses;
}
export const hasDuplicateIdentifiers = (buckets: VisualizationObject.IBucket[]) => {
const buffer: string[] = [];
const duplicates: string[] = [];
buckets.forEach(({ items }) => {
items.forEach((bucketItem: VisualizationObject.BucketItem) => {
const localIdentifier: string = unwrap(bucketItem).localIdentifier;
const isDuplicate = buffer.includes(localIdentifier);
buffer.push(localIdentifier);
if (isDuplicate && !duplicates.includes(localIdentifier)) {
duplicates.push(localIdentifier);
}
});
});
if (duplicates.length > 0) {
// tslint:disable-next-line:no-console max-line-length
console.warn(
`Duplicate identifier${duplicates.length > 1 ? "s" : ""} '${duplicates.join(
", ",
)}' detected in PivotTable. Please make sure all localIdentifiers are unique.`,
);
}
return duplicates.length > 0;
};