diff --git a/src/actions/fetch-entities-actions.js b/src/actions/fetch-entities-actions.js index bb5b2f76..ab492064 100644 --- a/src/actions/fetch-entities-actions.js +++ b/src/actions/fetch-entities-actions.js @@ -3,6 +3,58 @@ import SummitAPIRequest from "../utils/build-json/SummitAPIRequest"; import EventAPIRequest from "../utils/build-json/EventsAPIRequest"; import SpeakersAPIRequest from "../utils/build-json/SpeakersAPIRequest"; +const etagCache = new Map(); +const MAX_CACHE_SIZE = 100; + +export const clearEtagCacheForUrl = (urlPattern) => { + for (const key of etagCache.keys()) { + if (key.includes(urlPattern)) { + etagCache.delete(key); + } + } +}; + +const fetchWithEtag = async (url) => { + const headers = {}; + + if (etagCache.has(url)) { + const { etag } = etagCache.get(url); + if (etag) { + headers['If-None-Match'] = etag; + } + } + + const res = await fetch(url, { + method: 'GET', + cache: "no-store", + headers, + }); + + if (res.status === 304 && etagCache.has(url)) { + const { body } = etagCache.get(url); + return body; + } + + if (res.status === 200) { + const data = await res.json(); + const responseETAG = res.headers.get('etag'); + if (responseETAG) { + if (etagCache.size >= MAX_CACHE_SIZE) { + const oldest = etagCache.keys().next().value; + etagCache.delete(oldest); + } + etagCache.set(url, { etag: responseETAG, body: data }); + } + return data; + } + + if (!res.ok) { + console.error(`fetchWithEtag failed (${res.status}):`, url); + } + + return null; +}; + /** * @param summitId * @param eventId @@ -18,16 +70,7 @@ export const fetchEventById = async (summitId, eventId, accessToken = null) => { } const apiUrlWithParams = EventAPIRequest.build(apiUrl); - - return fetch(apiUrlWithParams, { - method: 'GET', - cache: "no-store", - }).then(async (response) => { - if (response.status === 200) { - return await response.json(); - } - return null; - }); + return fetchWithEtag(apiUrlWithParams); } /** @@ -39,15 +82,8 @@ export const fetchEventById = async (summitId, eventId, accessToken = null) => { export const fetchStreamingInfoByEventId = async (summitId, eventId, accessToken) => { const apiUrl = URI(`${process.env.GATSBY_SUMMIT_API_BASE_URL}/api/v1/summits/${summitId}/events/${eventId}/published/streaming-info`); apiUrl.addQuery('access_token', accessToken); - return fetch(apiUrl.toString(), { - method: 'GET', - cache: "no-store", - }).then(async (response) => { - if (response.status === 200) { - return await response.json(); - } - return null; - }); + const url = apiUrl.toString(); + return fetchWithEtag(url); } /** @@ -64,19 +100,11 @@ export const fetchEventTypeById = async (summitId, eventTypeId, accessToken = nu apiUrl.addQuery('access_token', accessToken); } - return fetch(apiUrl.toString(), { - method: 'GET', - cache: "no-store", - }).then(async (response) => { - if (response.status === 200) { - return await response.json(); - } - return null; - }); + const url = apiUrl.toString(); + return fetchWithEtag(url); } /** - * * @param summitId * @param locationId * @param expand @@ -94,19 +122,11 @@ export const fetchLocationById = async (summitId, locationId, expand, accessToke if (expand) apiUrl.addQuery('expand', expand); - return fetch(apiUrl.toString(), { - method: 'GET', - cache: "no-store", - }).then(async (response) => { - if (response.status === 200) { - return await response.json(); - } - return null; - }); + const url = apiUrl.toString(); + return fetchWithEtag(url); } /** - * * @param summitId * @param speakerId * @param accessToken @@ -122,20 +142,10 @@ export const fetchSpeakerById = async (summitId, speakerId, accessToken = null) } const apiUrlWithParams = SpeakersAPIRequest.build(apiUrl); - - return fetch(apiUrlWithParams, { - method: 'GET', - cache: "no-store", - }).then(async (response) => { - if (response.status === 200) { - return await response.json(); - } - return null; - }); + return fetchWithEtag(apiUrlWithParams); } /** - * * @param summitId * @param accessToken * @returns {Promise} @@ -149,16 +159,7 @@ export const fetchSummitById = async (summitId, accessToken = null) => { } const apiUrlWithParams = SummitAPIRequest.build(apiUrl); - - return fetch(apiUrlWithParams, { - method: 'GET', - cache: "no-store", - }).then(async (response) => { - if (response.status === 200) { - return await response.json(); - } - return null; - }); + return fetchWithEtag(apiUrlWithParams); } /** @@ -171,24 +172,17 @@ export const fetchTrackById = async (summitId, trackId, accessToken = null) => { let apiUrl = URI(`${process.env.GATSBY_SUMMIT_API_BASE_URL}/api/public/v1/summits/${summitId}/tracks/${trackId}`); const fields = [ - "id", "name", "code", "order", "parent_id", "color","text_color", + "id", "name", "code", "order", "parent_id", "color", "text_color", "subtracks.id", "subtracks.name", "subtracks.code", "subtracks.order", "subtracks.parent_id", "subtracks.color", "subtracks.text_color", ]; - const relations = ['subtracks','subtracks.none']; + const relations = ['subtracks', 'subtracks.none']; const expand = ['subtracks'] apiUrl.addQuery('fields', fields.join(',')); apiUrl.addQuery('relations', relations.join(',')); apiUrl.addQuery('expand', expand.join(',')); - return fetch(apiUrl.toString(), { - method: 'GET', - cache: "no-store", - }).then(async (response) => { - if (response.status === 200) { - return await response.json(); - } - return null; - }); -} + const url = apiUrl.toString(); + return fetchWithEtag(url); +} \ No newline at end of file diff --git a/src/workers/sync_strategies/activity_synch_strategy.js b/src/workers/sync_strategies/activity_synch_strategy.js index 2bd649bc..7bd5f5e7 100644 --- a/src/workers/sync_strategies/activity_synch_strategy.js +++ b/src/workers/sync_strategies/activity_synch_strategy.js @@ -1,5 +1,5 @@ import AbstractSynchStrategy from "./abstract_synch_strategy"; -import {fetchEventById, fetchStreamingInfoByEventId} from "../../actions/fetch-entities-actions"; +import {clearEtagCacheForUrl, fetchEventById, fetchStreamingInfoByEventId} from "../../actions/fetch-entities-actions"; import {insertSorted, intCheck, rebuildIndex} from "../../utils/arrayUtils"; import { BUCKET_EVENTS_DATA_KEY, @@ -161,8 +161,10 @@ class ActivitySynchStrategy extends AbstractSynchStrategy{ switch (entity_operator) { case 'INSERT': case 'UPDATE':{ + clearEtagCacheForUrl(`/v1/summits/${this.summit.id}/events/${entity_id}/published`); let entity = await fetchEventById(this.summit.id, entity_id, this.accessToken); if(this.accessToken && this._shouldFetchStreamingInfo(this.currentLocation)) { + clearEtagCacheForUrl(`/v1/summits/${this.summit.id}/events/${entity_id}/published/streaming-info`) const streaming_info = await fetchStreamingInfoByEventId(this.summit.id, entity_id, this.accessToken); if(streaming_info) entity = {...entity, ...streaming_info}; } diff --git a/src/workers/sync_strategies/activity_type_synch_strategy.js b/src/workers/sync_strategies/activity_type_synch_strategy.js index 4a0f796b..ce2cc7a0 100644 --- a/src/workers/sync_strategies/activity_type_synch_strategy.js +++ b/src/workers/sync_strategies/activity_type_synch_strategy.js @@ -1,5 +1,5 @@ import AbstractSynchStrategy from "./abstract_synch_strategy"; -import {fetchEventTypeById} from "../../actions/fetch-entities-actions"; +import {clearEtagCacheForUrl, fetchEventTypeById} from "../../actions/fetch-entities-actions"; import { BUCKET_EVENTS_DATA_KEY, BUCKET_SUMMIT_DATA_KEY, @@ -17,6 +17,8 @@ class ActivityTypeSynchStrategy extends AbstractSynchStrategy{ const {entity_operator, entity_id} = payload; + clearEtagCacheForUrl(`/v1/summits/${this.summit.id}/event-types/${entity_id}`); + const entity = await fetchEventTypeById(this.summit.id, entity_id, this.accessToken); if (entity_operator === 'UPDATE') { diff --git a/src/workers/sync_strategies/speaker_synch_strategy.js b/src/workers/sync_strategies/speaker_synch_strategy.js index 2e843e29..12c104a3 100644 --- a/src/workers/sync_strategies/speaker_synch_strategy.js +++ b/src/workers/sync_strategies/speaker_synch_strategy.js @@ -1,5 +1,5 @@ import AbstractSynchStrategy from "./abstract_synch_strategy"; -import {fetchSpeakerById} from "../../actions/fetch-entities-actions"; +import {clearEtagCacheForUrl, fetchSpeakerById} from "../../actions/fetch-entities-actions"; import { BUCKET_SUMMIT_DATA_KEY, BUCKET_EVENTS_DATA_KEY, @@ -23,6 +23,7 @@ class SpeakerSynchStrategy extends AbstractSynchStrategy { const {entity_operator, entity_id} = payload; if (entity_operator === 'UPDATE') { + clearEtagCacheForUrl(`/v1/summits/${this.summit.id}/speakers/${entity_id}`); const entity = await fetchSpeakerById(this.summit.id, entity_id, this.accessToken); diff --git a/src/workers/sync_strategies/summit_synch_strategy.js b/src/workers/sync_strategies/summit_synch_strategy.js index a0e3471e..2deccfad 100644 --- a/src/workers/sync_strategies/summit_synch_strategy.js +++ b/src/workers/sync_strategies/summit_synch_strategy.js @@ -1,5 +1,5 @@ import AbstractSynchStrategy from "./abstract_synch_strategy"; -import {fetchSummitById} from "../../actions/fetch-entities-actions"; +import {clearEtagCacheForUrl, fetchSummitById} from "../../actions/fetch-entities-actions"; import { BUCKET_SUMMIT_DATA_KEY, saveFile @@ -17,6 +17,8 @@ class SummitSynchStrategy extends AbstractSynchStrategy { const {entity_operator} = payload; + clearEtagCacheForUrl(`/v1/summits/${this.summit.id}?`); + let entity = await fetchSummitById(this.summit.id, this.accessToken); let eventsData = [...this.allEvents]; diff --git a/src/workers/sync_strategies/track_synch_strategy.js b/src/workers/sync_strategies/track_synch_strategy.js index 71efcd78..abf65476 100644 --- a/src/workers/sync_strategies/track_synch_strategy.js +++ b/src/workers/sync_strategies/track_synch_strategy.js @@ -1,5 +1,5 @@ import AbstractSynchStrategy from "./abstract_synch_strategy"; -import {fetchTrackById} from "../../actions/fetch-entities-actions"; +import {clearEtagCacheForUrl, fetchTrackById} from "../../actions/fetch-entities-actions"; import { BUCKET_EVENTS_DATA_KEY, BUCKET_EVENTS_IDX_DATA_KEY, @@ -159,6 +159,7 @@ class TrackSynchStrategy extends AbstractSynchStrategy { switch (entity_operator) { case 'INSERT': case 'UPDATE':{ + clearEtagCacheForUrl(`/v1/summits/${this.summit.id}/tracks/${entity_id}`); const entity = await fetchTrackById(this.summit.id, entity_id, this.accessToken); if (!entity) return Promise.reject('TrackSynchStrategy::process entity not found.'); return this._handleUpsert(entity, payload); diff --git a/src/workers/sync_strategies/venue_room_synch_strategy.js b/src/workers/sync_strategies/venue_room_synch_strategy.js index 91f27b48..edb856aa 100644 --- a/src/workers/sync_strategies/venue_room_synch_strategy.js +++ b/src/workers/sync_strategies/venue_room_synch_strategy.js @@ -1,5 +1,5 @@ import AbstractSynchStrategy from "./abstract_synch_strategy"; -import { fetchLocationById } from "../../actions/fetch-entities-actions"; +import { clearEtagCacheForUrl, fetchLocationById } from "../../actions/fetch-entities-actions"; import { BUCKET_EVENTS_DATA_KEY, BUCKET_EVENTS_IDX_DATA_KEY, @@ -20,6 +20,8 @@ class VenueRoomSynchStrategy extends AbstractSynchStrategy{ const {entity_operator, entity_id} = payload; + clearEtagCacheForUrl(`/v1/summits/${this.summit.id}/locations/${entity_id}`); + const entity = await fetchLocationById(this.summit.id, entity_id, 'floor,venue' , this.accessToken); let eventsData = [...this.allEvents];