Created
April 8, 2021 09:34
-
-
Save waldekmastykarz/654157658bc905e263ddd54c24fef7e2 to your computer and use it in GitHub Desktop.
Sample cache middleware for Microsoft Graph JS SDK
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| export function CacheMiddleware(expirationConfig) { | |
| this.nextMiddleware = undefined; | |
| this.expirationConfig = expirationConfig; | |
| const getHeaders = (headers) => { | |
| const h = {}; | |
| for (var header of headers.entries()) { | |
| h[header[0]] = header[1]; | |
| } | |
| return h; | |
| }; | |
| const blobToDataUrl = async (blob) => { | |
| return new Promise((resolve, reject) => { | |
| var reader = new FileReader(); | |
| reader.onload = function () { | |
| var dataUrl = reader.result; | |
| resolve(dataUrl); | |
| }; | |
| reader.readAsDataURL(blob); | |
| }); | |
| }; | |
| // from https://stackoverflow.com/a/16245768 | |
| const dataUrlToBlob = (dataUrl) => { | |
| const blobData = dataUrl.split(','); | |
| const contentType = blobData[0].replace('data:', '').replace(';base64', ''); | |
| return b64toBlob(blobData[1], contentType); | |
| }; | |
| const b64toBlob = (b64Data, contentType = '', sliceSize = 512) => { | |
| const byteCharacters = atob(b64Data); | |
| const byteArrays = []; | |
| for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) { | |
| const slice = byteCharacters.slice(offset, offset + sliceSize); | |
| const byteNumbers = new Array(slice.length); | |
| for (let i = 0; i < slice.length; i++) { | |
| byteNumbers[i] = slice.charCodeAt(i); | |
| } | |
| const byteArray = new Uint8Array(byteNumbers); | |
| byteArrays.push(byteArray); | |
| } | |
| const blob = new Blob(byteArrays, { type: contentType }); | |
| return blob; | |
| } | |
| const getExpiresOn = (url) => { | |
| if (!this.expirationConfig) { | |
| return undefined; | |
| } | |
| for (var i = 0; i < this.expirationConfig.length; i++) { | |
| const exp = this.expirationConfig[i]; | |
| if (url.indexOf(exp.path) > -1) { | |
| const expiresOn = new Date(); | |
| expiresOn.setMinutes(expiresOn.getMinutes() + exp.expirationInMinutes); | |
| return expiresOn; | |
| } | |
| } | |
| return undefined; | |
| } | |
| return { | |
| execute: async (context) => { | |
| console.debug(`Request: ${context.request}`); | |
| const requestKey = btoa(context.request); | |
| let response = window.sessionStorage.getItem(requestKey); | |
| let now = new Date(); | |
| if (response) { | |
| const resp = JSON.parse(response); | |
| const expiresOn = resp.expiresOn ? new Date(resp.expiresOn) : undefined; | |
| if (!expiresOn || expiresOn > now) { | |
| console.debug('-- from cache'); | |
| let body; | |
| if (resp.headers['content-type'].indexOf('application/json') > -1) { | |
| body = JSON.stringify(resp.body); | |
| } | |
| else { | |
| body = dataUrlToBlob(resp.body); | |
| } | |
| context.response = new Response(body, resp); | |
| return; | |
| } | |
| else { | |
| console.log('-- cache expired'); | |
| } | |
| } | |
| console.debug('-- from Graph'); | |
| await this.nextMiddleware.execute(context); | |
| if (context.options.method !== 'GET' || | |
| context.response.status !== 200) { | |
| // don't cache non-GET or failed requests | |
| return; | |
| } | |
| const resp = context.response.clone(); | |
| const expiresOn = getExpiresOn(resp.url); | |
| // reset date to catch expiration set to 0 | |
| now = new Date(); | |
| // don't cache if the item already expired | |
| if (expiresOn <= now) { | |
| return; | |
| } | |
| const headers = getHeaders(resp.headers); | |
| let body = ''; | |
| if (headers['content-type'].indexOf('application/json') >= 0) { | |
| body = await resp.json(); | |
| } | |
| else { | |
| body = await blobToDataUrl(await resp.blob()); | |
| } | |
| response = { | |
| url: resp.url, | |
| status: resp.status, | |
| statusText: resp.statusText, | |
| headers, | |
| body, | |
| expiresOn | |
| }; | |
| window.sessionStorage.setItem(requestKey, JSON.stringify(response)); | |
| }, | |
| setNext: (next) => { | |
| this.nextMiddleware = next; | |
| } | |
| } | |
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { getToken } from '../auth.js'; | |
| import { CacheMiddleware } from './CacheMiddleware.js'; | |
| // Create an authentication provider | |
| function AuthProvider() { | |
| nextMiddleware: undefined; | |
| return { | |
| getAccessToken: async () => { | |
| // Call getToken in auth.js | |
| return await getToken(); | |
| }, | |
| setNext: (next) => { | |
| this.nextMiddleware = next; | |
| } | |
| } | |
| }; | |
| const middleware = MicrosoftGraph.MiddlewareFactory.getDefaultMiddlewareChain(new AuthProvider()); | |
| middleware.unshift(new CacheMiddleware([ | |
| // email messages might change so let's refresh them once in a while | |
| { | |
| path: '/messages', | |
| expirationInMinutes: 5 | |
| }, | |
| // calendarView queries contain current date and time with seconds so no | |
| // point in caching them | |
| { | |
| path: '/calendarView', | |
| expirationInMinutes: 0 | |
| } | |
| ])); | |
| // Graph client singleton | |
| const graphClient = MicrosoftGraph.Client.initWithMiddleware({ middleware }); | |
| export default graphClient; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment