diff --git a/.eslintrc.js b/.eslintrc.js index 781cde0..046be60 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -139,7 +139,8 @@ module.exports = { "ignoreStrings": true, "ignoreRegExpLiterals": true, "ignoreTemplateLiterals": true, - "ignoreUrls": true + "ignoreUrls": true, + "ignoreComments": true } ], "max-lines": [ diff --git a/libs/core/README.md b/libs/core/README.md index b2c7dc7..d0bd312 100644 --- a/libs/core/README.md +++ b/libs/core/README.md @@ -16,6 +16,8 @@ The core library to use when creating an interface asset. - [Action](#action) - [Parameter](#parameter) - [Types](#types) +- [Services](#services) + - [Cache service](src/lib/services/README.md#cache-service) - [Help](#help) ## Decorators In Intuiface Core, we use [TypeScript decorators](https://www.typescriptlang.org/docs/handbook/decorators.html) to manage Properties, Triggers and Actions in Intuiface Composer. @@ -322,6 +324,11 @@ We included different types for properties and parameters : - Resource - Time +## Services +`@intuiface/core` exposes services that will help you access low level information (device's id, name, os...), hardware, cache... in an easy and cross-platform way. + +- [Cache service](src/lib/services/README.md#cache-service) + ## Help Found a problem, a bug? Or need some help? diff --git a/libs/core/src/lib/services/README.md b/libs/core/src/lib/services/README.md new file mode 100644 index 0000000..79a3bc4 --- /dev/null +++ b/libs/core/src/lib/services/README.md @@ -0,0 +1,253 @@ +# Services + +`@intuiface/core` exposes services that will help you access low level information (device's id, name, os...), hardware, cache... in an easy and cross-platform way. + +## Table of content + +- [Cache service](#cache-service) + - Methods: + - [fetch()](#fetch) + - [downloadFile()](#downloadfile) + - [listCacheEntries()](#listCacheEntries) + - [deleteCache()](#deletecache) + - [removeFromCache()](#removefromcache) + - [getCacheUri()](#getcacheuri) + - Types: + - [CacheStrategy](#cachestrategy) + - [CacheOptions](#cacheoptions) + - [CacheEntry](#cacheentry) + - [DownloadProgress](#downloadprogress) +- [Environment service](#environment-service) +- [USB serial service](#usb-serial-service) +- [System-info service](#system-info-service) + +___ + +## Cache service + +`CacheService` enhances the [`Fetch API`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) with caching strategy, allowing you to store locally request's responses and files and access them even offline. + +Cached data is stored through [Cache API](https://developer.mozilla.org/en-US/docs/Web/API/Cache) when using [`fetch()`](#fetch) and on file system (only for Player in-venue, not XP as a Webpage) when using [`downloadFile()`](#downloadfile). +Cache is persistant, which means it is kept between player launches. It can also be scoped to be associated to one experience or available accross all. See [CacheScope](#cachescope) for details. + +### fetch() +`fetch(request, init?, cacheOptions?, progressCallback?): Promise` + +Enhanced global [`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/fetch) with caching mechanism. Use fetch when you want to directly use the response content. To get path to a locally cached file, use [`downloadFile()`](#downloadfile) instead. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `request` | `string` \| `Request` | This defines the resource that you wish to fetch. See [`fetch()` parameters](https://developer.mozilla.org/en-US/docs/Web/API/fetch#parameters). | +| `init?` | `RequestInit` | An object containing any custom settings that you want to apply to the request, such as `headers`, `method`. See [`fetch()` parameters](https://developer.mozilla.org/en-US/docs/Web/API/fetch#parameters). | +| `cacheOptions?` | [`CacheOptions`](#cacheoptions) | An object containing cache behaviors for the request. | +| `progressCallback?` | [`DownloadProgressCallback`](#cacheoptions) | Use this callback to trace download progress. | + +#### Returns + +`Promise`<[`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)\> + +#### Usage +- Fetch a json file with _network first_ strategy: +```ts +try +{ + const response = await CacheService.fetch('https://httpbin.org/get', null, { + cacheName: 'myCache', + strategy: CacheStrategy.NetworkFirst + }); + + const json = await response.json(); + + // Do something with json +}catch(error){ + console.error(error); +} +``` + +### downloadFile() + +Downloads a file into local cache and returns its local uri +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `request` | `string` \| `Request` | This defines the resource that you wish to fetch. See [`fetch()` parameters](https://developer.mozilla.org/en-US/docs/Web/API/fetch#parameters). | +| `init?` | `RequestInit` | An object containing any custom settings that you want to apply to the request, such as `headers`, `method`. See [`fetch()` parameters](https://developer.mozilla.org/en-US/docs/Web/API/fetch#parameters). | +| `cacheOptions?` | [`CacheOptions`](#cacheoptions) | An object containing cache behaviors for the request. | +| `progressCallback?` | [`DownloadProgressCallback`](#cacheoptions) | Use this callback to trace download progress. | + + +#### Returns + +`Promise`: a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) that resolve to an URI as `string` that can be used as source (i.e. `src` attribut) of a HTML element such as ``. + +#### Usage +- download an image with _cache first_ strategy and progress feedback: +```ts +try +{ + const imageUri = await CacheService.downloadFile('https://httpbin.org/get', null, { + cacheName: 'myCache', + strategy: CacheStrategy.CacheFirst + }, + (progress)=> { + // compute percentage + const percent = Math.round(progress.receivedLength / progress.contentLength * 100); + console.log(`Downloaded ${percent}%`); + }); + + // Do something imageUri +}catch(error){ + console.error(error); +} +``` + +### listCacheEntries() + +List entries cached in given cache + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `cacheName` | `string` | Name of the cache | +| `cacheScope` | [`CacheScope`](#cachescope) | Scope of the cache entries to list | + + +#### Returns +`Promise`: a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) that resolve to an array of [`CacheEntry`](#cacheentry). + +#### Usage +```ts +const entries = await CacheService.listCacheEntries('myCache'); +console.table(entries); +``` + +### deleteCache() + +Delete given cache + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `cacheName` | `string` | Name of the cache | +| `cacheScope` | [`CacheScope`](#cachescope) | Scope of the cache to delete | +| `failOnError?` | `boolean` | If `true`, throws `Error` when deletion fails. Otherwise catch error silently. Defaults to `false`. | + +#### Returns +`Promise`: a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) that resolve when cache has been deleted. + + +### removeFromCache() + +Remove cached data for the given url + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `url` | `string` | URL of the resource to remove | +| `cacheName` | `string` | Name of the cache | +| `cacheScope` | [`CacheScope`](#cachescope) | Scope of this cached data to remove | + +#### Returns +`Promise`: a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) that resolve when data has been removed from cache. + +### getCacheUri() + +Get cache URI for the given url + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `url` | `string` | URL of the resource to remove | +| `cacheName` | `string` | Name of the cache | +| `cacheScope` | [`CacheScope`](#cachescope) | Scope of the cached resource | + +#### Returns + +`Promise`: a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) that resolve to an URI as `string` that can be used as source (i.e. `src` attribut) of a HTML element such as ``. + +___ + +### CacheStrategy + +`Enum` of available cache strategy +- `NetworkOnly`: + Only fetch response from network. _*Response will not be cached*_. + Use this when you always want the most up-to-date response and disable cache. +- `CacheOnly`: + Only fetch response from cache. _*No network request will be sent*_. + Use this when you know data is already cached and you absolutly don't want to update it. +- `NetworkFirst`: +Make a network request first and fallback with response from cache if it fails. Cache will be updated with response from network. +Use this when you want up-to-date response but accept cached data as fallback. +- `CacheFirst`: +Get response from cache first. If nothing is found, make a network request and cache response. +Use for performance and you want to avoid unecessary network request. + +### CacheScope + +`Enum` that defines if a cache is available across all experiences or only the current one. +- `Player`: +Player scope means cache will be shared between all experiences. +- `Experience`: +Experience scope means cache will be available only for current experience. +_*Note*_: When deleting experience, Player will delete all cache associated with this experience. + +### CacheOptions +An object configuring cache options for a request. +- `strategy`: [`CacheStrategy`](#cachestrategy) +Strategy to use when requesting a resource. +- `cacheName: string` +Name of the cache. +It's a way to segregate data and easily retreive cache entries. It can be considered as a folder and can include `/` to create sub-caches. +- `cacheScope?`: [`CacheScope`](#cachescope) +Indicates if cache is shared between experiences or only available for the current running experience. Defaults to `Experience`. +`Player` means cache is shared between experiences +`Experience` means it will be available only for current experience and deleted if experience is removed from devices +- `ignoreSearch?: boolean` +Ignore query parameters. Defaults to `false`. +- `cacheErrorResponse?: boolean` +Enable caching an error response. Defaults to `false`. +- `enabledForXPWeb?: boolean` +Enable caching for [XP as a Web page](https://support.intuiface.com/hc/en-us/articles/4407120931218-Details-about-Player-Next-Gen-on-the-Web). +By default cache is disabled in _XP as a Web page_ and the browser do it according to Cache-Control header in response. +If `true`, response will be cache through [Cache API](https://developer.mozilla.org/en-US/docs/Web/API/Cache) and browser will not clean it. _*So be careful not to use all user storage.*_ +- `enabledForComposer?: boolean` +Enable caching in Composer's Play mode. Defaults to false. + + +### CacheEntry +An object containing metadata about an entry in cache. + +- `cacheName: string` +Name of the cache containing this entry. +- `sourceUrl: string` +source URL from where the data was requested. +- `cacheUri?: string` +URI of cache entry stored on file system and that can be used as source of HTML elements (such as ``). _*Only available when access to file system is possible*_ (not for XP as webpage). +- `lastUpdate: Date` + Date of last time entry was updated + +### DownloadProgress +- `receivedLength: number` +Number of bytes received for this download +- `contentLength: number` +Number of total bytes to download from the `Response` content. + + +___ + +## Environment service + + +## USB serial service + + +## System-info service + diff --git a/libs/core/src/lib/services/cache.service.ts b/libs/core/src/lib/services/cache.service.ts new file mode 100644 index 0000000..508cfb3 --- /dev/null +++ b/libs/core/src/lib/services/cache.service.ts @@ -0,0 +1,211 @@ + +/** + * Metadata about stored data in cache + */ +export interface CacheEntry { + /** + * Name of the cache + */ + cacheName: string; + /** + * source URL from where the data was requested + */ + sourceUrl: string; + + /** + * URI of cache entry stored on file system and that can be used as source of HTML elements (such as ). + */ + cacheUri?: string; + + /** + * Date from last time entry was updated + */ + lastUpdate: Date; +} + +/** + * Cache strategies. + */ +export const enum CacheStrategy { + /** + * Only fetch response from network. Response will not be cached. + * Use this when you always want the most up-to-date response and disable cache. + */ + NetworkOnly = 'NetworkOnly', + + /** + * Only fetch response from cache. No network request will be sent. + * Use this when you know data is already cached and you absolutly don't want to update it. + */ + CacheOnly = 'CacheOnly', + + /** + * Make a network request first and fallback with response from cache if it fails. + * Use this when you want up-to-date response but accept cached data as fallback. + * Cache will be updated with response from network. + */ + NetworkFirst = 'NetworkFirst', + + /** + * Get response from cache first. If nothing is found, make a network request and cache response. + * Use performance and you want to avoid making unecessary network request. + */ + CacheFirst = 'CacheFirst' +} + +/** + * Cache scope defines if a cache is available across all experiences or only the current one. + */ +export const enum CacheScope { + /** + * Player scope means cache will be shared between all experiences. + */ + Player = 'player', + + /** + * Experience scope means cache will be available only for current experience. + * @remarks When deleting experience, Player will delete all cache associated with this experience. + */ + Experience = 'experience' +} + +/** + * Cache options + */ +export interface CacheOptions { + /** + * Strategy to use when requesting a resource + */ + strategy: CacheStrategy; + + /** + * Name of the cache + */ + cacheName: string; + + /** + * Indicates if cache is shared between experiences or only available for the current running experience. + * `player` means cache is shared between experiences + * `experience` means it will be available only for current experience and deleted if experience is removed from devices + */ + cacheScope?: CacheScope; + + /** + * Ignore query parameters. Defaults to false. + */ + ignoreSearch?: boolean; + + /** + * Enable caching an error response. Defaults to false. + */ + cacheErrorResponse?: boolean; + + /** + * Enable caching for XP as a Web page. + * By default cache is disabled and the browser do it according to Cache-Control header in response. + * If true, response will be cache through Cache API and browser will not clean it. + * So be careful not to use all user storage. + */ + enabledForXPWeb?: boolean; + + /** + * Enable caching in Composer's Play mode. Defaults to false. + */ + enabledForComposer?: boolean; +} + + +export interface DownloadProgress { + receivedLength: number; + contentLength: number; +} +export type DownloadProgressCallback = (progress: DownloadProgress) => void; + + +/** + * Service to make request or download file with a caching strategy + */ +export class CacheService { + + /** + * Enhanced global fetch() with caching mechanism. + * Use fetch when you want to directly use the response content. + * To get path to a locally cached file, use downloadFile() instead. + * @param request + * @param init + * @param cacheOptions + * @param progressCallback + * @returns + */ + public static async fetch(request: RequestInfo, + init?: RequestInit, + cacheOptions?: CacheOptions, + progressCallback?: DownloadProgressCallback): Promise + { + return new Promise(r => r(new Response(''))); + } + + /** + * Downloads a file into local cache and returns its local uri + * @param request + * @param init + * @param cacheOptions + * @param progressCallback + * @returns + */ + public static async downloadFile(request: RequestInfo, + init?: RequestInit, + cacheOptions?: CacheOptions, + progressCallback?: DownloadProgressCallback): Promise + { + return new Promise(r => r('')); + + } + + /** + * Get cache URI for the given url + * @param url + * @param cacheName + * @param cacheScope + */ + public static async getCacheURI(url: string, cacheName: string, cacheScope: CacheScope): Promise + { + return new Promise(r => r('')); + } + + /** + * List entries cached in given cache + * @param cacheName + * @param cacheScope + * @returns + */ + public static async listCacheEntries(cacheName: string, cacheScope: CacheScope): Promise + { + return new Promise(r => r([])); + + } + + /** + * Remove cached data for the given url + * @param url + * @param cacheName + * @param cacheScope + */ + public static async removeFromCache(url: string, cacheName: string, cacheScope: CacheScope): Promise + { + return new Promise(r => r()); + } + + /** + * Delete given cache + * @param cacheName + * @param cacheScope + * @param failOnError if true, throw PlayerError when delete fails. + */ + public static async deleteCache(cacheName: string, + cacheScope: CacheScope, + failOnError: boolean = true): Promise + { + return new Promise(r => r()); + } +} diff --git a/libs/core/src/public-api.ts b/libs/core/src/public-api.ts index 0ecfe8e..28fb8bc 100644 --- a/libs/core/src/public-api.ts +++ b/libs/core/src/public-api.ts @@ -15,6 +15,7 @@ export { IDisposable } from './lib/base/disposable'; export { EnvironmentService } from './lib/services/environment.service'; export { UsbSerialService } from './lib/services/usb.serial.service'; export { SystemInfoService } from './lib/services/system-info.service'; +export { CacheService, CacheStrategy, CacheOptions, CacheEntry, CacheScope, DownloadProgress, DownloadProgressCallback } from './lib/services/cache.service'; // types export { Path } from './lib/types/path.type';