From fa8418adcd4237a1a2bcadbea18f10b7edd561c0 Mon Sep 17 00:00:00 2001 From: Faruk AYDIN Date: Sun, 26 Feb 2023 15:40:47 +0100 Subject: [PATCH 01/54] fix: Create seed user with admin role --- packages/backend/bin/database/utils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/backend/bin/database/utils.ts b/packages/backend/bin/database/utils.ts index 3d38d90d..50007e4d 100644 --- a/packages/backend/bin/database/utils.ts +++ b/packages/backend/bin/database/utils.ts @@ -12,6 +12,7 @@ export async function createUser( const userParams = { email, password, + role: 'admin', }; try { From 5ee7b85cc42106d5d5520a2c33f8ddd5bb2a0a78 Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Sun, 15 Jan 2023 16:10:53 +0100 Subject: [PATCH 02/54] feat(filter): add filter app --- .../src/apps/filter/actions/continue/index.ts | 79 + .../backend/src/apps/filter/actions/index.ts | 3 + .../src/apps/filter/assets/favicon.svg | 8 + packages/backend/src/apps/filter/index.d.ts | 0 packages/backend/src/apps/filter/index.ts | 14 + .../backend/src/errors/already-processed.ts | 3 + .../backend/src/helpers/compute-parameters.ts | 7 + .../backend/src/helpers/global-variable.ts | 6 +- packages/backend/src/services/action.ts | 22 +- packages/backend/src/services/flow.ts | 7 +- packages/backend/src/workers/action.ts | 6 +- packages/types/index.d.ts | 1 + packages/web/package.json | 8 +- .../ControlledAutocomplete/index.tsx | 2 +- .../FlowSubstep/FilterConditions/index.tsx | 213 + .../web/src/components/FlowSubstep/index.tsx | 32 +- .../web/src/components/InputCreator/index.tsx | 2 - .../web/src/components/PowerInput/index.tsx | 2 +- packages/web/src/locales/en.json | 2 + packages/web/src/styles/theme.ts | 8 - packages/web/yarn.lock | 10816 ---------------- yarn.lock | 322 +- 22 files changed, 570 insertions(+), 10993 deletions(-) create mode 100644 packages/backend/src/apps/filter/actions/continue/index.ts create mode 100644 packages/backend/src/apps/filter/actions/index.ts create mode 100644 packages/backend/src/apps/filter/assets/favicon.svg create mode 100644 packages/backend/src/apps/filter/index.d.ts create mode 100644 packages/backend/src/apps/filter/index.ts create mode 100644 packages/backend/src/errors/already-processed.ts create mode 100644 packages/web/src/components/FlowSubstep/FilterConditions/index.tsx delete mode 100644 packages/web/yarn.lock diff --git a/packages/backend/src/apps/filter/actions/continue/index.ts b/packages/backend/src/apps/filter/actions/continue/index.ts new file mode 100644 index 00000000..39f77afd --- /dev/null +++ b/packages/backend/src/apps/filter/actions/continue/index.ts @@ -0,0 +1,79 @@ +import defineAction from '../../../../helpers/define-action'; + +type TGroupItem = { + key: string; + operator: keyof TOperators; + value: string; + id: string; +} + +type TGroup = Record<'and', TGroupItem[]>; + +const isEqual = (a: string, b: string) => a === b; +const isNotEqual = (a: string, b: string) => !isEqual(a, b) +const isGreaterThan = (a: string, b: string) => Number(a) > Number(b); +const isLessThan = (a: string, b: string) => Number(a) < Number(b); +const isGreaterThanOrEqual = (a: string, b: string) => Number(a) >= Number(b); +const isLessThanOrEqual = (a: string, b: string) => Number(a) <= Number(b); +const contains = (a: string, b: string) => a.includes(b); +const doesNotContain = (a: string, b: string) => !contains(a, b); + +type TOperatorFunc = (a: string, b: string) => boolean; + +type TOperators = { + equal: TOperatorFunc; + not_equal: TOperatorFunc; + greater_than: TOperatorFunc; + less_than: TOperatorFunc; + greater_than_or_equal: TOperatorFunc; + less_than_or_equal: TOperatorFunc; + contains: TOperatorFunc; + not_contains: TOperatorFunc; +}; + +const operators: TOperators = { + 'equal': isEqual, + 'not_equal': isNotEqual, + 'greater_than': isGreaterThan, + 'less_than': isLessThan, + 'greater_than_or_equal': isGreaterThanOrEqual, + 'less_than_or_equal': isLessThanOrEqual, + 'contains': contains, + 'not_contains': doesNotContain, +}; + +const operate = (operation: keyof TOperators, a: string, b: string) => { + return operators[operation](a, b); +}; + +export default defineAction({ + name: 'Continue if conditions match', + key: 'continueIfMatches', + description: 'Let the execution continue if the conditions match', + arguments: [], + + async run($) { + const orGroups = $.step.parameters.or as TGroup[]; + + const matchingGroups = orGroups.reduce((groups, group) => { + const matchingConditions = group.and + .filter((condition) => operate(condition.operator, condition.key, condition.value)); + + if (matchingConditions.length) { + return groups.concat([{ and: matchingConditions }]); + } + + return groups; + }, []); + + if (matchingGroups.length === 0) { + $.execution.exit(); + } + + $.setActionItem({ + raw: { + or: matchingGroups, + } + }); + }, +}); diff --git a/packages/backend/src/apps/filter/actions/index.ts b/packages/backend/src/apps/filter/actions/index.ts new file mode 100644 index 00000000..0880c5cd --- /dev/null +++ b/packages/backend/src/apps/filter/actions/index.ts @@ -0,0 +1,3 @@ +import continueIfMatches from './continue'; + +export default [continueIfMatches]; diff --git a/packages/backend/src/apps/filter/assets/favicon.svg b/packages/backend/src/apps/filter/assets/favicon.svg new file mode 100644 index 00000000..77d3ebe2 --- /dev/null +++ b/packages/backend/src/apps/filter/assets/favicon.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/packages/backend/src/apps/filter/index.d.ts b/packages/backend/src/apps/filter/index.d.ts new file mode 100644 index 00000000..e69de29b diff --git a/packages/backend/src/apps/filter/index.ts b/packages/backend/src/apps/filter/index.ts new file mode 100644 index 00000000..9b4a81ce --- /dev/null +++ b/packages/backend/src/apps/filter/index.ts @@ -0,0 +1,14 @@ +import defineApp from '../../helpers/define-app'; +import actions from './actions'; + +export default defineApp({ + name: 'Filter', + key: 'filter', + iconUrl: '{BASE_URL}/apps/filter/assets/favicon.svg', + authDocUrl: 'https://automatisch.io/docs/apps/filter/connection', + supportsConnections: false, + baseUrl: '', + apiBaseUrl: '', + primaryColor: '001F52', + actions, +}); diff --git a/packages/backend/src/errors/already-processed.ts b/packages/backend/src/errors/already-processed.ts new file mode 100644 index 00000000..2f3ef6a8 --- /dev/null +++ b/packages/backend/src/errors/already-processed.ts @@ -0,0 +1,3 @@ +import BaseError from './base'; + +export default class AlreadyProcessedError extends BaseError { } diff --git a/packages/backend/src/helpers/compute-parameters.ts b/packages/backend/src/helpers/compute-parameters.ts index 4e845db8..dbc0762d 100644 --- a/packages/backend/src/helpers/compute-parameters.ts +++ b/packages/backend/src/helpers/compute-parameters.ts @@ -38,6 +38,13 @@ export default function computeParameters( }; } + if (Array.isArray(value)) { + return { + ...result, + [key]: value.map(item => computeParameters(item, executionSteps)), + }; + } + return { ...result, [key]: value, diff --git a/packages/backend/src/helpers/global-variable.ts b/packages/backend/src/helpers/global-variable.ts index ed4dd968..1cbbcde6 100644 --- a/packages/backend/src/helpers/global-variable.ts +++ b/packages/backend/src/helpers/global-variable.ts @@ -13,6 +13,7 @@ import { IRequest, } from '@automatisch/types'; import EarlyExitError from '../errors/early-exit'; +import AlreadyProcessedError from '../errors/already-processed'; type GlobalVariableOptions = { connection?: Connection; @@ -77,6 +78,9 @@ const globalVariable = async ( execution: { id: execution?.id, testRun, + exit: () => { + throw new EarlyExitError(); + } }, lastExecutionStep: (await step?.getLastExecutionStep())?.toJSON(), triggerOutput: { @@ -93,7 +97,7 @@ const globalVariable = async ( !$.execution.testRun ) { // early exit as we do not want to process duplicate items in actual executions - throw new EarlyExitError(); + throw new AlreadyProcessedError(); } $.triggerOutput.data.push(triggerItem); diff --git a/packages/backend/src/services/action.ts b/packages/backend/src/services/action.ts index 39fd3987..f1d13373 100644 --- a/packages/backend/src/services/action.ts +++ b/packages/backend/src/services/action.ts @@ -5,6 +5,8 @@ import ExecutionStep from '../models/execution-step'; import computeParameters from '../helpers/compute-parameters'; import globalVariable from '../helpers/global-variable'; import HttpError from '../errors/http'; +import EarlyExitError from '../errors/early-exit'; +import AlreadyProcessedError from '../errors/already-processed'; type ProcessActionOptions = { flowId: string; @@ -44,13 +46,19 @@ export const processAction = async (options: ProcessActionOptions) => { try { await actionCommand.run($); } catch (error) { - if (error instanceof HttpError) { - $.actionOutput.error = error.details; - } else { - try { - $.actionOutput.error = JSON.parse(error.message); - } catch { - $.actionOutput.error = { error: error.message }; + const shouldEarlyExit = error instanceof EarlyExitError; + const shouldNotProcess = error instanceof AlreadyProcessedError; + const shouldNotConsiderAsError = shouldEarlyExit || shouldNotProcess; + + if (!shouldNotConsiderAsError) { + if (error instanceof HttpError) { + $.actionOutput.error = error.details; + } else { + try { + $.actionOutput.error = JSON.parse(error.message); + } catch { + $.actionOutput.error = { error: error.message }; + } } } } diff --git a/packages/backend/src/services/flow.ts b/packages/backend/src/services/flow.ts index 862066f7..9ff6d6fc 100644 --- a/packages/backend/src/services/flow.ts +++ b/packages/backend/src/services/flow.ts @@ -1,6 +1,7 @@ import Flow from '../models/flow'; import globalVariable from '../helpers/global-variable'; import EarlyExitError from '../errors/early-exit'; +import AlreadyProcessedError from '../errors/already-processed'; import HttpError from '../errors/http'; type ProcessFlowOptions = { @@ -29,7 +30,11 @@ export const processFlow = async (options: ProcessFlowOptions) => { await triggerCommand.run($); } } catch (error) { - if (error instanceof EarlyExitError === false) { + const shouldEarlyExit = error instanceof EarlyExitError; + const shouldNotProcess = error instanceof AlreadyProcessedError; + const shouldNotConsiderAsError = shouldEarlyExit || shouldNotProcess; + + if (!shouldNotConsiderAsError) { if (error instanceof HttpError) { $.triggerOutput.error = error.details; } else { diff --git a/packages/backend/src/workers/action.ts b/packages/backend/src/workers/action.ts index 40c2e6c8..22d1169e 100644 --- a/packages/backend/src/workers/action.ts +++ b/packages/backend/src/workers/action.ts @@ -21,7 +21,7 @@ const DEFAULT_DELAY_DURATION = 0; export const worker = new Worker( 'action', async (job) => { - const { stepId, flowId, executionId, computedParameters } = await processAction( + const { stepId, flowId, executionId, computedParameters, executionStep } = await processAction( job.data as JobData ); @@ -48,6 +48,10 @@ export const worker = new Worker( jobOptions.delay = delayAsMilliseconds(step.key, computedParameters); } + if (step.appKey === 'filter' && !executionStep.dataOut) { + return; + } + await actionQueue.add(jobName, jobPayload, jobOptions); }, { connection: redisConfig } diff --git a/packages/types/index.d.ts b/packages/types/index.d.ts index 7f56d0e7..e559ca78 100644 --- a/packages/types/index.d.ts +++ b/packages/types/index.d.ts @@ -296,6 +296,7 @@ export type IGlobalVariable = { execution?: { id: string; testRun: boolean; + exit: () => void; }; lastExecutionStep?: IExecutionStep; webhookUrl?: string; diff --git a/packages/web/package.json b/packages/web/package.json index ed9fed5e..8d4fa5de 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -9,9 +9,9 @@ "@emotion/react": "^11.4.1", "@emotion/styled": "^11.3.0", "@hookform/resolvers": "^2.8.8", - "@mui/icons-material": "^5.0.1", - "@mui/lab": "^5.0.0-alpha.60", - "@mui/material": "^5.0.2", + "@mui/icons-material": "^5.11.9", + "@mui/lab": "^5.0.0-alpha.120", + "@mui/material": "^5.11.10", "@testing-library/jest-dom": "^5.11.4", "@testing-library/react": "^11.1.0", "@testing-library/user-event": "^12.1.10", @@ -21,6 +21,7 @@ "@types/node": "^12.0.0", "@types/react": "^17.0.0", "@types/react-dom": "^17.0.0", + "@types/uuid": "^9.0.0", "clipboard-copy": "^4.0.1", "compare-versions": "^4.1.3", "graphql": "^15.6.0", @@ -38,6 +39,7 @@ "slate-history": "^0.66.0", "slate-react": "^0.72.9", "typescript": "^4.6.3", + "uuid": "^9.0.0", "web-vitals": "^1.0.1", "yup": "^0.32.11" }, diff --git a/packages/web/src/components/ControlledAutocomplete/index.tsx b/packages/web/src/components/ControlledAutocomplete/index.tsx index c209afa7..d0a3b997 100644 --- a/packages/web/src/components/ControlledAutocomplete/index.tsx +++ b/packages/web/src/components/ControlledAutocomplete/index.tsx @@ -69,7 +69,7 @@ function ControlledAutocomplete( }, fieldState, }) => ( -
+
{/* encapsulated with an element such as div to vertical spacing delegated from parent */} ; + +const createGroupItem = (): TGroupItem => ({ + key: '', + operator: operators[0].value, + value: '', + id: uuidv4(), +}); + +const createGroup = (): TGroup => ({ + and: [createGroupItem()] +}); + +const operators = [ + { + label: 'Equal', + value: 'equal', + }, + { + label: 'Not Equal', + value: 'not_equal', + }, + { + label: 'Greater Than', + value: 'greater_than', + }, + { + label: 'Less Than', + value: 'less_than', + }, + { + label: 'Greater Than Or Equal', + value: 'greater_than_or_equal', + }, + { + label: 'Less Than Or Equal', + value: 'less_than_or_equal', + }, + { + label: 'Contains', + value: 'contains', + }, + { + label: 'Not Contains', + value: 'not_contains', + }, +]; + +const createStringArgument = (argumentOptions: Omit): IField => { + return { + ...argumentOptions, + type: 'string', + required: true, + variables: true, + }; +}; + +const createDropdownArgument = (argumentOptions: Omit): IField => { + return { + ...argumentOptions, + required: true, + type: 'dropdown', + }; +}; + +type FilterConditionsProps = { + stepId: string; +}; + +function FilterConditions(props: FilterConditionsProps): React.ReactElement { + const { + stepId + } = props; + const formatMessage = useFormatMessage(); + const { control, setValue, getValues } = useFormContext(); + const groups = useWatch({ control, name: 'parameters.or' }); + const editorContext = React.useContext(EditorContext); + + React.useEffect(function addInitialGroupWhenEmpty() { + const groups = getValues('parameters.or'); + + if (!groups) { + setValue('parameters.or', [createGroup()]); + } + }, []); + + const appendGroup = React.useCallback(() => { + const values = getValues('parameters.or'); + + setValue('parameters.or', values.concat(createGroup())) + }, []); + + const appendGroupItem = React.useCallback((index) => { + const group = getValues(`parameters.or.${index}.and`); + setValue(`parameters.or.${index}.and`, group.concat(createGroupItem())); + }, []); + + const removeGroupItem = React.useCallback((groupIndex, groupItemIndex) => { + const group: TGroupItem[] = getValues(`parameters.or.${groupIndex}.and`); + + if (group.length === 1) { + const groups: TGroup[] = getValues('parameters.or'); + + setValue('parameters.or', groups.filter((group, index) => index !== groupIndex)); + } else { + setValue(`parameters.or.${groupIndex}.and`, group.filter((groupItem, index) => index !== groupItemIndex)); + } + }, []); + + return ( + + + {groups?.map((group: TGroup, groupIndex: number) => ( + <> + {groupIndex !== 0 && } + + + {groupIndex === 0 && formatMessage('filterConditions.onlyContinueIf')} + {groupIndex !== 0 && formatMessage('filterConditions.orContinueIf')} + + + {group?.and?.map((groupItem: TGroupItem, groupItemIndex: number) => ( + + + + + + + + + + + + + + + + removeGroupItem(groupIndex, groupItemIndex)} + sx={{ width: 61, height: 61 }} + > + + + + ))} + + + appendGroupItem(groupIndex)} + > + And + + + {(groups.length - 1) === groupIndex && + Or + } + + + ))} + + + ); +} + +export default FilterConditions; diff --git a/packages/web/src/components/FlowSubstep/index.tsx b/packages/web/src/components/FlowSubstep/index.tsx index cd9c0c28..00fccdeb 100644 --- a/packages/web/src/components/FlowSubstep/index.tsx +++ b/packages/web/src/components/FlowSubstep/index.tsx @@ -4,11 +4,12 @@ import Collapse from '@mui/material/Collapse'; import ListItem from '@mui/material/ListItem'; import Button from '@mui/material/Button'; import Stack from '@mui/material/Stack'; +import type { IField, IStep, ISubstep } from '@automatisch/types'; import { EditorContext } from 'contexts/Editor'; import FlowSubstepTitle from 'components/FlowSubstepTitle'; import InputCreator from 'components/InputCreator'; -import type { IField, IStep, ISubstep } from '@automatisch/types'; +import FilterConditions from './FilterConditions'; type FlowSubstepProps = { substep: ISubstep; @@ -84,20 +85,25 @@ function FlowSubstep(props: FlowSubstepProps): React.ReactElement { pb: 3, flexDirection: 'column', alignItems: 'flex-start', + position: 'relative' }} > - - {args?.map((argument) => ( - - ))} - + {!!args?.length && ( + + {args.map((argument) => ( + + ))} + + )} + + {step.appKey === 'filter' && } + + + + ); +} diff --git a/packages/web/src/graphql/mutations/delete-user.ee.ts b/packages/web/src/graphql/mutations/delete-user.ee.ts new file mode 100644 index 00000000..ed187077 --- /dev/null +++ b/packages/web/src/graphql/mutations/delete-user.ee.ts @@ -0,0 +1,7 @@ +import { gql } from '@apollo/client'; + +export const DELETE_USER = gql` + mutation DeleteUser($input: DeleteUserInput) { + deleteUser(input: $input) + } +`; diff --git a/packages/web/src/locales/en.json b/packages/web/src/locales/en.json index e900e543..0130b34f 100644 --- a/packages/web/src/locales/en.json +++ b/packages/web/src/locales/en.json @@ -89,6 +89,17 @@ "profileSettings.updatedEmail": "Your email has been updated.", "profileSettings.updatedPassword": "Your password has been updated.", "profileSettings.updatePassword": "Update password", + "profileSettings.deleteMyAccount": "Delete my account", + "profileSettings.deleteAccount": "Delete account", + "profileSettings.deleteAccountSubtitle": "This will permanently delete...", + "profileSettings.deleteAccountResult1": "Your account", + "profileSettings.deleteAccountResult2": "All your flows", + "profileSettings.deleteAccountResult3": "All your connections", + "profileSettings.deleteAccountResult4": "All execution history", + "deleteAccountDialog.title": "Delete account?", + "deleteAccountDialog.description": "This will permanently delete your account and all the associated data with it.", + "deleteAccountDialog.cancel": "Cancel?", + "deleteAccountDialog.confirm": "Yes, delete it", "notifications.title": "Notifications", "notification.releasedAt": "Released {relativeDate}", "webhookUrlInfo.title": "Your webhook URL", diff --git a/packages/web/src/pages/ProfileSettings/index.tsx b/packages/web/src/pages/ProfileSettings/index.tsx index 0c9580c4..9d1aa9a0 100644 --- a/packages/web/src/pages/ProfileSettings/index.tsx +++ b/packages/web/src/pages/ProfileSettings/index.tsx @@ -1,5 +1,8 @@ import * as React from 'react'; import { useMutation } from '@apollo/client'; +import Alert from '@mui/material/Alert'; +import AlertTitle from '@mui/material/AlertTitle'; +import Typography from '@mui/material/Typography'; import { styled } from '@mui/material/styles'; import Grid from '@mui/material/Grid'; import Button from '@mui/material/Button'; @@ -11,6 +14,7 @@ import PageTitle from 'components/PageTitle'; import Container from 'components/Container'; import Form from 'components/Form'; import TextField from 'components/TextField'; +import DeleteAccountDialog from 'components/DeleteAccountDialog/index.ee'; import { UPDATE_USER } from 'graphql/mutations/update-user'; import useFormatMessage from 'hooks/useFormatMessage'; import useCurrentUser from 'hooks/useCurrentUser'; @@ -38,6 +42,7 @@ const StyledForm = styled(Form)` `; function ProfileSettings() { + const [showDeleteAccountConfirmation, setShowDeleteAccountConfirmation] = React.useState(false); const [passwordDefaultValues, setPasswordDefaultValues] = React.useState({}); const { enqueueSnackbar } = useSnackbar(); const currentUser = useCurrentUser(); @@ -179,6 +184,40 @@ function ProfileSettings() { )} /> + + + + {formatMessage('profileSettings.deleteMyAccount')} + + + {formatMessage('profileSettings.deleteAccountSubtitle')} + + +
    +
  1. {formatMessage('profileSettings.deleteAccountResult1')}
  2. +
  3. {formatMessage('profileSettings.deleteAccountResult2')}
  4. +
  5. {formatMessage('profileSettings.deleteAccountResult3')}
  6. +
  7. {formatMessage('profileSettings.deleteAccountResult4')}
  8. +
+ + + + {showDeleteAccountConfirmation && ( + setShowDeleteAccountConfirmation(false)} + /> + )} +
+
); From 387a849269cb74cd878f0301b89dcfe9e46b5593 Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Thu, 2 Mar 2023 23:46:20 +0000 Subject: [PATCH 17/54] fix: delete current user in deleteUser mutation --- .../src/graphql/mutations/delete-user.ee.ts | 17 ++++------------- packages/backend/src/graphql/schema.graphql | 6 +----- .../components/DeleteAccountDialog/index.ee.tsx | 8 +------- .../web/src/graphql/mutations/delete-user.ee.ts | 4 ++-- 4 files changed, 8 insertions(+), 27 deletions(-) diff --git a/packages/backend/src/graphql/mutations/delete-user.ee.ts b/packages/backend/src/graphql/mutations/delete-user.ee.ts index b1f83097..3e894030 100644 --- a/packages/backend/src/graphql/mutations/delete-user.ee.ts +++ b/packages/backend/src/graphql/mutations/delete-user.ee.ts @@ -1,20 +1,11 @@ -import User from '../../models/user'; +import Context from '../../types/express/context'; import deleteUserQueue from '../../queues/delete-user.ee'; import { Duration } from 'luxon'; -type Params = { - input: { - id: string; - }; -}; +const deleteUser = async (_parent: unknown, params: never, context: Context) => { + const id = context.currentUser.id; -const deleteUser = async (_parent: unknown, params: Params) => { - const { id } = params.input; - await User - .query() - .findById(id) - .delete() - .throwIfNotFound(); + await context.currentUser.$query().delete(); const jobName = `Delete user - ${id}`; const jobPayload = { id }; diff --git a/packages/backend/src/graphql/schema.graphql b/packages/backend/src/graphql/schema.graphql index d40af41f..7e421f6e 100644 --- a/packages/backend/src/graphql/schema.graphql +++ b/packages/backend/src/graphql/schema.graphql @@ -54,7 +54,7 @@ type Mutation { updateStep(input: UpdateStepInput): Step deleteStep(input: DeleteStepInput): Step createUser(input: CreateUserInput): User - deleteUser(input: DeleteUserInput): Boolean + deleteUser: Boolean updateUser(input: UpdateUserInput): User forgotPassword(input: ForgotPasswordInput): Boolean resetPassword(input: ResetPasswordInput): Boolean @@ -340,10 +340,6 @@ input CreateUserInput { password: String! } -input DeleteUserInput { - id: String -} - input UpdateUserInput { email: String password: String diff --git a/packages/web/src/components/DeleteAccountDialog/index.ee.tsx b/packages/web/src/components/DeleteAccountDialog/index.ee.tsx index 75cd5577..59b9f308 100644 --- a/packages/web/src/components/DeleteAccountDialog/index.ee.tsx +++ b/packages/web/src/components/DeleteAccountDialog/index.ee.tsx @@ -27,13 +27,7 @@ export default function DeleteAccountDialog(props: TDeleteAccountDialogProps) { const navigate = useNavigate(); const handleConfirm = React.useCallback(async () => { - await deleteUser({ - variables: { - input: { - id: currentUser.id, - } - } - }); + await deleteUser(); authentication.updateToken(''); await apolloClient.clearStore(); diff --git a/packages/web/src/graphql/mutations/delete-user.ee.ts b/packages/web/src/graphql/mutations/delete-user.ee.ts index ed187077..462174eb 100644 --- a/packages/web/src/graphql/mutations/delete-user.ee.ts +++ b/packages/web/src/graphql/mutations/delete-user.ee.ts @@ -1,7 +1,7 @@ import { gql } from '@apollo/client'; export const DELETE_USER = gql` - mutation DeleteUser($input: DeleteUserInput) { - deleteUser(input: $input) + mutation DeleteUser { + deleteUser } `; From c1150d50b1c44936437a1ebb20feff8de01bf2d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C4=B1dvan=20Akca?= Date: Fri, 3 Mar 2023 11:34:53 +0300 Subject: [PATCH 18/54] feat: create signup form --- .../src/components/SignUpForm/index.ee.tsx | 126 ++++++++++++------ 1 file changed, 82 insertions(+), 44 deletions(-) diff --git a/packages/web/src/components/SignUpForm/index.ee.tsx b/packages/web/src/components/SignUpForm/index.ee.tsx index d3ecfd6d..1f95ee08 100644 --- a/packages/web/src/components/SignUpForm/index.ee.tsx +++ b/packages/web/src/components/SignUpForm/index.ee.tsx @@ -4,55 +4,31 @@ import { useMutation } from '@apollo/client'; import Paper from '@mui/material/Paper'; import Typography from '@mui/material/Typography'; import LoadingButton from '@mui/lab/LoadingButton'; +import * as yup from 'yup'; import useAuthentication from 'hooks/useAuthentication'; import * as URLS from 'config/urls'; import { LOGIN } from 'graphql/mutations/login'; import Form from 'components/Form'; import TextField from 'components/TextField'; +import { yupResolver } from '@hookform/resolvers/yup'; -function renderFields(props: { loading: boolean }) { - const { loading = false } = props; +const validationSchema = yup.object().shape({ + fullName: yup.string().required(), + email: yup.string().email().required(), + password: yup.string().required(), + confirmPassword: yup + .string() + .required() + .oneOf([yup.ref('password')], 'Passwords must match'), +}); - return () => { - return ( - <> - - - - - - Login - - - ); - }; -} +const initialValue = { + fullName: '', + email: '', + password: '', + confirmPassword: '', +}; function SignUpForm() { const navigate = useNavigate(); @@ -77,7 +53,7 @@ function SignUpForm() { authentication.updateToken(token); }; - const render = React.useMemo(() => renderFields({ loading }), [loading]); + //const render = React.useMemo(() => renderFields({ loading }), [loading]); return ( @@ -92,10 +68,72 @@ function SignUpForm() { }} gutterBottom > - Login + Sign Up - + ( + <> + + + + + + + + + + Sign Up + + + )} + /> ); } From 18089a80762390f9c68b8cac05220318e6b65e44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C4=B1dvan=20Akca?= Date: Fri, 3 Mar 2023 14:04:23 +0300 Subject: [PATCH 19/54] feat: add signup logic --- packages/backend/src/graphql/mutations/create-user.ee.ts | 4 +++- packages/backend/src/graphql/schema.graphql | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/graphql/mutations/create-user.ee.ts b/packages/backend/src/graphql/mutations/create-user.ee.ts index 804daf00..4a92faa6 100644 --- a/packages/backend/src/graphql/mutations/create-user.ee.ts +++ b/packages/backend/src/graphql/mutations/create-user.ee.ts @@ -4,11 +4,12 @@ type Params = { input: { email: string; password: string; + fullName: string; }; }; const createUser = async (_parent: unknown, params: Params) => { - const { email, password } = params.input; + const { email, password, fullName } = params.input; const existingUser = await User.query().findOne({ email }); @@ -19,6 +20,7 @@ const createUser = async (_parent: unknown, params: Params) => { const user = await User.query().insert({ email, password, + fullName, role: 'user', }); diff --git a/packages/backend/src/graphql/schema.graphql b/packages/backend/src/graphql/schema.graphql index 1e09a90e..76bf8310 100644 --- a/packages/backend/src/graphql/schema.graphql +++ b/packages/backend/src/graphql/schema.graphql @@ -335,6 +335,7 @@ input DeleteStepInput { } input CreateUserInput { + fullName: String! email: String! password: String! } @@ -428,6 +429,7 @@ input StepInput { type User { id: String fullName: String + password: String email: String role: String createdAt: String From d061eb7b588687aa54ee11192d6a094cc7c8f9a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C4=B1dvan=20Akca?= Date: Fri, 3 Mar 2023 14:10:54 +0300 Subject: [PATCH 20/54] feat: update migration logic and remove consolelog --- packages/web/src/components/LoginForm/index.tsx | 5 ++--- .../web/src/components/SignUpForm/index.ee.tsx | 15 +++++++++++---- .../web/src/graphql/mutations/create-user.ee.ts | 10 ++++++++++ 3 files changed, 23 insertions(+), 7 deletions(-) create mode 100644 packages/web/src/graphql/mutations/create-user.ee.ts diff --git a/packages/web/src/components/LoginForm/index.tsx b/packages/web/src/components/LoginForm/index.tsx index 64759980..b2354c31 100644 --- a/packages/web/src/components/LoginForm/index.tsx +++ b/packages/web/src/components/LoginForm/index.tsx @@ -51,9 +51,8 @@ function renderFields(props: { loading: boolean }) { Login - - - Don't have an Automatisch account yet? + + Don't have an Automatisch account yet? Sign Up diff --git a/packages/web/src/components/SignUpForm/index.ee.tsx b/packages/web/src/components/SignUpForm/index.ee.tsx index 1f95ee08..b2a85370 100644 --- a/packages/web/src/components/SignUpForm/index.ee.tsx +++ b/packages/web/src/components/SignUpForm/index.ee.tsx @@ -8,10 +8,11 @@ import * as yup from 'yup'; import useAuthentication from 'hooks/useAuthentication'; import * as URLS from 'config/urls'; -import { LOGIN } from 'graphql/mutations/login'; +import { CREATE_USER } from 'graphql/mutations/create-user.ee'; import Form from 'components/Form'; import TextField from 'components/TextField'; import { yupResolver } from '@hookform/resolvers/yup'; +import { LOGIN } from 'graphql/mutations/login'; const validationSchema = yup.object().shape({ fullName: yup.string().required(), @@ -33,6 +34,7 @@ const initialValue = { function SignUpForm() { const navigate = useNavigate(); const authentication = useAuthentication(); + const [createUser] = useMutation(CREATE_USER); const [login, { loading }] = useMutation(LOGIN); React.useEffect(() => { @@ -42,9 +44,16 @@ function SignUpForm() { }, [authentication.isAuthenticated]); const handleSubmit = async (values: any) => { + const { fullName, email, password } = values; + await createUser({ + variables: { + input: { fullName, email, password }, + }, + }); + const { data } = await login({ variables: { - input: values, + input: { email, password }, }, }); @@ -53,8 +62,6 @@ function SignUpForm() { authentication.updateToken(token); }; - //const render = React.useMemo(() => renderFields({ loading }), [loading]); - return ( Date: Fri, 3 Mar 2023 12:15:58 +0100 Subject: [PATCH 21/54] feat: Introduce automatisch cloud env variable --- packages/backend/src/config/app.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/backend/src/config/app.ts b/packages/backend/src/config/app.ts index 9571af84..d705d8d7 100644 --- a/packages/backend/src/config/app.ts +++ b/packages/backend/src/config/app.ts @@ -38,6 +38,7 @@ type AppConfig = { smtpUser: string; smtpPassword: string; fromEmail: string; + isCloud: boolean; licenseKey: string; }; @@ -104,6 +105,7 @@ const appConfig: AppConfig = { smtpUser: process.env.SMTP_USER, smtpPassword: process.env.SMTP_PASSWORD, fromEmail: process.env.FROM_EMAIL, + isCloud: process.env.AUTOMATISCH_CLOUD === 'true', licenseKey: process.env.LICENSE_KEY, }; From 394e747a887d836df2f6aa4f817fc2a844dd4ff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C4=B1dvan=20Akca?= Date: Fri, 3 Mar 2023 18:40:55 +0300 Subject: [PATCH 22/54] feat: remove helperText if there is no error --- .../src/components/SignUpForm/index.ee.tsx | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/web/src/components/SignUpForm/index.ee.tsx b/packages/web/src/components/SignUpForm/index.ee.tsx index b2a85370..e3ed3726 100644 --- a/packages/web/src/components/SignUpForm/index.ee.tsx +++ b/packages/web/src/components/SignUpForm/index.ee.tsx @@ -15,8 +15,8 @@ import { yupResolver } from '@hookform/resolvers/yup'; import { LOGIN } from 'graphql/mutations/login'; const validationSchema = yup.object().shape({ - fullName: yup.string().required(), - email: yup.string().email().required(), + fullName: yup.string().trim().required(), + email: yup.string().trim().email().required(), password: yup.string().required(), confirmPassword: yup .string() @@ -24,7 +24,7 @@ const validationSchema = yup.object().shape({ .oneOf([yup.ref('password')], 'Passwords must match'), }); -const initialValue = { +const initialValues = { fullName: '', email: '', password: '', @@ -79,7 +79,7 @@ function SignUpForm() { Date: Fri, 3 Mar 2023 18:43:18 +0300 Subject: [PATCH 23/54] feat: remove helperText from profile setting page if there is no error --- packages/web/src/pages/ProfileSettings/index.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/web/src/pages/ProfileSettings/index.tsx b/packages/web/src/pages/ProfileSettings/index.tsx index 0c9580c4..af3de5b4 100644 --- a/packages/web/src/pages/ProfileSettings/index.tsx +++ b/packages/web/src/pages/ProfileSettings/index.tsx @@ -153,7 +153,9 @@ function ProfileSettings() { margin="normal" type="password" error={touchedFields.password && !!errors?.password} - helperText={errors?.password?.message || ' '} + helperText={ + (touchedFields.password && errors?.password?.message) || '' + } />