Skip to content

Commit 51f2e07

Browse files
build: Fix release and improve TTL message (#267)
* fix: retention date grace on import * feat: More granular TTL for shortly expiring datasets * build: use GITHUB_TOKEN for checkout 4adb429
1 parent 15ec904 commit 51f2e07

7 files changed

Lines changed: 80 additions & 15795 deletions

File tree

.releaserc

Lines changed: 0 additions & 1 deletion
This file was deleted.

package-lock.json

Lines changed: 1 addition & 15769 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"name": "dune-buggy",
33
"version": "1.23.2",
4+
"private": true,
45
"description": "",
56
"main": "index.js",
67
"repository": {
@@ -99,4 +100,4 @@
99100
"last 1 safari version"
100101
]
101102
}
102-
}
103+
}

src/App.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
import _ from "lodash";
2121
import { getQueryStringValue, setQueryStringValue } from "./queryString";
2222
import "./global.css";
23-
import { log } from "./Utils";
23+
import Utils, { log } from "./Utils";
2424
import { toast, ToastContainer } from "react-toastify";
2525
import "react-toastify/dist/ReactToastify.css";
2626
import { ALL_TOGGLES, getVisibleToggles } from "./MapToggles";
@@ -427,7 +427,7 @@ class App extends React.Component {
427427
const timeLeftMs = retentionDate - now;
428428
const daysLeft = timeLeftMs / (1000 * 60 * 60 * 24);
429429
if (daysLeft <= 10) {
430-
log(`Dataset ${index + 1} will expire in ${Math.ceil(daysLeft)} days.`, "warn");
430+
log(`Dataset ${index + 1} will expire in ${Utils.formatTTLRemaining(timeLeftMs)}.`, "warn");
431431
}
432432
}
433433
}
@@ -829,7 +829,7 @@ class App extends React.Component {
829829
log(`Dataset ${index + 1} has expired and was deleted (Retention limit reached).`, "error");
830830
return "Expired";
831831
} else if (daysLeft <= 10) {
832-
log(`Dataset ${index + 1} will expire in ${Math.ceil(daysLeft)} days.`, "warn");
832+
log(`Dataset ${index + 1} will expire in ${Utils.formatTTLRemaining(timeLeftMs)}.`, "warn");
833833
return "Warning";
834834
}
835835
return "Valid";

src/Utils.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,34 @@ class Utils {
2727
}
2828
return timeStr;
2929
}
30+
31+
/**
32+
* Formats a duration for TTL warnings.
33+
* > 1 day: "X days"
34+
* 1h - 24h: "X hours"
35+
* 1m - 60m: "X minutes"
36+
* < 1m: "X seconds"
37+
*/
38+
static formatTTLRemaining(ms) {
39+
const days = ms / (1000 * 60 * 60 * 24);
40+
const hours = ms / (1000 * 60 * 60);
41+
const minutes = ms / (1000 * 60);
42+
const seconds = ms / 1000;
43+
44+
if (days >= 1) {
45+
const d = Math.ceil(days);
46+
return `${d} day${d > 1 ? "s" : ""}`;
47+
} else if (hours >= 1) {
48+
const h = Math.ceil(hours);
49+
return `${h} hour${h > 1 ? "s" : ""}`;
50+
} else if (minutes >= 1) {
51+
const m = Math.ceil(minutes);
52+
return `${m} minute${m > 1 ? "s" : ""}`;
53+
} else {
54+
const s = Math.ceil(seconds);
55+
return `${s} second${s !== 1 ? "s" : ""}`;
56+
}
57+
}
3058
}
3159

