Skip to content

Commit

Permalink
Enhancement: sample queries cache (#2239)
Browse files Browse the repository at this point in the history
  • Loading branch information
thewahome authored Nov 23, 2022
1 parent ade9a17 commit b11fa20
Show file tree
Hide file tree
Showing 13 changed files with 166 additions and 77 deletions.
18 changes: 15 additions & 3 deletions src/app/middleware/localStorageMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
import { samplesCache } from '../../modules/cache/samples.cache';
import { saveTheme } from '../../themes/theme-utils';
import { AppAction } from '../../types/action';
import { CHANGE_THEME_SUCCESS } from '../services/redux-constants';
import {
CHANGE_THEME_SUCCESS, SAMPLES_FETCH_SUCCESS
} from '../services/redux-constants';

const localStorageMiddleware = () => (next: any) => (action: AppAction) => {
if (action.type === CHANGE_THEME_SUCCESS) {
saveTheme(action.response);
switch (action.type) {
case CHANGE_THEME_SUCCESS:
saveTheme(action.response);
break;

case SAMPLES_FETCH_SUCCESS:
samplesCache.saveSamples(action.response);
break;

default:
break;
}
return next(action);
};
Expand Down
4 changes: 2 additions & 2 deletions src/app/services/actions/query-action-creators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { IQuery } from '../../../types/query-runner';
import { IStatus } from '../../../types/status';
import { ClientError } from '../../utils/error-utils/ClientError';
import { setStatusMessage } from '../../utils/status-message';
import { writeHistoryData } from '../../views/sidebar/history/history-utils';
import { historyCache } from '../../../modules/cache/history-utils';
import {
anonymousRequest,
authenticatedRequest,
Expand Down Expand Up @@ -180,7 +180,7 @@ async function createHistory(
result
};

writeHistoryData(historyItem);
historyCache.writeHistoryData(historyItem);

dispatch(addHistoryItem(historyItem));
return result;
Expand Down
6 changes: 3 additions & 3 deletions src/app/services/actions/request-history-action-creators.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

import { AppAction } from '../../../types/action';
import { IHistoryItem } from '../../../types/history';
import { bulkRemoveHistoryData, removeHistoryData } from '../../views/sidebar/history/history-utils';
import { historyCache } from '../../../modules/cache/history-utils';
import {
ADD_HISTORY_ITEM_SUCCESS,
REMOVE_ALL_HISTORY_ITEMS_SUCCESS,
Expand Down Expand Up @@ -35,7 +35,7 @@ export function removeHistoryItem(historyItem: IHistoryItem) {

delete historyItem.category;
return async (dispatch: Function) => {
return removeHistoryData(historyItem)
return historyCache.removeHistoryData(historyItem)
.then(() => {
dispatch({
type: REMOVE_HISTORY_ITEM_SUCCESS,
Expand All @@ -53,7 +53,7 @@ export function bulkRemoveHistoryItems(historyItems: IHistoryItem[]) {
});

return async (dispatch: Function) => {
return bulkRemoveHistoryData(listOfKeys)
return historyCache.bulkRemoveHistoryData(listOfKeys)
.then(() => {
dispatch({
type: REMOVE_ALL_HISTORY_ITEMS_SUCCESS,
Expand Down
8 changes: 7 additions & 1 deletion src/app/services/actions/samples-action-creators.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { geLocale } from '../../../appLocale';
import { samplesCache } from '../../../modules/cache/samples.cache';
import { AppDispatch } from '../../../store';
import { AppAction } from '../../../types/action';
import { IRequestOptions } from '../../../types/request';
import { queries } from '../../views/sidebar/sample-queries/queries';
import {
SAMPLES_FETCH_ERROR,
SAMPLES_FETCH_PENDING,
Expand Down Expand Up @@ -55,7 +57,11 @@ export function fetchSamples() {
const res = await response.json();
return dispatch(fetchSamplesSuccess(res.sampleQueries));
} catch (error) {
return dispatch(fetchSamplesError({ error }));
let cachedSamples = await samplesCache.readSamples();
if (cachedSamples.length === 0) {
cachedSamples = queries;
}
return dispatch(fetchSamplesError(cachedSamples));
}
};
}
8 changes: 2 additions & 6 deletions src/app/services/reducers/samples-reducers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,11 @@ describe('Samples Reducer', () => {
error: null
};

const mockResponse = {
status: 400
};

const newState = { ...initialState };
newState.error = mockResponse;
newState.error = 'error';
newState.queries = queries;

const queryAction = { type: SAMPLES_FETCH_ERROR, response: mockResponse };
const queryAction = { type: SAMPLES_FETCH_ERROR, response: queries };
const state = samples(initialState, queryAction);

expect(state).toEqual(newState);
Expand Down
5 changes: 2 additions & 3 deletions src/app/services/reducers/samples-reducers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { AppAction } from '../../../types/action';
import { queries } from '../../views/sidebar/sample-queries/queries';
import { SAMPLES_FETCH_ERROR, SAMPLES_FETCH_PENDING, SAMPLES_FETCH_SUCCESS } from '../redux-constants';

const initialState = {
Expand All @@ -25,8 +24,8 @@ export function samples(state = initialState, action: AppAction): any {
return {
...state,
pending: false,
queries,
error: action.response
queries: action.response,
error: 'error'
};
default:
return state;
Expand Down
6 changes: 5 additions & 1 deletion src/app/views/sidebar/history/History.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
DialogFooter, DialogType, getId, getTheme, IColumn, IconButton,
Label, MessageBar, MessageBarType, PrimaryButton, SearchBox, SelectionMode, styled, TooltipHost
} from '@fluentui/react';
import React, { useState } from 'react';
import React, { useEffect, useState } from 'react';
import { FormattedMessage, injectIntl } from 'react-intl';
import { useDispatch } from 'react-redux';

Expand Down Expand Up @@ -94,6 +94,10 @@ const History = (props: any) => {

const classes = classNames(props);

useEffect(() => {
setHistoryItems(history);
}, [history])

if (!history || history.length === 0) {
return NoResultsFound('We did not find any history items');
}
Expand Down
49 changes: 0 additions & 49 deletions src/app/views/sidebar/history/history-utils.ts

This file was deleted.

4 changes: 2 additions & 2 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { bulkAddHistoryItems } from './app/services/actions/request-history-acti
import { changeThemeSuccess } from './app/services/actions/theme-action-creator';
import { isValidHttpsUrl } from './app/utils/external-link-validation';
import App from './app/views/App';
import { readHistoryData } from './app/views/sidebar/history/history-utils';
import { historyCache } from './modules/cache/history-utils';
import { geLocale } from './appLocale';
import messages from './messages';
import { authenticationWrapper } from './modules/authentication';
Expand Down Expand Up @@ -137,7 +137,7 @@ if (devxApiUrl && isValidHttpsUrl(devxApiUrl)) {
appStore.dispatch(setDevxApiUrl(devxApi));
}

readHistoryData().then((data: any) => {
historyCache.readHistoryData().then((data: any) => {
if (data.length > 0) {
appStore.dispatch(bulkAddHistoryItems(data));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IHistoryItem } from '../../../../types/history';
import { writeHistoryData, readHistoryData, removeHistoryData } from './history-utils';
import { IHistoryItem } from '../../types/history';
import { historyCache } from './history-utils';


let historyItems: IHistoryItem[] = [];
Expand Down Expand Up @@ -43,8 +43,8 @@ describe('History utils should', () => {
status: 200
}
expect(historyItems.length).toBe(0);
await writeHistoryData(historyItem);
const historyData = await readHistoryData();
await historyCache.writeHistoryData(historyItem);
const historyData = await historyCache.readHistoryData();
expect(historyData.length).toBe(1);
});

Expand All @@ -61,9 +61,9 @@ describe('History utils should', () => {
duration: 200,
status: 200
}
await writeHistoryData(historyItem);
await historyCache.writeHistoryData(historyItem);
expect(historyItems.length).toBe(2);
await removeHistoryData(historyItem);
await historyCache.removeHistoryData(historyItem);
expect(historyItems.length).toBe(1);
});

Expand All @@ -83,7 +83,7 @@ describe('History utils should', () => {
}
historyItems.push(historyItem);
expect(historyItems.length).toBe(1);
const historyData = await readHistoryData();
const historyData = await historyCache.readHistoryData();
expect(historyData.length).toBe(0);
});
})
59 changes: 59 additions & 0 deletions src/modules/cache/history-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import localforage from 'localforage';
import { IHistoryItem } from '../../types/history';

const historyStorage = localforage.createInstance({
storeName: 'history',
name: 'GE_V4'
});

function historyItemHasExpired(createdAt: string): boolean {
const ageInDays: number = 30;
const dateToCompare = new Date();
dateToCompare.setDate(dateToCompare.getDate() - ageInDays);
const expiryDate = dateToCompare.getTime();
const createdTime = new Date(createdAt).getTime();
return (createdTime < expiryDate);
}

export const historyCache = (function () {

const writeHistoryData = (historyItem: IHistoryItem) => {
historyStorage.setItem(historyItem.createdAt, historyItem);
}

const readHistoryData = async (): Promise<IHistoryItem[]> => {
let historyData: IHistoryItem[] = [];
const keys = await historyStorage.keys();
for (const creationTime of keys) {
if (historyItemHasExpired(creationTime)) {
historyStorage.removeItem(creationTime);
} else {
const historyItem = await historyStorage.getItem(creationTime)! as IHistoryItem;
historyData = [...historyData, historyItem];
}
}
return historyData;
}

const removeHistoryData = async (historyItem: IHistoryItem) => {
await historyStorage.removeItem(historyItem.createdAt);
return true;
};

const bulkRemoveHistoryData = async (listOfKeys: string[]) => {
historyStorage.iterate((_value, key) => {
if (listOfKeys.includes(key)) {
historyStorage.removeItem(key);
}
}).then(() => {
return true;
});
};

return {
writeHistoryData,
bulkRemoveHistoryData,
removeHistoryData,
readHistoryData
}
})();
33 changes: 33 additions & 0 deletions src/modules/cache/samples.cache.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { ISampleQuery } from '../../types/query-runner';
import { samplesCache } from './samples.cache';

jest.mock('localforage', () => ({
// eslint-disable-next-line @typescript-eslint/no-empty-function
config: () => { },
createInstance: () => ({
// eslint-disable-next-line @typescript-eslint/no-empty-function
getItem: () => { },
// eslint-disable-next-line @typescript-eslint/no-empty-function
setItem: () => { }
})
}));

const queries: ISampleQuery[] = [];
describe('Samples Cache should', () => {
it('return the same queries after queries added', async () => {
expect(queries.length).toBe(0);
queries.push(
{
category: 'Getting Started',
method: 'GET',
humanName: 'my profile',
requestUrl: '/v1.0/me',
docLink: 'https://learn.microsoft.com/en-us/graph/api/user-get',
skipTest: false
},
);
samplesCache.saveSamples(queries);
const samplesData = await samplesCache.readSamples();
expect(samplesData).not.toBe(queries);
})
})
29 changes: 29 additions & 0 deletions src/modules/cache/samples.cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import localforage from 'localforage';
import { ISampleQuery } from '../../types/query-runner';

const samplesStorage = localforage.createInstance({
storeName: 'samples',
name: 'GE_V4'
});

const SAMPLE_KEY = 'sample-queries';

export const samplesCache = (function () {

const saveSamples = async (queries: ISampleQuery[]) => {
await samplesStorage.setItem(SAMPLE_KEY, JSON.stringify(queries));
}

const readSamples = async (): Promise<ISampleQuery[]> => {
const items = await samplesStorage.getItem(SAMPLE_KEY) as string;
if (items) {
return JSON.parse(items);
}
return [];
}

return {
saveSamples,
readSamples
}
})();

0 comments on commit b11fa20

Please sign in to comment.