diff --git a/src/context/authContext.tsx b/src/context/authContext.tsx index 497e83b..2c2c97f 100644 --- a/src/context/authContext.tsx +++ b/src/context/authContext.tsx @@ -45,37 +45,25 @@ function getAccessTokenForScope(silentFail: boolean, msalInstance: any, scope: a }); } -function getCentralApplicationsARM(accessToken: string, subscriptionId: string) { - return new Promise(async (resolve, reject) => { - axios.get(`https://management.azure.com/subscriptions/${subscriptionId}/providers/Microsoft.IoTCentral/IoTApps?api-version=2018-09-01`, { - headers: { - Authorization: 'Bearer ' + accessToken - } - }) - .then((res) => { - resolve(res.data.value); - }) - .catch((err) => { - reject(err); - }); - }); -} - -function getCentralTemplates(apps: Array, accessToken: string) { +function getCentralData(apps: Array, accessToken: string) { return new Promise(async (resolve, reject) => { const templatesMap = {}; const validTemplates: Array = []; const validApps: Array = []; for (const appIndex in apps) { - const appId = apps[appIndex]; - const res: any = await axios.get(`https://${appId}${Config.AppDNS}/api/preview/deviceTemplates`, { headers: { Authorization: 'Bearer ' + accessToken } }) - for (const templateIndex in res.data.value) { - if (res.data.value[templateIndex].displayName === Config.template) { - validApps.push(appId); - validTemplates.push(res.data.value[templateIndex].id) - templatesMap[appId] = res.data.value[templateIndex].id; - break; + const appId = apps[appIndex].properties.applicationId; + try { + const res: any = await axios.get(`https://${appId}${Config.AppDNS}/api/preview/deviceTemplates`, { headers: { Authorization: 'Bearer ' + accessToken } }) + for (const templateIndex in res.data.value) { + if (res.data.value[templateIndex].displayName === Config.template) { + validApps.push(appId); + validTemplates.push(res.data.value[templateIndex].id) + templatesMap[appId] = res.data.value[templateIndex].id; + break; + } } + } catch (err) { + console.log("Skipping app: " + appId); } } if (validTemplates.length > 0) { @@ -102,6 +90,22 @@ function getSubscriptionsARM(accessToken: string) { }); } +function getCentralApplicationsARM(accessToken: string, subscriptionId: string) { + return new Promise(async (resolve, reject) => { + axios.get(`https://management.azure.com/subscriptions/${subscriptionId}/providers/Microsoft.IoTCentral/IoTApps?api-version=2018-09-01`, { + headers: { + Authorization: 'Bearer ' + accessToken + } + }) + .then((res) => { + resolve(res.data.value); + }) + .catch((err) => { + reject(err); + }); + }); +} + function getTenantsARM(accessToken: string) { return new Promise(async (resolve, reject) => { axios.get(`https://management.azure.com/tenants?api-version=2020-01-01`, { @@ -202,46 +206,30 @@ export class AuthProvider extends React.PureComponent { selectSubscription = async (subscription: any) => { - let cachedApps, cachedFilter = null; + let cachedApps, cachedFilter: any = null; try { - cachedApps = JSON.parse(localStorage.getItem('cachedState') as string); - cachedFilter = JSON.parse(localStorage.getItem('cachedFilter') as string); + cachedApps = localStorage.getItem('cachedState'); + cachedFilter = localStorage.getItem('cachedFilter'); } catch { }; - if (Config.cacheAppDevices) { - if (cachedApps) { - if (Config.cacheAppFilters && cachedFilter) { cachedApps.filteredApps = cachedFilter; } - this.setState(cachedApps); - return; - } - } - - let filtered: Array = []; - if (Config.cacheAppFilters && cachedFilter) { - filtered = cachedFilter; - } - else { - for (const app in subscription.apps) { - filtered.push(subscription.apps[app].properties.applicationId); - } + if (Config.cacheApps && cachedApps !== null && cachedApps !== '') { + const newState: any = JSON.parse(cachedApps); + if (cachedFilter !== null && cachedFilter !== '') { newState.filteredApps = JSON.parse(cachedFilter) } + this.setState(newState); + return; } + const apps = Object.assign({}, subscription.apps); const centralToken: any = await getAccessTokenForScope(true, this.msalInstance, Scopes.Central, { account: this.state.loginAccount }); - const templates: any = await getCentralTemplates(filtered, centralToken.accessToken) - - const newApps: any = []; - for (const app of subscription.apps) { - if (templates.validApps.indexOf(app.properties.applicationId) > -1) { newApps.push(app); } - } - - subscription.apps = newApps; + const central: any = await getCentralData(apps, centralToken.accessToken) const state = { subscription: true, activeSubscription: subscription, - filteredApps: filtered, - filteredAppsTemplates: templates.templatesMap, - validTemplates: templates.validTemplates + centralApps: central.validApps, + filteredApps: Config.cacheApps && cachedFilter !== null && cachedFilter !== '' ? JSON.parse(cachedFilter) : central.validApps, + filteredAppsTemplates: central.templatesMap, + validTemplates: central.validTemplates } setTimeout(() => { @@ -282,6 +270,7 @@ export class AuthProvider extends React.PureComponent { return res.accessToken; } + // This list is contains the full list of apps per subscription getSubscriptionSelectorList = async () => { const subs: Array = []; if (this.state.authenticated) { @@ -291,12 +280,12 @@ export class AuthProvider extends React.PureComponent { } for (const i in this.state.loginSubscriptions) { - const sub = this.state.loginSubscriptions[i]; - const directory = directories[sub.tenantId]; - const armToken: any = await getAccessTokenForScope(true, this.msalInstance, Scopes.ARM, { account: this.state.loginAccount }); + const subscription = this.state.loginSubscriptions[i]; + const directory = directories[subscription.tenantId]; try { - const apps = await getCentralApplicationsARM(armToken.accessToken, sub.subscriptionId); - subs.push({ directory, subscription: sub, apps }); + const armToken: any = await getAccessTokenForScope(true, this.msalInstance, Scopes.ARM, { account: this.state.loginAccount }); + const apps = await getCentralApplicationsARM(armToken.accessToken, subscription.subscriptionId); + subs.push({ directory, subscription, apps }); } catch { console.log('no perms') } }; } @@ -310,6 +299,7 @@ export class AuthProvider extends React.PureComponent { state: any = { authenticated: false, subscription: false, + centralApps: [], filteredApps: [], filteredAppsTemplates: {}, loginAccount: {}, diff --git a/src/context/dataContext.tsx b/src/context/dataContext.tsx index c6031b9..75f21b0 100644 --- a/src/context/dataContext.tsx +++ b/src/context/dataContext.tsx @@ -26,25 +26,25 @@ function makeAPICall(api, accessToken: string) { }); } -async function getDevicesForApps(appHosts: Array, domain: string, token: string) { +async function getDevicesForApps(apps: Array, domain: string, token: string) { return new Promise(async (resolve, reject) => { const appDevices = {}; try { - for (const host in appHosts) { - const res: any = await makeAPICall(`https://${appHosts[host]}${domain}/api/preview/devices`, token) - appDevices[appHosts[host]] = res.data.value; + for (const appId in apps) { + const res: any = await makeAPICall(`https://${apps[appId]}${domain}/api/preview/devices`, token) + appDevices[apps[appId]] = res.data.value; } resolve(appDevices); } catch (err) { console.warn(err); reject(err); } }) } -async function getDeviceProperty(appHost: string, domain: string, deviceId: string, token: string) { +async function getDeviceProperty(appId: string, domain: string, deviceId: string, token: string) { return new Promise(async (resolve, reject) => { let res1, res2: any = null; try { - res1 = await makeAPICall(`https://${appHost}${domain}/api/preview/devices/${deviceId}/properties`, token) - res2 = await makeAPICall(`https://${appHost}${domain}/api/preview/devices/${deviceId}/cloudProperties`, token) + res1 = await makeAPICall(`https://${appId}${domain}/api/preview/devices/${deviceId}/properties`, token) + res2 = await makeAPICall(`https://${appId}${domain}/api/preview/devices/${deviceId}/cloudProperties`, token) resolve(Object.assign({}, res1.data || {}, res2.data || {})); } catch (err) { console.warn(err); reject(err); } }) @@ -60,9 +60,7 @@ async function getMapData(token: string, templates: any, appDevices: Array, console.log('Map fetch:' + new Date(Date.now())); - for (const appId in appDevices) { - if (filteredApps.indexOf(appId) === -1) { continue; } - + for (const appId of filteredApps) { const devices = appDevices[appId]; for (const device in devices) { if (validTemplates.indexOf(devices[device]['instanceOf']) > -1) { @@ -118,33 +116,39 @@ export class DataProvider extends React.PureComponent { this.startMapData(getToken, templates, filteredApps); } - getDevices = async (appHosts: Array, token: string, filteredApps: Array, overrideCache: boolean) => { + getDevices = async (apps: Array, token: string, overrideCache: boolean) => { // ONLY THIS METHOD SHOULD UPDATE THE devices STATE PROPERTY - let devices: any = null; - if (!overrideCache && Config.cacheAppDevices) { + let cachedDevices: any = null; + if (!overrideCache && Config.cacheDevices) { try { - devices = JSON.parse(localStorage.getItem('cachedDevices') as string); + cachedDevices = localStorage.getItem('cachedDevices'); } catch { }; } - if (!devices || devices === '') { - devices = await getDevicesForApps(appHosts, Config.AppDNS, token); - localStorage.setItem('cachedDevices', JSON.stringify(devices)); - } - - this.setState({ devices }); - - for (const appId in this.state.devices) { - if (filteredApps.indexOf(appId) === -1) { continue; } - const propDevices: any = this.state.devices[appId]; - const length = propDevices.length; - const newState = Object.assign({}, this.state.devices); - for (let i = 0; i < length; i++) { - const props = await getDeviceProperty(appId, Config.AppDNS, propDevices[i].id, token); - newState[appId][i]['__properties'] = props; - } + if (!overrideCache && Config.cacheDevices && cachedDevices !== null && cachedDevices !== '') { + const newState: any = JSON.parse(cachedDevices); this.setState({ devices: newState }); + return; } + + const devices: any = await getDevicesForApps(apps, Config.AppDNS, token); + + setTimeout(async () => { + for (const appId in devices) { + const newState = Object.assign({}, this.state.devices); + const propDevices: any = newState[appId]; + const length = propDevices.length; + for (let i = 0; i < length; i++) { + const props = await getDeviceProperty(appId, Config.AppDNS, propDevices[i].id, token); + newState[appId][i]['__properties'] = props; + } + localStorage.setItem('cachedDevices', JSON.stringify(newState)); + this.setState({ devices: newState }); + } + }, 2000); + + // fetch the list of devices first + this.setState({ devices }); } sendCommand = async (host: string, id: string, name: string, body: any, token: string) => { diff --git a/src/hooks/useDevices.ts b/src/hooks/useDevices.ts index 7a5b205..8e50425 100644 --- a/src/hooks/useDevices.ts +++ b/src/hooks/useDevices.ts @@ -18,7 +18,7 @@ const useDevices = (override: boolean) => { setError(null); try { const token = await authContext.getCentralAccessToken(); - await dataContext.getDevices(authContext.filteredApps, token, authContext.filteredApps, override); + await dataContext.getDevices(authContext.centralApps, token, override); setData({}); } catch (error) { setError(error); diff --git a/src/shared/appbar.tsx b/src/shared/appbar.tsx index 587b8ce..f825b1b 100644 --- a/src/shared/appbar.tsx +++ b/src/shared/appbar.tsx @@ -6,6 +6,7 @@ export default function AppBar({ authContext }) { const dom: any = []; for (let i in authContext.activeSubscription.apps) { const app = authContext.activeSubscription.apps[i]; + if (authContext.centralApps.indexOf(app.properties.applicationId) === -1) { continue; } const checked = authContext.filteredApps.indexOf(app.properties.applicationId) > -1; dom.push(
{app.properties.displayName}