3260
/**

src/localStorage.js

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -229,14 +229,40 @@ function isRestrictedLog(row) {
229229
return row.jsonPayload?.["@type"]?.includes("Restricted") || false;
230230
}
231231

232+
function calculateRetentionDate(logsArray) {
233+
let oldestTimestamp = Infinity;
234+
logsArray.forEach((row) => {
235+
const ts = new Date(
236+
row.timestamp || row.insertTimestamp || row.jsonPayload?.timestamp || row.jsonpayload?.timestamp
237+
).getTime();
238+
if (!isNaN(ts) && ts < oldestTimestamp) {
239+
oldestTimestamp = ts;
240+
}
241+
});
242+
243+
let retentionDateIdentifier = null;
244+
if (oldestTimestamp !== Infinity) {
245+
const fiftyFiveDaysMs = 55 * 24 * 60 * 60 * 1000;
246+
const oneHourMs = 60 * 60 * 1000;
247+
const expirationBasedOnLogs = oldestTimestamp + fiftyFiveDaysMs;
248+
const minimumRetention = Date.now() + oneHourMs;
249+
const chosenRetention = new Date(Math.max(expirationBasedOnLogs, minimumRetention));
250+
retentionDateIdentifier = chosenRetention.toISOString();
251+
} else {
252+
retentionDateIdentifier = new Date(Date.now() + 60 * 60 * 1000).toISOString();
253+
}
254+
return retentionDateIdentifier;
255+
}
256+
232257
export function ensureCorrectFormat(data) {
233258
let logsArray;
234259
//Handle if data is not array (like when reading a file).
235260
if (!Array.isArray(data)) {
236-
// If it's already in the correct format, return it as is.
261+
// If it's already in the correct format, return it as is, BUT RE-CALCULATE TTL for grace period.
237262
if (data && data.rawLogs && Array.isArray(data.rawLogs)) {
238263
return {
239264
...data,
265+
retentionDate: calculateRetentionDate(data.rawLogs),
240266
APIKEY: data.APIKEY || DEFAULT_API_KEY,
241267
};
242268
} else {
@@ -337,26 +363,8 @@ export function ensureCorrectFormat(data) {
337363

338364
if (!hasPoints) log("Bounds Calculation Failed: Could not find vehicle location data in any row.");
339365

340-
// Calculate retention date
341-
let oldestTimestamp = Infinity;
342-
logsArray.forEach((row) => {
343-
const ts = new Date(row.timestamp || row.insertTimestamp || row.jsonPayload?.timestamp).getTime();
344-
if (!isNaN(ts) && ts < oldestTimestamp) {
345-
oldestTimestamp = ts;
346-
}
347-
});
348-
349-
let retentionDateIdentifier = null;
350-
if (oldestTimestamp !== Infinity) {
351-
const fiftyFiveDaysMs = 55 * 24 * 60 * 60 * 1000;
352-
const oneHourMs = 60 * 60 * 1000;
353-
const expirationBasedOnLogs = oldestTimestamp + fiftyFiveDaysMs;
354-
const minimumRetention = Date.now() + oneHourMs;
355-
retentionDateIdentifier = new Date(Math.max(expirationBasedOnLogs, minimumRetention)).toISOString();
356-
} else {
357-
// If no valid timestamps found, default to 1 hour from now for safety
358-
retentionDateIdentifier = new Date(Date.now() + 60 * 60 * 1000).toISOString();
359-
}
366+
// Calculate retention date using the helper
367+
const retentionDateIdentifier = calculateRetentionDate(logsArray);
360368

361369
return {
362370
APIKEY: DEFAULT_API_KEY,

src/localStorage.test.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,4 +257,21 @@ describe("ensureCorrectFormat TTL Logic", () => {
257257
// Simple regex to check ISO format YYYY-MM-DDTHH:mm:ss.sssZ
258258
expect(result.retentionDate).toMatch(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/);
259259
});
260+
261+
it("should recalculate retentionDate on import, ignoring stale values in file", () => {
262+
// A file might include a retentionDate.
263+
const staleDate = new Date(Date.now() - 100 * ONE_DAY_MS).toISOString();
264+
const mockExportedFile = {
265+
rawLogs: [{ timestamp: new Date(Date.now() - 200 * ONE_DAY_MS).toISOString(), jsonPayload: { test: 1 } }],
266+
retentionDate: staleDate,
267+
APIKEY: "abc",
268+
};
269+
270+
const result = ensureCorrectFormat(mockExportedFile);
271+
const retention = new Date(result.retentionDate).getTime();
272+
const expectedMin = Date.now() + ONE_HOUR_MS;
273+
274+
expect(result.retentionDate).not.toBe(staleDate);
275+
expect(retention).toBeGreaterThanOrEqual(expectedMin - 1000);
276+
});
260277
});

0 commit comments

Comments
 (0)