Inji-400: Cache api calls and return initial values if network not available (#883)

* feat(inji-400): Use Initial Config for all properties incase cache and network not available.

* feat(inji-400): Create cache apis to fetch and cache issuer api's

* feat(inji-400): Update Initial Config comments and remove qa-inji urls

* feat(inji-400): Rename catchAPIMethod to generateCacheAPIFunction

* feat(inji-400): Rename qa inji url from warningDomainName

* feat(inji-400): Update logs for api calls

---------

Signed-off-by: Swati Goel <meet2swati@gmail.com>
Co-authored-by: Swati Goel <meet2swati@gmail.com>
This commit is contained in:
Tilak Puli
2023-10-04 15:44:15 +05:30
committed by GitHub
parent 443d946c38
commit ca83cb8158
6 changed files with 208 additions and 33 deletions

View File

@@ -20,6 +20,7 @@ import {
CredentialWrapper,
VerifiableCredential,
} from '../types/VC/EsignetMosipVC/vc';
import {CACHED_API} from '../shared/api';
const model = createModel(
{
@@ -360,21 +361,13 @@ export const IssuersMachine = model.createMachine(
},
services: {
downloadIssuersList: async () => {
const defaultIssuer = {
id: 'UIN, VID, AID',
displayName: 'UIN, VID, AID',
};
return await CACHED_API.fetchIssuers();
},
const response = await request('GET', '/residentmobileapp/issuers');
return [defaultIssuer, ...response.response.issuers];
},
downloadIssuerConfig: async (_, event) => {
const response = await request(
'GET',
`/residentmobileapp/issuers/${event.id}`,
);
return response.response;
return await CACHED_API.fetchIssuerConfig(event.id);
},
downloadCredential: async context => {
const body = await getBody(context);
const response = await fetch(

21
shared/InitialConfig.ts Normal file
View File

@@ -0,0 +1,21 @@
// Initial configs used by app if network is not available when app is opened for first time. once network is available response is cached and used from then
// Note: Need to keep this config in sync with actual mimoto response.
export const INITIAL_CONFIG = {
// These properties are stored in mosip-config github repo / inji-default.properties
allProperties: {
modelDownloadMaxRetry: '10',
audience: 'ida-binding',
allowedInternalAuthType: 'otp,bio-Finger,bio-Iris,bio-Face',
vcDownloadMaxRetry: '10',
minStorageRequiredForAuditEntry: '2',
minStorageRequired: '2',
vcDownloadPoolInterval: '6000',
issuer: 'residentapp',
allowedAuthType: 'demo,otp,bio-Finger,bio-Iris,bio-Face',
allowedEkycAuthType: 'demo,otp,bio-Finger,bio-Iris,bio-Face',
warningDomainName: '',
aboutInjiUrl: 'https://docs.mosip.io/inji',
faceSdkModelUrl: '',
},
};

168
shared/api.ts Normal file
View File

@@ -0,0 +1,168 @@
import {request} from './request';
import Storage, {API_CACHED_STORAGE_KEYS} from './storage';
import {COMMON_PROPS_KEY} from './commonprops/commonProps';
import {INITIAL_CONFIG} from './InitialConfig';
export const API_URLS = {
issuersList: {
method: 'GET',
buildURL: (): `/${string}` => '/residentmobileapp/issuers',
},
issuerConfig: {
method: 'GET',
buildURL: (issuerId: string): `/${string}` =>
`/residentmobileapp/issuers/${issuerId}`,
},
allProperties: {
method: 'GET',
buildURL: (): `/${string}` => '/residentmobileapp/allProperties',
},
};
export const API = {
fetchIssuers: async () => {
const defaultIssuer = {
id: 'UIN, VID, AID',
displayName: 'UIN, VID, AID',
};
const response = await request(
API_URLS.issuersList.method,
API_URLS.issuersList.buildURL(),
);
return [defaultIssuer, ...(response.response.issuers || [])];
},
fetchIssuerConfig: async (issuerId: string) => {
const response = await request(
API_URLS.issuerConfig.method,
API_URLS.issuerConfig.buildURL(issuerId),
);
return response.response;
},
fetchAllProperties: async () => {
const response = await request(
API_URLS.allProperties.method,
API_URLS.allProperties.buildURL(),
);
return response.response;
},
};
export const CACHED_API = {
fetchIssuers: () =>
generateCacheAPIFunction({
cacheKey: API_CACHED_STORAGE_KEYS.fetchIssuers,
fetchCall: API.fetchIssuers,
}),
fetchIssuerConfig: (issuerId: string) =>
generateCacheAPIFunction({
cacheKey: API_CACHED_STORAGE_KEYS.fetchIssuerConfig(issuerId),
fetchCall: API.fetchIssuerConfig.bind(null, issuerId),
}),
getAllProperties: () =>
generateCacheAPIFunction({
isCachePreferred: true,
cacheKey: COMMON_PROPS_KEY,
fetchCall: API.fetchAllProperties,
onErrorHardCodedValue: INITIAL_CONFIG.allProperties,
}),
};
interface GenerateCacheAPIFunctionProps {
isCachePreferred?: boolean;
cacheKey: string;
fetchCall: (...props: any) => Promise<any>;
onErrorHardCodedValue?: any;
}
async function generateCacheAPIFunction({
isCachePreferred = false,
cacheKey = '',
fetchCall = () => Promise.resolve(),
onErrorHardCodedValue = undefined,
}: GenerateCacheAPIFunctionProps) {
if (isCachePreferred) {
return await generateCacheAPIFunctionWithCachePreference(
cacheKey,
fetchCall,
onErrorHardCodedValue,
);
} else {
return await generateCacheAPIFunctionWithAPIPreference(
cacheKey,
fetchCall,
onErrorHardCodedValue,
);
}
}
async function generateCacheAPIFunctionWithCachePreference(
cacheKey: string,
fetchCall: (...props: any[]) => any,
onErrorHardCodedValue?: any,
) {
try {
const response = (await Storage.getItem(cacheKey)) as string;
if (response) {
return JSON.parse(response);
} else {
const response = await fetchCall();
Storage.setItem(cacheKey, JSON.stringify(response)).then(() =>
console.log('Cached response for ' + cacheKey),
);
return response;
}
} catch (error) {
console.warn(`Failed to load due to network issue in cache preferred api call.
cache key:${cacheKey} and has onErrorHardCodedValue:${
onErrorHardCodedValue != undefined
}`);
console.log(error);
if (onErrorHardCodedValue != undefined) {
return onErrorHardCodedValue;
} else {
throw error;
}
}
}
async function generateCacheAPIFunctionWithAPIPreference(
cacheKey: string,
fetchCall: (...props: any[]) => any,
onErrorHardCodedValue?: any,
) {
try {
const response = await fetchCall();
Storage.setItem(cacheKey, JSON.stringify(response)).then(() =>
console.log('Cached response for ' + cacheKey),
);
return response;
} catch (error) {
console.warn(`Failed to load due to network issue in API preferred api call.
cache key:${cacheKey} and has onErrorHardCodedValue:${
onErrorHardCodedValue != undefined
}`);
console.log(error);
const response = (await Storage.getItem(cacheKey)) as string;
if (response) {
return JSON.parse(response);
} else {
if (onErrorHardCodedValue != undefined) {
return onErrorHardCodedValue;
} else {
throw error;
}
}
}
}

View File

@@ -1,28 +1,13 @@
import { request } from '../request';
import Storage from '../storage';
import { init } from 'mosip-inji-face-sdk';
import { changeCrendetialRegistry } from '../constants';
import {init} from 'mosip-inji-face-sdk';
import {changeCrendetialRegistry} from '../constants';
import {CACHED_API} from '../api';
export const COMMON_PROPS_KEY: string =
'CommonPropsKey-' + '6964d04a-9268-11ed-a1eb-0242ac120002';
export default async function getAllConfigurations(host = undefined) {
try {
host && changeCrendetialRegistry(host);
const response = await Storage.getItem(COMMON_PROPS_KEY);
if (response) {
return JSON.parse(response);
} else {
const resp = await request('GET', '/residentmobileapp/allProperties');
const injiProps = resp.response;
const injiPropsString = JSON.stringify(injiProps);
await Storage.setItem(COMMON_PROPS_KEY, injiPropsString);
return injiProps;
}
} catch (error) {
console.log(error);
throw error;
}
host && changeCrendetialRegistry(host);
return await CACHED_API.getAllProperties();
}
export async function downloadModel() {

View File

@@ -5,6 +5,8 @@ import {
import {__AppId} from './GlobalVariables';
import {HOST, MIMOTO_BASE_URL} from './constants';
export type HTTP_METHOD = 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE';
export class BackendResponseError extends Error {
constructor(name: string, message: string) {
super(message);
@@ -13,7 +15,7 @@ export class BackendResponseError extends Error {
}
export async function request(
method: 'GET' | 'POST' | 'PATCH',
method: HTTP_METHOD,
path: `/${string}`,
body?: Record<string, unknown>,
host = MIMOTO_BASE_URL,

View File

@@ -27,6 +27,12 @@ import {VCMetadata} from './VCMetadata';
const MMKV = new MMKVLoader().initialize();
const vcDirectoryPath = `${DocumentDirectoryPath}/inji/VC`;
export const API_CACHED_STORAGE_KEYS = {
fetchIssuers: 'CACHE_FETCH_ISSUERS',
fetchIssuerConfig: (issuerId: string) =>
`CACHE_FETCH_ISSUER_CONFIG_${issuerId}`,
};
async function generateHmac(
encryptionKey: string,
data: string,