@clevercloud/client
Advanced tools
Comparing version 3.1.2 to 4.0.0
# Clever Client changelog | ||
## 4.0.0 (2020-03-19) | ||
### ⚠️ BREAKING CHANGES | ||
Seems like a milliseconds API for access logs on Warp10 was not a good idea. | ||
We changed it to be in microseconds and update the date util. | ||
* Make inner `getAccessLogsFromWarp10()` accept `from` and `to` in microseconds. | ||
* Make `getAccessLogsFromWarp10InBatches()` accept `from` and `to` in microseconds. | ||
* Rename date util `toISOStringWithMicrosecondPrecision()` into `toMicroIsoString()`. | ||
* Remove date util `asWarp10Timespan()`. | ||
* Remove date util `ONE_HOUR`. | ||
* Introduce date util `toMicroTimestamp()`. | ||
* Introduce date util `ONE_HOUR_MICROS`. | ||
* Introduce date util `ONE_SECOND_MICROS`. | ||
## 3.1.2 (2020-03-18) | ||
@@ -4,0 +20,0 @@ |
@@ -15,10 +15,14 @@ "use strict"; | ||
// "from" and "to" are timestamps in microseconds | ||
function getAccessLogsFromWarp10({ | ||
appId, | ||
realAddonId, | ||
timespan, | ||
from, | ||
to, | ||
warpToken | ||
}) { | ||
const [granularity, id] = realAddonId != null ? ['addon_id', realAddonId] : ['app_id', appId]; | ||
const body = `[ '${warpToken}' 'accessLogs' { '${granularity}' '${id}' } ${timespan} ] FETCH MERGE SORT VALUES`; | ||
const fromDate = (0, _date.toMicroIsoString)(from); | ||
const toDate = (0, _date.toMicroIsoString)(to); | ||
const body = `[ '${warpToken}' 'accessLogs' { '${granularity}' '${id}' } '${fromDate}' '${toDate}' ] FETCH MERGE SORT VALUES`; | ||
@@ -31,4 +35,5 @@ const responseHandler = data => data.flat(2).map(x => JSON.parse(x)); | ||
}); | ||
} | ||
} // "from" and "to" are timestamps in microseconds | ||
function getAccessLogsFromWarp10InBatches({ | ||
@@ -44,10 +49,10 @@ appId, | ||
function doCall(batchFrom) { | ||
const batchFromPlusOneHour = batchFrom + _date.ONE_HOUR; // Make sure we don't fetch after "to" | ||
const batchFromPlusOneHour = batchFrom + _date.ONE_HOUR_MICROS; // Make sure we don't fetch after "to" | ||
const [batchTo, shouldContinue] = batchFromPlusOneHour < to ? [batchFromPlusOneHour, true] : [to, false]; | ||
const timespan = (0, _date.asWarp10Timespan)(batchFrom, batchTo); | ||
getAccessLogsFromWarp10({ | ||
appId, | ||
realAddonId, | ||
timespan, | ||
from: batchFrom, | ||
to: batchTo, | ||
warpToken | ||
@@ -71,4 +76,3 @@ }).then(sendToWarp10).then(data => { | ||
// 3. Data arrives | ||
// 4. Schedule next loop in 1000ms later with fetch from="last" to="now" | ||
// The from/to timeframe will be around (fetch roundtrip) + 1000ms | ||
// 4. Schedule next loop 1 second later with fetch from="last log ts or first ts" to="now" | ||
@@ -81,15 +85,21 @@ | ||
}, sendToWarp10) { | ||
const emitter = new _componentEmitter.default(); | ||
const emitter = new _componentEmitter.default(); // Store last log between loops | ||
let lastLog; // "from" and "to" are timestamps in microseconds | ||
function doCall(from, to) { | ||
const timespan = (0, _date.asWarp10Timespan)(from, to); | ||
getAccessLogsFromWarp10({ | ||
appId, | ||
realAddonId, | ||
timespan, | ||
from, | ||
to, | ||
warpToken | ||
}).then(sendToWarp10).then(data => { | ||
emitter.emit('data', data); // Prevent huge recursive call stack | ||
emitter.emit('data', data); | ||
lastLog = data.slice(-1)[0] || lastLog; // From "last log TS + 1µs" to NOW | ||
setTimeout(() => doCall(to, Date.now()), 1000); | ||
const batchFrom = lastLog != null ? (0, _date.toMicroTimestamp)(lastLog.t) + 1 : from; | ||
const batchTo = (0, _date.toMicroTimestamp)(); // Prevent huge recursive call stack | ||
setTimeout(() => doCall(batchFrom, batchTo), 1000); | ||
}).catch(e => emitter.emit('error', e)); | ||
@@ -99,5 +109,5 @@ } // Trigger batch "loop" mechanism | ||
const now = Date.now(); | ||
doCall(now - 10 * 1000, now); | ||
const now = (0, _date.toMicroTimestamp)(); | ||
doCall(now - 10 * _date.ONE_SECOND_MICROS, now); | ||
return emitter; | ||
} |
@@ -6,25 +6,26 @@ "use strict"; | ||
}); | ||
exports.toTimestamp = toTimestamp; | ||
exports.asWarp10Timespan = asWarp10Timespan; | ||
exports.ONE_HOUR = void 0; | ||
const ONE_HOUR = 60 * 60 * 1000; | ||
exports.ONE_HOUR = ONE_HOUR; | ||
exports.toMicroIsoString = toMicroIsoString; | ||
exports.toMicroTimestamp = toMicroTimestamp; | ||
exports.ONE_SECOND_MICROS = exports.ONE_HOUR_MICROS = void 0; | ||
const ONE_HOUR_MICROS = 60 * 60 * 1000 * 1000; | ||
exports.ONE_HOUR_MICROS = ONE_HOUR_MICROS; | ||
const ONE_SECOND_MICROS = 1000 * 1000; | ||
exports.ONE_SECOND_MICROS = ONE_SECOND_MICROS; | ||
function toTimestamp(date) { | ||
return new Date(date).getTime(); | ||
function toMicroIsoString(microTimestamp) { | ||
const milliTimestamp = Math.floor(microTimestamp / 1000); | ||
const milliIsoString = new Date(milliTimestamp).toISOString(); | ||
const microSuffix = String(microTimestamp % 1000).padStart(3, '0'); | ||
return milliIsoString.replace(/Z$/, `${microSuffix}Z`); | ||
} | ||
function toISOStringWithMicrosecondPrecision(microseconds) { | ||
const milliseconds = Math.floor(microseconds / 1000); | ||
const isoStringMilli = new Date(milliseconds).toISOString(); | ||
const microPrecisionString = String(microseconds % 1000).padStart(3, '0'); | ||
return isoStringMilli.replace(/Z$/, `${microPrecisionString}Z`); | ||
} // Converts "from" and "to" ms timestamps to ISO 8601 with µs precision | ||
// also removes 1µs to "to" because Warp10 timespan are inclusive | ||
function toMicroTimestamp(isoString) { | ||
if (isoString == null) { | ||
return Date.now() * 1000; | ||
} | ||
function asWarp10Timespan(fromMilli, toMilli) { | ||
const fromMicro = fromMilli * 1000; | ||
const toMicro = toMilli * 1000 - 1; | ||
return `'${toISOStringWithMicrosecondPrecision(fromMicro)}' '${toISOStringWithMicrosecondPrecision(toMicro)}'`; | ||
const milliTimestamp = new Date(isoString).getTime(); | ||
const [, microSuffix = '000'] = isoString.match(/\.\d{3}(\d{3})?Z$/); | ||
const microseconds = Number(microSuffix); | ||
return milliTimestamp * 1000 + microseconds; | ||
} |
import Emitter from 'component-emitter'; | ||
import { asWarp10Timespan, ONE_HOUR } from './utils/date.js'; | ||
import { ONE_HOUR_MICROS, ONE_SECOND_MICROS, toMicroIsoString, toMicroTimestamp } from './utils/date.js'; | ||
function getAccessLogsFromWarp10 ({ appId, realAddonId, timespan, warpToken }) { | ||
// "from" and "to" are timestamps in microseconds | ||
function getAccessLogsFromWarp10 ({ appId, realAddonId, from, to, warpToken }) { | ||
@@ -10,3 +11,6 @@ const [granularity, id] = (realAddonId != null) | ||
const body = `[ '${warpToken}' 'accessLogs' { '${granularity}' '${id}' } ${timespan} ] FETCH MERGE SORT VALUES`; | ||
const fromDate = toMicroIsoString(from); | ||
const toDate = toMicroIsoString(to); | ||
const body = `[ '${warpToken}' 'accessLogs' { '${granularity}' '${id}' } '${fromDate}' '${toDate}' ] FETCH MERGE SORT VALUES`; | ||
const responseHandler = (data) => data.flat(2).map((x) => JSON.parse(x)); | ||
@@ -17,2 +21,3 @@ | ||
// "from" and "to" are timestamps in microseconds | ||
export function getAccessLogsFromWarp10InBatches ({ appId, realAddonId, from, to, warpToken }, sendToWarp10) { | ||
@@ -24,3 +29,3 @@ | ||
const batchFromPlusOneHour = batchFrom + ONE_HOUR; | ||
const batchFromPlusOneHour = batchFrom + ONE_HOUR_MICROS; | ||
// Make sure we don't fetch after "to" | ||
@@ -30,5 +35,4 @@ const [batchTo, shouldContinue] = (batchFromPlusOneHour < to) | ||
: [to, false]; | ||
const timespan = asWarp10Timespan(batchFrom, batchTo); | ||
getAccessLogsFromWarp10({ appId, realAddonId, timespan, warpToken }) | ||
getAccessLogsFromWarp10({ appId, realAddonId, from: batchFrom, to: batchTo, warpToken }) | ||
.then(sendToWarp10) | ||
@@ -55,4 +59,3 @@ .then((data) => { | ||
// 3. Data arrives | ||
// 4. Schedule next loop in 1000ms later with fetch from="last" to="now" | ||
// The from/to timeframe will be around (fetch roundtrip) + 1000ms | ||
// 4. Schedule next loop 1 second later with fetch from="last log ts or first ts" to="now" | ||
export function getContinuousAccessLogsFromWarp10 ({ appId, realAddonId, warpToken }, sendToWarp10) { | ||
@@ -62,11 +65,21 @@ | ||
// Store last log between loops | ||
let lastLog; | ||
// "from" and "to" are timestamps in microseconds | ||
function doCall (from, to) { | ||
const timespan = asWarp10Timespan(from, to); | ||
getAccessLogsFromWarp10({ appId, realAddonId, timespan, warpToken }) | ||
getAccessLogsFromWarp10({ appId, realAddonId, from, to, warpToken }) | ||
.then(sendToWarp10) | ||
.then((data) => { | ||
emitter.emit('data', data); | ||
lastLog = data.slice(-1)[0] || lastLog; | ||
// From "last log TS + 1µs" to NOW | ||
const batchFrom = (lastLog != null) ? toMicroTimestamp(lastLog.t) + 1 : from; | ||
const batchTo = toMicroTimestamp(); | ||
// Prevent huge recursive call stack | ||
setTimeout(() => doCall(to, Date.now()), 1000); | ||
setTimeout(() => doCall(batchFrom, batchTo), 1000); | ||
}) | ||
@@ -77,6 +90,6 @@ .catch((e) => emitter.emit('error', e)); | ||
// Trigger batch "loop" mechanism | ||
const now = Date.now(); | ||
doCall(now - 10 * 1000, now); | ||
const now = toMicroTimestamp(); | ||
doCall(now - 10 * ONE_SECOND_MICROS, now); | ||
return emitter; | ||
} |
@@ -1,20 +0,19 @@ | ||
export const ONE_HOUR = 60 * 60 * 1000; | ||
export const ONE_HOUR_MICROS = 60 * 60 * 1000 * 1000; | ||
export const ONE_SECOND_MICROS = 1000 * 1000; | ||
export function toTimestamp (date) { | ||
return (new Date(date)).getTime(); | ||
export function toMicroIsoString (microTimestamp) { | ||
const milliTimestamp = Math.floor(microTimestamp / 1000); | ||
const milliIsoString = new Date(milliTimestamp).toISOString(); | ||
const microSuffix = String(microTimestamp % 1000).padStart(3, '0'); | ||
return milliIsoString.replace(/Z$/, `${microSuffix}Z`); | ||
} | ||
function toISOStringWithMicrosecondPrecision (microseconds) { | ||
const milliseconds = Math.floor(microseconds / 1000); | ||
const isoStringMilli = new Date(milliseconds).toISOString(); | ||
const microPrecisionString = String(microseconds % 1000).padStart(3, '0'); | ||
return isoStringMilli.replace(/Z$/, `${microPrecisionString}Z`); | ||
export function toMicroTimestamp (isoString) { | ||
if (isoString == null) { | ||
return Date.now() * 1000; | ||
} | ||
const milliTimestamp = (new Date(isoString)).getTime(); | ||
const [, microSuffix = '000'] = isoString.match(/\.\d{3}(\d{3})?Z$/); | ||
const microseconds = Number(microSuffix); | ||
return milliTimestamp * 1000 + microseconds; | ||
} | ||
// Converts "from" and "to" ms timestamps to ISO 8601 with µs precision | ||
// also removes 1µs to "to" because Warp10 timespan are inclusive | ||
export function asWarp10Timespan (fromMilli, toMilli) { | ||
const fromMicro = fromMilli * 1000; | ||
const toMicro = (toMilli * 1000) - 1; | ||
return `'${toISOStringWithMicrosecondPrecision(fromMicro)}' '${toISOStringWithMicrosecondPrecision(toMicro)}'`; | ||
} |
{ | ||
"name": "@clevercloud/client", | ||
"version": "3.1.2", | ||
"version": "4.0.0", | ||
"description": "JavaScript REST client and utils for Clever Cloud's API", | ||
@@ -5,0 +5,0 @@ "homepage": "https://github.com/CleverCloud/clever-client.js", |
398676
12511