Merge branch 'release' into fix/form
This commit is contained in:
commit
05bb520479
|
|
@ -113,7 +113,11 @@ cypress-test:
|
|||
- cp $APPSMITH_SSL_CERTIFICATE /etc/certificate/dev.appsmith.com.pem
|
||||
- cp $APPSMITH_SSL_KEY /etc/certificate/dev.appsmith.com-key.pem
|
||||
- nginx
|
||||
- yarn test:ci
|
||||
# This command configures the cypress suite to point to our custom installation of sorry-cypress that will help us parallelize our tests
|
||||
- |
|
||||
DEBUG=cypress:* $(npm bin)/cypress version
|
||||
sed -i -e 's|api_url:.*$|api_url: "https://appsmith-cypress.herokuapp.com/"|g' /builds/theappsmith/internal-tools-client/cache/Cypress/4.1.0/Cypress/resources/app/packages/server/config/app.yml
|
||||
- BUILD_ID=$CI_COMMIT_SHORT_SHA yarn test:ci
|
||||
artifacts:
|
||||
when: always
|
||||
expire_in: 1 week
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@
|
|||
"isVisible": true,
|
||||
"isDisabled": false,
|
||||
"datePickerType": "DATE_PICKER",
|
||||
"dateFormat": "DD/MM/YYYY",
|
||||
"dateFormat": "YYYY-MM-DD",
|
||||
"label": "Date",
|
||||
"widgetName": "DatePicker1",
|
||||
"defaultDate": "2020-05-29T12:02:04.074+05:30",
|
||||
|
|
@ -75,7 +75,7 @@
|
|||
"isVisible": true,
|
||||
"isDisabled": false,
|
||||
"datePickerType": "DATE_PICKER",
|
||||
"dateFormat": "DD/MM/YYYY",
|
||||
"dateFormat": "YYYY-MM-DD",
|
||||
"label": "Date",
|
||||
"widgetName": "DatePicker2",
|
||||
"defaultDate": "2020-05-29T12:02:04.074+05:30",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
const testdata = require("../../../fixtures/testdata.json");
|
||||
|
||||
describe("API Panel Test Functionality ", function() {
|
||||
it("Test API copy/Move/delete feature", function() {
|
||||
cy.log("Login Successful");
|
||||
|
|
@ -8,7 +6,6 @@ describe("API Panel Test Functionality ", function() {
|
|||
cy.CreateAPI("FirstAPI");
|
||||
cy.log("Creation of FirstAPI Action successful");
|
||||
cy.CopyAPIToHome("FirstAPI");
|
||||
cy.DeleteAPI("FirstAPI");
|
||||
cy.MoveAPIToPage();
|
||||
cy.DeleteAPI("FirstAPI");
|
||||
cy.CreateAPI("FirstAPI");
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ describe("DatePicker Widget Functionality", function() {
|
|||
);
|
||||
});
|
||||
|
||||
it("Datepicker-Claer date validation", function() {
|
||||
it("Datepicker-Clear date validation", function() {
|
||||
const today = Cypress.moment()
|
||||
.add(0, "days")
|
||||
.format("DD/MM/YYYY");
|
||||
|
|
@ -58,37 +58,37 @@ describe("DatePicker Widget Functionality", function() {
|
|||
cy.PublishtheApp();
|
||||
cy.get(publishPage.datepickerWidget + " .bp3-input").should(
|
||||
"contain.value",
|
||||
today + " 00:00",
|
||||
"",
|
||||
);
|
||||
});
|
||||
|
||||
it("DatePicker-check Required field validation", function() {
|
||||
// Check the required checkbox
|
||||
cy.CheckWidgetProperties(commonlocators.requiredCheckbox);
|
||||
cy.get(formWidgetsPage.datepickerWidget + " .bp3-label").should(
|
||||
"contain.text",
|
||||
"From Date",
|
||||
);
|
||||
cy.PublishtheApp();
|
||||
cy.get(publishPage.datepickerWidget + " .bp3-label").should(
|
||||
"contain.text",
|
||||
"From Date",
|
||||
);
|
||||
});
|
||||
|
||||
it("DatePicker-uncheck Required field validation", function() {
|
||||
// Uncheck the required checkbox
|
||||
cy.UncheckWidgetProperties(commonlocators.requiredCheckbox);
|
||||
cy.get(formWidgetsPage.datepickerWidget + " .bp3-label").should(
|
||||
"contain.text",
|
||||
"From Date",
|
||||
);
|
||||
cy.PublishtheApp();
|
||||
cy.get(publishPage.datepickerWidget + " .bp3-label").should(
|
||||
"contain.text",
|
||||
"From Date",
|
||||
);
|
||||
});
|
||||
// it("DatePicker-check Required field validation", function() {
|
||||
// // Check the required checkbox
|
||||
// cy.CheckWidgetProperties(commonlocators.requiredCheckbox);
|
||||
// cy.get(formWidgetsPage.datepickerWidget + " .bp3-label").should(
|
||||
// "contain.text",
|
||||
// "From Date",
|
||||
// );
|
||||
// cy.PublishtheApp();
|
||||
// cy.get(publishPage.datepickerWidget + " .bp3-label").should(
|
||||
// "contain.text",
|
||||
// "From Date",
|
||||
// );
|
||||
// });
|
||||
//
|
||||
// it("DatePicker-uncheck Required field validation", function() {
|
||||
// // Uncheck the required checkbox
|
||||
// cy.UncheckWidgetProperties(commonlocators.requiredCheckbox);
|
||||
// cy.get(formWidgetsPage.datepickerWidget + " .bp3-label").should(
|
||||
// "contain.text",
|
||||
// "From Date",
|
||||
// );
|
||||
// cy.PublishtheApp();
|
||||
// cy.get(publishPage.datepickerWidget + " .bp3-label").should(
|
||||
// "contain.text",
|
||||
// "From Date",
|
||||
// );
|
||||
// });
|
||||
|
||||
it("DatePicker-check Visible field validation", function() {
|
||||
// Check the visible checkbox
|
||||
|
|
|
|||
|
|
@ -38,6 +38,11 @@ Cypress.Commands.add("DeleteApp", appName => {
|
|||
"response.body.responseMeta.status",
|
||||
200,
|
||||
);
|
||||
cy.wait("@organizations").should(
|
||||
"have.nested.property",
|
||||
"response.body.responseMeta.status",
|
||||
200,
|
||||
);
|
||||
cy.get('button span[icon="chevron-down"]').should("be.visible");
|
||||
cy.get(homePage.searchInput).type(appName, { force: true });
|
||||
cy.get(homePage.appMoreIcon)
|
||||
|
|
@ -352,9 +357,7 @@ Cypress.Commands.add("MoveAPIToPage", () => {
|
|||
.first()
|
||||
.click({ force: true });
|
||||
cy.get(apiwidget.moveTo).click({ force: true });
|
||||
cy.get(
|
||||
".single-select >div:contains('".concat(pageidcopy).concat("')"),
|
||||
).click({ force: true });
|
||||
cy.get(apiwidget.home).click({ force: true });
|
||||
cy.wait("@createNewApi").should(
|
||||
"have.nested.property",
|
||||
"response.body.responseMeta.status",
|
||||
|
|
|
|||
|
|
@ -26,7 +26,11 @@ echo "Got the target: $target"
|
|||
if [ "$target" == "ci" ]; then
|
||||
# On the CI server run the tests in parallel
|
||||
# This requires the projectId and the record_key to be configured in your environment variables. By default this is defined on the CI server
|
||||
$(npm bin)/cypress run --headless --browser chrome --record --parallel --group "Electrons on Gitlab CI" --spec "cypress/integration/Smoke_TestSuite/*/*"
|
||||
echo "Got the Build ID: $BUILD_ID"
|
||||
CYPRESS_PROJECT_ID=appsmith-project $(npm bin)/cypress run --headless --browser chrome \
|
||||
--record --key "random-key" --ci-build-id $BUILD_ID \
|
||||
--parallel --group "Electrons on Gitlab CI" \
|
||||
--spec "cypress/integration/Smoke_TestSuite/*/*"
|
||||
else
|
||||
$(npm bin)/cypress run --headless --browser chrome --spec "cypress/integration/Smoke_TestSuite/*/*"
|
||||
fi
|
||||
|
|
@ -69,8 +69,8 @@ class DatePickerComponent extends React.Component<
|
|||
componentDidUpdate(prevProps: DatePickerComponentProps) {
|
||||
if (
|
||||
this.props.selectedDate !== this.state.selectedDate &&
|
||||
!moment(this.props.selectedDate).isSame(
|
||||
moment(prevProps.selectedDate),
|
||||
!moment(this.props.selectedDate, this.props.dateFormat).isSame(
|
||||
moment(prevProps.selectedDate, this.props.dateFormat),
|
||||
"seconds",
|
||||
)
|
||||
) {
|
||||
|
|
@ -106,7 +106,7 @@ class DatePickerComponent extends React.Component<
|
|||
className={this.props.isLoading ? "bp3-skeleton" : ""}
|
||||
formatDate={this.formatDate}
|
||||
parseDate={this.parseDate}
|
||||
placeholder={this.props.dateFormat}
|
||||
placeholder={"Select Date"}
|
||||
disabled={this.props.isDisabled}
|
||||
showActionsBar={true}
|
||||
timePrecision={TimePrecision.MINUTE}
|
||||
|
|
@ -114,7 +114,7 @@ class DatePickerComponent extends React.Component<
|
|||
onChange={this.onDateSelected}
|
||||
value={
|
||||
this.state.selectedDate
|
||||
? moment(this.state.selectedDate).toDate()
|
||||
? this.parseDate(this.state.selectedDate)
|
||||
: null
|
||||
}
|
||||
maxDate={maxDate.toDate()}
|
||||
|
|
@ -137,16 +137,15 @@ class DatePickerComponent extends React.Component<
|
|||
}
|
||||
|
||||
formatDate = (date: Date): string => {
|
||||
const dateFormat = "DD/MM/YYYY HH:mm";
|
||||
return moment(date).format(dateFormat);
|
||||
return moment(date).format(this.props.dateFormat);
|
||||
};
|
||||
|
||||
parseDate = (dateStr: string): Date => {
|
||||
return moment(dateStr, "DD/MM/YYYY HH:mm").toDate();
|
||||
return moment(dateStr, this.props.dateFormat).toDate();
|
||||
};
|
||||
|
||||
onDateSelected = (selectedDate: Date) => {
|
||||
const date = selectedDate ? moment(selectedDate).toISOString(true) : "";
|
||||
const date = selectedDate ? this.formatDate(selectedDate) : "";
|
||||
this.setState({ selectedDate: date });
|
||||
this.props.onDateSelected(date);
|
||||
};
|
||||
|
|
@ -156,7 +155,7 @@ interface DatePickerComponentProps extends ComponentProps {
|
|||
label: string;
|
||||
dateFormat: string;
|
||||
enableTimePicker?: boolean;
|
||||
selectedDate: string;
|
||||
selectedDate?: string;
|
||||
minDate?: Date;
|
||||
maxDate?: Date;
|
||||
timezone?: string;
|
||||
|
|
@ -167,7 +166,7 @@ interface DatePickerComponentProps extends ComponentProps {
|
|||
}
|
||||
|
||||
interface DatePickerComponentState {
|
||||
selectedDate: string;
|
||||
selectedDate?: string;
|
||||
}
|
||||
|
||||
export default DatePickerComponent;
|
||||
|
|
|
|||
|
|
@ -367,6 +367,11 @@ class DynamicAutocompleteInput extends Component<Props, State> {
|
|||
let inputValue = this.props.input.value || "";
|
||||
if (typeof inputValue === "object") {
|
||||
inputValue = JSON.stringify(inputValue, null, 2);
|
||||
} else if (
|
||||
typeof inputValue === "number" ||
|
||||
typeof inputValue === "string"
|
||||
) {
|
||||
inputValue += "";
|
||||
}
|
||||
this.editor.setValue(inputValue);
|
||||
this.startAutocomplete();
|
||||
|
|
@ -383,6 +388,11 @@ class DynamicAutocompleteInput extends Component<Props, State> {
|
|||
// Safe update of value of the editor when value updated outside the editor
|
||||
if (typeof inputValue === "object") {
|
||||
inputValue = JSON.stringify(inputValue, null, 2);
|
||||
} else if (
|
||||
typeof inputValue === "number" ||
|
||||
typeof inputValue === "string"
|
||||
) {
|
||||
inputValue += "";
|
||||
}
|
||||
if ((!!inputValue || inputValue === "") && inputValue !== editorValue) {
|
||||
this.editor.setValue(inputValue);
|
||||
|
|
@ -574,7 +584,8 @@ class DynamicAutocompleteInput extends Component<Props, State> {
|
|||
}
|
||||
const showEvaluatedValue =
|
||||
this.state.isFocused &&
|
||||
("evaluatedValue" in this.props || "dataTreePath" in this.props);
|
||||
("evaluatedValue" in this.props ||
|
||||
("dataTreePath" in this.props && !!this.props.dataTreePath));
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { WIDGET_PADDING } from "constants/WidgetConstants";
|
|||
import styled, { css } from "styled-components";
|
||||
|
||||
const EDGE_RESIZE_HANDLE_WIDTH = 10;
|
||||
const CORNER_RESIZE_HANDLE_WIDTH = 40;
|
||||
const CORNER_RESIZE_HANDLE_WIDTH = 10;
|
||||
|
||||
export const VisibilityContainer = styled.div<{
|
||||
visible: boolean;
|
||||
|
|
|
|||
|
|
@ -195,6 +195,7 @@ const views = {
|
|||
props.set(event);
|
||||
}
|
||||
}}
|
||||
dataTreePath={""}
|
||||
isValid={props.isValid}
|
||||
errorMessage={props.validationMessage}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ export interface ControlData {
|
|||
expected: string;
|
||||
evaluatedValue: any;
|
||||
validationMessage?: string;
|
||||
dataTreePath: string;
|
||||
dataTreePath?: string;
|
||||
}
|
||||
|
||||
export interface ControlFunctions {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,7 @@
|
|||
import React from "react";
|
||||
import _ from "lodash";
|
||||
import BaseControl, { ControlProps } from "./BaseControl";
|
||||
import {
|
||||
ControlWrapper,
|
||||
StyledInputGroup,
|
||||
StyledPropertyPaneButton,
|
||||
} from "./StyledControls";
|
||||
import { ControlWrapper, StyledPropertyPaneButton } from "./StyledControls";
|
||||
import styled from "constants/DefaultTheme";
|
||||
import { FormIcons } from "icons/FormIcons";
|
||||
import { AnyStyledComponent } from "styled-components";
|
||||
|
|
@ -18,24 +14,6 @@ const StyledOptionControlWrapper = styled(ControlWrapper)`
|
|||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledOptionControlInputGroup = styled(StyledInputGroup)`
|
||||
margin-right: 2px;
|
||||
width: 100%;
|
||||
margin-bottom: 0;
|
||||
&&& {
|
||||
input {
|
||||
border: none;
|
||||
color: ${props => props.theme.colors.textOnDarkBG};
|
||||
background: ${props => props.theme.colors.paneInputBG};
|
||||
&:focus {
|
||||
border: none;
|
||||
color: ${props => props.theme.colors.textOnDarkBG};
|
||||
background: ${props => props.theme.colors.paneInputBG};
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledDynamicInput = styled.div`
|
||||
width: 100%;
|
||||
&&& {
|
||||
|
|
|
|||
|
|
@ -5,20 +5,23 @@ import { EventOrValueHandler } from "redux-form";
|
|||
class CodeEditorControl extends BaseControl<ControlProps> {
|
||||
render() {
|
||||
const {
|
||||
errorMessage,
|
||||
validationMessage,
|
||||
expected,
|
||||
propertyValue,
|
||||
isValid,
|
||||
dataTreePath,
|
||||
evaluatedValue,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<DynamicAutocompleteInput
|
||||
theme={"DARK"}
|
||||
input={{ value: propertyValue, onChange: this.onChange }}
|
||||
dataTreePath={dataTreePath}
|
||||
expected={expected}
|
||||
evaluatedValue={evaluatedValue}
|
||||
meta={{
|
||||
error: isValid ? "" : errorMessage,
|
||||
error: isValid ? "" : validationMessage,
|
||||
touched: true,
|
||||
}}
|
||||
singleLine={false}
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ class DatePickerControl extends BaseControl<
|
|||
}
|
||||
|
||||
onDateSelected = (date: Date): void => {
|
||||
const selectedDate = date ? moment(date).toISOString(true) : "";
|
||||
const selectedDate = date ? moment(date).toISOString(true) : undefined;
|
||||
this.setState({ selectedDate: selectedDate });
|
||||
this.updateProperty(this.props.propertyName, selectedDate);
|
||||
};
|
||||
|
|
@ -94,7 +94,7 @@ export interface DatePickerControlProps extends ControlProps {
|
|||
}
|
||||
|
||||
interface DatePickerControlState {
|
||||
selectedDate: string;
|
||||
selectedDate?: string;
|
||||
}
|
||||
|
||||
export default DatePickerControl;
|
||||
|
|
|
|||
|
|
@ -28,13 +28,23 @@ export const DEFAULT_API_ACTION: Partial<RestAction> = {
|
|||
export const API_CONSTANT = "API";
|
||||
export const DEFAULT_PROVIDER_OPTION = "Business Software";
|
||||
export const CONTENT_TYPE = "content-type";
|
||||
export const POST_BODY_FORMATS = [
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"raw",
|
||||
|
||||
export const POST_BODY_FORMAT_OPTIONS = [
|
||||
{ label: "json", value: "application/json" },
|
||||
{
|
||||
label: "x-www-form-urlencoded",
|
||||
value: "application/x-www-form-urlencoded",
|
||||
},
|
||||
{ label: "form-data", value: "multipart/form-data" },
|
||||
{ label: "raw", value: "raw" },
|
||||
];
|
||||
|
||||
export const POST_BODY_FORMAT_OPTIONS = POST_BODY_FORMATS.map(method => ({
|
||||
label: method,
|
||||
value: method,
|
||||
}));
|
||||
export const POST_BODY_FORMAT_OPTIONS_NO_MULTI_PART = POST_BODY_FORMAT_OPTIONS.filter(
|
||||
option => {
|
||||
return option.value !== "multipart/form-data";
|
||||
},
|
||||
);
|
||||
|
||||
export const POST_BODY_FORMATS = POST_BODY_FORMAT_OPTIONS.map(option => {
|
||||
return option.value;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ const FIELD_VALUES: Record<
|
|||
isRequired: "boolean",
|
||||
isVisible: "boolean",
|
||||
isDisabled: "boolean",
|
||||
onDateSelected: "undefined",
|
||||
onDateSelected: "Function Call",
|
||||
},
|
||||
TABLE_WIDGET: {
|
||||
tableData: "Array<Object>",
|
||||
|
|
@ -30,8 +30,8 @@ const FIELD_VALUES: Record<
|
|||
exportPDF: "boolean",
|
||||
exportExcel: "boolean",
|
||||
exportCsv: "boolean",
|
||||
onRowSelected: "undefined",
|
||||
onPageChange: "undefined",
|
||||
onRowSelected: "Function Call",
|
||||
onPageChange: "Function Call",
|
||||
},
|
||||
IMAGE_WIDGET: {
|
||||
image: "string",
|
||||
|
|
@ -43,7 +43,7 @@ const FIELD_VALUES: Record<
|
|||
defaultOptionValue: "string",
|
||||
isRequired: "boolean",
|
||||
isVisible: "boolean",
|
||||
onSelectionChange: "undefined",
|
||||
onSelectionChange: "Function Call",
|
||||
},
|
||||
TABS_WIDGET: {
|
||||
tabs: "Array<{ label: string, id: string }>",
|
||||
|
|
@ -53,7 +53,6 @@ const FIELD_VALUES: Record<
|
|||
CHART_WIDGET: {
|
||||
chartName: "string",
|
||||
chartType: "LINE_CHART | BAR_CHART | PIE_CHART | COLUMN_CHART | AREA_CHART",
|
||||
singleChartData: "Array<{ x: string, y: number }>",
|
||||
xAxisName: "string",
|
||||
yAxisName: "string",
|
||||
isVisible: "boolean",
|
||||
|
|
@ -71,7 +70,7 @@ const FIELD_VALUES: Record<
|
|||
isRequired: "boolean",
|
||||
isVisible: "boolean",
|
||||
isDisabled: "boolean",
|
||||
onTextChanged: "undefined",
|
||||
onTextChanged: "Function Call",
|
||||
},
|
||||
DROP_DOWN_WIDGET: {
|
||||
label: "string",
|
||||
|
|
@ -80,7 +79,7 @@ const FIELD_VALUES: Record<
|
|||
defaultOptionValue: "string",
|
||||
isRequired: "boolean",
|
||||
isVisible: "boolean",
|
||||
onOptionChange: "boolean",
|
||||
onOptionChange: "Function Call",
|
||||
},
|
||||
FORM_BUTTON_WIDGET: {
|
||||
text: "string",
|
||||
|
|
@ -88,7 +87,7 @@ const FIELD_VALUES: Record<
|
|||
disabledWhenInvalid: "boolean",
|
||||
resetFormOnClick: "boolean",
|
||||
isVisible: "boolean",
|
||||
onClick: "boolean",
|
||||
onClick: "Function Call",
|
||||
},
|
||||
MAP_WIDGET: {
|
||||
defaultMarkers: "Array<{ lat: number, long: number }>",
|
||||
|
|
@ -96,20 +95,20 @@ const FIELD_VALUES: Record<
|
|||
enablePickLocation: "boolean",
|
||||
enableCreateMarker: "boolean",
|
||||
isVisible: "boolean",
|
||||
onMarkerClick: "undefined",
|
||||
onCreateMarker: "undefined",
|
||||
onMarkerClick: "Function Call",
|
||||
onCreateMarker: "Function Call",
|
||||
},
|
||||
BUTTON_WIDGET: {
|
||||
text: "string",
|
||||
buttonStyle: "PRIMARY_BUTTON | SECONDARY_BUTTON | DANGER_BUTTON",
|
||||
isVisible: "boolean",
|
||||
onClick: "boolean",
|
||||
onClick: "Function Call",
|
||||
},
|
||||
RICH_TEXT_EDITOR_WIDGET: {
|
||||
defaultText: "string",
|
||||
isVisible: "boolean",
|
||||
isDisabled: "boolean",
|
||||
onTextChange: "undefined",
|
||||
onTextChange: "Function Call",
|
||||
},
|
||||
FILE_PICKER_WIDGET: {
|
||||
label: "string",
|
||||
|
|
@ -120,7 +119,7 @@ const FIELD_VALUES: Record<
|
|||
isRequired: "boolean",
|
||||
isVisible: "boolean",
|
||||
uploadedFileUrls: "string",
|
||||
onFilesSelected: "undefined",
|
||||
onFilesSelected: "Function Call",
|
||||
},
|
||||
CHECKBOX_WIDGET: {
|
||||
label: "string",
|
||||
|
|
@ -128,7 +127,7 @@ const FIELD_VALUES: Record<
|
|||
isRequired: "boolean",
|
||||
isDisabled: "boolean",
|
||||
isVisible: "boolean",
|
||||
onCheckChange: "undefined",
|
||||
onCheckChange: "Function Call",
|
||||
},
|
||||
FORM_WIDGET: {
|
||||
backgroundColor: "string",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
export const PLUGIN_NAME_POSTGRES = "PostgresDbPlugin";
|
||||
export const PLUGIN_NAME_MONGODB = " MongoDBPlugin";
|
||||
export const PLUGIN_NAME_DBS = [PLUGIN_NAME_POSTGRES, PLUGIN_NAME_MONGODB];
|
||||
|
||||
export const QUERY_BODY_FIELD = "actionConfiguration.body";
|
||||
export const PLUGIN_PACKAGE_POSTGRES = "postgres-plugin";
|
||||
export const PLUGIN_PACKAGE_MONGO = "mongo-plugin";
|
||||
export const PLUGIN_PACKAGE_DBS = [
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ export interface ApiActionConfig extends ActionConfig {
|
|||
}
|
||||
|
||||
export interface QueryActionConfig extends ActionConfig {
|
||||
queryString: string;
|
||||
body: string;
|
||||
}
|
||||
|
||||
export interface Action {
|
||||
|
|
|
|||
|
|
@ -86,10 +86,10 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
|
|||
datePickerType: "DATE_PICKER",
|
||||
rows: 1,
|
||||
label: "",
|
||||
dateFormat: "DD/MM/YYYY",
|
||||
dateFormat: "DD/MM/YYYY HH:mm",
|
||||
columns: 5,
|
||||
widgetName: "DatePicker",
|
||||
defaultDate: moment().toISOString(true),
|
||||
defaultDate: moment().format("DD/MM/YYYY HH:mm"),
|
||||
},
|
||||
TABLE_WIDGET: {
|
||||
rows: 7,
|
||||
|
|
@ -131,6 +131,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
|
|||
{ label: "Vegan", value: "VEGAN" },
|
||||
],
|
||||
widgetName: "Dropdown",
|
||||
defaultOptionValue: "VEG",
|
||||
},
|
||||
CHECKBOX_WIDGET: {
|
||||
rows: 1,
|
||||
|
|
@ -147,7 +148,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
|
|||
{ id: "1", label: "Male", value: "M" },
|
||||
{ id: "2", label: "Female", value: "F" },
|
||||
],
|
||||
defaultOptionValue: "1",
|
||||
defaultOptionValue: "M",
|
||||
widgetName: "RadioGroup",
|
||||
},
|
||||
ALERT_WIDGET: {
|
||||
|
|
@ -164,6 +165,8 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
|
|||
files: [],
|
||||
label: "Select Files",
|
||||
columns: 4,
|
||||
maxNumFiles: 1,
|
||||
maxFileSize: 5,
|
||||
widgetName: "FilePicker",
|
||||
isDefaultClickDisabled: true,
|
||||
},
|
||||
|
|
@ -233,8 +236,8 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
|
|||
view: [
|
||||
{
|
||||
type: "ICON_WIDGET",
|
||||
position: { left: 15, top: 0 },
|
||||
size: { rows: 1, cols: 1 },
|
||||
position: { left: 14, top: 0 },
|
||||
size: { rows: 1, cols: 2 },
|
||||
props: {
|
||||
iconName: "cross",
|
||||
iconSize: 24,
|
||||
|
|
@ -244,7 +247,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
|
|||
{
|
||||
type: "TEXT_WIDGET",
|
||||
position: { left: 0, top: 0 },
|
||||
size: { rows: 1, cols: 15 },
|
||||
size: { rows: 1, cols: 10 },
|
||||
props: {
|
||||
text: "Modal Title",
|
||||
textStyle: "HEADING",
|
||||
|
|
|
|||
|
|
@ -264,7 +264,7 @@ const ApiEditorForm: React.FC<Props> = (props: Props) => {
|
|||
actionConfigurationHeaders={actionConfigurationHeaders}
|
||||
actionConfiguration={actionConfigurationBody}
|
||||
change={props.change}
|
||||
dataTreePath={`${actionName}.config.actionConfiguration.body`}
|
||||
dataTreePath={`${actionName}.config.actionConfiguration`}
|
||||
/>
|
||||
)}
|
||||
</RequestParamsWrapper>
|
||||
|
|
|
|||
|
|
@ -127,12 +127,30 @@ export default function Pagination(props: PaginationProps) {
|
|||
marginBottom: "6px",
|
||||
}}
|
||||
>
|
||||
Configure the Table pageNo in the API.
|
||||
1. Configure the Table pageNo in the API.
|
||||
</p>
|
||||
<ExampleApi>
|
||||
http://api.example.com/users?pageNo={"{{Table1.pageNo}}"}
|
||||
</ExampleApi>
|
||||
</CalloutComponent>
|
||||
<CalloutComponent>
|
||||
<p
|
||||
style={{
|
||||
marginBottom: "6px",
|
||||
}}
|
||||
>
|
||||
2. Enable server side pagination in Table1
|
||||
</p>
|
||||
</CalloutComponent>
|
||||
<CalloutComponent>
|
||||
<p
|
||||
style={{
|
||||
marginBottom: "6px",
|
||||
}}
|
||||
>
|
||||
3. Call this API onPageChange in Table1.
|
||||
</p>
|
||||
</CalloutComponent>
|
||||
</PaginationTypeView>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import {
|
|||
POST_BODY_FORMAT_OPTIONS,
|
||||
POST_BODY_FORMATS,
|
||||
CONTENT_TYPE,
|
||||
POST_BODY_FORMAT_OPTIONS_NO_MULTI_PART,
|
||||
} from "constants/ApiEditorConstants";
|
||||
import { API_EDITOR_FORM_NAME } from "constants/forms";
|
||||
import FormLabel from "components/editorComponents/FormLabel";
|
||||
|
|
@ -62,7 +63,7 @@ const PostBodyData = (props: Props) => {
|
|||
} = props;
|
||||
return (
|
||||
<PostbodyContainer>
|
||||
<FormLabel>{"Post Body"}</FormLabel>
|
||||
<FormLabel>{"Body"}</FormLabel>
|
||||
<DropDownContainer>
|
||||
<Select
|
||||
className={"t--apiFormPostBodyType"}
|
||||
|
|
@ -72,19 +73,15 @@ const PostBodyData = (props: Props) => {
|
|||
onChange={(displayFormatObject: any) => {
|
||||
if (
|
||||
displayFormatObject &&
|
||||
displayFormatObject.value === POST_BODY_FORMATS[2]
|
||||
displayFormatObject.value === POST_BODY_FORMATS[3]
|
||||
) {
|
||||
setDisplayFormat(apiId, {
|
||||
label: POST_BODY_FORMATS[2],
|
||||
value: POST_BODY_FORMATS[2],
|
||||
});
|
||||
|
||||
setDisplayFormat(apiId, POST_BODY_FORMAT_OPTIONS[3]);
|
||||
return;
|
||||
}
|
||||
|
||||
const elementsIndex = actionConfigurationHeaders.findIndex(
|
||||
(element: { key: string; value: string }) =>
|
||||
element.key.toLowerCase() === CONTENT_TYPE,
|
||||
element.key.trim().toLowerCase() === CONTENT_TYPE,
|
||||
);
|
||||
|
||||
if (elementsIndex >= 0 && displayFormatObject) {
|
||||
|
|
@ -92,20 +89,18 @@ const PostBodyData = (props: Props) => {
|
|||
|
||||
updatedHeaders[elementsIndex] = {
|
||||
...updatedHeaders[elementsIndex],
|
||||
key: CONTENT_TYPE,
|
||||
value: displayFormatObject.value,
|
||||
};
|
||||
|
||||
onDisplayFormatChange(updatedHeaders);
|
||||
} else {
|
||||
setDisplayFormat(apiId, {
|
||||
label: POST_BODY_FORMATS[2],
|
||||
value: POST_BODY_FORMATS[2],
|
||||
});
|
||||
setDisplayFormat(apiId, POST_BODY_FORMAT_OPTIONS[3]);
|
||||
}
|
||||
}}
|
||||
value={displayFormat}
|
||||
width={300}
|
||||
options={POST_BODY_FORMAT_OPTIONS}
|
||||
options={POST_BODY_FORMAT_OPTIONS_NO_MULTI_PART}
|
||||
/>
|
||||
</DropDownContainer>
|
||||
|
||||
|
|
@ -113,13 +108,16 @@ const PostBodyData = (props: Props) => {
|
|||
<React.Fragment>
|
||||
<JSONEditorFieldWrapper className={"t--apiFormPostBody"}>
|
||||
<DynamicTextField
|
||||
name="actionConfiguration.body"
|
||||
expected={FIELD_VALUES.API_ACTION.body}
|
||||
name="actionConfiguration.body[0]"
|
||||
height={300}
|
||||
showLineNumbers
|
||||
allowTabIndent
|
||||
singleLine={false}
|
||||
dataTreePath={`${dataTreePath}[0]`}
|
||||
placeholder={
|
||||
'{\n "name":"{{ inputName.property }}",\n "preference":"{{ dropdownName.property }}"\n}\n\n\\\\Take widget inputs using {{ }}'
|
||||
}
|
||||
dataTreePath={`${dataTreePath}.body`}
|
||||
/>
|
||||
</JSONEditorFieldWrapper>
|
||||
</React.Fragment>
|
||||
|
|
@ -128,22 +126,29 @@ const PostBodyData = (props: Props) => {
|
|||
{displayFormat?.value === POST_BODY_FORMAT_OPTIONS[1].value && (
|
||||
<React.Fragment>
|
||||
<KeyValueFieldArray
|
||||
name="actionConfiguration.body[1]"
|
||||
dataTreePath={`${dataTreePath}[1]`}
|
||||
name="actionConfiguration.bodyFormData"
|
||||
dataTreePath={`${dataTreePath}.bodyFormData`}
|
||||
label=""
|
||||
/>
|
||||
</React.Fragment>
|
||||
)}
|
||||
|
||||
{/* Commenting this till we figure the code to create a multipart request
|
||||
{displayFormat?.value === POST_BODY_FORMAT_OPTIONS[2].value && (
|
||||
<React.Fragment>
|
||||
<KeyValueFieldArray name="actionConfiguration.bodyFormData" label="" />
|
||||
</React.Fragment>
|
||||
)} */}
|
||||
|
||||
{displayFormat?.value === POST_BODY_FORMAT_OPTIONS[3].value && (
|
||||
<React.Fragment>
|
||||
<JSONEditorFieldWrapper>
|
||||
<DynamicTextField
|
||||
name="actionConfiguration.body[2]"
|
||||
name="actionConfiguration.body"
|
||||
height={300}
|
||||
allowTabIndent
|
||||
singleLine={false}
|
||||
dataTreePath={`${dataTreePath}[2]`}
|
||||
dataTreePath={`${dataTreePath}.body`}
|
||||
/>
|
||||
</JSONEditorFieldWrapper>
|
||||
</React.Fragment>
|
||||
|
|
@ -185,7 +190,7 @@ export default connect((state: AppState) => {
|
|||
|
||||
return {
|
||||
displayFormat:
|
||||
extraFormData["displayFormat"] || POST_BODY_FORMAT_OPTIONS[2],
|
||||
extraFormData["displayFormat"] || POST_BODY_FORMAT_OPTIONS[3],
|
||||
contentType,
|
||||
apiId,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -257,7 +257,7 @@ const RapidApiEditorForm: React.FC<Props> = (props: Props) => {
|
|||
/>
|
||||
{postbodyResponsePresent && (
|
||||
<PostbodyContainer>
|
||||
<FormLabel>{"Post Body"}</FormLabel>
|
||||
<FormLabel>{"Body"}</FormLabel>
|
||||
{typeof actionConfigurationBodyFormData ===
|
||||
"object" && (
|
||||
<React.Fragment>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { ControlIcons } from "icons/ControlIcons";
|
|||
import PropertyControlFactory from "utils/PropertyControlFactory";
|
||||
import { WidgetProps } from "widgets/BaseWidget";
|
||||
import { PropertyControlPropsType } from "components/propertyControls";
|
||||
import { Tooltip, Position } from "@blueprintjs/core";
|
||||
import PropertyHelpLabel from "pages/Editor/PropertyPane/PropertyHelpLabel";
|
||||
import FIELD_EXPECTED_VALUE from "constants/FieldExpectedValue";
|
||||
|
||||
type Props = {
|
||||
|
|
@ -19,57 +19,6 @@ type Props = {
|
|||
onPropertyChange: (propertyName: string, propertyValue: any) => void;
|
||||
};
|
||||
|
||||
function UnderlinedLabel({
|
||||
tooltip,
|
||||
label,
|
||||
}: {
|
||||
tooltip?: string;
|
||||
label: string;
|
||||
}) {
|
||||
const toolTipDefined = tooltip !== undefined;
|
||||
return (
|
||||
<Tooltip
|
||||
disabled={!toolTipDefined}
|
||||
content={tooltip}
|
||||
position={Position.TOP}
|
||||
hoverOpenDelay={200}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
height: "22px",
|
||||
}}
|
||||
>
|
||||
<label
|
||||
style={
|
||||
toolTipDefined
|
||||
? {
|
||||
cursor: "help",
|
||||
}
|
||||
: {}
|
||||
}
|
||||
className={`t--property-control-label`}
|
||||
>
|
||||
{label}
|
||||
</label>
|
||||
<span
|
||||
className={"underline"}
|
||||
style={
|
||||
toolTipDefined
|
||||
? {
|
||||
borderBottom: "1px dashed",
|
||||
width: "100%",
|
||||
display: "inline-block",
|
||||
position: "relative",
|
||||
top: "-15px",
|
||||
}
|
||||
: {}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
const PropertyControl = (props: Props) => {
|
||||
const {
|
||||
widgetProperties,
|
||||
|
|
@ -136,8 +85,10 @@ const PropertyControl = (props: Props) => {
|
|||
}
|
||||
>
|
||||
<ControlPropertyLabelContainer>
|
||||
<UnderlinedLabel tooltip={propertyConfig.helpText} label={label} />
|
||||
|
||||
<PropertyHelpLabel
|
||||
tooltip={propertyConfig.helpText}
|
||||
label={label}
|
||||
/>
|
||||
{isConvertible && (
|
||||
<JSToggleButton
|
||||
active={isDynamic}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
import { Position, Tooltip } from "@blueprintjs/core";
|
||||
import React from "react";
|
||||
|
||||
type Props = {
|
||||
tooltip?: string;
|
||||
label: string;
|
||||
};
|
||||
|
||||
const PropertyHelpLabel = (props: Props) => {
|
||||
const toolTipDefined = props.tooltip !== undefined;
|
||||
return (
|
||||
<Tooltip
|
||||
disabled={!toolTipDefined}
|
||||
content={props.tooltip}
|
||||
position={Position.TOP}
|
||||
hoverOpenDelay={200}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
height: "22px",
|
||||
}}
|
||||
>
|
||||
<label
|
||||
style={
|
||||
toolTipDefined
|
||||
? {
|
||||
cursor: "help",
|
||||
}
|
||||
: {}
|
||||
}
|
||||
className={`t--property-control-label`}
|
||||
>
|
||||
{props.label}
|
||||
</label>
|
||||
<span
|
||||
className={"underline"}
|
||||
style={
|
||||
toolTipDefined
|
||||
? {
|
||||
borderBottom: "1px dashed",
|
||||
width: "100%",
|
||||
display: "inline-block",
|
||||
position: "relative",
|
||||
top: "-15px",
|
||||
}
|
||||
: {}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
export default PropertyHelpLabel;
|
||||
|
|
@ -4,6 +4,7 @@ import {
|
|||
InjectedFormProps,
|
||||
Field,
|
||||
FormSubmitHandler,
|
||||
formValueSelector,
|
||||
} from "redux-form";
|
||||
import {
|
||||
GridComponent,
|
||||
|
|
@ -31,6 +32,8 @@ import "@syncfusion/ej2-react-grids/styles/material.css";
|
|||
import { Colors } from "constants/Colors";
|
||||
import JSONViewer from "./JSONViewer";
|
||||
import { RestAction } from "entities/Action";
|
||||
import { connect } from "react-redux";
|
||||
import { AppState } from "reducers";
|
||||
|
||||
const QueryFormContainer = styled.div`
|
||||
font-size: 20px;
|
||||
|
|
@ -205,7 +208,7 @@ type QueryFormProps = {
|
|||
onDeleteClick: () => void;
|
||||
onSaveClick: () => void;
|
||||
onRunClick: () => void;
|
||||
createTemplate: (template: any, name: string) => void;
|
||||
createTemplate: (template: any) => void;
|
||||
onSubmit: FormSubmitHandler<RestAction>;
|
||||
isDeleting: boolean;
|
||||
allowSave: boolean;
|
||||
|
|
@ -223,7 +226,11 @@ type QueryFormProps = {
|
|||
};
|
||||
};
|
||||
|
||||
export type StateAndRouteProps = QueryFormProps;
|
||||
type ReduxProps = {
|
||||
actionName: string;
|
||||
};
|
||||
|
||||
export type StateAndRouteProps = QueryFormProps & ReduxProps;
|
||||
|
||||
type Props = StateAndRouteProps &
|
||||
InjectedFormProps<RestAction, StateAndRouteProps>;
|
||||
|
|
@ -400,18 +407,15 @@ const QueryEditorForm: React.FC<Props> = (props: Props) => {
|
|||
{isNewQuery && showTemplateMenu ? (
|
||||
<TemplateMenu
|
||||
createTemplate={templateString => {
|
||||
const name = isSQL
|
||||
? "actionConfiguration.query.cmd"
|
||||
: "actionConfiguration.query";
|
||||
|
||||
setMenuVisibility(false);
|
||||
createTemplate(templateString, name);
|
||||
createTemplate(templateString);
|
||||
}}
|
||||
selectedPluginPackage={selectedPluginPackage}
|
||||
/>
|
||||
) : isSQL ? (
|
||||
<Field
|
||||
name="actionConfiguration.query.cmd"
|
||||
name="actionConfiguration.body"
|
||||
dataTreePath={`${props.actionName}.config.actionConfiguration.body`}
|
||||
component={DynamicAutocompleteInput}
|
||||
className="textAreaStyles"
|
||||
mode="sql-js"
|
||||
|
|
@ -419,17 +423,11 @@ const QueryEditorForm: React.FC<Props> = (props: Props) => {
|
|||
/>
|
||||
) : (
|
||||
<Field
|
||||
name="actionConfiguration.query"
|
||||
name="actionConfiguration.body"
|
||||
dataTreePath={`${props.actionName}.config.actionConfiguration.body`}
|
||||
component={DynamicAutocompleteInput}
|
||||
className="textAreaStyles"
|
||||
mode="js-js"
|
||||
normalize={(value: any) => {
|
||||
try {
|
||||
return JSON.parse(value);
|
||||
} catch (e) {
|
||||
return value;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</form>
|
||||
|
|
@ -437,7 +435,7 @@ const QueryEditorForm: React.FC<Props> = (props: Props) => {
|
|||
{dataSources.length === 0 && (
|
||||
<NoDataSourceContainer>
|
||||
<p className="font18">
|
||||
Seems like you don’t have any Datasouces to create a query
|
||||
Seems like you don’t have any Datasources to create a query
|
||||
</p>
|
||||
<Button
|
||||
onClick={() =>
|
||||
|
|
@ -492,7 +490,17 @@ const QueryEditorForm: React.FC<Props> = (props: Props) => {
|
|||
);
|
||||
};
|
||||
|
||||
export default reduxForm<RestAction, StateAndRouteProps>({
|
||||
form: QUERY_EDITOR_FORM_NAME,
|
||||
enableReinitialize: true,
|
||||
})(QueryEditorForm);
|
||||
const valueSelector = formValueSelector(QUERY_EDITOR_FORM_NAME);
|
||||
const mapStateToProps = (state: AppState) => {
|
||||
const actionName = valueSelector(state, "name");
|
||||
return {
|
||||
actionName,
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(
|
||||
reduxForm<RestAction, StateAndRouteProps>({
|
||||
form: QUERY_EDITOR_FORM_NAME,
|
||||
enableReinitialize: true,
|
||||
})(QueryEditorForm),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -23,10 +23,13 @@ import {
|
|||
getPluginIdsOfPackageNames,
|
||||
getPluginPackageFromDatasourceId,
|
||||
} from "selectors/entitiesSelector";
|
||||
import { PLUGIN_PACKAGE_DBS } from "constants/QueryEditorConstants";
|
||||
import {
|
||||
PLUGIN_PACKAGE_DBS,
|
||||
QUERY_BODY_FIELD,
|
||||
} from "constants/QueryEditorConstants";
|
||||
import { getCurrentApplication } from "selectors/applicationSelectors";
|
||||
import { ApiPaneReduxState } from "reducers/uiReducers/apiPaneReducer";
|
||||
import { RestAction } from "entities/Action";
|
||||
import { QueryAction, RestAction } from "entities/Action";
|
||||
|
||||
const EmptyStateContainer = styled.div`
|
||||
display: flex;
|
||||
|
|
@ -156,7 +159,7 @@ class QueryEditor extends React.Component<Props> {
|
|||
|
||||
const mapStateToProps = (state: AppState): any => {
|
||||
const { runErrorMessage } = state.ui.queryPane;
|
||||
const formData = getFormValues(QUERY_EDITOR_FORM_NAME)(state) as RestAction;
|
||||
const formData = getFormValues(QUERY_EDITOR_FORM_NAME)(state) as QueryAction;
|
||||
const initialValues = getFormInitialValues(QUERY_EDITOR_FORM_NAME)(
|
||||
state,
|
||||
) as RestAction;
|
||||
|
|
@ -187,8 +190,8 @@ const mapDispatchToProps = (dispatch: any): any => ({
|
|||
deleteAction: (id: string) => dispatch(deleteQuery({ id })),
|
||||
runAction: (action: RestAction, actionId: string) =>
|
||||
dispatch(executeQuery({ action, actionId })),
|
||||
createTemplate: (template: any, name: string) => {
|
||||
dispatch(change(QUERY_EDITOR_FORM_NAME, name, template));
|
||||
createTemplate: (template: any) => {
|
||||
dispatch(change(QUERY_EDITOR_FORM_NAME, QUERY_BODY_FIELD, template));
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -369,8 +369,11 @@ export function* executeActionTriggers(
|
|||
export 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(evaluateDataTree);
|
||||
log.debug({ tree });
|
||||
const { triggers } = getDynamicValue(dynamicString, tree, responseData, true);
|
||||
log.debug({ triggers });
|
||||
if (triggers && triggers.length) {
|
||||
yield all(
|
||||
triggers.map(trigger => call(executeActionTriggers, trigger, event)),
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ import { AppState } from "reducers";
|
|||
import { Property } from "api/ActionAPI";
|
||||
import { changeApi, setDatasourceFieldText } from "actions/apiPaneActions";
|
||||
import {
|
||||
API_PATH_START_WITH_SLASH_ERROR,
|
||||
FIELD_REQUIRED_ERROR,
|
||||
UNIQUE_NAME_ERROR,
|
||||
VALID_FUNCTION_NAME_ERROR,
|
||||
|
|
@ -227,37 +226,6 @@ function* changeApiSaga(actionPayload: ReduxAction<{ id: string }>) {
|
|||
(header: any) => header.key.toLowerCase() === CONTENT_TYPE,
|
||||
);
|
||||
}
|
||||
|
||||
const actionConfigurationBody = data.actionConfiguration.body;
|
||||
|
||||
data.actionConfiguration.body = [];
|
||||
if (contentType) {
|
||||
if (
|
||||
contentType.value === POST_BODY_FORMAT_OPTIONS[0].value &&
|
||||
data.actionConfiguration.body
|
||||
) {
|
||||
data.actionConfiguration.body[0] = actionConfigurationBody;
|
||||
} else if (
|
||||
contentType.value === POST_BODY_FORMAT_OPTIONS[1].value &&
|
||||
data.actionConfiguration.body
|
||||
) {
|
||||
if (typeof actionConfigurationBody !== "object") {
|
||||
try {
|
||||
data.actionConfiguration.body[1] = JSON.parse(
|
||||
actionConfigurationBody,
|
||||
);
|
||||
} catch (e) {
|
||||
data.actionConfiguration.body[2] = actionConfigurationBody;
|
||||
}
|
||||
} else {
|
||||
data.actionConfiguration.body[1] = actionConfigurationBody;
|
||||
}
|
||||
} else {
|
||||
data.actionConfiguration.body[2] = actionConfigurationBody;
|
||||
}
|
||||
} else if (!contentType && data.actionConfiguration.body) {
|
||||
data.actionConfiguration.body[2] = actionConfigurationBody;
|
||||
}
|
||||
}
|
||||
|
||||
yield put(initialize(API_EDITOR_FORM_NAME, data));
|
||||
|
|
@ -383,10 +351,7 @@ function* updateFormFields(
|
|||
value: contentType.value,
|
||||
};
|
||||
} else {
|
||||
displayFormat = {
|
||||
label: POST_BODY_FORMATS[2],
|
||||
value: POST_BODY_FORMATS[2],
|
||||
};
|
||||
displayFormat = POST_BODY_FORMAT_OPTIONS[3];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ const generateConfigWithIds = (config: PropertyConfig) => {
|
|||
return config;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
function* getLocalPropertyPaneConfigSaga() {
|
||||
// FOR DEV WORK ONLY
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -26,13 +26,14 @@ import {
|
|||
getCurrentApplicationId,
|
||||
getCurrentPageId,
|
||||
} from "selectors/editorSelectors";
|
||||
import { initialize } from "redux-form";
|
||||
import { change, initialize } from "redux-form";
|
||||
import { getAction, getActionParams, getActionTimeout } from "./ActionSagas";
|
||||
import { AppState } from "reducers";
|
||||
import ActionAPI, {
|
||||
PaginationField,
|
||||
ExecuteActionRequest,
|
||||
ActionApiResponse,
|
||||
Property,
|
||||
} from "api/ActionAPI";
|
||||
import { QUERY_CONSTANT } from "constants/QueryEditorConstants";
|
||||
import { changeQuery, deleteQuerySuccess } from "actions/queryPaneActions";
|
||||
|
|
@ -128,12 +129,40 @@ function* updateDraftsSaga() {
|
|||
}
|
||||
}
|
||||
|
||||
function* updateDynamicBindingsSaga(
|
||||
actionPayload: ReduxActionWithMeta<string, { field: string }>,
|
||||
) {
|
||||
const field = actionPayload.meta.field;
|
||||
if (field === "dynamicBindingPathList") return;
|
||||
const value = actionPayload.payload;
|
||||
const { values } = yield select(getFormData, QUERY_EDITOR_FORM_NAME);
|
||||
if (!values.id) return;
|
||||
|
||||
const isDynamic = isDynamicValue(value);
|
||||
let dynamicBindings: Property[] = values.dynamicBindingPathList || [];
|
||||
console.log({ field, value, isDynamic, dynamicBindings });
|
||||
const fieldExists = _.some(dynamicBindings, { key: field });
|
||||
|
||||
if (!isDynamic && fieldExists) {
|
||||
dynamicBindings = dynamicBindings.filter(d => d.key !== field);
|
||||
}
|
||||
if (isDynamic && !fieldExists) {
|
||||
dynamicBindings.push({ key: field });
|
||||
}
|
||||
yield put(
|
||||
change(QUERY_EDITOR_FORM_NAME, "dynamicBindingPathList", dynamicBindings),
|
||||
);
|
||||
}
|
||||
|
||||
function* formValueChangeSaga(
|
||||
actionPayload: ReduxActionWithMeta<string, { field: string; form: string }>,
|
||||
) {
|
||||
const { form } = actionPayload.meta;
|
||||
if (form !== QUERY_EDITOR_FORM_NAME) return;
|
||||
yield all([call(updateDraftsSaga)]);
|
||||
yield all([
|
||||
call(updateDynamicBindingsSaga, actionPayload),
|
||||
call(updateDraftsSaga),
|
||||
]);
|
||||
}
|
||||
|
||||
function* handleQueryCreatedSaga(actionPayload: ReduxAction<RestAction>) {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import {
|
|||
CONTENT_TYPE,
|
||||
HTTP_METHODS,
|
||||
POST_BODY_FORMATS,
|
||||
POST_BODY_FORMAT_OPTIONS,
|
||||
} from "constants/ApiEditorConstants";
|
||||
import _ from "lodash";
|
||||
|
||||
|
|
@ -44,15 +45,17 @@ export const transformRestAction = (data: any): any => {
|
|||
contentType = contentTypeHeader.value;
|
||||
}
|
||||
}
|
||||
let formatIndex = 2;
|
||||
if (POST_BODY_FORMATS.includes(contentType)) {
|
||||
formatIndex = POST_BODY_FORMATS.indexOf(contentType);
|
||||
}
|
||||
let body = "";
|
||||
let body: any = "";
|
||||
|
||||
if (action.actionConfiguration.body) {
|
||||
body = action.actionConfiguration.body[formatIndex] || undefined;
|
||||
if (
|
||||
contentType !== POST_BODY_FORMAT_OPTIONS[1].value &&
|
||||
contentType !== POST_BODY_FORMAT_OPTIONS[2].value
|
||||
) {
|
||||
action.actionConfiguration.bodyFormData = undefined;
|
||||
if (action.actionConfiguration.body)
|
||||
body = action.actionConfiguration.body || undefined;
|
||||
}
|
||||
|
||||
if (!_.isString(body)) body = JSON.stringify(body);
|
||||
action = {
|
||||
...action,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { transformRestAction } from "transformers/RestActionTransformer";
|
||||
import { PluginType, RestAction } from "entities/Action";
|
||||
import { POST_BODY_FORMAT_OPTIONS } from "constants/ApiEditorConstants";
|
||||
|
||||
// jest.mock("POST_");
|
||||
|
||||
|
|
@ -85,7 +86,7 @@ describe("Api action transformer", () => {
|
|||
...BASE_ACTION.actionConfiguration,
|
||||
httpMethod: "POST",
|
||||
headers: [{ key: "content-type", value: "application/json" }],
|
||||
body: ["{ name: 'test' }", null],
|
||||
body: "{ name: 'test' }",
|
||||
},
|
||||
};
|
||||
const output = {
|
||||
|
|
@ -108,9 +109,9 @@ describe("Api action transformer", () => {
|
|||
...BASE_ACTION.actionConfiguration,
|
||||
httpMethod: "POST",
|
||||
headers: [
|
||||
{ key: "content-type", value: "application/x-www-form-urlencoded" },
|
||||
{ key: "content-type", value: POST_BODY_FORMAT_OPTIONS[1].value },
|
||||
],
|
||||
body: [{ name: "test" }, [{ key: "hey", value: "ho" }]],
|
||||
bodyFormData: [{ key: "hey", value: "ho" }],
|
||||
},
|
||||
};
|
||||
const output = {
|
||||
|
|
@ -119,9 +120,10 @@ describe("Api action transformer", () => {
|
|||
...BASE_ACTION.actionConfiguration,
|
||||
httpMethod: "POST",
|
||||
headers: [
|
||||
{ key: "content-type", value: "application/x-www-form-urlencoded" },
|
||||
{ key: "content-type", value: POST_BODY_FORMAT_OPTIONS[1].value },
|
||||
],
|
||||
body: '[{"key":"hey","value":"ho"}]',
|
||||
body: "",
|
||||
bodyFormData: [{ key: "hey", value: "ho" }],
|
||||
},
|
||||
};
|
||||
const result = transformRestAction(input);
|
||||
|
|
@ -135,7 +137,7 @@ describe("Api action transformer", () => {
|
|||
...BASE_ACTION.actionConfiguration,
|
||||
headers: [{ key: "content-type", value: "text/html" }],
|
||||
httpMethod: "POST",
|
||||
body: [{ name: "test" }, [{ key: "hey", value: "ho" }], "raw body"],
|
||||
body: "raw body",
|
||||
},
|
||||
};
|
||||
const output = {
|
||||
|
|
|
|||
|
|
@ -160,9 +160,7 @@ export const getDynamicValue = (
|
|||
includeTriggers = false,
|
||||
): JSExecutorResult => {
|
||||
// Get the {{binding}} bound values
|
||||
const { stringSegments: stringSegments, jsSnippets } = getDynamicBindings(
|
||||
dynamicBinding,
|
||||
);
|
||||
const { stringSegments, jsSnippets } = getDynamicBindings(dynamicBinding);
|
||||
if (stringSegments.length) {
|
||||
// Get the Data Tree value of those "binding "paths
|
||||
const values = jsSnippets.map((jsSnippet, index) => {
|
||||
|
|
@ -199,32 +197,55 @@ export const getValidatedTree = (tree: any) => {
|
|||
if (entity && entity.type) {
|
||||
const parsedEntity = { ...entity };
|
||||
Object.keys(entity).forEach((property: string) => {
|
||||
const value = entity[property];
|
||||
// Pass it through parse
|
||||
const {
|
||||
parsed,
|
||||
isValid,
|
||||
message,
|
||||
} = ValidationFactory.validateWidgetProperty(
|
||||
entity.type,
|
||||
property,
|
||||
value,
|
||||
entity,
|
||||
tree,
|
||||
const hasEvaluatedValue = _.has(
|
||||
parsedEntity,
|
||||
`evaluatedValues.${property}`,
|
||||
);
|
||||
parsedEntity[property] = parsed;
|
||||
if (property !== "evaluatedValues") {
|
||||
if (!("evaluatedValues" in parsedEntity)) {
|
||||
_.set(parsedEntity, "evaluatedValues", {});
|
||||
}
|
||||
if (!(property in parsedEntity.evaluatedValues)) {
|
||||
_.set(parsedEntity, `evaluatedValues.${property}`, value);
|
||||
}
|
||||
}
|
||||
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 (!isValid) {
|
||||
_.set(parsedEntity, `invalidProps.${property}`, true);
|
||||
_.set(parsedEntity, `validationMessages.${property}`, message);
|
||||
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 = _.isUndefined(transformed)
|
||||
? value
|
||||
: transformed;
|
||||
_.set(parsedEntity, `evaluatedValues.${property}`, evaluatedValue);
|
||||
}
|
||||
|
||||
const hasValidation = _.has(parsedEntity, `invalidProps.${property}`);
|
||||
if (!hasValidation && !isValid) {
|
||||
_.set(parsedEntity, `invalidProps.${property}`, true);
|
||||
_.set(parsedEntity, `validationMessages.${property}`, message);
|
||||
}
|
||||
}
|
||||
});
|
||||
return { ...tree, [entityKey]: parsedEntity };
|
||||
|
|
@ -317,6 +338,11 @@ export const createDependencyTree = (
|
|||
);
|
||||
});
|
||||
}
|
||||
if (entity.dynamicTriggers) {
|
||||
Object.keys(entity.dynamicTriggers).forEach(prop => {
|
||||
dependencyMap[`${entityKey}.${prop}`] = [];
|
||||
});
|
||||
}
|
||||
}
|
||||
if (entity.ENTITY_TYPE === ENTITY_TYPE.ACTION) {
|
||||
if (entity.dynamicBindingPathList.length) {
|
||||
|
|
@ -548,10 +574,21 @@ function validateAndParseWidgetProperty(
|
|||
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,
|
||||
|
|
@ -560,7 +597,7 @@ function validateAndParseWidgetProperty(
|
|||
} = ValidationFactory.validateWidgetProperty(
|
||||
widget.type,
|
||||
propertyName,
|
||||
evalPropertyValue,
|
||||
valueToValidate,
|
||||
widget,
|
||||
currentTree,
|
||||
);
|
||||
|
|
@ -572,18 +609,22 @@ function validateAndParseWidgetProperty(
|
|||
_.set(widget, `invalidProps.${propertyName}`, true);
|
||||
_.set(widget, `validationMessages.${propertyName}`, message);
|
||||
}
|
||||
const parsedCache = getParsedValueCache(propertyPath);
|
||||
if (
|
||||
!equal(parsedCache.value, parsed) ||
|
||||
(cachedDependencyValues !== undefined &&
|
||||
!equal(currentDependencyValues, cachedDependencyValues))
|
||||
) {
|
||||
parsedValueCache.set(propertyPath, {
|
||||
value: parsed,
|
||||
version: Date.now(),
|
||||
});
|
||||
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;
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function isWidget(entity: DataTreeEntity): boolean {
|
||||
|
|
@ -641,6 +682,7 @@ export function dependencySortedEvaluateDataTree(
|
|||
widgetEntity,
|
||||
currentTree,
|
||||
evalPropertyValue,
|
||||
unEvalPropertyValue,
|
||||
currentDependencyValues,
|
||||
cachedDependencyValues,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -8,14 +8,14 @@ import {
|
|||
import moment from "moment";
|
||||
import {
|
||||
WIDGET_TYPE_VALIDATION_ERROR,
|
||||
NAVIGATE_TO_VALIDATION_ERROR,
|
||||
// NAVIGATE_TO_VALIDATION_ERROR,
|
||||
} from "constants/messages";
|
||||
import { modalGetter } from "components/editorComponents/actioncreator/ActionCreator";
|
||||
// import { modalGetter } from "components/editorComponents/actioncreator/ActionCreator";
|
||||
import { WidgetProps } from "widgets/BaseWidget";
|
||||
import { DataTree } from "entities/DataTree/dataTreeFactory";
|
||||
import { PageListPayload } from "constants/ReduxActionConstants";
|
||||
import { isDynamicValue } from "./DynamicBindingUtils";
|
||||
const URL_REGEX = /^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([-.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$/;
|
||||
// import { PageListPayload } from "constants/ReduxActionConstants";
|
||||
// import { isDynamicValue } from "./DynamicBindingUtils";
|
||||
// const URL_REGEX = /^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([-.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$/;
|
||||
|
||||
export const VALIDATORS: Record<ValidationType, Validator> = {
|
||||
[VALIDATION_TYPES.TEXT]: (
|
||||
|
|
@ -393,17 +393,21 @@ export const VALIDATORS: Record<ValidationType, Validator> = {
|
|||
.hour(0)
|
||||
.minute(0)
|
||||
.second(0)
|
||||
.millisecond(0)
|
||||
.toISOString(true);
|
||||
.millisecond(0);
|
||||
|
||||
if (value === undefined) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: today,
|
||||
parsed: "",
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Date`,
|
||||
};
|
||||
}
|
||||
const isValid = moment(value).isValid();
|
||||
const parsed = isValid ? moment(value).toISOString(true) : today;
|
||||
const parsed = isValid
|
||||
? props.dateFormat
|
||||
? moment(value).format(props.dateFormat)
|
||||
: moment(value).toISOString(true)
|
||||
: today;
|
||||
return {
|
||||
isValid,
|
||||
parsed,
|
||||
|
|
@ -415,6 +419,14 @@ export const VALIDATORS: Record<ValidationType, Validator> = {
|
|||
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);
|
||||
|
|
@ -440,9 +452,12 @@ export const VALIDATORS: Record<ValidationType, Validator> = {
|
|||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
return {
|
||||
isValid: true,
|
||||
parsed: value,
|
||||
isValid: false,
|
||||
parsed: undefined,
|
||||
transformed: "undefined",
|
||||
message: "Not a function call",
|
||||
};
|
||||
},
|
||||
[VALIDATION_TYPES.ARRAY_ACTION_SELECTOR]: (
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ class DatePickerWidget extends BaseWidget<DatePickerWidgetProps, WidgetState> {
|
|||
return (
|
||||
<DatePickerComponent
|
||||
label={`${this.props.label}`}
|
||||
dateFormat={"DD/MM/YYYY HH:mm"}
|
||||
dateFormat={this.props.dateFormat}
|
||||
widgetId={this.props.widgetId}
|
||||
isDisabled={this.props.isDisabled}
|
||||
datePickerType={"DATE_PICKER"}
|
||||
|
|
|
|||
|
|
@ -21,12 +21,17 @@ import Dashboard from "@uppy/dashboard";
|
|||
import shallowequal from "shallowequal";
|
||||
import _ from "lodash";
|
||||
|
||||
class FilePickerWidget extends BaseWidget<FilePickerWidgetProps, WidgetState> {
|
||||
class FilePickerWidget extends BaseWidget<
|
||||
FilePickerWidgetProps,
|
||||
FilePickerWidgetState
|
||||
> {
|
||||
uppy: any;
|
||||
|
||||
constructor(props: FilePickerWidgetProps) {
|
||||
super(props);
|
||||
this.refreshUppy(props);
|
||||
this.state = {
|
||||
version: 0,
|
||||
};
|
||||
}
|
||||
|
||||
static getPropertyValidationMap(): WidgetPropertyValidationType {
|
||||
|
|
@ -135,6 +140,7 @@ class FilePickerWidget extends BaseWidget<FilePickerWidgetProps, WidgetState> {
|
|||
this.uppy.on("upload", () => {
|
||||
this.onFilesSelected();
|
||||
});
|
||||
this.setState({ version: this.state.version + 1 });
|
||||
};
|
||||
|
||||
static getTriggerPropertyMap(): TriggerPropertiesMap {
|
||||
|
|
@ -158,8 +164,8 @@ class FilePickerWidget extends BaseWidget<FilePickerWidgetProps, WidgetState> {
|
|||
handleFileUploaded = (result: ExecutionResult) => {
|
||||
if (result.success) {
|
||||
this.updateWidgetMetaProperty(
|
||||
"uploadedFileData",
|
||||
this.props.uploadedFileUrls,
|
||||
"uploadedFileUrls",
|
||||
this.props.uploadedFileUrlPaths,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
@ -208,6 +214,10 @@ class FilePickerWidget extends BaseWidget<FilePickerWidgetProps, WidgetState> {
|
|||
}
|
||||
}
|
||||
|
||||
export interface FilePickerWidgetState extends WidgetState {
|
||||
version: number;
|
||||
}
|
||||
|
||||
export interface FilePickerWidgetProps extends WidgetProps {
|
||||
label: string;
|
||||
maxNumFiles?: number;
|
||||
|
|
@ -216,7 +226,7 @@ export interface FilePickerWidgetProps extends WidgetProps {
|
|||
allowedFileTypes: string[];
|
||||
onFilesSelected?: string;
|
||||
isRequired?: boolean;
|
||||
uploadedFileUrls?: string;
|
||||
uploadedFileUrlPaths?: string;
|
||||
}
|
||||
|
||||
export default FilePickerWidget;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user