Merge branch 'release' of github.com:appsmithorg/appsmith into release

This commit is contained in:
Arpit Mohan 2020-07-21 17:44:45 +05:30
commit 05d9dac07f
36 changed files with 541 additions and 426 deletions

View File

@ -4,7 +4,7 @@
"createBlankApiCard": ".t--createBlankApiCard",
"eachProviderCard": ".t--eachProviderCard",
"nameOfApi": ".t--nameOfApi",
"ApiNameField": ".t--action-name-edit-field span",
"ApiNameField": ".bp3-editable-text",
"addToPageBtn": ".t--addToPageBtn",
"ApiDeleteBtn": ".t--apiFormDeleteBtn",
"ApiRunBtn": ".t--apiFormRunBtn",

View File

@ -331,7 +331,7 @@ Cypress.Commands.add("CreateAPI", apiname => {
.type(apiname)
.should("have.value", apiname)
.blur();
//cy.WaitAutoSave();
cy.WaitAutoSave();
// Added because api name edit takes some time to
// reflect in api sidebar after the call passes.
cy.wait(2000);
@ -363,13 +363,17 @@ Cypress.Commands.add("EditApiName", apiname => {
Cypress.Commands.add("WaitAutoSave", () => {
// wait for save query to trigger
cy.wait(200);
cy.wait("@saveQuery");
cy.wait("@saveAction");
//cy.wait("@postExecute");
});
Cypress.Commands.add("RunAPI", () => {
cy.get(ApiEditor.ApiRunBtn).click({ force: true });
cy.wait("@postExecute");
cy.wait("@postExecute").should(
"have.nested.property",
"response.body.responseMeta.status",
200,
);
});
Cypress.Commands.add("SaveAndRunAPI", () => {
@ -1184,6 +1188,7 @@ Cypress.Commands.add("createAndFillApi", (url, parameters) => {
cy.get(ApiEditor.ApiNameField).should("be.visible");
cy.expect(response.response.body.responseMeta.success).to.eq(true);
cy.get(ApiEditor.ApiNameField)
.click()
.invoke("text")
.then(text => {
const someText = text;

View File

@ -7,8 +7,12 @@ export interface Plugin {
name: string;
type: "API" | "DB";
packageName: string;
iconLocation?: string;
uiComponent: "ApiEditorForm" | "RapidApiEditorForm" | "DbEditorForm";
allowUserDatasources?: boolean;
templates: Record<string, string>;
responseType?: "TABLE" | "JSON";
documentationLink?: string;
}
export interface DatasourceForm {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 41 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 318 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1 +1,2 @@
export const DATASOURCE_CONSTANT = "DATASOURCE";
export const APPSMITH_IP_ADDRESS = "18.223.74.85";

View File

@ -87,6 +87,10 @@ export const HelpMap = {
path: "/core-concepts/apis/taking-inputs-from-widgets",
searchKey: "Taking Inputs from Widgets",
},
DATASOURCE_FORM: {
path: "/core-concepts/connecting-to-databases",
searchKey: "Connecting to databases",
},
};
export const HelpBaseURL = "https://docs.appsmith.com";

View File

@ -2,11 +2,6 @@ import React from "react";
import styled from "styled-components";
import _ from "lodash";
import { DATASOURCE_DB_FORM } from "constants/forms";
import { REST_PLUGIN_PACKAGE_NAME } from "constants/ApiEditorConstants";
import {
PLUGIN_PACKAGE_MONGO,
PLUGIN_PACKAGE_POSTGRES,
} from "constants/QueryEditorConstants";
import { Spinner } from "@blueprintjs/core";
import { DATA_SOURCES_EDITOR_URL } from "constants/routes";
import Collapsible from "./Collapsible";
@ -14,18 +9,17 @@ import history from "utils/history";
import FormLabel from "components/editorComponents/FormLabel";
import { Icon } from "@blueprintjs/core";
import FormTitle from "./FormTitle";
import ImageAlt from "assets/images/placeholder-image.svg";
import Postgres from "assets/images/Postgress.png";
import MongoDB from "assets/images/MongoDB.png";
import RestTemplateImage from "assets/images/RestAPI.png";
import { ControlProps } from "components/formControls/BaseControl";
import CenteredWrapper from "components/designSystems/appsmith/CenteredWrapper";
import CollapsibleHelp from "components/designSystems/appsmith/help/CollapsibleHelp";
import FormControlFactory from "utils/FormControlFactory";
import { HelpBaseURL, HelpMap } from "constants/HelpConstants";
import Button from "components/editorComponents/Button";
import { Datasource } from "api/DatasourcesApi";
import { reduxForm, InjectedFormProps, Field } from "redux-form";
import { BaseButton } from "components/designSystems/blueprint/ButtonComponent";
import { APPSMITH_IP_ADDRESS } from "constants/DatasourceEditorConstants";
interface DatasourceDBEditorProps {
onSave: (formValues: Datasource) => void;
@ -42,6 +36,7 @@ interface DatasourceDBEditorProps {
loadingFormConfigs: boolean;
formConfig: [];
isNewDatasource: boolean;
pluginImage: string;
}
interface DatasourceDBEditorState {
@ -106,6 +101,17 @@ const LoadingContainer = styled(CenteredWrapper)`
height: 50%;
`;
const StyledOpenDocsIcon = styled(Icon)`
svg {
width: 12px;
height: 18px;
}
`;
const CollapsibleWrapper = styled.div`
width: max-content;
`;
class DatasourceDBEditor extends React.Component<
Props,
DatasourceDBEditorState
@ -181,19 +187,6 @@ class DatasourceDBEditor extends React.Component<
return !_.isEmpty(errors);
};
getImageSrc = (pluginPackage: string) => {
switch (pluginPackage) {
case PLUGIN_PACKAGE_POSTGRES:
return Postgres;
case PLUGIN_PACKAGE_MONGO:
return MongoDB;
case REST_PLUGIN_PACKAGE_NAME:
return RestTemplateImage;
default:
return ImageAlt;
}
};
render() {
const { loadingFormConfigs, formConfig } = this.props;
const content = this.renderDataSourceConfigForm(formConfig);
@ -287,7 +280,6 @@ class DatasourceDBEditor extends React.Component<
renderDataSourceConfigForm = (sections: any) => {
const {
selectedPluginPackage,
isSaving,
applicationId,
pageId,
@ -321,16 +313,26 @@ class DatasourceDBEditor extends React.Component<
</span>
<br />
<FormTitleContainer>
<PluginImage
src={this.getImageSrc(selectedPluginPackage)}
alt="Datasource"
/>
<PluginImage src={this.props.pluginImage} alt="Datasource" />
<Field
name="name"
component={FormTitle}
focusOnMount={this.props.isNewDatasource}
/>
</FormTitleContainer>
<CollapsibleWrapper>
<CollapsibleHelp>
<span>{`Whitelist the IP ${APPSMITH_IP_ADDRESS} on your database instance to connect to it. `}</span>
<a
href={`${HelpBaseURL}${HelpMap["DATASOURCE_FORM"].path}`}
target="_blank"
rel="noopener noreferrer"
>
{"Read more "}
<StyledOpenDocsIcon icon="document-open" />
</a>
</CollapsibleHelp>
</CollapsibleWrapper>
{!_.isNil(sections)
? _.map(sections, this.renderMainSection)
: undefined}

View File

@ -3,18 +3,12 @@ import styled from "styled-components";
import { connect } from "react-redux";
import { initialize } from "redux-form";
import { Card, Spinner } from "@blueprintjs/core";
import { getDatasourcePlugins } from "selectors/entitiesSelector";
import {
getDatasourcePlugins,
getPluginImages,
} from "selectors/entitiesSelector";
import { Plugin } from "api/PluginApi";
import { DATASOURCE_DB_FORM } from "constants/forms";
import ImageAlt from "assets/images/placeholder-image.svg";
import Postgres from "assets/images/Postgress.png";
import MongoDB from "assets/images/MongoDB.png";
import RestTemplateImage from "assets/images/RestAPI.png";
import { REST_PLUGIN_PACKAGE_NAME } from "constants/ApiEditorConstants";
import {
PLUGIN_PACKAGE_POSTGRES,
PLUGIN_PACKAGE_MONGO,
} from "constants/QueryEditorConstants";
import {
selectPlugin,
createDatasourceFromForm,
@ -120,6 +114,7 @@ interface ReduxDispatchProps {
interface ReduxStateProps {
plugins: Plugin[];
currentApplication: UserApplication;
pluginImages: Record<string, string>;
}
type Props = ReduxStateProps & DatasourceHomeScreenProps & ReduxDispatchProps;
@ -138,21 +133,8 @@ class DatasourceHomeScreen extends React.Component<Props> {
});
};
getImageSrc = (packageName: string) => {
switch (packageName) {
case PLUGIN_PACKAGE_POSTGRES:
return Postgres;
case PLUGIN_PACKAGE_MONGO:
return MongoDB;
case REST_PLUGIN_PACKAGE_NAME:
return RestTemplateImage;
default:
return ImageAlt;
}
};
render() {
const { plugins, isSaving } = this.props;
const { plugins, isSaving, pluginImages } = this.props;
return (
<DatasourceHomePage>
@ -175,7 +157,7 @@ class DatasourceHomeScreen extends React.Component<Props> {
onClick={() => this.goToCreateDatasource(plugin.id)}
>
<img
src={this.getImageSrc(plugin.packageName)}
src={pluginImages[plugin.id]}
className="dataSourceImage"
alt="Datasource"
/>
@ -193,6 +175,7 @@ class DatasourceHomeScreen extends React.Component<Props> {
const mapStateToProps = (state: AppState): ReduxStateProps => {
return {
pluginImages: getPluginImages(state),
plugins: getDatasourcePlugins(state),
currentApplication: getCurrentApplication(state),
};

View File

@ -2,7 +2,12 @@ import React from "react";
import { connect } from "react-redux";
import { getFormValues, submit } from "redux-form";
import { AppState } from "reducers";
import { getPluginPackageFromId } from "selectors/entitiesSelector";
import _ from "lodash";
import {
getPluginPackageFromId,
getPluginImages,
getDatasource,
} from "selectors/entitiesSelector";
import {
updateDatasource,
testDatasource,
@ -19,7 +24,6 @@ import { RouteComponentProps } from "react-router";
interface ReduxStateProps {
formData: Datasource;
selectedPluginPackage: string;
currentPluginId: string;
isSaving: boolean;
currentApplication: UserApplication;
isTesting: boolean;
@ -27,6 +31,8 @@ interface ReduxStateProps {
loadingFormConfigs: boolean;
isDeleting: boolean;
newDatasource: string;
pluginImages: Record<string, string>;
pluginId: string;
}
type Props = ReduxStateProps &
@ -60,12 +66,15 @@ class DataSourceEditor extends React.Component<Props> {
isDeleting,
deleteDatasource,
newDatasource,
pluginImages,
pluginId,
} = this.props;
return (
<React.Fragment>
{datasourceId ? (
<DataSourceEditorForm
pluginImage={pluginImages[pluginId]}
applicationId={this.props.match.params.applicationId}
pageId={this.props.match.params.pageId}
isSaving={isSaving}
@ -96,21 +105,23 @@ class DataSourceEditor extends React.Component<Props> {
}
}
const mapStateToProps = (state: AppState): ReduxStateProps => {
const mapStateToProps = (state: AppState, props: any): ReduxStateProps => {
const { datasourcePane } = state.ui;
const { datasources, plugins } = state.entities;
const datasource = getDatasource(state, props.match.params.datasourceId);
const { formConfigs, loadingFormConfigs } = plugins;
const formData = getFormValues(DATASOURCE_DB_FORM)(state) as Datasource;
return {
pluginImages: getPluginImages(state),
formData,
pluginId: _.get(datasource, "pluginId", ""),
selectedPluginPackage: getPluginPackageFromId(
state,
datasourcePane.selectedPlugin,
),
isSaving: datasources.loading,
isDeleting: datasources.isDeleting,
currentPluginId: datasourcePane.selectedPlugin,
currentApplication: getCurrentApplication(state),
isTesting: datasources.isTesting,
formConfig: formConfigs[datasourcePane.selectedPlugin] || [],

View File

@ -10,8 +10,7 @@ import { Colors } from "constants/Colors";
import TreeDropdown from "components/editorComponents/actioncreator/TreeDropdown";
import { BaseTextInput } from "components/designSystems/appsmith/TextInputComponent";
import { getDataSources } from "selectors/editorSelectors";
import { getPlugins } from "selectors/entitiesSelector";
import { Plugin } from "api/PluginApi";
import { getPluginImages } from "selectors/entitiesSelector";
import {
initDatasourcePane,
storeDatastoreRefs,
@ -22,16 +21,7 @@ import { ControlIcons } from "icons/ControlIcons";
import { theme } from "constants/DefaultTheme";
import { selectPlugin } from "actions/datasourceActions";
import { fetchPluginForm } from "actions/pluginActions";
import ImageAlt from "assets/images/placeholder-image.svg";
import Postgres from "assets/images/Postgress.png";
import MongoDB from "assets/images/MongoDB.png";
import RestTemplateImage from "assets/images/RestAPI.png";
import { DATA_SOURCES_EDITOR_URL } from "constants/routes";
import { REST_PLUGIN_PACKAGE_NAME } from "constants/ApiEditorConstants";
import {
PLUGIN_PACKAGE_POSTGRES,
PLUGIN_PACKAGE_MONGO,
} from "constants/QueryEditorConstants";
import { AppState } from "reducers";
import { Datasource } from "api/DatasourcesApi";
import Fuse from "fuse.js";
@ -48,7 +38,7 @@ interface ReduxDispatchProps {
interface ReduxStateProps {
dataSources: Datasource[];
plugins: Plugin[];
pluginImages: Record<string, string>;
datastoreRefs: Record<string, any>;
formConfigs: Record<string, []>;
drafts: Record<string, Datasource>;
@ -253,22 +243,6 @@ class DataSourceSidebar extends React.Component<Props, State> {
return search ? fuse.search(search) : dataSources;
};
getImageSource = (pluginId: string) => {
const { plugins } = this.props;
const plugin = plugins.find(plugin => plugin.id === pluginId);
switch (plugin?.packageName) {
case REST_PLUGIN_PACKAGE_NAME:
return RestTemplateImage;
case PLUGIN_PACKAGE_MONGO:
return MongoDB;
case PLUGIN_PACKAGE_POSTGRES:
return Postgres;
default:
return ImageAlt;
}
};
renderItem = () => {
const {
match: {
@ -277,6 +251,7 @@ class DataSourceSidebar extends React.Component<Props, State> {
datastoreRefs,
deleteDatasource,
drafts,
pluginImages,
} = this.props;
const filteredList = this.getSearchFilteredList();
@ -292,7 +267,7 @@ class DataSourceSidebar extends React.Component<Props, State> {
>
<ActionItem>
<StyledImage
src={this.getImageSource(datasource.pluginId)}
src={pluginImages[datasource.pluginId]}
className="pluginImage"
alt="Plugin Image"
/>
@ -365,7 +340,7 @@ const mapStateToProps = (state: AppState): ReduxStateProps => {
return {
formConfigs: state.entities.plugins.formConfigs,
dataSources: getDataSources(state),
plugins: getPlugins(state),
pluginImages: getPluginImages(state),
datastoreRefs: state.ui.datasourcePane.datasourceRefs,
drafts,
};

View File

@ -1,4 +1,4 @@
import React, { useEffect, useRef, useState } from "react";
import React, { useState } from "react";
import { formValueSelector, InjectedFormProps, reduxForm } from "redux-form";
import CheckboxField from "components/editorComponents/form/fields/CheckboxField";
import styled, { createGlobalStyle } from "styled-components";
@ -10,6 +10,7 @@ import {
OptionTypeBase,
SingleValueProps,
} from "react-select";
import _ from "lodash";
import history from "utils/history";
import { DATA_SOURCES_EDITOR_URL } from "constants/routes";
import TemplateMenu from "./TemplateMenu";
@ -19,7 +20,6 @@ import DropdownField from "components/editorComponents/form/fields/DropdownField
import { BaseButton } from "components/designSystems/blueprint/ButtonComponent";
import { Datasource } from "api/DatasourcesApi";
import { QUERY_EDITOR_FORM_NAME } from "constants/forms";
import { PLUGIN_PACKAGE_POSTGRES } from "constants/QueryEditorConstants";
import { Colors } from "constants/Colors";
import JSONViewer from "./JSONViewer";
import Table from "./Table";
@ -28,13 +28,23 @@ import { connect } from "react-redux";
import { AppState } from "reducers";
import ActionNameEditor from "components/editorComponents/ActionNameEditor";
import DynamicTextField from "components/editorComponents/form/fields/DynamicTextField";
import { EditorModes } from "components/editorComponents/CodeEditor/EditorConfig";
import {
EditorModes,
EditorSize,
} from "components/editorComponents/CodeEditor/EditorConfig";
import CollapsibleHelp from "components/designSystems/appsmith/help/CollapsibleHelp";
import { HelpBaseURL, HelpMap } from "constants/HelpConstants";
import {
getPluginResponseTypes,
getPluginDocumentationLinks,
} from "selectors/entitiesSelector";
const QueryFormContainer = styled.div`
font-size: 20px;
padding: 20px 32px;
width: 100%;
max-height: 93vh;
display: flex;
flex-direction: column;
height: calc(100vh - ${props => props.theme.headerHeight});
a {
font-size: 14px;
line-height: 20px;
@ -45,7 +55,7 @@ const QueryFormContainer = styled.div`
border-radius: 4px;
border: 1px solid #d0d7dd;
font-size: 14px;
height: calc(100vh / 3);
height: calc(100vh / 4);
}
.statementTextArea {
font-size: 14px;
@ -82,17 +92,6 @@ const ActionButton = styled(BaseButton)`
}
`;
const ResponseContainer = styled.div`
margin-top: 20px;
`;
const ResponseContent = styled.div`
height: calc(
100vh - (100vh / 3) - 175px - ${props => props.theme.headerHeight}
);
overflow: auto;
`;
const DropdownSelect = styled.div`
font-size: 14px;
`;
@ -194,6 +193,27 @@ const StyledCheckbox = styled(CheckboxField)`
}
`;
const StyledOpenDocsIcon = styled(Icon)`
svg {
width: 12px;
height: 18px;
}
`;
const NameWrapper = styled.div`
width: 39%;
display: flex;
justify-content: space-between;
input {
margin: 0;
box-sizing: border-box;
}
`;
const CollapsibleWrapper = styled.div`
width: 200px;
`;
type QueryFormProps = {
onDeleteClick: () => void;
onRunClick: () => void;
@ -203,10 +223,9 @@ type QueryFormProps = {
dataSources: Datasource[];
DATASOURCES_OPTIONS: any;
executedQueryData: {
body: Record<string, any>[];
body: Record<string, any>[] | string;
};
applicationId: string;
selectedPluginPackage: string | undefined;
runErrorMessage: string | undefined;
pageId: string;
location: {
@ -216,6 +235,9 @@ type QueryFormProps = {
type ReduxProps = {
actionName: string;
responseType: string | undefined;
pluginId: string;
documentationLink: string | undefined;
};
export type StateAndRouteProps = QueryFormProps & ReduxProps;
@ -235,32 +257,28 @@ const QueryEditorForm: React.FC<Props> = (props: Props) => {
applicationId,
dataSources,
executedQueryData,
selectedPluginPackage,
createTemplate,
runErrorMessage,
pluginId,
responseType,
documentationLink,
} = props;
const [showTemplateMenu, setMenuVisibility] = useState(true);
const isSQL = selectedPluginPackage === PLUGIN_PACKAGE_POSTGRES;
const isNewQuery =
new URLSearchParams(window.location.search).get("new") === "true";
let queryOutput: {
body: Record<string, any>[];
} = { body: [] };
const inputEl = useRef<HTMLInputElement>();
let error = runErrorMessage;
let output: Record<string, any>[] | null = null;
if (executedQueryData) {
if (isSQL && executedQueryData.body.length) {
queryOutput = executedQueryData;
if (_.isString(executedQueryData.body)) {
error = executedQueryData.body;
} else {
output = executedQueryData.body;
}
}
useEffect(() => {
if (isNewQuery) {
inputEl.current?.select();
}
}, [isNewQuery]);
const isSQL = responseType === "TABLE";
const isNewQuery =
new URLSearchParams(window.location.search).get("new") === "true";
const MenuList = (props: MenuListComponentProps<{ children: Node }>) => {
return (
@ -316,7 +334,9 @@ const QueryEditorForm: React.FC<Props> = (props: Props) => {
<QueryFormContainer>
<form onSubmit={handleSubmit}>
<FormRow>
<ActionNameEditor />
<NameWrapper>
<ActionNameEditor />
</NameWrapper>
<DropdownSelect>
<DropdownField
placeholder="Datasource"
@ -350,8 +370,10 @@ const QueryEditorForm: React.FC<Props> = (props: Props) => {
<ActionButton
className="t--run-query"
text="Run"
accent="primary"
filled
loading={isRunning}
accent="primary"
onClick={onRunClick}
/>
<div>
<p className="popuptext">
@ -376,55 +398,68 @@ const QueryEditorForm: React.FC<Props> = (props: Props) => {
<ActionButton
className="t--run-query"
text="Run"
filled
loading={isRunning}
accent="secondary"
accent="primary"
onClick={onRunClick}
/>
)}
</ActionButtons>
</FormRow>
<div style={{ display: "flex", justifyContent: "space-between" }}>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "flex-end",
}}
>
<p className="statementTextArea">Query Statement</p>
{isSQL ? (
<a
href="https://www.postgresql.org/docs/12/index.html"
target="_blank"
rel="noopener noreferrer"
>
PostgreSQL docs
</a>
) : (
<a
href="https://docs.mongodb.com/manual/reference/command/nav-crud/"
target="_blank"
rel="noopener noreferrer"
>
Mongo docs
</a>
{documentationLink && (
<CollapsibleWrapper>
<CollapsibleHelp>
<a
href={documentationLink}
target="_blank"
rel="noopener noreferrer"
>
{"Documentation "}
<StyledOpenDocsIcon icon="document-open" />
</a>
</CollapsibleHelp>
</CollapsibleWrapper>
)}
</div>
{isNewQuery && showTemplateMenu && selectedPluginPackage ? (
{isNewQuery && showTemplateMenu && pluginId ? (
<TemplateMenu
createTemplate={templateString => {
setMenuVisibility(false);
createTemplate(templateString);
}}
selectedPluginPackage={selectedPluginPackage}
pluginId={pluginId}
/>
) : isSQL ? (
<DynamicTextField
name="actionConfiguration.body"
dataTreePath={`${props.actionName}.config.body`}
className="textAreaStyles"
mode={EditorModes.SQL_WITH_BINDING}
/>
<div>
<DynamicTextField
size={EditorSize.EXTENDED}
name="actionConfiguration.body"
dataTreePath={`${props.actionName}.config.body`}
className="textAreaStyles"
mode={EditorModes.SQL_WITH_BINDING}
/>
</div>
) : (
<DynamicTextField
name="actionConfiguration.body"
dataTreePath={`${props.actionName}.config.body`}
className="textAreaStyles"
mode={EditorModes.JSON_WITH_BINDING}
/>
<div>
<DynamicTextField
size={EditorSize.EXTENDED}
name="actionConfiguration.body"
dataTreePath={`${props.actionName}.config.body`}
className="textAreaStyles"
mode={EditorModes.JSON_WITH_BINDING}
/>
</div>
)}
<StyledCheckbox
intent="primary"
@ -452,29 +487,20 @@ const QueryEditorForm: React.FC<Props> = (props: Props) => {
</NoDataSourceContainer>
)}
{runErrorMessage && (
{error && (
<>
<p className="statementTextArea">Query error</p>
<ErrorMessage>{runErrorMessage}</ErrorMessage>
<ErrorMessage>{error}</ErrorMessage>
</>
)}
{executedQueryData && dataSources.length && (
<ResponseContainer>
{!error && output && dataSources.length && (
<>
<p className="statementTextArea">
{executedQueryData.body.length
? "Query response"
: "No data records to display"}
{output.length ? "Query response" : "No data records to display"}
</p>
{isSQL ? (
<Table data={queryOutput.body} />
) : (
<ResponseContent>
<JSONViewer src={executedQueryData.body} />
</ResponseContent>
)}
</ResponseContainer>
{isSQL ? <Table data={output} /> : <JSONViewer src={output} />}
</>
)}
</QueryFormContainer>
);
@ -483,8 +509,15 @@ const QueryEditorForm: React.FC<Props> = (props: Props) => {
const valueSelector = formValueSelector(QUERY_EDITOR_FORM_NAME);
const mapStateToProps = (state: AppState) => {
const actionName = valueSelector(state, "name");
const pluginId = valueSelector(state, "datasource.pluginId");
const responseTypes = getPluginResponseTypes(state);
const documentationLinks = getPluginDocumentationLinks(state);
return {
actionName,
pluginId,
responseType: responseTypes[pluginId],
documentationLink: documentationLinks[pluginId],
};
};

View File

@ -11,6 +11,10 @@ const OutputContainer = styled.div`
padding: 6px;
`;
const ResponseContent = styled.div`
overflow: auto;
`;
const Record = styled(Card)`
margin: 5px;
`;
@ -41,24 +45,28 @@ class JSONOutput extends React.Component<Props> {
if (!src.length) {
return (
<OutputContainer>
<Record>
<ReactJson src={src} {...reactJsonProps} />
</Record>
</OutputContainer>
<ResponseContent>
<OutputContainer>
<Record>
<ReactJson src={src} {...reactJsonProps} />
</Record>
</OutputContainer>
</ResponseContent>
);
}
return (
<OutputContainer>
{src.map((record, index) => {
return (
<Record key={index}>
<ReactJson src={record} {...reactJsonProps} />
</Record>
);
})}
</OutputContainer>
<ResponseContent>
<OutputContainer>
{src.map((record, index) => {
return (
<Record key={index}>
<ReactJson src={record} {...reactJsonProps} />
</Record>
);
})}
</OutputContainer>
</ResponseContent>
);
}
}

View File

@ -3,24 +3,16 @@ import styled from "styled-components";
import { Icon, Card, Spinner } from "@blueprintjs/core";
import { connect } from "react-redux";
import { AppState } from "reducers";
import ImageAlt from "assets/images/placeholder-image.svg";
import Postgres from "assets/images/Postgress.png";
import MongoDB from "assets/images/MongoDB.png";
import { createNewQueryName } from "utils/AppsmithUtils";
import { Plugin } from "api/PluginApi";
import {
getPlugins,
getPluginIdsOfPackageNames,
getPluginImages,
} from "selectors/entitiesSelector";
import { ActionDataState } from "reducers/entityReducers/actionsReducer";
import { Datasource } from "api/DatasourcesApi";
import history from "utils/history";
import { createActionRequest } from "actions/actionActions";
import {
PLUGIN_PACKAGE_MONGO,
PLUGIN_PACKAGE_POSTGRES,
PLUGIN_PACKAGE_DBS,
} from "constants/QueryEditorConstants";
import { PLUGIN_PACKAGE_DBS } from "constants/QueryEditorConstants";
import { Page } from "constants/ReduxActionConstants";
import {
QUERY_EDITOR_URL_WITH_SELECTED_PAGE_ID,
@ -131,8 +123,8 @@ type QueryHomeScreenProps = {
replace: (data: string) => void;
push: (data: string) => void;
};
plugins: Plugin[];
pages: Page[];
pluginImages: Record<string, string>;
};
class QueryHomeScreen extends React.Component<QueryHomeScreenProps> {
@ -161,23 +153,6 @@ class QueryHomeScreen extends React.Component<QueryHomeScreenProps> {
}
};
getImageSrc = (dataSource: Datasource) => {
const { plugins } = this.props;
const { pluginId } = dataSource;
const plugin = plugins.find(
(plugin: { id: string }) => plugin.id === pluginId,
);
switch (plugin?.packageName) {
case PLUGIN_PACKAGE_MONGO:
return MongoDB;
case PLUGIN_PACKAGE_POSTGRES:
return Postgres;
default:
return ImageAlt;
}
};
render() {
const {
dataSources,
@ -187,15 +162,9 @@ class QueryHomeScreen extends React.Component<QueryHomeScreenProps> {
history,
location,
isCreating,
pluginImages,
} = this.props;
const validDataSources: Array<Datasource> = [];
dataSources.forEach(dataSource => {
if (pluginIds?.includes(dataSource.pluginId)) {
validDataSources.push(dataSource);
}
});
const queryParams: string = location.search;
const destinationPageId = new URLSearchParams(location.search).get(
"importTo",
@ -224,11 +193,8 @@ class QueryHomeScreen extends React.Component<QueryHomeScreenProps> {
interactive={false}
className="eachDatasourceCard"
onClick={() => {
if (validDataSources.length) {
this.handleCreateNewQuery(
validDataSources[0].id,
queryParams,
);
if (dataSources.length) {
this.handleCreateNewQuery(dataSources[0].id, queryParams);
} else {
history.push(DATA_SOURCES_EDITOR_URL(applicationId, pageId));
}
@ -237,7 +203,7 @@ class QueryHomeScreen extends React.Component<QueryHomeScreenProps> {
<Icon icon="plus" iconSize={25} className="addIcon" />
<p className="createText">Blank Query</p>
</Card>
{validDataSources.map(dataSource => {
{dataSources.map(dataSource => {
return (
<Card
interactive={false}
@ -248,7 +214,7 @@ class QueryHomeScreen extends React.Component<QueryHomeScreenProps> {
}
>
<img
src={this.getImageSrc(dataSource)}
src={pluginImages[dataSource.pluginId]}
className="dataSourceImage"
alt="Datasource"
/>
@ -271,7 +237,7 @@ class QueryHomeScreen extends React.Component<QueryHomeScreenProps> {
const mapStateToProps = (state: AppState) => ({
pluginIds: getPluginIdsOfPackageNames(state, PLUGIN_PACKAGE_DBS),
plugins: getPlugins(state),
pluginImages: getPluginImages(state),
actions: state.entities.actions,
pages: state.entities.pageList.pages,
});

View File

@ -1,5 +1,8 @@
import React from "react";
import { TableWrapper } from "components/designSystems/appsmith/TableStyledWrappers";
import {
TableWrapper,
CellWrapper,
} from "components/designSystems/appsmith/TableStyledWrappers";
import { useTable, useFlexLayout } from "react-table";
import styled from "styled-components";
@ -7,6 +10,10 @@ interface TableProps {
data: Record<string, any>[];
}
/*
* 310 = height of the table header + rest of the items (excluding editor height)
* 100vh /4 = height of the editor
*/
const StyledTableWrapped = styled(TableWrapper)`
width: 100%;
height: auto;
@ -16,7 +23,7 @@ const StyledTableWrapped = styled(TableWrapper)`
overflow: auto;
height: auto;
max-height: calc(
100vh - (100vh / 3) - 230px - ${props => props.theme.headerHeight}
100vh - (100vh / 4) - 310px - ${props => props.theme.headerHeight}
);
}
}
@ -94,7 +101,9 @@ const Table = (props: TableProps) => {
data-rowindex={index}
data-colindex={cellIndex}
>
{cell.render("Cell")}
<CellWrapper isHidden={false}>
{cell.render("Cell")}
</CellWrapper>
</div>
);
})}

View File

@ -1,6 +1,8 @@
import React from "react";
import styled from "styled-components";
import Templates from "./Templates";
import { connect } from "react-redux";
import { AppState } from "reducers";
import { getPluginTemplates } from "selectors/entitiesSelector";
const Container = styled.div`
display: flex;
@ -42,10 +44,14 @@ const Row = styled.div`
interface TemplateMenuProps {
createTemplate: (template: any) => void;
selectedPluginPackage: string;
pluginId: string;
}
type Props = TemplateMenuProps;
type ReduxProps = {
allPluginTemplates: Record<string, any>;
};
type Props = TemplateMenuProps & ReduxProps;
class TemplateMenu extends React.Component<Props> {
nameInput!: HTMLDivElement | null;
@ -54,17 +60,18 @@ class TemplateMenu extends React.Component<Props> {
this.nameInput?.focus();
}
fetchTemplate = (queryType: React.ReactText) => {
const { selectedPluginPackage } = this.props;
const allTemplates = Templates[selectedPluginPackage];
fetchTemplate = (queryType: string) => {
const { pluginId, allPluginTemplates } = this.props;
const pluginTemplates = allPluginTemplates[pluginId];
if (allTemplates) {
return allTemplates[queryType];
if (pluginTemplates) {
return pluginTemplates[queryType];
}
};
render() {
const { createTemplate } = this.props;
const { createTemplate, allPluginTemplates, pluginId } = this.props;
const pluginTemplates = allPluginTemplates[pluginId];
return (
<Container
@ -86,50 +93,36 @@ class TemplateMenu extends React.Component<Props> {
Press enter to start with a blank state or select a template.
</div>
<div style={{ marginTop: "6px" }}>
<Row
onClick={e => {
const template = this.fetchTemplate("create");
createTemplate(template);
e.stopPropagation();
}}
>
<BulletPoint />
<Item>Create</Item>
</Row>
<Row
onClick={e => {
const template = this.fetchTemplate("read");
createTemplate(template);
e.stopPropagation();
}}
>
<BulletPoint />
<Item>Read</Item>
</Row>
<Row
onClick={e => {
const template = this.fetchTemplate("delete");
createTemplate(template);
e.stopPropagation();
}}
>
<BulletPoint />
<Item>Delete</Item>
</Row>
<Row
onClick={e => {
const template = this.fetchTemplate("update");
createTemplate(template);
e.stopPropagation();
}}
>
<BulletPoint />
<Item>Update</Item>
</Row>
{Object.entries(pluginTemplates).map(template => {
const templateKey = template[0];
return (
<Row
key={templateKey}
onClick={e => {
const template = this.fetchTemplate(templateKey);
createTemplate(template);
e.stopPropagation();
}}
>
<BulletPoint />
<Item>
{templateKey.charAt(0).toUpperCase() +
templateKey.slice(1).toLowerCase()}
</Item>
</Row>
);
})}
</div>
</Container>
);
}
}
export default TemplateMenu;
const mapStateToProps = (state: AppState) => {
return {
allPluginTemplates: getPluginTemplates(state),
};
};
export default connect(mapStateToProps)(TemplateMenu);

View File

@ -1,53 +0,0 @@
import {
PLUGIN_PACKAGE_MONGO,
PLUGIN_PACKAGE_POSTGRES,
} from "constants/QueryEditorConstants";
const Templates: Record<string, any> = {
[PLUGIN_PACKAGE_MONGO]: {
create: {
insert: "users",
documents: [
{
name: "{{Input1.text}}",
email: "{{Input2.text}}",
gender: "{{Dropdown2.selectedOptionValue}}",
},
],
},
read: {
find: "users",
filter: { id: { $gte: "{{Input1.text}}" } },
sort: { id: 1 },
limit: 10,
},
delete: {
delete: "users",
deletes: [{ q: { id: "{{Table1.selectedRow.id}}" } }],
},
update: {
update: "users",
updates: [
{
q: { id: "{{Table1.selectedRow.id}}" },
u: {
name: "{{Input1.text}}",
email: "{{Input2.text}}",
},
},
],
},
},
[PLUGIN_PACKAGE_POSTGRES]: {
create: `INSERT INTO users(name, gender)
VALUES ('{{Dropdown1.selectedOptionValue}}', '{{Input2.text}}');`,
read:
"SELECT * FROM users where name like '%{{Input1.text}}%' ORDER BY id LIMIT 10",
delete: `DELETE FROM users WHERE id={{Table1.selectedRow.id}}`,
update: `UPDATE users
Set status='{{Dropdown1.selectedOptionValue}}'
WHERE id={{Table1.selectedRow.id}};`,
},
};
export default Templates;

View File

@ -1,20 +0,0 @@
import { Plugin } from "api/PluginApi";
import {
PLUGIN_PACKAGE_MONGO,
PLUGIN_PACKAGE_POSTGRES,
} from "constants/QueryEditorConstants";
import ImageAlt from "assets/images/placeholder-image.svg";
import Postgres from "assets/images/Postgress.png";
import MongoDB from "assets/images/MongoDB.png";
export const getPluginImage = (plugins: Plugin[], pluginId?: string) => {
const plugin = plugins.find(plugin => plugin.id === pluginId);
switch (plugin?.packageName) {
case PLUGIN_PACKAGE_MONGO:
return MongoDB;
case PLUGIN_PACKAGE_POSTGRES:
return Postgres;
default:
return ImageAlt;
}
};

View File

@ -2,7 +2,6 @@ import React from "react";
import { RouteComponentProps } from "react-router";
import { connect } from "react-redux";
import { getFormValues, change } from "redux-form";
import _ from "lodash";
import styled from "styled-components";
import { QueryEditorRouteParams } from "constants/routes";
import QueryEditorForm from "./Form";
@ -16,15 +15,15 @@ import { Datasource } from "api/DatasourcesApi";
import { QueryPaneReduxState } from "reducers/uiReducers/queryPaneReducer";
import {
getPluginIdsOfPackageNames,
getPluginPackageFromDatasourceId,
getPlugins,
getPluginImages,
getDBDatasources,
} from "selectors/entitiesSelector";
import {
PLUGIN_PACKAGE_DBS,
QUERY_BODY_FIELD,
} from "constants/QueryEditorConstants";
import { QueryAction } from "entities/Action";
import { getPluginImage } from "pages/Editor/QueryEditor/helpers";
import Spinner from "components/editorComponents/Spinner";
import CenteredWrapper from "components/designSystems/appsmith/CenteredWrapper";
@ -52,10 +51,10 @@ type ReduxStateProps = {
runErrorMessage: Record<string, string>;
pluginIds: Array<string> | undefined;
executedQueryData: any;
selectedPluginPackage: string | undefined;
isCreating: boolean;
isMoving: boolean;
isCopying: boolean;
pluginImages: Record<string, string>;
};
type StateAndRouteProps = RouteComponentProps<QueryEditorRouteParams>;
@ -82,9 +81,9 @@ class QueryEditor extends React.Component<Props> {
match: {
params: { queryId },
},
pluginImages,
pluginIds,
executedQueryData,
selectedPluginPackage,
isCreating,
isMoving,
isCopying,
@ -107,17 +106,10 @@ class QueryEditor extends React.Component<Props> {
}
const { isRunning, isDeleting } = queryPane;
const validDataSources: Array<Datasource> = [];
dataSources.forEach(dataSource => {
if (pluginIds?.includes(dataSource.pluginId)) {
validDataSources.push(dataSource);
}
});
const DATASOURCES_OPTIONS = validDataSources.map(dataSource => ({
const DATASOURCES_OPTIONS = dataSources.map(dataSource => ({
label: dataSource.name,
value: dataSource.id,
image: getPluginImage(this.props.plugins, dataSource.pluginId),
image: pluginImages[dataSource.pluginId],
}));
return (
@ -134,7 +126,6 @@ class QueryEditor extends React.Component<Props> {
dataSources={dataSources}
createTemplate={createTemplate}
DATASOURCES_OPTIONS={DATASOURCES_OPTIONS}
selectedPluginPackage={selectedPluginPackage}
executedQueryData={executedQueryData[queryId]}
runErrorMessage={runErrorMessage[queryId]}
/>
@ -156,21 +147,16 @@ class QueryEditor extends React.Component<Props> {
const mapStateToProps = (state: AppState): ReduxStateProps => {
const { runErrorMessage } = state.ui.queryPane;
const formData = getFormValues(QUERY_EDITOR_FORM_NAME)(state) as QueryAction;
const datasourceId = _.get(formData, "datasource.id");
const selectedPluginPackage = getPluginPackageFromDatasourceId(
state,
datasourceId,
);
return {
pluginImages: getPluginImages(state),
plugins: getPlugins(state),
runErrorMessage,
pluginIds: getPluginIdsOfPackageNames(state, PLUGIN_PACKAGE_DBS),
dataSources: getDataSources(state),
dataSources: getDBDatasources(state),
executedQueryData: state.ui.queryPane.runQuerySuccessData,
queryPane: state.ui.queryPane,
formData,
selectedPluginPackage,
isCreating: state.ui.apiPane.isCreating,
isMoving: state.ui.apiPane.isMoving,
isCopying: state.ui.apiPane.isCopying,

View File

@ -9,8 +9,6 @@ import EditorSidebar from "pages/Editor/EditorSidebar";
import { QUERY_CONSTANT } from "constants/QueryEditorConstants";
import { QueryEditorRouteParams } from "constants/routes";
import { Datasource } from "api/DatasourcesApi";
import { getPluginImage } from "pages/Editor/QueryEditor/helpers";
import { Plugin } from "api/PluginApi";
import {
createActionRequest,
moveActionRequest,
@ -18,7 +16,7 @@ import {
deleteAction,
} from "actions/actionActions";
import { changeQuery, initQueryPane } from "actions/queryPaneActions";
import { getQueryActions, getPlugins } from "selectors/entitiesSelector";
import { getQueryActions, getPluginImages } from "selectors/entitiesSelector";
import { getNextEntityName } from "utils/AppsmithUtils";
import { getDataSources } from "selectors/editorSelectors";
import { QUERY_EDITOR_URL_WITH_SELECTED_PAGE_ID } from "constants/routes";
@ -53,7 +51,7 @@ const StyledImage = styled.img`
`;
interface ReduxStateProps {
plugins: Plugin[];
pluginImages: Record<string, string>;
queries: ActionDataState;
apiPane: ApiPaneReduxState;
actions: ActionDataState;
@ -128,10 +126,12 @@ class QuerySidebar extends React.Component<Props> {
};
renderItem = (query: RestAction) => {
const { pluginImages } = this.props;
return (
<ActionItem>
<StyledImage
src={getPluginImage(this.props.plugins, query.datasource.pluginId)}
src={pluginImages[query.datasource?.pluginId ?? ""]}
className="pluginImage"
alt="Plugin Image"
/>
@ -168,7 +168,7 @@ class QuerySidebar extends React.Component<Props> {
}
const mapStateToProps = (state: AppState): ReduxStateProps => ({
plugins: getPlugins(state),
pluginImages: getPluginImages(state),
queries: getQueryActions(state),
apiPane: state.ui.apiPane,
actions: state.entities.actions,

View File

@ -20,7 +20,6 @@ import {
} from "selectors/editorSelectors";
import { initialize } from "redux-form";
import { AppState } from "reducers";
import { QUERY_CONSTANT } from "constants/QueryEditorConstants";
import { changeQuery } from "actions/queryPaneActions";
import { getAction } from "selectors/entitiesSelector";
import { RestAction } from "entities/Action";

View File

@ -9,6 +9,7 @@ import { createSelector } from "reselect";
import { Datasource } from "api/DatasourcesApi";
import { Action } from "entities/Action";
import { find } from "lodash";
import ImageAlt from "assets/images/placeholder-image.svg";
export const getEntities = (state: AppState): AppState["entities"] =>
state.entities;
@ -124,6 +125,23 @@ export const getDatasourceDraft = (state: AppState, id: string) => {
export const getPlugins = (state: AppState) => state.entities.plugins.list;
export const getDBPlugins = createSelector(getPlugins, plugins =>
plugins.filter(plugin => plugin.type === QUERY_CONSTANT),
);
export const getDBDatasources = createSelector(
getDBPlugins,
getEntities,
(dbPlugins, entities) => {
const datasources = entities.datasources.list;
const dbPluginIds = dbPlugins.map(plugin => plugin.id);
return datasources.filter(datasource =>
dbPluginIds.includes(datasource.pluginId),
);
},
);
export const getQueryName = (state: AppState, actionId: string): string => {
const action = state.entities.actions.find((action: ActionData) => {
return action.config.id === actionId;
@ -137,6 +155,7 @@ export const getQueryActions = (state: AppState): ActionDataState => {
return action.config.pluginType === QUERY_CONSTANT;
});
};
const getCurrentPageId = (state: AppState) =>
state.entities.pageList.currentPageId;
@ -144,6 +163,49 @@ export const getDatasourcePlugins = createSelector(getPlugins, plugins => {
return plugins.filter(plugin => plugin?.allowUserDatasources ?? true);
});
export const getPluginImages = createSelector(getPlugins, plugins => {
const pluginImages: Record<string, string> = {};
plugins.forEach(plugin => {
pluginImages[plugin.id] = plugin?.iconLocation ?? ImageAlt;
});
return pluginImages;
});
export const getPluginTemplates = createSelector(getPlugins, plugins => {
const pluginTemplates: Record<string, any> = {};
plugins.forEach(plugin => {
pluginTemplates[plugin.id] = plugin.templates;
});
return pluginTemplates;
});
export const getPluginResponseTypes = createSelector(getPlugins, plugins => {
const pluginResponseTypes: Record<string, any> = {};
plugins.forEach(plugin => {
pluginResponseTypes[plugin.id] = plugin.responseType;
});
return pluginResponseTypes;
});
export const getPluginDocumentationLinks = createSelector(
getPlugins,
plugins => {
const pluginDocumentationLinks: Record<string, string | undefined> = {};
plugins.forEach(plugin => {
pluginDocumentationLinks[plugin.id] = plugin.documentationLink;
});
return pluginDocumentationLinks;
},
);
export const getActionsForCurrentPage = createSelector(
getCurrentPageId,
getActions,
@ -162,6 +224,7 @@ export const getActionResponses = createSelector(getActions, actions => {
return responses;
});
export const getAction = (
state: AppState,
actionId: string,

58
deploy/aws/base-install.sh Executable file
View File

@ -0,0 +1,58 @@
#!/bin/bash
set -o errexit
if [[ $EUID > 0 ]]; then
echo "Please run with sudo." >&2
exit 1
fi
install_package() {
sudo apt-get -y update --quiet
sudo apt-get install -y ntp bc python3-pip --quiet
pip3 install boto3
sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common virtualenv python3-setuptools --quiet
# Installing docker
sudo apt-get -y --quiet install gnupg-agent
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
sudo apt-get -y update --quiet
sudo apt-get -y install docker-ce docker-ce-cli containerd.io --quiet
# Installing docker compose
if [ ! -f /usr/bin/docker-compose ];then
echo "Installing docker-compose"
sudo curl -L "https://github.com/docker/compose/releases/download/1.26.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
fi
}
install_package
#Download boot.sh and schedule at boot time.
app_path="/home/ubuntu/appsmith"
script_path="script"
boot_script_path=$app_path/$script_path
boot_file_name="boot.sh"
config_ssl_file_name="configure-ssl.sh"
mkdir -p $boot_script_path
sudo chown -R ubuntu:ubuntu $app_path
cd $boot_script_path
sudo curl -O https://raw.githubusercontent.com/appsmithorg/appsmith/release/deploy/configure-ssl.sh
sudo chown ubuntu:ubuntu $boot_script_path/$config_ssl_file_name && sudo chmod +x $boot_script_path/$config_ssl_file_name
sudo curl -O https://raw.githubusercontent.com/appsmithorg/appsmith/feature/deploy-script/deploy/aws/boot.sh
sudo chown ubuntu:ubuntu $boot_script_path/$boot_file_name && sudo chmod +x $boot_script_path/$boot_file_name
USER="ubuntu"
CRON_FILE="/var/spool/cron/crontabs/$USER"
echo "@reboot /bin/bash $boot_script_path/$boot_file_name" >> $CRON_FILE
sudo chmod 0600 $CRON_FILE

96
deploy/aws/boot.sh Executable file
View File

@ -0,0 +1,96 @@
#!/bin/bash
set -o errexit
# Check if Lock File exists, if not create it and set trap on exit
if { set -C; 2>/dev/null >/home/ubuntu/.appsmith.lock; }; then
trap "rm -f /home/ubuntu/.appsmith.lock" EXIT
else
exit
fi
start_docker() {
if [ `sudo systemctl is-active docker.service` == "inactive" ];then
echo "Starting docker"
sudo systemctl start docker.service
fi
}
# generate random string
generate_random_string() {
value=`cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 10 | head -n 1`
echo $value
}
start_docker
install_dir="/home/ubuntu/appsmith"
if [ ! -d $install_dir ];then
mkdir -p $install_dir
fi
chown -R ubuntu:ubuntu $install_dir
mongo_host="mongo"
mongo_database="appsmith"
mongo_root_user=$( generate_random_string )
mongo_root_password=$( generate_random_string )
user_encryption_password=$( generate_random_string )
user_encryption_salt=$( generate_random_string )
custom_domain=""
NGINX_SSL_CMNT=""
if [[ -z $custom_domain ]]; then
NGINX_SSL_CMNT="#"
fi
script_dir="/script"
mkdir -p "$install_dir/$script_dir"
chown -R ubuntu:ubuntu "$install_dir/$script_dir"
cd $install_dir/$script_dir
mkdir -p template
cd template
echo $PWD
curl -O https://raw.githubusercontent.com/appsmithorg/appsmith/release/deploy/template/docker-compose.yml.sh
curl -O https://raw.githubusercontent.com/appsmithorg/appsmith/release/deploy/template/init-letsencrypt.sh.sh
curl -O https://raw.githubusercontent.com/appsmithorg/appsmith/release/deploy/template/mongo-init.js.sh
curl -O https://raw.githubusercontent.com/appsmithorg/appsmith/release/deploy/template/docker.env.sh
curl -O https://raw.githubusercontent.com/appsmithorg/appsmith/release/deploy/template/nginx_app.conf.sh
curl -O https://raw.githubusercontent.com/appsmithorg/appsmith/release/deploy/template/encryption.env.sh
cd ..
echo $PWD
# Role - Folder
for directory_name in nginx certbot mongo/db opa/config
do
if [[ ! -d "$install_dir/data/$directory_name" ]];then
mkdir -p "$install_dir/data/$directory_name"
fi
done
echo "Generating the configuration files from the templates"
. ./template/nginx_app.conf.sh
. ./template/docker-compose.yml.sh
. ./template/mongo-init.js.sh
. ./template/docker.env.sh
. ./template/encryption.env.sh
declare -A fileInfo
fileInfo[/data/nginx/app.conf.template]="nginx_app.conf"
fileInfo[/docker-compose.yml]="docker-compose.yml"
fileInfo[/data/mongo/init.js]="mongo-init.js"
fileInfo[/docker.env]="docker.env"
fileInfo[/encryption.env]="encryption.env"
for f in ${!fileInfo[@]}
do
mv -f ${fileInfo[$f]} $install_dir/$f
done
cd $install_dir
echo "Pull Images: $PWD"
sudo docker-compose pull
echo "docker compose $PWD"
sudo docker-compose -f docker-compose.yml up -d --remove-orphans

19
deploy/aws/configure-ssl.sh Executable file
View File

@ -0,0 +1,19 @@
#!/bin/bash
set -o errexit
read -p 'Enter your domain / subdomain name (example.com / app.example.com): ' custom_domain
NGINX_SSL_CMNT=""
install_dir="/home/ubuntu/appsmith"
TEMPLATE_PATH="$install_dir/script/template"
. $TEMPLATE_PATH/nginx_app.conf.sh
. $TEMPLATE_PATH/init-letsencrypt.sh.sh
chmod 0755 init-letsencrypt.sh
mv -f app.conf $install_dir/data/nginx/app.conf
mv -f init-letsencrypt.sh $install_dir/init-letsencrypt.sh
cd $install_dir
sudo ./init-letsencrypt.sh

View File

@ -4,7 +4,7 @@ if [ ! -f docker-compose.yml ]; then
touch docker-compose.yml
fi
cat > docker-compose.yml << EOF
cat >| docker-compose.yml << EOF
version: "3.7"
services:

View File

@ -4,7 +4,7 @@ if [ ! -f docker-compose.yml ]; then
touch docker-compose.yml
fi
cat > docker.env << EOF
cat >| docker.env << EOF
# Read our documentation on how to configure these features
# https://docs.appsmith.com/v/v1.1/enabling-3p-services

View File

@ -4,8 +4,8 @@ if [ ! -f encryption.env ]; then
touch encryption.env
fi
cat > encryption.env << EOF
cat >| encryption.env << EOF
APPSMITH_ENCRYPTION_PASSWORD=$user_encryption_password
APPSMITH_ENCRYPTION_SALT=$user_encryption_salt
EOF
EOF

View File

@ -6,7 +6,7 @@ fi
cat > init-letsencrypt.sh << EOF
cat >| init-letsencrypt.sh << EOF
#!/bin/bash
if ! [ -x "\$(command -v docker-compose)" ]; then

View File

@ -6,7 +6,7 @@ fi
cat > mongo-init.js << EOF
cat >| mongo-init.js << EOF
let error = false
print("**** Going to start Mongo seed ****")

View File

@ -5,7 +5,7 @@ if [ ! -f nginx_app.conf ]; then
fi
# This template file is different from the others because of the sub_filter commands in the Nginx configuration
# Those variables are substituted inside the Docker container for appsmith-editor during bootup.
# Those variables are substituted inside the Docker container for appsmith-editor during bootup.
# Hence we wish to prevent environment substitution here.
# Relevant variables will be replaced at the end of this file via sed command
@ -26,7 +26,7 @@ $NGINX_SSL_CMNT server_name $custom_domain ;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
location / {
try_files $uri /index.html =404;
@ -49,7 +49,7 @@ $NGINX_SSL_CMNT server_name $custom_domain ;
location /f {
proxy_pass https://cdn.optimizely.com/;
}
location /api {
proxy_pass http://appsmith-internal-server:8080;
}
@ -101,7 +101,7 @@ $NGINX_SSL_CMNT
$NGINX_SSL_CMNT location /f {
$NGINX_SSL_CMNT proxy_pass https://cdn.optimizely.com/;
$NGINX_SSL_CMNT }
$NGINX_SSL_CMNT
$NGINX_SSL_CMNT
$NGINX_SSL_CMNT location /api {
$NGINX_SSL_CMNT proxy_pass http://appsmith-internal-server:8080;
$NGINX_SSL_CMNT }
@ -115,7 +115,7 @@ $NGINX_SSL_CMNT proxy_pass http://appsmith-internal-server:8080;
$NGINX_SSL_CMNT }
$NGINX_SSL_CMNT
$NGINX_SSL_CMNT }
' > nginx_app.conf
' >| nginx_app.conf
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i '' "s/\$NGINX_SSL_CMNT/$NGINX_SSL_CMNT/g" nginx_app.conf
@ -123,4 +123,4 @@ if [[ "$OSTYPE" == "darwin"* ]]; then
else
sed -i "s/\$NGINX_SSL_CMNT/$NGINX_SSL_CMNT/g" nginx_app.conf
sed -i "s/\$custom_domain/$custom_domain/g" nginx_app.conf
fi
fi