Merge branch 'release' of gitlab.com:theappsmith/internal-tools-client into feature/fix-71

This commit is contained in:
Abhinav Jha 2020-01-06 16:33:42 +05:30
commit c6906e1781
53 changed files with 1195 additions and 414 deletions

View File

@ -1,16 +0,0 @@
Concepts
========
Widgets
- WidgetProperties
- WIDGET_PROPERTY_LABEL (STRING)
- WIDGET_PROPERTY_TYPE (STRING)
- WIDGET_PROPERTY_DEFAULT (STRING)
- WIDGET_PROPERTY_ENABLED (BOOLEAN)
- WIDGET_PROPERTY_META
- ALLOWED_VALUES (LIST<STRING>)
- Components
- WidgetCards
- WIDGET_TYPE (STRING)
- WIDGET_CARD_LABEL (STRING)
- WIDGET_CARD_ENABLED (BOOLEAN)
- WIDGET_CARD_ICON (UTF-16)

View File

@ -86,7 +86,7 @@
"scripts": {
"analyze": "source-map-explorer 'build/static/js/*.js'",
"start": "REACT_APP_ENVIRONMENT=DEVELOPMENT HOST=dev.appsmith.com craco start",
"build": "craco --max-old-space-size=2048 build",
"build": "craco --max-old-space-size=2048 build",
"test": "CI=true craco test",
"eject": "react-scripts eject",
"start-prod": "REACT_APP_ENVIRONMENT=PRODUCTION craco start",

View File

@ -0,0 +1,59 @@
import {
ReduxActionTypes,
ReduxActionErrorTypes,
} from "constants/ReduxActionConstants";
import { CurrentUserDetailsRequestPayload } from "constants/userConstants";
import { VerifyTokenRequest, TokenPasswordUpdateRequest } from "api/UserApi";
export const logoutUserSuccess = () => ({
type: ReduxActionTypes.LOGOUT_USER_SUCCESS,
});
export const logoutUserError = (error: any) => ({
type: ReduxActionErrorTypes.LOGOUT_USER_ERROR,
payload: {
error,
},
});
export const fetchCurrentUser = () => ({
type: ReduxActionTypes.FETCH_USER_INIT,
payload: CurrentUserDetailsRequestPayload,
});
export const setCurrentUserDetails = () => ({
type: ReduxActionTypes.SET_CURRENT_USER_INIT,
payload: CurrentUserDetailsRequestPayload,
});
export const verifyInviteSuccess = () => ({
type: ReduxActionTypes.VERIFY_INVITE_SUCCESS,
});
export const verifyInvite = (payload: VerifyTokenRequest) => ({
type: ReduxActionTypes.VERIFY_INVITE_INIT,
payload,
});
export const verifyInviteError = (error: any) => ({
type: ReduxActionErrorTypes.VERIFY_INVITE_ERROR,
payload: { error },
});
export const invitedUserSignup = (
payload: TokenPasswordUpdateRequest & { resolve: any; reject: any },
) => ({
type: ReduxActionTypes.INVITED_USER_SIGNUP,
payload,
});
export const invitedUserSignupSuccess = () => ({
type: ReduxActionTypes.INVITED_USER_SIGNUP_SUCCESS,
});
export const invitedUserSignupError = (error: any) => ({
type: ReduxActionErrorTypes.INVITED_USER_SIGNUP_ERROR,
payload: {
error,
},
});

View File

@ -57,6 +57,7 @@ export interface RestAction {
pageId?: string;
actionConfiguration: Partial<APIConfigRequest>;
jsonPathKeys: string[];
cacheResponse?: string;
}
export interface ExecuteActionRequest extends APIRequest {

View File

@ -1,6 +1,7 @@
import { AxiosPromise } from "axios";
import Api from "./Api";
import { ApiResponse } from "./ApiResponses";
import { getAppsmithConfigs } from "configs";
export interface LoginUserRequest {
email: string;
@ -21,15 +22,13 @@ export interface ForgotPasswordRequest {
email: string;
}
export interface ResetPasswordRequest {
export interface TokenPasswordUpdateRequest {
token: string;
user: {
password: string;
email: string;
};
password: string;
email: string;
}
export interface ResetPasswordVerifyTokenRequest {
export interface VerifyTokenRequest {
email: string;
token: string;
}
@ -49,45 +48,67 @@ export interface InviteUserRequest {
}
class UserApi extends Api {
//TODO(abhinav): make a baseURL, to which the other paths are added.
static createURL = "v1/users";
static forgotPasswordURL = "v1/users/forgotPassword";
static verifyResetPasswordTokenURL = "v1/users/verifyPasswordResetToken";
static resetPasswordURL = "v1/users/resetPassword";
static fetchUserURL = "v1/users";
static usersURL = "v1/users";
static forgotPasswordURL = `${UserApi.usersURL}/forgotPassword`;
static verifyResetPasswordTokenURL = `${UserApi.usersURL}/verifyPasswordResetToken`;
static resetPasswordURL = `${UserApi.usersURL}/resetPassword`;
static inviteUserURL = "v1/users/invite";
static verifyInviteTokenURL = `${UserApi.inviteUserURL}/verify`;
static confirmUserInviteURL = `${UserApi.inviteUserURL}/confirm`;
static logoutURL = "/logout";
static createUser(
request: CreateUserRequest,
): AxiosPromise<CreateUserResponse> {
return Api.post(UserApi.createURL, request);
return Api.post(UserApi.usersURL, request);
}
static fetchUser(request: FetchUserRequest): AxiosPromise<FetchUserResponse> {
return Api.get(UserApi.usersURL + "/" + request.id);
}
static forgotPassword(
request: ForgotPasswordRequest,
): AxiosPromise<ApiResponse> {
return Api.get(UserApi.forgotPasswordURL, request);
}
static resetPassword(
request: ResetPasswordRequest,
): AxiosPromise<ApiResponse> {
return Api.put(UserApi.resetPasswordURL, request);
return Api.post(UserApi.forgotPasswordURL, request);
}
static verifyResetPasswordToken(
request: ResetPasswordVerifyTokenRequest,
request: VerifyTokenRequest,
): AxiosPromise<ApiResponse> {
return Api.get(UserApi.verifyResetPasswordTokenURL, request);
}
static fetchUser(request: FetchUserRequest): AxiosPromise<FetchUserResponse> {
return Api.get(UserApi.fetchUserURL + "/" + request.id);
static resetPassword(
request: TokenPasswordUpdateRequest,
): AxiosPromise<ApiResponse> {
return Api.put(UserApi.resetPasswordURL, request);
}
static inviteUser(request: InviteUserRequest): AxiosPromise<ApiResponse> {
request.status = "INVITED";
return Api.post(UserApi.inviteUserURL, request);
}
static verifyUserInvite(
request: VerifyTokenRequest,
): AxiosPromise<ApiResponse> {
return Api.get(UserApi.verifyInviteTokenURL, request);
}
static confirmInvitedUserSignup(
request: TokenPasswordUpdateRequest,
): AxiosPromise<ApiResponse> {
return Api.put(UserApi.confirmUserInviteURL, request);
}
static logoutUser(): AxiosPromise<ApiResponse> {
const { baseUrl } = getAppsmithConfigs();
return Api.post(UserApi.logoutURL, undefined, undefined, {
baseURL: baseUrl,
withCredentials: true,
});
}
}
export default UserApi;

View File

@ -14,9 +14,14 @@ type DropdownProps = {
meta: WrappedFieldMetaProps;
onCreateOption: (inputValue: string) => void;
formatCreateLabel?: (value: string) => React.ReactNode;
noOptionsMessage?: (obj: { inputValue: string }) => string;
};
const selectStyles = {
placeholder: (provided: any) => ({
...provided,
color: "#a3b3bf",
}),
singleValue: (provided: any) => ({
...provided,
backgroundColor: "rgba(104,113,239,0.1)",
@ -67,9 +72,11 @@ class CreatableDropdown extends React.Component<DropdownProps> {
onCreateOption,
input,
formatCreateLabel,
noOptionsMessage,
} = this.props;
const optionalProps: Partial<DropdownProps> = {};
if (formatCreateLabel) optionalProps.formatCreateLabel = formatCreateLabel;
if (noOptionsMessage) optionalProps.noOptionsMessage = noOptionsMessage;
return (
<Creatable
placeholder={placeholder}

View File

@ -14,6 +14,10 @@ type DropdownProps = {
};
const selectStyles = {
placeholder: (provided: any) => ({
...provided,
color: "#a3b3bf",
}),
control: (styles: any, state: any) => ({
...styles,
width: 100,

View File

@ -1,10 +1,10 @@
import React from "react";
import { AnchorButton, IButtonProps, MaybeElement } from "@blueprintjs/core";
import styled, { css } from "styled-components";
import { TextComponentProps } from "./TextComponent";
import { ButtonStyle } from "widgets/ButtonWidget";
import { Theme } from "constants/DefaultTheme";
import _ from "lodash";
import { ComponentProps } from "components/designSystems/appsmith/BaseComponent";
const getButtonColorStyles = (props: { theme: Theme } & ButtonStyleProps) => {
if (props.filled) return props.theme.colors.textOnDarkBG;
@ -98,7 +98,8 @@ BaseButton.defaultProps = {
minimal: true,
};
interface ButtonContainerProps extends TextComponentProps {
interface ButtonContainerProps extends ComponentProps {
text?: string;
icon?: MaybeElement;
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
disabled?: boolean;

View File

@ -15,6 +15,7 @@ export interface TextComponentProps extends ComponentProps {
ellipsize?: boolean;
textStyle?: TextStyle;
isLoading: boolean;
allowHtml: boolean;
}
class TextComponent extends React.Component<TextComponentProps> {
@ -37,14 +38,22 @@ class TextComponent extends React.Component<TextComponentProps> {
}
render() {
return (
<Text
className={this.getTextClass(this.props.textStyle)}
ellipsize={this.props.ellipsize}
>
{this.props.text}
</Text>
);
const { allowHtml, textStyle, text, ellipsize } = this.props;
if (allowHtml && text) {
const markup = { __html: text };
return (
<div
dangerouslySetInnerHTML={markup}
className={this.getTextClass(textStyle)}
/>
);
} else {
return (
<Text className={this.getTextClass(textStyle)} ellipsize={ellipsize}>
{text}
</Text>
);
}
}
}

View File

@ -6,6 +6,8 @@ import {
Grid,
Inject,
Resize,
Page,
SelectionSettingsModel,
} from "@syncfusion/ej2-react-grids";
import * as React from "react";
import styled from "constants/DefaultTheme";
@ -25,38 +27,72 @@ const StyledGridComponent = styled(GridComponent)`
background-color: #fafafa;
}
`;
const settings: SelectionSettingsModel = {
type: "Multiple",
};
export default class TableComponent extends React.Component<
TableComponentProps,
{}
> {
private grid: Grid | null | undefined;
public rowSelected = () => {
rowSelected = () => {
if (this.grid) {
/** Get the selected row indexes */
const selectedrowindex: number[] = this.grid.getSelectedRowIndexes();
/** Get the selected records. */
const selectedrecords: object[] = this.grid.getSelectedRecords();
this.props.onRowClick(selectedrecords[0], selectedrowindex[0]);
if (selectedrecords.length !== 0) {
this.props.onRowClick(selectedrecords[0], selectedrowindex[0]);
}
}
};
public dataBound = () => {
reCalculatePageSize = () => {
if (this.grid) {
/** height of the each row */
const rowHeight: number = this.grid.getRowHeight();
/** Grid height */
const gridHeight: number = this.grid.height as number;
/** initial page size */
const pageSize: number = this.grid.pageSettings.pageSize as number;
/** new page size is obtained here */
const pageResize: any = (gridHeight - pageSize * rowHeight) / rowHeight;
this.grid.pageSettings.pageSize = pageSize + Math.round(pageResize);
}
};
dataBound = () => {
if (this.grid) {
this.grid.autoFitColumns();
}
};
public render() {
shouldComponentUpdate(nextProps: TableComponentProps) {
const propsNotEqual =
JSON.stringify(nextProps.data) !== JSON.stringify(this.props.data) ||
nextProps.height !== this.props.height ||
nextProps.width !== this.props.width;
return propsNotEqual;
}
componentDidUpdate(prevProps: TableComponentProps) {
if (prevProps.height !== this.props.height) {
this.reCalculatePageSize();
}
}
render() {
return (
<StyledGridComponent
selectionSettings={settings}
dataSource={this.props.data}
selectedRowIndex={this.props.selectedRowIndex}
rowSelected={this.rowSelected}
ref={(g: GridComponent) => (this.grid = g)}
width={this.props.width - 16}
height={this.props.height - 62}
height={this.props.height - 107}
dataBound={this.dataBound}
allowPaging={true}
>
<Inject services={[Resize]} />
<Inject services={[Resize, Page]} />
<ColumnsDirective>
{this.props.columns.map(col => {
return <ColumnDirective key={col.field} field={col.field} />;

View File

@ -11,7 +11,7 @@ import { formatBytes } from "utils/helpers";
import { APIEditorRouteParams } from "constants/routes";
import { ApiPaneReduxState } from "reducers/uiReducers/apiPaneReducer";
import LoadingOverlayScreen from "components/editorComponents/LoadingOverlayScreen";
import DynamicAutocompleteInput from "components/editorComponents/DynamicAutocompleteInput";
import CodeEditor from "components/editorComponents/CodeEditor";
const ResponseWrapper = styled.div`
position: relative;
@ -138,12 +138,13 @@ const ApiResponseView = (props: Props) => {
key: "body",
title: "Response Body",
panelComponent: (
<DynamicAutocompleteInput
<CodeEditor
input={{
value: response.body
? JSON.stringify(response.body, null, 2)
: "",
}}
height={700}
/>
),
},

View File

@ -21,19 +21,19 @@ interface Props {
class CodeEditor extends React.Component<Props> {
textArea = React.createRef<HTMLTextAreaElement>();
editor: any;
constructor(props: Props) {
super(props);
}
componentDidMount(): void {
if (this.textArea.current) {
const readOnly = !this.props.input.onChange;
this.editor = cm.fromTextArea(this.textArea.current, {
mode: { name: "javascript", json: true },
value: this.props.input.value,
readOnly,
lineNumbers: true,
tabSize: 2,
indentWithTabs: true,
lineWrapping: true,
});
this.editor.setSize(null, this.props.height);
}
}

View File

@ -1,43 +1,146 @@
import React, { Component } from "react";
import { connect } from "react-redux";
import { AppState } from "reducers";
import styled from "styled-components";
import CodeMirror, { EditorConfiguration } from "codemirror";
import styled, { createGlobalStyle } from "styled-components";
import CodeMirror, { EditorConfiguration, LineHandle } from "codemirror";
import "codemirror/lib/codemirror.css";
import "codemirror/theme/monokai.css";
import "codemirror/addon/hint/show-hint";
import "codemirror/addon/hint/show-hint.css";
import "codemirror/addon/hint/javascript-hint";
import "codemirror/addon/display/placeholder";
import {
getNameBindingsWithData,
getNameBindingsForAutocomplete,
NameBindingsWithData,
} from "selectors/nameBindingsWithDataSelector";
import { AUTOCOMPLETE_MATCH_REGEX } from "constants/BindingsConstants";
import ErrorTooltip from "components/editorComponents/ErrorTooltip";
import { WrappedFieldInputProps, WrappedFieldMetaProps } from "redux-form";
import _ from "lodash";
import { parseDynamicString } from "utils/DynamicBindingUtils";
require("codemirror/mode/javascript/javascript");
const Wrapper = styled.div<{ height?: number; theme?: "LIGHT" | "DARK" }>`
border: ${props => props.theme !== "DARK" && "1px solid #d0d7dd"};
const HintStyles = createGlobalStyle`
.CodeMirror-hints {
position: absolute;
z-index: 10;
overflow: hidden;
list-style: none;
margin: 0;
padding: 5px;
font-size: 90%;
font-family: monospace;
max-height: 20em;
width: 200px;
overflow-y: auto;
background: #FFFFFF;
border: 1px solid #EBEFF2;
box-shadow: 0px 2px 4px rgba(67, 70, 74, 0.14);
border-radius: 4px;
}
.CodeMirror-hint {
height: 32px;
padding: 3px;
margin: 0;
white-space: pre;
color: #2E3D49;
cursor: pointer;
display: flex;
align-items: center;
font-size: 14px;
}
li.CodeMirror-hint-active {
background: #E9FAF3;
border-radius: 4px;
}
`;
const Wrapper = styled.div<{
borderStyle?: THEME;
hasError: boolean;
}>`
border: 1px solid;
border-color: ${props =>
props.hasError
? props.theme.colors.error
: props.borderStyle !== THEMES.DARK
? "#d0d7dd"
: "transparent"};
border-radius: 4px;
display: flex;
flex: 1;
flex-direction: column;
flex-direction: row;
position: relative;
text-transform: none;
min-height: 32px;
height: ${props => (props.height ? `${props.height}px` : "32px")};
overflow: hidden;
height: auto;
&& {
.binding-highlight {
color: ${props =>
props.borderStyle === THEMES.DARK ? "#f7c75b" : "#ffb100"};
font-weight: 700;
}
.CodeMirror {
flex: 1;
line-height: 21px;
z-index: 0;
border-radius: 4px;
height: auto;
}
.CodeMirror pre.CodeMirror-placeholder {
color: #a3b3bf;
}
}
`;
const IconContainer = styled.div`
.bp3-icon {
border-radius: 4px 0 0 4px;
margin: 0;
height: 32px;
width: 30px;
display: flex;
align-items: center;
justify-content: center;
background-color: #eef2f5;
svg {
height: 20px;
width: 20px;
path {
fill: #979797;
}
}
}
`;
const THEMES = {
LIGHT: "LIGHT",
DARK: "DARK",
};
type THEME = "LIGHT" | "DARK";
interface ReduxStateProps {
dynamicData: NameBindingsWithData;
}
type Props = ReduxStateProps & {
input: {
value: string;
onChange?: (value: string) => void;
};
theme?: "LIGHT" | "DARK";
export type DynamicAutocompleteInputProps = {
placeholder?: string;
leftIcon?: Function;
height?: number;
theme?: THEME;
meta?: Partial<WrappedFieldMetaProps>;
showLineNumbers?: boolean;
allowTabIndent?: boolean;
};
type Props = ReduxStateProps &
DynamicAutocompleteInputProps & {
input: Partial<WrappedFieldInputProps>;
};
class DynamicAutocompleteInput extends Component<Props> {
textArea = React.createRef<HTMLTextAreaElement>();
editor: any;
@ -47,31 +150,45 @@ class DynamicAutocompleteInput extends Component<Props> {
const options: EditorConfiguration = {};
if (this.props.theme === "DARK") options.theme = "monokai";
if (!this.props.input.onChange) options.readOnly = true;
if (this.props.showLineNumbers) options.lineNumbers = true;
const extraKeys: Record<string, any> = {
"Ctrl-Space": "autocomplete",
};
if (!this.props.allowTabIndent) extraKeys["Tab"] = false;
this.editor = CodeMirror.fromTextArea(this.textArea.current, {
mode: { name: "javascript", globalVars: true },
viewportMargin: 10,
value: this.props.input.value,
tabSize: 2,
indentWithTabs: true,
lineWrapping: true,
extraKeys: { "Ctrl-Space": "autocomplete" },
showHint: true,
extraKeys,
...options,
});
this.editor.on("change", this.handleChange);
this.editor.on("keyup", this.handleAutocompleteVisibility);
this.editor.on("change", _.debounce(this.handleChange, 200));
this.editor.on("cursorActivity", this.handleAutocompleteVisibility);
this.editor.setOption("hintOptions", {
completeSingle: false,
globalScope: this.props.dynamicData,
});
if (this.props.height) {
this.editor.setSize(0, this.props.height);
}
this.editor.eachLine(this.highlightBindings);
}
}
componentDidUpdate(): void {
if (this.editor) {
const editorValue = this.editor.getValue();
const inputValue = this.props.input.value;
if (inputValue && inputValue !== editorValue) {
let inputValue = this.props.input.value;
if (typeof inputValue === "object") {
inputValue = JSON.stringify(inputValue, null, 2);
}
if ((!!inputValue || inputValue === "") && inputValue !== editorValue) {
this.editor.setValue(inputValue);
this.editor.setCursor(this.editor.lineCount(), 0);
}
}
}
@ -81,27 +198,82 @@ class DynamicAutocompleteInput extends Component<Props> {
if (this.props.input.onChange) {
this.props.input.onChange(value);
}
this.editor.eachLine(this.highlightBindings);
};
handleAutocompleteVisibility = (cm: any, event: any) => {
if (!cm.state.completionActive && event.keyCode !== 13) {
handleAutocompleteVisibility = (cm: any) => {
let cursorBetweenBinding = false;
const cursor = this.editor.getCursor();
const value = this.editor.getValue();
let cumulativeCharCount = 0;
parseDynamicString(value).forEach(segment => {
const start = cumulativeCharCount;
const dynamicStart = segment.indexOf("{{");
const dynamicDoesStart = dynamicStart > -1;
const dynamicEnd = segment.indexOf("}}");
const dynamicDoesEnd = dynamicEnd > -1;
const dynamicStartIndex = dynamicStart + start + 1;
const dynamicEndIndex = dynamicEnd + start + 1;
if (
dynamicDoesStart &&
cursor.ch > dynamicStartIndex &&
((dynamicDoesEnd && cursor.ch < dynamicEndIndex) ||
(!dynamicDoesEnd && cursor.ch > dynamicStartIndex))
) {
cursorBetweenBinding = true;
}
cumulativeCharCount = start + segment.length;
});
const shouldShow = cursorBetweenBinding && !cm.state.completionActive;
if (shouldShow) {
cm.showHint(cm);
}
};
highlightBindings = (line: LineHandle) => {
const lineNo = this.editor.getLineNumber(line);
let match;
while ((match = AUTOCOMPLETE_MATCH_REGEX.exec(line.text)) != null) {
const start = match.index;
const end = AUTOCOMPLETE_MATCH_REGEX.lastIndex;
this.editor.markText(
{ ch: start, line: lineNo },
{ ch: end, line: lineNo },
{
className: "binding-highlight",
},
);
}
};
render() {
const { input, theme } = this.props;
const height = this.editor ? this.editor.doc.height + 20 : null;
const { input, meta, theme } = this.props;
const hasError = !!(meta && meta.error);
let showError = false;
if (this.editor) {
showError = hasError && this.editor.hasFocus();
}
return (
<Wrapper height={height} theme={theme}>
<textarea ref={this.textArea} defaultValue={input.value} />
</Wrapper>
<ErrorTooltip message={meta ? meta.error : ""} isOpen={showError}>
<Wrapper borderStyle={theme} hasError={hasError}>
<HintStyles />
<IconContainer>
{this.props.leftIcon && <this.props.leftIcon />}
</IconContainer>
<textarea
ref={this.textArea}
{..._.omit(this.props.input, ["onChange", "value"])}
defaultValue={input.value}
placeholder={this.props.placeholder}
/>
</Wrapper>
</ErrorTooltip>
);
}
}
const mapStateToProps = (state: AppState): ReduxStateProps => ({
dynamicData: getNameBindingsWithData(state),
dynamicData: getNameBindingsForAutocomplete(state),
});
export default connect(mapStateToProps)(DynamicAutocompleteInput);

View File

@ -3,8 +3,10 @@ import { Popover } from "@blueprintjs/core";
import styled from "styled-components";
const Wrapper = styled.div`
flex: 1;
.bp3-popover-target {
width: 100%;
height: 100%;
}
.bp3-popover {
.bp3-popover-arrow {

View File

@ -58,16 +58,24 @@ const TagInputComponent = (props: TagInputProps) => {
props.input.onChange &&
props.input.onChange(newValues.filter(Boolean).join(","));
};
const onTagsChange = (values: React.ReactNode[]) => {
const _values = values as string[];
commitValues(_values);
};
const onKeyDown = (e: any) => {
if (e.key === "," || e.key === "Enter" || e.key === " ") {
// Add new values to the tags on comma, return key, space and Tab press.
if (
e.key === "," ||
e.key === "Enter" ||
e.key === " " ||
e.key === "Tab"
) {
const newValues = [...values, e.target.value];
commitValues(newValues);
setCurrentValue("");
e.preventDefault();
} else if (e.key === "Backspace") {
if (e.target.value.length === 0) {
const newValues = values.slice(0, -1);
@ -76,9 +84,13 @@ const TagInputComponent = (props: TagInputProps) => {
}
};
// The input text field where the user can type in needs to handle the scenario where
// The input field is reset on adding tag.
const handleInputChange = (e: any) => {
if ([",", " ", "Enter"].indexOf(e.target.value) === -1) {
setCurrentValue(e.target.value);
} else {
setCurrentValue("");
}
};
@ -90,7 +102,6 @@ const TagInputComponent = (props: TagInputProps) => {
placeholder={props.placeholder}
values={_values || [""]}
separator={props.separator || ","}
addOnBlur
addOnPaste
onChange={onTagsChange}
onKeyDown={onKeyDown}

View File

@ -11,6 +11,7 @@ type FormFooterProps = {
submitText?: string;
cancelText?: string;
submitOnEnter?: boolean;
canSubmit: boolean;
};
const FooterActions = styled.div`
@ -46,6 +47,7 @@ export const FormFooter = (props: FormFooterProps) => {
type={props.submitOnEnter ? "submit" : "button"}
intent="primary"
onClick={props.onSubmit}
disabled={!props.canSubmit}
loading={props.submitting}
large
/>

View File

@ -37,11 +37,12 @@ const DatasourcesField = (
component={CreatableDropdown}
isLoading={props.datasources.loading}
options={options}
placeholder="Data Source"
placeholder="https://<base-url>.com"
onCreateOption={props.createDatasource}
format={(value: string) => _.find(options, { value })}
parse={(option: { value: string }) => (option ? option.value : null)}
formatCreateLabel={(value: string) => `Create data source "${value}"`}
noOptionsMessage={() => "No data sources created"}
/>
);
};

View File

@ -1,10 +1,11 @@
import React from "react";
import { Field, BaseFieldProps } from "redux-form";
import { TextInputProps } from "components/designSystems/appsmith/TextInputComponent";
import DynamicAutocompleteInput from "components/editorComponents/DynamicAutocompleteInput";
import DynamicAutocompleteInput, {
DynamicAutocompleteInputProps,
} from "components/editorComponents/DynamicAutocompleteInput";
class DynamicTextField extends React.Component<
BaseFieldProps & TextInputProps
BaseFieldProps & DynamicAutocompleteInputProps
> {
render() {
return <Field component={DynamicAutocompleteInput} {...this.props} />;

View File

@ -1,8 +1,12 @@
import React from "react";
import { Field } from "redux-form";
import DynamicAutocompleteInput from "components/editorComponents/DynamicAutocompleteInput";
import DynamicAutocompleteInput, {
DynamicAutocompleteInputProps,
} from "components/editorComponents/DynamicAutocompleteInput";
const JSONEditorField = (props: { name: string }) => {
type Props = { name: string } & DynamicAutocompleteInputProps;
const JSONEditorField = (props: Props) => {
return <Field name={props.name} component={DynamicAutocompleteInput} />;
};

View File

@ -19,7 +19,11 @@ const renderComponent = (
<TagInputComponent {...componentProps} />
<FormFieldError
error={componentProps.meta.touched && componentProps.meta.error}
error={
componentProps.meta.touched &&
!componentProps.meta.pristine &&
componentProps.meta.error
}
/>
</React.Fragment>
);

View File

@ -1,8 +1,9 @@
import React from "react";
import React, { ChangeEvent } from "react";
import BaseControl, { ControlProps } from "./BaseControl";
import { ControlType } from "constants/PropertyControlConstants";
import { ControlWrapper } from "./StyledControls";
import DynamicAutocompleteInput from "components/editorComponents/DynamicAutocompleteInput";
import { EventOrValueHandler } from "redux-form";
class CodeEditorControl extends BaseControl<ControlProps> {
render() {
return (
@ -16,7 +17,9 @@ class CodeEditorControl extends BaseControl<ControlProps> {
);
}
onChange = (value: string) => {
onChange: EventOrValueHandler<ChangeEvent<any>> = (
value: string | ChangeEvent,
) => {
this.updateProperty(this.props.propertyName, value);
};

View File

@ -8,12 +8,7 @@ import DynamicAutocompleteInput from "components/editorComponents/DynamicAutocom
class InputTextControl extends BaseControl<InputControlProps> {
render() {
const {
// validationMessage,
propertyValue,
// isValid,
label,
} = this.props;
const { validationMessage, propertyValue, isValid, label } = this.props;
return (
<ControlWrapper>
<label>{label}</label>
@ -23,6 +18,10 @@ class InputTextControl extends BaseControl<InputControlProps> {
value: propertyValue,
onChange: this.onTextChange,
}}
meta={{
error: isValid ? "" : validationMessage,
touched: true,
}}
theme={"DARK"}
/>
</StyledDynamicInput>

View File

@ -2,6 +2,5 @@
// TODO (hetu): Remove useless escapes and re-enable the above lint rule
export type NamePathBindingMap = Record<string, string>;
export const DATA_BIND_REGEX = /(.*?){{(\s*(.*?)\s*)}}(.*?)/g;
export const DATA_PATH_REGEX = /[\w\.\[\]\d]+/;
export const DATA_BIND_AUTOCOMPLETE = /({{)(.*)(}{0,2}?)/;
export const AUTOCOMPLETE_MATCH_REGEX = /{{\s*.*?\s*}}/g;
/* eslint-enable no-useless-escape */

View File

@ -106,8 +106,6 @@ export const ReduxActionTypes: { [key: string]: string } = {
EXECUTE_PAGE_LOAD_ACTIONS: "EXECUTE_PAGE_LOAD_ACTIONS",
SWITCH_ORGANIZATION_INIT: "SWITCH_ORGANIZATION_INIT",
SWITCH_ORGANIZATION_SUCCESS: "SWITCH_ORGANIZATION_SUCCESS",
LOGOUT_USER_INIT: "LOGOUT_USER_INIT",
LOGOUT_USER_SUCCESS: "LOGOUT_USER_SUCCESS",
FETCH_ORG_ROLES_INIT: "FETCH_ORG_ROLES_INIT",
FETCH_ORG_ROLES_SUCCESS: "FETCH_ORG_ROLES_SUCCESS",
FETCH_ORG_INIT: "FETCH_ORG_INIT",
@ -119,6 +117,12 @@ export const ReduxActionTypes: { [key: string]: string } = {
FETCH_USER_SUCCESS: "FETCH_USER_SUCCESS",
SET_CURRENT_USER_INIT: "SET_CURRENT_USER_INIT",
SET_CURRENT_USER_SUCCESS: "SET_CURRENT_USER_SUCCESS",
LOGOUT_USER_INIT: "LOGOUT_USER_INIT",
LOGOUT_USER_SUCCESS: "LOGOUT_USER_SUCCESS",
VERIFY_INVITE_INIT: "VERIFY_INVITE_INIT",
VERIFY_INVITE_SUCCESS: "VERIFY_INVITE_SUCCESS",
INVITED_USER_SIGNUP_SUCCESS: "INVITED_USER_SIGNUP_SUCCESS",
INVITED_USER_SIGNUP_INIT: "INVITED_USER_SIGNUP_INIT",
};
export type ReduxActionType = typeof ReduxActionTypes[keyof typeof ReduxActionTypes];
@ -162,7 +166,6 @@ export const ReduxActionErrorTypes: { [key: string]: string } = {
SWITCH_ORGANIZATION_ERROR: "SWITCH_ORGANIZATION_ERROR",
FORGOT_PASSWORD_ERROR: "FORGOT_PASSWORD_ERROR",
RESET_PASSWORD_VERIFY_TOKEN_ERROR: "RESET_PASSWORD_VERIFY_TOKEN_ERROR",
LOGOUT_USER_ERROR: "LOGOUT_USER_ERROR",
FETCH_ORG_ROLES_ERROR: "FETCH_ORG_ROLES_ERROR",
INVITE_USERS_TO_ORG_ERROR: "INVITE_USERS_TO_ORG_ERROR",
SAVE_ORG_ERROR: "SAVE_ORG_ERROR",
@ -170,6 +173,8 @@ export const ReduxActionErrorTypes: { [key: string]: string } = {
FETCH_ORGS_ERROR: "FETCH_ORGS_ERROR",
FETCH_USER_ERROR: "FETCH_USER_ERROR",
SET_CURRENT_USER_ERROR: "SET_CURRENT_USER_ERROR",
LOGOUT_USER_ERROR: "LOGOUT_USER_ERROR",
VERIFY_INVITE_ERROR: "VERIFY_INVITE_ERROR",
};
export const ReduxFormActionTypes: { [key: string]: string } = {
@ -190,6 +195,13 @@ export type ReduxActionWithoutPayload = Pick<ReduxAction<undefined>, "type">;
export interface ReduxActionWithMeta<T, M> extends ReduxAction<T> {
meta: M;
}
export interface PromisePayload {
reject: any;
resolve: any;
}
export interface ReduxActionWithPromise<T> extends ReduxAction<T> {
payload: T & PromisePayload;
}
export interface ReduxActionErrorPayload {
message: string;

View File

@ -2,6 +2,11 @@ export const API_EDITOR_FORM_NAME = "ApiEditorForm";
export const CREATE_APPLICATION_FORM_NAME = "CreateApplicationForm";
export const INVITE_USERS_TO_ORG_FORM = "InviteUsersToOrgForm";
export const LOGIN_FORM_NAME = "LoginForm";
export const LOGIN_FORM_EMAIL_FIELD_NAME = "username";
export const LOGIN_FORM_PASSWORD_FIELD_NAME = "password";
export const SIGNUP_FORM_NAME = "SignupForm";
export const FORGOT_PASSWORD_FORM_NAME = "ForgotPasswordForm";
export const RESET_PASSWORD_FORM_NAME = "ResetPasswordForm";
export const CREATE_PASSWORD_FORM_NAME = "CreatePasswordForm";

View File

@ -99,3 +99,21 @@ export const INVITE_USERS_SUBMIT_SUCCESS =
"The users have been invited successfully";
export const INVITE_USERS_VALIDATION_EMAILS_EMPTY =
"Please enter the user emails";
export const CREATE_PASSWORD_PAGE_PASSWORD_INPUT_LABEL = "New Password";
export const CREATE_PASSWORD_PAGE_PASSWORD_INPUT_PLACEHOLDER = "New Password";
export const CREATE_PASSWORD_LOGIN_LINK_TEXT =
"Already know the password? Login";
export const CREATE_PASSWORD_PAGE_TITLE = "Set Password";
export const CREATE_PASSWORD_SUBMIT_BUTTON_TEXT = "Create";
export const CREATE_PASSWORD_PAGE_SUBTITLE =
"Set a new password for your account ";
export const CREATE_PASSWORD_RESET_SUCCESS =
"Your password has been set. Please";
export const CREATE_PASSWORD_RESET_SUCCESS_LOGIN_LINK = "login";
export const CREATE_PASSWORD_EXPIRED_TOKEN =
"The invite link has expired. Please try requesting a new invite";
export const CREATE_PASSWORD_INVALID_TOKEN =
"The invite link is invalid. Please try request a new invite";

View File

@ -1,6 +1,7 @@
export type OrgRole = {
id: string;
name: string;
isDefault?: boolean;
};
export type Org = {

View File

@ -4,3 +4,7 @@ export type User = {
currentOrganizationId: string;
organizationIds: string[];
};
export const CurrentUserDetailsRequestPayload = {
id: "me",
};

View File

@ -25,4 +25,13 @@ export const FormIcons: {
<Icon icon={IconNames.PLUS} color={props.color} iconSize={props.height} />
</IconWrapper>
),
SLASH_ICON: (props: IconProps) => (
<IconWrapper {...props}>
<Icon
icon={IconNames.SLASH}
color={props.color}
iconSize={props.height}
/>
</IconWrapper>
),
};

View File

@ -2,11 +2,11 @@
@import "~@blueprintjs/core/lib/css/blueprint.css";
@import "~@blueprintjs/icons/lib/css/blueprint-icons.css";
@import '../node_modules/@syncfusion/ej2-base/styles/material.css';
@import '../node_modules/@syncfusion/ej2-buttons/styles/material.css';
@import '../node_modules/@syncfusion/ej2-calendars/styles/material.css';
@import '../node_modules/@syncfusion/ej2-dropdowns/styles/material.css';
@import '../node_modules/@syncfusion/ej2-inputs/styles/material.css';
@import '../node_modules/@syncfusion/ej2-base/styles/material.css';
@import '../node_modules/@syncfusion/ej2-buttons/styles/material.css';
@import '../node_modules/@syncfusion/ej2-calendars/styles/material.css';
@import '../node_modules/@syncfusion/ej2-dropdowns/styles/material.css';
@import '../node_modules/@syncfusion/ej2-inputs/styles/material.css';
@import '../node_modules/@syncfusion/ej2-navigations/styles/material.css';
@import '../node_modules/@syncfusion/ej2-popups/styles/material.css';
@import '../node_modules/@syncfusion/ej2-splitbuttons/styles/material.css';

View File

@ -3,7 +3,7 @@ import moment from "moment-timezone";
export type JSExecutorGlobal = Record<string, object>;
export interface JSExecutor {
execute: (src: string, data: JSExecutorGlobal) => string;
execute: (src: string, data: JSExecutorGlobal) => any;
registerLibrary: (accessor: string, lib: any) => void;
unRegisterLibrary: (accessor: string) => void;
}

View File

@ -3,7 +3,7 @@ declare let Realm: any;
export default class RealmExecutor implements JSExecutor {
rootRealm: any;
creaetSafeObject: any;
createSafeObject: any;
extrinsics: any[] = [];
createSafeFunction: (unsafeFn: Function) => Function;
@ -17,7 +17,7 @@ export default class RealmExecutor implements JSExecutor {
}
})
`);
this.creaetSafeObject = this.rootRealm.evaluate(`
this.createSafeObject = this.rootRealm.evaluate(`
(function creaetSafeObject(unsafeObject) {
return JSON.parse(JSON.stringify(unsafeObject));
})
@ -29,14 +29,23 @@ export default class RealmExecutor implements JSExecutor {
unRegisterLibrary(accessor: string) {
this.rootRealm.global[accessor] = null;
}
private convertToMainScope(result: any) {
if (typeof result === "object") {
if (Array.isArray(result)) {
return Object.assign([], result);
}
return Object.assign({}, result);
}
return result;
}
execute(sourceText: string, data: JSExecutorGlobal) {
const safeData = this.creaetSafeObject(data);
const safeData = this.createSafeObject(data);
let result;
try {
result = this.rootRealm.evaluate(sourceText, safeData);
} catch (e) {
//TODO(Satbir): Return an object with an error message.
}
return result;
return this.convertToMainScope(result);
}
}

View File

@ -20,13 +20,18 @@ const PropertyPaneConfigResponse = {
label: "Button Style",
controlType: "DROP_DOWN",
options: [
{ id: "1.2.1", label: "Primary Button", value: "PRIMARY_BUTTON" },
{
id: "1.2.2",
label: "Primary Button",
value: "PRIMARY_BUTTON",
},
{
label: "Secondary Button",
value: "SECONDARY_BUTTON",
},
{ id: "1.2.3", label: "Danger Button", value: "DANGER_BUTTON" },
{
label: "Danger Button",
value: "DANGER_BUTTON",
},
],
},
{
@ -74,13 +79,28 @@ const PropertyPaneConfigResponse = {
label: "Text Style",
controlType: "DROP_DOWN",
options: [
{ id: "3.2.1", label: "Heading", value: "HEADING" },
{ id: "3.2.2", label: "Label", value: "LABEL" },
{ id: "3.2.3", label: "Body", value: "BODY" },
{
label: "Heading",
value: "HEADING",
},
{
label: "Label",
value: "LABEL",
},
{
label: "Body",
value: "BODY",
},
],
},
{
id: "3.3",
propertyName: "allowHtml",
label: "Allow HTML",
controlType: "SWITCH",
},
{
id: "3.4",
propertyName: "isVisible",
label: "Visible",
controlType: "SWITCH",
@ -97,15 +117,15 @@ const PropertyPaneConfigResponse = {
id: "4.1",
propertyName: "image",
label: "Image",
placeholderText: "Enter URL",
controlType: "INPUT_TEXT",
placeholderText: "Enter URL",
},
{
id: "4.1",
propertyName: "defaultImage",
label: "Default Image",
placeholderText: "Enter URL",
controlType: "INPUT_TEXT",
placeholderText: "Enter URL",
},
{
id: "4.3",
@ -132,8 +152,8 @@ const PropertyPaneConfigResponse = {
propertyName: "label",
label: "Label",
controlType: "INPUT_TEXT",
inputType: "TEXT",
placeholderText: "Label the widget",
inputType: "TEXT",
},
{
id: "5.2",
@ -141,11 +161,26 @@ const PropertyPaneConfigResponse = {
label: "Data Type",
controlType: "DROP_DOWN",
options: [
{ id: "5.2.1", label: "Text", value: "TEXT" },
{ id: "5.2.2", label: "Number", value: "NUMBER" },
{ id: "5.2.3", label: "Password", value: "PASSWORD" },
{ id: "5.2.4", label: "Phone Number", value: "PHONE_NUMBER" },
{ id: "5.2.5", label: "Email", value: "EMAIL" },
{
label: "Text",
value: "TEXT",
},
{
label: "Number",
value: "NUMBER",
},
{
label: "Password",
value: "PASSWORD",
},
{
label: "Phone Number",
value: "PHONE_NUMBER",
},
{
label: "Email",
value: "EMAIL",
},
],
},
{
@ -167,24 +202,24 @@ const PropertyPaneConfigResponse = {
propertyName: "maxChars",
label: "Max Chars",
controlType: "INPUT_TEXT",
inputType: "INTEGER",
placeholderText: "Enter the max length",
inputType: "INTEGER",
},
{
id: "5.6",
propertyName: "regex",
label: "Regex",
controlType: "INPUT_TEXT",
inputType: "TEXT",
placeholderText: "Enter the regex",
inputType: "TEXT",
},
{
id: "5.7",
propertyName: "errorMessage",
label: "Error Message",
controlType: "INPUT_TEXT",
inputType: "TEXT",
placeholderText: "Enter the message",
inputType: "TEXT",
},
{
id: "5.8",
@ -223,8 +258,8 @@ const PropertyPaneConfigResponse = {
propertyName: "label",
label: "Label",
controlType: "INPUT_TEXT",
inputType: "TEXT",
placeholderText: "Label the widget",
inputType: "TEXT",
},
{
id: "6.2",
@ -248,10 +283,10 @@ const PropertyPaneConfigResponse = {
},
{
sectionName: "Actions",
id: "5.1.1",
id: "6.1.1",
children: [
{
id: "5.1.2",
id: "6.1.2",
propertyName: "onToggle",
label: "onToggle",
controlType: "ACTION_SELECTOR",
@ -271,7 +306,7 @@ const PropertyPaneConfigResponse = {
controlType: "COLOR_PICKER",
},
{
id: "6.3",
id: "7.2",
propertyName: "isVisible",
label: "Visible",
controlType: "SWITCH",
@ -298,36 +333,26 @@ const PropertyPaneConfigResponse = {
sectionName: "General",
id: "9",
children: [
// {
// id: "9.1",
// propertyName: "datePickerType",
// label: "Picker Type",
// controlType: "DROP_DOWN",
// options: [
// { label: "Date Picker", value: "DATE_PICKER" },
// { label: "Date Range Picker", value: "DATE_RANGE_PICKER" },
// ],
// },
{
id: "9.2",
propertyName: "label",
label: "Label",
placeholderText: "Enter Label",
controlType: "INPUT_TEXT",
placeholderText: "Enter Label",
},
{
id: "9.3",
propertyName: "defaultDate",
label: "Default Date",
placeholderText: "Enter Default Date",
controlType: "DATE_PICKER",
placeholderText: "Enter Default Date",
},
{
id: "9.5",
label: "Timezone",
placeholderText: "Select Timezone",
propertyName: "timezone",
label: "Timezone",
controlType: "TIMEZONE_PICKER",
placeholderText: "Select Timezone",
},
{
id: "9.6",
@ -373,12 +398,6 @@ const PropertyPaneConfigResponse = {
label: "Enter Table Label",
controlType: "INPUT_TEXT",
},
// {
// id: "11.2",
// propertyName: "tableData",
// label: "Enter data array",
// controlType: "INPUT_TEXT",
// },
{
id: "11.2",
propertyName: "tableData",
@ -410,12 +429,6 @@ const PropertyPaneConfigResponse = {
sectionName: "Actions",
id: "12",
children: [
{
id: "12.1",
propertyName: "tableActions",
label: "Record action",
controlType: "RECORD_ACTION_SELECTOR",
},
{
id: "12.2",
propertyName: "onRowSelected",
@ -442,8 +455,14 @@ const PropertyPaneConfigResponse = {
label: "Selection Type",
controlType: "DROP_DOWN",
options: [
{ id: "13.1.1", label: "Single Select", value: "SINGLE_SELECT" },
{ id: "13.1.2", label: "Multi Select", value: "MULTI_SELECT" },
{
label: "Single Select",
value: "SINGLE_SELECT",
},
{
label: "Multi Select",
value: "MULTI_SELECT",
},
],
},
{
@ -579,65 +598,57 @@ const PropertyPaneConfigResponse = {
id: "18.1",
propertyName: "label",
label: "Label",
inputType: "INTEGER",
placeholderText: "Enter Label",
controlType: "INPUT_TEXT",
placeholderText: "Enter Label",
inputType: "INTEGER",
},
{
id: "18.2",
propertyName: "maxNumFiles",
label: "No. of files",
placeholderText: "Enter No. of files",
controlType: "INPUT_TEXT",
placeholderText: "Enter No. of files",
},
{
id: "18.3",
propertyName: "allowedFileTypes",
label: "Allowed File Types",
controlType: "MULTI_SELECT",
placeholderText: "Enter No. of files",
options: [
{
id: "18.3.1",
label: "Any File",
value: "*",
},
{
id: "18.3.2",
label: "Images",
value: "image/*",
},
{
id: "18.3.3",
label: "Videos",
value: "video/*",
},
{
id: "18.3.4",
label: "Audio",
value: "audio/*",
},
{
id: "18.3.5",
label: "Text",
value: "text/*",
},
{
id: "18.3.6",
label: "JPEG",
value: "image/jpeg",
},
{
id: "18.3.7",
label: "PNG",
value: "*.png",
},
{
id: "18.3.8",
label: "GIF",
value: "*.gif",
},
],
controlType: "MULTI_SELECT",
},
{
id: "18.4",
@ -649,7 +660,7 @@ const PropertyPaneConfigResponse = {
},
],
},
configVersion: 1,
name: "propertyPane",
};
export default PropertyPaneConfigResponse;

View File

@ -11,10 +11,10 @@ import DynamicTextField from "components/editorComponents/form/fields/DynamicTex
import DropdownField from "components/editorComponents/form/fields/DropdownField";
import DatasourcesField from "components/editorComponents/form/fields/DatasourcesField";
import KeyValueFieldArray from "components/editorComponents/form/fields/KeyValueFieldArray";
import JSONEditorField from "components/editorComponents/form/fields/JSONEditorField";
import ApiResponseView from "components/editorComponents/ApiResponseView";
import { API_EDITOR_FORM_NAME } from "constants/forms";
import LoadingOverlayScreen from "components/editorComponents/LoadingOverlayScreen";
import { FormIcons } from "icons/FormIcons";
const Form = styled.form`
display: flex;
@ -65,8 +65,6 @@ const ActionButton = styled(BaseButton)`
const JSONEditorFieldWrapper = styled.div`
margin: 5px;
border: 1px solid #d0d7dd;
border-radius: 4px;
`;
const DatasourceWrapper = styled.div`
@ -107,7 +105,11 @@ const ApiEditorForm: React.FC<Props> = (props: Props) => {
{isSaving && <LoadingOverlayScreen>Saving...</LoadingOverlayScreen>}
<MainConfiguration>
<FormRow>
<TextField name="name" placeholder="API Name *" showError />
<TextField
name="name"
placeholder="API name (camel case)"
showError
/>
<ActionButtons>
<ActionButton
text="Delete"
@ -141,10 +143,9 @@ const ApiEditorForm: React.FC<Props> = (props: Props) => {
<DatasourcesField name="datasource.id" pluginId={pluginId} />
</DatasourceWrapper>
<DynamicTextField
placeholder="API Path"
placeholder="v1/method"
name="actionConfiguration.path"
leftIcon="slash"
showError
leftIcon={FormIcons.SLASH_ICON}
/>
</FormRow>
</MainConfiguration>
@ -162,7 +163,12 @@ const ApiEditorForm: React.FC<Props> = (props: Props) => {
<React.Fragment>
<FormLabel>{"Post Body"}</FormLabel>
<JSONEditorFieldWrapper>
<JSONEditorField name="actionConfiguration.body" />
<DynamicTextField
name="actionConfiguration.body"
height={300}
showLineNumbers
allowTabIndent
/>
</JSONEditorFieldWrapper>
</React.Fragment>
)}

View File

@ -0,0 +1,219 @@
import React, { useLayoutEffect } from "react";
import { AppState } from "reducers";
import { Link, withRouter, RouteComponentProps } from "react-router-dom";
import { connect } from "react-redux";
import { InjectedFormProps, reduxForm, Field } from "redux-form";
import { CREATE_PASSWORD_FORM_NAME } from "constants/forms";
import { ReduxActionTypes } from "constants/ReduxActionConstants";
import { getIsTokenValid, getIsValidatingToken } from "selectors/authSelectors";
import { Icon } from "@blueprintjs/core";
import TextField from "components/editorComponents/form/fields/TextField";
import FormMessage, {
FormMessageProps,
MessageAction,
} from "components/editorComponents/form/FormMessage";
import Spinner from "components/editorComponents/Spinner";
import FormButton from "components/editorComponents/FormButton";
import FormGroup from "components/editorComponents/form/FormGroup";
import StyledForm from "components/editorComponents/Form";
import { isEmptyString, isStrongPassword } from "utils/formhelpers";
import {
CreatePasswordFormValues,
createPasswordSubmitHandler,
} from "./helpers";
import {
AuthCardHeader,
AuthCardFooter,
AuthCardContainer,
AuthCardBody,
AuthCardNavLink,
FormActions,
} from "./StyledComponents";
import { AUTH_LOGIN_URL } from "constants/routes";
import {
CREATE_PASSWORD_PAGE_PASSWORD_INPUT_LABEL,
CREATE_PASSWORD_PAGE_PASSWORD_INPUT_PLACEHOLDER,
CREATE_PASSWORD_LOGIN_LINK_TEXT,
CREATE_PASSWORD_SUBMIT_BUTTON_TEXT,
CREATE_PASSWORD_PAGE_SUBTITLE,
CREATE_PASSWORD_PAGE_TITLE,
FORM_VALIDATION_INVALID_PASSWORD,
FORM_VALIDATION_EMPTY_PASSWORD,
CREATE_PASSWORD_EXPIRED_TOKEN,
CREATE_PASSWORD_INVALID_TOKEN,
CREATE_PASSWORD_RESET_SUCCESS,
CREATE_PASSWORD_RESET_SUCCESS_LOGIN_LINK,
PRIVACY_POLICY_LINK,
TERMS_AND_CONDITIONS_LINK,
} from "constants/messages";
const validate = (values: CreatePasswordFormValues) => {
const errors: CreatePasswordFormValues = {};
if (!values.password || isEmptyString(values.password)) {
errors.password = FORM_VALIDATION_EMPTY_PASSWORD;
} else if (!isStrongPassword(values.password)) {
errors.password = FORM_VALIDATION_INVALID_PASSWORD;
}
return errors;
};
type CreatePasswordProps = InjectedFormProps<
CreatePasswordFormValues,
{
verifyToken: (token: string, email: string) => void;
isTokenValid: boolean;
validatingToken: boolean;
}
> & {
verifyToken: (token: string, email: string) => void;
isTokenValid: boolean;
validatingToken: boolean;
} & RouteComponentProps<{ email: string; token: string }>;
export const CreatePassword = (props: CreatePasswordProps) => {
const {
error,
handleSubmit,
pristine,
submitting,
submitSucceeded,
submitFailed,
initialValues,
isTokenValid,
validatingToken,
verifyToken,
valid,
} = props;
useLayoutEffect(() => {
if (initialValues.token && initialValues.email)
verifyToken(initialValues.token, initialValues.email);
}, [initialValues.token, initialValues.email, verifyToken]);
const showInvalidMessage = !initialValues.token || !initialValues.email;
const showExpiredMessage = !isTokenValid && !validatingToken;
const showSuccessMessage = submitSucceeded && !pristine;
const showFailureMessage = submitFailed && !!error;
let message = "";
let messageActions: MessageAction[] | undefined = undefined;
if (showExpiredMessage) {
message = CREATE_PASSWORD_EXPIRED_TOKEN;
}
if (showInvalidMessage) {
message = CREATE_PASSWORD_INVALID_TOKEN;
}
if (showSuccessMessage) {
message = CREATE_PASSWORD_RESET_SUCCESS;
messageActions = [
{
url: AUTH_LOGIN_URL,
text: CREATE_PASSWORD_RESET_SUCCESS_LOGIN_LINK,
intent: "success",
},
];
}
if (showFailureMessage) {
message = error;
}
const messageTagProps: FormMessageProps = {
intent:
showInvalidMessage || showExpiredMessage || showFailureMessage
? "danger"
: "success",
message,
actions: messageActions,
};
if (showInvalidMessage || showExpiredMessage) {
return <FormMessage {...messageTagProps} />;
}
if (!isTokenValid && validatingToken) {
return <Spinner />;
}
return (
<AuthCardContainer>
{(showSuccessMessage || showFailureMessage) && (
<FormMessage {...messageTagProps} />
)}
<AuthCardHeader>
<h1>{CREATE_PASSWORD_PAGE_TITLE}</h1>
<h5>{CREATE_PASSWORD_PAGE_SUBTITLE}</h5>
</AuthCardHeader>
<AuthCardBody>
<StyledForm onSubmit={handleSubmit(createPasswordSubmitHandler)}>
<FormGroup
intent={error ? "danger" : "none"}
label={CREATE_PASSWORD_PAGE_PASSWORD_INPUT_LABEL}
>
<TextField
name="password"
type="password"
placeholder={CREATE_PASSWORD_PAGE_PASSWORD_INPUT_PLACEHOLDER}
showError
/>
</FormGroup>
<Field type="hidden" name="email" component="input" />
<Field type="hidden" name="token" component="input" />
<FormActions>
<FormButton
type="submit"
text={CREATE_PASSWORD_SUBMIT_BUTTON_TEXT}
intent="primary"
disabled={pristine || !valid}
loading={submitting}
/>
</FormActions>
</StyledForm>
</AuthCardBody>
<AuthCardNavLink to={AUTH_LOGIN_URL}>
{CREATE_PASSWORD_LOGIN_LINK_TEXT}
<Icon icon="arrow-right" intent="primary" />
</AuthCardNavLink>
<AuthCardFooter>
<Link to="#">{PRIVACY_POLICY_LINK}</Link>
<Link to="#">{TERMS_AND_CONDITIONS_LINK}</Link>
</AuthCardFooter>
</AuthCardContainer>
);
};
export default connect(
(state: AppState, props: CreatePasswordProps) => {
const queryParams = new URLSearchParams(props.location.search);
return {
initialValues: {
email: queryParams.get("email") || undefined,
token: queryParams.get("token") || undefined,
},
isTokenValid: getIsTokenValid(state),
validatingToken: getIsValidatingToken(state),
};
},
(dispatch: any) => ({
verifyToken: (token: string, email: string) =>
dispatch({
type: ReduxActionTypes.VERIFY_INVITE_INIT,
payload: { token, email },
}),
}),
)(
reduxForm<
CreatePasswordFormValues,
{
verifyToken: (token: string, email: string) => void;
validatingToken: boolean;
isTokenValid: boolean;
}
>({
validate,
form: CREATE_PASSWORD_FORM_NAME,
touchOnBlur: true,
})(withRouter(CreatePassword)),
);

View File

@ -3,7 +3,11 @@ import { Link, useLocation } from "react-router-dom";
import { connect } from "react-redux";
import { InjectedFormProps, reduxForm, formValueSelector } from "redux-form";
import { Icon } from "@blueprintjs/core";
import { LOGIN_FORM_NAME } from "constants/forms";
import {
LOGIN_FORM_NAME,
LOGIN_FORM_EMAIL_FIELD_NAME,
LOGIN_FORM_PASSWORD_FIELD_NAME,
} from "constants/forms";
import { getAppsmithConfigs } from "configs";
import { FORGOT_PASSWORD_URL, SIGN_UP_URL } from "constants/routes";
import { LOGIN_SUBMIT_PATH } from "constants/ApiConstants";
@ -47,15 +51,17 @@ import {
const validate = (values: LoginFormValues) => {
const errors: LoginFormValues = {};
if (!values.password || isEmptyString(values.password)) {
errors.password = FORM_VALIDATION_EMPTY_PASSWORD;
} else if (!isStrongPassword(values.password)) {
errors.password = FORM_VALIDATION_INVALID_PASSWORD;
const email = values[LOGIN_FORM_EMAIL_FIELD_NAME];
const password = values[LOGIN_FORM_PASSWORD_FIELD_NAME];
if (!password || isEmptyString(password)) {
errors[LOGIN_FORM_PASSWORD_FIELD_NAME] = FORM_VALIDATION_EMPTY_PASSWORD;
} else if (!isStrongPassword(password)) {
errors[LOGIN_FORM_PASSWORD_FIELD_NAME] = FORM_VALIDATION_INVALID_PASSWORD;
}
if (!values.username || isEmptyString(values.username)) {
errors.username = FORM_VALIDATION_EMPTY_EMAIL;
} else if (!isEmail(values.username)) {
errors.username = FORM_VALIDATION_INVALID_EMAIL;
if (!email || isEmptyString(email)) {
errors[LOGIN_FORM_EMAIL_FIELD_NAME] = FORM_VALIDATION_EMPTY_EMAIL;
} else if (!isEmail(email)) {
errors[LOGIN_FORM_EMAIL_FIELD_NAME] = FORM_VALIDATION_INVALID_EMAIL;
}
return errors;
};
@ -66,7 +72,7 @@ type LoginFormProps = { emailValue: string } & InjectedFormProps<
>;
export const Login = (props: LoginFormProps) => {
const { error, pristine } = props;
const { error, pristine, valid } = props;
const location = useLocation();
const queryParams = new URLSearchParams(location.search);
@ -108,7 +114,7 @@ export const Login = (props: LoginFormProps) => {
label={LOGIN_PAGE_EMAIL_INPUT_LABEL}
>
<TextField
name="username"
name={LOGIN_FORM_EMAIL_FIELD_NAME}
type="email"
placeholder={LOGIN_PAGE_EMAIL_INPUT_PLACEHOLDER}
showError
@ -120,7 +126,7 @@ export const Login = (props: LoginFormProps) => {
>
<TextField
type="password"
name="password"
name={LOGIN_FORM_PASSWORD_FIELD_NAME}
placeholder={LOGIN_PAGE_PASSWORD_INPUT_PLACEHOLDER}
showError
/>
@ -129,6 +135,7 @@ export const Login = (props: LoginFormProps) => {
<FormActions>
<FormButton
type="submit"
disabled={pristine || !valid}
text={LOGIN_PAGE_LOGIN_BUTTON_TEXT}
intent="primary"
/>
@ -153,7 +160,7 @@ export const Login = (props: LoginFormProps) => {
const selector = formValueSelector(LOGIN_FORM_NAME);
export default connect(state => ({
emailValue: selector(state, "email"),
emailValue: selector(state, LOGIN_FORM_EMAIL_FIELD_NAME),
}))(
reduxForm<LoginFormValues, { emailValue: string }>({
validate,

View File

@ -65,6 +65,7 @@ export const SignUp = (props: InjectedFormProps<SignupFormValues>) => {
submitFailed,
submitSucceeded,
pristine,
valid,
} = props;
return (
<AuthCardContainer>
@ -113,7 +114,7 @@ export const SignUp = (props: InjectedFormProps<SignupFormValues>) => {
<FormActions>
<FormButton
type="submit"
disabled={pristine}
disabled={pristine || !valid}
loading={submitting}
text={SIGNUP_PAGE_SUBMIT_BUTTON_TEXT}
intent="primary"

View File

@ -19,6 +19,8 @@ export type ResetPasswordFormValues = {
email?: string;
};
export type CreatePasswordFormValues = ResetPasswordFormValues;
export type ForgotPasswordFormValues = {
email?: string;
};
@ -64,6 +66,27 @@ export const resetPasswordSubmitHandler = (
});
};
export const createPasswordSubmitHandler = (
values: CreatePasswordFormValues,
dispatch: any,
): Promise<any> => {
const { token, email, password } = values;
return new Promise((resolve, reject) => {
dispatch({
type: ReduxActionTypes.INVITED_USER_SIGNUP_INIT,
payload: {
resolve,
reject,
token,
email,
password,
},
});
}).catch(error => {
throw new SubmissionError(error);
});
};
export const forgotPasswordSubmitHandler = (
values: ForgotPasswordFormValues,
dispatch: any,

View File

@ -8,6 +8,7 @@ import { AuthContainer, AuthCard } from "./StyledComponents";
import SignUp from "./SignUp";
import ForgotPassword from "./ForgotPassword";
import ResetPassword from "./ResetPassword";
import CreatePassword from "./CreatePassword";
export const UserAuth = () => {
const { path } = useRouteMatch();
@ -31,6 +32,11 @@ export const UserAuth = () => {
path={`${path}/forgotPassword`}
component={ForgotPassword}
/>
<Route
exact
path={`${path}/createPassword`}
component={CreatePassword}
/>
</Switch>
</CSSTransition>
</TransitionGroup>

View File

@ -60,6 +60,9 @@ export const options = (
{
content: <Link to="/org/settings">Organization Settings</Link>,
},
{
content: <Link to="/applications">Applications</Link>,
},
{
content: <Link to="/org/users">Members</Link>,
},

View File

@ -2,19 +2,14 @@ import React from "react";
import { Route } from "react-router-dom";
import { useDispatch } from "react-redux";
import { useSelector } from "store";
import { ReduxActionTypes } from "constants/ReduxActionConstants";
import { hasAuthExpired } from "utils/storage";
import { User } from "constants/userConstants";
import { setCurrentUserDetails } from "actions/userActions";
export const checkAuth = (dispatch: any, currentUser?: User) => {
return hasAuthExpired().then(hasExpired => {
if (!currentUser || hasExpired) {
dispatch({
type: ReduxActionTypes.SET_CURRENT_USER_INIT,
payload: {
id: "me",
},
});
dispatch(setCurrentUserDetails());
}
});
};

View File

@ -1,4 +1,4 @@
import React, { useLayoutEffect } from "react";
import React, { useEffect } from "react";
import { connect } from "react-redux";
import styled from "styled-components";
import { useHistory } from "react-router-dom";
@ -37,7 +37,7 @@ import FormGroup from "components/editorComponents/form/FormGroup";
import SelectField from "components/editorComponents/form/fields/SelectField";
import { ReduxActionTypes } from "constants/ReduxActionConstants";
import { AppState } from "reducers";
import { getRoles } from "selectors/organizationSelectors";
import { getRoles, getDefaultRole } from "selectors/organizationSelectors";
import { OrgRole } from "constants/orgConstants";
import { isEmail } from "utils/formhelpers";
@ -110,9 +110,10 @@ const StyledInviteFieldGroup = styled.div`
const renderInviteUsersByRoleForm = (
renderer: WrappedFieldArrayProps<InviteUsersToOrgByRoleValues> & {
roles?: OrgRole[];
role?: OrgRole;
},
) => {
const { fields, roles } = renderer;
const { fields, roles, role } = renderer;
return (
<React.Fragment>
{fields.map((field, index) => {
@ -148,7 +149,12 @@ const renderInviteUsersByRoleForm = (
);
})}
<FormActionButton
onClick={() => fields.push({ id: generateReactKey() })}
onClick={() =>
fields.push({
id: generateReactKey(),
role: !!role ? role.id : undefined,
})
}
text={INVITE_USERS_ADD_EMAIL_LIST_FIELD}
large
icon="plus"
@ -162,10 +168,12 @@ type InviteUsersFormProps = InjectedFormProps<
{
fetchRoles: () => void;
roles?: OrgRole[];
defaultRole?: OrgRole;
}
> & {
fetchRoles: () => void;
roles?: OrgRole[];
defaultRole?: OrgRole;
};
export const InviteUsersForm = (props: InviteUsersFormProps) => {
@ -177,13 +185,27 @@ export const InviteUsersForm = (props: InviteUsersFormProps) => {
error,
fetchRoles,
roles,
initialize,
defaultRole,
pristine,
} = props;
const history = useHistory();
useLayoutEffect(() => {
useEffect(() => {
if (!roles) {
fetchRoles();
} else {
initialize({
usersByRole: [
{
id: generateReactKey(),
role: !!defaultRole ? defaultRole.id : undefined,
},
],
});
}
}, [fetchRoles, roles]);
}, [fetchRoles, roles, defaultRole, initialize]);
return (
<StyledForm>
{submitSucceeded && (
@ -210,6 +232,7 @@ export const InviteUsersForm = (props: InviteUsersFormProps) => {
<FormFooter
divider
onSubmit={handleSubmit(inviteUsersToOrgSubmitHandler)}
canSubmit={!pristine}
submitting={submitting && !submitFailed}
onCancel={() => history.goBack()}
submitOnEnter={false}
@ -223,6 +246,7 @@ export default connect(
(state: AppState) => {
return {
roles: getRoles(state),
defaultRole: getDefaultRole(state),
};
},
(dispatch: any) => ({
@ -231,16 +255,9 @@ export default connect(
)(
reduxForm<
InviteUsersToOrgFormValues,
{ fetchRoles: () => void; roles?: OrgRole[] }
{ fetchRoles: () => void; roles?: OrgRole[]; defaultRole?: OrgRole }
>({
form: INVITE_USERS_TO_ORG_FORM,
validate,
initialValues: {
usersByRole: [
{
id: generateReactKey(),
},
],
},
})(InviteUsersForm),
);

View File

@ -22,6 +22,18 @@ const authReducer = createReducer(initialState, {
isValidatingToken: false,
isTokenValid: false,
}),
[ReduxActionTypes.VERIFY_INVITE_INIT]: () => ({
isTokenValid: false,
isValidatingToken: true,
}),
[ReduxActionTypes.VERIFY_INVITE_SUCCESS]: () => ({
isValidatingToken: false,
isTokenValid: true,
}),
[ReduxActionErrorTypes.VERIFY_INVITE_ERROR]: () => ({
isValidatingToken: false,
isTokenValid: false,
}),
});
export interface AuthState {

View File

@ -34,11 +34,12 @@ import {
createActionSuccess,
deleteActionSuccess,
FetchActionsPayload,
runApiAction,
updateActionSuccess,
} from "actions/actionActions";
import {
evaluateDynamicBoundValue,
getDynamicBindings,
getDynamicValue,
isDynamicValue,
} from "utils/DynamicBindingUtils";
import { validateResponse } from "./ErrorSagas";
@ -83,7 +84,7 @@ const createActionErrorResponse = (
export function* evaluateDynamicBoundValueSaga(path: string): any {
const nameBindingsWithData = yield select(getNameBindingsWithData);
return evaluateDynamicBoundValue(nameBindingsWithData, path);
return getDynamicValue(`{{${path}}}`, nameBindingsWithData);
}
export function* getActionParams(jsonPathKeys: string[] | undefined) {
@ -97,7 +98,9 @@ export function* getActionParams(jsonPathKeys: string[] | undefined) {
);
const dynamicBindings: Record<string, string> = {};
jsonPathKeys.forEach((key, i) => {
dynamicBindings[key] = values[i];
let value = values[i];
if (typeof value === "object") value = JSON.stringify(value);
dynamicBindings[key] = value;
});
return mapToPropList(dynamicBindings);
}
@ -303,6 +306,7 @@ export function* updateActionSaga(
intent: Intent.SUCCESS,
});
yield put(updateActionSuccess({ data: response.data }));
yield put(runApiAction(data.id));
}
} catch (error) {
yield put({
@ -344,8 +348,9 @@ export function* runApiActionSaga(action: ReduxAction<string>) {
getFormData,
API_EDITOR_FORM_NAME,
);
const actionObject: PageAction = yield select(getAction, values.id);
let action: ExecuteActionRequest["action"] = { id: values.id };
let jsonPathKeys = values.jsonPathKeys;
let jsonPathKeys = actionObject.jsonPathKeys;
if (!valid) {
console.error("Form error");
return;
@ -356,7 +361,7 @@ export function* runApiActionSaga(action: ReduxAction<string>) {
if (isDynamicValue(actionString)) {
const { paths } = getDynamicBindings(actionString);
// Replace cause the existing keys could have been updated
jsonPathKeys = paths;
jsonPathKeys = paths.filter(path => !!path);
} else {
jsonPathKeys = [];
}

View File

@ -88,9 +88,9 @@ function* syncApiParamsSaga(
}
} else if (field.includes("actionConfiguration.queryParameters")) {
const { values } = yield select(getFormData, API_EDITOR_FORM_NAME);
const path = values.actionConfiguration.path;
const path = values.actionConfiguration.path || "";
const pathHasParams = path.indexOf("?") > -1;
const currentPath = values.actionConfiguration.path.substring(
const currentPath = path.substring(
0,
pathHasParams ? path.indexOf("?") : undefined,
);
@ -127,14 +127,16 @@ function* changeApiSaga(actionPayload: ReduxAction<{ id: string }>) {
const data = _.isEmpty(draft) ? action : draft;
yield put(initialize(API_EDITOR_FORM_NAME, data));
history.push(API_EDITOR_ID_URL(applicationId, pageId, id));
// Sync the api params my mocking a change action
yield call(syncApiParamsSaga, {
type: ReduxFormActionTypes.ARRAY_REMOVE,
payload: data.actionConfiguration.queryParameters,
meta: {
field: "actionConfiguration.queryParameters",
},
});
if (data.actionConfiguration && data.actionConfiguration.queryParameters) {
// Sync the api params my mocking a change action
yield call(syncApiParamsSaga, {
type: ReduxFormActionTypes.ARRAY_REMOVE,
payload: data.actionConfiguration.queryParameters,
meta: {
field: "actionConfiguration.queryParameters",
},
});
}
}
function* updateDraftsSaga() {

View File

@ -1,6 +1,7 @@
import { call, takeLatest, put, all } from "redux-saga/effects";
import {
ReduxAction,
ReduxActionWithPromise,
ReduxActionTypes,
ReduxActionErrorTypes,
} from "constants/ReduxActionConstants";
@ -8,8 +9,8 @@ import UserApi, {
CreateUserRequest,
CreateUserResponse,
ForgotPasswordRequest,
ResetPasswordRequest,
ResetPasswordVerifyTokenRequest,
VerifyTokenRequest,
TokenPasswordUpdateRequest,
FetchUserRequest,
FetchUserResponse,
} from "api/UserApi";
@ -23,14 +24,18 @@ import {
import { fetchOrgsSaga } from "./OrgSagas";
import { resetAuthExpiration } from "utils/storage";
import {
logoutUserSuccess,
fetchCurrentUser,
logoutUserError,
verifyInviteSuccess,
verifyInviteError,
invitedUserSignupError,
invitedUserSignupSuccess,
} from "actions/userActions";
export function* createUserSaga(
action: ReduxAction<{
resolve: any;
reject: any;
email: string;
password: string;
}>,
action: ReduxActionWithPromise<CreateUserRequest>,
) {
const { email, password, resolve, reject } = action.payload;
try {
@ -68,7 +73,7 @@ export function* createUserSaga(
}
export function* forgotPasswordSaga(
action: ReduxAction<{ resolve: any; reject: any; email: string }>,
action: ReduxActionWithPromise<ForgotPasswordRequest>,
) {
const { email, resolve, reject } = action.payload;
@ -98,21 +103,13 @@ export function* forgotPasswordSaga(
}
export function* resetPasswordSaga(
action: ReduxAction<{
resolve: any;
reject: any;
email: string;
token: string;
password: string;
}>,
action: ReduxActionWithPromise<TokenPasswordUpdateRequest>,
) {
const { email, token, password, resolve, reject } = action.payload;
try {
const request: ResetPasswordRequest = {
user: {
email,
password,
},
const request: TokenPasswordUpdateRequest = {
email,
password,
token,
};
const response: ApiResponse = yield callAPI(UserApi.resetPassword, request);
@ -137,6 +134,32 @@ export function* resetPasswordSaga(
});
}
}
export function* invitedUserSignupSaga(
action: ReduxActionWithPromise<TokenPasswordUpdateRequest>,
) {
const { email, token, password, resolve, reject } = action.payload;
try {
const request: TokenPasswordUpdateRequest = { email, password, token };
const response: ApiResponse = yield callAPI(
UserApi.confirmInvitedUserSignup,
request,
);
const isValidResponse = yield validateResponse(response);
if (!isValidResponse) {
const errorMessage = yield getResponseErrorMessage(response);
yield call(reject, { _error: errorMessage });
} else {
yield put(invitedUserSignupSuccess());
yield call(resolve);
}
} catch (error) {
console.log(error);
yield call(reject, { _error: error.message });
yield put(invitedUserSignupError(error));
}
}
type InviteUserPayload = {
email: string;
groupIds: string[];
@ -154,10 +177,8 @@ export function* inviteUser(payload: InviteUserPayload, reject: any) {
}
export function* inviteUsers(
action: ReduxAction<{
action: ReduxActionWithPromise<{
data: Array<{ roleId: string; emails: string[] }>;
resolve: any;
reject: any;
}>,
) {
try {
@ -200,11 +221,11 @@ export function* inviteUsers(
}
export function* verifyResetPasswordTokenSaga(
action: ReduxAction<{ token: string; email: string }>,
action: ReduxAction<VerifyTokenRequest>,
) {
try {
const request: ResetPasswordVerifyTokenRequest = action.payload;
const response: ApiResponse = yield callAPI(
const request: VerifyTokenRequest = action.payload;
const response: ApiResponse = yield call(
UserApi.verifyResetPasswordToken,
request,
);
@ -222,6 +243,20 @@ export function* verifyResetPasswordTokenSaga(
}
}
export function* verifyUserInviteSaga(action: ReduxAction<VerifyTokenRequest>) {
try {
const request: VerifyTokenRequest = action.payload;
const response: ApiResponse = yield call(UserApi.verifyUserInvite, request);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
yield put(verifyInviteSuccess());
}
} catch (error) {
console.log(error);
yield put(verifyInviteError(error));
}
}
export function* fetchUserSaga(action: ReduxAction<FetchUserRequest>) {
try {
const request: FetchUserRequest = action.payload;
@ -255,6 +290,20 @@ export function* setCurrentUserSaga(action: ReduxAction<FetchUserRequest>) {
}
}
export function* logoutSaga() {
try {
const response: ApiResponse = yield call(UserApi.logoutUser);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
yield put(logoutUserSuccess());
yield put(fetchCurrentUser());
}
} catch (error) {
console.log(error);
yield put(logoutUserError(error));
}
}
export default function* userSagas() {
yield all([
takeLatest(ReduxActionTypes.CREATE_USER_INIT, createUserSaga),
@ -267,5 +316,11 @@ export default function* userSagas() {
takeLatest(ReduxActionTypes.INVITE_USERS_TO_ORG_INIT, inviteUsers),
takeLatest(ReduxActionTypes.FETCH_USER_INIT, fetchUserSaga),
takeLatest(ReduxActionTypes.SET_CURRENT_USER_INIT, setCurrentUserSaga),
takeLatest(ReduxActionTypes.LOGOUT_USER_INIT, logoutSaga),
takeLatest(ReduxActionTypes.VERIFY_INVITE_INIT, verifyUserInviteSaga),
takeLatest(
ReduxActionTypes.INVITED_USER_SIGNUP_INIT,
invitedUserSignupSaga,
),
]);
}

View File

@ -1,4 +1,5 @@
import { AppState, DataTree } from "reducers";
import { ActionDataState } from "reducers/entityReducers/actionsReducer";
export const getDataTree = (state: AppState): DataTree => state.entities;
@ -15,3 +16,6 @@ export const getPluginIdOfName = (
if (!plugin) return undefined;
return plugin.id;
};
export const getActions = (state: AppState): ActionDataState["data"] =>
state.entities.actions.data;

View File

@ -1,5 +1,6 @@
import { getFormValues, isDirty, isValid } from "redux-form";
import { getFormValues, isValid } from "redux-form";
import { AppState } from "reducers";
import { RestAction } from "api/ActionAPI";
type GetFormData = (
state: AppState,
@ -7,8 +8,9 @@ type GetFormData = (
) => { values: object; dirty: boolean; valid: boolean };
export const getFormData: GetFormData = (state, formName) => {
const values = getFormValues(formName)(state);
const dirty = isDirty(formName)(state);
const values = getFormValues(formName)(state) as RestAction;
const drafts = state.ui.apiPane.drafts;
const dirty = values.id in drafts;
const valid = isValid(formName)(state);
return { values, dirty, valid };
};

View File

@ -1,7 +1,9 @@
import { DataTree } from "reducers";
import { AppState, DataTree } from "reducers";
import { JSONPath } from "jsonpath-plus";
import { createSelector } from "reselect";
import { getDataTree } from "./entitiesSelector";
import { getActions, getDataTree } from "./entitiesSelector";
import { ActionDataState } from "reducers/entityReducers/actionsReducer";
import createCachedSelector from "re-reselect";
export type NameBindingsWithData = Record<string, object>;
export const getNameBindingsWithData = createSelector(
@ -22,3 +24,25 @@ export const getNameBindingsWithData = createSelector(
return nameBindingsWithData;
},
);
// For autocomplete. Use actions cached responses if
// there isn't a response already
export const getNameBindingsForAutocomplete = createCachedSelector(
getNameBindingsWithData,
getActions,
(namedBindings: NameBindingsWithData, actions: ActionDataState["data"]) => {
const cachedResponses: Record<string, any> = {};
if (actions && actions.length) {
actions.forEach(action => {
if (!(action.name in namedBindings) && action.cacheResponse) {
try {
cachedResponses[action.name] = JSON.parse(action.cacheResponse);
} catch (e) {
cachedResponses[action.name] = action.cacheResponse;
}
}
});
}
return { ...namedBindings, ...cachedResponses };
},
)((state: AppState) => state.entities.actions.data.length);

View File

@ -20,3 +20,7 @@ export const getCurrentOrg = createSelector(
export const getRoles = (state: AppState): OrgRole[] | undefined => {
return state.ui.orgs.roles;
};
export const getDefaultRole = (state: AppState): OrgRole | undefined => {
return state.ui.orgs.roles?.find(role => role.isDefault);
};

View File

@ -1,25 +1,10 @@
import _ from "lodash";
import { WidgetProps } from "widgets/BaseWidget";
import {
DATA_BIND_AUTOCOMPLETE,
DATA_BIND_REGEX,
} from "constants/BindingsConstants";
import { DATA_BIND_REGEX } from "constants/BindingsConstants";
import ValidationFactory from "./ValidationFactory";
import JSExecutionManagerSingleton from "jsExecution/JSExecutionManagerSingleton";
import { NameBindingsWithData } from "selectors/nameBindingsWithDataSelector";
export const isDynamicAutocompleteMatch = (value: string): boolean =>
DATA_BIND_AUTOCOMPLETE.test(value);
export const getDynamicAutocompleteSearchTerm = (value: string): string => {
const bindings = value.match(DATA_BIND_AUTOCOMPLETE) || [];
if (bindings.length > 0) {
return bindings[2];
} else {
return "";
}
};
export const isDynamicValue = (value: string): boolean =>
DATA_BIND_REGEX.test(value);

View File

@ -19,6 +19,7 @@ class TextWidget extends BaseWidget<TextWidgetProps, WidgetState> {
widgetId={this.props.widgetId}
key={this.props.widgetId}
textStyle={this.props.textStyle}
allowHtml={this.props.allowHtml}
text={this.props.text}
isLoading={this.props.isLoading}
/>
@ -36,6 +37,7 @@ export interface TextWidgetProps extends WidgetProps {
text?: string;
textStyle: TextStyle;
isLoading: boolean;
allowHtml: boolean;
}
export default TextWidget;

View File

@ -2246,9 +2246,9 @@
"@babel/types" "^7.3.0"
"@types/chance@^1.0.7":
version "1.0.7"
resolved "https://registry.yarnpkg.com/@types/chance/-/chance-1.0.7.tgz#c680a3891a505d8c626ec3a46bb1c0496419dfb6"
integrity sha512-LBOkJ7899SSLm08KicLYX3DqWUhfDspMLWNGuV1UPpL3iUENSvI0THGlf05n9yNHTR7zDlV/mCGZ7ZJ0ws8v3Q==
version "1.0.8"
resolved "https://registry.yarnpkg.com/@types/chance/-/chance-1.0.8.tgz#48e0a5648f54487a9128915fe090154fb1a2f348"
integrity sha512-S1VWROFGexiUYyBFsP0NRjp6PkJ4rKECQ3HQJ0cJKW/2BcNyas9lXATRdnTNnN4CsICjEXTltAalAo8dJQQLKg==
"@types/codemirror@^0.0.82":
version "0.0.82"
@ -2340,16 +2340,16 @@
"@types/istanbul-lib-report" "*"
"@types/jest@^24.0.22":
version "24.0.24"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-24.0.24.tgz#0f2f523dc77cc1bc6bef34eaf287ede887a73f05"
integrity sha512-vgaG968EDPSJPMunEDdZvZgvxYSmeH8wKqBlHSkBt1pV2XlLEVDzsj1ZhLuI4iG4Pv841tES61txSBF0obh4CQ==
version "24.0.25"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-24.0.25.tgz#2aba377824ce040114aa906ad2cac2c85351360f"
integrity sha512-hnP1WpjN4KbGEK4dLayul6lgtys6FPz0UfxMeMQCv0M+sTnzN3ConfiO72jHgLxl119guHgI8gLqDOrRLsyp2g==
dependencies:
jest-diff "^24.3.0"
"@types/json-schema@^7.0.3":
version "7.0.3"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.3.tgz#bdfd69d61e464dcc81b25159c270d75a73c1a636"
integrity sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==
version "7.0.4"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339"
integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==
"@types/lodash@^4.14.120":
version "4.14.149"
@ -2376,14 +2376,14 @@
"@types/node" "*"
"@types/node@*":
version "12.12.21"
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.21.tgz#aa44a6363291c7037111c47e4661ad210aded23f"
integrity sha512-8sRGhbpU+ck1n0PGAUgVrWrWdjSW2aqNeyC15W88GRsMpSwzv6RJGlLhE7s2RhVSOdyDmxbqlWSeThq4/7xqlA==
version "13.1.2"
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.1.2.tgz#fe94285bf5e0782e1a9e5a8c482b1c34465fa385"
integrity sha512-B8emQA1qeKerqd1dmIsQYnXi+mmAzTB7flExjmy5X1aVAKFNNNDubkavwR13kR6JnpeLp3aLoJhwn9trWPAyFQ==
"@types/node@^10.12.18":
version "10.17.11"
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.11.tgz#46ba035fb917b31c948280dbea22ab8838f386a4"
integrity sha512-dNd2pp8qTzzNLAs3O8nH3iU9DG9866KHq9L3ISPB7DOGERZN81nW/5/g/KzMJpCU8jrbCiMRBzV9/sCEdRosig==
version "10.17.13"
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.13.tgz#ccebcdb990bd6139cd16e84c39dc2fb1023ca90c"
integrity sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg==
"@types/normalize-package-data@^2.4.0":
version "2.4.0"
@ -2435,9 +2435,9 @@
"@types/react" "*"
"@types/react-native@*":
version "0.60.25"
resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.60.25.tgz#65cb0bf5dd0631079215b63525458e4123c1c90e"
integrity sha512-827dIVvSTxSH5uTpsJJH7O4wpRuw0rm3yIzRL3a2yKawA0nyhgC1GPKTXHFIn2GfSdXn1Gty2dJ+k6uDZF3MWQ==
version "0.60.27"
resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.60.27.tgz#d6313256f3cf9a685bd8885efddb86b85b9a4820"
integrity sha512-W8YrhnnHBwybruJp57enWc9dP9G7okcQkm88TMPZZKMVNiJIZj17q/rruMLuuuER+SaGrYjhO+BA3MpfkiTaSQ==
dependencies:
"@types/prop-types" "*"
"@types/react" "*"
@ -2596,9 +2596,9 @@
source-map "^0.6.1"
"@types/webpack@^4.41.0":
version "4.41.0"
resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.0.tgz#b813a044d8b0dec7dfcd7622fdbe327bde06eb9a"
integrity sha512-tWkdf9nO0zFgAY/EumUKwrDUhraHKDqCPhwfFR/R8l0qnPdgb9le0Gzhvb7uzVpouuDGBgiE//ZdY+5jcZy2TA==
version "4.41.1"
resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.1.tgz#571f0e137ce698710dd2637f7d222811eb83e274"
integrity sha512-n9fP8UrMxOi1wiM3oM+vMZHMJJ7WoQohqd63C20cmKOFkNEy9Q8hyZyDR6PWdvSYt3V3A7cwDq/kWxHlRYYZEg==
dependencies:
"@types/anymatch" "*"
"@types/node" "*"
@ -2613,46 +2613,46 @@
integrity sha512-gCubfBUZ6KxzoibJ+SCUc/57Ms1jz5NjHe4+dI2krNmU5zCPAphyLJYyTOg06ueIyfj+SaCUqmzun7ImlxDcKg==
"@types/yargs@^13.0.0":
version "13.0.3"
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.3.tgz#76482af3981d4412d65371a318f992d33464a380"
integrity sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==
version "13.0.4"
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.4.tgz#53d231cebe1a540e7e13727fc1f0d13ad4a9ba3b"
integrity sha512-Ke1WmBbIkVM8bpvsNEcGgQM70XcEh/nbpxQhW7FhrsbCsXSY9BmLB1+LHtD7r9zrsOcFlLiF+a/UeJsdfw3C5A==
dependencies:
"@types/yargs-parser" "*"
"@typescript-eslint/eslint-plugin@^2.0.0", "@typescript-eslint/eslint-plugin@^2.8.0":
version "2.12.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.12.0.tgz#0da7cbca7b24f4c6919e9eb31c704bfb126f90ad"
integrity sha512-1t4r9rpLuEwl3hgt90jY18wJHSyb0E3orVL3DaqwmpiSDHmHiSspVsvsFF78BJ/3NNG3qmeso836jpuBWYziAA==
version "2.14.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.14.0.tgz#c74447400537d4eb7aae1e31879ab43e6c662a8a"
integrity sha512-sneOJ3Hu0m5whJiVIxGBZZZMxMJ7c0LhAJzeMJgHo+n5wFs+/6rSR/gl7crkdR2kNwfOOSdzdc0gMvatG4dX2Q==
dependencies:
"@typescript-eslint/experimental-utils" "2.12.0"
"@typescript-eslint/experimental-utils" "2.14.0"
eslint-utils "^1.4.3"
functional-red-black-tree "^1.0.1"
regexpp "^3.0.0"
tsutils "^3.17.1"
"@typescript-eslint/experimental-utils@2.12.0":
version "2.12.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.12.0.tgz#e0a76ffb6293e058748408a191921e453c31d40d"
integrity sha512-jv4gYpw5N5BrWF3ntROvCuLe1IjRenLy5+U57J24NbPGwZFAjhnM45qpq0nDH1y/AZMb3Br25YiNVwyPbz6RkA==
"@typescript-eslint/experimental-utils@2.14.0":
version "2.14.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.14.0.tgz#e9179fa3c44e00b3106b85d7b69342901fb43e3b"
integrity sha512-KcyKS7G6IWnIgl3ZpyxyBCxhkBPV+0a5Jjy2g5HxlrbG2ZLQNFeneIBVXdaBCYOVjvGmGGFKom1kgiAY75SDeQ==
dependencies:
"@types/json-schema" "^7.0.3"
"@typescript-eslint/typescript-estree" "2.12.0"
"@typescript-eslint/typescript-estree" "2.14.0"
eslint-scope "^5.0.0"
"@typescript-eslint/parser@^2.0.0", "@typescript-eslint/parser@^2.8.0":
version "2.12.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.12.0.tgz#393f1604943a4ca570bb1a45bc8834e9b9158884"
integrity sha512-lPdkwpdzxEfjI8TyTzZqPatkrswLSVu4bqUgnB03fHSOwpC7KSerPgJRgIAf11UGNf7HKjJV6oaPZI4AghLU6g==
version "2.14.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.14.0.tgz#30fa0523d86d74172a5e32274558404ba4262cd6"
integrity sha512-haS+8D35fUydIs+zdSf4BxpOartb/DjrZ2IxQ5sR8zyGfd77uT9ZJZYF8+I0WPhzqHmfafUBx8MYpcp8pfaoSA==
dependencies:
"@types/eslint-visitor-keys" "^1.0.0"
"@typescript-eslint/experimental-utils" "2.12.0"
"@typescript-eslint/typescript-estree" "2.12.0"
"@typescript-eslint/experimental-utils" "2.14.0"
"@typescript-eslint/typescript-estree" "2.14.0"
eslint-visitor-keys "^1.1.0"
"@typescript-eslint/typescript-estree@2.12.0":
version "2.12.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.12.0.tgz#bd9e547ccffd17dfab0c3ab0947c80c8e2eb914c"
integrity sha512-rGehVfjHEn8Frh9UW02ZZIfJs6SIIxIu/K1bbci8rFfDE/1lQ8krIJy5OXOV3DVnNdDPtoiPOdEANkLMrwXbiQ==
"@typescript-eslint/typescript-estree@2.14.0":
version "2.14.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.14.0.tgz#c67698acdc14547f095eeefe908958d93e1a648d"
integrity sha512-pnLpUcMNG7GfFFfNQbEX6f1aPa5fMnH2G9By+A1yovYI4VIOK2DzkaRuUlIkbagpAcrxQHLqovI1YWqEcXyRnA==
dependencies:
debug "^4.1.1"
eslint-visitor-keys "^1.1.0"
@ -3184,9 +3184,9 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1:
color-convert "^1.9.0"
ansi-styles@^4.0.0, ansi-styles@^4.1.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.0.tgz#5681f0dcf7ae5880a7841d8831c4724ed9cc0172"
integrity sha512-7kFQgnEaMdRtwf6uSfUnVr9gSGC7faurn+J/Mv90/W+iTtN0405/nLdopfMWwchyxhbGYl6TC4Sccn9TUkGAgg==
version "4.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359"
integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==
dependencies:
"@types/color-name" "^1.1.1"
color-convert "^2.0.1"
@ -3452,7 +3452,7 @@ asynckit@^0.4.0:
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
atob@^2.1.1:
atob@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
@ -4174,13 +4174,13 @@ browserslist@4.7.3:
node-releases "^1.1.40"
browserslist@^4.0.0, browserslist@^4.6.0, browserslist@^4.6.2, browserslist@^4.6.4, browserslist@^4.8.0, browserslist@^4.8.2:
version "4.8.2"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.8.2.tgz#b45720ad5fbc8713b7253c20766f701c9a694289"
integrity sha512-+M4oeaTplPm/f1pXDw84YohEv7B1i/2Aisei8s4s6k3QsoSHa7i5sz8u/cGQkkatCPxMASKxPualR4wwYgVboA==
version "4.8.3"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.8.3.tgz#65802fcd77177c878e015f0e3189f2c4f627ba44"
integrity sha512-iU43cMMknxG1ClEZ2MDKeonKE1CCrFVkQK2AqO2YWFmvIrx4JWrvQ4w4hQez6EpVI8rHTtqh/ruHHDHSOKxvUg==
dependencies:
caniuse-lite "^1.0.30001015"
caniuse-lite "^1.0.30001017"
electron-to-chromium "^1.3.322"
node-releases "^1.1.42"
node-releases "^1.1.44"
bser@2.1.1:
version "2.1.1"
@ -4395,10 +4395,10 @@ caniuse-api@^3.0.0:
lodash.memoize "^4.1.2"
lodash.uniq "^4.5.0"
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30000989, caniuse-lite@^1.0.30001010, caniuse-lite@^1.0.30001012, caniuse-lite@^1.0.30001015:
version "1.0.30001016"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001016.tgz#16ea48d7d6e8caf3cad3295c2d746fe38c4e7f66"
integrity sha512-yYQ2QfotceRiH4U+h1Us86WJXtVHDmy3nEKIdYPsZCYnOV5/tMgGbmoIlrMzmh2VXlproqYtVaKeGDBkMZifFA==
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30000989, caniuse-lite@^1.0.30001010, caniuse-lite@^1.0.30001012, caniuse-lite@^1.0.30001017:
version "1.0.30001017"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001017.tgz#d3ad6ec18148b9bd991829958d9d7e562bb78cd6"
integrity sha512-EDnZyOJ6eYh6lHmCvCdHAFbfV4KJ9lSdfv4h/ppEhrU/Yudkl7jujwMZ1we6RX7DXqBfT04pVMQ4J+1wcTlsKA==
capture-exit@^2.0.0:
version "2.0.0"
@ -4695,9 +4695,9 @@ code-point-at@^1.0.0:
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
codemirror@^5.50.0:
version "5.50.0"
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.50.0.tgz#aeacd18f225735b17cbab98908edace87fedcdab"
integrity sha512-32LAmGcBNhKtJP4WGgkcaCVQDyChAyaWA6jasg778ziZzo3PWBuhpAQIJMO8//Id45RoaLyXjuhcRUBoS8Vg+Q==
version "5.50.2"
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.50.2.tgz#32ddfe2b50193fcf573d8141c4a31d267c92b4a3"
integrity sha512-PPjUsC1oXSM86lunKrw609P1oM0Wu8z9rqzjbeyBYCcx44VL41aUpccdOf1PfAZtTONlmN3sT3p2etLNYa1OGg==
collapse-white-space@^1.0.0, collapse-white-space@^1.0.2:
version "1.0.5"
@ -4940,17 +4940,17 @@ copy-to-clipboard@^3.0.8:
toggle-selection "^1.0.6"
core-js-compat@^3.1.1, core-js-compat@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.0.tgz#4eb6cb69d03d99159ed7c860cd5fcf7d23a62ea9"
integrity sha512-Z3eCNjGgoYluH89Jt4wVkfYsc/VdLrA2/woX5lm0isO/pCT+P+Y+o65bOuEnjDJLthdwTBxbCVzptTXtc18fJg==
version "3.6.1"
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.1.tgz#39638c935c83c93a793abb628b252ec43e85783a"
integrity sha512-2Tl1EuxZo94QS2VeH28Ebf5g3xbPZG/hj/N5HDDy4XMP/ImR0JIer/nggQRiMN91Q54JVkGbytf42wO29oXVHg==
dependencies:
browserslist "^4.8.2"
semver "7.0.0"
core-js-pure@^3.0.0, core-js-pure@^3.0.1:
version "3.6.0"
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.0.tgz#c86e14d9316659af04dd54266addc9271f6164f8"
integrity sha512-+YSSo7UFDFuVvMUr1HVFYArb22pYIKRDISBo6V50kRuS0MsXgsuDWmJYFu6dJsJupr77S486xRnDkr/BWQQonw==
version "3.6.1"
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.1.tgz#59acfb71caf2fb495aae4c1a0b2a7f2c1b65267e"
integrity sha512-yKiUdvQWq66xUc408duxUCxFHuDfz5trF5V4xnQzb8C7P/5v2gFUdyNWQoevyAeGYB1hl1X/pzGZ20R3WxZQBA==
core-js@^1.0.0:
version "1.2.7"
@ -4963,9 +4963,9 @@ core-js@^2.4.0:
integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==
core-js@^3.0.1, core-js@^3.0.4, core-js@^3.4.1:
version "3.6.0"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.0.tgz#2b854e451de1967d1e29896025cdc13a2518d9ea"
integrity sha512-AHPTNKzyB+YwgDWoSOCaid9PUSEF6781vsfiK8qUz62zRR448/XgK2NtCbpiUGizbep8Lrpt0Du19PpGGZvw3Q==
version "3.6.1"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.1.tgz#39d5e2e346258cc01eb7d44345b1c3c014ca3f05"
integrity sha512-186WjSik2iTGfDjfdCZAxv2ormxtKgemjC3SI6PL31qOA0j5LhTDVjHChccoc7brwLvpvLPiMyRlcO88C4l1QQ==
core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2"
@ -5370,9 +5370,9 @@ cyclist@^1.0.1:
integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=
cypress@^3.7.0:
version "3.8.0"
resolved "https://registry.yarnpkg.com/cypress/-/cypress-3.8.0.tgz#7d4cd08f81f9048ee36760cc9ee3b9014f9e84ab"
integrity sha512-gtEbqCgKETRc3pQFMsELRgIBNgiQg7vbOWTrCi7WE7bgOwNCaW9PEX8Jb3UN8z/maIp9WwzoFfeySfelYY7nRA==
version "3.8.1"
resolved "https://registry.yarnpkg.com/cypress/-/cypress-3.8.1.tgz#7821084e0ead81d35ffa29f2141c977dfdfc2343"
integrity sha512-eLk5OpL/ZMDfQx9t7ZaDUAGVcvSOPTi7CG1tiUnu9BGk7caBiDhuFi3Tz/D5vWqH/Dl6Uh4X+Au4W+zh0xzbXw==
dependencies:
"@cypress/listr-verbose-renderer" "0.4.1"
"@cypress/xvfb" "1.2.4"
@ -6079,9 +6079,9 @@ escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
escodegen@^1.11.0, escodegen@^1.9.1:
version "1.12.0"
resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.12.0.tgz#f763daf840af172bb3a2b6dd7219c0e17f7ff541"
integrity sha512-TuA+EhsanGcme5T3R0L80u4t8CpbXQjegRmf7+FPTJrtCTErXFeelblRgHQa1FofEzqYYJmJ/OqjTwREp9qgmg==
version "1.12.1"
resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.12.1.tgz#08770602a74ac34c7a90ca9229e7d51e379abc76"
integrity sha512-Q8t2YZ+0e0pc7NRVj3B4tSQ9rim1oi4Fh46k2xhJ2qOiEwhQfdjyEQddWdj7ZFaKmU+5104vn1qrcjEPWq+bgQ==
dependencies:
esprima "^3.1.3"
estraverse "^4.2.0"
@ -6091,9 +6091,9 @@ escodegen@^1.11.0, escodegen@^1.9.1:
source-map "~0.6.1"
eslint-config-prettier@^6.1.0:
version "6.7.0"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.7.0.tgz#9a876952e12df2b284adbd3440994bf1f39dfbb9"
integrity sha512-FamQVKM3jjUVwhG4hEMnbtsq7xOIDm+SY5iBPfR8gKsJoAB2IQnNF+bk1+8Fy44Nq7PPJaLvkRxILYdJWoguKQ==
version "6.9.0"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.9.0.tgz#430d24822e82f7deb1e22a435bfa3999fae4ad64"
integrity sha512-k4E14HBtcLv0uqThaI6I/n1LEqROp8XaPu6SO9Z32u5NlGRC07Enu1Bh2KEFw4FNHbekH8yzbIU9kUGxbiGmCA==
dependencies:
get-stdin "^6.0.0"
@ -8020,6 +8020,15 @@ internal-ip@^4.3.0:
default-gateway "^4.2.0"
ipaddr.js "^1.9.0"
internal-slot@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.2.tgz#9c2e9fb3cd8e5e4256c6f45fe310067fcfa378a3"
integrity sha512-2cQNfwhAfJIkU4KZPkDI+Gj5yNNnbqi40W9Gge6dfnk4TocEVm00B3bdiL+JINrbGJil2TeHvM4rETGzk/f/0g==
dependencies:
es-abstract "^1.17.0-next.1"
has "^1.0.3"
side-channel "^1.0.2"
interpret@^1.0.0, interpret@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296"
@ -10309,10 +10318,10 @@ node-notifier@^5.4.2:
shellwords "^0.1.1"
which "^1.3.0"
node-releases@^1.1.29, node-releases@^1.1.40, node-releases@^1.1.42:
version "1.1.43"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.43.tgz#2c6ca237f88ce11d49631f11190bb01f8d0549f2"
integrity sha512-Rmfnj52WNhvr83MvuAWHEqXVoZXCcDQssSOffU4n4XOL9sPrP61mSZ88g25NqmABDvH7PiAlFCzoSCSdzA293w==
node-releases@^1.1.29, node-releases@^1.1.40, node-releases@^1.1.44:
version "1.1.44"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.44.tgz#cd66438a6eb875e3eb012b6a12e48d9f4326ffd7"
integrity sha512-NwbdvJyR7nrcGrXvKAvzc5raj/NkoJudkarh2yIpJ4t0NH4aqjUDz/486P+ynIW5eokKOfzGNRdYoLfBlomruw==
dependencies:
semver "^6.3.0"
@ -10738,9 +10747,9 @@ p-limit@^1.1.0:
p-try "^1.0.0"
p-limit@^2.0.0, p-limit@^2.2.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.1.tgz#aa07a788cc3151c939b5131f63570f0dd2009537"
integrity sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==
version "2.2.2"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e"
integrity sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==
dependencies:
p-try "^2.0.0"
@ -11807,9 +11816,9 @@ postcss@7.0.21:
supports-color "^6.1.0"
postcss@^7, postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.23, postcss@^7.0.5, postcss@^7.0.6:
version "7.0.25"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.25.tgz#dd2a2a753d50b13bed7a2009b4a18ac14d9db21e"
integrity sha512-NXXVvWq9icrm/TgQC0O6YVFi4StfJz46M1iNd/h6B26Nvh/HKI+q4YZtFN/EjcInZliEscO/WL10BXnc1E5nwg==
version "7.0.26"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.26.tgz#5ed615cfcab35ba9bbb82414a4fa88ea10429587"
integrity sha512-IY4oRjpXWYshuTDFxMVkJDtWIk2LhsTlu8bZnbEJA4+bYT16Lvpo8Qv6EvDumhYRgzjZl489pmsY3qVgJQ08nA==
dependencies:
chalk "^2.4.2"
source-map "^0.6.1"
@ -11984,9 +11993,9 @@ pseudomap@^1.0.2:
integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
psl@^1.1.24, psl@^1.1.28:
version "1.6.0"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.6.0.tgz#60557582ee23b6c43719d9890fb4170ecd91e110"
integrity sha512-SYKKmVel98NCOYXpkwUqZqh0ahZeeKfmisiLIcEZdsb+WbLv02g/dI5BUmZnIyOe7RzZtLax81nnb2HbvC2tzA==
version "1.7.0"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.7.0.tgz#f1c4c47a8ef97167dea5d6bbf4816d736e884a3c"
integrity sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==
public-encrypt@^4.0.0:
version "4.0.3"
@ -12298,9 +12307,9 @@ react-docgen-typescript-loader@^3.6.0:
react-docgen-typescript "^1.15.0"
react-docgen-typescript@^1.15.0:
version "1.16.1"
resolved "https://registry.yarnpkg.com/react-docgen-typescript/-/react-docgen-typescript-1.16.1.tgz#3c4570601b2e765280adf570a0f344b35111604d"
integrity sha512-r2FyrOa9ynupwKzufHn8IqhWt8aYts3TZz/LWzJGFMyH8AaHQ/Z4LY+fbaI5Gs46DmeyNJvzg2dDELV2k/bL0A==
version "1.16.2"
resolved "https://registry.yarnpkg.com/react-docgen-typescript/-/react-docgen-typescript-1.16.2.tgz#d5f26ba6591ac4bc61628c514d492de461ae7c2c"
integrity sha512-nECrg2qih81AKp0smkxXebF72/2EjmEn7gXSlWLDHLbpGcbw2yIorol24fw1FWqvndIY82sfSd0x/SyfMKY1Jw==
react-docgen@^4.1.1:
version "4.1.1"
@ -12855,9 +12864,9 @@ redux-saga@^1.1.3:
"@redux-saga/core" "^1.1.3"
"redux@^3.6.0 || ^4.0.0", redux@^4.0.0, redux@^4.0.1, redux@^4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.4.tgz#4ee1aeb164b63d6a1bcc57ae4aa0b6e6fa7a3796"
integrity sha512-vKv4WdiJxOWKxK0yRoaK3Y4pxxB0ilzVx6dszU2W8wLxlb2yikRph4iV/ymtdJ6ZxpBLFbyrxklnT5yBbQSl3Q==
version "4.0.5"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f"
integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==
dependencies:
loose-envify "^1.4.0"
symbol-observable "^1.2.0"
@ -12918,7 +12927,7 @@ regex-parser@2.2.10:
resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.2.10.tgz#9e66a8f73d89a107616e63b39d4deddfee912b37"
integrity sha512-8t6074A68gHfU8Neftl0Le6KTDwfGAj7IyjPIMSfikI2wJUTHDMaIq42bUsfVnj8mhx0R+45rdUXHGpN164avA==
regexp.prototype.flags@^1.2.0:
regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75"
integrity sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==
@ -13319,9 +13328,9 @@ rxjs@^5.0.0-beta.11:
symbol-observable "1.0.1"
rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.5.3:
version "6.5.3"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.3.tgz#510e26317f4db91a7eb1de77d9dd9ba0a4899a3a"
integrity sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==
version "6.5.4"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.4.tgz#e0777fe0d184cec7872df147f303572d414e211c"
integrity sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==
dependencies:
tslib "^1.9.0"
@ -13654,6 +13663,14 @@ shellwords@^0.1.1:
resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==
side-channel@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.2.tgz#df5d1abadb4e4bf4af1cd8852bf132d2f7876947"
integrity sha512-7rL9YlPHg7Ancea1S96Pa8/QWb4BtXL/TZvS6B8XFetGBeuhAsfmUspK6DokBeZ64+Kj9TCNRD/30pVz1BvQNA==
dependencies:
es-abstract "^1.17.0-next.1"
object-inspect "^1.7.0"
signal-exit@^3.0.0, signal-exit@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
@ -13783,9 +13800,9 @@ source-list-map@^2.0.0:
integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
source-map-explorer@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/source-map-explorer/-/source-map-explorer-2.1.2.tgz#305d61efbb1d1ebbf24a9fffef718635054d509c"
integrity sha512-rgujHHHwf0/HCSTFbdTPJETGTgbGqVDD068Ob/wfV41u3AdU8iknSvGTDoU8vCIUeZuLnHX4JYsQ1RMd129XCQ==
version "2.2.1"
resolved "https://registry.yarnpkg.com/source-map-explorer/-/source-map-explorer-2.2.1.tgz#4bd236a1fe73e8b9db475f60197c48fc041928b8"
integrity sha512-vfjS5IRaENgXtTq2Bym0ctEorYGYNHLVCVZZeL1fvkD5iBoWKMlUhF/oFBTcTn9cxry1flplyvk0QGTROafB3Q==
dependencies:
btoa "^1.2.1"
chalk "^3.0.0"
@ -13797,14 +13814,14 @@ source-map-explorer@^2.1.1:
open "^7.0.0"
source-map "^0.7.3"
temp "^0.9.1"
yargs "^15.0.2"
yargs "^15.1.0"
source-map-resolve@^0.5.0, source-map-resolve@^0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259"
integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==
version "0.5.3"
resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a"
integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==
dependencies:
atob "^2.1.1"
atob "^2.1.2"
decode-uri-component "^0.2.0"
resolve-url "^0.2.1"
source-map-url "^0.4.0"
@ -14092,14 +14109,16 @@ string-width@^4.1.0, string-width@^4.2.0:
strip-ansi "^6.0.0"
"string.prototype.matchall@^4.0.0 || ^3.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.1.tgz#f10fdfa8d1fee12e149fddda14f211b6bb3527da"
integrity sha512-vIObm+BWuKKJovfh2/PPCAmePTDdubrgCkhQnbP0oAwD7Jj8IyIM57Hu7Ma7oDdg4oVdh5S1Rd8RviBIPZ8Pfg==
version "4.0.2"
resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.2.tgz#48bb510326fb9fdeb6a33ceaa81a6ea04ef7648e"
integrity sha512-N/jp6O5fMf9os0JU3E72Qhf590RSRZU/ungsL/qJUYVTNv7hTG0P/dbPjxINVN9jpscu3nzYwKESU3P3RY5tOg==
dependencies:
define-properties "^1.1.3"
es-abstract "^1.17.0-next.1"
es-abstract "^1.17.0"
has-symbols "^1.0.1"
regexp.prototype.flags "^1.2.0"
internal-slot "^1.0.2"
regexp.prototype.flags "^1.3.0"
side-channel "^1.0.2"
string.prototype.padend@^3.0.0:
version "3.1.0"
@ -14449,9 +14468,9 @@ terser-webpack-plugin@^1.2.4, terser-webpack-plugin@^1.4.1, terser-webpack-plugi
worker-farm "^1.7.0"
terser@^4.1.2, terser@^4.3.9:
version "4.4.3"
resolved "https://registry.yarnpkg.com/terser/-/terser-4.4.3.tgz#401abc52b88869cf904412503b1eb7da093ae2f0"
integrity sha512-0ikKraVtRDKGzHrzkCv5rUNDzqlhmhowOBqC0XqUHFpW+vJ45+20/IFBcebwKfiS2Z9fJin6Eo+F1zLZsxi8RA==
version "4.5.1"
resolved "https://registry.yarnpkg.com/terser/-/terser-4.5.1.tgz#63b52d6b6ce344aa6fedcd0ee06a695799eb50bd"
integrity sha512-lH9zLIbX8PRBEFCTvfHGCy0s9HEKnNso1Dx9swSopF3VUnFLB8DpQ61tHxoofovNC/sG0spajJM3EIIRSTByiQ==
dependencies:
commander "^2.20.0"
source-map "~0.6.1"
@ -14801,9 +14820,9 @@ uglify-js@3.4.x:
source-map "~0.6.1"
uglify-js@^3.1.4:
version "3.7.2"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.7.2.tgz#cb1a601e67536e9ed094a92dd1e333459643d3f9"
integrity sha512-uhRwZcANNWVLrxLfNFEdltoPNhECUR3lc+UdJoG9CBpMcSnKyWA94tc3eAujB1GcMY5Uwq8ZMp4qWpxWYDQmaA==
version "3.7.3"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.7.3.tgz#f918fce9182f466d5140f24bb0ff35c2d32dcc6a"
integrity sha512-7tINm46/3puUA4hCkKYo4Xdts+JDaVC9ZPRcG8Xw9R4nhO/gZgUM3TENq8IF4Vatk8qCig4MzP/c8G4u2BkVQg==
dependencies:
commander "~2.20.3"
source-map "~0.6.1"
@ -15352,9 +15371,9 @@ webpack@4.41.2:
webpack-sources "^1.4.1"
webpack@^4.33.0, webpack@^4.38.0:
version "4.41.4"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.41.4.tgz#4bec4125224bdf50efa8be6226c19047599cd034"
integrity sha512-Lc+2uB6NjpCWsHI3trkoISOI64h9QYIXenbEWj3bn3oyjfB1lEBXjWAfAyY2sM0rZn41oD5V91OLwKRwS6Wp8Q==
version "4.41.5"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.41.5.tgz#3210f1886bce5310e62bb97204d18c263341b77c"
integrity sha512-wp0Co4vpyumnp3KlkmpM5LWuzvZYayDwM2n17EHFr4qxBBbRokC7DJawPJC7TfSFZ9HZ6GsdH40EBj4UV0nmpw==
dependencies:
"@webassemblyjs/ast" "1.8.5"
"@webassemblyjs/helper-module-context" "1.8.5"
@ -15820,10 +15839,10 @@ yargs@^13.3.0:
y18n "^4.0.0"
yargs-parser "^13.1.1"
yargs@^15.0.2:
version "15.0.2"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.0.2.tgz#4248bf218ef050385c4f7e14ebdf425653d13bd3"
integrity sha512-GH/X/hYt+x5hOat4LMnCqMd8r5Cv78heOMIJn1hr7QPPBqfeC6p89Y78+WB9yGDvfpCvgasfmWLzNzEioOUD9Q==
yargs@^15.1.0:
version "15.1.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.1.0.tgz#e111381f5830e863a89550bd4b136bb6a5f37219"
integrity sha512-T39FNN1b6hCW4SOIk1XyTOWxtXdcen0t+XYrysQmChzSipvhBO8Bj0nK1ozAasdk24dNWuMZvr4k24nz+8HHLg==
dependencies:
cliui "^6.0.0"
decamelize "^1.2.0"