-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c7cfff0
commit a8ea8d2
Showing
6 changed files
with
314 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -88,6 +88,7 @@ | |
{ | ||
"allowSingleLine": false | ||
} | ||
] | ||
], | ||
"newline-per-chained-call": "off" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
module.exports = function EcowattController(logger, tempoModel) { | ||
/** | ||
* @api {get} /edf/tempo/today Get tempo data today | ||
* @apiName Get tempo data | ||
* @apiGroup Tempo | ||
*/ | ||
async function getTempoToday(req, res) { | ||
logger.info(`Tempo.getDataToday`); | ||
const response = await tempoModel.getDataWithRetry(); | ||
const cachePeriodInSecond = 60 * 60; | ||
res.set('Cache-control', `public, max-age=${cachePeriodInSecond}`); | ||
res.json(response); | ||
} | ||
|
||
return { | ||
getTempoToday, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
const axios = require('axios'); | ||
const dayjs = require('dayjs'); | ||
const retry = require('async-retry'); | ||
|
||
const utc = require('dayjs/plugin/utc'); | ||
const timezone = require('dayjs/plugin/timezone'); | ||
const customParseFormat = require('dayjs/plugin/customParseFormat'); | ||
|
||
dayjs.extend(utc); | ||
dayjs.extend(timezone); | ||
dayjs.extend(customParseFormat); | ||
|
||
const TEMPO_CACHE_KEY = 'tempo'; | ||
const TEMPO_REDIS_EXPIRY_IN_SECONDS = 5 * 24 * 60 * 60; // 5 days | ||
|
||
module.exports = function TempoModel(logger, redisClient) { | ||
// the key is the same as ecowatt | ||
const { ECOWATT_BASIC_HTTP } = process.env; | ||
|
||
async function getDataFromCache(date) { | ||
return redisClient.get(`${TEMPO_CACHE_KEY}:${date}`); | ||
} | ||
|
||
async function getDataLiveOrFromCache() { | ||
const todayStartDate = dayjs().tz('Europe/Paris').startOf('day').format('YYYY-MM-DDTHH:mm:ssZ'); | ||
const tomorrowStartDate = dayjs().tz('Europe/Paris').add(1, 'day').startOf('day').format('YYYY-MM-DDTHH:mm:ssZ'); | ||
const tomorrowEndDate = dayjs().tz('Europe/Paris').add(2, 'day').startOf('day').format('YYYY-MM-DDTHH:mm:ssZ'); | ||
|
||
// Get today data from cache | ||
let todayData = await getDataFromCache(todayStartDate); | ||
let tomorrowData = await getDataFromCache(tomorrowStartDate); | ||
|
||
let accessToken; | ||
|
||
if (!todayData || !tomorrowData) { | ||
// Get new access token | ||
const { data: dataToken } = await axios.post('https://digital.iservices.rte-france.com/token/oauth/', null, { | ||
headers: { | ||
authorization: `Basic ${ECOWATT_BASIC_HTTP}`, | ||
}, | ||
}); | ||
accessToken = dataToken.access_token; | ||
} | ||
|
||
if (!todayData) { | ||
try { | ||
const { data: todayLiveData } = await axios.get( | ||
'https://digital.iservices.rte-france.com/open_api/tempo_like_supply_contract/v1/tempo_like_calendars', | ||
{ | ||
params: { | ||
start_date: todayStartDate, | ||
end_date: tomorrowStartDate, | ||
}, | ||
headers: { | ||
authorization: `Bearer ${accessToken}`, | ||
}, | ||
}, | ||
); | ||
todayData = todayLiveData.tempo_like_calendars.values[0].value.toLowerCase(); | ||
// Set cache | ||
await redisClient.set(`${TEMPO_CACHE_KEY}:${todayStartDate}`, todayData, { | ||
EX: TEMPO_REDIS_EXPIRY_IN_SECONDS, | ||
}); | ||
} catch (e) { | ||
logger.debug(e); | ||
todayData = 'unknown'; | ||
} | ||
} | ||
|
||
if (!tomorrowData) { | ||
try { | ||
const { data: tomorrowLiveData } = await axios.get( | ||
'https://digital.iservices.rte-france.com/open_api/tempo_like_supply_contract/v1/tempo_like_calendars', | ||
{ | ||
params: { | ||
start_date: tomorrowStartDate, | ||
end_date: tomorrowEndDate, | ||
}, | ||
headers: { | ||
authorization: `Bearer ${accessToken}`, | ||
}, | ||
}, | ||
); | ||
tomorrowData = tomorrowLiveData.tempo_like_calendars.values[0].value.toLowerCase(); | ||
// Set cache | ||
await redisClient.set(`${TEMPO_CACHE_KEY}:${tomorrowStartDate}`, tomorrowData, { | ||
EX: TEMPO_REDIS_EXPIRY_IN_SECONDS, | ||
}); | ||
} catch (e) { | ||
logger.debug(e); | ||
tomorrowData = 'unknown'; | ||
// Set cache for 30 minutes to avoid querying to much the API | ||
await redisClient.set(`${TEMPO_CACHE_KEY}:${tomorrowStartDate}`, tomorrowData, { | ||
EX: 30 * 60, // null set to 30 minutes | ||
}); | ||
} | ||
} | ||
|
||
return { | ||
today: todayData, | ||
tomorrow: tomorrowData, | ||
}; | ||
} | ||
|
||
async function getDataWithRetry() { | ||
const options = { | ||
retries: 3, | ||
factor: 2, | ||
minTimeout: 50, | ||
}; | ||
return retry(async () => getDataLiveOrFromCache(), options); | ||
} | ||
|
||
return { | ||
getDataLiveOrFromCache, | ||
getDataWithRetry, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
const request = require('supertest'); | ||
const { expect } = require('chai'); | ||
const nock = require('nock'); | ||
|
||
describe('GET /edf/tempo/today', () => { | ||
it('should return tempo data', async () => { | ||
nock('https://digital.iservices.rte-france.com') | ||
.post('/token/oauth/', () => true) | ||
.reply(200, { | ||
access_token: 'access_token', | ||
expires_in: 100, | ||
}); | ||
nock('https://digital.iservices.rte-france.com') | ||
.get('/open_api/tempo_like_supply_contract/v1/tempo_like_calendars') | ||
.query(() => true) | ||
.reply(200, { | ||
tempo_like_calendars: { | ||
start_date: '2024-09-02T00:00:00+02:00', | ||
end_date: '2024-09-03T00:00:00+02:00', | ||
values: [ | ||
{ | ||
start_date: '2024-09-02T00:00:00+02:00', | ||
end_date: '2024-09-03T00:00:00+02:00', | ||
value: 'BLUE', | ||
updated_date: '2024-09-01T10:20:00+02:00', | ||
}, | ||
], | ||
}, | ||
}); | ||
nock('https://digital.iservices.rte-france.com') | ||
.get('/open_api/tempo_like_supply_contract/v1/tempo_like_calendars') | ||
.query(() => true) | ||
.reply(400, { | ||
error: 'TMPLIKSUPCON_TMPLIKCAL_F04', | ||
error_description: | ||
'The value of "end_date" field is incorrect. It is not possible to recover data to this term.', | ||
error_uri: '', | ||
error_details: { | ||
transaction_id: 'Id-2fc9d566cff9ded9d39d0ee7', | ||
}, | ||
}); | ||
const response = await request(TEST_BACKEND_APP) | ||
.get('/edf/tempo/today') | ||
.set('Accept', 'application/json') | ||
.expect('Content-Type', /json/) | ||
.expect(200); | ||
expect(response.headers).to.have.property('cache-control', 'public, max-age=3600'); | ||
expect(response.body).to.deep.equal({ | ||
today: 'blue', | ||
tomorrow: 'unknown', | ||
}); | ||
// From cache | ||
const responseFromCache = await request(TEST_BACKEND_APP) | ||
.get('/edf/tempo/today') | ||
.set('Accept', 'application/json') | ||
.expect('Content-Type', /json/) | ||
.expect(200); | ||
expect(responseFromCache.headers).to.have.property('cache-control', 'public, max-age=3600'); | ||
expect(responseFromCache.body).to.deep.equal({ | ||
today: 'blue', | ||
tomorrow: 'unknown', | ||
}); | ||
}); | ||
it('should return tempo data with 2 unknown', async () => { | ||
nock('https://digital.iservices.rte-france.com') | ||
.post('/token/oauth/', () => true) | ||
.reply(200, { | ||
access_token: 'access_token', | ||
expires_in: 100, | ||
}); | ||
nock('https://digital.iservices.rte-france.com') | ||
.get('/open_api/tempo_like_supply_contract/v1/tempo_like_calendars') | ||
.query(() => true) | ||
.reply(400, { | ||
error: 'TMPLIKSUPCON_TMPLIKCAL_F04', | ||
error_description: | ||
'The value of "end_date" field is incorrect. It is not possible to recover data to this term.', | ||
error_uri: '', | ||
error_details: { | ||
transaction_id: 'Id-2fc9d566cff9ded9d39d0ee7', | ||
}, | ||
}); | ||
nock('https://digital.iservices.rte-france.com') | ||
.get('/open_api/tempo_like_supply_contract/v1/tempo_like_calendars') | ||
.query(() => true) | ||
.reply(400, { | ||
error: 'TMPLIKSUPCON_TMPLIKCAL_F04', | ||
error_description: | ||
'The value of "end_date" field is incorrect. It is not possible to recover data to this term.', | ||
error_uri: '', | ||
error_details: { | ||
transaction_id: 'Id-2fc9d566cff9ded9d39d0ee7', | ||
}, | ||
}); | ||
const response = await request(TEST_BACKEND_APP) | ||
.get('/edf/tempo/today') | ||
.set('Accept', 'application/json') | ||
.expect('Content-Type', /json/) | ||
.expect(200); | ||
expect(response.headers).to.have.property('cache-control', 'public, max-age=3600'); | ||
expect(response.body).to.deep.equal({ | ||
today: 'unknown', | ||
tomorrow: 'unknown', | ||
}); | ||
}); | ||
it('should return tempo data with 2 blue', async () => { | ||
nock('https://digital.iservices.rte-france.com') | ||
.post('/token/oauth/', () => true) | ||
.reply(200, { | ||
access_token: 'access_token', | ||
expires_in: 100, | ||
}); | ||
nock('https://digital.iservices.rte-france.com') | ||
.get('/open_api/tempo_like_supply_contract/v1/tempo_like_calendars') | ||
.query(() => true) | ||
.reply(200, { | ||
tempo_like_calendars: { | ||
start_date: '2024-09-02T00:00:00+02:00', | ||
end_date: '2024-09-03T00:00:00+02:00', | ||
values: [ | ||
{ | ||
start_date: '2024-09-02T00:00:00+02:00', | ||
end_date: '2024-09-03T00:00:00+02:00', | ||
value: 'BLUE', | ||
updated_date: '2024-09-01T10:20:00+02:00', | ||
}, | ||
], | ||
}, | ||
}); | ||
nock('https://digital.iservices.rte-france.com') | ||
.get('/open_api/tempo_like_supply_contract/v1/tempo_like_calendars') | ||
.query(() => true) | ||
.reply(200, { | ||
tempo_like_calendars: { | ||
start_date: '2024-09-02T00:00:00+02:00', | ||
end_date: '2024-09-03T00:00:00+02:00', | ||
values: [ | ||
{ | ||
start_date: '2024-09-02T00:00:00+02:00', | ||
end_date: '2024-09-03T00:00:00+02:00', | ||
value: 'BLUE', | ||
updated_date: '2024-09-01T10:20:00+02:00', | ||
}, | ||
], | ||
}, | ||
}); | ||
const response = await request(TEST_BACKEND_APP) | ||
.get('/edf/tempo/today') | ||
.set('Accept', 'application/json') | ||
.expect('Content-Type', /json/) | ||
.expect(200); | ||
expect(response.headers).to.have.property('cache-control', 'public, max-age=3600'); | ||
expect(response.body).to.deep.equal({ | ||
today: 'blue', | ||
tomorrow: 'blue', | ||
}); | ||
// From cache | ||
const responseFromCache = await request(TEST_BACKEND_APP) | ||
.get('/edf/tempo/today') | ||
.set('Accept', 'application/json') | ||
.expect('Content-Type', /json/) | ||
.expect(200); | ||
expect(responseFromCache.headers).to.have.property('cache-control', 'public, max-age=3600'); | ||
expect(responseFromCache.body).to.deep.equal({ | ||
today: 'blue', | ||
tomorrow: 'blue', | ||
}); | ||
}); | ||
}); |