diff --git a/manifest.konnector b/manifest.konnector index ea10c3b916b828333c16faf85c3d8ef685e318e3..17bc04d5e350c2dc6755d398820ab7d2a7c30404 100644 --- a/manifest.konnector +++ b/manifest.konnector @@ -81,5 +81,6 @@ } } }, - "manifest_version": "2" + "manifest_version": "2", + "on_delete_account": "onDeleteAccount.js" } diff --git a/package.json b/package.json index eb3c5f85ada17a90a7928cd8d91bd1490381b56a..3d76bd2734418c82bf344741e0cf2f35b37238cf 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,8 @@ "start": "node ./src/index.js", "dev": "cozy-konnector-dev", "standalone": "cozy-konnector-standalone", + "onDeleteAccount:standalone": "cozy-konnector-standalone src/onDeleteAccount.js", + "onDeleteAccount": "cozy-konnector-dev src/onDeleteAccount.js", "test": "jest", "test:cov": "jest --coverage", "pretest": "npm run clean", diff --git a/src/core/contractActivation.js b/src/core/contractActivation.js index 0e8242f58d5e7d3a7fee90fb2983a9728f642b76..a1d05dbc9ccafc4c2a4d71b6f93e11f50d3e9a5e 100644 --- a/src/core/contractActivation.js +++ b/src/core/contractActivation.js @@ -32,7 +32,7 @@ async function activateContract( } const { response } = await soapRequest({ - url: url, + url: `${url}/enedis_SGE_CommandeCollectePublicationMesures/1.0`, headers: sgeHeaders, xml: commanderCollectePublicationMesures( appLogin, @@ -57,7 +57,12 @@ async function activateContract( try { return parseServiceId(parsedReply) } catch (error) { - log('error', 'Error whileactivating contract: ' + error) + log('error', 'Error while activating contract: ' + error) + log( + 'error', + `Enedis issue ${parsedReply.Envelope.Body.Fault.detail.erreur.resultat.$.code}: ${parsedReply.Envelope.Body.Fault.faultstring}` + ) + //TODO: handle SGT4B8: Il existe déjà plusieurs demandes en cours sur le point ? throw errors.LOGIN_FAILED } } diff --git a/src/core/contractStartDate.js b/src/core/contractStartDate.js index cc8770f7e385c15425626a680c7b7d57dd238183..051d4edad9e2d795e056e08c63739fcbd594fc21 100644 --- a/src/core/contractStartDate.js +++ b/src/core/contractStartDate.js @@ -42,6 +42,10 @@ async function getContractStartDate(url, apiAuthKey, userLogin, pointId) { return parseContractStartDate(result) } catch (error) { log('error', 'Error while processing contract start date: ' + error) + log( + 'error', + `Enedis issue ${result.Envelope.Body.Fault.detail.erreur.resultat.$.code}: ${result.Envelope.Body.Fault.faultstring}` + ) throw errors.NOT_EXISTING_DIRECTORY } } diff --git a/src/core/contractTermination.js b/src/core/contractTermination.js index aa127c07e7d0527bcc2effbfc299a3968b347f29..b558c76be29d4e27ecff4f42b8a0682cb2ae77d2 100644 --- a/src/core/contractTermination.js +++ b/src/core/contractTermination.js @@ -21,14 +21,14 @@ async function terminateContract( pointId, serviceId ) { - log('info', 'activateContract') + log('info', 'terminateContract') const sgeHeaders = { 'Content-Type': 'text/xml;charset=UTF-8', apikey: apiAuthKey, } const { response } = await soapRequest({ - url: url, + url: `${url}/enedis_SGE_CommandeArretServiceSouscritMesures/1.0`, headers: sgeHeaders, xml: commanderArretServiceSouscritMesures( appLogin, @@ -50,9 +50,19 @@ async function terminateContract( try { // We don't need any action on reply for now + if (parsedReply.Envelope.Body.Fault) { + log( + 'error', + `Enedis issue ${parsedReply.Envelope.Body.Fault.detail.erreur.resultat.$.code}: ${parsedReply.Envelope.Body.Fault.faultstring}` + ) + } return parsedReply } catch (error) { log('error', 'Error while parsing user contract termination: ' + error) + log( + 'error', + `Enedis issue ${parsedReply.Envelope.Body.Fault.detail.erreur.resultat.$.code}: ${parsedReply.Envelope.Body.Fault.faultstring}` + ) throw errors.VENDOR_DOWN } } diff --git a/src/core/contractVerification.js b/src/core/contractVerification.js index 1bd598a9e424d2a424cc9ee1bb27c4db7fd6f3dd..1dd96cf37c8fe01918786ad311cfbb6ff1e3c55e 100644 --- a/src/core/contractVerification.js +++ b/src/core/contractVerification.js @@ -4,7 +4,7 @@ const soapRequest = require('easy-soap-request') const { parseTags, parseValue, parseContracts } = require('../helpers/parsing') const { rechercherServicesSouscritsMesures } = require('../requests/sge') const xml2js = require('xml2js') -const { contractState } = require('./types/enum') +const { contractState, contractLibelle } = require('./types/enum') /** * @param {string} url @@ -37,12 +37,25 @@ async function verifyContract(url, apiAuthKey, appLogin, contractId, pointId) { }) try { - const currentContract = parseContracts(parsedReply)[0] - if (currentContract.etatCode === contractState.ACTIF) + const currentContracts = parseContracts(parsedReply)[0] + let currentContract = null + if (Array.isArray(currentContracts)) { + currentContract = parseContracts(parsedReply)[0] + } else { + currentContract = parseContracts(parsedReply) + } + if ( + currentContract.etatCode === contractState.ACTIF && + currentContract.serviceSouscritLibelle === contractLibelle.ACTIF + ) return currentContract.serviceSouscritId return null } catch (error) { log('error', 'Error while parsing user contract: ' + error) + log( + 'error', + `Enedis issue ${parsedReply.Envelope.Body.Fault.detail.erreur.resultat.$.code}: ${parsedReply.Envelope.Body.Fault.faultstring}` + ) throw errors.LOGIN_FAILED } } diff --git a/src/core/findUserPdl.js b/src/core/findUserPdl.js index e7c46bbdbb9de4c59ff76f66d36da898e97f3f08..de191fe87554f369bbf4c31ead3cc9393c8185e4 100644 --- a/src/core/findUserPdl.js +++ b/src/core/findUserPdl.js @@ -50,6 +50,10 @@ async function findUserPdl( return parseUserPdl(parsedReply) } catch (error) { log('error', 'Error while parsing user PDL: ' + error) + log( + 'error', + `Enedis issue ${parsedReply.Envelope.Body.Fault.detail.erreur.resultat.$.code}: ${parsedReply.Envelope.Body.Fault.faultstring}` + ) throw errors.LOGIN_FAILED } } diff --git a/src/core/types/enum.js b/src/core/types/enum.js index 9e744e3fe1707963b9f26b325538450d96465741..e1cf0f322c54a41361a23d390f055d06b88532c6 100644 --- a/src/core/types/enum.js +++ b/src/core/types/enum.js @@ -8,4 +8,14 @@ const contractState = { ACTIF: 'ACTIF', } -module.exports = { contractState } +/** + * Enum for contractLibelle values. + * @readonly + * @enum {number} + */ +const contractLibelle = { + ACTIF: + 'Collecte de la courbe de charge au pas 30 min avec transmission quotidienne des données brutes en soutirage', +} + +module.exports = { contractState, contractLibelle } diff --git a/src/helpers/account.js b/src/helpers/account.js new file mode 100644 index 0000000000000000000000000000000000000000..f55b5f148b48d28da3c66dd3c50dac3a6f264368 --- /dev/null +++ b/src/helpers/account.js @@ -0,0 +1,41 @@ +const { log } = require('cozy-konnector-libs') +const { iSLocal } = require('./env') + +function getAccountId() { + log('info', `getAccountId`) + try { + return JSON.parse(process.env.COZY_FIELDS).account + } catch (err) { + throw new Error(`You must provide 'account' in COZY_FIELDS: ${err.message}`) + } +} + +function getAccountRev() { + log('info', `getAccountRev`) + try { + return iSLocal() + ? 'fakeAccountRev' + : JSON.parse(process.env.COZY_FIELDS).account_rev + } catch (err) { + throw new Error(`You must provide 'account' in COZY_FIELDS: ${err.message}`) + } +} + +function getAccountSecret() { + try { + return iSLocal() + ? { + baseUrl: 'https://apis.grandlyon.fr', + sgeLogin: 'donnees.energie@grandlyon.com', + contractId: '4921350', + apiAuthKey: + 'eyJ4NXQiOiJaR1l4TVRBeVlqaGxNbVU0T1dReE56SXhZbU01TUdZMk5XVmxNak15TWpVeE9XUXdOakppWlE9PSIsImtpZCI6ImdhdGV3YXlfY2VydGlmaWNhdGVfYWxpYXMiLCJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJMREFQXC9JUEo5MTUzQGNhcmJvbi5zdXBlciIsImFwcGxpY2F0aW9uIjp7Im93bmVyIjoiTERBUFwvSVBKOTE1MyIsInRpZXJRdW90YVR5cGUiOm51bGwsInRpZXIiOiJVbmxpbWl0ZWQiLCJuYW1lIjoiU0dFIiwiaWQiOjMwLCJ1dWlkIjoiYjU0MWYxZDEtYTMzYi00ODYyLThiOGQtNTk4MDljYjQzNWRjIn0sImlzcyI6Imh0dHBzOlwvXC9hcGlzLmdyYW5kbHlvbi5mcjo0NDNcL29hdXRoMlwvdG9rZW4iLCJ0aWVySW5mbyI6eyJVbmxpbWl0ZWQiOnsidGllclF1b3RhVHlwZSI6InJlcXVlc3RDb3VudCIsImdyYXBoUUxNYXhDb21wbGV4aXR5IjowLCJncmFwaFFMTWF4RGVwdGgiOjAsInN0b3BPblF1b3RhUmVhY2giOnRydWUsInNwaWtlQXJyZXN0TGltaXQiOjAsInNwaWtlQXJyZXN0VW5pdCI6bnVsbH19LCJrZXl0eXBlIjoiUFJPRFVDVElPTiIsInBlcm1pdHRlZFJlZmVyZXIiOiIiLCJzdWJzY3JpYmVkQVBJcyI6W3sic3Vic2NyaWJlclRlbmFudERvbWFpbiI6ImNhcmJvbi5zdXBlciIsIm5hbWUiOiJlbmVkaXNfU0RFX3JlY2hlcmNoZS1wb2ludCIsImNvbnRleHQiOiJcL2VuZWRpc19TREVfcmVjaGVyY2hlLXBvaW50XC8xLjAiLCJwdWJsaXNoZXIiOiJhZG1pbiIsInZlcnNpb24iOiIxLjAiLCJzdWJzY3JpcHRpb25UaWVyIjoiVW5saW1pdGVkIn0seyJzdWJzY3JpYmVyVGVuYW50RG9tYWluIjoiY2FyYm9uLnN1cGVyIiwibmFtZSI6ImVuZWRpc19TR0VfQ29tbWFuZGVUcmFuc21pc3Npb25IaXN0b3JpcXVlTWVzdXJlcyIsImNvbnRleHQiOiJcL2VuZWRpc19TR0VfQ29tbWFuZGVUcmFuc21pc3Npb25IaXN0b3JpcXVlTWVzdXJlc1wvMS4wIiwicHVibGlzaGVyIjoiYWRtaW4iLCJ2ZXJzaW9uIjoiMS4wIiwic3Vic2NyaXB0aW9uVGllciI6IlVubGltaXRlZCJ9LHsic3Vic2NyaWJlclRlbmFudERvbWFpbiI6ImNhcmJvbi5zdXBlciIsIm5hbWUiOiJlbmVkaXNfU0dFX1JlY2hlcmNoZVNlcnZpY2VzTWVzdXJlcyIsImNvbnRleHQiOiJcL2VuZWRpc19TR0VfUmVjaGVyY2hlU2VydmljZXNNZXN1cmVzXC8xLjAiLCJwdWJsaXNoZXIiOiJhZG1pbiIsInZlcnNpb24iOiIxLjAiLCJzdWJzY3JpcHRpb25UaWVyIjoiVW5saW1pdGVkIn0seyJzdWJzY3JpYmVyVGVuYW50RG9tYWluIjoiY2FyYm9uLnN1cGVyIiwibmFtZSI6ImVuZWRpc19TR0VfQ29uc3VsdGF0aW9uTWVzdXJlcyIsImNvbnRleHQiOiJcL2VuZWRpc19TR0VfQ29uc3VsdGF0aW9uTWVzdXJlc1wvMS4wIiwicHVibGlzaGVyIjoiYWRtaW4iLCJ2ZXJzaW9uIjoiMS4wIiwic3Vic2NyaXB0aW9uVGllciI6IlVubGltaXRlZCJ9LHsic3Vic2NyaWJlclRlbmFudERvbWFpbiI6ImNhcmJvbi5zdXBlciIsIm5hbWUiOiJlbmVkaXNfU0dFX0NvbnN1bHRhdGlvbk1lc3VyZXNEZXRhaWxsZWVzIiwiY29udGV4dCI6IlwvZW5lZGlzX1NHRV9Db25zdWx0YXRpb25NZXN1cmVzRGV0YWlsbGVlc1wvMS4wIiwicHVibGlzaGVyIjoiYWRtaW4iLCJ2ZXJzaW9uIjoiMS4wIiwic3Vic2NyaXB0aW9uVGllciI6IlVubGltaXRlZCJ9LHsic3Vic2NyaWJlclRlbmFudERvbWFpbiI6ImNhcmJvbi5zdXBlciIsIm5hbWUiOiJlbmVkaXNfU0dFX0NvbnN1bHRhdGlvbkRvbm5lZXNUZWNobmlxdWVzQ29udHJhY3R1ZSIsImNvbnRleHQiOiJcL2VuZWRpc19TR0VfQ29uc3VsdGF0aW9uRG9ubmVlc1RlY2huaXF1ZXNDb250cmFjdHVlbGxlc1wvMS4wIiwicHVibGlzaGVyIjoiYWRtaW4iLCJ2ZXJzaW9uIjoiMS4wIiwic3Vic2NyaXB0aW9uVGllciI6IlVubGltaXRlZCJ9LHsic3Vic2NyaWJlclRlbmFudERvbWFpbiI6ImNhcmJvbi5zdXBlciIsIm5hbWUiOiJlbmVkaXNfU0dFX0NvbW1hbmRlQ29sbGVjdGVQdWJsaWNhdGlvbk1lc3VyZXMiLCJjb250ZXh0IjoiXC9lbmVkaXNfU0dFX0NvbW1hbmRlQ29sbGVjdGVQdWJsaWNhdGlvbk1lc3VyZXNcLzEuMCIsInB1Ymxpc2hlciI6ImFkbWluIiwidmVyc2lvbiI6IjEuMCIsInN1YnNjcmlwdGlvblRpZXIiOiJVbmxpbWl0ZWQifSx7InN1YnNjcmliZXJUZW5hbnREb21haW4iOiJjYXJib24uc3VwZXIiLCJuYW1lIjoiZW5lZGlzX1NHRV9Db21tYW5kZUFycmV0U2VydmljZVNvdXNjcml0TWVzdXJlcyIsImNvbnRleHQiOiJcL2VuZWRpc19TR0VfQ29tbWFuZGVBcnJldFNlcnZpY2VTb3VzY3JpdE1lc3VyZXNcLzEuMCIsInB1Ymxpc2hlciI6ImFkbWluIiwidmVyc2lvbiI6IjEuMCIsInN1YnNjcmlwdGlvblRpZXIiOiJVbmxpbWl0ZWQifV0sInRva2VuX3R5cGUiOiJhcGlLZXkiLCJwZXJtaXR0ZWRJUCI6IiIsImlhdCI6MTY1MDg5MDYwNiwianRpIjoiYWQzNjNlNzAtMTVhOS00NzQ1LWExNDEtOGIxOTU5MjQwZTM3In0=.UK5Jx1WRA0A6KVUFK5rs5RH7e9qAWn4v_AmmX_CHD3MNzZWikaUFD1UmS_VvMfWvqLMvRBqbkVH0X5na46e8cZA6WYaQuI5xu9oEOptv8HRY3VX-4VBpjvmTcSSRmCN6AqyalggWbzVlDsKCpVyFWavGi-DFrd7Fe9U41kNs33Li-C9LU794LEaqBPfC8avNwHv8UTzkpExR1jwCXjkLXQ-M8txHgdGGYqZ2VN0_8DeuJ7BOYQGmmENxMt0orm9k9ubtLIijnfNAOLRUZcboQRAURE_IdtuBRG0EWBkkDocefWysmckKBt6lUMtYjxpzWCDyw9UYyQAaPC1Ogy6oAg==', + } + : JSON.parse(process.env.COZY_PARAMETERS).secret + } catch (err) { + throw new Error( + `You must provide 'account-types' in COZY_PARAMETERS: ${err.message}` + ) + } +} +module.exports = { getAccountId, getAccountRev, getAccountSecret } diff --git a/src/helpers/env.js b/src/helpers/env.js index 18398087f3fd006ceb96ae7933c3a6932a77d267..a87b99ae8eb194da8d9dcb750b85b1ecae54e5f3 100644 --- a/src/helpers/env.js +++ b/src/helpers/env.js @@ -1,5 +1,4 @@ function iSLocal() { - console.log(process.env.NODE_ENV) return ( process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'local' || diff --git a/src/helpers/parsing.js b/src/helpers/parsing.js index ee1d5f1dcd5663e036f7dc4faccf48b2aa66943d..a1f9e8ff3669f1823a252557cae16b5770c169a9 100644 --- a/src/helpers/parsing.js +++ b/src/helpers/parsing.js @@ -33,7 +33,7 @@ function parseContractStartDate(result) { /** * Return User contract start date * @param {string} result - * @returns {Contract[]} + * @returns {Contract[] | Contract} */ function parseContracts(result) { log('info', 'Parsing contract') diff --git a/src/index.js b/src/index.js index 507e81f7fa9d643b9029aadee23f61edf89c7e93..eacaca76d0524cfea61cbd387070d7bc84958c16 100644 --- a/src/index.js +++ b/src/index.js @@ -155,9 +155,12 @@ async function start(fields, cozyParameters) { const userConsent = await getBoConsent(accountData.data.consentId) const user = await verifyUserIdentity(fields, baseUrl, apiAuthKey, sgeLogin) - if (user.lastname !== userConsent.lastname || !user) { + if ( + user.lastname.toLocaleUpperCase() !== + userConsent.lastname.toLocaleUpperCase() || + !user + ) { log('error', `Invalid or not found consent for user`) - await deleteBoConsent() if (userConsent.serviceId) { await terminateContract( baseUrl, @@ -167,6 +170,7 @@ async function start(fields, cozyParameters) { fields.pointId, userConsent.serviceId ) + await deleteBoConsent() } else { log('error', `No service id retrieved from BO`) throw errors.VENDOR_DOWN @@ -379,17 +383,26 @@ function processData(doctype = 'com.grandlyon.enedis.day') { throw err } // Return only needed part of info - const data = parseSgeXmlData(result) - const processedDailyData = await storeData( - await formateDataForDoctype(data), - doctype, - ['year', 'month', 'day', 'hour', 'minute'] - ) + log('info', doctype) + try { + const data = parseSgeXmlData(result) + const processedDailyData = await storeData( + await formateDataForDoctype(data), + doctype, + ['year', 'month', 'day', 'hour', 'minute'] + ) - log('info', 'Agregate enedis daily data for month and year') - if (doctype === 'com.grandlyon.enedis.day') { - log('info', 'Agregating...') - await agregateMonthAndYearData(processedDailyData) + log('info', 'Agregate enedis daily data for month and year') + if (doctype === 'com.grandlyon.enedis.day') { + log('info', 'Agregating...') + await agregateMonthAndYearData(processedDailyData) + } + } catch (e) { + if (doctype === 'com.grandlyon.enedis.minute') { + log('warn', `No half-hour activated`) + } else { + log('error', `Unkown error ${e}`) + } } } } diff --git a/src/onDeleteAccount.js b/src/onDeleteAccount.js new file mode 100644 index 0000000000000000000000000000000000000000..a3fc29dcd4d144099d9c6dff0028aab8e8865a4f --- /dev/null +++ b/src/onDeleteAccount.js @@ -0,0 +1,66 @@ +const { log, errors } = require('cozy-konnector-libs') +const { getAccountRev, getAccountSecret } = require('./helpers/account') +const { getBoConsent, deleteBoConsent } = require('./requests/bo') +const { terminateContract } = require('./core/contractTermination') +const { getAccount } = require('./requests/cozy') +// const getAccountSecret = require('./helpers/getAccountSecret') +const moment = require('moment') +require('moment-timezone') +moment.locale('fr') // set the language +moment.tz.setDefault('Europe/Paris') // set the timezone +const { iSLocal } = require('./helpers/env') +const ACCOUNT_ID = iSLocal() ? 'default_account_id' : 'enedis-sge-grandlyon' + +async function onDeleteAccount() { + log('info', 'Deleting account ...') + log('info', 'Getting secrets ...') + const secrets = getAccountSecret() + const accountRev = getAccountRev() + + if (accountRev) { + log('info', 'Account rev exist') + const accountData = await getAccount(ACCOUNT_ID) + const userConsent = await getBoConsent(accountData.data.consentId) + + if (userConsent.pointId && userConsent.pointId) { + log('error', `Consent found for user`) + if (userConsent.serviceId) { + await deleteBoConsent(userConsent.id) + await terminateContract( + secrets.baseUrl, + secrets.apiAuthKey, + secrets.sgeLogin, + secrets.contractId, + userConsent.pointId, + userConsent.serviceId + ) + } else { + log('error', `No service id retrieved from BO`) + throw errors.VENDOR_DOWN + } + } + + log('info', 'Deleting account succeed') + } else { + log( + 'error', + 'No account revision was found, something went wrong during the deletion of said account' + ) + throw errors.VENDOR_DOWN + } +} + +onDeleteAccount().then( + () => { + log('info', `onDeleteAccount: Successfully delete consent and account.`) + }, + err => { + log( + 'error', + `onDeleteAccount: An error occured during script: ${err.message}` + ) + throw errors.VENDOR_DOWN + } +) + +module.exports = { onDeleteAccount } diff --git a/webpack.config.js b/webpack.config.js index e60d80f45c206a0e2888cbc79899add29262d6a6..14002e62cf04a71ce9edf8550e75916e720282d4 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -4,7 +4,7 @@ const webpack = require('webpack') const fs = require('fs') const SvgoInstance = require('svgo') -const entry = require('./package.json').main +const index = require('./package.json').main const readManifest = () => JSON.parse(fs.readFileSync(path.join(__dirname, './manifest.konnector'))) @@ -28,7 +28,10 @@ try { const appIconRX = iconName && new RegExp(`[^/]*/${iconName}`) module.exports = { - entry, + entry: { + index, + onDeleteAccount: './src/onDeleteAccount.js', + }, target: 'node', mode: 'none', output: {