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