diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000000000000000000000000000000000000..a8926a50ca780291a6a3f86604260a66c3dcd8d8 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "endOfLine": "auto", + "semi": false, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "es5" +} diff --git a/src/index.js b/src/index.js index c34d6fd31bc03b79ebcf87cfc0d31d240e67b03d..3532f780e7a20613f00751d9471e02995aa3a58e 100644 --- a/src/index.js +++ b/src/index.js @@ -4,7 +4,7 @@ const { addData, hydrateAndFilter, errors, - cozyClient + cozyClient, } = require('cozy-konnector-libs') const getAccountId = require('./helpers/getAccountId') @@ -17,13 +17,18 @@ moment.locale('fr') // set the language moment.tz.setDefault('Europe/Paris') // set the timezone /*** Connector Constants ***/ -const startDailyDate = moment().subtract(32, 'month') +const manualExecution = process.env.COZY_JOB_MANUAL_EXECUTION +const startDailyDate = manualExecution + ? moment().subtract(12, 'month') + : moment().subtract(32, 'month') const startDailyDateString = startDailyDate.format('YYYY-MM-DD') const startLoadDate = moment().subtract(7, 'day') const startLoadDateString = startLoadDate.format('YYYY-MM-DD') const endDate = moment() const endDateString = endDate.format('YYYY-MM-DD') const baseUrl = 'https://gw.prd.api.enedis.fr' +const dailyDataURL = `${baseUrl}/v4/metering_data/daily_consumption` +const loadCurveURL = `${baseUrl}/v4/metering_data/consumption_load_curve` /** * The start function is run by the BaseKonnector instance only when it got all the account @@ -32,17 +37,18 @@ const baseUrl = 'https://gw.prd.api.enedis.fr' * cozyParameters are static parameters, independents from the account. Most often, it can be a * secret api key. * @param {Object} fields - * @param {string} fields.access_token - a google access token - * @param {string} fields.refresh_token - a google refresh token + * @param {string} fields.access_token - access token + * @param {string} fields.refresh_token - refresh token * @param {Object} cozyParameters - cozy parameters * @param {boolean} doRetry - whether we should use the refresh token or not */ async function start(fields, cozyParameters, doRetry = true) { log('info', 'Starting the enedis konnector') + log('info', `Manual execution: ${manualExecution}`) const accountId = getAccountId() + let usage_point_id = '' try { const { access_token } = fields - let usage_point_id = '' if ( this._account && this._account.oauth_callback_results && @@ -52,6 +58,9 @@ async function start(fields, cozyParameters, doRetry = true) { ',' ) usage_point_id = usage_points_id[0] + } else if (fields.usage_point_id) { + // In case of refresh token, we retrieve the usage point id from the fields + usage_point_id = fields.usage_point_id } else { log('error', 'no usage_point_id found') throw errors.USER_ACTION_NEEDED_OAUTH_OUTDATED @@ -67,7 +76,6 @@ async function start(fields, cozyParameters, doRetry = true) { ) log('info', 'Agregate enedis daily data for month and year') await agregateMonthAndYearData(processedDailyData) - log('info', 'Process enedis load data') await startLoadDataProcess(access_token, usage_point_id) } catch (err) { @@ -87,10 +95,10 @@ async function start(fields, cozyParameters, doRetry = true) { log('info', `Error during refresh ${err.message}`) throw errors.USER_ACTION_NEEDED_OAUTH_OUTDATED } - log('info', 'refresh response') log('info', JSON.stringify(body)) fields.access_token = body.attributes.oauth.access_token + fields.usage_point_id = usage_point_id return start(fields, cozyParameters, false) } log('error', `Error during authentication: ${err.message}`) @@ -98,7 +106,7 @@ async function start(fields, cozyParameters, doRetry = true) { } else { log('error', 'caught an unexpected error') log('error', err.message) - throw errors.VENDOR_DOWN + throw err } } } @@ -111,8 +119,8 @@ async function getDailyData(token, usagePointID) { const dataRequest = { method: 'GET', uri: - baseUrl + - '/v4/metering_data/daily_consumption?start=' + + dailyDataURL + + '?start=' + startDailyDateString + '&end=' + endDateString + @@ -120,8 +128,8 @@ async function getDailyData(token, usagePointID) { usagePointID, headers: { Accept: 'application/json', - Authorization: 'Bearer ' + token - } + Authorization: 'Bearer ' + token, + }, } const response = await rp(dataRequest) return response @@ -133,33 +141,88 @@ async function getDailyData(token, usagePointID) { * If yes only call once the api */ async function startLoadDataProcess(token, usagePointID) { - log('info', 'Check history') - const isHistory = await isHistoryLoaded('com.grandlyon.enedis.minute') - if (isHistory) { - log('info', 'launch process without history') - await launchLoadDataProcess( - token, - usagePointID, - startLoadDateString, - endDateString - ) - } else { - log('info', 'launch process with history') - for (var i = 0; i < 4; i++) { - const increamentedStartDate = moment(startLoadDate) - const incrementedEndDate = moment(endDate) - const increamentedStartDateString = increamentedStartDate - .subtract(7 * i, 'day') - .format('YYYY-MM-DD') - const incrementedEndDateString = incrementedEndDate - .subtract(7 * i, 'day') - .format('YYYY-MM-DD') + log('info', 'Check consent for user') + const isConsent = await checkConsentForLoadCurve( + token, + usagePointID, + startLoadDateString, + endDateString + ) + if (isConsent) { + log('info', 'Check history') + const isHistory = await isHistoryLoaded('com.grandlyon.enedis.minute') + log('info', `isHistory: ${isHistory}`) + if (isHistory || manualExecution) { + log('info', 'launch process without history') await launchLoadDataProcess( token, usagePointID, - increamentedStartDateString, - incrementedEndDateString + startLoadDateString, + endDateString ) + } else { + log('info', 'launch process with history') + for (var i = 0; i < 4; i++) { + const increamentedStartDate = moment(startLoadDate) + const incrementedEndDate = moment(endDate) + const increamentedStartDateString = increamentedStartDate + .subtract(7 * i, 'day') + .format('YYYY-MM-DD') + const incrementedEndDateString = incrementedEndDate + .subtract(7 * i, 'day') + .format('YYYY-MM-DD') + await launchLoadDataProcess( + token, + usagePointID, + increamentedStartDateString, + incrementedEndDateString + ) + } + } + } +} + +/** + * Request API and check return code + * Return true or false + */ +async function checkConsentForLoadCurve( + token, + usagePointID, + _startDate, + _endDate +) { + const dataRequest = { + method: 'GET', + uri: + loadCurveURL + + '?start=' + + _startDate + + '&end=' + + _endDate + + '&usage_point_id=' + + usagePointID, + headers: { + Accept: 'application/json', + Authorization: 'Bearer ' + token, + }, + } + try { + await rp(dataRequest) + log('info', 'Consent found for load curve') + return true + } catch (err) { + if ( + (err.statusCode === 400 || err.code === 400) && + err.message.search('ADAM-ERR0075') > 0 + ) { + log('info', 'No consent for load curve') + return false + } else if (err.statusCode === 403 || err.code === 403) { + log('info', 'No consent for load curve') + return false + } else { + throw err } } } @@ -202,8 +265,8 @@ async function getLoadData(token, usagePointID, _startDate, _endDate) { const dataRequest = { method: 'GET', uri: - baseUrl + - '/v4/metering_data/consumption_load_curve?start=' + + loadCurveURL + + '?start=' + _startDate + '&end=' + _endDate + @@ -211,22 +274,11 @@ async function getLoadData(token, usagePointID, _startDate, _endDate) { usagePointID, headers: { Accept: 'application/json', - Authorization: 'Bearer ' + token - } - } - try { - const response = await rp(dataRequest) - return response - } catch (err) { - if (err.statusCode === 404 || err.code === 404) { - log('warning', 'caught an 404 error') - log('warning', err.message) - log('warning', err) - return null - } else { - throw err - } + Authorization: 'Bearer ' + token, + }, } + const response = await rp(dataRequest) + return response } /** @@ -241,7 +293,7 @@ async function processData(data, doctype, filterKeys) { const formatedData = await formateData(intervalData, doctype) // Remove data for existing days into the DB const filteredData = await hydrateAndFilter(formatedData, doctype, { - keys: filterKeys + keys: filterKeys, }) // Store new day data await storeData(filteredData, doctype, filterKeys) @@ -271,7 +323,7 @@ async function agregateMonthAndYearData(data) { ) await storeData(agregatedMonthData, 'com.grandlyon.enedis.month', [ 'year', - 'month' + 'month', ]) // Agregation for Year data const agregatedYearData = await buildAgregatedData( @@ -311,7 +363,7 @@ async function agregateHourlyData(data) { 'year', 'month', 'day', - 'hour' + 'hour', ]) } } @@ -322,7 +374,7 @@ async function agregateHourlyData(data) { async function storeData(data, doctype, filterKeys) { log('debug', doctype, 'Store into') const filteredDocuments = await hydrateAndFilter(data, doctype, { - keys: filterKeys + keys: filterKeys, }) return await addData(filteredDocuments, doctype) } @@ -349,7 +401,7 @@ async function formateData(data, doctype) { month: parseInt(date.format('M')), day: parseInt(date.format('D')), hour: parseInt(date.format('H')), - minute: parseInt(date.format('m')) + minute: parseInt(date.format('m')), } } }) @@ -402,7 +454,7 @@ async function buildDataFromKey(doctype, key, value) { month: parseInt(month), day: parseInt(day), hour: parseInt(hour), - minute: 0 + minute: 0, } } diff --git a/webpack.config.js b/webpack.config.js index 23e5ee8c50f117e18d37be4622cace9b896c2f4b..64fb892d912cb55dbf5bd9fd023a418cdce9533c 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -12,9 +12,9 @@ const readManifest = () => const svgo = new SvgoInstance({ plugins: [ { - inlineStyles: { onlyMatchedOnce: false } - } - ] + inlineStyles: { onlyMatchedOnce: false }, + }, + ], }) let iconName @@ -33,7 +33,7 @@ module.exports = { mode: 'none', output: { path: path.join(__dirname, 'build'), - filename: 'index.js' + filename: 'index.js', }, plugins: [ new CopyPlugin([ @@ -42,19 +42,19 @@ module.exports = { { from: 'README.md' }, { from: 'assets', transform: optimizeSVGIcon }, { from: '.travis.yml' }, - { from: 'LICENSE' } + { from: 'LICENSE' }, ]), new webpack.DefinePlugin({ - __WEBPACK_PROVIDED_MANIFEST__: JSON.stringify(readManifest()) - }) + __WEBPACK_PROVIDED_MANIFEST__: JSON.stringify(readManifest()), + }), ], module: { // to ignore the warnings like : // WARNING in ../libs/node_modules/bindings/bindings.js 76:22-40 // Critical dependency: the request of a dependency is an expression // Since we cannot change this dependency. I think it won't hide more important messages - exprContextCritical: false - } + exprContextCritical: false, + }, } function optimizeSVGIcon(buffer, path) {