renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
143 lines • 4.93 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AbstractGithubGraphqlCacheStrategy = void 0;
const dequal_1 = require("dequal");
const luxon_1 = require("luxon");
const util_1 = require("../util");
/**
* Cache strategy handles the caching Github GraphQL items
* and reconciling them with newly obtained ones from paginated queries.
*/
class AbstractGithubGraphqlCacheStrategy {
cacheNs;
cacheKey;
/**
* Time period after which a cache record is considered expired.
*/
static cacheTTLDays = 30;
/**
* The time which is used during single cache access cycle.
*/
now = luxon_1.DateTime.now().toUTC();
/**
* Set of all versions which were reconciled
* during the current cache access cycle.
*/
reconciledVersions;
/**
* These fields will be persisted.
*/
items;
createdAt = this.now;
/**
* This flag indicates whether there is any new or updated items
*/
hasNovelty = false;
constructor(cacheNs, cacheKey) {
this.cacheNs = cacheNs;
this.cacheKey = cacheKey;
}
/**
* Load data previously persisted by this strategy
* for given `cacheNs` and `cacheKey`.
*/
async getItems() {
if (this.items) {
return this.items;
}
let result = {
items: {},
createdAt: this.createdAt.toISO(),
};
const storedData = await this.load();
if (storedData) {
const cacheTTLDuration = {
hours: AbstractGithubGraphqlCacheStrategy.cacheTTLDays * 24,
};
if (!(0, util_1.isDateExpired)(this.now, storedData.createdAt, cacheTTLDuration)) {
result = storedData;
}
}
this.createdAt = luxon_1.DateTime.fromISO(result.createdAt).toUTC();
this.items = result.items;
return this.items;
}
/**
* If package release exists longer than this cache can exist,
* we assume it won't updated/removed on the Github side.
*/
isStabilized(item) {
const unstableDuration = {
hours: AbstractGithubGraphqlCacheStrategy.cacheTTLDays * 24,
};
return (0, util_1.isDateExpired)(this.now, item.releaseTimestamp, unstableDuration);
}
/**
* Process items received from GraphQL page
* ordered by `releaseTimestamp` in descending order
* (fresh versions go first).
*/
async reconcile(items) {
const cachedItems = await this.getItems();
let isPaginationDone = false;
for (const item of items) {
const { version } = item;
const oldItem = cachedItems[version];
// If we reached previously stored item that is stabilized,
// we assume the further pagination will not yield any new items.
//
// However, we don't break the loop here, allowing to reconcile
// the entire page of items. This protects us from unusual cases
// when release authors intentionally break the timeline. Therefore,
// while it feels appealing to break early, please don't do that.
if (oldItem && this.isStabilized(oldItem)) {
isPaginationDone = true;
}
// Check if item is new or updated
if (!oldItem || !(0, dequal_1.dequal)(oldItem, item)) {
this.hasNovelty = true;
}
cachedItems[version] = item;
this.reconciledVersions ??= new Set();
this.reconciledVersions.add(version);
}
this.items = cachedItems;
return isPaginationDone;
}
/**
* Handle removed items for packages that are not stabilized
* and return the list of all items.
*/
async finalizeAndReturn() {
const cachedItems = await this.getItems();
let resultItems;
let hasDeletedItems = false;
if (this.reconciledVersions) {
resultItems = {};
for (const [version, item] of Object.entries(cachedItems)) {
if (this.reconciledVersions.has(version) || this.isStabilized(item)) {
resultItems[version] = item;
}
else {
hasDeletedItems = true;
}
}
}
else {
resultItems = cachedItems;
}
if (this.hasNovelty || hasDeletedItems) {
await this.store(resultItems);
}
return Object.values(resultItems);
}
async store(cachedItems) {
const cacheRecord = {
items: cachedItems,
createdAt: this.createdAt.toISO(),
};
await this.persist(cacheRecord);
}
}
exports.AbstractGithubGraphqlCacheStrategy = AbstractGithubGraphqlCacheStrategy;
//# sourceMappingURL=abstract-cache-strategy.js.map