From d9af00d3833302072fbf592f0a2bb4d2b07af453 Mon Sep 17 00:00:00 2001 From: Yoan VALLET <ext.sopra.yvallet@grandlyon.com> Date: Tue, 2 Jun 2020 18:48:21 +0200 Subject: [PATCH] feat: rework of konnectors --- .../Konnector/KonnectorForm.tsx | 120 ++---- .../Konnector/KonnectorLoading.tsx | 56 ++- .../Konnector/KonnectorLoginForm.tsx | 134 +++++++ .../Konnector/KonnectorNotFound.tsx | 1 + .../Konnector/KonnectorOAuthForm.tsx | 20 +- .../Konnector/KonnectorResult.tsx | 110 +++++- .../KonnectorViewer/KonnectorViewer.tsx | 23 +- .../KonnectorViewerCard copy.tsx | 342 ------------------ .../KonnectorViewer/KonnectorViewerCard.tsx | 243 ++----------- src/services/accountService.ts | 4 +- src/services/connectionService.ts | 14 +- src/services/konnectorStatusService.ts | 7 +- src/services/triggersService.ts | 21 ++ 13 files changed, 401 insertions(+), 694 deletions(-) create mode 100644 src/components/ContentComponents/Konnector/KonnectorLoginForm.tsx delete mode 100644 src/components/ContentComponents/KonnectorViewer/KonnectorViewerCard copy.tsx diff --git a/src/components/ContentComponents/Konnector/KonnectorForm.tsx b/src/components/ContentComponents/Konnector/KonnectorForm.tsx index 9a36bc472..c85fece9a 100644 --- a/src/components/ContentComponents/Konnector/KonnectorForm.tsx +++ b/src/components/ContentComponents/Konnector/KonnectorForm.tsx @@ -1,106 +1,54 @@ -import React from 'react' +import React, { useState } from 'react' import { translate } from 'cozy-ui/react/I18n' import IFluidConfig from 'services/IFluidConfig' +import { Konnector, Trigger } from 'doctypes' -import StyledIconButton from 'components/CommonKit/IconButton/StyledIconButton' -import StyledButton from 'components/CommonKit/Button/StyledButton' -import TrailingIcon from 'assets/icons/ico/trailing-icon.svg' +import KonnectorLoginForm from 'components/ContentComponents/Konnector/KonnectorLoginForm' +import KonnectorOAuthForm from 'components/ContentComponents/Konnector/KonnectorOAuthForm' +import KonnectorLoading from 'components/ContentComponents/Konnector/KonnectorLoading' interface KonnectorFormProps { fluidConfig: IFluidConfig - login: string - setLogin: Function - password: string - setPassword: Function - loading: boolean - error: string - handleSubmit: Function - t: Function + konnector: Konnector + handleConnexion: Function } const KonnectorForm: React.FC<KonnectorFormProps> = ({ fluidConfig, - login, - setLogin, - password, - setPassword, - loading, - error, - handleSubmit, - t, + konnector, + handleConnexion, }: KonnectorFormProps) => { - const konnectorName: string = fluidConfig.konnectorConfig.name - const konnectorType: string = fluidConfig.konnectorConfig.type - const siteLink: string = fluidConfig.siteLink + const oAuth: boolean = fluidConfig.konnectorConfig.oauth + const [account, setAccount] = useState<Account | null>(null) + const [trigger, setTrigger] = useState<Trigger | null>(null) - function revealPassword(idInput: string) { - const input = document.getElementById(idInput) - if (input) { - if (input.getAttribute('type') === 'password') { - input.setAttribute('type', 'text') - } else { - input.setAttribute('type', 'password') - } - } + const handleForm = (_account: Account, _trigger: Trigger) => { + setAccount(_account) + setTrigger(_trigger) + } + + const handleKonnectorLoading = () => { + handleConnexion(account) } return ( - <form - className="form" - onSubmit={(e: React.FormEvent<HTMLFormElement>) => handleSubmit(e)} - > - {t('KONNECTORCONFIG.LABEL_FILLIN')} {konnectorName} - <div className="form-group"> - <div className="form-message">{error}</div> - <input - id={'idFieldLogin' + konnectorType} - type="text" - className="form-control form-input" - aria-describedby="emailHelp" - placeholder="Adresse e-mail" - name="login" - onChange={(e: React.ChangeEvent<HTMLInputElement>) => - setLogin(e.target.value) - } - value={login} - ></input> - </div> - <div className="form-group"> - <div className="form-message">{error}</div> - <input - id={'idFieldPassword' + konnectorType} - type="password" - className="form-control form-input" - aria-describedby="PasswordHelp" - placeholder="Mot de passe" - name="password" - onChange={(e: React.ChangeEvent<HTMLInputElement>) => - setPassword(e.target.value) - } - value={password} + <> + {account && trigger ? ( + <KonnectorLoading + trigger={trigger} + handleKonnectorLoading={handleKonnectorLoading} + /> + ) : !oAuth ? ( + <KonnectorLoginForm fluidConfig={fluidConfig} onSuccess={handleForm} /> + ) : ( + <KonnectorOAuthForm + konnector={konnector} + siteLink={fluidConfig.siteLink} + onSuccess={handleForm} /> - <span> - <StyledIconButton - icon={TrailingIcon} - className="form-trailing-icon" - size={22} - onClick={() => revealPassword('idFieldPassword' + konnectorType)} - /> - </span> - </div> - <StyledButton type="submit" color="primary" disabled={loading}> - {t('KONNECTORCONFIG.BTN_CONNECTION')} - </StyledButton> - <StyledButton - type="button" - color="secondary" - disabled={loading} - onClick={() => window.open(siteLink, '_blank')} - > - {t('KONNECTORCONFIG.BTN_NOACCOUNT')} - </StyledButton> - </form> + )} + </> ) } diff --git a/src/components/ContentComponents/Konnector/KonnectorLoading.tsx b/src/components/ContentComponents/Konnector/KonnectorLoading.tsx index e502a959a..9f24e828a 100644 --- a/src/components/ContentComponents/Konnector/KonnectorLoading.tsx +++ b/src/components/ContentComponents/Konnector/KonnectorLoading.tsx @@ -1,7 +1,17 @@ -import React from 'react' +import React, { useState, useEffect } from 'react' +import { withClient, Client } from 'cozy-client' import { translate } from 'cozy-ui/react/I18n' -import Lottie from 'react-lottie' +import { isKonnectorRunning } from 'cozy-harvest-lib/dist/helpers/triggers' +import { + KonnectorJob, + ERROR_EVENT, + LOGIN_SUCCESS_EVENT, + SUCCESS_EVENT, +} from 'cozy-harvest-lib/dist/models/KonnectorJob' +import { Trigger } from 'doctypes' + +import Lottie from 'react-lottie' import * as loadingData from 'assets/anims/bounceloading.json' const loadingOptions = { @@ -14,12 +24,52 @@ const loadingOptions = { } interface KonnectorLoadingProps { + trigger: Trigger + handleKonnectorLoading: Function + client: Client t: Function } const KonnectorLoading: React.FC<KonnectorLoadingProps> = ({ + trigger, + handleKonnectorLoading, + client, t, }: KonnectorLoadingProps) => { + const callbackResponse = (state: string) => { + console.log('event', state) + handleKonnectorLoading(state) + } + + useEffect(() => { + let subscribed = true + async function getData() { + if (!isKonnectorRunning(trigger)) { + const konnectorJob = new KonnectorJob(client, trigger) + const test = await konnectorJob.launch() + console.log(test) + console.log(konnectorJob) + const result = konnectorJob.getStatus() + console.log(result) + const result2 = konnectorJob.jobWatcher + console.log(result2) + konnectorJob.jobWatcher.on(ERROR_EVENT, () => { + callbackResponse(ERROR_EVENT) + }) + konnectorJob.jobWatcher.on(LOGIN_SUCCESS_EVENT, () => { + callbackResponse(LOGIN_SUCCESS_EVENT) + }) + konnectorJob.jobWatcher.on(SUCCESS_EVENT, () => { + callbackResponse(SUCCESS_EVENT) + }) + } + } + getData() + return () => { + subscribed = false + } + }, []) + return ( <div className="kload-content"> <Lottie options={loadingOptions} height={50} width={50} /> @@ -33,4 +83,4 @@ const KonnectorLoading: React.FC<KonnectorLoadingProps> = ({ ) } -export default translate()(KonnectorLoading) +export default translate()(withClient(KonnectorLoading)) diff --git a/src/components/ContentComponents/Konnector/KonnectorLoginForm.tsx b/src/components/ContentComponents/Konnector/KonnectorLoginForm.tsx new file mode 100644 index 000000000..77affd21d --- /dev/null +++ b/src/components/ContentComponents/Konnector/KonnectorLoginForm.tsx @@ -0,0 +1,134 @@ +import React, { useState } from 'react' +import { withClient, Client } from 'cozy-client' +import { translate } from 'cozy-ui/react/I18n' + +import IFluidConfig from 'services/IFluidConfig' + +import StyledIconButton from 'components/CommonKit/IconButton/StyledIconButton' +import StyledButton from 'components/CommonKit/Button/StyledButton' +import TrailingIcon from 'assets/icons/ico/trailing-icon.svg' +import { ConnectionService } from 'services/connectionService' + +interface KonnectorLoginFormProps { + fluidConfig: IFluidConfig + onSuccess: Function + client: Client + t: Function +} + +const KonnectorLoginForm: React.FC<KonnectorLoginFormProps> = ({ + fluidConfig, + onSuccess, + client, + t, +}: KonnectorLoginFormProps) => { + const konnectorName: string = fluidConfig.konnectorConfig.name + const konnectorType: string = fluidConfig.konnectorConfig.type + const siteLink: string = fluidConfig.siteLink + + const [login, setLogin] = useState<string>('') + const [password, setPassword] = useState<string>('') + const [error, setError] = useState<string>('') + const [loading, setLoading] = useState<boolean>(false) + + function revealPassword(idInput: string) { + const input = document.getElementById(idInput) + if (input) { + if (input.getAttribute('type') === 'password') { + input.setAttribute('type', 'text') + } else { + input.setAttribute('type', 'password') + } + } + } + + const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => { + e.preventDefault() + try { + setError('') + setLoading(true) + if (!login || !password) { + // TODO - translation + setError('Please enter a login and password') + setLoading(false) + return null + } + const connectionService = new ConnectionService( + client, + fluidConfig.konnectorConfig.slug, + login, + password + ) + const { account, trigger } = await connectionService.connectNewUser() + if (!trigger) { + // TODO - translation + setError('Error during account creation') + setLoading(false) + return null + } + onSuccess(account, trigger) + } catch (error) { + setLoading(false) + } + } + + return ( + <form + className="form" + onSubmit={(e: React.FormEvent<HTMLFormElement>) => handleSubmit(e)} + > + {t('KONNECTORCONFIG.LABEL_FILLIN')} {konnectorName} + <div className="form-group"> + <div className="form-message">{error}</div> + <input + id={'idFieldLogin' + konnectorType} + type="text" + className="form-control form-input" + aria-describedby="emailHelp" + placeholder="Adresse e-mail" + name="login" + onChange={(e: React.ChangeEvent<HTMLInputElement>) => + setLogin(e.target.value) + } + value={login} + ></input> + </div> + <div className="form-group"> + <div className="form-message">{error}</div> + <input + id={'idFieldPassword' + konnectorType} + type="password" + className="form-control form-input" + aria-describedby="PasswordHelp" + placeholder="Mot de passe" + name="password" + onChange={(e: React.ChangeEvent<HTMLInputElement>) => + setPassword(e.target.value) + } + value={password} + /> + <span> + <StyledIconButton + icon={TrailingIcon} + className="form-trailing-icon" + size={22} + onClick={() => revealPassword('idFieldPassword' + konnectorType)} + /> + </span> + </div> + <StyledButton type="submit" color="primary" disabled={loading}> + {t('KONNECTORCONFIG.BTN_CONNECTION')} + </StyledButton> + <StyledButton + type="button" + color="secondary" + disabled={loading} + onClick={() => window.open(siteLink, '_blank')} + > + {t('KONNECTORCONFIG.BTN_NOACCOUNT')} + </StyledButton> + </form> + ) +} + +export default translate()(withClient(KonnectorLoginForm)) diff --git a/src/components/ContentComponents/Konnector/KonnectorNotFound.tsx b/src/components/ContentComponents/Konnector/KonnectorNotFound.tsx index 6432b778a..c7c876d06 100644 --- a/src/components/ContentComponents/Konnector/KonnectorNotFound.tsx +++ b/src/components/ContentComponents/Konnector/KonnectorNotFound.tsx @@ -13,6 +13,7 @@ const KonnectorNotFound: React.FC<KonnectorNotFoundProps> = ({ t, }: KonnectorNotFoundProps) => { const openKonnectorURL = () => { + // TODO - Use getstoreinstallationurl from client - https://docs.cozy.io/en/cozy-client/api/cozy-client/#getstoreinstallationurl-string console.log(window.location) const hostname = window.location.origin.replace('ecolyo', 'store') const url = hostname + '/#/discover/' + konnectorSlug diff --git a/src/components/ContentComponents/Konnector/KonnectorOAuthForm.tsx b/src/components/ContentComponents/Konnector/KonnectorOAuthForm.tsx index 19381bb90..6461ee961 100644 --- a/src/components/ContentComponents/Konnector/KonnectorOAuthForm.tsx +++ b/src/components/ContentComponents/Konnector/KonnectorOAuthForm.tsx @@ -1,15 +1,19 @@ import React from 'react' +import { Client, withClient } from 'cozy-client' import { translate } from 'cozy-ui/react/I18n' -import { Konnector } from 'doctypes' +import { Konnector, Trigger } from 'doctypes' import OAuthForm from 'components/ContentComponents/OAuth/OAuthForm' import StyledButton from 'components/CommonKit/Button/StyledButton' +import { AccountService } from 'services/accountService' +import { TriggerService } from 'services/triggersService' interface KonnectorOAuthFormProps { konnector: Konnector siteLink: string onSuccess: Function loading: boolean + client: Client t: Function } @@ -18,15 +22,25 @@ const KonnectorOAuthForm: React.FC<KonnectorOAuthFormProps> = ({ siteLink, onSuccess, loading, + client, t, }: KonnectorOAuthFormProps) => { + const handleSuccess = async (accountId: string) => { + const account = await AccountService.getAccount(client, accountId) + if (!account) { + onSuccess(null, null) + } + const triggersServices = new TriggerService(client, account, konnector) + const trigger: Trigger = await triggersServices.createTrigger() + onSuccess(account, trigger) + } return ( <div className="koauthform"> <div className="koauthform-text text-16-normal"> {t('oauth.connect.' + konnector.slug + '.info')} </div> <div className="koauthform-button"> - <OAuthForm konnector={konnector} onSuccess={onSuccess} /> + <OAuthForm konnector={konnector} onSuccess={handleSuccess} /> </div> <div className="koauthform-text text-16-bold"> <div className="text-16-bold"> @@ -50,4 +64,4 @@ const KonnectorOAuthForm: React.FC<KonnectorOAuthFormProps> = ({ ) } -export default translate()(KonnectorOAuthForm) +export default translate()(withClient(KonnectorOAuthForm)) diff --git a/src/components/ContentComponents/Konnector/KonnectorResult.tsx b/src/components/ContentComponents/Konnector/KonnectorResult.tsx index d6cf0f870..fa5f3161a 100644 --- a/src/components/ContentComponents/Konnector/KonnectorResult.tsx +++ b/src/components/ContentComponents/Konnector/KonnectorResult.tsx @@ -1,42 +1,122 @@ -import React from 'react' +import React, { useState, useContext, useEffect } from 'react' +import { withClient, Client } from 'cozy-client' import { translate } from 'cozy-ui/react/I18n' +import { AppContext } from 'components/Contexts/AppContextProvider' + import StyledButton from 'components/CommonKit/Button/StyledButton' import StyledBlackSpinner from 'components/CommonKit/Spinner/StyledBlackSpinner' +import { Account, Konnector, Trigger } from 'doctypes' +import { + getKonnectorJobError, + getKonnectorStatus, + getLastSuccessDate, +} from 'cozy-harvest-lib/dist/helpers/triggers' +import { TriggerService } from 'services/triggersService' +import { AccountService } from 'services/accountService' + interface KonnectorResultProps { - date: string - updating: boolean - errored: boolean - updateKonnector: (event: any) => void - deleteAccount: (event: any) => void + account: Account + konnector: Konnector + client: Client t: Function } const KonnectorResult: React.FC<KonnectorResultProps> = ({ - date, - updating, - errored, - updateKonnector, - deleteAccount, + account, + konnector, + client, t, }: KonnectorResultProps) => { + const [updating, setUpdating] = useState<boolean>(false) + const [lastExecutionDate, setLastExecutionDate] = useState<string>('-') + const [status, setStatus] = useState<string>('') + + const context = useContext(AppContext) + + // TODO - rework + const updateKonnector = async () => { + setUpdating(true) + try { + const triggersServices = new TriggerService(client, account, konnector) + if (trigger !== null) { + triggersServices.setTrigger(trigger) + } + const job = await triggersServices.launchTrigger() + if (!job) { + throw new Error(`Error during trigger launching`) + } + setlaunchedJob(job) + } catch (error) { + setUpdating(false) + } + } + + const deleteAccount = async () => { + setUpdating(true) + try { + if (account) { + const delAccount = await AccountService.deleteAccount(client, account) + await context.refreshFluidTypes() + } + } catch (error) { + setUpdating(false) + } + } + + useEffect(() => { + let subscribed = true + async function getData() { + const _trigger = await TriggerService.fetchTriggerFromAccount( + client, + account + ) + console.log(_trigger) + if (subscribed && _trigger) { + console.log('trigger', _trigger) + const triggerState = await TriggerService.fetchTriggerState( + client, + _trigger + ) + if (subscribed && triggerState) { + console.log(triggerState) + // TODO - format date + setLastExecutionDate( + new Date(triggerState.last_execution).toDateString() + ) + setStatus(triggerState.status) + await context.refreshFluidTypes() + } + } + } + getData() + return () => { + subscribed = false + } + }, []) + return ( <div className="accordion-update-result"> <div className="accordion-update"> <div className={ - errored + status === 'errored' ? 'accordion-caption-red text-16-normal' : 'accordion-caption text-16-normal' } > {t('KONNECTORCONFIG.LABEL_UPDATEDAT')} </div> - <div>{date}</div> + <div>{lastExecutionDate}</div> </div> <div className="inline-buttons"> - <StyledButton type="button" color="primary" onClick={updateKonnector}> + <StyledButton + type="button" + color="primary" + onClick={updateKonnector} + disabled={updating} + > {updating ? ( <StyledBlackSpinner size="2em" /> ) : ( @@ -56,4 +136,4 @@ const KonnectorResult: React.FC<KonnectorResultProps> = ({ ) } -export default translate()(KonnectorResult) +export default translate()(withClient(KonnectorResult)) diff --git a/src/components/ContentComponents/KonnectorViewer/KonnectorViewer.tsx b/src/components/ContentComponents/KonnectorViewer/KonnectorViewer.tsx index 0b12f3c51..5f9c8ec19 100644 --- a/src/components/ContentComponents/KonnectorViewer/KonnectorViewer.tsx +++ b/src/components/ContentComponents/KonnectorViewer/KonnectorViewer.tsx @@ -1,9 +1,8 @@ import React, { useState, useEffect } from 'react' import { withClient, Client } from 'cozy-client' -import { Konnector, Trigger } from 'doctypes' +import { Konnector } from 'doctypes' import KonnectorService from 'services/konnectorService' -import KonnectorStatusService from 'services/konnectorStatusService' import KonnectorViewerCard from 'components/ContentComponents/KonnectorViewer/KonnectorViewerCard' import IFluidConfig from 'services/IFluidConfig' @@ -20,7 +19,6 @@ const KonnectorViewer: React.FC<KonnectorViewerProps> = ({ isParam = false, }: KonnectorViewerProps) => { const [konnector, setKonnector] = useState<Konnector | null>(null) - const [lastTrigger, setLastTrigger] = useState<Trigger | null>(null) const [loaded, setLoaded] = useState<boolean>(false) useEffect(() => { @@ -29,28 +27,10 @@ const KonnectorViewer: React.FC<KonnectorViewerProps> = ({ client, fluidConfig.konnectorConfig.slug ) - const kss = new KonnectorStatusService(client) - async function getData() { const _konnector: Konnector = await konnectorService.fetchKonnector() if (subscribed && _konnector) { setKonnector(_konnector) - const allTriggers = await kss.getAllTriggers() - if (allTriggers) { - const lastTrigger = allTriggers - .filter( - (trigger: Trigger) => - trigger.worker === 'konnector' && - trigger.message.konnector === fluidConfig.konnectorConfig.slug - ) - .sort((a: string | number | Date, b: string | number | Date) => - new Date(a) > new Date(b) ? 1 : -1 - ) - .shift() - if (subscribed && lastTrigger) { - setLastTrigger(lastTrigger) - } - } } if (subscribed) { setLoaded(true) @@ -68,7 +48,6 @@ const KonnectorViewer: React.FC<KonnectorViewerProps> = ({ <KonnectorViewerCard fluidConfig={fluidConfig} konnector={konnector} - trigger={lastTrigger} isParam={isParam} /> )} diff --git a/src/components/ContentComponents/KonnectorViewer/KonnectorViewerCard copy.tsx b/src/components/ContentComponents/KonnectorViewer/KonnectorViewerCard copy.tsx deleted file mode 100644 index 46858c540..000000000 --- a/src/components/ContentComponents/KonnectorViewer/KonnectorViewerCard copy.tsx +++ /dev/null @@ -1,342 +0,0 @@ -import React, { useState, useEffect, useRef, useContext } from 'react' -import { ConnectionService } from 'services/connectionService' -import { TriggerService } from 'services/triggersService' -import { AccountService } from 'services/accountService' - -import { AppContext } from 'components/Contexts/AppContextProvider' - -import { JobService, JobState } from 'services/jobsService' -import { FluidType } from 'enum/fluid.enum' - -import { withClient, Client } from 'cozy-client' -import { translate } from 'cozy-ui/react/I18n' - -import { getPicto, getAddPicto, getFuildType, getParamPicto } from 'utils/utils' -import chevronDown from 'assets/icons/ico/chevron-down.svg' -import chevronUp from 'assets/icons/ico/chevron-up.svg' - -import StyledIcon from 'components/CommonKit/Icon/StyledIcon' -import StyledIconButton from 'components/CommonKit/IconButton/StyledIconButton' - -import failurePicto from 'components/ContentComponents/KonnectorViewer/picto-failure.png' -import KonnectorStatusService from 'services/konnectorStatusService' -import IFluidConfig from 'services/IFluidConfig' - -import KonnectorForm from 'components/ContentComponents/Konnector/KonnectorForm' -import KonnectorOAuthForm from 'components/ContentComponents/Konnector/KonnectorOAuthForm' -import KonnectorLoading from 'components/ContentComponents/Konnector/KonnectorLoading' -import KonnectorResult from 'components/ContentComponents/Konnector/KonnectorResult' - -import { Konnector, Trigger } from 'doctypes' - -interface KonnectorViewerCardProps { - fluidConfig: IFluidConfig - konnector: Konnector - trigger: Trigger | null - client: Client - isParam: boolean - t: Function -} - -const KonnectorViewerCard: React.FC<KonnectorViewerCardProps> = ({ - fluidConfig, - konnector, - trigger, - client, - isParam, - t, -}: KonnectorViewerCardProps) => { - const [login, setLogin] = useState<string>('') - const [password, setPassword] = useState<string>('') - const [loading, setLoading] = useState<boolean>(false) - const [updating, setUpdating] = useState<boolean>(false) - const [konnectorAccountId, setAccountId] = useState<string>('') - const [isKonnectorAcc, setAccount] = useState<boolean>(false) - const [setActive, setActiveState] = useState('') - const [setHeight, setHeightState] = useState('0px') - const [updateTrigger, setUpdateTrigger] = useState<any>(null) - const [updateDate, setUpdateDate] = useState<string>( - new Date('0001-01-01T00:00:00Z').toDateString() - ) - // const [frequency, setFrequency] = useState<string>('daily') - const [error, setError] = useState<string>('') - const [jobState, setJobState] = useState<string>('') - const [launchedJob, setlaunchedJob] = useState<any>(null) - const content: React.MutableRefObject<null> = useRef<null>(null) - const type: string = fluidConfig.konnectorConfig.type - const fluid: FluidType = getFuildType(type) - const iconType = getPicto(fluid) - const context = useContext(AppContext) - - const iconAddType = isParam ? getParamPicto(fluid) : getAddPicto(fluid) - - const toggleAccordion = () => { - setActiveState(setActive === '' ? 'active' : '') - setHeightState( - setActive === 'active' && - content && - content.current && - content.current.scrollHeight - ? '0px' - : `${content.current.scrollHeight}px` - ) - } - - const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => { - try { - e.preventDefault() - setError('') - setLoading(true) - if (!login || !password) { - setError('Please enter a login and password') - setLoading(false) - return null - } - setJobState(JobState.Running) - const connectionService = new ConnectionService( - client, - fluidConfig.konnectorConfig.slug, - login, - password - ) - const data = await connectionService.connectNewUser() - setLoading(false) - if (!data) { - setError('Error during the user connection') - setLoading(false) - setJobState(JobState.Errored) - return null - } - trigger = await KonnectorStatusService.getSingleTrigger( - client, - data.trigger_id - ) - setUpdateTrigger(trigger) - setlaunchedJob(data) - setUpdateDate(new Date(data.queued_at).toDateString()) - setAccountId(data.message.account) - setError('Connected') - setAccount(true) - return data - } catch (error) { - setLoading(false) - setError(error.message) - } - } - - const deleteAccount = async () => { - try { - const account = await AccountService.getAccount( - client, - konnectorAccountId - ) - if (!account) { - setError('Error fetching account') - setLoading(false) - } - const delAccount = await AccountService.deleteAccount(client, account) - if (!delAccount) { - setError('Error during deletion account') - setLoading(false) - } - setError('') - setJobState('') - toggleAccordion() - trigger = null - setAccount(false) - await context.refreshFluidTypes() - } catch (error) { - setLoading(false) - setError(error.message) - } - } - - const updateKonnector = async () => { - setError('') - setJobState('') - setUpdating(true) - try { - const account = await AccountService.getAccount( - client, - konnectorAccountId - ) - const triggersServices = new TriggerService(client, account, konnector) - if (updateTrigger !== null) { - triggersServices.setTrigger(updateTrigger) - } else { - triggersServices.setTrigger(trigger) - } - const job = await triggersServices.launchTrigger() - if (!job) { - throw new Error(`Error during trigger launching`) - } - setlaunchedJob(job) - setUpdateDate(new Date(job.queued_at).toDateString()) - } catch (error) { - setUpdating(false) - setError(error.message) - } - } - - const initOauthAccount = async (oauthAccountId: string) => { - setError('') - setJobState('') - setLoading(true) - try { - const account = await AccountService.getAccount(client, oauthAccountId) - const triggersServices = new TriggerService(client, account, konnector) - const trigger: Trigger = await triggersServices.createTrigger() - if (!trigger) { - throw new Error(`Error during trigger creation`) - } - //Launch the creation trigger - const job = await triggersServices.launchTrigger() - if (!job) { - throw new Error(`Error during trigger launching`) - } - setUpdateTrigger(trigger) - setlaunchedJob(job) - setUpdateDate(new Date(job.queued_at).toDateString()) - setAccountId(job.message.account) - setError('Connected') - setAccount(true) - } catch (error) { - setLoading(false) - setError(error.message) - } - } - - const getIcon = (jobState: string) => { - switch (jobState) { - case JobState.Errored: - return failurePicto - // case JobState.Running: - // return pendingPicto - // case JobState.Done: - // return successPicto - default: - return '' - } - } - - const getKonnectorStateMarkup = (jobState: string) => { - const iconSrc = getIcon(jobState) - if (iconSrc === '') return '' - return <img className="state-icon" src={iconSrc}></img> - } - - const jobStateCallBack = async (state: string) => { - setJobState(state) - if (state !== JobState.Running) { - setLoading(false) - setUpdating(false) - setJobState(state) - context.refreshFluidTypes() - } - } - - useEffect(() => { - let subscribed = true - async function getTriggerData() { - if (trigger || launchedJob) { - const jobService = new JobService(client) - let runningJob = null - - if (trigger) { - let triggerState = null - triggerState = TriggerService.fetchStateFromTrigger(trigger) - const konnectorAcc = TriggerService.fetchKonnectorAccountFromTrigger( - trigger - ) - if (subscribed && konnectorAcc.konnector === konnector.slug) { - setAccountId(konnectorAcc.account) - setAccount(true) - } - if (subscribed && !launchedJob) setJobState(triggerState.status) - if (triggerState.status === JobState.Running) - runningJob = jobService.fetchJob(triggerState.last_executed_job_id) - } - if (launchedJob) runningJob = launchedJob - if (runningJob) jobService.watch(runningJob, jobStateCallBack) - } - } - getTriggerData() - return () => { - subscribed = false - } - }, [trigger, launchedJob]) - - return ( - <> - <div className={`accordion ${setActive}`}> - <div> - <div - className={`accordion-header ${setActive}`} - onClick={toggleAccordion} - > - <div className="accordion-icon"> - {!isKonnectorAcc ? ( - <StyledIcon className="icon" icon={iconAddType} size={49} /> - ) : ( - <StyledIcon className="icon" icon={iconType} size={49} /> - )} - </div> - <div className="state-picto"> - {getKonnectorStateMarkup(jobState)} - </div> - <div className="accordion-info"> - <div className="accordion-title text-16-normal"> - {!isKonnectorAcc - ? t('KONNECTORCONFIG.LABEL_CONNECTTO_' + FluidType[fluid]) - : t('FLUID.' + FluidType[fluid] + '.LABEL')} - </div> - </div> - <StyledIconButton icon={setActive ? chevronUp : chevronDown} /> - </div> - <div - ref={content} - style={{ maxHeight: `${setHeight}` }} - className={`accordion-content ${setActive}`} - > - {!isKonnectorAcc ? ( - !fluidConfig.konnectorConfig.oauth ? ( - <KonnectorForm - fluidConfig={fluidConfig} - login={login} - setLogin={setLogin} - password={password} - setPassword={setPassword} - loading={loading} - error={error} - handleSubmit={handleSubmit} - /> - ) : ( - <KonnectorOAuthForm - konnector={konnector} - siteLink={fluidConfig.siteLink} - onSuccess={initOauthAccount} - loading={loading} - /> - ) - ) : loading ? ( - <KonnectorLoading /> - ) : ( - <KonnectorResult - date={ - trigger - ? TriggerService.fetchDateFromTrigger(trigger) - : updateDate - } - updating={updating} - errored={jobState === JobState.Errored} - updateKonnector={updateKonnector} - deleteAccount={deleteAccount} - /> - )} - </div> - </div> - </div> - </> - ) -} -export default translate()(withClient(KonnectorViewerCard)) diff --git a/src/components/ContentComponents/KonnectorViewer/KonnectorViewerCard.tsx b/src/components/ContentComponents/KonnectorViewer/KonnectorViewerCard.tsx index 6239cc01d..e0d85934d 100644 --- a/src/components/ContentComponents/KonnectorViewer/KonnectorViewerCard.tsx +++ b/src/components/ContentComponents/KonnectorViewer/KonnectorViewerCard.tsx @@ -1,10 +1,6 @@ -import React, { useState, useEffect, useRef, useContext } from 'react' -import { ConnectionService } from 'services/connectionService' -import { TriggerService } from 'services/triggersService' +import React, { useState, useEffect, useRef } from 'react' import { AccountService } from 'services/accountService' -import { AppContext } from 'components/Contexts/AppContextProvider' - import { JobService, JobState } from 'services/jobsService' import { FluidType } from 'enum/fluid.enum' @@ -19,13 +15,10 @@ import StyledIcon from 'components/CommonKit/Icon/StyledIcon' import StyledIconButton from 'components/CommonKit/IconButton/StyledIconButton' import failurePicto from 'components/ContentComponents/KonnectorViewer/picto-failure.png' -import KonnectorStatusService from 'services/konnectorStatusService' import IFluidConfig from 'services/IFluidConfig' import KonnectorNotFound from 'components/ContentComponents/Konnector/KonnectorNotFound' import KonnectorForm from 'components/ContentComponents/Konnector/KonnectorForm' -import KonnectorOAuthForm from 'components/ContentComponents/Konnector/KonnectorOAuthForm' -import KonnectorLoading from 'components/ContentComponents/Konnector/KonnectorLoading' import KonnectorResult from 'components/ContentComponents/Konnector/KonnectorResult' import { Konnector, Trigger } from 'doctypes' @@ -33,7 +26,6 @@ import { Konnector, Trigger } from 'doctypes' interface KonnectorViewerCardProps { fluidConfig: IFluidConfig konnector: Konnector - trigger: Trigger | null client: Client isParam: boolean t: Function @@ -42,32 +34,22 @@ interface KonnectorViewerCardProps { const KonnectorViewerCard: React.FC<KonnectorViewerCardProps> = ({ fluidConfig, konnector, - trigger, client, isParam, t, }: KonnectorViewerCardProps) => { - const [login, setLogin] = useState<string>('') - const [password, setPassword] = useState<string>('') - const [loading, setLoading] = useState<boolean>(false) - const [updating, setUpdating] = useState<boolean>(false) - const [konnectorAccountId, setAccountId] = useState<string>('') - const [isKonnectorAcc, setAccount] = useState<boolean>(false) + const [account, setAccount] = useState<Account | null>(null) + const [setActive, setActiveState] = useState('') const [setHeight, setHeightState] = useState('0px') - const [updateTrigger, setUpdateTrigger] = useState<Trigger | null>(null) - const [updateDate, setUpdateDate] = useState<string>( - new Date('0001-01-01T00:00:00Z').toDateString() - ) - // const [frequency, setFrequency] = useState<string>('daily') + const [error, setError] = useState<string>('') const [jobState, setJobState] = useState<string>('') - const [launchedJob, setlaunchedJob] = useState<any>(null) + const content: React.MutableRefObject<null> = useRef<null>(null) const type: string = fluidConfig.konnectorConfig.type const fluid: FluidType = getFuildType(type) const iconType = getPicto(fluid) - const context = useContext(AppContext) const iconAddType = isParam ? getParamPicto(fluid) : getAddPicto(fluid) @@ -83,130 +65,7 @@ const KonnectorViewerCard: React.FC<KonnectorViewerCardProps> = ({ ) } - const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => { - try { - e.preventDefault() - setError('') - setLoading(true) - if (!login || !password) { - setError('Please enter a login and password') - setLoading(false) - return null - } - setJobState(JobState.Running) - const connectionService = new ConnectionService( - client, - fluidConfig.konnectorConfig.slug, - login, - password - ) - const data = await connectionService.connectNewUser() - setLoading(false) - if (!data) { - setError('Error during the user connection') - setLoading(false) - setJobState(JobState.Errored) - return null - } - trigger = await KonnectorStatusService.getSingleTrigger( - client, - data.trigger_id - ) - setUpdateTrigger(trigger) - setlaunchedJob(data) - setUpdateDate(new Date(data.queued_at).toDateString()) - setAccountId(data.message.account) - setError('Connected') - setAccount(true) - return data - } catch (error) { - setLoading(false) - setError(error.message) - } - } - - const deleteAccount = async () => { - try { - const account = await AccountService.getAccount( - client, - konnectorAccountId - ) - if (!account) { - setError('Error fetching account') - setLoading(false) - } - const delAccount = await AccountService.deleteAccount(client, account) - if (!delAccount) { - setError('Error during deletion account') - setLoading(false) - } - setError('') - setJobState('') - toggleAccordion() - trigger = null - setAccount(false) - await context.refreshFluidTypes() - } catch (error) { - setLoading(false) - setError(error.message) - } - } - - const updateKonnector = async () => { - setError('') - setJobState('') - setUpdating(true) - try { - const account = await AccountService.getAccount( - client, - konnectorAccountId - ) - const triggersServices = new TriggerService(client, account, konnector) - if (updateTrigger !== null) { - triggersServices.setTrigger(updateTrigger) - } else { - triggersServices.setTrigger(trigger) - } - const job = await triggersServices.launchTrigger() - if (!job) { - throw new Error(`Error during trigger launching`) - } - setlaunchedJob(job) - setUpdateDate(new Date(job.queued_at).toDateString()) - } catch (error) { - setUpdating(false) - setError(error.message) - } - } - - const initOauthAccount = async (oauthAccountId: string) => { - setError('') - setJobState('') - setLoading(true) - try { - const account = await AccountService.getAccount(client, oauthAccountId) - const triggersServices = new TriggerService(client, account, konnector) - const trigger: Trigger = await triggersServices.createTrigger() - if (!trigger) { - throw new Error(`Error during trigger creation`) - } - //Launch the creation trigger - const job = await triggersServices.launchTrigger() - if (!job) { - throw new Error(`Error during trigger launching`) - } - setUpdateTrigger(trigger) - setlaunchedJob(job) - setUpdateDate(new Date(job.queued_at).toDateString()) - setAccountId(job.message.account) - setError('Connected') - setAccount(true) - } catch (error) { - setLoading(false) - setError(error.message) - } - } - + // TODO - rework const getIcon = (jobState: string) => { switch (jobState) { case JobState.Errored: @@ -215,53 +74,37 @@ const KonnectorViewerCard: React.FC<KonnectorViewerCardProps> = ({ return '' } } - + // TODO - rework const getKonnectorStateMarkup = (jobState: string) => { const iconSrc = getIcon(jobState) if (iconSrc === '') return '' return <img className="state-icon" src={iconSrc}></img> } - const jobStateCallBack = async (state: string) => { - setJobState(state) - if (state !== JobState.Running) { - setLoading(false) - setUpdating(false) - setJobState(state) - context.refreshFluidTypes() - } + const handleConnexion = (_account: Account) => { + setAccount(_account) } useEffect(() => { let subscribed = true - async function getTriggerData() { - if (konnector && (trigger || launchedJob)) { - const jobService = new JobService(client) - let runningJob = null - - if (trigger) { - let triggerState = null - triggerState = TriggerService.fetchStateFromTrigger(trigger) - const konnectorAcc = TriggerService.fetchKonnectorAccountFromTrigger( - trigger - ) - if (subscribed && konnectorAcc.konnector === konnector.slug) { - setAccountId(konnectorAcc.account) - setAccount(true) - } - if (subscribed && !launchedJob) setJobState(triggerState.status) - if (triggerState.status === JobState.Running) - runningJob = jobService.fetchJob(triggerState.last_executed_job_id) + async function getData() { + if (konnector) { + const _account = await AccountService.getAccountByType( + client, + konnector.slug + ) + if (subscribed && _account) { + console.log(konnector.slug) + console.log(_account) + setAccount(_account) } - if (launchedJob) runningJob = launchedJob - if (runningJob) jobService.watch(runningJob, jobStateCallBack) } } - getTriggerData() + getData() return () => { subscribed = false } - }, [trigger, launchedJob]) + }, []) return ( <> @@ -272,7 +115,7 @@ const KonnectorViewerCard: React.FC<KonnectorViewerCardProps> = ({ onClick={toggleAccordion} > <div className="accordion-icon"> - {!isKonnectorAcc ? ( + {!account ? ( <StyledIcon className="icon" icon={iconAddType} size={49} /> ) : ( <StyledIcon className="icon" icon={iconType} size={49} /> @@ -283,7 +126,7 @@ const KonnectorViewerCard: React.FC<KonnectorViewerCardProps> = ({ </div> <div className="accordion-info"> <div className="accordion-title text-16-normal"> - {!isKonnectorAcc + {!account ? t('KONNECTORCONFIG.LABEL_CONNECTTO_' + FluidType[fluid]) : t('FLUID.' + FluidType[fluid] + '.LABEL')} </div> @@ -300,40 +143,14 @@ const KonnectorViewerCard: React.FC<KonnectorViewerCardProps> = ({ <KonnectorNotFound konnectorSlug={fluidConfig.konnectorConfig.slug} /> - ) : !isKonnectorAcc ? ( - !fluidConfig.konnectorConfig.oauth ? ( - <KonnectorForm - fluidConfig={fluidConfig} - login={login} - setLogin={setLogin} - password={password} - setPassword={setPassword} - loading={loading} - error={error} - handleSubmit={handleSubmit} - /> - ) : ( - <KonnectorOAuthForm - konnector={konnector} - siteLink={fluidConfig.siteLink} - onSuccess={initOauthAccount} - loading={loading} - /> - ) - ) : loading ? ( - <KonnectorLoading /> - ) : ( - <KonnectorResult - date={ - trigger - ? TriggerService.fetchDateFromTrigger(trigger) - : updateDate - } - updating={updating} - errored={jobState === JobState.Errored} - updateKonnector={updateKonnector} - deleteAccount={deleteAccount} + ) : !account ? ( + <KonnectorForm + fluidConfig={fluidConfig} + konnector={konnector} + handleConnexion={handleConnexion} /> + ) : ( + <KonnectorResult account={account} konnector={konnector} /> )} </div> </div> diff --git a/src/services/accountService.ts b/src/services/accountService.ts index 373279023..807f597c5 100644 --- a/src/services/accountService.ts +++ b/src/services/accountService.ts @@ -104,9 +104,9 @@ export class AccountService { .find('io.cozy.accounts') // eslint-disable-next-line @typescript-eslint/camelcase .where({ account_type: type }) - // .sortBy([{ 'cozyMetadata.updatedAt': 'desc' }]) .limitBy(1) - return await client.query(query) + const result = await client.query(query) + return result.data[0] ? result.data[0] : null } catch (error) { throw error } diff --git a/src/services/connectionService.ts b/src/services/connectionService.ts index 7108fedf6..6fb4a3044 100644 --- a/src/services/connectionService.ts +++ b/src/services/connectionService.ts @@ -48,11 +48,15 @@ export class ConnectionService { if (!trigger) { throw new Error(`Error during trigger creation`) } - //Launch the creation trigger - const job = await triggersServices.launchTrigger() - if (!job) { - throw new Error(`Error during trigger launching`) + return { + account: account, + trigger: trigger, } - return job + //Launch the creation trigger + // const job = await triggersServices.launchTrigger() + // if (!job) { + // throw new Error(`Error during trigger launching`) + // } + // return job } } diff --git a/src/services/konnectorStatusService.ts b/src/services/konnectorStatusService.ts index 0b241f9f9..8ab8c3bde 100644 --- a/src/services/konnectorStatusService.ts +++ b/src/services/konnectorStatusService.ts @@ -16,6 +16,7 @@ export default class KonnectorStatusService { return 'konnector status' } + // TODO - move to triggerservices async getTriggerbyKonnector(konnector: Konnector) { const query = this._client .find('io.cozy.triggers') @@ -120,13 +121,13 @@ export default class KonnectorStatusService { ), ]) const data: FluidType[] = [] - if (elecData && elecData.data && elecData.data[0]) { + if (elecData) { data.push(fluidConfig[FluidType.ELECTRICITY].fluidTypeId) } - if (gasData && gasData.data && gasData.data[0]) { + if (gasData) { data.push(fluidConfig[FluidType.GAS].fluidTypeId) } - if (waterData && waterData.data && waterData.data[0]) { + if (waterData) { data.push(fluidConfig[FluidType.WATER].fluidTypeId) } return data diff --git a/src/services/triggersService.ts b/src/services/triggersService.ts index 790798be4..566954e6c 100644 --- a/src/services/triggersService.ts +++ b/src/services/triggersService.ts @@ -110,4 +110,25 @@ export class TriggerService { trigger.current_state.last_execution ).toLocaleString(DateTime.DATETIME_MED) } + + static async fetchTriggerFromAccount(client: Client, account: Account) { + if (account == null) return null + const query = client + .find('io.cozy.triggers') + .where({ 'message.account': account._id }) + .sortBy([{ 'cozyMetadata.updatedAt': 'desc' }]) + .limitBy(1) + const result = await client.query(query) + return result.data[0] ? result.data[0] : null + } + + static async fetchTriggerState(client: Client, trigger: Trigger) { + if (trigger == null) return null + const triggerState = await client + .getStackClient() + .fetchJSON('GET', `/jobs/triggers/${trigger._id}`) + return triggerState.data.attributes.current_state + ? triggerState.data.attributes.current_state + : null + } } -- GitLab