Merge branch 'master' of github.com:appsmithorg/appsmith
This commit is contained in:
commit
d977befc54
|
|
@ -1,18 +1,18 @@
|
|||
module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
parser: "@typescript-eslint/parser",
|
||||
plugins: ["react", "@typescript-eslint", "prettier", "react-hooks"],
|
||||
extends: [
|
||||
"plugin:react/recommended", // Uses the recommended rules from @eslint-plugin-react
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"prettier/@typescript-eslint",
|
||||
"plugin:prettier/recommended"
|
||||
"plugin:prettier/recommended",
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features
|
||||
sourceType: "module", // Allows for the use of imports
|
||||
ecmaFeatures: {
|
||||
jsx: true // Allows for the parsing of JSX
|
||||
}
|
||||
jsx: true, // Allows for the parsing of JSX
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
"@typescript-eslint/explicit-function-return-type": 0,
|
||||
|
|
@ -20,11 +20,12 @@ module.exports = {
|
|||
"react-hooks/rules-of-hooks": "error",
|
||||
"@typescript-eslint/no-use-before-define": 0,
|
||||
"@typescript-eslint/no-var-requires": 0,
|
||||
"import/no-webpack-loader-syntax": 0
|
||||
},
|
||||
settings: {
|
||||
react: {
|
||||
pragma: "React",
|
||||
version: "detect" // Tells eslint-plugin-react to automatically detect the version of React to use
|
||||
}
|
||||
}
|
||||
version: "detect", // Tells eslint-plugin-react to automatically detect the version of React to use
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
GIT_SHA=$(eval git rev-parse HEAD)
|
||||
echo $GIT_SHA
|
||||
REACT_APP_SENTRY_RELEASE=$GIT_SHA craco --max-old-space-size=4096 build --config craco.build.config.js
|
||||
REACT_APP_SENTRY_RELEASE=$GIT_SHA EXTEND_ESLINT=true craco --max-old-space-size=4096 build --config craco.build.config.js
|
||||
|
||||
rm ./build/static/js/*.js.map
|
||||
echo "build finished"
|
||||
echo "build finished"
|
||||
|
|
|
|||
|
|
@ -12,5 +12,9 @@
|
|||
"json": false
|
||||
},
|
||||
"viewportHeight": 900,
|
||||
"viewportWidth": 1400
|
||||
}
|
||||
"viewportWidth": 1400,
|
||||
"retries": {
|
||||
"runMode": 2,
|
||||
"openMode": 0
|
||||
}
|
||||
}
|
||||
|
|
|
|||
62
app/client/cypress/fixtures/SimpleBinding.json
Normal file
62
app/client/cypress/fixtures/SimpleBinding.json
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"dsl": {
|
||||
"widgetName": "MainContainer",
|
||||
"backgroundColor": "none",
|
||||
"rightColumn": 1224,
|
||||
"snapColumns": 16,
|
||||
"detachFromLayout": true,
|
||||
"widgetId": "0",
|
||||
"topRow": 0,
|
||||
"bottomRow": 1280,
|
||||
"containerStyle": "none",
|
||||
"snapRows": 33,
|
||||
"parentRowSpace": 1,
|
||||
"type": "CANVAS_WIDGET",
|
||||
"canExtend": true,
|
||||
"dynamicBindings": {},
|
||||
"version": 6,
|
||||
"minHeight": 1292,
|
||||
"parentColumnSpace": 1,
|
||||
"leftColumn": 0,
|
||||
"children": [
|
||||
{
|
||||
"isVisible": true,
|
||||
"text": "Label",
|
||||
"textStyle": "LABEL",
|
||||
"textAlign": "LEFT",
|
||||
"widgetName": "Text1",
|
||||
"type": "TEXT_WIDGET",
|
||||
"isLoading": false,
|
||||
"parentColumnSpace": 74,
|
||||
"parentRowSpace": 40,
|
||||
"leftColumn": 3,
|
||||
"rightColumn": 7,
|
||||
"topRow": 4,
|
||||
"bottomRow": 5,
|
||||
"parentId": "0",
|
||||
"widgetId": "pcznwg0g8k"
|
||||
},
|
||||
{
|
||||
"isVisible": true,
|
||||
"text": "{{Text1.text}}",
|
||||
"textStyle": "LABEL",
|
||||
"textAlign": "LEFT",
|
||||
"widgetName": "Text2",
|
||||
"type": "TEXT_WIDGET",
|
||||
"isLoading": false,
|
||||
"parentColumnSpace": 74,
|
||||
"parentRowSpace": 40,
|
||||
"leftColumn": 3,
|
||||
"rightColumn": 7,
|
||||
"topRow": 6,
|
||||
"bottomRow": 7,
|
||||
"parentId": "0",
|
||||
"widgetId": "tgnz7xg7a3",
|
||||
"dynamicBindings": {
|
||||
"text": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"layoutOnLoadActions": []
|
||||
}
|
||||
|
|
@ -81,59 +81,6 @@
|
|||
"widgetId": "xlrmeiioaa"
|
||||
}
|
||||
],
|
||||
"blueprint": {
|
||||
"view": [
|
||||
{
|
||||
"type": "TEXT_WIDGET",
|
||||
"size": {
|
||||
"rows": 1,
|
||||
"cols": 12
|
||||
},
|
||||
"position": {
|
||||
"top": 0,
|
||||
"left": 0
|
||||
},
|
||||
"props": {
|
||||
"text": "Form",
|
||||
"textStyle": "HEADING"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "FORM_BUTTON_WIDGET",
|
||||
"size": {
|
||||
"rows": 1,
|
||||
"cols": 4
|
||||
},
|
||||
"position": {
|
||||
"top": 11,
|
||||
"left": 12
|
||||
},
|
||||
"props": {
|
||||
"text": "Submit",
|
||||
"buttonStyle": "PRIMARY_BUTTON",
|
||||
"disabledWhenInvalid": true,
|
||||
"resetFormOnClick": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "FORM_BUTTON_WIDGET",
|
||||
"size": {
|
||||
"rows": 1,
|
||||
"cols": 4
|
||||
},
|
||||
"position": {
|
||||
"top": 11,
|
||||
"left": 8
|
||||
},
|
||||
"props": {
|
||||
"text": "Reset",
|
||||
"buttonStyle": "SECONDARY_BUTTON",
|
||||
"disabledWhenInvalid": false,
|
||||
"resetFormOnClick": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"minHeight": 520,
|
||||
"type": "CANVAS_WIDGET",
|
||||
"isLoading": false,
|
||||
|
|
@ -147,76 +94,6 @@
|
|||
"widgetId": "sidaue1kdu"
|
||||
}
|
||||
],
|
||||
"blueprint": {
|
||||
"view": [
|
||||
{
|
||||
"type": "CANVAS_WIDGET",
|
||||
"position": {
|
||||
"top": 0,
|
||||
"left": 0
|
||||
},
|
||||
"props": {
|
||||
"containerStyle": "none",
|
||||
"canExtend": false,
|
||||
"detachFromLayout": true,
|
||||
"children": [],
|
||||
"blueprint": {
|
||||
"view": [
|
||||
{
|
||||
"type": "TEXT_WIDGET",
|
||||
"size": {
|
||||
"rows": 1,
|
||||
"cols": 12
|
||||
},
|
||||
"position": {
|
||||
"top": 0,
|
||||
"left": 0
|
||||
},
|
||||
"props": {
|
||||
"text": "Form",
|
||||
"textStyle": "HEADING"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "FORM_BUTTON_WIDGET",
|
||||
"size": {
|
||||
"rows": 1,
|
||||
"cols": 4
|
||||
},
|
||||
"position": {
|
||||
"top": 11,
|
||||
"left": 12
|
||||
},
|
||||
"props": {
|
||||
"text": "Submit",
|
||||
"buttonStyle": "PRIMARY_BUTTON",
|
||||
"disabledWhenInvalid": true,
|
||||
"resetFormOnClick": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "FORM_BUTTON_WIDGET",
|
||||
"size": {
|
||||
"rows": 1,
|
||||
"cols": 4
|
||||
},
|
||||
"position": {
|
||||
"top": 11,
|
||||
"left": 8
|
||||
},
|
||||
"props": {
|
||||
"text": "Reset",
|
||||
"buttonStyle": "SECONDARY_BUTTON",
|
||||
"disabledWhenInvalid": false,
|
||||
"resetFormOnClick": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "FORM_WIDGET",
|
||||
"isLoading": false,
|
||||
"parentColumnSpace": 74,
|
||||
|
|
@ -230,4 +107,4 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
const dsl = require("../../../fixtures/SimpleBinding.json");
|
||||
const widgetsPage = require("../../../locators/Widgets.json");
|
||||
|
||||
describe("Binding the multiple widgets and validating default data", function() {
|
||||
before(() => {
|
||||
cy.addDsl(dsl);
|
||||
});
|
||||
|
||||
it("Checks if delete will remove bindings", function() {
|
||||
cy.get(widgetsPage.textWidget)
|
||||
.first()
|
||||
.click({ force: true });
|
||||
cy.get("body").type("{del}", { force: true });
|
||||
|
||||
cy.get(widgetsPage.textWidget)
|
||||
.first()
|
||||
.should("not.have.text", "Label");
|
||||
});
|
||||
});
|
||||
|
|
@ -49,6 +49,7 @@ describe("Binding the multiple Widgets and validating NavigateTo Page", function
|
|||
cy.get(publish.inputGrp)
|
||||
.first()
|
||||
.type("123");
|
||||
|
||||
cy.get(widgetsPage.chartWidget).should("be.visible");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ describe("Form reset functionality", function() {
|
|||
.contains("Reset")
|
||||
.click();
|
||||
|
||||
cy.wait(500);
|
||||
|
||||
cy.get(".tr")
|
||||
.eq(2)
|
||||
.should("not.have.class", "selected-row");
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
"searchloc": "input[placeholder='Enter location to search']",
|
||||
"imagecontainer": ".t--draggable-imagewidget span.t--widget-name",
|
||||
"imageinner": ".t--draggable-imagewidget img",
|
||||
"chartInnerText": ".t--draggable-chartwidget text",
|
||||
"chartInnerText": ".t--property-control-title",
|
||||
"inputChartValue": ".t--property-control-chartdata .CodeMirror textarea",
|
||||
"chartButton": ".t--property-control-chartdata button",
|
||||
"rectangleChart": ".t--draggable-chartwidget g rect",
|
||||
|
|
|
|||
|
|
@ -882,8 +882,8 @@ Cypress.Commands.add(
|
|||
|
||||
Cypress.Commands.add("widgetText", (text, inputcss, innercss) => {
|
||||
cy.get(commonlocators.editWidgetName)
|
||||
.dblclick({ force: true })
|
||||
.type(text, { force: true })
|
||||
.click({ force: true })
|
||||
.type(text)
|
||||
.type("{enter}");
|
||||
cy.get(inputcss)
|
||||
.first()
|
||||
|
|
@ -1174,23 +1174,7 @@ Cypress.Commands.add("getAlert", alertcss => {
|
|||
.contains("Success")
|
||||
.click({ force: true });
|
||||
});
|
||||
Cypress.Commands.add("widgetText", (text, inputcss, innercss) => {
|
||||
cy.get(commonlocators.editWidgetName)
|
||||
.dblclick({ force: true })
|
||||
.type(text)
|
||||
.type("{enter}");
|
||||
cy.get(inputcss)
|
||||
.first()
|
||||
.trigger("mouseover", { force: true });
|
||||
cy.get(innercss).should("have.text", text);
|
||||
});
|
||||
Cypress.Commands.add("radioInput", (index, text) => {
|
||||
cy.get(widgetsPage.RadioInput)
|
||||
.eq(index)
|
||||
.click()
|
||||
.clear()
|
||||
.type(text);
|
||||
});
|
||||
|
||||
Cypress.Commands.add("tabVerify", (index, text) => {
|
||||
cy.get(".t--property-control-tabs input")
|
||||
.eq(index)
|
||||
|
|
@ -1213,26 +1197,6 @@ Cypress.Commands.add("togglebarDisable", value => {
|
|||
.uncheck({ force: true })
|
||||
.should("not.checked");
|
||||
});
|
||||
Cypress.Commands.add("radiovalue", (value, value2) => {
|
||||
cy.get(value)
|
||||
.click()
|
||||
.clear()
|
||||
.type(value2);
|
||||
});
|
||||
Cypress.Commands.add("optionValue", (value, value2) => {
|
||||
cy.get(value)
|
||||
.click()
|
||||
.clear()
|
||||
.type(value2);
|
||||
});
|
||||
Cypress.Commands.add("dropdownDynamic", text => {
|
||||
cy.wait(2000);
|
||||
cy.get("ul[class='bp3-menu']")
|
||||
.first()
|
||||
.contains(text)
|
||||
.click({ force: true })
|
||||
.should("have.text", text);
|
||||
});
|
||||
|
||||
Cypress.Commands.add("getAlert", alertcss => {
|
||||
cy.get(commonlocators.dropdownSelectButton).click({ force: true });
|
||||
|
|
@ -1251,16 +1215,7 @@ Cypress.Commands.add("getAlert", alertcss => {
|
|||
.contains("Success")
|
||||
.click({ force: true });
|
||||
});
|
||||
Cypress.Commands.add("widgetText", (text, inputcss, innercss) => {
|
||||
cy.get(commonlocators.editWidgetName)
|
||||
.dblclick({ force: true })
|
||||
.type(text)
|
||||
.type("{enter}");
|
||||
cy.get(inputcss)
|
||||
.first()
|
||||
.trigger("mouseover", { force: true });
|
||||
cy.get(innercss).should("have.text", text);
|
||||
});
|
||||
|
||||
Cypress.Commands.add("radioInput", (index, text) => {
|
||||
cy.get(widgetsPage.RadioInput)
|
||||
.eq(index)
|
||||
|
|
@ -1648,8 +1603,6 @@ Cypress.Commands.add("startServerAndRoutes", () => {
|
|||
cy.route("DELETE", "/api/v1/applications/*").as("deleteApp");
|
||||
cy.route("DELETE", "/api/v1/actions/*").as("deleteAction");
|
||||
cy.route("DELETE", "/api/v1/pages/*").as("deletePage");
|
||||
|
||||
cy.route("GET", "/api/v1/plugins/*/form").as("getPluginForm");
|
||||
cy.route("POST", "/api/v1/datasources").as("createDatasource");
|
||||
cy.route("POST", "/api/v1/datasources/test").as("testDatasource");
|
||||
cy.route("PUT", "/api/v1/datasources/*").as("saveDatasource");
|
||||
|
|
|
|||
|
|
@ -117,14 +117,14 @@
|
|||
"typescript": "^3.9.2",
|
||||
"unescape-js": "^1.1.4",
|
||||
"url-search-params-polyfill": "^8.0.0",
|
||||
"workerize-loader": "^1.2.0"
|
||||
"worker-loader": "^3.0.2"
|
||||
},
|
||||
"scripts": {
|
||||
"analyze": "source-map-explorer 'build/static/js/*.js'",
|
||||
"start": "REACT_APP_BASE_URL=https://release-api.appsmith.com REACT_APP_ENVIRONMENT=DEVELOPMENT HOST=dev.appsmith.com craco start",
|
||||
"start": "EXTEND_ESLINT=true REACT_APP_ENVIRONMENT=DEVELOPMENT HOST=dev.appsmith.com craco start",
|
||||
"build": "./build.sh",
|
||||
"build-local": "craco --max-old-space-size=4096 build --config craco.build.config.js",
|
||||
"build-staging": " REACT_APP_ENVIRONMENT=STAGING craco --max-old-space-size=4096 build --config craco.build.config.js",
|
||||
"build-staging": "REACT_APP_ENVIRONMENT=STAGING craco --max-old-space-size=4096 build --config craco.build.config.js",
|
||||
"test": "CYPRESS_BASE_URL=https://dev.appsmith.com cypress/test.sh",
|
||||
"test:ci": "CYPRESS_BASE_URL=https://dev.appsmith.com cypress/test.sh --env=ci",
|
||||
"eject": "react-scripts eject",
|
||||
|
|
@ -209,4 +209,4 @@
|
|||
"pre-commit": "lint-staged"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -140,7 +140,6 @@
|
|||
};
|
||||
|
||||
</script>
|
||||
<script rel="prefetch" type="text/javascript" src="/shims/realms-shim.umd.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/tinymce/5.4.0/tinymce.min.js" referrerpolicy="origin"></script>
|
||||
</body>
|
||||
|
||||
|
|
|
|||
10
app/client/public/shims/realms-shim.umd.min.js
vendored
10
app/client/public/shims/realms-shim.umd.min.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -6,3 +6,8 @@ export const batchAction = (action: ReduxAction<any>) => ({
|
|||
});
|
||||
|
||||
export type BatchAction<T> = ReduxAction<ReduxAction<T>>;
|
||||
|
||||
export const batchActionSuccess = (actions: ReduxAction<any>[]) => ({
|
||||
type: ReduxActionTypes.BATCH_UPDATES_SUCCESS,
|
||||
payload: actions,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -32,6 +32,14 @@ export const fetchPage = (pageId: string): ReduxAction<FetchPageRequest> => {
|
|||
};
|
||||
};
|
||||
|
||||
export const fetchPublishedPage = (pageId: string, bustCache = false) => ({
|
||||
type: ReduxActionTypes.FETCH_PUBLISHED_PAGE_INIT,
|
||||
payload: {
|
||||
pageId,
|
||||
bustCache,
|
||||
},
|
||||
});
|
||||
|
||||
export const fetchPageSuccess = () => {
|
||||
return {
|
||||
type: ReduxActionTypes.FETCH_PAGE_SUCCESS,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import _ from "lodash";
|
||||
import axios, { AxiosInstance, AxiosRequestConfig } from "axios";
|
||||
import {
|
||||
REQUEST_TIMEOUT_MS,
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ const Hit = (props: { hit: { path: string } }) => {
|
|||
|
||||
const DefaultHelpMenuItem = (props: {
|
||||
item: { label: string; link?: string; id?: string; icon: React.ReactNode };
|
||||
onSelect: Function;
|
||||
onSelect: () => void;
|
||||
}) => {
|
||||
return (
|
||||
<li className="ais-Hits-item">
|
||||
|
|
|
|||
|
|
@ -1,14 +1,7 @@
|
|||
import { bindingHint } from "components/editorComponents/CodeEditor/hintHelpers";
|
||||
import { MockCodemirrorEditor } from "../../../../test/__mocks__/CodeMirrorEditorMock";
|
||||
import RealmExecutor from "jsExecution/RealmExecutor";
|
||||
jest.mock("jsExecution/RealmExecutor");
|
||||
|
||||
describe("hint helpers", () => {
|
||||
beforeAll(() => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
|
||||
// @ts-ignore
|
||||
RealmExecutor.mockClear();
|
||||
});
|
||||
describe("binding hint helper", () => {
|
||||
it("is initialized correctly", () => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
|
||||
|
|
|
|||
|
|
@ -83,6 +83,10 @@ const ToastComponent = (props: Props) => {
|
|||
|
||||
const Toaster = {
|
||||
show: (config: Props) => {
|
||||
if (typeof config.message !== "string") {
|
||||
console.error("Toast message needs to be a string");
|
||||
return;
|
||||
}
|
||||
toast(
|
||||
<ToastComponent
|
||||
{...config}
|
||||
|
|
|
|||
|
|
@ -24,14 +24,13 @@ import {
|
|||
const Wrapper = styled.div`
|
||||
.dynamic-text-field {
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
font-size: 14px;
|
||||
height: calc(100vh / 4);
|
||||
}
|
||||
|
||||
&& {
|
||||
.CodeMirror-lines {
|
||||
padding: 16px 20px;
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ const FIELD_VALUES: Record<
|
|||
},
|
||||
CANVAS_WIDGET: {},
|
||||
ICON_WIDGET: {},
|
||||
SKELETON_WIDGET: {},
|
||||
CONTAINER_WIDGET: {
|
||||
backgroundColor: "string",
|
||||
isVisible: "boolean",
|
||||
|
|
|
|||
|
|
@ -71,6 +71,10 @@ export const HelpMap = {
|
|||
path: "",
|
||||
searchKey: "",
|
||||
},
|
||||
SKELETON_WIDGET: {
|
||||
path: "",
|
||||
searchKey: "",
|
||||
},
|
||||
TABS_WIDGET: {
|
||||
path: "",
|
||||
searchKey: "Tabs",
|
||||
|
|
|
|||
|
|
@ -285,6 +285,8 @@ export const ReduxActionTypes: { [key: string]: string } = {
|
|||
UNDO_DELETE_WIDGET: "UNDO_DELETE_WIDGET",
|
||||
CUT_SELECTED_WIDGET: "CUT_SELECTED_WIDGET",
|
||||
WIDGET_ADD_CHILDREN: "WIDGET_ADD_CHILDREN",
|
||||
SET_EVALUATED_TREE: "SET_EVALUATED_TREE",
|
||||
BATCH_UPDATES_SUCCESS: "BATCH_UPDATES_SUCCESS",
|
||||
};
|
||||
|
||||
export type ReduxActionType = typeof ReduxActionTypes[keyof typeof ReduxActionTypes];
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ export enum WidgetTypes {
|
|||
ICON_WIDGET = "ICON_WIDGET",
|
||||
FILE_PICKER_WIDGET = "FILE_PICKER_WIDGET",
|
||||
VIDEO_WIDGET = "VIDEO_WIDGET",
|
||||
SKELETON_WIDGET = "SKELETON_WIDGET",
|
||||
}
|
||||
|
||||
export type WidgetType = keyof typeof WidgetTypes;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ export const VALIDATION_TYPES = {
|
|||
ACTION_SELECTOR: "ACTION_SELECTOR",
|
||||
ARRAY_ACTION_SELECTOR: "ARRAY_ACTION_SELECTOR",
|
||||
SELECTED_TAB: "SELECTED_TAB",
|
||||
DEFAULT_OPTION_VALUE: "DEFAULT_OPTION_VALUE",
|
||||
};
|
||||
|
||||
export type ValidationResponse = {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ export type ActionDescription<T> = {
|
|||
payload: T;
|
||||
};
|
||||
|
||||
type ActionDispatcher<T, A extends string[]> = (
|
||||
export type ActionDispatcher<T, A extends string[]> = (
|
||||
...args: A
|
||||
) => ActionDescription<T>;
|
||||
|
||||
|
|
@ -76,60 +76,39 @@ type DataTreeSeed = {
|
|||
};
|
||||
|
||||
export class DataTreeFactory {
|
||||
static create(
|
||||
{ actions, widgets, widgetsMeta, pageList, appData }: DataTreeSeed,
|
||||
// TODO(hetu)
|
||||
// temporary fix for not getting functions while normal evals which crashes the app
|
||||
// need to remove this after we get a proper solve
|
||||
withFunctions?: boolean,
|
||||
): DataTree {
|
||||
static create({
|
||||
actions,
|
||||
widgets,
|
||||
widgetsMeta,
|
||||
pageList,
|
||||
appData,
|
||||
}: DataTreeSeed): DataTree {
|
||||
const dataTree: DataTree = {};
|
||||
const actionPaths = [];
|
||||
actions.forEach(a => {
|
||||
const config = a.config;
|
||||
actions.forEach(action => {
|
||||
let dynamicBindingPathList: Property[] = [];
|
||||
// update paths
|
||||
if (
|
||||
config.dynamicBindingPathList &&
|
||||
config.dynamicBindingPathList.length
|
||||
action.config.dynamicBindingPathList &&
|
||||
action.config.dynamicBindingPathList.length
|
||||
) {
|
||||
dynamicBindingPathList = config.dynamicBindingPathList.map(d => ({
|
||||
...d,
|
||||
key: `config.${d.key}`,
|
||||
}));
|
||||
dynamicBindingPathList = action.config.dynamicBindingPathList.map(
|
||||
d => ({
|
||||
...d,
|
||||
key: `config.${d.key}`,
|
||||
}),
|
||||
);
|
||||
}
|
||||
dataTree[config.name] = {
|
||||
...a,
|
||||
actionId: config.id,
|
||||
name: config.name,
|
||||
pluginType: config.pluginType,
|
||||
config: config.actionConfiguration,
|
||||
dataTree[action.config.name] = {
|
||||
run: {},
|
||||
actionId: action.config.id,
|
||||
name: action.config.name,
|
||||
pluginType: action.config.pluginType,
|
||||
config: action.config.actionConfiguration,
|
||||
dynamicBindingPathList,
|
||||
data: a.data ? a.data.body : {},
|
||||
run: withFunctions
|
||||
? function(
|
||||
this: DataTreeAction,
|
||||
onSuccess: string,
|
||||
onError: string,
|
||||
params = "",
|
||||
) {
|
||||
return {
|
||||
type: "RUN_ACTION",
|
||||
payload: {
|
||||
actionId: this.actionId,
|
||||
onSuccess: onSuccess ? `{{${onSuccess.toString()}}}` : "",
|
||||
onError: onError ? `{{${onError.toString()}}}` : "",
|
||||
params,
|
||||
},
|
||||
};
|
||||
}
|
||||
: {},
|
||||
data: action.data ? action.data.body : {},
|
||||
ENTITY_TYPE: ENTITY_TYPE.ACTION,
|
||||
isLoading: action.isLoading,
|
||||
};
|
||||
if (withFunctions) {
|
||||
actionPaths.push(`${config.name}.run`);
|
||||
}
|
||||
dataTree.actionPaths && dataTree.actionPaths.push();
|
||||
});
|
||||
Object.keys(widgets).forEach(w => {
|
||||
const widget = { ...widgets[w] };
|
||||
|
|
@ -165,63 +144,7 @@ export class DataTreeFactory {
|
|||
};
|
||||
});
|
||||
|
||||
if (withFunctions) {
|
||||
dataTree.navigateTo = function(
|
||||
pageNameOrUrl: string,
|
||||
params: Record<string, unknown>,
|
||||
) {
|
||||
return {
|
||||
type: "NAVIGATE_TO",
|
||||
payload: { pageNameOrUrl, params },
|
||||
};
|
||||
};
|
||||
actionPaths.push("navigateTo");
|
||||
|
||||
dataTree.showAlert = function(message: string, style: string) {
|
||||
return {
|
||||
type: "SHOW_ALERT",
|
||||
payload: { message, style },
|
||||
};
|
||||
};
|
||||
actionPaths.push("showAlert");
|
||||
|
||||
// dataTree.url = url;
|
||||
dataTree.showModal = function(modalName: string) {
|
||||
return {
|
||||
type: "SHOW_MODAL_BY_NAME",
|
||||
payload: { modalName },
|
||||
};
|
||||
};
|
||||
actionPaths.push("showModal");
|
||||
|
||||
dataTree.closeModal = function(modalName: string) {
|
||||
return {
|
||||
type: "CLOSE_MODAL",
|
||||
payload: { modalName },
|
||||
};
|
||||
};
|
||||
actionPaths.push("closeModal");
|
||||
|
||||
dataTree.storeValue = function(key: string, value: string) {
|
||||
return {
|
||||
type: "STORE_VALUE",
|
||||
payload: { key, value },
|
||||
};
|
||||
};
|
||||
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.actionPaths = actionPaths;
|
||||
|
||||
dataTree.appsmith = { ...appData } as DataTreeAppsmith;
|
||||
(dataTree.appsmith as DataTreeAppsmith).ENTITY_TYPE = ENTITY_TYPE.APPSMITH;
|
||||
return dataTree;
|
||||
|
|
|
|||
|
|
@ -1,107 +0,0 @@
|
|||
import RealmExecutor from "./RealmExecutor";
|
||||
import moment from "moment-timezone";
|
||||
import { ActionDescription } from "entities/DataTree/dataTreeFactory";
|
||||
import { btoa, atob, version as BASE64LIBVERSION } from "js-base64";
|
||||
import { VERSION as lodashVersion } from "lodash";
|
||||
export type JSExecutorGlobal = Record<string, object>;
|
||||
export type JSExecutorResult = {
|
||||
result: any;
|
||||
triggers?: ActionDescription<any>[];
|
||||
};
|
||||
export interface JSExecutor {
|
||||
execute: (
|
||||
src: string,
|
||||
data: JSExecutorGlobal,
|
||||
callbackData?: any,
|
||||
) => JSExecutorResult;
|
||||
registerLibrary: (accessor: string, lib: any) => void;
|
||||
unRegisterLibrary: (accessor: string) => void;
|
||||
}
|
||||
|
||||
enum JSExecutorType {
|
||||
REALM,
|
||||
}
|
||||
|
||||
export type ExtraLibrary = {
|
||||
version: string;
|
||||
docsURL: string;
|
||||
displayName: string;
|
||||
accessor: string;
|
||||
lib: any;
|
||||
};
|
||||
|
||||
export const extraLibraries: ExtraLibrary[] = [
|
||||
{
|
||||
accessor: "_",
|
||||
lib: window._,
|
||||
version: lodashVersion,
|
||||
docsURL: `https://lodash.com/docs/${lodashVersion}`,
|
||||
displayName: "lodash",
|
||||
},
|
||||
{
|
||||
accessor: "moment",
|
||||
lib: moment,
|
||||
version: moment.version,
|
||||
docsURL: `https://momentjs.com/docs/`,
|
||||
displayName: "moment",
|
||||
},
|
||||
{
|
||||
accessor: "btoa",
|
||||
lib: btoa,
|
||||
version: BASE64LIBVERSION,
|
||||
docsURL: "https://github.com/dankogai/js-base64#readme",
|
||||
displayName: "btoa",
|
||||
},
|
||||
{
|
||||
accessor: "atob",
|
||||
lib: atob,
|
||||
version: BASE64LIBVERSION,
|
||||
docsURL: "https://github.com/dankogai/js-base64#readme",
|
||||
displayName: "atob",
|
||||
},
|
||||
];
|
||||
|
||||
class JSExecutionManager {
|
||||
currentExecutor: JSExecutor;
|
||||
executors: Record<JSExecutorType, JSExecutor>;
|
||||
registerLibrary(accessor: string, lib: any) {
|
||||
Object.keys(this.executors).forEach(type => {
|
||||
const executor = this.executors[(type as any) as JSExecutorType];
|
||||
executor.registerLibrary(accessor, lib);
|
||||
});
|
||||
}
|
||||
unRegisterLibrary(accessor: string) {
|
||||
Object.keys(this.executors).forEach(type => {
|
||||
const executor = this.executors[(type as any) as JSExecutorType];
|
||||
executor.unRegisterLibrary(accessor);
|
||||
});
|
||||
}
|
||||
switchExecutor(type: JSExecutorType) {
|
||||
const executor = this.executors[type];
|
||||
if (!executor) {
|
||||
throw new Error("Executor does not exist");
|
||||
}
|
||||
this.currentExecutor = executor;
|
||||
}
|
||||
constructor() {
|
||||
const realmExecutor = new RealmExecutor();
|
||||
this.executors = {
|
||||
[JSExecutorType.REALM]: realmExecutor,
|
||||
};
|
||||
this.currentExecutor = realmExecutor;
|
||||
|
||||
extraLibraries.forEach(config => {
|
||||
this.registerLibrary(config.accessor, config.lib);
|
||||
});
|
||||
}
|
||||
evaluateSync(
|
||||
jsSrc: string,
|
||||
data: JSExecutorGlobal,
|
||||
callbackData?: any,
|
||||
): JSExecutorResult {
|
||||
return this.currentExecutor.execute(jsSrc, data, callbackData);
|
||||
}
|
||||
}
|
||||
const JSExecutionManagerSingleton = new JSExecutionManager();
|
||||
|
||||
export default JSExecutionManagerSingleton;
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
import {
|
||||
JSExecutorGlobal,
|
||||
JSExecutor,
|
||||
JSExecutorResult,
|
||||
} from "./JSExecutionManagerSingleton";
|
||||
import JSONFn from "json-fn";
|
||||
import log from "loglevel";
|
||||
declare let Realm: any;
|
||||
|
||||
export default class RealmExecutor implements JSExecutor {
|
||||
rootRealm: any;
|
||||
createSafeObject: any;
|
||||
extrinsics: any[] = [];
|
||||
createSafeFunction: (unsafeFn: Function) => Function;
|
||||
|
||||
libraries: Record<string, any> = {};
|
||||
constructor() {
|
||||
this.rootRealm = Realm.makeRootRealm();
|
||||
this.registerLibrary("JSONFn", JSONFn);
|
||||
this.createSafeFunction = this.rootRealm.evaluate(`
|
||||
(function createSafeFunction(unsafeFn) {
|
||||
return function safeFn(...args) {
|
||||
return unsafeFn(...args);
|
||||
}
|
||||
})
|
||||
`);
|
||||
// After parsing the data we add a triggers list on the global scope to
|
||||
// push to it during any script execution
|
||||
// We replace all action descriptor functions with our pusher function
|
||||
// which has reference to the triggers via binding
|
||||
this.createSafeObject = this.rootRealm.evaluate(
|
||||
`
|
||||
(function createSafeObject(unsafeObject) {
|
||||
const safeObject = JSONFn.parse(JSONFn.stringify(unsafeObject));
|
||||
if(safeObject.actionPaths) {
|
||||
safeObject.triggers = [];
|
||||
const pusher = function (action, ...payload) {
|
||||
const actionPayload = action(...payload);
|
||||
this.triggers.push(actionPayload);
|
||||
}
|
||||
safeObject.actionPaths.forEach(path => {
|
||||
const action = _.get(safeObject, path);
|
||||
const entity = _.get(safeObject, path.split(".")[0])
|
||||
if(action) {
|
||||
_.set(safeObject, path, pusher.bind(safeObject, action.bind(entity)))
|
||||
}
|
||||
})
|
||||
}
|
||||
return safeObject
|
||||
})
|
||||
`,
|
||||
);
|
||||
}
|
||||
|
||||
registerLibrary(accessor: string, lib: any) {
|
||||
this.rootRealm.global[accessor] = lib;
|
||||
}
|
||||
unRegisterLibrary(accessor: string) {
|
||||
this.rootRealm.global[accessor] = null;
|
||||
}
|
||||
private convertToMainScope(result: any) {
|
||||
if (typeof result === "object") {
|
||||
if (Array.isArray(result)) {
|
||||
return Object.assign([], result);
|
||||
}
|
||||
return Object.assign({}, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
execute(
|
||||
sourceText: string,
|
||||
data: JSExecutorGlobal,
|
||||
callbackData?: any,
|
||||
): JSExecutorResult {
|
||||
const safeCallbackData = this.createSafeObject(callbackData || {});
|
||||
const safeData = this.createSafeObject(data);
|
||||
try {
|
||||
// We create a closed function and evaluate that
|
||||
// This is to send any triggers received during evaluations
|
||||
// triggers should already be defined in the safeData
|
||||
const scriptToEvaluate = `
|
||||
function closedFunction () {
|
||||
const result = ${sourceText};
|
||||
return { result, triggers }
|
||||
}
|
||||
closedFunction()
|
||||
`;
|
||||
|
||||
const scriptWithCallback = `
|
||||
function callback (script) {
|
||||
const userFunction = script;
|
||||
const result = userFunction(CALLBACK_DATA);
|
||||
return { result, triggers };
|
||||
}
|
||||
callback(${sourceText});
|
||||
`;
|
||||
const script = callbackData ? scriptWithCallback : scriptToEvaluate;
|
||||
const data = callbackData
|
||||
? { ...safeData, CALLBACK_DATA: safeCallbackData }
|
||||
: safeData;
|
||||
const { result, triggers } = this.rootRealm.evaluate(script, data);
|
||||
return {
|
||||
result: this.convertToMainScope(result),
|
||||
triggers,
|
||||
};
|
||||
} catch (e) {
|
||||
log.debug(`Error: "${e.message}" when evaluating {{${sourceText}}}`);
|
||||
log.debug(e);
|
||||
return { result: undefined, triggers: [] };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -434,6 +434,12 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
|
|||
mapCenter: { lat: -34.397, long: 150.644 },
|
||||
defaultMarkers: [{ lat: -34.397, long: 150.644, title: "Test A" }],
|
||||
},
|
||||
SKELETON_WIDGET: {
|
||||
isLoading: true,
|
||||
rows: 1,
|
||||
columns: 1,
|
||||
widgetName: "Skeleton",
|
||||
},
|
||||
},
|
||||
configVersion: 1,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import React, { Component } from "react";
|
||||
import { RouteComponentProps, Link } from "react-router-dom";
|
||||
import { connect } from "react-redux";
|
||||
import { ReduxActionTypes } from "constants/ReduxActionConstants";
|
||||
import { getIsFetchingPage } from "selectors/appViewSelectors";
|
||||
import styled from "styled-components";
|
||||
import { ContainerWidgetProps } from "widgets/ContainerWidget";
|
||||
|
|
@ -18,6 +17,11 @@ import {
|
|||
} from "selectors/editorSelectors";
|
||||
import ConfirmRunModal from "pages/Editor/ConfirmRunModal";
|
||||
import { getCurrentApplication } from "selectors/applicationSelectors";
|
||||
import {
|
||||
isPermitted,
|
||||
PERMISSION_TYPE,
|
||||
} from "../Applications/permissionHelpers";
|
||||
import { fetchPublishedPage } from "actions/pageActions";
|
||||
|
||||
const Section = styled.section`
|
||||
background: ${props => props.theme.colors.bodyBG};
|
||||
|
|
@ -33,15 +37,10 @@ type AppViewerPageContainerProps = {
|
|||
currentPageName?: string;
|
||||
currentAppName?: string;
|
||||
fetchPage: (pageId: string, bustCache?: boolean) => void;
|
||||
currentAppPermissions?: string[];
|
||||
} & RouteComponentProps<AppViewerRouteParams>;
|
||||
|
||||
class AppViewerPageContainer extends Component<AppViewerPageContainerProps> {
|
||||
componentDidMount() {
|
||||
const { pageId } = this.props.match.params;
|
||||
if (pageId) {
|
||||
this.props.fetchPage(pageId, true);
|
||||
}
|
||||
}
|
||||
componentDidUpdate(previously: AppViewerPageContainerProps) {
|
||||
const { pageId } = this.props.match.params;
|
||||
if (
|
||||
|
|
@ -52,6 +51,28 @@ class AppViewerPageContainer extends Component<AppViewerPageContainerProps> {
|
|||
}
|
||||
}
|
||||
render() {
|
||||
let appsmithEditorLink;
|
||||
if (
|
||||
this.props.currentAppPermissions &&
|
||||
isPermitted(
|
||||
this.props.currentAppPermissions,
|
||||
PERMISSION_TYPE.MANAGE_APPLICATION,
|
||||
)
|
||||
) {
|
||||
appsmithEditorLink = (
|
||||
<p>
|
||||
Please add widgets to this page in the
|
||||
<Link
|
||||
to={BUILDER_PAGE_URL(
|
||||
this.props.match.params.applicationId,
|
||||
this.props.match.params.pageId,
|
||||
)}
|
||||
>
|
||||
Appsmith Editor
|
||||
</Link>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
const pageNotFound = (
|
||||
<Centered>
|
||||
<NonIdealState
|
||||
|
|
@ -63,19 +84,7 @@ class AppViewerPageContainer extends Component<AppViewerPageContainerProps> {
|
|||
/>
|
||||
}
|
||||
title="This page seems to be blank"
|
||||
description={
|
||||
<p>
|
||||
Please add widgets to this page in the
|
||||
<Link
|
||||
to={BUILDER_PAGE_URL(
|
||||
this.props.match.params.applicationId,
|
||||
this.props.match.params.pageId,
|
||||
)}
|
||||
>
|
||||
Appsmith Editor
|
||||
</Link>
|
||||
</p>
|
||||
}
|
||||
description={appsmithEditorLink}
|
||||
/>
|
||||
</Centered>
|
||||
);
|
||||
|
|
@ -118,19 +127,14 @@ const mapStateToProps = (state: AppState) => {
|
|||
widgets: getCanvasWidgetDsl(state),
|
||||
currentPageName: getCurrentPageName(state),
|
||||
currentAppName: currentApp?.name,
|
||||
currentAppPermissions: currentApp?.userPermissions,
|
||||
};
|
||||
return props;
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch: any) => ({
|
||||
fetchPage: (pageId: string, bustCache = false) =>
|
||||
dispatch({
|
||||
type: ReduxActionTypes.FETCH_PUBLISHED_PAGE_INIT,
|
||||
payload: {
|
||||
pageId,
|
||||
bustCache,
|
||||
},
|
||||
}),
|
||||
dispatch(fetchPublishedPage(pageId, bustCache)),
|
||||
});
|
||||
|
||||
export default connect(
|
||||
|
|
|
|||
|
|
@ -80,9 +80,69 @@ const OrgSection = styled.div``;
|
|||
|
||||
const PaddingWrapper = styled.div`
|
||||
width: ${props => props.theme.card.minWidth + props.theme.spaces[5] * 2}px;
|
||||
margin: ${props => props.theme.spaces[6] + 1}px
|
||||
${props => props.theme.spaces[12] + 2}px
|
||||
margin: ${props => props.theme.spaces[6] + 1}px 0px
|
||||
${props => props.theme.spaces[6] + 1}px 0px;
|
||||
|
||||
@media screen and (min-width: 1500px) {
|
||||
margin-right: ${props => props.theme.spaces[12] - 1}px;
|
||||
.bp3-card {
|
||||
width: ${props => props.theme.card.minWidth}px;
|
||||
height: ${props => props.theme.card.minHeight}px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1500px) and (max-width: 1512px) {
|
||||
width: ${props => props.theme.card.minWidth + props.theme.spaces[4] * 2}px;
|
||||
margin-right: ${props => props.theme.spaces[12] - 1}px;
|
||||
.bp3-card {
|
||||
width: ${props => props.theme.card.minWidth - 5}px;
|
||||
height: ${props => props.theme.card.minHeight - 5}px;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1478px) and (max-width: 1500px) {
|
||||
width: ${props => props.theme.card.minWidth + props.theme.spaces[4] * 2}px;
|
||||
margin-right: ${props => props.theme.spaces[11] + 1}px;
|
||||
.bp3-card {
|
||||
width: ${props => props.theme.card.minWidth - 8}px;
|
||||
height: ${props => props.theme.card.minHeight - 8}px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1447px) and (max-width: 1477px) {
|
||||
width: ${props => props.theme.card.minWidth + props.theme.spaces[3] * 2}px;
|
||||
margin-right: ${props => props.theme.spaces[11] - 4}px;
|
||||
.bp3-card {
|
||||
width: ${props => props.theme.card.minWidth - 8}px;
|
||||
height: ${props => props.theme.card.minHeight - 8}px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1417px) and (max-width: 1446px) {
|
||||
width: ${props => props.theme.card.minWidth + props.theme.spaces[3] * 2}px;
|
||||
margin-right: ${props => props.theme.spaces[11] - 8}px;
|
||||
.bp3-card {
|
||||
width: ${props => props.theme.card.minWidth - 11}px;
|
||||
height: ${props => props.theme.card.minHeight - 11}px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1400px) and (max-width: 1417px) {
|
||||
width: ${props => props.theme.card.minWidth + props.theme.spaces[2] * 2}px;
|
||||
margin-right: ${props => props.theme.spaces[11] - 12}px;
|
||||
.bp3-card {
|
||||
width: ${props => props.theme.card.minWidth - 15}px;
|
||||
height: ${props => props.theme.card.minHeight - 15}px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1400px) {
|
||||
width: ${props => props.theme.card.minWidth + props.theme.spaces[2] * 2}px;
|
||||
margin-right: ${props => props.theme.spaces[11] - 16}px;
|
||||
.bp3-card {
|
||||
width: ${props => props.theme.card.minWidth - 15}px;
|
||||
height: ${props => props.theme.card.minHeight - 15}px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledDialog = styled(Dialog)<{ setMaxWidth?: boolean }>`
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import {
|
|||
DataTree,
|
||||
} from "entities/DataTree/dataTreeFactory";
|
||||
import { useSelector } from "react-redux";
|
||||
import { evaluateDataTreeWithoutFunctions } from "selectors/dataTreeSelectors";
|
||||
import { getDataTree } from "selectors/dataTreeSelectors";
|
||||
import PerformanceTracker, {
|
||||
PerformanceTransactionName,
|
||||
} from "utils/PerformanceTracker";
|
||||
|
|
@ -31,7 +31,7 @@ export const EntityProperties = (props: {
|
|||
);
|
||||
});
|
||||
let entity: any;
|
||||
const dataTree: DataTree = useSelector(evaluateDataTreeWithoutFunctions);
|
||||
const dataTree: DataTree = useSelector(getDataTree);
|
||||
if (props.isCurrentPage && dataTree[props.entityName]) {
|
||||
entity = dataTree[props.entityName];
|
||||
} else if (props.entity) {
|
||||
|
|
@ -71,7 +71,7 @@ export const EntityProperties = (props: {
|
|||
case ENTITY_TYPE.WIDGET:
|
||||
const type: Exclude<
|
||||
Partial<WidgetType>,
|
||||
"CANVAS_WIDGET" | "ICON_WIDGET"
|
||||
"CANVAS_WIDGET" | "ICON_WIDGET" | "SKELETON_WIDGET"
|
||||
> = entity.type;
|
||||
config = entityDefinitions[type];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import React, { useState } from "react";
|
||||
import styled from "styled-components";
|
||||
import { extraLibraries } from "jsExecution/JSExecutionManagerSingleton";
|
||||
import { Collapse, Icon, IconName, Tooltip } from "@blueprintjs/core";
|
||||
import { IconNames } from "@blueprintjs/icons";
|
||||
import { Colors } from "constants/Colors";
|
||||
import { BindingText } from "pages/Editor/APIEditor/Form";
|
||||
import { extraLibraries } from "utils/DynamicBindingUtils";
|
||||
|
||||
const Wrapper = styled.div`
|
||||
font-size: 13px;
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ export const getWidgetProperies = (
|
|||
entityDefinitions[
|
||||
widgetProps.type as Exclude<
|
||||
Partial<WidgetType>,
|
||||
"CANVAS_WIDGET" | "ICON_WIDGET"
|
||||
"CANVAS_WIDGET" | "ICON_WIDGET" | "SKELETON_WIDGET"
|
||||
>
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -219,6 +219,7 @@ const TabContainerView = styled.div`
|
|||
|
||||
.react-tabs__tab-panel {
|
||||
border: 1px solid #ebeff2;
|
||||
overflow: scroll;
|
||||
}
|
||||
.react-tabs__tab-list {
|
||||
margin: 0px;
|
||||
|
|
@ -226,9 +227,7 @@ const TabContainerView = styled.div`
|
|||
`;
|
||||
|
||||
const SettingsWrapper = styled.div`
|
||||
padding-left: 15px;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
padding: 5px 10px;
|
||||
`;
|
||||
|
||||
const AddWidgetButton = styled(BaseButton)`
|
||||
|
|
@ -246,6 +245,10 @@ const OutputHeader = styled.div`
|
|||
align-items: center;
|
||||
`;
|
||||
|
||||
const FieldWrapper = styled.div`
|
||||
margin-top: 15px;
|
||||
`;
|
||||
|
||||
type QueryFormProps = {
|
||||
onDeleteClick: () => void;
|
||||
onRunClick: () => void;
|
||||
|
|
@ -484,23 +487,28 @@ const QueryEditorForm: React.FC<Props> = (props: Props) => {
|
|||
{
|
||||
key: "query",
|
||||
title: "Query",
|
||||
panelComponent:
|
||||
editorConfig && editorConfig.length > 0 ? (
|
||||
editorConfig.map(renderEachConfig)
|
||||
) : (
|
||||
<>
|
||||
<ErrorMessage>An unexpected error occurred</ErrorMessage>
|
||||
<Tag
|
||||
round
|
||||
intent="warning"
|
||||
interactive
|
||||
minimal
|
||||
onClick={() => window.location.reload()}
|
||||
>
|
||||
Refresh
|
||||
</Tag>
|
||||
</>
|
||||
),
|
||||
panelComponent: (
|
||||
<SettingsWrapper>
|
||||
{editorConfig && editorConfig.length > 0 ? (
|
||||
editorConfig.map(renderEachConfig)
|
||||
) : (
|
||||
<>
|
||||
<ErrorMessage>
|
||||
An unexpected error occurred
|
||||
</ErrorMessage>
|
||||
<Tag
|
||||
round
|
||||
intent="warning"
|
||||
interactive
|
||||
minimal
|
||||
onClick={() => window.location.reload()}
|
||||
>
|
||||
Refresh
|
||||
</Tag>
|
||||
</>
|
||||
)}
|
||||
</SettingsWrapper>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "settings",
|
||||
|
|
@ -573,13 +581,13 @@ const renderEachConfig = (section: any): any => {
|
|||
try {
|
||||
const { configProperty } = propertyControlOrSection;
|
||||
return (
|
||||
<div key={configProperty} style={{ marginTop: "8px" }}>
|
||||
<FieldWrapper key={configProperty}>
|
||||
{FormControlFactory.createControl(
|
||||
{ ...propertyControlOrSection },
|
||||
{},
|
||||
false,
|
||||
)}
|
||||
</div>
|
||||
</FieldWrapper>
|
||||
);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { createReducer } from "utils/AppsmithUtils";
|
|||
import { ReduxActionTypes, ReduxAction } from "constants/ReduxActionConstants";
|
||||
import { UpdateWidgetMetaPropertyPayload } from "actions/metaActions";
|
||||
|
||||
export type MetaState = Record<string, object>;
|
||||
export type MetaState = Record<string, Record<string, unknown>>;
|
||||
|
||||
const initialState: MetaState = {};
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import { MapWidgetProps } from "widgets/MapWidget";
|
|||
import { ModalWidgetProps } from "widgets/ModalWidget";
|
||||
import { IconWidgetProps } from "widgets/IconWidget";
|
||||
import { VideoWidgetProps } from "widgets/VideoWidget";
|
||||
import { SkeletonWidgetProps } from "../../widgets/SkeletonWidget";
|
||||
|
||||
const initialState: WidgetConfigReducerState = WidgetConfigResponse;
|
||||
|
||||
|
|
@ -74,6 +75,7 @@ export interface WidgetConfigReducerState {
|
|||
CANVAS_WIDGET: Partial<ContainerWidgetProps<WidgetProps>> &
|
||||
WidgetConfigProps;
|
||||
ICON_WIDGET: Partial<IconWidgetProps> & WidgetConfigProps;
|
||||
SKELETON_WIDGET: Partial<SkeletonWidgetProps> & WidgetConfigProps;
|
||||
};
|
||||
configVersion: number;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
import { createReducer } from "utils/AppsmithUtils";
|
||||
import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants";
|
||||
|
||||
export type EvaluationDependencyState = {
|
||||
dependencyMap: Record<string, Array<string>>;
|
||||
dependencyTree: Array<[string, string]>;
|
||||
};
|
||||
|
||||
const initialState: EvaluationDependencyState = {
|
||||
dependencyMap: {},
|
||||
dependencyTree: [],
|
||||
};
|
||||
|
||||
const evaluationDependencyReducer = createReducer(initialState, {
|
||||
[ReduxActionTypes.SET_EVALUATION_DEPENDENCIES]: (
|
||||
state: EvaluationDependencyState,
|
||||
action: ReduxAction<EvaluationDependencyState>,
|
||||
) => action.payload,
|
||||
});
|
||||
|
||||
export default evaluationDependencyReducer;
|
||||
8
app/client/src/reducers/evalutationReducers/index.ts
Normal file
8
app/client/src/reducers/evalutationReducers/index.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import { combineReducers } from "redux";
|
||||
import evaluatedTreeReducer from "./treeReducer";
|
||||
import evaluationDependencyReducer from "./dependencyReducer";
|
||||
|
||||
export default combineReducers({
|
||||
tree: evaluatedTreeReducer,
|
||||
dependencies: evaluationDependencyReducer,
|
||||
});
|
||||
16
app/client/src/reducers/evalutationReducers/treeReducer.ts
Normal file
16
app/client/src/reducers/evalutationReducers/treeReducer.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { createReducer } from "utils/AppsmithUtils";
|
||||
import { DataTree } from "entities/DataTree/dataTreeFactory";
|
||||
import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants";
|
||||
|
||||
export type EvaluatedTreeState = DataTree;
|
||||
|
||||
const initialState: EvaluatedTreeState = {};
|
||||
|
||||
const evaluatedTreeReducer = createReducer(initialState, {
|
||||
[ReduxActionTypes.SET_EVALUATED_TREE]: (
|
||||
state: EvaluatedTreeState,
|
||||
action: ReduxAction<DataTree>,
|
||||
) => action.payload,
|
||||
});
|
||||
|
||||
export default evaluatedTreeReducer;
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import { combineReducers } from "redux";
|
||||
import entityReducer from "./entityReducers";
|
||||
import uiReducer from "./uiReducers";
|
||||
import evaluationsReducer from "./evalutationReducers";
|
||||
import { reducer as formReducer } from "redux-form";
|
||||
import { CanvasWidgetsReduxState } from "./entityReducers/canvasWidgetsReducer";
|
||||
import { EditorReduxState } from "./uiReducers/editorReducer";
|
||||
|
|
@ -34,10 +35,13 @@ import { PageDSLsReduxState } from "./uiReducers/pageDSLReducer";
|
|||
import { ConfirmRunActionReduxState } from "./uiReducers/confirmRunActionReducer";
|
||||
import { AppDataState } from "reducers/entityReducers/appReducer";
|
||||
import { DatasourceNameReduxState } from "./uiReducers/datasourceNameReducer";
|
||||
import { EvaluatedTreeState } from "./evalutationReducers/treeReducer";
|
||||
import { EvaluationDependencyState } from "./evalutationReducers/dependencyReducer";
|
||||
|
||||
const appReducer = combineReducers({
|
||||
entities: entityReducer,
|
||||
ui: uiReducer,
|
||||
evaluations: evaluationsReducer,
|
||||
form: formReducer,
|
||||
});
|
||||
|
||||
|
|
@ -80,4 +84,8 @@ export interface AppState {
|
|||
meta: MetaState;
|
||||
app: AppDataState;
|
||||
};
|
||||
evaluations: {
|
||||
tree: EvaluatedTreeState;
|
||||
dependencies: EvaluationDependencyState;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,15 +22,7 @@ import {
|
|||
takeEvery,
|
||||
takeLatest,
|
||||
} from "redux-saga/effects";
|
||||
import {
|
||||
evaluateDataTreeWithFunctions,
|
||||
evaluateDataTreeWithoutFunctions,
|
||||
} from "selectors/dataTreeSelectors";
|
||||
import {
|
||||
getDynamicBindings,
|
||||
getDynamicValue,
|
||||
isDynamicValue,
|
||||
} from "utils/DynamicBindingUtils";
|
||||
import { getDynamicBindings, isDynamicValue } from "utils/DynamicBindingUtils";
|
||||
import {
|
||||
ActionDescription,
|
||||
RunActionPayload,
|
||||
|
|
@ -52,8 +44,8 @@ import {
|
|||
import {
|
||||
executeApiActionRequest,
|
||||
executeApiActionSuccess,
|
||||
updateAction,
|
||||
showRunActionConfirmModal,
|
||||
updateAction,
|
||||
} from "actions/actionActions";
|
||||
import { Action, RestAction } from "entities/Action";
|
||||
import ActionAPI, {
|
||||
|
|
@ -83,6 +75,7 @@ import PerformanceTracker, {
|
|||
PerformanceTransactionName,
|
||||
} from "utils/PerformanceTracker";
|
||||
import { getCurrentApplication } from "selectors/applicationSelectors";
|
||||
import { evaluateDynamicTrigger, evaluateSingleValue } from "./evaluationsSaga";
|
||||
|
||||
function* navigateActionSaga(
|
||||
action: { pageNameOrUrl: string; params: Record<string, string> },
|
||||
|
|
@ -204,10 +197,7 @@ const isErrorResponse = (response: ActionApiResponse) => {
|
|||
};
|
||||
|
||||
export function* evaluateDynamicBoundValueSaga(path: string): any {
|
||||
log.debug("Evaluating data tree to get action binding value");
|
||||
const tree = yield select(evaluateDataTreeWithoutFunctions);
|
||||
const dynamicResult = getDynamicValue(`{{${path}}}`, tree);
|
||||
return dynamicResult.result;
|
||||
return yield call(evaluateSingleValue, `{{${path}}}`);
|
||||
}
|
||||
|
||||
const EXECUTION_PARAM_PATH = "this.params";
|
||||
|
|
@ -483,15 +473,20 @@ function* executeActionTriggers(
|
|||
|
||||
function* executeAppAction(action: ReduxAction<ExecuteActionPayload>) {
|
||||
const { dynamicString, event, responseData } = action.payload;
|
||||
log.debug("Evaluating data tree to get action trigger");
|
||||
log.debug({ dynamicString });
|
||||
const tree = yield select(evaluateDataTreeWithFunctions);
|
||||
log.debug({ tree });
|
||||
const { triggers } = getDynamicValue(dynamicString, tree, responseData, true);
|
||||
log.debug({ dynamicString, responseData });
|
||||
|
||||
const triggers = yield call(
|
||||
evaluateDynamicTrigger,
|
||||
dynamicString,
|
||||
responseData,
|
||||
);
|
||||
|
||||
log.debug({ triggers });
|
||||
if (triggers && triggers.length) {
|
||||
yield all(
|
||||
triggers.map(trigger => call(executeActionTriggers, trigger, event)),
|
||||
triggers.map((trigger: ActionDescription<any>) =>
|
||||
call(executeActionTriggers, trigger, event),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
if (event.callback) event.callback({ success: true });
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import _ from "lodash";
|
||||
import { put, debounce, takeEvery, all } from "redux-saga/effects";
|
||||
import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants";
|
||||
import { batchActionSuccess } from "../actions/batchActions";
|
||||
|
||||
const BATCH_PRIORITY = {
|
||||
[ReduxActionTypes.SET_META_PROP]: {
|
||||
|
|
@ -62,6 +63,7 @@ function* executeBatchSaga() {
|
|||
yield put(sagaAction);
|
||||
}
|
||||
}
|
||||
yield put(batchActionSuccess(batch));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { fetchEditorConfigs } from "actions/configsActions";
|
|||
import {
|
||||
fetchPage,
|
||||
fetchPageList,
|
||||
fetchPublishedPage,
|
||||
setAppMode,
|
||||
updateAppStore,
|
||||
} from "actions/pageActions";
|
||||
|
|
@ -26,6 +27,7 @@ import { validateResponse } from "./ErrorSagas";
|
|||
import { extractCurrentDSL } from "utils/WidgetPropsUtils";
|
||||
import { APP_MODE } from "reducers/entityReducers/appReducer";
|
||||
import { getAppStoreName } from "constants/AppConstants";
|
||||
import { getDefaultPageId } from "./selectors";
|
||||
|
||||
const getAppStore = (appId: string) => {
|
||||
const appStoreName = getAppStoreName(appId);
|
||||
|
|
@ -146,7 +148,7 @@ export function* populatePageDSLsSaga() {
|
|||
}
|
||||
|
||||
export function* initializeAppViewerSaga(
|
||||
action: ReduxAction<{ pageId: string; applicationId: string }>,
|
||||
action: ReduxAction<{ applicationId: string }>,
|
||||
) {
|
||||
const { applicationId } = action.payload;
|
||||
yield all([
|
||||
|
|
@ -160,16 +162,23 @@ export function* initializeAppViewerSaga(
|
|||
take(ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS),
|
||||
]);
|
||||
|
||||
yield put(setAppMode(APP_MODE.PUBLISHED));
|
||||
yield put(updateAppStore(getAppStore(applicationId)));
|
||||
const pageId = yield select(getDefaultPageId);
|
||||
|
||||
if (pageId) {
|
||||
yield put(fetchPublishedPage(pageId, true));
|
||||
yield take(ReduxActionTypes.FETCH_PUBLISHED_PAGE_SUCCESS);
|
||||
|
||||
yield put(setAppMode(APP_MODE.PUBLISHED));
|
||||
yield put(updateAppStore(getAppStore(applicationId)));
|
||||
|
||||
yield put({
|
||||
type: ReduxActionTypes.INITIALIZE_PAGE_VIEWER_SUCCESS,
|
||||
});
|
||||
if ("serviceWorker" in navigator) {
|
||||
yield put({
|
||||
type: ReduxActionTypes.FETCH_ALL_PUBLISHED_PAGES,
|
||||
type: ReduxActionTypes.INITIALIZE_PAGE_VIEWER_SUCCESS,
|
||||
});
|
||||
if ("serviceWorker" in navigator) {
|
||||
yield put({
|
||||
type: ReduxActionTypes.FETCH_ALL_PUBLISHED_PAGES,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ import {
|
|||
fetchActionsForPage,
|
||||
setActionsToExecuteOnPageLoad,
|
||||
} from "actions/actionActions";
|
||||
import { clearCaches } from "utils/DynamicBindingUtils";
|
||||
import { clearEvalCache } from "./evaluationsSaga";
|
||||
import { UrlDataState } from "reducers/entityReducers/appReducer";
|
||||
import { getQueryParams } from "utils/AppsmithUtils";
|
||||
import PerformanceTracker, {
|
||||
|
|
@ -162,7 +162,7 @@ export function* fetchPageSaga(
|
|||
const isValidResponse = yield validateResponse(fetchPageResponse);
|
||||
if (isValidResponse) {
|
||||
// Clear any existing caches
|
||||
clearCaches();
|
||||
yield call(clearEvalCache);
|
||||
// Set url params
|
||||
yield call(setDataUrl);
|
||||
// Get Canvas payload
|
||||
|
|
@ -225,7 +225,7 @@ export function* fetchPublishedPageSaga(
|
|||
const isValidResponse = yield validateResponse(response);
|
||||
if (isValidResponse) {
|
||||
// Clear any existing caches
|
||||
clearCaches();
|
||||
yield call(clearEvalCache);
|
||||
// Set url params
|
||||
yield call(setDataUrl);
|
||||
// Get Canvas payload
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { ApplicationPagePayload } from "api/ApplicationApi";
|
||||
|
||||
export const getDefaultPageId = (
|
||||
pages?: ApplicationPagePayload[],
|
||||
): string | undefined => {
|
||||
|
|
|
|||
|
|
@ -57,7 +57,6 @@ import {
|
|||
RenderModes,
|
||||
WidgetType,
|
||||
} from "constants/WidgetConstants";
|
||||
import ValidationFactory from "utils/ValidationFactory";
|
||||
import WidgetConfigResponse from "mockResponses/WidgetConfigResponse";
|
||||
import {
|
||||
saveCopiedWidgets,
|
||||
|
|
@ -78,6 +77,9 @@ import {
|
|||
getCurrentPageId,
|
||||
} from "selectors/editorSelectors";
|
||||
import { forceOpenPropertyPane } from "actions/widgetActions";
|
||||
import { getDataTree } from "selectors/dataTreeSelectors";
|
||||
import { DataTreeWidget } from "entities/DataTree/dataTreeFactory";
|
||||
import { validateProperty } from "./evaluationsSaga";
|
||||
|
||||
function getChildWidgetProps(
|
||||
parent: FlattenedWidgetProps,
|
||||
|
|
@ -160,6 +162,7 @@ function* generateChildWidgets(
|
|||
);
|
||||
}
|
||||
widget.parentId = parent.widgetId;
|
||||
delete widget.blueprint;
|
||||
return { widgetId: widget.widgetId, widgets };
|
||||
}
|
||||
|
||||
|
|
@ -631,7 +634,9 @@ function* setWidgetDynamicPropertySaga(
|
|||
yield put(updateWidgetProperty(widgetId, propertyName, value));
|
||||
} else {
|
||||
delete dynamicProperties[propertyName];
|
||||
const { parsed } = ValidationFactory.validateWidgetProperty(
|
||||
// TODO (hetu) can we eliminate this use of validation
|
||||
const { parsed } = yield call(
|
||||
validateProperty,
|
||||
widget.type,
|
||||
propertyName,
|
||||
propertyValue,
|
||||
|
|
@ -668,6 +673,39 @@ function* resetChildrenMetaSaga(action: ReduxAction<{ widgetId: string }>) {
|
|||
const childId = childrenIds[childIndex];
|
||||
yield put(resetWidgetMetaProperty(childId));
|
||||
}
|
||||
yield call(resetEvaluatedWidgetMetaProperties, childrenIds);
|
||||
}
|
||||
|
||||
// This is needed because evaluation takes some time and we can reset the props
|
||||
// in the evaluated value much faster like this
|
||||
function* resetEvaluatedWidgetMetaProperties(widgetIds: string[]) {
|
||||
const evaluatedDataTree = yield select(getDataTree);
|
||||
const updates: Record<string, DataTreeWidget> = {};
|
||||
for (const index in widgetIds) {
|
||||
const widgetId = widgetIds[index];
|
||||
const widget = _.find(evaluatedDataTree, { widgetId }) as DataTreeWidget;
|
||||
const widgetToUpdate = { ...widget };
|
||||
const metaPropsMap = WidgetFactory.getWidgetMetaPropertiesMap(widget.type);
|
||||
const defaultPropertiesMap = WidgetFactory.getWidgetDefaultPropertiesMap(
|
||||
widget.type,
|
||||
);
|
||||
Object.keys(metaPropsMap).forEach(metaProp => {
|
||||
if (metaProp in defaultPropertiesMap) {
|
||||
widgetToUpdate[metaProp] = widget[defaultPropertiesMap[metaProp]];
|
||||
} else {
|
||||
widgetToUpdate[metaProp] = metaPropsMap[metaProp];
|
||||
}
|
||||
});
|
||||
updates[widget.widgetName] = widgetToUpdate;
|
||||
}
|
||||
const newEvaluatedDataTree = {
|
||||
...evaluatedDataTree,
|
||||
...updates,
|
||||
};
|
||||
yield put({
|
||||
type: ReduxActionTypes.SET_EVALUATED_TREE,
|
||||
payload: newEvaluatedDataTree,
|
||||
});
|
||||
}
|
||||
|
||||
function* updateCanvasSize(
|
||||
|
|
@ -997,6 +1035,12 @@ function* addTableWidgetFromQuerySaga(action: ReduxAction<string>) {
|
|||
parentRowSpace: 1,
|
||||
parentColumnSpace: 1,
|
||||
isLoading: false,
|
||||
props: {
|
||||
tableData: `{{${queryName}.data}}`,
|
||||
dynamicBindings: {
|
||||
tableData: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
const {
|
||||
leftColumn,
|
||||
|
|
@ -1035,14 +1079,6 @@ function* addTableWidgetFromQuerySaga(action: ReduxAction<string>) {
|
|||
payload: { widgetId: newWidget.newWidgetId },
|
||||
});
|
||||
yield put(forceOpenPropertyPane(newWidget.newWidgetId));
|
||||
yield put(
|
||||
updateWidgetPropertyRequest(
|
||||
newWidget.newWidgetId,
|
||||
"tableData",
|
||||
`{{${queryName}.data}}`,
|
||||
RenderModes.CANVAS,
|
||||
),
|
||||
);
|
||||
} catch (error) {
|
||||
AppToaster.show({
|
||||
message: "Failed to add the widget",
|
||||
|
|
|
|||
227
app/client/src/sagas/evaluationsSaga.ts
Normal file
227
app/client/src/sagas/evaluationsSaga.ts
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
import {
|
||||
all,
|
||||
call,
|
||||
fork,
|
||||
put,
|
||||
select,
|
||||
take,
|
||||
takeLatest,
|
||||
} from "redux-saga/effects";
|
||||
import { eventChannel, EventChannel } from "redux-saga";
|
||||
import {
|
||||
ReduxAction,
|
||||
ReduxActionErrorTypes,
|
||||
ReduxActionTypes,
|
||||
} from "constants/ReduxActionConstants";
|
||||
import {
|
||||
getDataTree,
|
||||
getUnevaluatedDataTree,
|
||||
} from "selectors/dataTreeSelectors";
|
||||
import WidgetFactory, { WidgetTypeConfigMap } from "../utils/WidgetFactory";
|
||||
import Worker from "worker-loader!../workers/evaluation.worker";
|
||||
import {
|
||||
EVAL_WORKER_ACTIONS,
|
||||
EvalError,
|
||||
EvalErrorTypes,
|
||||
} from "../utils/DynamicBindingUtils";
|
||||
import { ToastType } from "react-toastify";
|
||||
import { AppToaster } from "../components/editorComponents/ToastComponent";
|
||||
import log from "loglevel";
|
||||
import _ from "lodash";
|
||||
import { WidgetType } from "../constants/WidgetConstants";
|
||||
import { WidgetProps } from "../widgets/BaseWidget";
|
||||
|
||||
let evaluationWorker: Worker;
|
||||
let workerChannel: EventChannel<any>;
|
||||
let widgetTypeConfigMap: WidgetTypeConfigMap;
|
||||
|
||||
const initEvaluationWorkers = () => {
|
||||
widgetTypeConfigMap = WidgetFactory.getWidgetTypeConfigMap();
|
||||
evaluationWorker = new Worker();
|
||||
workerChannel = eventChannel(emitter => {
|
||||
evaluationWorker.addEventListener("message", emitter);
|
||||
// The subscriber must return an unsubscribe function
|
||||
return () => {
|
||||
evaluationWorker.removeEventListener("message", emitter);
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const evalErrorHandler = (errors: EvalError[]) => {
|
||||
errors.forEach(error => {
|
||||
if (error.type === EvalErrorTypes.DEPENDENCY_ERROR) {
|
||||
AppToaster.show({
|
||||
message: error.message,
|
||||
type: ToastType.ERROR,
|
||||
});
|
||||
}
|
||||
log.debug(error);
|
||||
});
|
||||
};
|
||||
|
||||
function* evaluateTreeSaga() {
|
||||
const unEvalTree = yield select(getUnevaluatedDataTree);
|
||||
log.debug({ unEvalTree });
|
||||
evaluationWorker.postMessage({
|
||||
action: EVAL_WORKER_ACTIONS.EVAL_TREE,
|
||||
dataTree: unEvalTree,
|
||||
widgetTypeConfigMap,
|
||||
});
|
||||
const workerResponse = yield take(workerChannel);
|
||||
const { errors, dataTree } = workerResponse.data;
|
||||
const parsedDataTree = JSON.parse(dataTree);
|
||||
log.debug({ dataTree: parsedDataTree });
|
||||
evalErrorHandler(errors);
|
||||
yield put({
|
||||
type: ReduxActionTypes.SET_EVALUATED_TREE,
|
||||
payload: parsedDataTree,
|
||||
});
|
||||
}
|
||||
|
||||
export function* evaluateSingleValue(binding: string) {
|
||||
if (evaluationWorker) {
|
||||
const evalTree = yield select(getDataTree);
|
||||
evaluationWorker.postMessage({
|
||||
action: EVAL_WORKER_ACTIONS.EVAL_SINGLE,
|
||||
dataTree: evalTree,
|
||||
binding,
|
||||
});
|
||||
const workerResponse = yield take(workerChannel);
|
||||
const { errors, value } = workerResponse.data;
|
||||
evalErrorHandler(errors);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
export function* evaluateDynamicTrigger(
|
||||
dynamicTrigger: string,
|
||||
callbackData: any,
|
||||
) {
|
||||
if (evaluationWorker) {
|
||||
const unEvalTree = yield select(getUnevaluatedDataTree);
|
||||
evaluationWorker.postMessage({
|
||||
action: EVAL_WORKER_ACTIONS.EVAL_TRIGGER,
|
||||
dataTree: unEvalTree,
|
||||
dynamicTrigger,
|
||||
callbackData,
|
||||
});
|
||||
const workerResponse = yield take(workerChannel);
|
||||
const { errors, triggers } = workerResponse.data;
|
||||
evalErrorHandler(errors);
|
||||
return triggers;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
export function* clearEvalCache() {
|
||||
if (evaluationWorker) {
|
||||
evaluationWorker.postMessage({
|
||||
action: EVAL_WORKER_ACTIONS.CLEAR_CACHE,
|
||||
});
|
||||
yield take(workerChannel);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export function* clearEvalPropertyCache(propertyPath: string) {
|
||||
if (evaluationWorker) {
|
||||
evaluationWorker.postMessage({
|
||||
action: EVAL_WORKER_ACTIONS.CLEAR_PROPERTY_CACHE,
|
||||
propertyPath,
|
||||
});
|
||||
yield take(workerChannel);
|
||||
}
|
||||
}
|
||||
|
||||
export function* validateProperty(
|
||||
widgetType: WidgetType,
|
||||
property: string,
|
||||
value: any,
|
||||
props: WidgetProps,
|
||||
) {
|
||||
if (evaluationWorker) {
|
||||
evaluationWorker.postMessage({
|
||||
action: EVAL_WORKER_ACTIONS.VALIDATE_PROPERTY,
|
||||
widgetType,
|
||||
property,
|
||||
value,
|
||||
props,
|
||||
});
|
||||
return yield take(workerChannel);
|
||||
}
|
||||
return { isValid: true, parsed: value };
|
||||
}
|
||||
|
||||
const EVALUATE_REDUX_ACTIONS = [
|
||||
// Actions
|
||||
ReduxActionTypes.FETCH_ACTIONS_SUCCESS,
|
||||
ReduxActionTypes.FETCH_ACTIONS_VIEW_MODE_SUCCESS,
|
||||
ReduxActionErrorTypes.FETCH_ACTIONS_ERROR,
|
||||
ReduxActionErrorTypes.FETCH_ACTIONS_VIEW_MODE_ERROR,
|
||||
ReduxActionTypes.FETCH_ACTIONS_FOR_PAGE_SUCCESS,
|
||||
ReduxActionTypes.SUBMIT_CURL_FORM_SUCCESS,
|
||||
ReduxActionTypes.CREATE_ACTION_SUCCESS,
|
||||
ReduxActionTypes.UPDATE_ACTION_PROPERTY,
|
||||
ReduxActionTypes.DELETE_ACTION_SUCCESS,
|
||||
ReduxActionTypes.COPY_ACTION_SUCCESS,
|
||||
ReduxActionTypes.MOVE_ACTION_SUCCESS,
|
||||
ReduxActionTypes.RUN_ACTION_REQUEST,
|
||||
ReduxActionTypes.RUN_ACTION_SUCCESS,
|
||||
ReduxActionErrorTypes.RUN_ACTION_ERROR,
|
||||
ReduxActionTypes.EXECUTE_API_ACTION_REQUEST,
|
||||
ReduxActionTypes.EXECUTE_API_ACTION_SUCCESS,
|
||||
ReduxActionErrorTypes.EXECUTE_ACTION_ERROR,
|
||||
// App Data
|
||||
ReduxActionTypes.SET_APP_MODE,
|
||||
ReduxActionTypes.FETCH_USER_DETAILS_SUCCESS,
|
||||
ReduxActionTypes.SET_URL_DATA,
|
||||
ReduxActionTypes.UPDATE_APP_STORE,
|
||||
// Widgets
|
||||
ReduxActionTypes.UPDATE_LAYOUT,
|
||||
ReduxActionTypes.UPDATE_WIDGET_PROPERTY,
|
||||
ReduxActionTypes.UPDATE_WIDGET_NAME_SUCCESS,
|
||||
// Widget Meta
|
||||
ReduxActionTypes.SET_META_PROP,
|
||||
ReduxActionTypes.RESET_WIDGET_META,
|
||||
// Pages
|
||||
ReduxActionTypes.FETCH_PAGE_SUCCESS,
|
||||
ReduxActionTypes.FETCH_PUBLISHED_PAGE_SUCCESS,
|
||||
// Batches
|
||||
ReduxActionTypes.BATCH_UPDATES_SUCCESS,
|
||||
];
|
||||
|
||||
function* evaluationChangeListenerSaga() {
|
||||
initEvaluationWorkers();
|
||||
yield call(evaluateTreeSaga);
|
||||
while (true) {
|
||||
const action: ReduxAction<any> = yield take(EVALUATE_REDUX_ACTIONS);
|
||||
// When batching success action happens, we need to only evaluate
|
||||
// if the batch had any action we need to evaluate properties for
|
||||
if (action.type === ReduxActionTypes.BATCH_UPDATES_SUCCESS) {
|
||||
const batchedActionTypes = action.payload.map(
|
||||
(batchedAction: ReduxAction<any>) => batchedAction.type,
|
||||
);
|
||||
if (
|
||||
_.intersection(EVALUATE_REDUX_ACTIONS, batchedActionTypes).length === 0
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
log.debug(`Evaluating`, { action });
|
||||
yield fork(evaluateTreeSaga);
|
||||
}
|
||||
// TODO(hetu) need an action to stop listening and evaluate (exit app)
|
||||
}
|
||||
|
||||
export default function* evaluationSagaListeners() {
|
||||
yield all([
|
||||
takeLatest(
|
||||
ReduxActionTypes.INITIALIZE_EDITOR_SUCCESS,
|
||||
evaluationChangeListenerSaga,
|
||||
),
|
||||
takeLatest(
|
||||
ReduxActionTypes.INITIALIZE_PAGE_VIEWER_SUCCESS,
|
||||
evaluationChangeListenerSaga,
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@ import queryPaneSagas from "./QueryPaneSagas";
|
|||
import modalSagas from "./ModalSagas";
|
||||
import batchSagas from "./BatchSagas";
|
||||
import themeSagas from "./ThemeSaga";
|
||||
import evaluationsSaga from "./evaluationsSaga";
|
||||
|
||||
export function* rootSaga() {
|
||||
yield all([
|
||||
|
|
@ -42,5 +43,6 @@ export function* rootSaga() {
|
|||
spawn(modalSagas),
|
||||
spawn(batchSagas),
|
||||
spawn(themeSagas),
|
||||
spawn(evaluationsSaga),
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,11 +55,8 @@ export const getWidgetNamePrefix = (
|
|||
return state.entities.widgetConfig.config[type].widgetName;
|
||||
};
|
||||
|
||||
export const getDefaultPageId = (state: AppState, pageId?: string): string => {
|
||||
const { pages } = state.entities.pageList;
|
||||
const page = pages.find(page => page.pageId === pageId);
|
||||
return page ? page.pageId : pages[0].pageId;
|
||||
};
|
||||
export const getDefaultPageId = (state: AppState): string | undefined =>
|
||||
state.entities.pageList.defaultPageId;
|
||||
|
||||
export const getExistingWidgetNames = createSelector(
|
||||
getWidgets,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { createSelector } from "reselect";
|
||||
import { getActionsForCurrentPage, getAppData } from "./entitiesSelector";
|
||||
import { ActionDataState } from "reducers/entityReducers/actionsReducer";
|
||||
import { getEvaluatedDataTree } from "utils/DynamicBindingUtils";
|
||||
import { DataTree, DataTreeFactory } from "entities/DataTree/dataTreeFactory";
|
||||
import { getWidgets, getWidgetsMeta } from "sagas/selectors";
|
||||
import * as log from "loglevel";
|
||||
|
|
@ -10,6 +9,7 @@ import { getPageList } from "./appViewSelectors";
|
|||
import PerformanceTracker, {
|
||||
PerformanceTransactionName,
|
||||
} from "utils/PerformanceTracker";
|
||||
import { AppState } from "../reducers";
|
||||
|
||||
export const getUnevaluatedDataTree = createSelector(
|
||||
getActionsForCurrentPage,
|
||||
|
|
@ -22,43 +22,26 @@ export const getUnevaluatedDataTree = createSelector(
|
|||
PerformanceTransactionName.CONSTRUCT_UNEVAL_TREE,
|
||||
);
|
||||
const pageList = pageListPayload || [];
|
||||
const unevalTree = DataTreeFactory.create(
|
||||
{
|
||||
actions,
|
||||
widgets,
|
||||
widgetsMeta,
|
||||
pageList,
|
||||
appData,
|
||||
},
|
||||
true,
|
||||
);
|
||||
const unevalTree = DataTreeFactory.create({
|
||||
actions,
|
||||
widgets,
|
||||
widgetsMeta,
|
||||
pageList,
|
||||
appData,
|
||||
});
|
||||
PerformanceTracker.stopTracking();
|
||||
return unevalTree;
|
||||
},
|
||||
);
|
||||
|
||||
export const evaluateDataTree = createSelector(
|
||||
getUnevaluatedDataTree,
|
||||
(dataTree: DataTree): DataTree => {
|
||||
PerformanceTracker.startTracking(
|
||||
PerformanceTransactionName.DATA_TREE_EVALUATION,
|
||||
);
|
||||
const evalDataTree = getEvaluatedDataTree(dataTree);
|
||||
PerformanceTracker.stopTracking();
|
||||
return evalDataTree;
|
||||
},
|
||||
);
|
||||
|
||||
export const evaluateDataTreeWithFunctions = evaluateDataTree;
|
||||
export const evaluateDataTreeWithoutFunctions = evaluateDataTree;
|
||||
export const getDataTree = (state: AppState) => state.evaluations.tree;
|
||||
|
||||
// For autocomplete. Use actions cached responses if
|
||||
// there isn't a response already
|
||||
export const getDataTreeForAutocomplete = createSelector(
|
||||
evaluateDataTreeWithoutFunctions,
|
||||
getDataTree,
|
||||
getActionsForCurrentPage,
|
||||
(tree: DataTree, actions: ActionDataState) => {
|
||||
log.debug("Evaluating data tree to get autocomplete values");
|
||||
const cachedResponses: Record<string, any> = {};
|
||||
if (actions && actions.length) {
|
||||
actions.forEach(action => {
|
||||
|
|
|
|||
|
|
@ -2,27 +2,33 @@ import { createSelector } from "reselect";
|
|||
|
||||
import { AppState } from "reducers";
|
||||
import { WidgetConfigReducerState } from "reducers/entityReducers/widgetConfigReducer";
|
||||
import { WidgetCardProps, WidgetProps } from "widgets/BaseWidget";
|
||||
import {
|
||||
WIDGET_STATIC_PROPS,
|
||||
WidgetCardProps,
|
||||
WidgetProps,
|
||||
} from "widgets/BaseWidget";
|
||||
import { WidgetSidebarReduxState } from "reducers/uiReducers/widgetSidebarReducer";
|
||||
import CanvasWidgetsNormalizer from "normalizers/CanvasWidgetsNormalizer";
|
||||
import { getEntities } from "./entitiesSelector";
|
||||
import {
|
||||
FlattenedWidgetProps,
|
||||
CanvasWidgetsReduxState,
|
||||
FlattenedWidgetProps,
|
||||
} from "reducers/entityReducers/canvasWidgetsReducer";
|
||||
import { PageListReduxState } from "reducers/entityReducers/pageListReducer";
|
||||
|
||||
import { OccupiedSpace } from "constants/editorConstants";
|
||||
import { evaluateDataTreeWithoutFunctions } from "selectors/dataTreeSelectors";
|
||||
import { getDataTree } from "selectors/dataTreeSelectors";
|
||||
import _ from "lodash";
|
||||
import { ContainerWidgetProps } from "widgets/ContainerWidget";
|
||||
import { DataTreeWidget } from "entities/DataTree/dataTreeFactory";
|
||||
import { getActions } from "sagas/selectors";
|
||||
import { DataTreeWidget, ENTITY_TYPE } from "entities/DataTree/dataTreeFactory";
|
||||
import { getActions, getWidgetsMeta } from "sagas/selectors";
|
||||
|
||||
import * as log from "loglevel";
|
||||
import PerformanceTracker, {
|
||||
PerformanceTransactionName,
|
||||
} from "utils/PerformanceTracker";
|
||||
import { getCanvasWidgets } from "./entitiesSelector";
|
||||
import { MetaState } from "../reducers/entityReducers/metaReducer";
|
||||
import { WidgetTypes } from "../constants/WidgetConstants";
|
||||
|
||||
const getWidgetConfigs = (state: AppState) => state.entities.widgetConfig;
|
||||
const getWidgetSideBar = (state: AppState) => state.ui.widgetSidebar;
|
||||
|
|
@ -104,26 +110,28 @@ export const getWidgetCards = createSelector(
|
|||
);
|
||||
|
||||
export const getCanvasWidgetDsl = createSelector(
|
||||
getEntities,
|
||||
evaluateDataTreeWithoutFunctions,
|
||||
getCanvasWidgets,
|
||||
getDataTree,
|
||||
(
|
||||
entities: AppState["entities"],
|
||||
canvasWidgets: CanvasWidgetsReduxState,
|
||||
evaluatedDataTree,
|
||||
): ContainerWidgetProps<WidgetProps> => {
|
||||
PerformanceTracker.startTracking(
|
||||
PerformanceTransactionName.CONSTRUCT_CANVAS_DSL,
|
||||
);
|
||||
log.debug("Evaluating data tree to get canvas widgets");
|
||||
log.debug({ evaluatedDataTree });
|
||||
const widgets = { ...entities.canvasWidgets };
|
||||
Object.keys(widgets).forEach(widgetKey => {
|
||||
const evaluatedWidget = _.find(evaluatedDataTree, {
|
||||
widgetId: widgetKey,
|
||||
});
|
||||
const widgets: Record<string, DataTreeWidget> = {};
|
||||
Object.keys(canvasWidgets).forEach(widgetKey => {
|
||||
const canvasWidget = canvasWidgets[widgetKey];
|
||||
const evaluatedWidget = evaluatedDataTree[
|
||||
canvasWidget.widgetName
|
||||
] as DataTreeWidget;
|
||||
if (evaluatedWidget) {
|
||||
widgets[widgetKey] = evaluatedWidget as DataTreeWidget;
|
||||
widgets[widgetKey] = createCanvasWidget(canvasWidget, evaluatedWidget);
|
||||
} else {
|
||||
widgets[widgetKey] = createLoadingWidget(canvasWidget);
|
||||
}
|
||||
});
|
||||
|
||||
const denormalizedWidgets = CanvasWidgetsNormalizer.denormalize("0", {
|
||||
canvasWidgets: widgets,
|
||||
});
|
||||
|
|
@ -197,3 +205,32 @@ export const getActionById = createSelector(
|
|||
}
|
||||
},
|
||||
);
|
||||
|
||||
const createCanvasWidget = (
|
||||
canvasWidget: FlattenedWidgetProps,
|
||||
evaluatedWidget: DataTreeWidget,
|
||||
) => {
|
||||
const widgetStaticProps = _.pick(
|
||||
canvasWidget,
|
||||
Object.keys(WIDGET_STATIC_PROPS),
|
||||
);
|
||||
return {
|
||||
...evaluatedWidget,
|
||||
...widgetStaticProps,
|
||||
};
|
||||
};
|
||||
|
||||
const createLoadingWidget = (
|
||||
canvasWidget: FlattenedWidgetProps,
|
||||
): DataTreeWidget => {
|
||||
const widgetStaticProps = _.pick(
|
||||
canvasWidget,
|
||||
Object.keys(WIDGET_STATIC_PROPS),
|
||||
) as WidgetProps;
|
||||
return {
|
||||
...widgetStaticProps,
|
||||
type: WidgetTypes.SKELETON_WIDGET,
|
||||
ENTITY_TYPE: ENTITY_TYPE.WIDGET,
|
||||
isLoading: true,
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { Datasource } from "api/DatasourcesApi";
|
|||
import { Action } from "entities/Action";
|
||||
import { find } from "lodash";
|
||||
import ImageAlt from "assets/images/placeholder-image.svg";
|
||||
import { CanvasWidgetsReduxState } from "../reducers/entityReducers/canvasWidgetsReducer";
|
||||
|
||||
export const getEntities = (state: AppState): AppState["entities"] =>
|
||||
state.entities;
|
||||
|
|
@ -267,3 +268,6 @@ export const isActionDirty = (id: string) =>
|
|||
});
|
||||
|
||||
export const getAppData = (state: AppState) => state.entities.app;
|
||||
|
||||
export const getCanvasWidgets = (state: AppState): CanvasWidgetsReduxState =>
|
||||
state.entities.canvasWidgets;
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
import { createSelector } from "reselect";
|
||||
import { AppState } from "reducers";
|
||||
import { PropertyPaneReduxState } from "reducers/uiReducers/propertyPaneReducer";
|
||||
import { PropertyPaneConfigState } from "reducers/entityReducers/propertyPaneConfigReducer";
|
||||
import {
|
||||
PropertyPaneConfigState,
|
||||
PropertySection,
|
||||
} from "reducers/entityReducers/propertyPaneConfigReducer";
|
||||
import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer";
|
||||
import { PropertySection } from "reducers/entityReducers/propertyPaneConfigReducer";
|
||||
import { WidgetProps } from "widgets/BaseWidget";
|
||||
import { DataTree, DataTreeWidget } from "entities/DataTree/dataTreeFactory";
|
||||
import _ from "lodash";
|
||||
import { evaluateDataTreeWithoutFunctions } from "selectors/dataTreeSelectors";
|
||||
import { getDataTree } from "selectors/dataTreeSelectors";
|
||||
import * as log from "loglevel";
|
||||
import { getCanvasWidgets } from "./entitiesSelector";
|
||||
|
||||
const getPropertyPaneState = (state: AppState): PropertyPaneReduxState =>
|
||||
state.ui.propertyPane;
|
||||
|
|
@ -16,9 +19,6 @@ const getPropertyPaneState = (state: AppState): PropertyPaneReduxState =>
|
|||
const getPropertyPaneConfig = (state: AppState): PropertyPaneConfigState =>
|
||||
state.entities.propertyConfig;
|
||||
|
||||
const getCanvasWidgets = (state: AppState): CanvasWidgetsReduxState =>
|
||||
state.entities.canvasWidgets;
|
||||
|
||||
export const getCurrentWidgetId = createSelector(
|
||||
getPropertyPaneState,
|
||||
(propertyPane: PropertyPaneReduxState) => propertyPane.widgetId,
|
||||
|
|
@ -37,12 +37,11 @@ export const getCurrentWidgetProperties = createSelector(
|
|||
|
||||
export const getWidgetPropsForPropertyPane = createSelector(
|
||||
getCurrentWidgetProperties,
|
||||
evaluateDataTreeWithoutFunctions,
|
||||
getDataTree,
|
||||
(
|
||||
widget: WidgetProps | undefined,
|
||||
evaluatedTree: DataTree,
|
||||
): WidgetProps | undefined => {
|
||||
log.debug("Evaluating data tree to get property pane validations");
|
||||
if (!widget) return undefined;
|
||||
const evaluatedWidget = _.find(evaluatedTree, {
|
||||
widgetId: widget.widgetId,
|
||||
|
|
|
|||
|
|
@ -1,28 +1,11 @@
|
|||
import _ from "lodash";
|
||||
import _, { VERSION as lodashVersion } from "lodash";
|
||||
import {
|
||||
DATA_BIND_REGEX,
|
||||
DATA_BIND_REGEX_GLOBAL,
|
||||
} from "constants/BindingsConstants";
|
||||
import ValidationFactory from "./ValidationFactory";
|
||||
import JSExecutionManagerSingleton, {
|
||||
JSExecutorResult,
|
||||
} from "jsExecution/JSExecutionManagerSingleton";
|
||||
import unescapeJS from "unescape-js";
|
||||
import toposort from "toposort";
|
||||
import {
|
||||
DataTree,
|
||||
DataTreeEntity,
|
||||
DataTreeWidget,
|
||||
ENTITY_TYPE,
|
||||
} from "entities/DataTree/dataTreeFactory";
|
||||
import equal from "fast-deep-equal/es6";
|
||||
import WidgetFactory from "utils/WidgetFactory";
|
||||
import { AppToaster } from "components/editorComponents/ToastComponent";
|
||||
import { ToastType } from "react-toastify";
|
||||
import { Action } from "entities/Action";
|
||||
import PerformanceTracker, {
|
||||
PerformanceTransactionName,
|
||||
} from "utils/PerformanceTracker";
|
||||
import moment from "moment-timezone";
|
||||
import { atob, btoa, version as BASE64LIBVERSION } from "js-base64";
|
||||
|
||||
type StringTuple = [string, string];
|
||||
|
||||
|
|
@ -77,27 +60,6 @@ export function getDynamicStringSegments(dynamicString: string): string[] {
|
|||
return stringSegments;
|
||||
}
|
||||
|
||||
const getAllPaths = (
|
||||
tree: Record<string, any>,
|
||||
prefix = "",
|
||||
): Record<string, true> => {
|
||||
return Object.keys(tree).reduce((res: Record<string, true>, el): Record<
|
||||
string,
|
||||
true
|
||||
> => {
|
||||
if (Array.isArray(tree[el])) {
|
||||
const key = `${prefix}${el}`;
|
||||
return { ...res, [key]: true };
|
||||
} else if (typeof tree[el] === "object" && tree[el] !== null) {
|
||||
const key = `${prefix}${el}`;
|
||||
return { ...res, [key]: true, ...getAllPaths(tree[el], `${key}.`) };
|
||||
} else {
|
||||
const key = `${prefix}${el}`;
|
||||
return { ...res, [key]: true };
|
||||
}
|
||||
}, {});
|
||||
};
|
||||
|
||||
export const getDynamicBindings = (
|
||||
dynamicString: string,
|
||||
): { stringSegments: string[]; jsSnippets: string[] } => {
|
||||
|
|
@ -120,671 +82,64 @@ export const getDynamicBindings = (
|
|||
return { stringSegments: stringSegments, jsSnippets: paths };
|
||||
};
|
||||
|
||||
// Paths are expected to have "{name}.{path}" signature
|
||||
// Also returns any action triggers found after evaluating value
|
||||
export const evaluateDynamicBoundValue = (
|
||||
data: DataTree,
|
||||
path: string,
|
||||
callbackData?: any,
|
||||
): JSExecutorResult => {
|
||||
const unescapedJS = unescapeJS(path).replace(/(\r\n|\n|\r)/gm, "");
|
||||
return JSExecutionManagerSingleton.evaluateSync(
|
||||
unescapedJS,
|
||||
data,
|
||||
callbackData,
|
||||
);
|
||||
};
|
||||
|
||||
// For creating a final value where bindings could be in a template format
|
||||
export const createDynamicValueString = (
|
||||
binding: string,
|
||||
subBindings: string[],
|
||||
subValues: string[],
|
||||
): string => {
|
||||
// Replace the string with the data tree values
|
||||
let finalValue = binding;
|
||||
subBindings.forEach((b, i) => {
|
||||
let value = subValues[i];
|
||||
if (Array.isArray(value) || _.isObject(value)) {
|
||||
value = JSON.stringify(value);
|
||||
}
|
||||
try {
|
||||
if (JSON.parse(value)) {
|
||||
value = value.replace(/\\([\s\S])|(")/g, "\\$1$2");
|
||||
}
|
||||
} catch (e) {
|
||||
// do nothing
|
||||
}
|
||||
finalValue = finalValue.replace(b, value);
|
||||
});
|
||||
return finalValue;
|
||||
};
|
||||
|
||||
export const getDynamicValue = (
|
||||
dynamicBinding: string,
|
||||
data: DataTree,
|
||||
callBackData?: any,
|
||||
includeTriggers = false,
|
||||
): JSExecutorResult => {
|
||||
// Get the {{binding}} bound values
|
||||
const { stringSegments, jsSnippets } = getDynamicBindings(dynamicBinding);
|
||||
if (stringSegments.length) {
|
||||
// Get the Data Tree value of those "binding "paths
|
||||
const values = jsSnippets.map((jsSnippet, index) => {
|
||||
if (jsSnippet) {
|
||||
const result = evaluateDynamicBoundValue(data, jsSnippet, callBackData);
|
||||
if (includeTriggers) {
|
||||
return result;
|
||||
} else {
|
||||
return { result: result.result };
|
||||
}
|
||||
} else {
|
||||
return { result: stringSegments[index], triggers: [] };
|
||||
}
|
||||
});
|
||||
|
||||
// if it is just one binding, no need to create template string
|
||||
if (stringSegments.length === 1) return values[0];
|
||||
// else return a string template with bindings
|
||||
const templateString = createDynamicValueString(
|
||||
dynamicBinding,
|
||||
stringSegments,
|
||||
values.map(v => v.result),
|
||||
);
|
||||
return {
|
||||
result: templateString,
|
||||
};
|
||||
}
|
||||
return { result: undefined, triggers: [] };
|
||||
};
|
||||
|
||||
export const getValidatedTree = (tree: any) => {
|
||||
return Object.keys(tree).reduce((tree, entityKey: string) => {
|
||||
const entity = tree[entityKey];
|
||||
if (entity && entity.type) {
|
||||
const parsedEntity = { ...entity };
|
||||
Object.keys(entity).forEach((property: string) => {
|
||||
const hasEvaluatedValue = _.has(
|
||||
parsedEntity,
|
||||
`evaluatedValues.${property}`,
|
||||
);
|
||||
const hasValidation = _.has(parsedEntity, `invalidProps.${property}`);
|
||||
const isSpecialField = [
|
||||
"dynamicBindings",
|
||||
"dynamicTriggers",
|
||||
"dynamicProperties",
|
||||
"evaluatedValues",
|
||||
"invalidProps",
|
||||
"validationMessages",
|
||||
].includes(property);
|
||||
const isDynamicField =
|
||||
_.has(parsedEntity, `dynamicBindings.${property}`) ||
|
||||
_.has(parsedEntity, `dynamicTriggers.${property}`);
|
||||
|
||||
if (
|
||||
!isSpecialField &&
|
||||
!isDynamicField &&
|
||||
(!hasValidation || !hasEvaluatedValue)
|
||||
) {
|
||||
const value = entity[property];
|
||||
// Pass it through parse
|
||||
const {
|
||||
parsed,
|
||||
isValid,
|
||||
message,
|
||||
transformed,
|
||||
} = ValidationFactory.validateWidgetProperty(
|
||||
entity.type,
|
||||
property,
|
||||
value,
|
||||
entity,
|
||||
tree,
|
||||
);
|
||||
parsedEntity[property] = parsed;
|
||||
if (!hasEvaluatedValue) {
|
||||
const evaluatedValue = isValid
|
||||
? parsed
|
||||
: _.isUndefined(transformed)
|
||||
? value
|
||||
: transformed;
|
||||
const safeEvaluatedValue = removeFunctions(evaluatedValue);
|
||||
_.set(
|
||||
parsedEntity,
|
||||
`evaluatedValues.${property}`,
|
||||
safeEvaluatedValue,
|
||||
);
|
||||
}
|
||||
|
||||
const hasValidation = _.has(parsedEntity, `invalidProps.${property}`);
|
||||
if (!hasValidation && !isValid) {
|
||||
_.set(parsedEntity, `invalidProps.${property}`, true);
|
||||
_.set(parsedEntity, `validationMessages.${property}`, message);
|
||||
}
|
||||
}
|
||||
});
|
||||
return { ...tree, [entityKey]: parsedEntity };
|
||||
}
|
||||
return tree;
|
||||
}, tree);
|
||||
};
|
||||
|
||||
let dependencyTreeCache: any = {};
|
||||
let cachedDataTreeString = "";
|
||||
|
||||
export function getEvaluatedDataTree(dataTree: DataTree): DataTree {
|
||||
// Create Dependencies DAG
|
||||
const dataTreeString = JSON.stringify(dataTree);
|
||||
// Stringify before doing a fast equals because the data tree has functions and fast equal will always treat those as changed values
|
||||
// Better solve will be to prune functions
|
||||
const shouldCreateDependencyTree = !equal(
|
||||
dataTreeString,
|
||||
cachedDataTreeString,
|
||||
);
|
||||
PerformanceTracker.startTracking(
|
||||
PerformanceTransactionName.CREATE_DEPENDENCIES,
|
||||
{ isCacheMiss: shouldCreateDependencyTree },
|
||||
);
|
||||
if (shouldCreateDependencyTree) {
|
||||
cachedDataTreeString = dataTreeString;
|
||||
dependencyTreeCache = createDependencyTree(dataTree);
|
||||
}
|
||||
const {
|
||||
dependencyMap,
|
||||
sortedDependencies,
|
||||
dependencyTree,
|
||||
} = dependencyTreeCache;
|
||||
PerformanceTracker.stopTracking();
|
||||
// Evaluate Tree
|
||||
PerformanceTracker.startTracking(
|
||||
PerformanceTransactionName.SORTED_DEPENDENCY_EVALUATION,
|
||||
{
|
||||
dependencies: sortedDependencies,
|
||||
dependencyCount: sortedDependencies.length,
|
||||
dataTreeSize: cachedDataTreeString.length,
|
||||
},
|
||||
);
|
||||
const evaluatedTree = dependencySortedEvaluateDataTree(
|
||||
dataTree,
|
||||
dependencyMap,
|
||||
sortedDependencies,
|
||||
);
|
||||
PerformanceTracker.stopTracking();
|
||||
|
||||
// Set Loading Widgets
|
||||
PerformanceTracker.startTracking(
|
||||
PerformanceTransactionName.SET_WIDGET_LOADING,
|
||||
);
|
||||
const treeWithLoading = setTreeLoading(evaluatedTree, dependencyTree);
|
||||
PerformanceTracker.stopTracking();
|
||||
|
||||
// Validate Widgets
|
||||
PerformanceTracker.startTracking(
|
||||
PerformanceTransactionName.VALIDATE_DATA_TREE,
|
||||
);
|
||||
const validated = getValidatedTree(treeWithLoading);
|
||||
PerformanceTracker.stopTracking();
|
||||
// dataTreeCache = validated;
|
||||
return validated;
|
||||
export enum EvalErrorTypes {
|
||||
DEPENDENCY_ERROR = "DEPENDENCY_ERROR",
|
||||
EVAL_PROPERTY_ERROR = "EVAL_PROPERTY_ERROR",
|
||||
EVAL_TREE_ERROR = "EVAL_TREE_ERROR",
|
||||
UNESCAPE_STRING_ERROR = "UNESCAPE_STRING_ERROR",
|
||||
EVAL_ERROR = "EVAL_ERROR",
|
||||
}
|
||||
|
||||
type DynamicDependencyMap = Record<string, Array<string>>;
|
||||
export const createDependencyTree = (
|
||||
dataTree: DataTree,
|
||||
): {
|
||||
sortedDependencies: Array<string>;
|
||||
dependencyTree: Array<StringTuple>;
|
||||
dependencyMap: DynamicDependencyMap;
|
||||
} => {
|
||||
const dependencyMap: DynamicDependencyMap = {};
|
||||
const allKeys = getAllPaths(dataTree);
|
||||
Object.keys(dataTree).forEach(entityKey => {
|
||||
const entity = dataTree[entityKey];
|
||||
if (entity && "ENTITY_TYPE" in entity) {
|
||||
if (entity.ENTITY_TYPE === ENTITY_TYPE.WIDGET) {
|
||||
// Set default property dependency
|
||||
const defaultProperties = WidgetFactory.getWidgetDefaultPropertiesMap(
|
||||
entity.type,
|
||||
);
|
||||
Object.keys(defaultProperties).forEach(property => {
|
||||
dependencyMap[`${entityKey}.${property}`] = [
|
||||
`${entityKey}.${defaultProperties[property]}`,
|
||||
];
|
||||
});
|
||||
if (entity.dynamicBindings) {
|
||||
Object.keys(entity.dynamicBindings).forEach(propertyName => {
|
||||
// using unescape to remove new lines from bindings which interfere with our regex extraction
|
||||
const unevalPropValue = _.get(entity, propertyName);
|
||||
const { jsSnippets } = getDynamicBindings(unevalPropValue);
|
||||
const existingDeps =
|
||||
dependencyMap[`${entityKey}.${propertyName}`] || [];
|
||||
dependencyMap[`${entityKey}.${propertyName}`] = existingDeps.concat(
|
||||
jsSnippets.filter(jsSnippet => !!jsSnippet),
|
||||
);
|
||||
});
|
||||
}
|
||||
if (entity.dynamicTriggers) {
|
||||
Object.keys(entity.dynamicTriggers).forEach(prop => {
|
||||
dependencyMap[`${entityKey}.${prop}`] = [];
|
||||
});
|
||||
}
|
||||
}
|
||||
if (entity.ENTITY_TYPE === ENTITY_TYPE.ACTION) {
|
||||
if (entity.dynamicBindingPathList.length) {
|
||||
entity.dynamicBindingPathList.forEach(prop => {
|
||||
// using unescape to remove new lines from bindings which interfere with our regex extraction
|
||||
const unevalPropValue = _.get(entity, prop.key);
|
||||
const { jsSnippets } = getDynamicBindings(unevalPropValue);
|
||||
const existingDeps =
|
||||
dependencyMap[`${entityKey}.${prop.key}`] || [];
|
||||
dependencyMap[`${entityKey}.${prop.key}`] = existingDeps.concat(
|
||||
jsSnippets.filter(jsSnippet => !!jsSnippet),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
Object.keys(dependencyMap).forEach(key => {
|
||||
dependencyMap[key] = _.flatten(
|
||||
dependencyMap[key].map(path => calculateSubDependencies(path, allKeys)),
|
||||
);
|
||||
});
|
||||
const dependencyTree: Array<StringTuple> = [];
|
||||
Object.keys(dependencyMap).forEach((key: string) => {
|
||||
if (dependencyMap[key].length) {
|
||||
dependencyMap[key].forEach(dep => dependencyTree.push([key, dep]));
|
||||
} else {
|
||||
// Set no dependency
|
||||
dependencyTree.push([key, ""]);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
// sort dependencies and remove empty dependencies
|
||||
const sortedDependencies = toposort(dependencyTree)
|
||||
.reverse()
|
||||
.filter(d => !!d);
|
||||
|
||||
return { sortedDependencies, dependencyMap, dependencyTree };
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
AppToaster.show({
|
||||
message: e.message,
|
||||
type: ToastType.ERROR,
|
||||
});
|
||||
return { sortedDependencies: [], dependencyMap: {}, dependencyTree: [] };
|
||||
}
|
||||
export type EvalError = {
|
||||
type: EvalErrorTypes;
|
||||
message: string;
|
||||
context?: Record<string, any>;
|
||||
};
|
||||
|
||||
const calculateSubDependencies = (
|
||||
path: string,
|
||||
all: Record<string, true>,
|
||||
): Array<string> => {
|
||||
const subDeps: Array<string> = [];
|
||||
const identifiers = path.match(/[a-zA-Z_$][a-zA-Z_$0-9.]*/g) || [path];
|
||||
identifiers.forEach((identifier: string) => {
|
||||
if (all.hasOwnProperty(identifier)) {
|
||||
subDeps.push(identifier);
|
||||
} else {
|
||||
const subIdentifiers =
|
||||
identifier.match(/[a-zA-Z_$][a-zA-Z_$0-9]*/g) || [];
|
||||
let current = "";
|
||||
for (let i = 0; i < subIdentifiers.length; i++) {
|
||||
const key = `${current}${current ? "." : ""}${subIdentifiers[i]}`;
|
||||
if (key in all) {
|
||||
current = key;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (current && current.includes(".")) subDeps.push(current);
|
||||
}
|
||||
});
|
||||
return _.uniq(subDeps);
|
||||
export enum EVAL_WORKER_ACTIONS {
|
||||
EVAL_TREE = "EVAL_TREE",
|
||||
EVAL_SINGLE = "EVAL_SINGLE",
|
||||
EVAL_TRIGGER = "EVAL_TRIGGER",
|
||||
CLEAR_PROPERTY_CACHE = "CLEAR_PROPERTY_CACHE",
|
||||
CLEAR_CACHE = "CLEAR_CACHE",
|
||||
VALIDATE_PROPERTY = "VALIDATE_PROPERTY",
|
||||
}
|
||||
|
||||
export type ExtraLibrary = {
|
||||
version: string;
|
||||
docsURL: string;
|
||||
displayName: string;
|
||||
accessor: string;
|
||||
lib: any;
|
||||
};
|
||||
|
||||
export const setTreeLoading = (
|
||||
dataTree: DataTree,
|
||||
dependencyMap: Array<StringTuple>,
|
||||
) => {
|
||||
const widgets: string[] = [];
|
||||
const isLoadingActions: string[] = [];
|
||||
|
||||
// Fetch all actions that are in loading state
|
||||
Object.keys(dataTree).forEach(e => {
|
||||
const entity = dataTree[e];
|
||||
if (entity && "ENTITY_TYPE" in entity) {
|
||||
if (entity.ENTITY_TYPE === ENTITY_TYPE.WIDGET) {
|
||||
widgets.push(e);
|
||||
} else if (
|
||||
entity.ENTITY_TYPE === ENTITY_TYPE.ACTION &&
|
||||
entity.isLoading
|
||||
) {
|
||||
isLoadingActions.push(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// get all widget dependencies of those actions
|
||||
isLoadingActions
|
||||
.reduce(
|
||||
(allEntities: string[], curr) =>
|
||||
allEntities.concat(getEntityDependencies(dependencyMap, curr, widgets)),
|
||||
[],
|
||||
)
|
||||
// set loading to true for those widgets
|
||||
.forEach(w => {
|
||||
const entity = dataTree[w] as DataTreeWidget;
|
||||
entity.isLoading = true;
|
||||
});
|
||||
return dataTree;
|
||||
};
|
||||
|
||||
export const getEntityDependencies = (
|
||||
dependencyMap: Array<StringTuple>,
|
||||
entity: string,
|
||||
entities: string[],
|
||||
): Array<string> => {
|
||||
const entityDeps: Record<string, string[]> = dependencyMap
|
||||
.map(d => [d[1].split(".")[0], d[0].split(".")[0]])
|
||||
.filter(d => d[0] !== d[1])
|
||||
.reduce((deps: Record<string, string[]>, dep) => {
|
||||
const key: string = dep[0];
|
||||
const value: string = dep[1];
|
||||
return {
|
||||
...deps,
|
||||
[key]: deps[key] ? deps[key].concat(value) : [value],
|
||||
};
|
||||
}, {});
|
||||
|
||||
if (entity in entityDeps) {
|
||||
const recFind = (
|
||||
keys: Array<string>,
|
||||
deps: Record<string, string[]>,
|
||||
): Array<string> => {
|
||||
let allDeps: string[] = [];
|
||||
keys
|
||||
.filter(k => entities.includes(k))
|
||||
.forEach(e => {
|
||||
allDeps = allDeps.concat([e]);
|
||||
if (e in deps) {
|
||||
allDeps = allDeps.concat([...recFind(deps[e], deps)]);
|
||||
}
|
||||
});
|
||||
return allDeps;
|
||||
};
|
||||
return recFind(entityDeps[entity], entityDeps);
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
const dynamicPropValueCache: Map<
|
||||
string,
|
||||
export const extraLibraries: ExtraLibrary[] = [
|
||||
{
|
||||
unEvaluated: any;
|
||||
evaluated: any;
|
||||
}
|
||||
> = new Map();
|
||||
|
||||
const parsedValueCache: Map<
|
||||
string,
|
||||
accessor: "_",
|
||||
lib: _,
|
||||
version: lodashVersion,
|
||||
docsURL: `https://lodash.com/docs/${lodashVersion}`,
|
||||
displayName: "lodash",
|
||||
},
|
||||
{
|
||||
value: any;
|
||||
version: number;
|
||||
}
|
||||
> = new Map();
|
||||
|
||||
const getDynamicPropValueCache = (propertyPath: string) =>
|
||||
dynamicPropValueCache.get(propertyPath);
|
||||
|
||||
const getParsedValueCache = (propertyPath: string) =>
|
||||
parsedValueCache.get(propertyPath) || {
|
||||
value: undefined,
|
||||
version: 0,
|
||||
};
|
||||
|
||||
export const clearPropertyCache = (propertyPath: string) =>
|
||||
parsedValueCache.delete(propertyPath);
|
||||
|
||||
const dependencyCache: Map<string, any[]> = new Map();
|
||||
|
||||
export const clearCaches = () => {
|
||||
dynamicPropValueCache.clear();
|
||||
dependencyCache.clear();
|
||||
parsedValueCache.clear();
|
||||
};
|
||||
|
||||
function getCurrentDependencyValues(
|
||||
propertyDependencies: Array<string>,
|
||||
currentTree: DataTree,
|
||||
currentPropertyPath: string,
|
||||
): Array<string> {
|
||||
return propertyDependencies
|
||||
? propertyDependencies
|
||||
.map((path: string) => {
|
||||
//*** Remove current path from data tree because cached value contains evaluated version while this contains unevaluated version */
|
||||
const cleanDataTree = _.omit(currentTree, [currentPropertyPath]);
|
||||
return _.get(cleanDataTree, path);
|
||||
})
|
||||
.filter((data: any) => {
|
||||
return data !== undefined;
|
||||
})
|
||||
: [];
|
||||
}
|
||||
|
||||
function evaluateDynamicProperty(
|
||||
propertyPath: string,
|
||||
currentTree: DataTree,
|
||||
unEvalPropertyValue: any,
|
||||
currentDependencyValues: Array<string>,
|
||||
cachedDependencyValues?: Array<string>,
|
||||
): any {
|
||||
const cacheObj = getDynamicPropValueCache(propertyPath);
|
||||
const isCacheHit =
|
||||
cacheObj &&
|
||||
equal(cacheObj.unEvaluated, unEvalPropertyValue) &&
|
||||
cachedDependencyValues !== undefined &&
|
||||
equal(currentDependencyValues, cachedDependencyValues);
|
||||
if (isCacheHit && cacheObj) {
|
||||
return cacheObj.evaluated;
|
||||
} else {
|
||||
const dynamicResult = getDynamicValue(unEvalPropertyValue, currentTree);
|
||||
dynamicPropValueCache.set(propertyPath, {
|
||||
evaluated: dynamicResult.result,
|
||||
unEvaluated: unEvalPropertyValue,
|
||||
});
|
||||
dependencyCache.set(propertyPath, currentDependencyValues);
|
||||
return dynamicResult.result;
|
||||
}
|
||||
}
|
||||
|
||||
function validateAndParseWidgetProperty(
|
||||
propertyPath: string,
|
||||
widget: DataTreeWidget,
|
||||
currentTree: DataTree,
|
||||
evalPropertyValue: any,
|
||||
unEvalPropertyValue: string,
|
||||
currentDependencyValues: Array<string>,
|
||||
cachedDependencyValues?: Array<string>,
|
||||
): any {
|
||||
const propertyName = propertyPath.split(".")[1];
|
||||
let valueToValidate = evalPropertyValue;
|
||||
if (widget.dynamicTriggers && propertyName in widget.dynamicTriggers) {
|
||||
const { triggers } = getDynamicValue(
|
||||
unEvalPropertyValue,
|
||||
currentTree,
|
||||
undefined,
|
||||
true,
|
||||
);
|
||||
valueToValidate = triggers;
|
||||
}
|
||||
const {
|
||||
parsed,
|
||||
isValid,
|
||||
message,
|
||||
transformed,
|
||||
} = ValidationFactory.validateWidgetProperty(
|
||||
widget.type,
|
||||
propertyName,
|
||||
valueToValidate,
|
||||
widget,
|
||||
currentTree,
|
||||
);
|
||||
const evaluatedValue = isValid
|
||||
? parsed
|
||||
: _.isUndefined(transformed)
|
||||
? evalPropertyValue
|
||||
: transformed;
|
||||
const safeEvaluatedValue = removeFunctions(evaluatedValue);
|
||||
_.set(widget, `evaluatedValues.${propertyName}`, safeEvaluatedValue);
|
||||
if (!isValid) {
|
||||
_.set(widget, `invalidProps.${propertyName}`, true);
|
||||
_.set(widget, `validationMessages.${propertyName}`, message);
|
||||
}
|
||||
if (widget.dynamicTriggers && propertyName in widget.dynamicTriggers) {
|
||||
return unEvalPropertyValue;
|
||||
} else {
|
||||
const parsedCache = getParsedValueCache(propertyPath);
|
||||
if (
|
||||
!equal(parsedCache.value, parsed) ||
|
||||
(cachedDependencyValues !== undefined &&
|
||||
!equal(currentDependencyValues, cachedDependencyValues))
|
||||
) {
|
||||
parsedValueCache.set(propertyPath, {
|
||||
value: parsed,
|
||||
version: Date.now(),
|
||||
});
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
|
||||
function isWidget(entity: DataTreeEntity): boolean {
|
||||
return "ENTITY_TYPE" in entity && entity.ENTITY_TYPE === ENTITY_TYPE.WIDGET;
|
||||
}
|
||||
|
||||
export function dependencySortedEvaluateDataTree(
|
||||
dataTree: DataTree,
|
||||
dependencyMap: DynamicDependencyMap,
|
||||
sortedDependencies: Array<string>,
|
||||
): DataTree {
|
||||
const tree = _.cloneDeep(dataTree);
|
||||
try {
|
||||
return sortedDependencies.reduce(
|
||||
(currentTree: DataTree, propertyPath: string) => {
|
||||
// PerformanceTracker.startTracking(PerformanceTransactionName.EVALUATE_BINDING, { binding: propertyPath }, true)
|
||||
const entityName = propertyPath.split(".")[0];
|
||||
const entity: DataTreeEntity = currentTree[entityName];
|
||||
const unEvalPropertyValue = _.get(currentTree as any, propertyPath);
|
||||
let evalPropertyValue;
|
||||
const propertyDependencies = dependencyMap[propertyPath];
|
||||
const currentDependencyValues = getCurrentDependencyValues(
|
||||
propertyDependencies,
|
||||
currentTree,
|
||||
propertyPath,
|
||||
);
|
||||
const cachedDependencyValues = dependencyCache.get(propertyPath);
|
||||
const requiresEval = isDynamicValue(unEvalPropertyValue);
|
||||
if (requiresEval) {
|
||||
try {
|
||||
evalPropertyValue = evaluateDynamicProperty(
|
||||
propertyPath,
|
||||
currentTree,
|
||||
unEvalPropertyValue,
|
||||
currentDependencyValues,
|
||||
cachedDependencyValues,
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
evalPropertyValue = undefined;
|
||||
}
|
||||
} else {
|
||||
evalPropertyValue = unEvalPropertyValue;
|
||||
// If we have stored any previous dependency cache, clear it
|
||||
// since it is no longer a binding
|
||||
if (cachedDependencyValues && cachedDependencyValues.length) {
|
||||
dependencyCache.set(propertyPath, []);
|
||||
}
|
||||
}
|
||||
if (isWidget(entity)) {
|
||||
const widgetEntity: DataTreeWidget = entity as DataTreeWidget;
|
||||
const propertyName = propertyPath.split(".")[1];
|
||||
if (propertyName) {
|
||||
let parsedValue = validateAndParseWidgetProperty(
|
||||
propertyPath,
|
||||
widgetEntity,
|
||||
currentTree,
|
||||
evalPropertyValue,
|
||||
unEvalPropertyValue,
|
||||
currentDependencyValues,
|
||||
cachedDependencyValues,
|
||||
);
|
||||
const defaultPropertyMap = WidgetFactory.getWidgetDefaultPropertiesMap(
|
||||
widgetEntity.type,
|
||||
);
|
||||
const hasDefaultProperty = propertyName in defaultPropertyMap;
|
||||
if (hasDefaultProperty) {
|
||||
const defaultProperty = defaultPropertyMap[propertyName];
|
||||
parsedValue = overwriteDefaultDependentProps(
|
||||
defaultProperty,
|
||||
parsedValue,
|
||||
propertyPath,
|
||||
widgetEntity,
|
||||
);
|
||||
}
|
||||
// PerformanceTracker.stopTracking();
|
||||
return _.set(currentTree, propertyPath, parsedValue);
|
||||
}
|
||||
// PerformanceTracker.stopTracking();
|
||||
return _.set(currentTree, propertyPath, evalPropertyValue);
|
||||
} else {
|
||||
// PerformanceTracker.stopTracking();
|
||||
return _.set(currentTree, propertyPath, evalPropertyValue);
|
||||
}
|
||||
},
|
||||
tree,
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return tree;
|
||||
}
|
||||
}
|
||||
|
||||
const overwriteDefaultDependentProps = (
|
||||
defaultProperty: string,
|
||||
propertyValue: any,
|
||||
propertyPath: string,
|
||||
entity: DataTreeWidget,
|
||||
) => {
|
||||
const defaultPropertyCache = getParsedValueCache(
|
||||
`${entity.widgetName}.${defaultProperty}`,
|
||||
);
|
||||
const propertyCache = getParsedValueCache(propertyPath);
|
||||
if (
|
||||
propertyValue === undefined ||
|
||||
propertyCache.version < defaultPropertyCache.version
|
||||
) {
|
||||
return defaultPropertyCache.value;
|
||||
}
|
||||
return propertyValue;
|
||||
};
|
||||
|
||||
// We need to remove functions from data tree to avoid any unexpected identifier while JSON parsing
|
||||
// Check issue https://github.com/appsmithorg/appsmith/issues/719
|
||||
const removeFunctions = (value: any) => {
|
||||
if (_.isFunction(value)) {
|
||||
return "Function call";
|
||||
} else if (_.isObject(value) && _.some(value, _.isFunction)) {
|
||||
return JSON.parse(JSON.stringify(value));
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
Need to evaluated values
|
||||
Need to validate widget values
|
||||
Need to replace with default values
|
||||
|
||||
*/
|
||||
accessor: "moment",
|
||||
lib: moment,
|
||||
version: moment.version,
|
||||
docsURL: `https://momentjs.com/docs/`,
|
||||
displayName: "moment",
|
||||
},
|
||||
{
|
||||
accessor: "btoa",
|
||||
lib: btoa,
|
||||
version: BASE64LIBVERSION,
|
||||
docsURL: "https://github.com/dankogai/js-base64#readme",
|
||||
displayName: "btoa",
|
||||
},
|
||||
{
|
||||
accessor: "atob",
|
||||
lib: atob,
|
||||
version: BASE64LIBVERSION,
|
||||
docsURL: "https://github.com/dankogai/js-base64#readme",
|
||||
displayName: "atob",
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
// // import RealmExecutor from "jsExecution/RealmExecutor";
|
||||
// import {
|
||||
// mockExecute,
|
||||
// mockRegisterLibrary,
|
||||
|
|
@ -12,12 +11,6 @@
|
|||
// import { DataTree, ENTITY_TYPE } from "entities/DataTree/dataTreeFactory";
|
||||
// import { RenderModes, WidgetTypes } from "constants/WidgetConstants";
|
||||
//
|
||||
// jest.mock("jsExecution/RealmExecutor", () => {
|
||||
// return jest.fn().mockImplementation(() => {
|
||||
// return { execute: mockExecute, registerLibrary: mockRegisterLibrary };
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// beforeAll(() => {
|
||||
// mockRegisterLibrary.mockClear();
|
||||
// mockExecute.mockClear();
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import WidgetBuilderRegistry from "./WidgetRegistry";
|
||||
import PropertyControlRegistry from "./PropertyControlRegistry";
|
||||
import ValidationRegistry from "./ValidationRegistry";
|
||||
|
||||
export const editorInitializer = async () => {
|
||||
WidgetBuilderRegistry.registerWidgetBuilders();
|
||||
PropertyControlRegistry.registerPropertyControlBuilders();
|
||||
ValidationRegistry.registerInternalValidators();
|
||||
|
||||
const moment = await import("moment-timezone");
|
||||
moment.tz.setDefault(moment.tz.guess());
|
||||
|
|
|
|||
|
|
@ -1,60 +0,0 @@
|
|||
import { WidgetType } from "constants/WidgetConstants";
|
||||
import WidgetFactory from "./WidgetFactory";
|
||||
import {
|
||||
VALIDATION_TYPES,
|
||||
ValidationResponse,
|
||||
ValidationType,
|
||||
Validator,
|
||||
} from "constants/WidgetValidation";
|
||||
import { WidgetProps } from "widgets/BaseWidget";
|
||||
import { DataTree } from "entities/DataTree/dataTreeFactory";
|
||||
|
||||
export const BASE_WIDGET_VALIDATION = {
|
||||
isLoading: VALIDATION_TYPES.BOOLEAN,
|
||||
isVisible: VALIDATION_TYPES.BOOLEAN,
|
||||
isDisabled: VALIDATION_TYPES.BOOLEAN,
|
||||
};
|
||||
|
||||
export type WidgetPropertyValidationType = Record<
|
||||
string,
|
||||
ValidationType | Validator
|
||||
>;
|
||||
|
||||
class ValidationFactory {
|
||||
static validationMap: Map<ValidationType, Validator> = new Map();
|
||||
|
||||
static registerValidator(
|
||||
validationType: ValidationType,
|
||||
validator: Validator,
|
||||
) {
|
||||
this.validationMap.set(validationType, validator);
|
||||
}
|
||||
|
||||
static validateWidgetProperty(
|
||||
widgetType: WidgetType,
|
||||
property: string,
|
||||
value: any,
|
||||
props: WidgetProps,
|
||||
dataTree?: DataTree,
|
||||
): ValidationResponse {
|
||||
// TODO WIDGETFACTORY
|
||||
const propertyValidationTypes = WidgetFactory.getWidgetPropertyValidationMap(
|
||||
widgetType,
|
||||
);
|
||||
const validationTypeOrValidator = propertyValidationTypes[property];
|
||||
let validator;
|
||||
|
||||
if (typeof validationTypeOrValidator === "function") {
|
||||
validator = validationTypeOrValidator;
|
||||
} else {
|
||||
validator = this.validationMap.get(validationTypeOrValidator);
|
||||
}
|
||||
if (validator) {
|
||||
return validator(value, props, dataTree);
|
||||
} else {
|
||||
return { isValid: true, parsed: value };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ValidationFactory;
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
import ValidationFactory from "./ValidationFactory";
|
||||
import { VALIDATION_TYPES } from "constants/WidgetValidation";
|
||||
import { VALIDATORS } from "./Validators";
|
||||
|
||||
class ValidationRegistry {
|
||||
static registerInternalValidators() {
|
||||
Object.keys(VALIDATION_TYPES).forEach(type => {
|
||||
ValidationFactory.registerValidator(type, VALIDATORS[type]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default ValidationRegistry;
|
||||
|
|
@ -1,523 +0,0 @@
|
|||
import _ from "lodash";
|
||||
import {
|
||||
ISO_DATE_FORMAT,
|
||||
VALIDATION_TYPES,
|
||||
ValidationResponse,
|
||||
ValidationType,
|
||||
Validator,
|
||||
} from "constants/WidgetValidation";
|
||||
import moment from "moment";
|
||||
import { WIDGET_TYPE_VALIDATION_ERROR } from "constants/messages";
|
||||
import { WidgetProps } from "widgets/BaseWidget";
|
||||
import { DataTree } from "entities/DataTree/dataTreeFactory";
|
||||
|
||||
export const VALIDATORS: Record<ValidationType, Validator> = {
|
||||
[VALIDATION_TYPES.TEXT]: (
|
||||
value: any,
|
||||
props: WidgetProps,
|
||||
dataTree?: DataTree,
|
||||
): ValidationResponse => {
|
||||
let parsed = value;
|
||||
if (_.isUndefined(value) || value === null) {
|
||||
return {
|
||||
isValid: true,
|
||||
parsed: value,
|
||||
message: "",
|
||||
};
|
||||
}
|
||||
if (_.isObject(value)) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: JSON.stringify(value, null, 2),
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: text`,
|
||||
};
|
||||
}
|
||||
let isValid = _.isString(value);
|
||||
if (!isValid) {
|
||||
try {
|
||||
parsed = _.toString(value);
|
||||
isValid = true;
|
||||
} catch (e) {
|
||||
console.error(`Error when parsing ${value} to string`);
|
||||
console.error(e);
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: "",
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: text`,
|
||||
};
|
||||
}
|
||||
}
|
||||
return { isValid, parsed };
|
||||
},
|
||||
[VALIDATION_TYPES.REGEX]: (
|
||||
value: any,
|
||||
props: WidgetProps,
|
||||
dataTree?: DataTree,
|
||||
): ValidationResponse => {
|
||||
const { isValid, parsed, message } = VALIDATORS[VALIDATION_TYPES.TEXT](
|
||||
value,
|
||||
props,
|
||||
dataTree,
|
||||
);
|
||||
|
||||
if (isValid) {
|
||||
try {
|
||||
new RegExp(parsed);
|
||||
} catch (e) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: parsed,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: regex`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return { isValid, parsed, message };
|
||||
},
|
||||
[VALIDATION_TYPES.NUMBER]: (
|
||||
value: any,
|
||||
props: WidgetProps,
|
||||
dataTree?: DataTree,
|
||||
): ValidationResponse => {
|
||||
let parsed = value;
|
||||
if (_.isUndefined(value)) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: 0,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: number`,
|
||||
};
|
||||
}
|
||||
let isValid = _.isNumber(value);
|
||||
if (!isValid) {
|
||||
try {
|
||||
parsed = _.toNumber(value);
|
||||
if (isNaN(parsed)) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: 0,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: number`,
|
||||
};
|
||||
}
|
||||
isValid = true;
|
||||
} catch (e) {
|
||||
console.error(`Error when parsing ${value} to number`);
|
||||
console.error(e);
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: 0,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: number`,
|
||||
};
|
||||
}
|
||||
}
|
||||
return { isValid, parsed };
|
||||
},
|
||||
[VALIDATION_TYPES.BOOLEAN]: (
|
||||
value: any,
|
||||
props: WidgetProps,
|
||||
dataTree?: DataTree,
|
||||
): ValidationResponse => {
|
||||
let parsed = value;
|
||||
if (_.isUndefined(value)) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: false,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: boolean`,
|
||||
};
|
||||
}
|
||||
const isBoolean = _.isBoolean(value);
|
||||
const isStringTrueFalse = value === "true" || value === "false";
|
||||
const isValid = isBoolean || isStringTrueFalse;
|
||||
if (isStringTrueFalse) parsed = value !== "false";
|
||||
if (!isValid) {
|
||||
return {
|
||||
isValid: isValid,
|
||||
parsed: parsed,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: boolean`,
|
||||
};
|
||||
}
|
||||
return { isValid, parsed };
|
||||
},
|
||||
[VALIDATION_TYPES.OBJECT]: (
|
||||
value: any,
|
||||
props: WidgetProps,
|
||||
dataTree?: DataTree,
|
||||
): ValidationResponse => {
|
||||
let parsed = value;
|
||||
if (_.isUndefined(value)) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: {},
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Object`,
|
||||
};
|
||||
}
|
||||
let isValid = _.isObject(value);
|
||||
if (!isValid) {
|
||||
try {
|
||||
parsed = JSON.parse(value);
|
||||
isValid = true;
|
||||
} catch (e) {
|
||||
console.error(`Error when parsing ${value} to object`);
|
||||
console.error(e);
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: {},
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Object`,
|
||||
};
|
||||
}
|
||||
}
|
||||
return { isValid, parsed };
|
||||
},
|
||||
[VALIDATION_TYPES.ARRAY]: (
|
||||
value: any,
|
||||
props: WidgetProps,
|
||||
dataTree?: DataTree,
|
||||
): ValidationResponse => {
|
||||
let parsed = value;
|
||||
try {
|
||||
if (_.isUndefined(value)) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: [],
|
||||
transformed: undefined,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Array/List`,
|
||||
};
|
||||
}
|
||||
if (_.isString(value)) {
|
||||
parsed = JSON.parse(parsed as string);
|
||||
}
|
||||
if (!Array.isArray(parsed)) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: [],
|
||||
transformed: parsed,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Array/List`,
|
||||
};
|
||||
}
|
||||
return { isValid: true, parsed, transformed: parsed };
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: [],
|
||||
transformed: parsed,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Array/List`,
|
||||
};
|
||||
}
|
||||
},
|
||||
[VALIDATION_TYPES.TABS_DATA]: (
|
||||
value: any,
|
||||
props: WidgetProps,
|
||||
dataTree?: DataTree,
|
||||
): ValidationResponse => {
|
||||
const { isValid, parsed } = VALIDATORS[VALIDATION_TYPES.ARRAY](
|
||||
value,
|
||||
props,
|
||||
dataTree,
|
||||
);
|
||||
if (!isValid) {
|
||||
return {
|
||||
isValid,
|
||||
parsed,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Tabs Data`,
|
||||
};
|
||||
} else if (!_.every(parsed, datum => _.isObject(datum))) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: [],
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Tabs Data`,
|
||||
};
|
||||
}
|
||||
return { isValid, parsed };
|
||||
},
|
||||
[VALIDATION_TYPES.TABLE_DATA]: (
|
||||
value: any,
|
||||
props: WidgetProps,
|
||||
dataTree?: DataTree,
|
||||
): ValidationResponse => {
|
||||
const { isValid, transformed, parsed } = VALIDATORS[VALIDATION_TYPES.ARRAY](
|
||||
value,
|
||||
props,
|
||||
dataTree,
|
||||
);
|
||||
if (!isValid) {
|
||||
return {
|
||||
isValid,
|
||||
parsed: [],
|
||||
transformed,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: [{ "Col1" : "val1", "Col2" : "val2" }]`,
|
||||
};
|
||||
}
|
||||
const isValidTableData = _.every(parsed, datum => {
|
||||
return (
|
||||
_.isObject(datum) &&
|
||||
Object.keys(datum).filter(key => _.isString(key) && key.length === 0)
|
||||
.length === 0
|
||||
);
|
||||
});
|
||||
if (!isValidTableData) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: [],
|
||||
transformed,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: [{ "Col1" : "val1", "Col2" : "val2" }]`,
|
||||
};
|
||||
}
|
||||
return { isValid, parsed };
|
||||
},
|
||||
[VALIDATION_TYPES.CHART_DATA]: (
|
||||
value: any,
|
||||
props: WidgetProps,
|
||||
dataTree?: DataTree,
|
||||
): ValidationResponse => {
|
||||
const { isValid, parsed } = VALIDATORS[VALIDATION_TYPES.ARRAY](
|
||||
value,
|
||||
props,
|
||||
dataTree,
|
||||
);
|
||||
if (!isValid) {
|
||||
return {
|
||||
isValid,
|
||||
parsed,
|
||||
transformed: parsed,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Chart Data`,
|
||||
};
|
||||
}
|
||||
let validationMessage = "";
|
||||
let index = 0;
|
||||
const isValidChartData = _.every(
|
||||
parsed,
|
||||
(datum: { name: string; data: any }) => {
|
||||
const validatedResponse: {
|
||||
isValid: boolean;
|
||||
parsed: Record<string, unknown>;
|
||||
message?: string;
|
||||
} = VALIDATORS[VALIDATION_TYPES.ARRAY](datum.data, props, dataTree);
|
||||
validationMessage = `${index}##${WIDGET_TYPE_VALIDATION_ERROR}: [{ "x": "val", "y": "val" }]`;
|
||||
let isValidChart = validatedResponse.isValid;
|
||||
if (validatedResponse.isValid) {
|
||||
datum.data = validatedResponse.parsed;
|
||||
isValidChart = _.every(
|
||||
datum.data,
|
||||
(chartPoint: { x: string; y: any }) => {
|
||||
return (
|
||||
_.isObject(chartPoint) &&
|
||||
_.isString(chartPoint.x) &&
|
||||
!_.isUndefined(chartPoint.y)
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
index++;
|
||||
return isValidChart;
|
||||
},
|
||||
);
|
||||
if (!isValidChartData) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: [],
|
||||
transformed: parsed,
|
||||
message: validationMessage,
|
||||
};
|
||||
}
|
||||
return { isValid, parsed, transformed: parsed };
|
||||
},
|
||||
[VALIDATION_TYPES.MARKERS]: (
|
||||
value: any,
|
||||
props: WidgetProps,
|
||||
dataTree?: DataTree,
|
||||
): ValidationResponse => {
|
||||
const { isValid, parsed } = VALIDATORS[VALIDATION_TYPES.ARRAY](
|
||||
value,
|
||||
props,
|
||||
dataTree,
|
||||
);
|
||||
if (!isValid) {
|
||||
return {
|
||||
isValid,
|
||||
parsed,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Marker Data`,
|
||||
};
|
||||
} else if (!_.every(parsed, datum => _.isObject(datum))) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: [],
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Marker Data`,
|
||||
};
|
||||
}
|
||||
return { isValid, parsed };
|
||||
},
|
||||
[VALIDATION_TYPES.OPTIONS_DATA]: (
|
||||
value: any,
|
||||
props: WidgetProps,
|
||||
dataTree?: DataTree,
|
||||
): ValidationResponse => {
|
||||
const { isValid, parsed } = VALIDATORS[VALIDATION_TYPES.ARRAY](
|
||||
value,
|
||||
props,
|
||||
dataTree,
|
||||
);
|
||||
if (!isValid) {
|
||||
return {
|
||||
isValid,
|
||||
parsed,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Options Data`,
|
||||
};
|
||||
}
|
||||
const isValidOption = (option: { label: any; value: any }) =>
|
||||
_.isString(option.label) &&
|
||||
_.isString(option.value) &&
|
||||
!_.isEmpty(option.label) &&
|
||||
!_.isEmpty(option.value);
|
||||
|
||||
const hasOptions = _.every(parsed, (datum: { label: any; value: any }) => {
|
||||
if (_.isObject(datum)) {
|
||||
return isValidOption(datum);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
const validOptions = parsed.filter(isValidOption);
|
||||
const uniqValidOptions = _.uniqBy(validOptions, "value");
|
||||
|
||||
if (!hasOptions || uniqValidOptions.length !== validOptions.length) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: uniqValidOptions,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Options Data`,
|
||||
};
|
||||
}
|
||||
return { isValid, parsed };
|
||||
},
|
||||
[VALIDATION_TYPES.DATE]: (
|
||||
dateString: string,
|
||||
props: WidgetProps,
|
||||
dataTree?: DataTree,
|
||||
): ValidationResponse => {
|
||||
const today = moment()
|
||||
.hour(0)
|
||||
.minute(0)
|
||||
.second(0)
|
||||
.millisecond(0);
|
||||
const dateFormat = props.dateFormat ? props.dateFormat : ISO_DATE_FORMAT;
|
||||
|
||||
const todayDateString = today.format(dateFormat);
|
||||
if (dateString === undefined) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: "",
|
||||
message:
|
||||
`${WIDGET_TYPE_VALIDATION_ERROR}: Date ` + props.dateFormat
|
||||
? props.dateFormat
|
||||
: "",
|
||||
};
|
||||
}
|
||||
const isValid = moment(dateString, dateFormat).isValid();
|
||||
const parsed = isValid ? dateString : todayDateString;
|
||||
return {
|
||||
isValid,
|
||||
parsed,
|
||||
message: isValid ? "" : `${WIDGET_TYPE_VALIDATION_ERROR}: Date`,
|
||||
};
|
||||
},
|
||||
[VALIDATION_TYPES.ACTION_SELECTOR]: (
|
||||
value: any,
|
||||
props: WidgetProps,
|
||||
dataTree?: DataTree,
|
||||
): ValidationResponse => {
|
||||
if (Array.isArray(value) && value.length) {
|
||||
return {
|
||||
isValid: true,
|
||||
parsed: undefined,
|
||||
transformed: "Function Call",
|
||||
};
|
||||
}
|
||||
/*
|
||||
if (_.isString(value)) {
|
||||
if (value.indexOf("navigateTo") !== -1) {
|
||||
const pageNameOrUrl = modalGetter(value);
|
||||
if (dataTree) {
|
||||
if (isDynamicValue(pageNameOrUrl)) {
|
||||
return {
|
||||
isValid: true,
|
||||
parsed: value,
|
||||
};
|
||||
}
|
||||
const isPage =
|
||||
(dataTree.pageList as PageListPayload).findIndex(
|
||||
page => page.pageName === pageNameOrUrl,
|
||||
) !== -1;
|
||||
const isValidUrl = URL_REGEX.test(pageNameOrUrl);
|
||||
if (!(isValidUrl || isPage)) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: value,
|
||||
message: `${NAVIGATE_TO_VALIDATION_ERROR}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: undefined,
|
||||
transformed: "undefined",
|
||||
message: "Not a function call",
|
||||
};
|
||||
},
|
||||
[VALIDATION_TYPES.ARRAY_ACTION_SELECTOR]: (
|
||||
value: any,
|
||||
props: WidgetProps,
|
||||
dataTree?: DataTree,
|
||||
): ValidationResponse => {
|
||||
const { isValid, parsed, message } = VALIDATORS[VALIDATION_TYPES.ARRAY](
|
||||
value,
|
||||
props,
|
||||
dataTree,
|
||||
);
|
||||
let isValidFinal = isValid;
|
||||
let finalParsed = parsed.slice();
|
||||
if (isValid) {
|
||||
finalParsed = parsed.map((value: any) => {
|
||||
const { isValid, message } = VALIDATORS[
|
||||
VALIDATION_TYPES.ACTION_SELECTOR
|
||||
](value.dynamicTrigger, props, dataTree);
|
||||
|
||||
isValidFinal = isValidFinal && isValid;
|
||||
return {
|
||||
...value,
|
||||
message: message,
|
||||
isValid: isValid,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
isValid: isValidFinal,
|
||||
parsed: finalParsed,
|
||||
message: message,
|
||||
};
|
||||
},
|
||||
[VALIDATION_TYPES.SELECTED_TAB]: (
|
||||
value: any,
|
||||
props: WidgetProps,
|
||||
dataTree?: DataTree,
|
||||
): ValidationResponse => {
|
||||
const tabs =
|
||||
props.tabs && _.isString(props.tabs)
|
||||
? JSON.parse(props.tabs)
|
||||
: props.tabs && Array.isArray(props.tabs)
|
||||
? props.tabs
|
||||
: [];
|
||||
const tabNames = tabs.map((i: { label: string; id: string }) => i.label);
|
||||
const isValidTabName = tabNames.includes(value);
|
||||
return {
|
||||
isValid: isValidTabName,
|
||||
parsed: value,
|
||||
message: isValidTabName
|
||||
? ""
|
||||
: `${WIDGET_TYPE_VALIDATION_ERROR}: Invalid tab name.`,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
|
@ -8,7 +8,7 @@ import {
|
|||
import {
|
||||
WidgetPropertyValidationType,
|
||||
BASE_WIDGET_VALIDATION,
|
||||
} from "./ValidationFactory";
|
||||
} from "./WidgetValidation";
|
||||
import React from "react";
|
||||
|
||||
type WidgetDerivedPropertyType = any;
|
||||
|
|
@ -142,8 +142,33 @@ class WidgetFactory {
|
|||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
static getWidgetTypeConfigMap(): WidgetTypeConfigMap {
|
||||
const typeConfigMap: WidgetTypeConfigMap = {};
|
||||
WidgetFactory.getWidgetTypes().forEach(type => {
|
||||
typeConfigMap[type] = {
|
||||
validations: WidgetFactory.getWidgetPropertyValidationMap(type),
|
||||
defaultProperties: WidgetFactory.getWidgetDefaultPropertiesMap(type),
|
||||
derivedProperties: WidgetFactory.getWidgetDerivedPropertiesMap(type),
|
||||
triggerProperties: WidgetFactory.getWidgetTriggerPropertiesMap(type),
|
||||
metaProperties: WidgetFactory.getWidgetMetaPropertiesMap(type),
|
||||
};
|
||||
});
|
||||
return typeConfigMap;
|
||||
}
|
||||
}
|
||||
|
||||
export type WidgetTypeConfigMap = Record<
|
||||
string,
|
||||
{
|
||||
validations: WidgetPropertyValidationType;
|
||||
derivedProperties: WidgetDerivedPropertyType;
|
||||
triggerProperties: TriggerPropertiesMap;
|
||||
defaultProperties: Record<string, string>;
|
||||
metaProperties: Record<string, any>;
|
||||
}
|
||||
>;
|
||||
|
||||
export interface WidgetCreationException {
|
||||
message: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,6 +80,10 @@ import IconWidget, {
|
|||
} from "widgets/IconWidget";
|
||||
|
||||
import CanvasWidget, { ProfiledCanvasWidget } from "widgets/CanvasWidget";
|
||||
import SkeletonWidget, {
|
||||
ProfiledSkeletonWidget,
|
||||
SkeletonWidgetProps,
|
||||
} from "../widgets/SkeletonWidget";
|
||||
export default class WidgetBuilderRegistry {
|
||||
static registerWidgetBuilders() {
|
||||
WidgetFactory.registerWidgetBuilder(
|
||||
|
|
@ -376,5 +380,19 @@ export default class WidgetBuilderRegistry {
|
|||
IconWidget.getDefaultPropertiesMap(),
|
||||
IconWidget.getMetaPropertiesMap(),
|
||||
);
|
||||
|
||||
WidgetFactory.registerWidgetBuilder(
|
||||
WidgetTypes.SKELETON_WIDGET,
|
||||
{
|
||||
buildWidget(widgetProps: SkeletonWidgetProps): JSX.Element {
|
||||
return <ProfiledSkeletonWidget {...widgetProps} />;
|
||||
},
|
||||
},
|
||||
SkeletonWidget.getPropertyValidationMap(),
|
||||
SkeletonWidget.getDerivedPropertiesMap(),
|
||||
SkeletonWidget.getTriggerPropertyMap(),
|
||||
SkeletonWidget.getDefaultPropertiesMap(),
|
||||
SkeletonWidget.getMetaPropertiesMap(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
16
app/client/src/utils/WidgetValidation.ts
Normal file
16
app/client/src/utils/WidgetValidation.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import {
|
||||
VALIDATION_TYPES,
|
||||
ValidationType,
|
||||
Validator,
|
||||
} from "constants/WidgetValidation";
|
||||
|
||||
export const BASE_WIDGET_VALIDATION = {
|
||||
isLoading: VALIDATION_TYPES.BOOLEAN,
|
||||
isVisible: VALIDATION_TYPES.BOOLEAN,
|
||||
isDisabled: VALIDATION_TYPES.BOOLEAN,
|
||||
};
|
||||
|
||||
export type WidgetPropertyValidationType = Record<
|
||||
string,
|
||||
ValidationType | Validator
|
||||
>;
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
import TernServer from "./TernServer";
|
||||
import { MockCodemirrorEditor } from "../../../test/__mocks__/CodeMirrorEditorMock";
|
||||
jest.mock("jsExecution/RealmExecutor");
|
||||
|
||||
describe("Tern server", () => {
|
||||
it("Check whether the correct value is being sent to tern", () => {
|
||||
|
|
@ -96,7 +95,7 @@ describe("Tern server", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it(`Check whether the position is evaluated correctly for placing the selected
|
||||
it(`Check whether the position is evaluated correctly for placing the selected
|
||||
autocomplete value`, () => {
|
||||
const ternServer = new TernServer({});
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ import ErrorBoundary from "components/editorComponents/ErrorBoundry";
|
|||
import {
|
||||
BASE_WIDGET_VALIDATION,
|
||||
WidgetPropertyValidationType,
|
||||
} from "utils/ValidationFactory";
|
||||
} from "utils/WidgetValidation";
|
||||
import {
|
||||
DerivedPropertiesMap,
|
||||
TriggerPropertiesMap,
|
||||
|
|
@ -325,6 +325,23 @@ export interface WidgetPositionProps extends WidgetRowCols {
|
|||
detachFromLayout?: boolean;
|
||||
}
|
||||
|
||||
export const WIDGET_STATIC_PROPS = {
|
||||
leftColumn: true,
|
||||
rightColumn: true,
|
||||
topRow: true,
|
||||
bottomRow: true,
|
||||
minHeight: true,
|
||||
parentColumnSpace: true,
|
||||
parentRowSpace: true,
|
||||
children: true,
|
||||
type: true,
|
||||
widgetId: true,
|
||||
widgetName: true,
|
||||
parentId: true,
|
||||
renderMode: true,
|
||||
detachFromLayout: true,
|
||||
};
|
||||
|
||||
export interface WidgetDisplayProps {
|
||||
//TODO(abhinav): Some of these props are mandatory
|
||||
isVisible?: boolean;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { EventType } from "constants/ActionConstants";
|
|||
import {
|
||||
WidgetPropertyValidationType,
|
||||
BASE_WIDGET_VALIDATION,
|
||||
} from "utils/ValidationFactory";
|
||||
} from "utils/WidgetValidation";
|
||||
import { VALIDATION_TYPES } from "constants/WidgetValidation";
|
||||
import { TriggerPropertiesMap } from "utils/WidgetFactory";
|
||||
import * as Sentry from "@sentry/react";
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, { lazy, Suspense } from "react";
|
||||
import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
|
||||
import { WidgetType } from "constants/WidgetConstants";
|
||||
import { WidgetPropertyValidationType } from "utils/ValidationFactory";
|
||||
import { WidgetPropertyValidationType } from "utils/WidgetValidation";
|
||||
import { VALIDATION_TYPES } from "constants/WidgetValidation";
|
||||
import Skeleton from "components/utils/Skeleton";
|
||||
import * as Sentry from "@sentry/react";
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { VALIDATION_TYPES } from "constants/WidgetValidation";
|
|||
import {
|
||||
WidgetPropertyValidationType,
|
||||
BASE_WIDGET_VALIDATION,
|
||||
} from "utils/ValidationFactory";
|
||||
} from "utils/WidgetValidation";
|
||||
import {
|
||||
TriggerPropertiesMap,
|
||||
DerivedPropertiesMap,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import DatePickerComponent from "components/designSystems/blueprint/DatePickerCo
|
|||
import {
|
||||
WidgetPropertyValidationType,
|
||||
BASE_WIDGET_VALIDATION,
|
||||
} from "utils/ValidationFactory";
|
||||
} from "utils/WidgetValidation";
|
||||
import { VALIDATION_TYPES } from "constants/WidgetValidation";
|
||||
import {
|
||||
DerivedPropertiesMap,
|
||||
|
|
|
|||
|
|
@ -7,11 +7,9 @@ import _ from "lodash";
|
|||
import {
|
||||
WidgetPropertyValidationType,
|
||||
BASE_WIDGET_VALIDATION,
|
||||
} from "utils/ValidationFactory";
|
||||
} from "utils/WidgetValidation";
|
||||
import { VALIDATION_TYPES } from "constants/WidgetValidation";
|
||||
import { TriggerPropertiesMap } from "utils/WidgetFactory";
|
||||
import { VALIDATORS } from "utils/Validators";
|
||||
import { DataTree } from "entities/DataTree/dataTreeFactory";
|
||||
import { Intent as BlueprintIntent } from "@blueprintjs/core";
|
||||
import * as Sentry from "@sentry/react";
|
||||
import withMeta, { WithMeta } from "./MetaHOC";
|
||||
|
|
@ -28,42 +26,7 @@ class DropdownWidget extends BaseWidget<DropdownWidgetProps, WidgetState> {
|
|||
// onOptionChange: VALIDATION_TYPES.ACTION_SELECTOR,
|
||||
selectedOptionValueArr: VALIDATION_TYPES.ARRAY,
|
||||
selectedOptionValues: VALIDATION_TYPES.ARRAY,
|
||||
defaultOptionValue: (
|
||||
value: string | string[],
|
||||
props: WidgetProps,
|
||||
dataTree?: DataTree,
|
||||
) => {
|
||||
let values = value;
|
||||
|
||||
if (props) {
|
||||
if (props.selectionType === "SINGLE_SELECT") {
|
||||
return VALIDATORS[VALIDATION_TYPES.TEXT](value, props, dataTree);
|
||||
} else if (props.selectionType === "MULTI_SELECT") {
|
||||
if (typeof value === "string") {
|
||||
try {
|
||||
values = JSON.parse(value);
|
||||
if (!Array.isArray(values)) {
|
||||
throw new Error();
|
||||
}
|
||||
} catch {
|
||||
values = value.length ? value.split(",") : [];
|
||||
if (values.length > 0) {
|
||||
values = values.map(value => value.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(values)) {
|
||||
values = _.uniq(values);
|
||||
}
|
||||
|
||||
return {
|
||||
isValid: true,
|
||||
parsed: values,
|
||||
};
|
||||
},
|
||||
defaultOptionValue: VALIDATION_TYPES.DEFAULT_OPTION_VALUE,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import OneDrive from "@uppy/onedrive";
|
|||
import {
|
||||
WidgetPropertyValidationType,
|
||||
BASE_WIDGET_VALIDATION,
|
||||
} from "utils/ValidationFactory";
|
||||
} from "utils/WidgetValidation";
|
||||
import { VALIDATION_TYPES } from "constants/WidgetValidation";
|
||||
import { EventType, ExecutionResult } from "constants/ActionConstants";
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { EventType, ExecutionResult } from "constants/ActionConstants";
|
|||
import {
|
||||
BASE_WIDGET_VALIDATION,
|
||||
WidgetPropertyValidationType,
|
||||
} from "utils/ValidationFactory";
|
||||
} from "utils/WidgetValidation";
|
||||
import { VALIDATION_TYPES } from "constants/WidgetValidation";
|
||||
import { TriggerPropertiesMap } from "utils/WidgetFactory";
|
||||
import * as Sentry from "@sentry/react";
|
||||
|
|
|
|||
|
|
@ -11,8 +11,12 @@ import withMeta from "./MetaHOC";
|
|||
class FormWidget extends ContainerWidget {
|
||||
checkInvalidChildren = (children: WidgetProps[]): boolean => {
|
||||
return _.some(children, child => {
|
||||
if ("children" in child) return this.checkInvalidChildren(child.children);
|
||||
if ("isValid" in child) return !child.isValid;
|
||||
if ("children" in child) {
|
||||
return this.checkInvalidChildren(child.children);
|
||||
}
|
||||
if ("isValid" in child) {
|
||||
return !child.isValid;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// import React from "react";
|
||||
// import { render, fireEvent } from "@testing-library/react";
|
||||
// import ImageWidget, { ImageWidgetProps } from "./ImageWidget";
|
||||
// import RealmExecutor from "../jsExecution/RealmExecutor";
|
||||
|
||||
// import { useDrag } from "react-dnd";
|
||||
// import { Provider } from "react-redux";
|
||||
|
|
@ -10,8 +9,6 @@
|
|||
|
||||
// import "@testing-library/jest-dom";
|
||||
|
||||
// jest.mock("jsExecution/RealmExecutor");
|
||||
|
||||
// jest.mock("react-dnd", () => ({
|
||||
// useDrag: jest.fn().mockReturnValue([{ isDragging: false }, jest.fn()]),
|
||||
// }));
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import ImageComponent from "components/designSystems/appsmith/ImageComponent";
|
|||
import {
|
||||
WidgetPropertyValidationType,
|
||||
BASE_WIDGET_VALIDATION,
|
||||
} from "utils/ValidationFactory";
|
||||
} from "utils/WidgetValidation";
|
||||
import { VALIDATION_TYPES } from "constants/WidgetValidation";
|
||||
import * as Sentry from "@sentry/react";
|
||||
import { EventType } from "constants/ActionConstants";
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { EventType } from "constants/ActionConstants";
|
|||
import {
|
||||
WidgetPropertyValidationType,
|
||||
BASE_WIDGET_VALIDATION,
|
||||
} from "utils/ValidationFactory";
|
||||
} from "utils/WidgetValidation";
|
||||
import { VALIDATION_TYPES } from "constants/WidgetValidation";
|
||||
import { FIELD_REQUIRED_ERROR } from "constants/messages";
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import React from "react";
|
|||
import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
|
||||
import { WidgetType } from "constants/WidgetConstants";
|
||||
import MapComponent from "components/designSystems/appsmith/MapComponent";
|
||||
import { WidgetPropertyValidationType } from "utils/ValidationFactory";
|
||||
import { WidgetPropertyValidationType } from "utils/WidgetValidation";
|
||||
import { VALIDATION_TYPES } from "constants/WidgetValidation";
|
||||
import { EventType } from "constants/ActionConstants";
|
||||
import { TriggerPropertiesMap } from "utils/WidgetFactory";
|
||||
|
|
|
|||
|
|
@ -2,13 +2,15 @@ import React from "react";
|
|||
import BaseWidget, { WidgetProps } from "./BaseWidget";
|
||||
import _ from "lodash";
|
||||
import { EditorContext } from "../components/editorComponents/EditorContextProvider";
|
||||
import { clearPropertyCache } from "../utils/DynamicBindingUtils";
|
||||
import { clearEvalPropertyCache } from "sagas/evaluationsSaga";
|
||||
import { ExecuteActionPayload } from "../constants/ActionConstants";
|
||||
|
||||
type DebouncedExecuteActionPayload = Omit<
|
||||
ExecuteActionPayload,
|
||||
"dynamicString"
|
||||
> & { dynamicString?: string };
|
||||
> & {
|
||||
dynamicString?: string;
|
||||
};
|
||||
|
||||
export interface WithMeta {
|
||||
updateWidgetMetaProperty: (
|
||||
|
|
@ -87,7 +89,7 @@ const withMeta = (WrappedWidget: typeof BaseWidget) => {
|
|||
[...this.updatedProperties.keys()].forEach(propertyName => {
|
||||
if (updateWidgetMetaProperty) {
|
||||
const propertyValue = this.state[propertyName];
|
||||
clearPropertyCache(`${widgetName}.${propertyName}`);
|
||||
clearEvalPropertyCache(`${widgetName}.${propertyName}`);
|
||||
updateWidgetMetaProperty(widgetId, propertyName, propertyValue);
|
||||
this.updatedProperties.delete(propertyName);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { EventType } from "constants/ActionConstants";
|
|||
import {
|
||||
WidgetPropertyValidationType,
|
||||
BASE_WIDGET_VALIDATION,
|
||||
} from "utils/ValidationFactory";
|
||||
} from "utils/WidgetValidation";
|
||||
import { VALIDATION_TYPES } from "constants/WidgetValidation";
|
||||
import { TriggerPropertiesMap } from "utils/WidgetFactory";
|
||||
import * as Sentry from "@sentry/react";
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import React, { lazy, Suspense } from "react";
|
|||
import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
|
||||
import { WidgetType } from "constants/WidgetConstants";
|
||||
import { EventType } from "constants/ActionConstants";
|
||||
import { WidgetPropertyValidationType } from "utils/ValidationFactory";
|
||||
import { WidgetPropertyValidationType } from "utils/WidgetValidation";
|
||||
import { VALIDATION_TYPES } from "constants/WidgetValidation";
|
||||
import {
|
||||
TriggerPropertiesMap,
|
||||
|
|
|
|||
27
app/client/src/widgets/SkeletonWidget.tsx
Normal file
27
app/client/src/widgets/SkeletonWidget.tsx
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import React from "react";
|
||||
import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
|
||||
import { WidgetType } from "constants/WidgetConstants";
|
||||
import * as Sentry from "@sentry/react";
|
||||
import styled from "styled-components";
|
||||
|
||||
const SkeletonWrapper = styled.div`
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
class SkeletonWidget extends BaseWidget<SkeletonWidgetProps, WidgetState> {
|
||||
getPageView() {
|
||||
return <SkeletonWrapper className="bp3-skeleton" />;
|
||||
}
|
||||
|
||||
getWidgetType(): WidgetType {
|
||||
return "SKELETON_WIDGET";
|
||||
}
|
||||
}
|
||||
|
||||
export interface SkeletonWidgetProps extends WidgetProps {
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
export default SkeletonWidget;
|
||||
export const ProfiledSkeletonWidget = Sentry.withProfiler(SkeletonWidget);
|
||||
|
|
@ -15,7 +15,7 @@ import { VALIDATION_TYPES } from "constants/WidgetValidation";
|
|||
import {
|
||||
BASE_WIDGET_VALIDATION,
|
||||
WidgetPropertyValidationType,
|
||||
} from "utils/ValidationFactory";
|
||||
} from "utils/WidgetValidation";
|
||||
import { ColumnAction } from "components/propertyControls/ColumnActionSelectorControl";
|
||||
import { TriggerPropertiesMap } from "utils/WidgetFactory";
|
||||
import Skeleton from "components/utils/Skeleton";
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import TabsComponent from "components/designSystems/appsmith/TabsComponent";
|
|||
import { WidgetType, WidgetTypes } from "constants/WidgetConstants";
|
||||
import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
|
||||
import WidgetFactory, { TriggerPropertiesMap } from "utils/WidgetFactory";
|
||||
import { WidgetPropertyValidationType } from "utils/ValidationFactory";
|
||||
import { WidgetPropertyValidationType } from "utils/WidgetValidation";
|
||||
import { VALIDATION_TYPES } from "constants/WidgetValidation";
|
||||
import _ from "lodash";
|
||||
import { EventType } from "constants/ActionConstants";
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { VALIDATION_TYPES } from "constants/WidgetValidation";
|
|||
import {
|
||||
WidgetPropertyValidationType,
|
||||
BASE_WIDGET_VALIDATION,
|
||||
} from "utils/ValidationFactory";
|
||||
} from "utils/WidgetValidation";
|
||||
import { DerivedPropertiesMap } from "utils/WidgetFactory";
|
||||
import * as Sentry from "@sentry/react";
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { VALIDATION_TYPES } from "constants/WidgetValidation";
|
|||
import {
|
||||
WidgetPropertyValidationType,
|
||||
BASE_WIDGET_VALIDATION,
|
||||
} from "utils/ValidationFactory";
|
||||
} from "utils/WidgetValidation";
|
||||
import { TriggerPropertiesMap } from "utils/WidgetFactory";
|
||||
import Skeleton from "components/utils/Skeleton";
|
||||
import * as Sentry from "@sentry/react";
|
||||
|
|
|
|||
1604
app/client/src/workers/evaluation.worker.ts
Normal file
1604
app/client/src/workers/evaluation.worker.ts
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -11,9 +11,3 @@ export const mockExecute = jest.fn().mockImplementation((src, data) => {
|
|||
});
|
||||
|
||||
export const mockRegisterLibrary = jest.fn();
|
||||
|
||||
// jest.mock("jsExecution/RealmExecutor", () => {
|
||||
// jest.fn().mockImplementation(() => {
|
||||
// return { execute: mockExecute, registerLibrary: mockRegisterLibrary };
|
||||
// });
|
||||
// });
|
||||
|
|
|
|||
7
app/client/typings/worker-loader/index.d.ts
vendored
Normal file
7
app/client/typings/worker-loader/index.d.ts
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
declare module "worker-loader!*" {
|
||||
class WebpackWorker extends Worker {
|
||||
constructor();
|
||||
}
|
||||
|
||||
export default WebpackWorker;
|
||||
}
|
||||
|
|
@ -1836,10 +1836,10 @@
|
|||
"@types/yargs" "^15.0.0"
|
||||
chalk "^3.0.0"
|
||||
|
||||
"@jest/types@^26.3.0":
|
||||
version "26.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.3.0.tgz#97627bf4bdb72c55346eef98e3b3f7ddc4941f71"
|
||||
integrity sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==
|
||||
"@jest/types@^26.5.2":
|
||||
version "26.5.2"
|
||||
resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.5.2.tgz#44c24f30c8ee6c7f492ead9ec3f3c62a5289756d"
|
||||
integrity sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==
|
||||
dependencies:
|
||||
"@types/istanbul-lib-coverage" "^2.0.0"
|
||||
"@types/istanbul-reports" "^3.0.0"
|
||||
|
|
@ -1969,9 +1969,9 @@
|
|||
uuid "^3.3.2"
|
||||
|
||||
"@optimizely/optimizely-sdk@^4.0.0":
|
||||
version "4.3.3"
|
||||
resolved "https://registry.yarnpkg.com/@optimizely/optimizely-sdk/-/optimizely-sdk-4.3.3.tgz#91db4072f8e439d997370ad48c25106610f2e4a3"
|
||||
integrity sha512-tz6GyJMM4TUpLTsoyO9ZKNB5/UswtKSF4jLlBG1N6hWVi+bCJUa25M4Ok6DA0izZ2k0Y2xmUQDVmV4p23Li5Gw==
|
||||
version "4.3.4"
|
||||
resolved "https://registry.yarnpkg.com/@optimizely/optimizely-sdk/-/optimizely-sdk-4.3.4.tgz#b323b91dc8af9656dde8bcf696801bd71443e202"
|
||||
integrity sha512-DqaEg9YwiwnfDjaDmbST2cu0/7W/yQJqQ+tBwIEwh/HqiSgs8oQJX7sNG2Ql2fFwlIzG7APkIx/oxwbXpp8LPg==
|
||||
dependencies:
|
||||
"@optimizely/js-sdk-datafile-manager" "^0.8.0"
|
||||
"@optimizely/js-sdk-event-processor" "^0.6.0"
|
||||
|
|
@ -2074,14 +2074,14 @@
|
|||
resolved "https://registry.yarnpkg.com/@scarf/scarf/-/scarf-1.1.0.tgz#b84b4a91cd938a688d36245b7a7db6fbc476a499"
|
||||
integrity sha512-b2iE8kjjzzUo2WZ0xuE2N77kfnTds7ClrDxcz3Atz7h2XrNVoAPUoT75i7CY0st5x++70V91Y+c6RpBX9MX7Jg==
|
||||
|
||||
"@sentry/browser@5.25.0":
|
||||
version "5.25.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-5.25.0.tgz#4e3d2132ba1f2e2b26f73c49cbb6977ee9c9fea9"
|
||||
integrity sha512-QDVUbUuTu58xCdId0eUO4YzpvrPdoUw1ryVy/Yep9Es/HD0fiSyO1Js0eQVkV/EdXtyo2pomc1Bpy7dbn2EJ2w==
|
||||
"@sentry/browser@5.26.0":
|
||||
version "5.26.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-5.26.0.tgz#e90a197fb94c5f26c8e05d6a539c118f33c7d598"
|
||||
integrity sha512-52kNVpy10Zd3gJRGFkhnOQvr80WJg7+XBqjMOE0//Akh4PfvEK3IqmAjVqysz6aHdruwTTivKF4ZoAxL/pA7Rg==
|
||||
dependencies:
|
||||
"@sentry/core" "5.25.0"
|
||||
"@sentry/types" "5.25.0"
|
||||
"@sentry/utils" "5.25.0"
|
||||
"@sentry/core" "5.26.0"
|
||||
"@sentry/types" "5.26.0"
|
||||
"@sentry/utils" "5.26.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/cli@^1.58.0":
|
||||
|
|
@ -2095,69 +2095,69 @@
|
|||
progress "^2.0.3"
|
||||
proxy-from-env "^1.1.0"
|
||||
|
||||
"@sentry/core@5.25.0":
|
||||
version "5.25.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.25.0.tgz#525ad37f9e8a95603768e3b74b437d5235a51578"
|
||||
integrity sha512-hY6Zmo7t/RV+oZuvXHP6nyAj/QnZr2jW0e7EbL5YKMV8q0vlnjcE0LgqFXme726OJemoLk67z+sQOJic/Ztehg==
|
||||
"@sentry/core@5.26.0":
|
||||
version "5.26.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.26.0.tgz#9b5fe4de8a869d733ebcc77f5ec9c619f8717a51"
|
||||
integrity sha512-Ubrw7K52orTVsaxpz8Su40FPXugKipoQC+zPrXcH+JIMB+o18kutF81Ae4WzuUqLfP7YB91eAlRrP608zw0EXA==
|
||||
dependencies:
|
||||
"@sentry/hub" "5.25.0"
|
||||
"@sentry/minimal" "5.25.0"
|
||||
"@sentry/types" "5.25.0"
|
||||
"@sentry/utils" "5.25.0"
|
||||
"@sentry/hub" "5.26.0"
|
||||
"@sentry/minimal" "5.26.0"
|
||||
"@sentry/types" "5.26.0"
|
||||
"@sentry/utils" "5.26.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/hub@5.25.0":
|
||||
version "5.25.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.25.0.tgz#6932535604cafaee1ac7f361b0e7c2ce8f7e7bc3"
|
||||
integrity sha512-kOlOiJV8wMX50lYpzMlOXBoH7MNG0Ho4RTusdZnXZBaASq5/ljngDJkLr6uylNjceZQP21wzipCQajsJMYB7EQ==
|
||||
"@sentry/hub@5.26.0":
|
||||
version "5.26.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.26.0.tgz#b2bbd8128cd5915f2ee59cbc29fff30272d74ec5"
|
||||
integrity sha512-lAYeWvvhGYS6eQ5d0VEojw0juxGc3v4aAu8VLvMKWcZ1jXD13Bhc46u9Nvf4qAY6BAQsJDQcpEZLpzJu1bk1Qw==
|
||||
dependencies:
|
||||
"@sentry/types" "5.25.0"
|
||||
"@sentry/utils" "5.25.0"
|
||||
"@sentry/types" "5.26.0"
|
||||
"@sentry/utils" "5.26.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/minimal@5.25.0":
|
||||
version "5.25.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.25.0.tgz#447b5406b45c8c436c461abea4474d6a849ed975"
|
||||
integrity sha512-9JFKuW7U+1vPO86k3+XRtJyooiVZsVOsFFO4GulBzepi3a0ckNyPgyjUY1saLH+cEHx18hu8fGgajvI8ANUF2g==
|
||||
"@sentry/minimal@5.26.0":
|
||||
version "5.26.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.26.0.tgz#851dea3644153ed3ac4837fa8ed5661d94e7a313"
|
||||
integrity sha512-mdFo3FYaI1W3KEd8EHATYx8mDOZIxeoUhcBLlH7Iej6rKvdM7p8GoECrmHPU1l6sCCPtBuz66QT5YeXc7WILsA==
|
||||
dependencies:
|
||||
"@sentry/hub" "5.25.0"
|
||||
"@sentry/types" "5.25.0"
|
||||
"@sentry/hub" "5.26.0"
|
||||
"@sentry/types" "5.26.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/react@^5.24.2":
|
||||
version "5.25.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/react/-/react-5.25.0.tgz#269f54db9d6f92410bee07117f8d8e03b219e068"
|
||||
integrity sha512-lZwiFj+BQtmaj+Do9hcRSJcdrTisSGq2521/Xm9qGPbhsRW8uTHMJjkDgMHriYxxqXYOQrY9FisJwvkPpkroow==
|
||||
version "5.26.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/react/-/react-5.26.0.tgz#0462430757ac0ab0c10de803f39fb41d5ced1caa"
|
||||
integrity sha512-oC1wwfhckV8HHJTs4Zot5JIwEftcltPuC8cOedenDor5SKKbMeNufKw0ZgW82j9DSZMjh053LKkIZmO7zYf8eQ==
|
||||
dependencies:
|
||||
"@sentry/browser" "5.25.0"
|
||||
"@sentry/minimal" "5.25.0"
|
||||
"@sentry/types" "5.25.0"
|
||||
"@sentry/utils" "5.25.0"
|
||||
"@sentry/browser" "5.26.0"
|
||||
"@sentry/minimal" "5.26.0"
|
||||
"@sentry/types" "5.26.0"
|
||||
"@sentry/utils" "5.26.0"
|
||||
hoist-non-react-statics "^3.3.2"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/tracing@^5.24.2":
|
||||
version "5.25.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-5.25.0.tgz#1cfbcf085a7a3b679f417058d09590298ddaa255"
|
||||
integrity sha512-KcyHEGFpqSDubHrdWT/vF2hKkjw/ts6NpJ6tPDjBXUNz98BHdAyMKtLOFTCeJFply7/s5fyiAYu44M+M6IG3Bw==
|
||||
version "5.26.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-5.26.0.tgz#33ee0426da14836e54e7b9a8838e4d7d0cb14b70"
|
||||
integrity sha512-N9qWGmKrFJYKFTZBe8zVT3Qiju0+9bbNJuyun69T+fqP3PCDh+aRlRiP+OKTJyeCZjNG5HIvIlU8wTVUDoYfjQ==
|
||||
dependencies:
|
||||
"@sentry/hub" "5.25.0"
|
||||
"@sentry/minimal" "5.25.0"
|
||||
"@sentry/types" "5.25.0"
|
||||
"@sentry/utils" "5.25.0"
|
||||
"@sentry/hub" "5.26.0"
|
||||
"@sentry/minimal" "5.26.0"
|
||||
"@sentry/types" "5.26.0"
|
||||
"@sentry/utils" "5.26.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/types@5.25.0":
|
||||
version "5.25.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.25.0.tgz#3bcf95e118d655d3f4e8bfa5f0be2e1fe4ea5307"
|
||||
integrity sha512-8M4PREbcar+15wrtEqcwfcU33SS+2wBSIOd/NrJPXJPTYxi49VypCN1mZBDyWkaK+I+AuQwI3XlRPCfsId3D1A==
|
||||
"@sentry/types@5.26.0":
|
||||
version "5.26.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.26.0.tgz#b0cbacb0b24cd86620fb296b46cf7277bb004a3e"
|
||||
integrity sha512-ugpa1ePOhK55pjsyutAsa2tiJVQEyGYCaOXzaheg/3+EvhMdoW+owiZ8wupfvPhtZFIU3+FPOVz0d5k9K5d1rw==
|
||||
|
||||
"@sentry/utils@5.25.0":
|
||||
version "5.25.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.25.0.tgz#b132034be66d7381d30879d2a9e09216fed28342"
|
||||
integrity sha512-Hz5spdIkMSRH5NR1YFOp5qbsY5Ud2lKhEQWlqxcVThMG5YNUc10aYv5ijL19v0YkrC2rqPjCRm7GrVtzOc7bXQ==
|
||||
"@sentry/utils@5.26.0":
|
||||
version "5.26.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.26.0.tgz#09a3d01d91747f38f796cafeb24f8fd86e4fa05f"
|
||||
integrity sha512-F2gnHIAWbjiowcAgxz3VpKxY/NQ39NTujEd/NPnRTWlRynLFg3bAV+UvZFXljhYJeN3b/zRlScNDcpCWTrtZGw==
|
||||
dependencies:
|
||||
"@sentry/types" "5.25.0"
|
||||
"@sentry/types" "5.26.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/webpack-plugin@^1.12.1":
|
||||
|
|
@ -2871,9 +2871,9 @@
|
|||
loader-utils "^1.2.3"
|
||||
|
||||
"@testing-library/dom@^7.24.2":
|
||||
version "7.24.3"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.24.3.tgz#dae3071463cf28dc7755b43d9cf2202e34cbb85d"
|
||||
integrity sha512-6eW9fUhEbR423FZvoHRwbWm9RUUByLWGayYFNVvqTnQLYvsNpBS4uEuKH9aqr3trhxFwGVneJUonehL3B1sHJw==
|
||||
version "7.26.0"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.26.0.tgz#da4d052dc426a4ccc916303369c6e7552126f680"
|
||||
integrity sha512-fyKFrBbS1IigaE3FV21LyeC7kSGF84lqTlSYdKmGaHuK2eYQ/bXVPM5vAa2wx/AU1iPD6oQHsxy2QQ17q9AMCg==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.10.4"
|
||||
"@babel/runtime" "^7.10.3"
|
||||
|
|
@ -2881,6 +2881,7 @@
|
|||
aria-query "^4.2.2"
|
||||
chalk "^4.1.0"
|
||||
dom-accessibility-api "^0.5.1"
|
||||
lz-string "^1.4.4"
|
||||
pretty-format "^26.4.2"
|
||||
|
||||
"@testing-library/jest-dom@^5.11.4":
|
||||
|
|
@ -3016,9 +3017,9 @@
|
|||
"@types/node" "*"
|
||||
|
||||
"@types/googlemaps@^3.39.6":
|
||||
version "3.39.14"
|
||||
resolved "https://registry.yarnpkg.com/@types/googlemaps/-/googlemaps-3.39.14.tgz#971a7b474e7dcf6af1019fdccb91a69f74c5b262"
|
||||
integrity sha512-MB8zwqarykWxCayoWcQAeKWpFTgKlCz7Z+PpFeEimAe4keQ1o1Q3Y/5r5Td52gd164QmTUHGxGlKkmUEVrdbHA==
|
||||
version "3.40.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/googlemaps/-/googlemaps-3.40.0.tgz#786743648ab464fbeaa4cfe15230a7297273b309"
|
||||
integrity sha512-KcAYVKjd5fL0Ur9G4xNL5YG/Bp5HFfdd8s/7j97eFcTyTpp7vIRcf8mtRBAIOM3QNgN2iJhSEecWTG2x8D+bnQ==
|
||||
|
||||
"@types/hast@^2.0.0":
|
||||
version "2.3.1"
|
||||
|
|
@ -3097,15 +3098,15 @@
|
|||
dependencies:
|
||||
jest-diff "^24.3.0"
|
||||
|
||||
"@types/json-schema@^7.0.3", "@types/json-schema@^7.0.5":
|
||||
"@types/json-schema@^7.0.3", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6":
|
||||
version "7.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0"
|
||||
integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==
|
||||
|
||||
"@types/lodash@^4.14.120":
|
||||
version "4.14.161"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.161.tgz#a21ca0777dabc6e4f44f3d07f37b765f54188b18"
|
||||
integrity sha512-EP6O3Jkr7bXvZZSZYlsgt5DIjiGr0dXP1/jVEwVLTFgg0d+3lWVQkRavYVQszV7dYUwvg0B8R0MBDpcmXg7XIA==
|
||||
version "4.14.162"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.162.tgz#65d78c397e0d883f44afbf1f7ba9867022411470"
|
||||
integrity sha512-alvcho1kRUnnD1Gcl4J+hK0eencvzq9rmzvFPRmP5rPHx9VVsJj6bKLTATPVf9ktgv4ujzh7T+XWKp+jhuODig==
|
||||
|
||||
"@types/mdast@^3.0.0":
|
||||
version "3.0.3"
|
||||
|
|
@ -3134,14 +3135,14 @@
|
|||
"@types/node" "*"
|
||||
|
||||
"@types/node@*":
|
||||
version "14.11.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.11.5.tgz#fecad41c041cae7f2404ad4b2d0742fdb628b305"
|
||||
integrity sha512-jVFzDV6NTbrLMxm4xDSIW/gKnk8rQLF9wAzLWIOg+5nU6ACrIMndeBdXci0FGtqJbP9tQvm6V39eshc96TO2wQ==
|
||||
version "14.11.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.11.8.tgz#fe2012f2355e4ce08bca44aeb3abbb21cf88d33f"
|
||||
integrity sha512-KPcKqKm5UKDkaYPTuXSx8wEP7vE9GnuaXIZKijwRYcePpZFDVuy2a57LarFKiORbHOuTOOwYzxVxcUzsh2P2Pw==
|
||||
|
||||
"@types/node@^10.12.18":
|
||||
version "10.17.37"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.37.tgz#40d03db879993799c3819e298b003f055e8ecafe"
|
||||
integrity sha512-4c38N7p9k9yqdcANh/WExTahkBgOTmggCyrTvVcbE8ByqO3g8evt/407v/I4X/gdfUkIyZBSQh/Rc3tvuwlVGw==
|
||||
version "10.17.39"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.39.tgz#ce1122758d0608de8303667cebf171f44192629b"
|
||||
integrity sha512-dJLCxrpQmgyxYGcl0Ae9MTsQgI22qHHcGFj/8VKu7McJA5zQpnuGjoksnxbo1JxSjW/Nahnl13W8MYZf01CZHA==
|
||||
|
||||
"@types/normalize-package-data@^2.4.0":
|
||||
version "2.4.0"
|
||||
|
|
@ -3266,9 +3267,9 @@
|
|||
"@types/react" "*"
|
||||
|
||||
"@types/react-select@^3.0.5":
|
||||
version "3.0.21"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-select/-/react-select-3.0.21.tgz#089e1caa98dd653347cf4057b087957d26580d67"
|
||||
integrity sha512-jVtukxaARMQCJC74ikGzpxrzxDFPkcGEwLsFIsc/bAn2rcBUlJ0WgP71ShUc/Q0nnk4ClZn2jBm2tbh6HtxB3A==
|
||||
version "3.0.22"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-select/-/react-select-3.0.22.tgz#b88306365e99fa86809a5c0ce0f1b4e8d0b626bf"
|
||||
integrity sha512-fqgmC979JPr/6476Pau6QnmI9zVV664R7Q92Ld1rgTn+umtUXT5X3+PO/x6O4imCZnh7XCqZcouabWAlAQJNpQ==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
"@types/react-dom" "*"
|
||||
|
|
@ -3310,9 +3311,9 @@
|
|||
"@types/react" "*"
|
||||
|
||||
"@types/react@*", "@types/react@^16.8.2":
|
||||
version "16.9.51"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.51.tgz#f8aa51ffa9996f1387f63686696d9b59713d2b60"
|
||||
integrity sha512-lQa12IyO+DMlnSZ3+AGHRUiUcpK47aakMMoBG8f7HGxJT8Yfe+WE128HIXaHOHVPReAW0oDS3KAI0JI2DDe1PQ==
|
||||
version "16.9.52"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.52.tgz#c46c72d1a1d8d9d666f4dd2066c0e22600ccfde1"
|
||||
integrity sha512-EHRjmnxiNivwhGdMh9sz1Yw9AUxTSZFxKqdBWAAzyZx3sufWwx6ogqHYh/WB1m/I4ZpjkoZLExF5QTy2ekVi/Q==
|
||||
dependencies:
|
||||
"@types/prop-types" "*"
|
||||
csstype "^3.0.2"
|
||||
|
|
@ -3372,9 +3373,9 @@
|
|||
integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==
|
||||
|
||||
"@types/styled-components@^5.1.3":
|
||||
version "5.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/styled-components/-/styled-components-5.1.3.tgz#6fab3d9c8f7d9a15cbb89d379d850c985002f363"
|
||||
integrity sha512-HGpirof3WOhiX17lb61Q/tpgqn48jxO8EfZkdJ8ueYqwLbK2AHQe/G08DasdA2IdKnmwOIP1s9X2bopxKXgjRw==
|
||||
version "5.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/styled-components/-/styled-components-5.1.4.tgz#11f167dbde268635c66adc89b5a5db2e69d75384"
|
||||
integrity sha512-78f5Zuy0v/LTQNOYfpH+CINHpchzMMmAt9amY2YNtSgsk1TmlKm8L2Wijss/mtTrsUAVTm2CdGB8VOM65vA8xg==
|
||||
dependencies:
|
||||
"@types/hoist-non-react-statics" "*"
|
||||
"@types/react" "*"
|
||||
|
|
@ -3475,44 +3476,35 @@
|
|||
"@types/yargs-parser" "*"
|
||||
|
||||
"@types/yargs@^15.0.0":
|
||||
version "15.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.7.tgz#dad50a7a234a35ef9460737a56024287a3de1d2b"
|
||||
integrity sha512-Gf4u3EjaPNcC9cTu4/j2oN14nSVhr8PQ+BvBcBQHAhDZfl0bVIiLgvnRXv/dn58XhTm9UXvBpvJpDlwV65QxOA==
|
||||
version "15.0.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.8.tgz#7644904cad7427eb704331ea9bf1ee5499b82e23"
|
||||
integrity sha512-b0BYzFUzBpOhPjpl1wtAHU994jBeKF4TKVlT7ssFv44T617XNcPdRoG4AzHLVshLzlrF7i3lTelH7UbuNYV58Q==
|
||||
dependencies:
|
||||
"@types/yargs-parser" "*"
|
||||
|
||||
"@typescript-eslint/eslint-plugin@^2.10.0":
|
||||
version "2.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.19.0.tgz#bf743448a4633e4b52bee0c40148ba072ab3adbd"
|
||||
version "2.34.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.34.0.tgz#6f8ce8a46c7dea4a6f1d171d2bb8fbae6dac2be9"
|
||||
integrity sha512-4zY3Z88rEE99+CNvTbXSyovv2z9PNOVffTWD2W8QF5s2prBQtwN2zadqERcrHpcR7O/+KMI3fcTAmUUhK/iQcQ==
|
||||
dependencies:
|
||||
"@typescript-eslint/experimental-utils" "2.19.0"
|
||||
eslint-utils "^1.4.3"
|
||||
"@typescript-eslint/experimental-utils" "2.34.0"
|
||||
functional-red-black-tree "^1.0.1"
|
||||
regexpp "^3.0.0"
|
||||
tsutils "^3.17.1"
|
||||
|
||||
"@typescript-eslint/eslint-plugin@^4.4.0":
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.4.0.tgz#0321684dd2b902c89128405cf0385e9fe8561934"
|
||||
integrity sha512-RVt5wU9H/2H+N/ZrCasTXdGbUTkbf7Hfi9eLiA8vPQkzUJ/bLDCC3CsoZioPrNcnoyN8r0gT153dC++A4hKBQQ==
|
||||
version "4.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.4.1.tgz#b8acea0373bd2a388ac47df44652f00bf8b368f5"
|
||||
integrity sha512-O+8Utz8pb4OmcA+Nfi5THQnQpHSD2sDUNw9AxNHpuYOo326HZTtG8gsfT+EAYuVrFNaLyNb2QnUNkmTRDskuRA==
|
||||
dependencies:
|
||||
"@typescript-eslint/experimental-utils" "4.4.0"
|
||||
"@typescript-eslint/scope-manager" "4.4.0"
|
||||
"@typescript-eslint/experimental-utils" "4.4.1"
|
||||
"@typescript-eslint/scope-manager" "4.4.1"
|
||||
debug "^4.1.1"
|
||||
functional-red-black-tree "^1.0.1"
|
||||
regexpp "^3.0.0"
|
||||
semver "^7.3.2"
|
||||
tsutils "^3.17.1"
|
||||
|
||||
"@typescript-eslint/experimental-utils@2.19.0":
|
||||
version "2.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.19.0.tgz#d5ca732f22c009e515ba09fcceb5f2127d841568"
|
||||
integrity sha512-zwpg6zEOPbhB3+GaQfufzlMUOO6GXCNZq6skk+b2ZkZAIoBhVoanWK255BS1g5x9bMwHpLhX0Rpn5Fc3NdCZdg==
|
||||
dependencies:
|
||||
"@types/json-schema" "^7.0.3"
|
||||
"@typescript-eslint/typescript-estree" "2.19.0"
|
||||
eslint-scope "^5.0.0"
|
||||
|
||||
"@typescript-eslint/experimental-utils@2.34.0":
|
||||
version "2.34.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz#d3524b644cdb40eebceca67f8cf3e4cc9c8f980f"
|
||||
|
|
@ -3523,15 +3515,15 @@
|
|||
eslint-scope "^5.0.0"
|
||||
eslint-utils "^2.0.0"
|
||||
|
||||
"@typescript-eslint/experimental-utils@4.4.0":
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.4.0.tgz#62a05d3f543b8fc5dec4982830618ea4d030e1a9"
|
||||
integrity sha512-01+OtK/oWeSJTjQcyzDztfLF1YjvKpLFo+JZmurK/qjSRcyObpIecJ4rckDoRCSh5Etw+jKfdSzVEHevh9gJ1w==
|
||||
"@typescript-eslint/experimental-utils@4.4.1":
|
||||
version "4.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.4.1.tgz#40613b9757fa0170de3e0043254dbb077cafac0c"
|
||||
integrity sha512-Nt4EVlb1mqExW9cWhpV6pd1a3DkUbX9DeyYsdoeziKOpIJ04S2KMVDO+SEidsXRH/XHDpbzXykKcMTLdTXH6cQ==
|
||||
dependencies:
|
||||
"@types/json-schema" "^7.0.3"
|
||||
"@typescript-eslint/scope-manager" "4.4.0"
|
||||
"@typescript-eslint/types" "4.4.0"
|
||||
"@typescript-eslint/typescript-estree" "4.4.0"
|
||||
"@typescript-eslint/scope-manager" "4.4.1"
|
||||
"@typescript-eslint/types" "4.4.1"
|
||||
"@typescript-eslint/typescript-estree" "4.4.1"
|
||||
eslint-scope "^5.0.0"
|
||||
eslint-utils "^2.0.0"
|
||||
|
||||
|
|
@ -3546,40 +3538,27 @@
|
|||
eslint-visitor-keys "^1.1.0"
|
||||
|
||||
"@typescript-eslint/parser@^4.4.0":
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.4.0.tgz#65974db9a75f23b036f17b37e959b5f99b659ec0"
|
||||
integrity sha512-yc14iEItCxoGb7W4Nx30FlTyGpU9r+j+n1LUK/exlq2eJeFxczrz/xFRZUk2f6yzWfK+pr1DOTyQnmDkcC4TnA==
|
||||
version "4.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.4.1.tgz#25fde9c080611f303f2f33cedb145d2c59915b80"
|
||||
integrity sha512-S0fuX5lDku28Au9REYUsV+hdJpW/rNW0gWlc4SXzF/kdrRaAVX9YCxKpziH7djeWT/HFAjLZcnY7NJD8xTeUEg==
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager" "4.4.0"
|
||||
"@typescript-eslint/types" "4.4.0"
|
||||
"@typescript-eslint/typescript-estree" "4.4.0"
|
||||
"@typescript-eslint/scope-manager" "4.4.1"
|
||||
"@typescript-eslint/types" "4.4.1"
|
||||
"@typescript-eslint/typescript-estree" "4.4.1"
|
||||
debug "^4.1.1"
|
||||
|
||||
"@typescript-eslint/scope-manager@4.4.0":
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.4.0.tgz#2f3dd27692a12cc9a046a90ba6a9d8cb7731190a"
|
||||
integrity sha512-r2FIeeU1lmW4K3CxgOAt8djI5c6Q/5ULAgdVo9AF3hPMpu0B14WznBAtxrmB/qFVbVIB6fSx2a+EVXuhSVMEyA==
|
||||
"@typescript-eslint/scope-manager@4.4.1":
|
||||
version "4.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.4.1.tgz#d19447e60db2ce9c425898d62fa03b2cce8ea3f9"
|
||||
integrity sha512-2oD/ZqD4Gj41UdFeWZxegH3cVEEH/Z6Bhr/XvwTtGv66737XkR4C9IqEkebCuqArqBJQSj4AgNHHiN1okzD/wQ==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "4.4.0"
|
||||
"@typescript-eslint/visitor-keys" "4.4.0"
|
||||
"@typescript-eslint/types" "4.4.1"
|
||||
"@typescript-eslint/visitor-keys" "4.4.1"
|
||||
|
||||
"@typescript-eslint/types@4.4.0":
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.4.0.tgz#63440ef87a54da7399a13bdd4b82060776e9e621"
|
||||
integrity sha512-nU0VUpzanFw3jjX+50OTQy6MehVvf8pkqFcURPAE06xFNFenMj1GPEI6IESvp7UOHAnq+n/brMirZdR+7rCrlA==
|
||||
|
||||
"@typescript-eslint/typescript-estree@2.19.0":
|
||||
version "2.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.19.0.tgz#6bd7310b9827e04756fe712909f26956aac4b196"
|
||||
integrity sha512-n6/Xa37k0jQdwpUszffi19AlNbVCR0sdvCs3DmSKMD7wBttKY31lhD2fug5kMD91B2qW4mQldaTEc1PEzvGu8w==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
eslint-visitor-keys "^1.1.0"
|
||||
glob "^7.1.6"
|
||||
is-glob "^4.0.1"
|
||||
lodash "^4.17.15"
|
||||
semver "^6.3.0"
|
||||
tsutils "^3.17.1"
|
||||
"@typescript-eslint/types@4.4.1":
|
||||
version "4.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.4.1.tgz#c507b35cf523bc7ba00aae5f75ee9b810cdabbc1"
|
||||
integrity sha512-KNDfH2bCyax5db+KKIZT4rfA8rEk5N0EJ8P0T5AJjo5xrV26UAzaiqoJCxeaibqc0c/IvZxp7v2g3difn2Pn3w==
|
||||
|
||||
"@typescript-eslint/typescript-estree@2.34.0":
|
||||
version "2.34.0"
|
||||
|
|
@ -3594,13 +3573,13 @@
|
|||
semver "^7.3.2"
|
||||
tsutils "^3.17.1"
|
||||
|
||||
"@typescript-eslint/typescript-estree@4.4.0":
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.4.0.tgz#16a2df7c16710ddd5406b32b86b9c1124b1ca526"
|
||||
integrity sha512-Fh85feshKXwki4nZ1uhCJHmqKJqCMba+8ZicQIhNi5d5jSQFteWiGeF96DTjO8br7fn+prTP+t3Cz/a/3yOKqw==
|
||||
"@typescript-eslint/typescript-estree@4.4.1":
|
||||
version "4.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.4.1.tgz#598f6de488106c2587d47ca2462c60f6e2797cb8"
|
||||
integrity sha512-wP/V7ScKzgSdtcY1a0pZYBoCxrCstLrgRQ2O9MmCUZDtmgxCO/TCqOTGRVwpP4/2hVfqMz/Vw1ZYrG8cVxvN3g==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "4.4.0"
|
||||
"@typescript-eslint/visitor-keys" "4.4.0"
|
||||
"@typescript-eslint/types" "4.4.1"
|
||||
"@typescript-eslint/visitor-keys" "4.4.1"
|
||||
debug "^4.1.1"
|
||||
globby "^11.0.1"
|
||||
is-glob "^4.0.1"
|
||||
|
|
@ -3608,12 +3587,12 @@
|
|||
semver "^7.3.2"
|
||||
tsutils "^3.17.1"
|
||||
|
||||
"@typescript-eslint/visitor-keys@4.4.0":
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.4.0.tgz#0a9118344082f14c0f051342a74b42dfdb012640"
|
||||
integrity sha512-oBWeroUZCVsHLiWRdcTXJB7s1nB3taFY8WGvS23tiAlT6jXVvsdAV4rs581bgdEjOhn43q6ro7NkOiLKu6kFqA==
|
||||
"@typescript-eslint/visitor-keys@4.4.1":
|
||||
version "4.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.4.1.tgz#1769dc7a9e2d7d2cfd3318b77ed8249187aed5c3"
|
||||
integrity sha512-H2JMWhLaJNeaylSnMSQFEhT/S/FsJbebQALmoJxMPMxLtlVAMy2uJP/Z543n9IizhjRayLSqoInehCeNW9rWcw==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "4.4.0"
|
||||
"@typescript-eslint/types" "4.4.1"
|
||||
eslint-visitor-keys "^2.0.0"
|
||||
|
||||
"@uppy/companion-client@^1.4.1", "@uppy/companion-client@^1.5.4":
|
||||
|
|
@ -4292,10 +4271,10 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2:
|
|||
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
|
||||
integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
|
||||
|
||||
ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4:
|
||||
version "6.12.5"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.5.tgz#19b0e8bae8f476e5ba666300387775fb1a00a4da"
|
||||
integrity sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==
|
||||
ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5:
|
||||
version "6.12.6"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
|
||||
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
|
||||
dependencies:
|
||||
fast-deep-equal "^3.1.1"
|
||||
fast-json-stable-stringify "^2.0.0"
|
||||
|
|
@ -4646,11 +4625,6 @@ ast-types-flow@0.0.7, ast-types-flow@^0.0.7:
|
|||
resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad"
|
||||
integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0=
|
||||
|
||||
ast-types@0.11.3:
|
||||
version "0.11.3"
|
||||
resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.11.3.tgz#c20757fe72ee71278ea0ff3d87e5c2ca30d9edf8"
|
||||
integrity sha512-XA5o5dsNw8MhyW0Q7MWXJWc4oOzZKbdsEJq45h7c8q/d9DwWZ5F2ugUc1PuMLPGsUnphCt/cNDHu8JeBbxf1qA==
|
||||
|
||||
ast-types@0.13.3:
|
||||
version "0.13.3"
|
||||
resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.3.tgz#50da3f28d17bdbc7969a3a2d83a0e4a72ae755a7"
|
||||
|
|
@ -4663,6 +4637,13 @@ ast-types@^0.13.2:
|
|||
dependencies:
|
||||
tslib "^2.0.1"
|
||||
|
||||
ast-types@^0.14.2:
|
||||
version "0.14.2"
|
||||
resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.14.2.tgz#600b882df8583e3cd4f2df5fa20fa83759d4bdfd"
|
||||
integrity sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA==
|
||||
dependencies:
|
||||
tslib "^2.0.1"
|
||||
|
||||
astral-regex@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9"
|
||||
|
|
@ -4994,13 +4975,13 @@ babel-plugin-named-asset-import@^0.3.1, babel-plugin-named-asset-import@^0.3.6:
|
|||
integrity sha512-1aGDUfL1qOOIoqk9QKGIo2lANk+C7ko/fqH0uIyC71x3PEGz0uVP8ISgfEsFuG+FKmjHTvFK/nNM8dowpmUxLA==
|
||||
|
||||
babel-plugin-react-docgen@^4.0.0, babel-plugin-react-docgen@^4.1.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-react-docgen/-/babel-plugin-react-docgen-4.2.0.tgz#4f425692f0ca06c73a1462274d370a3ac0637b46"
|
||||
integrity sha512-B3tjZwKskcia9TsqkND+9OTjl/F5A5OBvRJ6Ktg34CONoxm+kB3CJ52wk5TjbszX9gqCPcAuc0GgkhT0CLuT/Q==
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-react-docgen/-/babel-plugin-react-docgen-4.2.1.tgz#7cc8e2f94e8dc057a06e953162f0810e4e72257b"
|
||||
integrity sha512-UQ0NmGHj/HAqi5Bew8WvNfCk8wSsmdgNd8ZdMjBCICtyCJCq9LiqgqvjCYe570/Wg7AQArSq1VQ60Dd/CHN7mQ==
|
||||
dependencies:
|
||||
ast-types "^0.14.2"
|
||||
lodash "^4.17.15"
|
||||
react-docgen "^5.0.0"
|
||||
recast "^0.14.7"
|
||||
|
||||
"babel-plugin-styled-components@>= 1", babel-plugin-styled-components@^1.10.7:
|
||||
version "1.11.1"
|
||||
|
|
@ -5685,9 +5666,9 @@ caniuse-api@^3.0.0:
|
|||
lodash.uniq "^4.5.0"
|
||||
|
||||
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30000989, caniuse-lite@^1.0.30001035, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001135:
|
||||
version "1.0.30001146"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001146.tgz#c61fcb1474520c1462913689201fb292ba6f447c"
|
||||
integrity sha512-VAy5RHDfTJhpxnDdp2n40GPPLp3KqNrXz1QqFv4J64HvArKs8nuNMOWkB3ICOaBTU/Aj4rYAo/ytdQDDFF/Pug==
|
||||
version "1.0.30001148"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001148.tgz#dc97c7ed918ab33bf8706ddd5e387287e015d637"
|
||||
integrity sha512-E66qcd0KMKZHNJQt9hiLZGE3J4zuTqE1OnU53miEVtylFbwOEmeA5OsRu90noZful+XGSQOni1aT2tiqu/9yYw==
|
||||
|
||||
capture-exit@^2.0.0:
|
||||
version "2.0.0"
|
||||
|
|
@ -5829,9 +5810,9 @@ chokidar@^2.0.4, chokidar@^2.1.8:
|
|||
fsevents "^1.2.7"
|
||||
|
||||
chokidar@^3.3.0, chokidar@^3.4.1:
|
||||
version "3.4.2"
|
||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.2.tgz#38dc8e658dec3809741eb3ef7bb0a47fe424232d"
|
||||
integrity sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==
|
||||
version "3.4.3"
|
||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.3.tgz#c1df38231448e45ca4ac588e6c79573ba6a57d5b"
|
||||
integrity sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==
|
||||
dependencies:
|
||||
anymatch "~3.1.1"
|
||||
braces "~3.0.2"
|
||||
|
|
@ -5839,7 +5820,7 @@ chokidar@^3.3.0, chokidar@^3.4.1:
|
|||
is-binary-path "~2.1.0"
|
||||
is-glob "~4.0.1"
|
||||
normalize-path "~3.0.0"
|
||||
readdirp "~3.4.0"
|
||||
readdirp "~3.5.0"
|
||||
optionalDependencies:
|
||||
fsevents "~2.1.2"
|
||||
|
||||
|
|
@ -6077,21 +6058,21 @@ color-name@^1.0.0, color-name@~1.1.4:
|
|||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
|
||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||
|
||||
color-string@^1.5.2:
|
||||
version "1.5.3"
|
||||
resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc"
|
||||
integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==
|
||||
color-string@^1.5.4:
|
||||
version "1.5.4"
|
||||
resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.4.tgz#dd51cd25cfee953d138fe4002372cc3d0e504cb6"
|
||||
integrity sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==
|
||||
dependencies:
|
||||
color-name "^1.0.0"
|
||||
simple-swizzle "^0.2.2"
|
||||
|
||||
color@^3.0.0:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/color/-/color-3.1.2.tgz#68148e7f85d41ad7649c5fa8c8106f098d229e10"
|
||||
integrity sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/color/-/color-3.1.3.tgz#ca67fb4e7b97d611dcde39eceed422067d91596e"
|
||||
integrity sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==
|
||||
dependencies:
|
||||
color-convert "^1.9.1"
|
||||
color-string "^1.5.2"
|
||||
color-string "^1.5.4"
|
||||
|
||||
colorette@^1.2.1:
|
||||
version "1.2.1"
|
||||
|
|
@ -6602,9 +6583,9 @@ css-what@2.1:
|
|||
integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==
|
||||
|
||||
css-what@^3.2.1:
|
||||
version "3.4.1"
|
||||
resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.1.tgz#81cb70b609e4b1351b1e54cbc90fd9c54af86e2e"
|
||||
integrity sha512-wHOppVDKl4vTAOWzJt5Ek37Sgd9qq1Bmj/T1OjvicWbU5W7ru7Pqbn0Jdqii3Drx/h+dixHKXNhZYx7blthL7g==
|
||||
version "3.4.2"
|
||||
resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4"
|
||||
integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==
|
||||
|
||||
css.escape@^1.5.1:
|
||||
version "1.5.1"
|
||||
|
|
@ -7170,9 +7151,9 @@ doctypes@^1.1.0:
|
|||
integrity sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=
|
||||
|
||||
dom-accessibility-api@^0.5.1:
|
||||
version "0.5.3"
|
||||
resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.3.tgz#0ea493c924d4070dfbf531c4aaca3d7a2c601aab"
|
||||
integrity sha512-yfqzAi1GFxK6EoJIZKgxqJyK6j/OjEFEUi2qkNThD/kUhoCFSG1izq31B5xuxzbJBGw9/67uPtkPMYAzWL7L7Q==
|
||||
version "0.5.4"
|
||||
resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.4.tgz#b06d059cdd4a4ad9a79275f9d414a5c126241166"
|
||||
integrity sha512-TvrjBckDy2c6v6RLxPv5QXOnU+SmF9nBII5621Ve5fu6Z/BDrENurBEvlC1f44lKEUVqOpK4w9E5Idc5/EgkLQ==
|
||||
|
||||
dom-converter@^0.2:
|
||||
version "0.2.0"
|
||||
|
|
@ -7349,9 +7330,9 @@ ejs@^3.1.5:
|
|||
jake "^10.6.1"
|
||||
|
||||
electron-to-chromium@^1.3.247, electron-to-chromium@^1.3.378, electron-to-chromium@^1.3.571:
|
||||
version "1.3.578"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.578.tgz#e6671936f4571a874eb26e2e833aa0b2c0b776e0"
|
||||
integrity sha512-z4gU6dA1CbBJsAErW5swTGAaU2TBzc2mPAonJb00zqW1rOraDo2zfBMDRvaz9cVic+0JEZiYbHWPw/fTaZlG2Q==
|
||||
version "1.3.579"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.579.tgz#58bf17499de6edf697e1442017d8569bce0d301a"
|
||||
integrity sha512-9HaGm4UDxCtcmIqWWdv79pGgpRZWTqr+zg6kxp0MelSHfe1PNjrI8HXy1HgTSy4p0iQETGt8/ElqKFLW008BSA==
|
||||
|
||||
elegant-spinner@^1.0.1:
|
||||
version "1.0.1"
|
||||
|
|
@ -7587,9 +7568,9 @@ es6-symbol@^3.1.1, es6-symbol@~3.1.3:
|
|||
ext "^1.1.2"
|
||||
|
||||
escalade@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.0.tgz#e8e2d7c7a8b76f6ee64c2181d6b8151441602d4e"
|
||||
integrity sha512-mAk+hPSO8fLDkhV7V0dXazH5pDc6MrjBTPyD3VeKzxnVFjH1MIxbCdqGZB9O8+EwWakZs3ZCbDS4IpRt79V1ig==
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
|
||||
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
|
||||
|
||||
escape-html@^1.0.3, escape-html@~1.0.3:
|
||||
version "1.0.3"
|
||||
|
|
@ -7740,15 +7721,15 @@ eslint-plugin-react@7.19.0:
|
|||
xregexp "^4.3.0"
|
||||
|
||||
eslint-plugin-react@^7.21.3:
|
||||
version "7.21.3"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.21.3.tgz#71655d2af5155b19285ec929dd2cdc67a4470b52"
|
||||
integrity sha512-OI4GwTCqyIb4ipaOEGLWdaOHCXZZydStAsBEPB2e1ZfNM37bojpgO1BoOQbFb0eLVz3QLDx7b+6kYcrxCuJfhw==
|
||||
version "7.21.4"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.21.4.tgz#31060b2e5ff82b12e24a3cc33edb7d12f904775c"
|
||||
integrity sha512-uHeQ8A0hg0ltNDXFu3qSfFqTNPXm1XithH6/SY318UX76CMj7Q599qWpgmMhVQyvhq36pm7qvoN3pb6/3jsTFg==
|
||||
dependencies:
|
||||
array-includes "^3.1.1"
|
||||
array.prototype.flatmap "^1.2.3"
|
||||
doctrine "^2.1.0"
|
||||
has "^1.0.3"
|
||||
jsx-ast-utils "^2.4.1"
|
||||
jsx-ast-utils "^2.4.1 || ^3.0.0"
|
||||
object.entries "^1.1.2"
|
||||
object.fromentries "^2.0.2"
|
||||
object.values "^1.1.1"
|
||||
|
|
@ -11056,7 +11037,7 @@ jstransformer@1.0.0:
|
|||
is-promise "^2.0.0"
|
||||
promise "^7.0.1"
|
||||
|
||||
jsx-ast-utils@^2.2.1, jsx-ast-utils@^2.2.3, jsx-ast-utils@^2.4.1:
|
||||
jsx-ast-utils@^2.2.1, jsx-ast-utils@^2.2.3:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz#1114a4c1209481db06c690c2b4f488cc665f657e"
|
||||
integrity sha512-z1xSldJ6imESSzOjd3NNkieVJKRlKYSOtMG8SFyCj2FIrvSaSuli/WjpBkEzCBoR9bYYYFgqJw61Xhu7Lcgk+w==
|
||||
|
|
@ -11064,6 +11045,14 @@ jsx-ast-utils@^2.2.1, jsx-ast-utils@^2.2.3, jsx-ast-utils@^2.4.1:
|
|||
array-includes "^3.1.1"
|
||||
object.assign "^4.1.0"
|
||||
|
||||
"jsx-ast-utils@^2.4.1 || ^3.0.0":
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.1.0.tgz#642f1d7b88aa6d7eb9d8f2210e166478444fa891"
|
||||
integrity sha512-d4/UOjg+mxAWxCiF0c5UTSwyqbchkbqCvK87aBovhnh8GtysTjWmgC63tY0cJx/HzGgm9qnA147jVBdpOiQ2RA==
|
||||
dependencies:
|
||||
array-includes "^3.1.1"
|
||||
object.assign "^4.1.1"
|
||||
|
||||
killable@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892"
|
||||
|
|
@ -11543,6 +11532,11 @@ lru-cache@^5.1.1:
|
|||
dependencies:
|
||||
yallist "^3.0.2"
|
||||
|
||||
lz-string@^1.4.4:
|
||||
version "1.4.4"
|
||||
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26"
|
||||
integrity sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=
|
||||
|
||||
magic-string@^0.25.0, magic-string@^0.25.5, magic-string@^0.25.7:
|
||||
version "0.25.7"
|
||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051"
|
||||
|
|
@ -12195,9 +12189,9 @@ namespace-emitter@^2.0.1:
|
|||
integrity sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g==
|
||||
|
||||
nan@^2.12.1, nan@^2.13.2:
|
||||
version "2.14.1"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01"
|
||||
integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==
|
||||
version "2.14.2"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19"
|
||||
integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==
|
||||
|
||||
nanoid@^2.0.4:
|
||||
version "2.1.11"
|
||||
|
|
@ -13977,11 +13971,11 @@ pretty-format@^25.2.1, pretty-format@^25.5.0:
|
|||
react-is "^16.12.0"
|
||||
|
||||
pretty-format@^26.4.2:
|
||||
version "26.4.2"
|
||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.4.2.tgz#d081d032b398e801e2012af2df1214ef75a81237"
|
||||
integrity sha512-zK6Gd8zDsEiVydOCGLkoBoZuqv8VTiHyAbKznXe/gaph/DAeZOmit9yMfgIz5adIgAMMs5XfoYSwAX3jcCO1tA==
|
||||
version "26.5.2"
|
||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.5.2.tgz#5d896acfdaa09210683d34b6dc0e6e21423cd3e1"
|
||||
integrity sha512-VizyV669eqESlkOikKJI8Ryxl/kPpbdLwNdPs2GrbQs18MpySB5S0Yo0N7zkg2xTRiFq4CFw8ct5Vg4a0xP0og==
|
||||
dependencies:
|
||||
"@jest/types" "^26.3.0"
|
||||
"@jest/types" "^26.5.2"
|
||||
ansi-regex "^5.0.0"
|
||||
ansi-styles "^4.0.0"
|
||||
react-is "^16.12.0"
|
||||
|
|
@ -13992,9 +13986,9 @@ pretty-hrtime@^1.0.3:
|
|||
integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=
|
||||
|
||||
prismjs@^1.21.0, prismjs@^1.8.4:
|
||||
version "1.21.0"
|
||||
resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.21.0.tgz#36c086ec36b45319ec4218ee164c110f9fc015a3"
|
||||
integrity sha512-uGdSIu1nk3kej2iZsLyDoJ7e9bnPzIgY0naW/HdknGj61zScaprVEVGHrPoXqI+M9sP0NDnTK2jpkvmldpuqDw==
|
||||
version "1.22.0"
|
||||
resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.22.0.tgz#73c3400afc58a823dd7eed023f8e1ce9fd8977fa"
|
||||
integrity sha512-lLJ/Wt9yy0AiSYBf212kK3mM5L8ycwlyTlSxHBAneXLR0nzFMlZ5y7riFPF3E33zXOF2IH95xdY5jIyZbM9z/w==
|
||||
optionalDependencies:
|
||||
clipboard "^2.0.0"
|
||||
|
||||
|
|
@ -14005,7 +13999,7 @@ prismjs@~1.17.0:
|
|||
optionalDependencies:
|
||||
clipboard "^2.0.0"
|
||||
|
||||
private@^0.1.8, private@~0.1.5:
|
||||
private@^0.1.8:
|
||||
version "0.1.8"
|
||||
resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
|
||||
integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==
|
||||
|
|
@ -14087,9 +14081,9 @@ prop-types@^15.5.0, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.8,
|
|||
react-is "^16.8.1"
|
||||
|
||||
property-information@^5.0.0, property-information@^5.3.0:
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.5.0.tgz#4dc075d493061a82e2b7d096f406e076ed859943"
|
||||
integrity sha512-RgEbCx2HLa1chNgvChcx+rrCWD0ctBmGSE0M7lVm1yyv4UbvbrWoXp/BkVLZefzjrRBGW8/Js6uh/BnlHXFyjA==
|
||||
version "5.6.0"
|
||||
resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.6.0.tgz#61675545fb23002f245c6540ec46077d4da3ed69"
|
||||
integrity sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==
|
||||
dependencies:
|
||||
xtend "^4.0.0"
|
||||
|
||||
|
|
@ -14407,9 +14401,9 @@ react-app-polyfill@^1.0.6:
|
|||
whatwg-fetch "^3.0.0"
|
||||
|
||||
react-base-table@^1.9.1:
|
||||
version "1.11.3"
|
||||
resolved "https://registry.yarnpkg.com/react-base-table/-/react-base-table-1.11.3.tgz#9b77b84347e905ec68bd0b5e9639ac4c41b53e5e"
|
||||
integrity sha512-JBYtRQBVrNxdP2QpuRyi3+FXRViTmNuCHeV6LwTpWY10x/AwqtGmamNdtYI5hZ95f8lIvo2nTfZeRgOmdyveQw==
|
||||
version "1.12.0"
|
||||
resolved "https://registry.yarnpkg.com/react-base-table/-/react-base-table-1.12.0.tgz#3fc39f1cc7d1a4b349572e1d2b283626987d5dcc"
|
||||
integrity sha512-CaS8iI8JyK5rGjggfupZWKBPypNxlXrjOi+ndUsLMCJaoKCgUXZSlYv2oihq44c10MgaH/eEgEvkMV6dIGa59w==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.0.0"
|
||||
classnames "^2.2.5"
|
||||
|
|
@ -14959,9 +14953,9 @@ react-syntax-highlighter@^11.0.2:
|
|||
refractor "^2.4.1"
|
||||
|
||||
react-table@^7.0.0:
|
||||
version "7.5.2"
|
||||
resolved "https://registry.yarnpkg.com/react-table/-/react-table-7.5.2.tgz#d82ceee3d4d40b119159bce1708f084a95d3435c"
|
||||
integrity sha512-qiceR/gQUOBsO/q1z1wZ3RbRvkRt39Gbzo631HiPuWmo+eTmTgaXDqLGzCmU+bOr81PB6kDxXhoWQR8hiWaUJA==
|
||||
version "7.6.0"
|
||||
resolved "https://registry.yarnpkg.com/react-table/-/react-table-7.6.0.tgz#83d765b96505b5332d108a2e0c27ab653f5a78c3"
|
||||
integrity sha512-16kRTypBWz9ZwLnPWA8hc3eIC64POzO9GaMBiKaCcVM+0QOQzt0G7ebzGUM8SW0CYUpVM+glv1kMXrWj9tr3Sw==
|
||||
|
||||
react-tabs@^3.0.0:
|
||||
version "3.1.1"
|
||||
|
|
@ -15159,10 +15153,10 @@ readdirp@~3.2.0:
|
|||
dependencies:
|
||||
picomatch "^2.0.4"
|
||||
|
||||
readdirp@~3.4.0:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada"
|
||||
integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==
|
||||
readdirp@~3.5.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e"
|
||||
integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==
|
||||
dependencies:
|
||||
picomatch "^2.2.1"
|
||||
|
||||
|
|
@ -15183,16 +15177,6 @@ recast@0.19.1:
|
|||
private "^0.1.8"
|
||||
source-map "~0.6.1"
|
||||
|
||||
recast@^0.14.7:
|
||||
version "0.14.7"
|
||||
resolved "https://registry.yarnpkg.com/recast/-/recast-0.14.7.tgz#4f1497c2b5826d42a66e8e3c9d80c512983ff61d"
|
||||
integrity sha512-/nwm9pkrcWagN40JeJhkPaRxiHXBRkXyRh/hgU088Z/v+qCy+zIHHY6bC6o7NaKAxPqtE6nD8zBH1LfU0/Wx6A==
|
||||
dependencies:
|
||||
ast-types "0.11.3"
|
||||
esprima "~4.0.0"
|
||||
private "~0.1.5"
|
||||
source-map "~0.6.1"
|
||||
|
||||
recast@^0.18.1:
|
||||
version "0.18.10"
|
||||
resolved "https://registry.yarnpkg.com/recast/-/recast-0.18.10.tgz#605ebbe621511eb89b6356a7e224bff66ed91478"
|
||||
|
|
@ -15927,6 +15911,15 @@ schema-utils@^2.0.1, schema-utils@^2.5.0, schema-utils@^2.6.0, schema-utils@^2.6
|
|||
ajv "^6.12.4"
|
||||
ajv-keywords "^3.5.2"
|
||||
|
||||
schema-utils@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.0.0.tgz#67502f6aa2b66a2d4032b4279a2944978a0913ef"
|
||||
integrity sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==
|
||||
dependencies:
|
||||
"@types/json-schema" "^7.0.6"
|
||||
ajv "^6.12.5"
|
||||
ajv-keywords "^3.5.2"
|
||||
|
||||
scriptjs@^2.5.8:
|
||||
version "2.5.9"
|
||||
resolved "https://registry.yarnpkg.com/scriptjs/-/scriptjs-2.5.9.tgz#343915cd2ec2ed9bfdde2b9875cd28f59394b35f"
|
||||
|
|
@ -17305,14 +17298,14 @@ ts-pnp@^1.1.2, ts-pnp@^1.1.6:
|
|||
integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==
|
||||
|
||||
tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3:
|
||||
version "1.14.0"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.0.tgz#d624983f3e2c5e0b55307c3dd6c86acd737622c6"
|
||||
integrity sha512-+Zw5lu0D9tvBMjGP8LpvMb0u2WW2QV3y+D8mO6J+cNzCYIN4sVy43Bf9vl92nqFahutN0I8zHa7cc4vihIshnw==
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
||||
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
||||
|
||||
tslib@^2.0.0, tslib@^2.0.1:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.2.tgz#462295631185db44b21b1ea3615b63cd1c038242"
|
||||
integrity sha512-wAH28hcEKwna96/UacuWaVspVLkg4x1aDM9JlzqaQTOFczCktkVAb5fmXChgandR1EraDPs2w8P+ozM+oafwxg==
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c"
|
||||
integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==
|
||||
|
||||
tslib@~1.13.0:
|
||||
version "1.13.0"
|
||||
|
|
@ -17837,9 +17830,9 @@ void-elements@^3.1.0:
|
|||
integrity sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=
|
||||
|
||||
vue-docgen-api@^4.1.0:
|
||||
version "4.32.4"
|
||||
resolved "https://registry.yarnpkg.com/vue-docgen-api/-/vue-docgen-api-4.32.4.tgz#a74f4dfce99a078a4a87dcb779c6cdd480eeab01"
|
||||
integrity sha512-dtPOg9uCnclBOiWASMDMBjYhrXbRDlADZNTMkc5NtF1eSXzltN7GvB7P3YO92M5IvUeHwq84A9BqGBBbye5oWg==
|
||||
version "4.33.0"
|
||||
resolved "https://registry.yarnpkg.com/vue-docgen-api/-/vue-docgen-api-4.33.0.tgz#59630636373ee3f06b0a36820d6235fdb9adfbae"
|
||||
integrity sha512-kNIAE8kKsZtOqROqDyMGa30AVGcwo2D2g+Z2thuVbEcOAqRc4eM72PoFwqX9ZRqQSdj4TnqIGI3EfF4KHTZ/EQ==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.6.0"
|
||||
"@babel/types" "^7.6.0"
|
||||
|
|
@ -17851,7 +17844,7 @@ vue-docgen-api@^4.1.0:
|
|||
pug "^3.0.0"
|
||||
recast "0.19.1"
|
||||
ts-map "^1.0.3"
|
||||
vue-inbrowser-compiler-utils "^4.32.1"
|
||||
vue-inbrowser-compiler-utils "^4.33.0"
|
||||
|
||||
vue-docgen-loader@^1.3.0-beta.0:
|
||||
version "1.5.0"
|
||||
|
|
@ -17863,10 +17856,10 @@ vue-docgen-loader@^1.3.0-beta.0:
|
|||
loader-utils "^1.2.3"
|
||||
querystring "^0.2.0"
|
||||
|
||||
vue-inbrowser-compiler-utils@^4.32.1:
|
||||
version "4.32.1"
|
||||
resolved "https://registry.yarnpkg.com/vue-inbrowser-compiler-utils/-/vue-inbrowser-compiler-utils-4.32.1.tgz#d8774a4b7e91677d4d17d441485f5eafc77bc65d"
|
||||
integrity sha512-IL8rBV3lCyHErqD8sBdQhWz3zJ/wLzG6JfoSzZ3K6HShS5QqIQfJN0GESvzIos6EGvmtByEf4TTJnjm12b51VQ==
|
||||
vue-inbrowser-compiler-utils@^4.33.0:
|
||||
version "4.33.0"
|
||||
resolved "https://registry.yarnpkg.com/vue-inbrowser-compiler-utils/-/vue-inbrowser-compiler-utils-4.33.0.tgz#3bb510a7931c62d076a035f71dc31a5329e4e015"
|
||||
integrity sha512-9VxLYc+J8o3Z1H6GpnPYD9J2lMcP4ahi1BoYTcRM6Yw0F/17Gbypog3yVCyh1VO4uDoIldi4b9ZF33zvR5KEvg==
|
||||
dependencies:
|
||||
camelcase "^5.3.1"
|
||||
|
||||
|
|
@ -18522,6 +18515,14 @@ worker-loader@^2.0.0:
|
|||
loader-utils "^1.0.0"
|
||||
schema-utils "^0.4.0"
|
||||
|
||||
worker-loader@^3.0.2:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/worker-loader/-/worker-loader-3.0.4.tgz#bbc0af0af7e2d972c1105001ab215497c00b98ca"
|
||||
integrity sha512-puFIebctLf/xB5Vex9QTX4Zr+wR6hQZqgVEg7QeUTA0I8XzTmGr620ryvY8E448C/hJ+eE+NKiIX9xyFcQNFbQ==
|
||||
dependencies:
|
||||
loader-utils "^2.0.0"
|
||||
schema-utils "^3.0.0"
|
||||
|
||||
worker-rpc@^0.1.0:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/worker-rpc/-/worker-rpc-0.1.1.tgz#cb565bd6d7071a8f16660686051e969ad32f54d5"
|
||||
|
|
@ -18529,13 +18530,6 @@ worker-rpc@^0.1.0:
|
|||
dependencies:
|
||||
microevent.ts "~0.1.1"
|
||||
|
||||
workerize-loader@^1.2.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/workerize-loader/-/workerize-loader-1.3.0.tgz#4995cf2ff2b45dd6dc60e4411e63f5ae2c704d36"
|
||||
integrity sha512-utWDc8K6embcICmRBUUkzanPgKBb8yM1OHfh6siZfiMsswE8wLCa9CWS+L7AARz0+Th4KH4ZySrqer/OJ9WuWw==
|
||||
dependencies:
|
||||
loader-utils "^2.0.0"
|
||||
|
||||
wrap-ansi@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-3.0.1.tgz#288a04d87eda5c286e060dfe8f135ce8d007f8ba"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,14 @@
|
|||
package com.appsmith.external.pluginExceptions;
|
||||
|
||||
public class StaleConnectionException extends RuntimeException {
|
||||
public StaleConnectionException() {
|
||||
}
|
||||
|
||||
public StaleConnectionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public StaleConnectionException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
plugin.id=dynamo-plugin
|
||||
plugin.class=com.external.plugins.DynamoPlugin
|
||||
plugin.version=1.0-SNAPSHOT
|
||||
plugin.provider=tech@appsmith.com
|
||||
plugin.dependencies=
|
||||
141
app/server/appsmith-plugins/dynamoPlugin/pom.xml
Normal file
141
app/server/appsmith-plugins/dynamoPlugin/pom.xml
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.external.plugins</groupId>
|
||||
<artifactId>dynamoPlugin</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<name>dynamoPlugin</name>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<java.version>11</java.version>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
<plugin.id>dynamo-plugin</plugin.id>
|
||||
<plugin.class>com.external.plugins.DynamoPlugin</plugin.class>
|
||||
<plugin.version>1.0-SNAPSHOT</plugin.version>
|
||||
<plugin.provider>tech@appsmith.com</plugin.provider>
|
||||
<plugin.dependencies/>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.pf4j</groupId>
|
||||
<artifactId>pf4j-spring</artifactId>
|
||||
<version>0.6.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.appsmith</groupId>
|
||||
<artifactId>interfaces</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.8</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>dynamodb</artifactId>
|
||||
<version>2.15.3</version>
|
||||
<scope>compile</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Test Dependencies -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.13.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>testcontainers</artifactId>
|
||||
<version>1.15.0-rc2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-test</artifactId>
|
||||
<version>3.2.11.RELEASE</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.4</version>
|
||||
<configuration>
|
||||
<minimizeJar>false</minimizeJar>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<manifestEntries>
|
||||
<Plugin-Id>${plugin.id}</Plugin-Id>
|
||||
<Plugin-Class>${plugin.class}</Plugin-Class>
|
||||
<Plugin-Version>${plugin.version}</Plugin-Version>
|
||||
<Plugin-Provider>${plugin.provider}</Plugin-Provider>
|
||||
</manifestEntries>
|
||||
</transformer>
|
||||
</transformers>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-dependencies</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<includeScope>runtime</includeScope>
|
||||
<outputDirectory>${project.build.directory}/lib</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
364
app/server/appsmith-plugins/dynamoPlugin/src/main/java/com/external/plugins/DynamoPlugin.java
vendored
Normal file
364
app/server/appsmith-plugins/dynamoPlugin/src/main/java/com/external/plugins/DynamoPlugin.java
vendored
Normal file
|
|
@ -0,0 +1,364 @@
|
|||
package com.external.plugins;
|
||||
|
||||
import com.appsmith.external.models.ActionConfiguration;
|
||||
import com.appsmith.external.models.ActionExecutionResult;
|
||||
import com.appsmith.external.models.AuthenticationDTO;
|
||||
import com.appsmith.external.models.DatasourceConfiguration;
|
||||
import com.appsmith.external.models.DatasourceTestResult;
|
||||
import com.appsmith.external.models.Endpoint;
|
||||
import com.appsmith.external.pluginExceptions.AppsmithPluginError;
|
||||
import com.appsmith.external.pluginExceptions.AppsmithPluginException;
|
||||
import com.appsmith.external.plugins.BasePlugin;
|
||||
import com.appsmith.external.plugins.PluginExecutor;
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
import org.pf4j.Extension;
|
||||
import org.pf4j.PluginWrapper;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import reactor.core.publisher.Mono;
|
||||
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
|
||||
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
|
||||
import software.amazon.awssdk.core.SdkBytes;
|
||||
import software.amazon.awssdk.core.SdkField;
|
||||
import software.amazon.awssdk.core.SdkPojo;
|
||||
import software.amazon.awssdk.regions.Region;
|
||||
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
|
||||
import software.amazon.awssdk.services.dynamodb.DynamoDbClientBuilder;
|
||||
import software.amazon.awssdk.services.dynamodb.model.DynamoDbResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class DynamoPlugin extends BasePlugin {
|
||||
|
||||
public DynamoPlugin(PluginWrapper wrapper) {
|
||||
super(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamo plugin receives the query as json of the following format:
|
||||
* {
|
||||
* "action": "GetItem",
|
||||
* "parameters": {...} // Depends on the action above.
|
||||
* }
|
||||
*
|
||||
* DynamoDB actions and parameters reference:
|
||||
* https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Operations_Amazon_DynamoDB.html
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
@Extension
|
||||
public static class DynamoPluginExecutor implements PluginExecutor<DynamoDbClient> {
|
||||
|
||||
@Override
|
||||
public Mono<ActionExecutionResult> execute(DynamoDbClient ddb,
|
||||
DatasourceConfiguration datasourceConfiguration,
|
||||
ActionConfiguration actionConfiguration) {
|
||||
|
||||
ActionExecutionResult result = new ActionExecutionResult();
|
||||
|
||||
final String action = actionConfiguration.getPath();
|
||||
if (StringUtils.isEmpty(action)) {
|
||||
return Mono.error(new AppsmithPluginException(
|
||||
AppsmithPluginError.PLUGIN_ERROR,
|
||||
"Missing action name (like `ListTables`, `GetItem` etc.)."
|
||||
));
|
||||
}
|
||||
|
||||
final String body = actionConfiguration.getBody();
|
||||
Map<String, Object> parameters = null;
|
||||
try {
|
||||
if (!StringUtils.isEmpty(body)) {
|
||||
parameters = objectMapper.readValue(body, HashMap.class);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
final String message = "Error parsing the JSON body: " + e.getMessage();
|
||||
log.warn(message, e);
|
||||
return Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, message));
|
||||
}
|
||||
|
||||
final Class<?> requestClass;
|
||||
try {
|
||||
requestClass = Class.forName("software.amazon.awssdk.services.dynamodb.model." + action + "Request");
|
||||
} catch (ClassNotFoundException e) {
|
||||
return Mono.error(new AppsmithPluginException(
|
||||
AppsmithPluginError.PLUGIN_ERROR,
|
||||
"Unknown action: `" + action + "`. Note that action names are case-sensitive."
|
||||
));
|
||||
}
|
||||
|
||||
try {
|
||||
final Method actionExecuteMethod = DynamoDbClient.class.getMethod(
|
||||
// Convert `ListTables` to `listTables`, which is the name of the method to execute this action.
|
||||
toLowerCamelCase(action),
|
||||
requestClass
|
||||
);
|
||||
final DynamoDbResponse response = (DynamoDbResponse) actionExecuteMethod.invoke(ddb, plainToSdk(parameters, requestClass));
|
||||
result.setBody(sdkToPlain(response));
|
||||
} catch (AppsmithPluginException | InvocationTargetException | IllegalAccessException | NoSuchMethodException | ClassNotFoundException e) {
|
||||
final String message = "Error executing the DynamoDB Action: " + (e.getCause() == null ? e : e.getCause()).getMessage();
|
||||
log.warn(message, e);
|
||||
return Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, message));
|
||||
}
|
||||
|
||||
result.setIsExecutionSuccess(true);
|
||||
return Mono.just(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<DynamoDbClient> datasourceCreate(DatasourceConfiguration datasourceConfiguration) {
|
||||
final DynamoDbClientBuilder builder = DynamoDbClient.builder();
|
||||
|
||||
if (!CollectionUtils.isEmpty(datasourceConfiguration.getEndpoints())) {
|
||||
final Endpoint endpoint = datasourceConfiguration.getEndpoints().get(0);
|
||||
builder.endpointOverride(URI.create("http://" + endpoint.getHost() + ":" + endpoint.getPort()));
|
||||
}
|
||||
|
||||
final AuthenticationDTO authentication = datasourceConfiguration.getAuthentication();
|
||||
if (authentication == null || StringUtils.isEmpty(authentication.getDatabaseName())) {
|
||||
return Mono.error(new AppsmithPluginException(
|
||||
AppsmithPluginError.PLUGIN_ERROR,
|
||||
"Missing region in datasource."
|
||||
));
|
||||
}
|
||||
|
||||
builder.region(Region.of(authentication.getDatabaseName()));
|
||||
|
||||
builder.credentialsProvider(StaticCredentialsProvider.create(
|
||||
AwsBasicCredentials.create(authentication.getUsername(), authentication.getPassword())
|
||||
));
|
||||
|
||||
return Mono.justOrEmpty(builder.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void datasourceDestroy(DynamoDbClient client) {
|
||||
if (client != null) {
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> validateDatasource(@NonNull DatasourceConfiguration datasourceConfiguration) {
|
||||
Set<String> invalids = new HashSet<>();
|
||||
|
||||
final AuthenticationDTO authentication = datasourceConfiguration.getAuthentication();
|
||||
if (authentication == null) {
|
||||
invalids.add("Missing AWS Access Key ID and Secret Access Key.");
|
||||
} else {
|
||||
if (StringUtils.isEmpty(authentication.getUsername())) {
|
||||
invalids.add("Missing AWS Access Key ID.");
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(authentication.getPassword())) {
|
||||
invalids.add("Missing AWS Secret Access Key.");
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(authentication.getDatabaseName())) {
|
||||
invalids.add("Missing region configuration.");
|
||||
}
|
||||
}
|
||||
|
||||
return invalids;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<DatasourceTestResult> testDatasource(DatasourceConfiguration datasourceConfiguration) {
|
||||
return datasourceCreate(datasourceConfiguration)
|
||||
.map(client -> {
|
||||
client.close();
|
||||
return true;
|
||||
})
|
||||
.defaultIfEmpty(false)
|
||||
.map(isValid -> BooleanUtils.isTrue(isValid)
|
||||
? new DatasourceTestResult()
|
||||
: new DatasourceTestResult("Unable to create DynamoDB Client.")
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static String toLowerCamelCase(String action) {
|
||||
return action.substring(0, 1).toLowerCase() + action.substring(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a map that conforms to what a valid DynamoDB request should look like, this function will convert into
|
||||
* a DynamoDBRequest object from AWS SDK. This is done using Java's reflection API.
|
||||
* @param mapping Mapping object representing the request details.
|
||||
* @param type Request type that should be created. Eg., ListTablesRequest.class, PutItemRequest.class etc.
|
||||
* @param <T> Type param of the request class.
|
||||
* @return An object of the request class, containing details of the request from the mapping.
|
||||
* @throws IllegalAccessException Thrown if any of the SDK methods' contracts change.
|
||||
* @throws InvocationTargetException Thrown if any of the SDK methods' contracts change.
|
||||
* @throws NoSuchMethodException Thrown if any of the SDK methods' contracts change.
|
||||
* @throws ClassNotFoundException Thrown if any of the builder class could not be found corresponding to the action class.
|
||||
*/
|
||||
public static <T> T plainToSdk(Map<String, Object> mapping, Class<T> type)
|
||||
throws IllegalAccessException, InvocationTargetException, NoSuchMethodException,
|
||||
AppsmithPluginException, ClassNotFoundException {
|
||||
|
||||
final Class<?> builderType = Class.forName(type.getName() + "$Builder");
|
||||
|
||||
final Object builder = type.getMethod("builder").invoke(null);
|
||||
|
||||
if (mapping != null) {
|
||||
for (final Map.Entry<String, Object> entry : mapping.entrySet()) {
|
||||
final String setterName = getSetterMethodName(entry.getKey());
|
||||
Object value = entry.getValue();
|
||||
|
||||
if (value instanceof String) {
|
||||
// AWS SDK has two data types that are represented as Strings in JSON, namely strings and binary.
|
||||
// We look at the parameter types for the setter method to decide which it should be, and then set
|
||||
// convert the value if needed before calling the setter.
|
||||
final Method setterMethod = findMethod(builderType, method -> {
|
||||
final Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
return method.getName().equals(setterName)
|
||||
&& (SdkBytes.class.isAssignableFrom(parameterTypes[0]) || String.class.isAssignableFrom(parameterTypes[0]));
|
||||
});
|
||||
if (setterMethod == null) {
|
||||
throw new AppsmithPluginException(
|
||||
AppsmithPluginError.PLUGIN_ERROR,
|
||||
"Invalid attribute/value by name " + entry.getKey()
|
||||
);
|
||||
}
|
||||
if (SdkBytes.class.isAssignableFrom(setterMethod.getParameterTypes()[0])) {
|
||||
value = SdkBytes.fromUtf8String((String) value);
|
||||
}
|
||||
setterMethod.invoke(builder, value);
|
||||
|
||||
} else if (value instanceof Boolean
|
||||
|| value instanceof Integer
|
||||
|| value instanceof Float
|
||||
|| value instanceof Double) {
|
||||
// These data types have a setter method that takes a the value as is. Nothing fancy here.
|
||||
builderType.getMethod(setterName, value.getClass()).invoke(builder, value);
|
||||
|
||||
} else if (value instanceof Map) {
|
||||
// For maps, we go recursive, applying this transformation to each value, and replacing with the
|
||||
// result in the map. Generic types in the setter method's signature are used to convert the values.
|
||||
final Method setterMethod = findMethod(builderType, m -> m.getName().equals(setterName));
|
||||
final ParameterizedType valueType = (ParameterizedType) setterMethod.getGenericParameterTypes()[0];
|
||||
final Map<String, Object> transformedMap = new HashMap<>();
|
||||
for (final Map.Entry<String, Object> innerEntry : ((Map<String, Object>) value).entrySet()) {
|
||||
Object innerValue = innerEntry.getValue();
|
||||
if (innerValue instanceof Map) {
|
||||
innerValue = plainToSdk((Map) innerValue, (Class<?>) valueType.getActualTypeArguments()[1]);
|
||||
}
|
||||
transformedMap.put(innerEntry.getKey(), innerValue);
|
||||
}
|
||||
value = transformedMap;
|
||||
if (!Map.class.isAssignableFrom((Class<?>) valueType.getRawType())) {
|
||||
// Some setters don't take a plain map. For example, some require an `AttributeValue` instance
|
||||
// for objects that are just maps in JSON. So, we make that conversion here.
|
||||
value = plainToSdk((Map) value, (Class<T>) valueType.getRawType());
|
||||
}
|
||||
setterMethod.invoke(builder, value);
|
||||
|
||||
} else if (value instanceof Collection) {
|
||||
// For linear collections, the process is similar to that of maps.
|
||||
final Collection<Object> valueAsCollection = (Collection) value;
|
||||
// Find method by name and exclude the varargs version of the method.
|
||||
final Method setterMethod = findMethod(builderType, m -> m.getName().equals(setterName) && !m.getParameterTypes()[0].getName().startsWith("[L"));
|
||||
final ParameterizedType valueType = (ParameterizedType) setterMethod.getGenericParameterTypes()[0];
|
||||
final Collection<Object> reTypedList = new ArrayList<>();
|
||||
for (final Object innerValue : valueAsCollection) {
|
||||
if (innerValue instanceof Map) {
|
||||
reTypedList.add(plainToSdk((Map) innerValue, (Class<?>) valueType.getActualTypeArguments()[0]));
|
||||
} else if (innerValue instanceof String && SdkBytes.class.isAssignableFrom((Class<?>) valueType.getActualTypeArguments()[0])) {
|
||||
reTypedList.add(SdkBytes.fromUtf8String((String) innerValue));
|
||||
} else {
|
||||
reTypedList.add(innerValue);
|
||||
}
|
||||
}
|
||||
setterMethod.invoke(builder, reTypedList);
|
||||
|
||||
} else {
|
||||
throw new AppsmithPluginException(
|
||||
AppsmithPluginError.PLUGIN_ERROR,
|
||||
"Unknown value type while deserializing:" + value.getClass().getName()
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (T) builderType.getMethod("build").invoke(builder);
|
||||
}
|
||||
|
||||
private static Method findMethod(Class<?> builderType, Predicate<Method> predicate) {
|
||||
return Arrays.stream(builderType.getMethods())
|
||||
.filter(predicate)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the name of the setter method in AWS SDK that will set the value of the field given by the argument.
|
||||
* @param key Name of the field for which to compute the setter method's name.
|
||||
* @return Name of the setter method that will set the value of the given `key` field.
|
||||
*/
|
||||
private static String getSetterMethodName(final String key) {
|
||||
if ("NULL".equals(key)) {
|
||||
// Since `null` is a reserved word in Java, AWS SDK uses `nul` for this field.
|
||||
return "nul";
|
||||
} else if (isUpperCase(key)) {
|
||||
return key.toLowerCase();
|
||||
} else {
|
||||
return toLowerCamelCase(key);
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, Object> sdkToPlain(SdkPojo response) {
|
||||
final Map<String, Object> plain = new HashMap<>();
|
||||
|
||||
for (final SdkField<?> field : response.sdkFields()) {
|
||||
Object value = field.getValueOrDefault(response);
|
||||
|
||||
if (value instanceof SdkPojo) {
|
||||
value = sdkToPlain((SdkPojo) value);
|
||||
|
||||
} else if (value instanceof Map) {
|
||||
final Map<String, Object> valueAsMap = (Map) value;
|
||||
final Map<String, Object> plainMap = new HashMap<>();
|
||||
for (final Map.Entry<String, Object> entry : valueAsMap.entrySet()) {
|
||||
final var key = entry.getKey();
|
||||
Object innerValue = entry.getValue();
|
||||
if (innerValue instanceof SdkPojo) {
|
||||
innerValue = sdkToPlain((SdkPojo) innerValue);
|
||||
}
|
||||
plainMap.put(key, innerValue);
|
||||
}
|
||||
value = plainMap;
|
||||
|
||||
}
|
||||
|
||||
plain.put(field.memberName(), value);
|
||||
}
|
||||
|
||||
return plain;
|
||||
}
|
||||
|
||||
private static boolean isUpperCase(String s) {
|
||||
for (char c : s.toCharArray()) {
|
||||
if (!Character.isUpperCase(c)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,188 @@
|
|||
{
|
||||
"editor": [
|
||||
{
|
||||
"sectionName": "",
|
||||
"id": 1,
|
||||
"children": [
|
||||
{
|
||||
"label": "Action",
|
||||
"configProperty": "actionConfiguration.path",
|
||||
"controlType": "DROP_DOWN",
|
||||
"isRequired": true,
|
||||
"initialValue": "GetItem",
|
||||
"options": [
|
||||
{
|
||||
"label": "BatchGetItem",
|
||||
"value":"BatchGetItem"
|
||||
},
|
||||
{
|
||||
"label": "BatchWriteItem",
|
||||
"value": "BatchWriteItem"
|
||||
},
|
||||
{
|
||||
"label": "CreateBackup",
|
||||
"value": "CreateBackup"
|
||||
},
|
||||
{
|
||||
"label": "CreateGlobalTable",
|
||||
"value": "CreateGlobalTable"
|
||||
},
|
||||
{
|
||||
"label": "CreateTable",
|
||||
"value": "CreateTable"
|
||||
},
|
||||
{
|
||||
"label": "DeleteBackup",
|
||||
"value": "DeleteBackup"
|
||||
},
|
||||
{
|
||||
"label": "DeleteItem",
|
||||
"value": "DeleteItem"
|
||||
},
|
||||
{
|
||||
"label": "DeleteTable",
|
||||
"value": "DeleteTable"
|
||||
},
|
||||
{
|
||||
"label": "DescribeBackup",
|
||||
"value": "DescribeBackup"
|
||||
},
|
||||
{
|
||||
"label": "DescribeContinuousBackups",
|
||||
"value": "DescribeContinuousBackups"
|
||||
},
|
||||
{
|
||||
"label": "DescribeContributorInsights",
|
||||
"value": "DescribeContributorInsights"
|
||||
},
|
||||
{
|
||||
"label": "DescribeEndpoints",
|
||||
"value": "DescribeEndpoints"
|
||||
},
|
||||
{
|
||||
"label": "DescribeGlobalTable",
|
||||
"value": "DescribeGlobalTable"
|
||||
},
|
||||
{
|
||||
"label": "DescribeGlobalTableSettings",
|
||||
"value": "DescribeGlobalTableSettings"
|
||||
},
|
||||
{
|
||||
"label": "DescribeLimits",
|
||||
"value": "DescribeLimits"
|
||||
},
|
||||
{
|
||||
"label": "DescribeTable",
|
||||
"value": "DescribeTable"
|
||||
},
|
||||
{
|
||||
"label": "DescribeTableReplicaAutoScaling",
|
||||
"value": "DescribeTableReplicaAutoScaling"
|
||||
},
|
||||
{
|
||||
"label": "DescribeTimeToLive",
|
||||
"value": "DescribeTimeToLive"
|
||||
},
|
||||
{
|
||||
"label": "GetItem",
|
||||
"value": "GetItem"
|
||||
},
|
||||
{
|
||||
"label": "ListBackups",
|
||||
"value": "ListBackups"
|
||||
},
|
||||
{
|
||||
"label": "ListContributorInsights",
|
||||
"value": "ListContributorInsights"
|
||||
},
|
||||
{
|
||||
"label": "ListGlobalTables",
|
||||
"value": "ListGlobalTables"
|
||||
},
|
||||
{
|
||||
"label": "ListTables",
|
||||
"value": "ListTables"
|
||||
},
|
||||
{
|
||||
"label": "ListTagsOfResource",
|
||||
"value": "ListTagsOfResource"
|
||||
},
|
||||
{
|
||||
"label": "PutItem",
|
||||
"value": "PutItem"
|
||||
},
|
||||
{
|
||||
"label": "Query",
|
||||
"value": "Query"
|
||||
},
|
||||
{
|
||||
"label": "RestoreTableFromBackup",
|
||||
"value": "RestoreTableFromBackup"
|
||||
},
|
||||
{
|
||||
"label": "RestoreTableToPointInTime",
|
||||
"value": "RestoreTableToPointInTime"
|
||||
},
|
||||
{
|
||||
"label": "Scan",
|
||||
"value": "Scan"
|
||||
},
|
||||
{
|
||||
"label": "TagResource",
|
||||
"value": "TagResource"
|
||||
},
|
||||
{
|
||||
"label": "TransactGetItems",
|
||||
"value": "TransactGetItems"
|
||||
},
|
||||
{
|
||||
"label": "TransactWriteItems",
|
||||
"value": "TransactWriteItems"
|
||||
},
|
||||
{
|
||||
"label": "UntagResource",
|
||||
"value": "UntagResource"
|
||||
},
|
||||
{
|
||||
"label": "UpdateContinuousBackups",
|
||||
"value": "UpdateContinuousBackups"
|
||||
},
|
||||
{
|
||||
"label": "UpdateContributorInsights",
|
||||
"value": "UpdateContributorInsights"
|
||||
},
|
||||
{
|
||||
"label": "UpdateGlobalTable",
|
||||
"value": "UpdateGlobalTable"
|
||||
},
|
||||
{
|
||||
"label": "UpdateGlobalTableSettings",
|
||||
"value": "UpdateGlobalTableSettings"
|
||||
},
|
||||
{
|
||||
"label": "UpdateItem",
|
||||
"value": "UpdateItem"
|
||||
},
|
||||
{
|
||||
"label": "UpdateTable",
|
||||
"value": "UpdateTable"
|
||||
},
|
||||
{
|
||||
"label": "UpdateTableReplicaAutoScaling",
|
||||
"value": "UpdateTableReplicaAutoScaling"
|
||||
},
|
||||
{
|
||||
"label": "UpdateTimeToLive",
|
||||
"value": "UpdateTimeToLive"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "",
|
||||
"configProperty": "actionConfiguration.body",
|
||||
"controlType": "QUERY_DYNAMIC_TEXT"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
{
|
||||
"form": [
|
||||
{
|
||||
"sectionName": "Details",
|
||||
"id": 1,
|
||||
"children": [
|
||||
{
|
||||
"label": "Region",
|
||||
"configProperty": "datasourceConfiguration.authentication.databaseName",
|
||||
"controlType": "DROP_DOWN",
|
||||
"isRequired": true,
|
||||
"initialValue": "AP_SOUTH_1",
|
||||
"options": [
|
||||
{
|
||||
"label": "ap-south-1",
|
||||
"value": "ap-south-1"
|
||||
},
|
||||
{
|
||||
"label": "eu-south-1",
|
||||
"value": "eu-south-1"
|
||||
},
|
||||
{
|
||||
"label": "us-gov-east-1",
|
||||
"value": "us-gov-east-1"
|
||||
},
|
||||
{
|
||||
"label": "ca-central-1",
|
||||
"value": "ca-central-1"
|
||||
},
|
||||
{
|
||||
"label": "eu-central-1",
|
||||
"value": "eu-central-1"
|
||||
},
|
||||
{
|
||||
"label": "us-west-1",
|
||||
"value": "us-west-1"
|
||||
},
|
||||
{
|
||||
"label": "us-west-2",
|
||||
"value": "us-west-2"
|
||||
},
|
||||
{
|
||||
"label": "af-south-1",
|
||||
"value": "af-south-1"
|
||||
},
|
||||
{
|
||||
"label": "eu-north-1",
|
||||
"value": "eu-north-1"
|
||||
},
|
||||
{
|
||||
"label": "eu-west-3",
|
||||
"value": "eu-west-3"
|
||||
},
|
||||
{
|
||||
"label": "eu-west-2",
|
||||
"value": "eu-west-2"
|
||||
},
|
||||
{
|
||||
"label": "eu-west-1",
|
||||
"value": "eu-west-1"
|
||||
},
|
||||
{
|
||||
"label": "ap-northeast-2",
|
||||
"value": "ap-northeast-2"
|
||||
},
|
||||
{
|
||||
"label": "ap-northeast-1",
|
||||
"value": "ap-northeast-1"
|
||||
},
|
||||
{
|
||||
"label": "me-south-1",
|
||||
"value": "me-south-1"
|
||||
},
|
||||
{
|
||||
"label": "sa-east-1",
|
||||
"value": "sa-east-1"
|
||||
},
|
||||
{
|
||||
"label": "ap-east-1",
|
||||
"value": "ap-east-1"
|
||||
},
|
||||
{
|
||||
"label": "cn-north-1",
|
||||
"value": "cn-north-1"
|
||||
},
|
||||
{
|
||||
"label": "us-gov-west-1",
|
||||
"value": "us-gov-west-1"
|
||||
},
|
||||
{
|
||||
"label": "ap-southeast-1",
|
||||
"value": "ap-southeast-1"
|
||||
},
|
||||
{
|
||||
"label": "ap-southeast-2",
|
||||
"value": "ap-southeast-2"
|
||||
},
|
||||
{
|
||||
"label": "us-iso-east-1",
|
||||
"value": "us-iso-east-1"
|
||||
},
|
||||
{
|
||||
"label": "us-east-1",
|
||||
"value": "us-east-1"
|
||||
},
|
||||
{
|
||||
"label": "us-east-2",
|
||||
"value": "us-east-2"
|
||||
},
|
||||
{
|
||||
"label": "cn-northwest-1",
|
||||
"value": "cn-northwest-1"
|
||||
},
|
||||
{
|
||||
"label": "us-isob-east-1",
|
||||
"value": "us-isob-east-1"
|
||||
},
|
||||
{
|
||||
"label": "aws-global",
|
||||
"value": "aws-global"
|
||||
},
|
||||
{
|
||||
"label": "aws-cn-global",
|
||||
"value": "aws-cn-global"
|
||||
},
|
||||
{
|
||||
"label": "aws-us-gov-global",
|
||||
"value": "aws-us-gov-global"
|
||||
},
|
||||
{
|
||||
"label": "aws-iso-global",
|
||||
"value": "aws-iso-global"
|
||||
},
|
||||
{
|
||||
"label": "aws-iso-b-global",
|
||||
"value": "aws-iso-b-global"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"sectionName": "Endpoint Override (Overrides above region)",
|
||||
"children": [
|
||||
{
|
||||
"label": "Host Address (for overriding endpoint only)",
|
||||
"configProperty": "datasourceConfiguration.endpoints[*].host",
|
||||
"controlType": "KEYVALUE_ARRAY",
|
||||
"validationMessage": "Please enter a valid host",
|
||||
"validationRegex": "^((?![/:]).)*$"
|
||||
},
|
||||
{
|
||||
"label": "Port",
|
||||
"configProperty": "datasourceConfiguration.endpoints[*].port",
|
||||
"dataType": "NUMBER",
|
||||
"controlType": "KEYVALUE_ARRAY"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "AWS Access Key ID",
|
||||
"configProperty": "datasourceConfiguration.authentication.username",
|
||||
"controlType": "INPUT_TEXT",
|
||||
"isRequired": true,
|
||||
"placeholderText": "",
|
||||
"initialValue": ""
|
||||
},
|
||||
{
|
||||
"label": "AWS Secret Access Key",
|
||||
"configProperty": "datasourceConfiguration.authentication.password",
|
||||
"controlType": "INPUT_TEXT",
|
||||
"isRequired": true,
|
||||
"placeholderText": "",
|
||||
"initialValue": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
206
app/server/appsmith-plugins/dynamoPlugin/src/test/java/com/external/plugins/DynamoPluginTest.java
vendored
Normal file
206
app/server/appsmith-plugins/dynamoPlugin/src/test/java/com/external/plugins/DynamoPluginTest.java
vendored
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
package com.external.plugins;
|
||||
|
||||
import com.appsmith.external.models.ActionConfiguration;
|
||||
import com.appsmith.external.models.ActionExecutionResult;
|
||||
import com.appsmith.external.models.AuthenticationDTO;
|
||||
import com.appsmith.external.models.DatasourceConfiguration;
|
||||
import com.appsmith.external.models.Endpoint;
|
||||
import lombok.extern.log4j.Log4j;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
|
||||
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
|
||||
import software.amazon.awssdk.regions.Region;
|
||||
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
|
||||
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
|
||||
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
|
||||
import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest;
|
||||
import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement;
|
||||
import software.amazon.awssdk.services.dynamodb.model.KeyType;
|
||||
import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput;
|
||||
import software.amazon.awssdk.services.dynamodb.model.PutItemRequest;
|
||||
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@Log4j
|
||||
public class DynamoPluginTest {
|
||||
|
||||
private final static DynamoPlugin.DynamoPluginExecutor pluginExecutor = new DynamoPlugin.DynamoPluginExecutor();
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@ClassRule
|
||||
public static GenericContainer container = new GenericContainer(CompletableFuture.completedFuture("amazon/dynamodb-local"))
|
||||
.withExposedPorts(8000);
|
||||
|
||||
private final static DatasourceConfiguration dsConfig = new DatasourceConfiguration();
|
||||
|
||||
@BeforeClass
|
||||
public static void setUp() {
|
||||
final String host = "localhost";
|
||||
final Integer port = container.getMappedPort(8000);
|
||||
|
||||
final StaticCredentialsProvider credentialsProvider = StaticCredentialsProvider.create(
|
||||
AwsBasicCredentials.create("dummy", "dummy")
|
||||
);
|
||||
|
||||
DynamoDbClient ddb = DynamoDbClient.builder()
|
||||
.region(Region.AP_SOUTH_1)
|
||||
.endpointOverride(URI.create("http://" + host + ":" + port))
|
||||
.credentialsProvider(credentialsProvider)
|
||||
.build();
|
||||
|
||||
ddb.createTable(CreateTableRequest.builder()
|
||||
.tableName("cities")
|
||||
.attributeDefinitions(
|
||||
AttributeDefinition.builder().attributeName("Id").attributeType(ScalarAttributeType.S).build()
|
||||
)
|
||||
.keySchema(
|
||||
KeySchemaElement.builder().attributeName("Id").keyType(KeyType.HASH).build()
|
||||
)
|
||||
.provisionedThroughput(
|
||||
ProvisionedThroughput.builder().readCapacityUnits(5L).writeCapacityUnits(5L).build()
|
||||
)
|
||||
.build());
|
||||
|
||||
ddb.putItem(PutItemRequest.builder()
|
||||
.tableName("cities")
|
||||
.item(Map.of(
|
||||
"Id", AttributeValue.builder().s("1").build(),
|
||||
"City", AttributeValue.builder().s("New Delhi").build()
|
||||
))
|
||||
.build());
|
||||
|
||||
ddb.putItem(PutItemRequest.builder()
|
||||
.tableName("cities")
|
||||
.item(Map.of(
|
||||
"Id", AttributeValue.builder().s("2").build(),
|
||||
"City", AttributeValue.builder().s("Bangalore").build()
|
||||
))
|
||||
.build());
|
||||
|
||||
System.out.println(ddb.listTables());
|
||||
|
||||
Endpoint endpoint = new Endpoint();
|
||||
endpoint.setHost(host);
|
||||
endpoint.setPort(port.longValue());
|
||||
dsConfig.setAuthentication(new AuthenticationDTO());
|
||||
dsConfig.getAuthentication().setUsername("dummy");
|
||||
dsConfig.getAuthentication().setPassword("dummy");
|
||||
dsConfig.getAuthentication().setDatabaseName(Region.AP_SOUTH_1.toString());
|
||||
dsConfig.setEndpoints(List.of(endpoint));
|
||||
}
|
||||
|
||||
private Mono<ActionExecutionResult> execute(String action, String jsonActionConfiguration) {
|
||||
ActionConfiguration actionConfiguration = new ActionConfiguration();
|
||||
actionConfiguration.setPath(action);
|
||||
actionConfiguration.setBody(jsonActionConfiguration);
|
||||
|
||||
return pluginExecutor
|
||||
.datasourceCreate(dsConfig)
|
||||
.flatMap(conn -> pluginExecutor.execute(conn, dsConfig, actionConfiguration));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListTables() {
|
||||
StepVerifier.create(execute("ListTables", null))
|
||||
.assertNext(result -> {
|
||||
assertNotNull(result);
|
||||
assertTrue(result.getIsExecutionSuccess());
|
||||
assertNotNull(result.getBody());
|
||||
assertArrayEquals(
|
||||
new String[]{"cities"},
|
||||
((Map<String, List<String>>) result.getBody()).get("TableNames").toArray()
|
||||
);
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetItem() {
|
||||
final String body = "{\n" +
|
||||
" \"TableName\": \"cities\",\n" +
|
||||
" \"Key\": {\n" +
|
||||
" \"Id\": {\n" +
|
||||
" \"S\": \"1\"\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}\n";
|
||||
|
||||
StepVerifier.create(execute("GetItem", body))
|
||||
.assertNext(result -> {
|
||||
assertNotNull(result);
|
||||
assertTrue(result.getIsExecutionSuccess());
|
||||
assertNotNull(result.getBody());
|
||||
final Map<String, Map<String, Object>> item = ((Map<String, Map<String, Map<String, Object>>>) result.getBody()).get("Item");
|
||||
assertEquals("New Delhi", item.get("City").get("S"));
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutItem() {
|
||||
final String body = "{\n" +
|
||||
" \"TableName\": \"cities\",\n" +
|
||||
" \"Item\": {\n" +
|
||||
" \"Id\": {\n" +
|
||||
" \"S\": \"9\"\n" +
|
||||
" },\n" +
|
||||
" \"City\": {\n" +
|
||||
" \"S\": \"Mumbai\"\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}\n";
|
||||
|
||||
StepVerifier.create(execute("PutItem", body))
|
||||
.assertNext(result -> {
|
||||
assertNotNull(result);
|
||||
assertTrue(result.getIsExecutionSuccess());
|
||||
assertNotNull(result.getBody());
|
||||
assertNotNull(((Map<String, List<String>>) result.getBody()).get("Attributes"));
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateItem() {
|
||||
final String body = "{\n" +
|
||||
" \"TableName\": \"cities\",\n" +
|
||||
" \"Key\": {\n" +
|
||||
" \"Id\": {\n" +
|
||||
" \"S\": \"2\"\n" +
|
||||
" }\n" +
|
||||
" },\n" +
|
||||
" \"UpdateExpression\": \"set City = :new_city\",\n" +
|
||||
" \"ExpressionAttributeValues\": {\n" +
|
||||
" \":new_city\": {\n" +
|
||||
" \"S\": \"Bengaluru\"\n" +
|
||||
" }\n" +
|
||||
" },\n" +
|
||||
" \"ReturnValues\": \"ALL_NEW\"\n" +
|
||||
"}\n";
|
||||
|
||||
StepVerifier.create(execute("UpdateItem", body))
|
||||
.assertNext(result -> {
|
||||
assertNotNull(result);
|
||||
assertTrue(result.getIsExecutionSuccess());
|
||||
assertNotNull(result.getBody());
|
||||
final Map<String, Map<String, Object>> attributes = ((Map<String, Map<String, Map<String, Object>>>) result.getBody()).get("Attributes");
|
||||
assertEquals("Bengaluru", attributes.get("City").get("S"));
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
}
|
||||
129
app/server/appsmith-plugins/dynamoPlugin/src/test/java/com/external/plugins/PlainToSdkTests.java
vendored
Normal file
129
app/server/appsmith-plugins/dynamoPlugin/src/test/java/com/external/plugins/PlainToSdkTests.java
vendored
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
package com.external.plugins;
|
||||
|
||||
import org.junit.Test;
|
||||
import software.amazon.awssdk.services.dynamodb.model.GetItemRequest;
|
||||
import software.amazon.awssdk.services.dynamodb.model.ListTablesRequest;
|
||||
import software.amazon.awssdk.services.dynamodb.model.PutItemRequest;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.external.plugins.DynamoPlugin.plainToSdk;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class PlainToSdkTests {
|
||||
|
||||
@Test
|
||||
public void testListTablesNull() throws Exception {
|
||||
final ListTablesRequest request = plainToSdk(
|
||||
null,
|
||||
ListTablesRequest.class
|
||||
);
|
||||
|
||||
assertNotNull(request);
|
||||
assertNull(request.exclusiveStartTableName());
|
||||
assertNull(request.limit());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListTables() throws Exception {
|
||||
final ListTablesRequest request = plainToSdk(
|
||||
Map.of(
|
||||
"ExclusiveStartTableName", "table_name",
|
||||
"Limit", 1
|
||||
),
|
||||
ListTablesRequest.class
|
||||
);
|
||||
|
||||
assertNotNull(request);
|
||||
assertEquals("table_name", request.exclusiveStartTableName());
|
||||
assertEquals(1, request.limit().intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutItem() throws Exception {
|
||||
final PutItemRequest request = plainToSdk(
|
||||
Map.of(
|
||||
"ConditionalOperator", "conditional operator value",
|
||||
"ConditionExpression", "conditional expression value",
|
||||
"ExpressionAttributeNames", Map.of(
|
||||
"#P", "Percentile"
|
||||
),
|
||||
"ExpressionAttributeValues", Map.of(
|
||||
":token1", Map.of("S", "value1"),
|
||||
":token2", Map.of("N", "42")
|
||||
),
|
||||
"Item", Map.of(
|
||||
"one", Map.of("B", "binary blob as a string"),
|
||||
"two", Map.of("BOOL", true),
|
||||
"three", Map.of("BS", List.of("binary blob 1", "binary blob 2")),
|
||||
"four", Map.of("L", List.of(
|
||||
Map.of("S", "string in list 1"),
|
||||
Map.of("S", "string in list 2"),
|
||||
Map.of("S", "string in list 3")
|
||||
)),
|
||||
"five", Map.of("M", Map.of(
|
||||
"key1", "val1",
|
||||
"key2", "val2"
|
||||
)),
|
||||
"six", Map.of("N", "1234"),
|
||||
"seven", Map.of("NS", List.of("12", "34", "56")),
|
||||
"eight", Map.of("NULL", true),
|
||||
"nine", Map.of("S", "string value")
|
||||
),
|
||||
"TableName", "table_name"
|
||||
),
|
||||
PutItemRequest.class
|
||||
);
|
||||
|
||||
assertNotNull(request);
|
||||
assertEquals("conditional operator value", request.conditionalOperatorAsString());
|
||||
assertEquals("conditional expression value", request.conditionExpression());
|
||||
assertEquals(9, request.item().size());
|
||||
assertEquals("table_name", request.tableName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetItem() throws Exception {
|
||||
final GetItemRequest request = plainToSdk(
|
||||
Map.of(
|
||||
"AttributesToGet", List.of("one", "two", "three"),
|
||||
"ConsistentRead", true,
|
||||
"ExpressionAttributeNames", Map.of(
|
||||
"#P", "Percentile"
|
||||
),
|
||||
"Key", Map.of(
|
||||
"one", Map.of("B", "binary blob as a string"),
|
||||
"two", Map.of("BOOL", true),
|
||||
"three", Map.of("BS", List.of("binary blob 1", "binary blob 2")),
|
||||
"four", Map.of("L", List.of(
|
||||
Map.of("S", "string in list 1"),
|
||||
Map.of("S", "string in list 2"),
|
||||
Map.of("S", "string in list 3")
|
||||
)),
|
||||
"five", Map.of("M", Map.of(
|
||||
"key1", "val1",
|
||||
"key2", "val2"
|
||||
)),
|
||||
"six", Map.of("N", "1234"),
|
||||
"seven", Map.of("NS", List.of("12", "34", "56")),
|
||||
"eight", Map.of("NULL", true),
|
||||
"nine", Map.of("S", "string value")
|
||||
),
|
||||
"TableName", "table_name"
|
||||
),
|
||||
GetItemRequest.class
|
||||
);
|
||||
|
||||
assertNotNull(request);
|
||||
assertArrayEquals(new String[]{"one", "two", "three"}, request.attributesToGet().toArray());
|
||||
assertTrue(request.consistentRead());
|
||||
assertEquals(9, request.key().size());
|
||||
assertEquals("table_name", request.tableName());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
plugin.id=elasticsearch-plugin
|
||||
plugin.class=com.external.plugins.ElasticSearchPlugin
|
||||
plugin.version=1.0-SNAPSHOT
|
||||
plugin.provider=tech@appsmith.com
|
||||
plugin.dependencies=
|
||||
132
app/server/appsmith-plugins/elasticSearchPlugin/pom.xml
Normal file
132
app/server/appsmith-plugins/elasticSearchPlugin/pom.xml
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.external.plugins</groupId>
|
||||
<artifactId>elasticSearchPlugin</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<name>elasticSearchPlugin</name>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<java.version>11</java.version>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
<plugin.id>elasticsearch-plugin</plugin.id>
|
||||
<plugin.class>com.external.plugins.ElasticSearchPlugin</plugin.class>
|
||||
<plugin.version>1.0-SNAPSHOT</plugin.version>
|
||||
<plugin.provider>tech@appsmith.com</plugin.provider>
|
||||
<plugin.dependencies/>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.pf4j</groupId>
|
||||
<artifactId>pf4j-spring</artifactId>
|
||||
<version>0.6.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.appsmith</groupId>
|
||||
<artifactId>interfaces</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.8</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.elasticsearch.client</groupId>
|
||||
<artifactId>elasticsearch-rest-client</artifactId>
|
||||
<version>7.9.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Test Dependencies -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.13.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-test</artifactId>
|
||||
<version>3.3.5.RELEASE</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>testcontainers</artifactId>
|
||||
<version>1.15.0-rc2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>elasticsearch</artifactId>
|
||||
<version>1.15.0-rc2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.4</version>
|
||||
<configuration>
|
||||
<minimizeJar>false</minimizeJar>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<manifestEntries>
|
||||
<Plugin-Id>${plugin.id}</Plugin-Id>
|
||||
<Plugin-Class>${plugin.class}</Plugin-Class>
|
||||
<Plugin-Version>${plugin.version}</Plugin-Version>
|
||||
<Plugin-Provider>${plugin.provider}</Plugin-Provider>
|
||||
</manifestEntries>
|
||||
</transformer>
|
||||
</transformers>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-dependencies</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<includeScope>runtime</includeScope>
|
||||
<outputDirectory>${project.build.directory}/lib</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,202 @@
|
|||
package com.external.plugins;
|
||||
|
||||
import com.appsmith.external.models.ActionConfiguration;
|
||||
import com.appsmith.external.models.ActionExecutionResult;
|
||||
import com.appsmith.external.models.AuthenticationDTO;
|
||||
import com.appsmith.external.models.DatasourceConfiguration;
|
||||
import com.appsmith.external.models.DatasourceTestResult;
|
||||
import com.appsmith.external.models.Endpoint;
|
||||
import com.appsmith.external.pluginExceptions.AppsmithPluginError;
|
||||
import com.appsmith.external.pluginExceptions.AppsmithPluginException;
|
||||
import com.appsmith.external.plugins.BasePlugin;
|
||||
import com.appsmith.external.plugins.PluginExecutor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpHost;
|
||||
import org.apache.http.StatusLine;
|
||||
import org.apache.http.auth.AuthScope;
|
||||
import org.apache.http.auth.UsernamePasswordCredentials;
|
||||
import org.apache.http.client.CredentialsProvider;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.impl.client.BasicCredentialsProvider;
|
||||
import org.apache.http.message.BasicHeader;
|
||||
import org.apache.http.nio.entity.NStringEntity;
|
||||
import org.elasticsearch.client.Request;
|
||||
import org.elasticsearch.client.Response;
|
||||
import org.elasticsearch.client.RestClient;
|
||||
import org.elasticsearch.client.RestClientBuilder;
|
||||
import org.pf4j.Extension;
|
||||
import org.pf4j.PluginWrapper;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class ElasticSearchPlugin extends BasePlugin {
|
||||
|
||||
public ElasticSearchPlugin(PluginWrapper wrapper) {
|
||||
super(wrapper);
|
||||
}
|
||||
|
||||
@Slf4j
|
||||
@Extension
|
||||
public static class ElasticSearchPluginExecutor implements PluginExecutor<RestClient> {
|
||||
|
||||
@Override
|
||||
public Mono<ActionExecutionResult> execute(RestClient client,
|
||||
DatasourceConfiguration datasourceConfiguration,
|
||||
ActionConfiguration actionConfiguration) {
|
||||
final ActionExecutionResult result = new ActionExecutionResult();
|
||||
|
||||
String body = actionConfiguration.getBody();
|
||||
|
||||
final String path = actionConfiguration.getPath();
|
||||
final Request request = new Request(actionConfiguration.getHttpMethod().toString(), path);
|
||||
ContentType contentType = ContentType.APPLICATION_JSON;
|
||||
|
||||
if (isBulkQuery(path)) {
|
||||
contentType = ContentType.create("application/x-ndjson");
|
||||
|
||||
// If body is a JSON Array, convert it to an ND-JSON string.
|
||||
if (body != null && body.trim().startsWith("[")) {
|
||||
final StringBuilder ndJsonBuilder = new StringBuilder();
|
||||
try {
|
||||
List<Object> commands = objectMapper.readValue(body, ArrayList.class);
|
||||
for (Object object : commands) {
|
||||
ndJsonBuilder.append(objectMapper.writeValueAsString(object)).append("\n");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
final String message = "Error converting array to ND-JSON: " + e.getMessage();
|
||||
log.warn(message, e);
|
||||
return Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, message));
|
||||
}
|
||||
body = ndJsonBuilder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
if (body != null) {
|
||||
request.setEntity(new NStringEntity(body, contentType));
|
||||
}
|
||||
|
||||
try {
|
||||
final String responseBody = new String(
|
||||
client.performRequest(request).getEntity().getContent().readAllBytes());
|
||||
result.setBody(objectMapper.readValue(responseBody, HashMap.class));
|
||||
} catch (IOException e) {
|
||||
final String message = "Error performing request: " + e.getMessage();
|
||||
log.warn(message, e);
|
||||
return Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, message));
|
||||
}
|
||||
|
||||
result.setIsExecutionSuccess(true);
|
||||
return Mono.just(result);
|
||||
}
|
||||
|
||||
private static boolean isBulkQuery(String path) {
|
||||
return path.split("\\?", 1)[0].matches(".*\\b_bulk$");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<RestClient> datasourceCreate(DatasourceConfiguration datasourceConfiguration) {
|
||||
final List<HttpHost> hosts = new ArrayList<>();
|
||||
|
||||
for (Endpoint endpoint : datasourceConfiguration.getEndpoints()) {
|
||||
hosts.add(new HttpHost(endpoint.getHost(), endpoint.getPort().intValue(), "http"));
|
||||
}
|
||||
|
||||
final RestClientBuilder clientBuilder = RestClient.builder(hosts.toArray(new HttpHost[]{}));
|
||||
|
||||
final AuthenticationDTO authentication = datasourceConfiguration.getAuthentication();
|
||||
if (authentication != null
|
||||
&& !StringUtils.isEmpty(authentication.getUsername())
|
||||
&& !StringUtils.isEmpty(authentication.getPassword())) {
|
||||
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
|
||||
credentialsProvider.setCredentials(
|
||||
AuthScope.ANY,
|
||||
new UsernamePasswordCredentials(authentication.getUsername(), authentication.getPassword())
|
||||
);
|
||||
|
||||
clientBuilder
|
||||
.setHttpClientConfigCallback(
|
||||
httpClientBuilder -> httpClientBuilder
|
||||
.setDefaultCredentialsProvider(credentialsProvider)
|
||||
);
|
||||
}
|
||||
|
||||
if (!CollectionUtils.isEmpty(datasourceConfiguration.getHeaders())) {
|
||||
clientBuilder.setDefaultHeaders(
|
||||
(Header[]) datasourceConfiguration.getHeaders()
|
||||
.stream()
|
||||
.map(h -> new BasicHeader(h.getKey(), h.getValue()))
|
||||
.toArray()
|
||||
);
|
||||
}
|
||||
|
||||
return Mono.just(clientBuilder.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void datasourceDestroy(RestClient client) {
|
||||
try {
|
||||
client.close();
|
||||
} catch (IOException e) {
|
||||
log.warn("Error closing connection to ElasticSearch.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> validateDatasource(DatasourceConfiguration datasourceConfiguration) {
|
||||
Set<String> invalids = new HashSet<>();
|
||||
|
||||
if (CollectionUtils.isEmpty(datasourceConfiguration.getEndpoints())) {
|
||||
invalids.add("No endpoint provided. Please provide a host:port where ElasticSearch is reachable.");
|
||||
}
|
||||
|
||||
return invalids;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<DatasourceTestResult> testDatasource(DatasourceConfiguration datasourceConfiguration) {
|
||||
return datasourceCreate(datasourceConfiguration)
|
||||
.map(client -> {
|
||||
if (client == null) {
|
||||
return new DatasourceTestResult("Null client object to ElasticSearch.");
|
||||
}
|
||||
|
||||
// This HEAD request is to check if an index exists. It response with 200 if the index exists,
|
||||
// 404 if it doesn't. We just check for either of these two.
|
||||
// Ref: https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-exists.html
|
||||
Request request = new Request("HEAD", "/potentially-missing-index?local=true");
|
||||
|
||||
final Response response;
|
||||
try {
|
||||
response = client.performRequest(request);
|
||||
} catch (IOException e) {
|
||||
return new DatasourceTestResult("Error running HEAD request: " + e.getMessage());
|
||||
}
|
||||
|
||||
final StatusLine statusLine = response.getStatusLine();
|
||||
|
||||
try {
|
||||
client.close();
|
||||
} catch (IOException e) {
|
||||
log.warn("Error closing ElasticSearch client that was made for testing.", e);
|
||||
}
|
||||
|
||||
if (statusLine.getStatusCode() != 404 && statusLine.getStatusCode() != 200) {
|
||||
return new DatasourceTestResult(
|
||||
"Unexpected response from ElasticSearch: " + statusLine);
|
||||
}
|
||||
|
||||
return new DatasourceTestResult();
|
||||
})
|
||||
.onErrorResume(error -> Mono.just(new DatasourceTestResult(error.getMessage())));
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user