Add function to download part of the Data tree as a file (#458)
This commit is contained in:
parent
8722e246e0
commit
f7ec5209cf
|
|
@ -49,6 +49,7 @@
|
||||||
"codemirror": "^5.55.0",
|
"codemirror": "^5.55.0",
|
||||||
"copy-to-clipboard": "^3.3.1",
|
"copy-to-clipboard": "^3.3.1",
|
||||||
"craco-alias": "^2.1.1",
|
"craco-alias": "^2.1.1",
|
||||||
|
"downloadjs": "^1.4.7",
|
||||||
"eslint": "^6.4.0",
|
"eslint": "^6.4.0",
|
||||||
"fast-deep-equal": "^3.1.1",
|
"fast-deep-equal": "^3.1.1",
|
||||||
"flow-bin": "^0.91.0",
|
"flow-bin": "^0.91.0",
|
||||||
|
|
@ -158,6 +159,7 @@
|
||||||
"@storybook/preset-create-react-app": "^3.1.4",
|
"@storybook/preset-create-react-app": "^3.1.4",
|
||||||
"@storybook/react": "^5.3.19",
|
"@storybook/react": "^5.3.19",
|
||||||
"@types/codemirror": "^0.0.96",
|
"@types/codemirror": "^0.0.96",
|
||||||
|
"@types/downloadjs": "^1.4.2",
|
||||||
"@types/jest": "^24.0.22",
|
"@types/jest": "^24.0.22",
|
||||||
"@types/react-beautiful-dnd": "^11.0.4",
|
"@types/react-beautiful-dnd": "^11.0.4",
|
||||||
"@types/react-select": "^3.0.5",
|
"@types/react-select": "^3.0.5",
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,17 @@ const ALERT_STYLE_OPTIONS = [
|
||||||
{ label: "Error", value: "'error'", id: "error" },
|
{ label: "Error", value: "'error'", id: "error" },
|
||||||
{ label: "Warning", value: "'warning'", id: "warning" },
|
{ label: "Warning", value: "'warning'", id: "warning" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const FILE_TYPE_OPTIONS = [
|
||||||
|
{ label: "Plain text", value: "'text/plain'", id: "text/plain" },
|
||||||
|
{ label: "HTML", value: "'text/html'", id: "text/html" },
|
||||||
|
{ label: "CSV", value: "'text/csv'", id: "text/csv" },
|
||||||
|
{ label: "JSON", value: "'application/json'", id: "application/json" },
|
||||||
|
{ label: "JPEG", value: "'image/jpeg'", id: "image/jpeg" },
|
||||||
|
{ label: "PNG", value: "'image/png'", id: "image/png" },
|
||||||
|
{ label: "SVG", value: "'image/svg+xml'", id: "image/svg+xml" },
|
||||||
|
];
|
||||||
|
|
||||||
const ACTION_TRIGGER_REGEX = /^{{([\s\S]*?)\(([\s\S]*?)\)}}$/g;
|
const ACTION_TRIGGER_REGEX = /^{{([\s\S]*?)\(([\s\S]*?)\)}}$/g;
|
||||||
//Old Regex:: /\(\) => ([\s\S]*?)(\([\s\S]*?\))/g;
|
//Old Regex:: /\(\) => ([\s\S]*?)(\([\s\S]*?\))/g;
|
||||||
const ACTION_ANONYMOUS_FUNC_REGEX = /\(\) => (({[\s\S]*?})|([\s\S]*?)(\([\s\S]*?\)))/g;
|
const ACTION_ANONYMOUS_FUNC_REGEX = /\(\) => (({[\s\S]*?})|([\s\S]*?)(\([\s\S]*?\)))/g;
|
||||||
|
|
@ -162,6 +173,73 @@ const storeValueTextGetter = (value: string) => {
|
||||||
return "";
|
return "";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const downloadDataSetter = (changeValue: any, currentValue: string): string => {
|
||||||
|
const matches = [...currentValue.matchAll(ACTION_TRIGGER_REGEX)];
|
||||||
|
const args = matches[0][2].split(",");
|
||||||
|
args[0] = `'${changeValue}'`;
|
||||||
|
const result = currentValue.replace(
|
||||||
|
ACTION_TRIGGER_REGEX,
|
||||||
|
`{{$1(${args.join(",")})}}`,
|
||||||
|
);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
const downloadDataGetter = (value: string) => {
|
||||||
|
const matches = [...value.matchAll(ACTION_TRIGGER_REGEX)];
|
||||||
|
if (matches.length) {
|
||||||
|
const funcArgs = matches[0][2];
|
||||||
|
const arg = funcArgs.split(",")[0];
|
||||||
|
return arg.substring(1, arg.length - 1);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
|
||||||
|
const downloadFileNameSetter = (
|
||||||
|
changeValue: any,
|
||||||
|
currentValue: string,
|
||||||
|
): string => {
|
||||||
|
const matches = [...currentValue.matchAll(ACTION_TRIGGER_REGEX)];
|
||||||
|
const args = matches[0][2].split(",");
|
||||||
|
args[1] = `'${changeValue}'`;
|
||||||
|
return currentValue.replace(
|
||||||
|
ACTION_TRIGGER_REGEX,
|
||||||
|
`{{$1(${args.join(",")})}}`,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const downloadFileNameGetter = (value: string) => {
|
||||||
|
const matches = [...value.matchAll(ACTION_TRIGGER_REGEX)];
|
||||||
|
if (matches.length) {
|
||||||
|
const funcArgs = matches[0][2];
|
||||||
|
const arg = funcArgs.split(",")[1];
|
||||||
|
return arg ? arg.substring(1, arg.length - 1) : "";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
|
||||||
|
const downloadFileTypeSetter = (
|
||||||
|
changeValue: any,
|
||||||
|
currentValue: string,
|
||||||
|
): string => {
|
||||||
|
const matches = [...currentValue.matchAll(ACTION_TRIGGER_REGEX)];
|
||||||
|
const args = matches[0][2].split(",");
|
||||||
|
args[2] = changeValue as string;
|
||||||
|
return currentValue.replace(
|
||||||
|
ACTION_TRIGGER_REGEX,
|
||||||
|
`{{$1(${args.join(",")})}}`,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const downloadFileTypeGetter = (value: string) => {
|
||||||
|
const matches = [...value.matchAll(ACTION_TRIGGER_REGEX)];
|
||||||
|
if (matches.length) {
|
||||||
|
const funcArgs = matches[0][2];
|
||||||
|
const arg = funcArgs.split(",")[2];
|
||||||
|
return arg ? arg.trim() : "";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
|
||||||
type ActionCreatorProps = {
|
type ActionCreatorProps = {
|
||||||
value: string;
|
value: string;
|
||||||
isValid: boolean;
|
isValid: boolean;
|
||||||
|
|
@ -178,6 +256,7 @@ const ActionType = {
|
||||||
navigateTo: "navigateTo",
|
navigateTo: "navigateTo",
|
||||||
showAlert: "showAlert",
|
showAlert: "showAlert",
|
||||||
storeValue: "storeValue",
|
storeValue: "storeValue",
|
||||||
|
download: "download",
|
||||||
};
|
};
|
||||||
type ActionType = typeof ActionType[keyof typeof ActionType];
|
type ActionType = typeof ActionType[keyof typeof ActionType];
|
||||||
|
|
||||||
|
|
@ -282,6 +361,9 @@ const FieldType = {
|
||||||
ALERT_TYPE_SELECTOR_FIELD: "ALERT_TYPE_SELECTOR_FIELD",
|
ALERT_TYPE_SELECTOR_FIELD: "ALERT_TYPE_SELECTOR_FIELD",
|
||||||
KEY_TEXT_FIELD: "KEY_TEXT_FIELD",
|
KEY_TEXT_FIELD: "KEY_TEXT_FIELD",
|
||||||
VALUE_TEXT_FIELD: "VALUE_TEXT_FIELD",
|
VALUE_TEXT_FIELD: "VALUE_TEXT_FIELD",
|
||||||
|
DOWNLOAD_DATA_FIELD: "DOWNLOAD_DATA_FIELD",
|
||||||
|
DOWNLOAD_FILE_NAME_FIELD: "DOWNLOAD_FILE_NAME_FIELD",
|
||||||
|
DOWNLOAD_FILE_TYPE_FIELD: "DOWNLOAD_FILE_TYPE_FIELD",
|
||||||
};
|
};
|
||||||
type FieldType = typeof FieldType[keyof typeof FieldType];
|
type FieldType = typeof FieldType[keyof typeof FieldType];
|
||||||
|
|
||||||
|
|
@ -404,6 +486,22 @@ const fieldConfigs: FieldConfigs = {
|
||||||
setter: storeValueTextSetter,
|
setter: storeValueTextSetter,
|
||||||
view: ViewTypes.TEXT_VIEW,
|
view: ViewTypes.TEXT_VIEW,
|
||||||
},
|
},
|
||||||
|
[FieldType.DOWNLOAD_DATA_FIELD]: {
|
||||||
|
getter: downloadDataGetter,
|
||||||
|
setter: downloadDataSetter,
|
||||||
|
view: ViewTypes.TEXT_VIEW,
|
||||||
|
},
|
||||||
|
[FieldType.DOWNLOAD_FILE_NAME_FIELD]: {
|
||||||
|
getter: downloadFileNameGetter,
|
||||||
|
setter: downloadFileNameSetter,
|
||||||
|
view: ViewTypes.TEXT_VIEW,
|
||||||
|
},
|
||||||
|
[FieldType.DOWNLOAD_FILE_TYPE_FIELD]: {
|
||||||
|
getter: downloadFileTypeGetter,
|
||||||
|
setter: (option: any, currentValue: string) =>
|
||||||
|
downloadFileTypeSetter(option.value, currentValue),
|
||||||
|
view: ViewTypes.SELECTOR_VIEW,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const baseOptions: any = [
|
const baseOptions: any = [
|
||||||
|
|
@ -440,6 +538,10 @@ const baseOptions: any = [
|
||||||
label: "Store Value",
|
label: "Store Value",
|
||||||
value: ActionType.storeValue,
|
value: ActionType.storeValue,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "Download",
|
||||||
|
value: ActionType.download,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
function getOptionsWithChildren(
|
function getOptionsWithChildren(
|
||||||
options: TreeDropdownOption[],
|
options: TreeDropdownOption[],
|
||||||
|
|
@ -567,6 +669,19 @@ function getFieldFromValue(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (value.indexOf("download") !== -1) {
|
||||||
|
fields.push(
|
||||||
|
{
|
||||||
|
field: FieldType.DOWNLOAD_DATA_FIELD,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: FieldType.DOWNLOAD_FILE_NAME_FIELD,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: FieldType.DOWNLOAD_FILE_TYPE_FIELD,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
return fields;
|
return fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -606,6 +721,7 @@ function renderField(props: {
|
||||||
case FieldType.CLOSE_MODAL_FIELD:
|
case FieldType.CLOSE_MODAL_FIELD:
|
||||||
case FieldType.PAGE_SELECTOR_FIELD:
|
case FieldType.PAGE_SELECTOR_FIELD:
|
||||||
case FieldType.ALERT_TYPE_SELECTOR_FIELD:
|
case FieldType.ALERT_TYPE_SELECTOR_FIELD:
|
||||||
|
case FieldType.DOWNLOAD_FILE_TYPE_FIELD:
|
||||||
let label = "";
|
let label = "";
|
||||||
let defaultText = "Select Action";
|
let defaultText = "Select Action";
|
||||||
let options = props.apiOptionTree;
|
let options = props.apiOptionTree;
|
||||||
|
|
@ -655,10 +771,15 @@ function renderField(props: {
|
||||||
defaultText = "Select Page";
|
defaultText = "Select Page";
|
||||||
}
|
}
|
||||||
if (fieldType === FieldType.ALERT_TYPE_SELECTOR_FIELD) {
|
if (fieldType === FieldType.ALERT_TYPE_SELECTOR_FIELD) {
|
||||||
label = "type";
|
label = "Type";
|
||||||
options = ALERT_STYLE_OPTIONS;
|
options = ALERT_STYLE_OPTIONS;
|
||||||
defaultText = "Select type";
|
defaultText = "Select type";
|
||||||
}
|
}
|
||||||
|
if (fieldType === FieldType.DOWNLOAD_FILE_TYPE_FIELD) {
|
||||||
|
label = "Type";
|
||||||
|
options = FILE_TYPE_OPTIONS;
|
||||||
|
defaultText = "Select file type (optional)";
|
||||||
|
}
|
||||||
viewElement = (view as (props: SelectorViewProps) => JSX.Element)({
|
viewElement = (view as (props: SelectorViewProps) => JSX.Element)({
|
||||||
options: options,
|
options: options,
|
||||||
label: label,
|
label: label,
|
||||||
|
|
@ -695,6 +816,8 @@ function renderField(props: {
|
||||||
case FieldType.URL_FIELD:
|
case FieldType.URL_FIELD:
|
||||||
case FieldType.KEY_TEXT_FIELD:
|
case FieldType.KEY_TEXT_FIELD:
|
||||||
case FieldType.VALUE_TEXT_FIELD:
|
case FieldType.VALUE_TEXT_FIELD:
|
||||||
|
case FieldType.DOWNLOAD_DATA_FIELD:
|
||||||
|
case FieldType.DOWNLOAD_FILE_NAME_FIELD:
|
||||||
let fieldLabel = "";
|
let fieldLabel = "";
|
||||||
if (fieldType === FieldType.ALERT_TEXT_FIELD) {
|
if (fieldType === FieldType.ALERT_TEXT_FIELD) {
|
||||||
fieldLabel = "Message";
|
fieldLabel = "Message";
|
||||||
|
|
@ -704,6 +827,10 @@ function renderField(props: {
|
||||||
fieldLabel = "Key";
|
fieldLabel = "Key";
|
||||||
} else if (fieldType === FieldType.VALUE_TEXT_FIELD) {
|
} else if (fieldType === FieldType.VALUE_TEXT_FIELD) {
|
||||||
fieldLabel = "Value";
|
fieldLabel = "Value";
|
||||||
|
} else if (fieldType === FieldType.DOWNLOAD_DATA_FIELD) {
|
||||||
|
fieldLabel = "Data to download";
|
||||||
|
} else if (fieldType === FieldType.DOWNLOAD_FILE_NAME_FIELD) {
|
||||||
|
fieldLabel = "File name with extension";
|
||||||
}
|
}
|
||||||
viewElement = (view as (props: TextViewProps) => JSX.Element)({
|
viewElement = (view as (props: TextViewProps) => JSX.Element)({
|
||||||
label: fieldLabel,
|
label: fieldLabel,
|
||||||
|
|
|
||||||
|
|
@ -197,6 +197,14 @@ export class DataTreeFactory {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
actionPaths.push("storeValue");
|
actionPaths.push("storeValue");
|
||||||
|
|
||||||
|
dataTree.download = function(data: string, name: string, type: string) {
|
||||||
|
return {
|
||||||
|
type: "DOWNLOAD",
|
||||||
|
payload: { data, name, type },
|
||||||
|
};
|
||||||
|
};
|
||||||
|
actionPaths.push("download");
|
||||||
}
|
}
|
||||||
|
|
||||||
dataTree.pageList = pageList;
|
dataTree.pageList = pageList;
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,11 @@ import {
|
||||||
all,
|
all,
|
||||||
call,
|
call,
|
||||||
put,
|
put,
|
||||||
|
race,
|
||||||
select,
|
select,
|
||||||
take,
|
take,
|
||||||
takeEvery,
|
takeEvery,
|
||||||
takeLatest,
|
takeLatest,
|
||||||
race,
|
|
||||||
} from "redux-saga/effects";
|
} from "redux-saga/effects";
|
||||||
import {
|
import {
|
||||||
evaluateDataTreeWithFunctions,
|
evaluateDataTreeWithFunctions,
|
||||||
|
|
@ -75,6 +75,8 @@ import { PLUGIN_TYPE_API } from "constants/ApiEditorConstants";
|
||||||
import { DEFAULT_EXECUTE_ACTION_TIMEOUT_MS } from "constants/ApiConstants";
|
import { DEFAULT_EXECUTE_ACTION_TIMEOUT_MS } from "constants/ApiConstants";
|
||||||
import { updateAppStore } from "actions/pageActions";
|
import { updateAppStore } from "actions/pageActions";
|
||||||
import { getAppStoreName } from "constants/AppConstants";
|
import { getAppStoreName } from "constants/AppConstants";
|
||||||
|
import downloadjs from "downloadjs";
|
||||||
|
import { getType, Types } from "utils/TypeHelpers";
|
||||||
|
|
||||||
function* navigateActionSaga(
|
function* navigateActionSaga(
|
||||||
action: { pageNameOrUrl: string; params: Record<string, string> },
|
action: { pageNameOrUrl: string; params: Record<string, string> },
|
||||||
|
|
@ -134,6 +136,36 @@ function* storeValueLocally(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function* downloadSaga(
|
||||||
|
action: { data: any; name: string; type: string },
|
||||||
|
event: ExecuteActionPayloadEvent,
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const { data, name, type } = action;
|
||||||
|
if (!name) {
|
||||||
|
AppToaster.show({
|
||||||
|
message: "Download failed. File name was not provided",
|
||||||
|
type: "error",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const dataType = getType(data);
|
||||||
|
if (dataType === Types.ARRAY || dataType === Types.OBJECT) {
|
||||||
|
const jsonString = JSON.stringify(data, null, 2);
|
||||||
|
downloadjs(jsonString, name, type);
|
||||||
|
} else {
|
||||||
|
downloadjs(data, name, type);
|
||||||
|
}
|
||||||
|
if (event.callback) event.callback({ success: true });
|
||||||
|
} catch (err) {
|
||||||
|
AppToaster.show({
|
||||||
|
message: `Download failed. ${err}`,
|
||||||
|
type: "error",
|
||||||
|
});
|
||||||
|
if (event.callback) event.callback({ success: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const getActionTimeout = (
|
export const getActionTimeout = (
|
||||||
state: AppState,
|
state: AppState,
|
||||||
actionId: string,
|
actionId: string,
|
||||||
|
|
@ -383,6 +415,9 @@ function* executeActionTriggers(
|
||||||
case "STORE_VALUE":
|
case "STORE_VALUE":
|
||||||
yield call(storeValueLocally, trigger.payload, event);
|
yield call(storeValueLocally, trigger.payload, event);
|
||||||
break;
|
break;
|
||||||
|
case "DOWNLOAD":
|
||||||
|
yield call(downloadSaga, trigger.payload, event);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
yield put(
|
yield put(
|
||||||
executeActionError({
|
executeActionError({
|
||||||
|
|
|
||||||
|
|
@ -237,4 +237,8 @@ export const GLOBAL_FUNCTIONS = {
|
||||||
"!doc": "Store key value data locally",
|
"!doc": "Store key value data locally",
|
||||||
"!type": "fn(key: string, value: any) -> void",
|
"!type": "fn(key: string, value: any) -> void",
|
||||||
},
|
},
|
||||||
|
download: {
|
||||||
|
"!doc": "Download anything as a file",
|
||||||
|
"!type": "fn(data: any, fileName: string, fileType?: string) -> void",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3590,6 +3590,11 @@
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/dom4/-/dom4-2.0.1.tgz#506d5781b9bcab81bd9a878b198aec7dee2a6033"
|
resolved "https://registry.yarnpkg.com/@types/dom4/-/dom4-2.0.1.tgz#506d5781b9bcab81bd9a878b198aec7dee2a6033"
|
||||||
|
|
||||||
|
"@types/downloadjs@^1.4.2":
|
||||||
|
version "1.4.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/downloadjs/-/downloadjs-1.4.2.tgz#6734e60840cd8df3f0f8937c80e67efe7de6f886"
|
||||||
|
integrity sha512-9UWO+nrRhwyKcFe45SFc98sWI8RGwpVwtZiZzi0W/dWdrp1SiUWFNANLlAXZb5QCMFDbeRiAQEhRn5btLd4a4w==
|
||||||
|
|
||||||
"@types/eslint-visitor-keys@^1.0.0":
|
"@types/eslint-visitor-keys@^1.0.0":
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d"
|
resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d"
|
||||||
|
|
@ -7332,6 +7337,11 @@ dotenv@^6.2.0:
|
||||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064"
|
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064"
|
||||||
integrity sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==
|
integrity sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==
|
||||||
|
|
||||||
|
downloadjs@^1.4.7:
|
||||||
|
version "1.4.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/downloadjs/-/downloadjs-1.4.7.tgz#f69f96f940e0d0553dac291139865a3cd0101e3c"
|
||||||
|
integrity sha1-9p+W+UDg0FU9rCkROYZaPNAQHjw=
|
||||||
|
|
||||||
duplexer@^0.1.1:
|
duplexer@^0.1.1:
|
||||||
version "0.1.1"
|
version "0.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1"
|
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user