Skip to content

Commit 623ba7e

Browse files
Merge branch 'main' into shweaver/mgt-spinner
2 parents 6261f58 + ef299a4 commit 623ba7e

File tree

8 files changed

+127
-10
lines changed

8 files changed

+127
-10
lines changed

.vscode/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@
1010
"semibold",
1111
"xmlns"
1212
],
13-
"tslint.configFile": "./config/tslint.json"
13+
"tslint.configFile": "./config/tslint.json"
1414
}

packages/mgt-element/src/IBatch.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ export class BatchResponse {
3838
* @memberof BatchResponse
3939
*/
4040
public content: any;
41+
/**
42+
* The header of the response
43+
* @type {*}
44+
* @memberof BatchResponse
45+
*/
46+
public headers: string[];
4147
}
4248

4349
/**

packages/mgt/src/components/mgt-login/mgt-login.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@ export class MgtLogin extends MgtTemplatedComponent {
178178
if (this.userDetails.personImage) {
179179
this._image = this.userDetails.personImage;
180180
}
181+
182+
this.fireCustomEvent('loginCompleted');
181183
} else {
182184
this.userDetails = null;
183185
}

packages/mgt/src/graph/graph.photos.ts

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const cacheSchema: CacheSchema = {
2929
/**
3030
* photo object stored in cache
3131
*/
32-
interface CachePhoto extends CacheItem {
32+
export interface CachePhoto extends CacheItem {
3333
/**
3434
* user tag associated with photo
3535
*/
@@ -79,8 +79,7 @@ export async function getPhotoForResource(graph: IGraph, resource: string, scope
7979
if (!response.ok) {
8080
return null;
8181
}
82-
83-
const eTag = response.headers.get('ETag');
82+
const eTag = response.headers.get('eTag');
8483
const blob = await blobToBase64(await response.blob());
8584
return { eTag, photo: blob };
8685
} catch (e) {
@@ -121,15 +120,32 @@ export async function getContactPhoto(graph: IGraph, contactId: string): Promise
121120
export async function getUserPhoto(graph: IGraph, userId: string): Promise<string> {
122121
let cache: CacheStore<CachePhoto>;
123122
let photoDetails: CachePhoto;
123+
124124
if (photosCacheEnabled()) {
125125
cache = CacheService.getCache<CachePhoto>(cacheSchema, userStore);
126126
photoDetails = await cache.getValue(userId);
127127
if (photoDetails && getPhotoInvalidationTime() > Date.now() - photoDetails.timeCached) {
128128
return photoDetails.photo;
129+
} else if (photoDetails) {
130+
// there is a photo in the cache, but it's stale
131+
try {
132+
const response = await graph.api(`users/${userId}/photo`).get();
133+
if (
134+
response &&
135+
(response['@odata.mediaEtag'] !== photoDetails.eTag ||
136+
(response['@odata.mediaEtag'] === null && response.eTag === null))
137+
) {
138+
// set photoDetails to null so that photo gets pulled from the graph later
139+
photoDetails = null;
140+
}
141+
} catch {
142+
return null;
143+
}
129144
}
130145
}
131146

132-
photoDetails = await getPhotoForResource(graph, `users/${userId}`, ['user.readbasic.all']);
147+
// if there is a photo in the cache, we got here because it was stale
148+
photoDetails = photoDetails || (await getPhotoForResource(graph, `users/${userId}`, ['user.readbasic.all']));
133149
if (photosCacheEnabled() && photoDetails) {
134150
cache.putValue(userId, photoDetails);
135151
}
@@ -152,7 +168,20 @@ export async function myPhoto(graph: IGraph): Promise<string> {
152168
}
153169
}
154170

155-
photoDetails = await getPhotoForResource(graph, 'me', ['user.read']);
171+
try {
172+
const response = await graph.api(`me/photo`).get();
173+
if (
174+
response &&
175+
(response['@odata.mediaEtag'] !== photoDetails.eTag ||
176+
(response['@odata.mediaEtag'] === null && response.eTag === null))
177+
) {
178+
photoDetails = null;
179+
}
180+
} catch {
181+
return null;
182+
}
183+
184+
photoDetails = photoDetails || (await getPhotoForResource(graph, 'me', ['user.read']));
156185
if (photosCacheEnabled()) {
157186
cache.putValue('me', photoDetails || {});
158187
}

packages/mgt/src/graph/graph.user.ts

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ import {
1515
getPhotoFromCache,
1616
getPhotoInvalidationTime,
1717
photosCacheEnabled,
18-
storePhotoInCache
18+
storePhotoInCache,
19+
CachePhoto
1920
} from './graph.photos';
2021
import { IDynamicPerson } from './types';
2122

@@ -114,6 +115,10 @@ export async function getUserWithPhoto(graph: IGraph, userId?: string): Promise<
114115
let cache: CacheStore<CacheUser>;
115116
let photo = null;
116117
let user: IDynamicPerson;
118+
let cachedPhoto: CachePhoto;
119+
const resource = userId ? `users/${userId}` : 'me';
120+
const scopes = userId ? ['user.readbasic.all'] : ['user.read'];
121+
117122
// attempt to get user and photo from cache if enabled
118123
if (usersCacheEnabled()) {
119124
cache = CacheService.getCache<CacheUser>(cacheSchema, userStore);
@@ -123,11 +128,21 @@ export async function getUserWithPhoto(graph: IGraph, userId?: string): Promise<
123128
}
124129
}
125130
if (photosCacheEnabled()) {
126-
const cachedPhoto = await getPhotoFromCache(userId || 'me', 'users');
131+
cachedPhoto = await getPhotoFromCache(userId || 'me', 'users');
127132
if (cachedPhoto && getPhotoInvalidationTime() > Date.now() - cachedPhoto.timeCached) {
128133
photo = cachedPhoto.photo;
134+
} else if (cachedPhoto) {
135+
try {
136+
const response = await graph.api(`${resource}/photo`).get();
137+
if (response && response['@odata.mediaEtag'] && response['@odata.mediaEtag'] === cachedPhoto.eTag) {
138+
// put current image into the cache to update the timestamp since etag is the same
139+
storePhotoInCache(userId || 'me', 'users', cachedPhoto);
140+
photo = cachedPhoto.photo;
141+
}
142+
} catch (e) {}
129143
}
130144
}
145+
131146
if (!photo && !user) {
132147
// batch calls
133148
const batch = graph.createBatch();
@@ -139,8 +154,17 @@ export async function getUserWithPhoto(graph: IGraph, userId?: string): Promise<
139154
batch.get('photo', 'me/photo/$value', ['user.read']);
140155
}
141156
const response = await batch.executeAll();
157+
const eTag = response.get('photo').headers['ETag'];
142158
photo = response.get('photo').content;
143159
user = response.get('user').content;
160+
161+
// store user & photo in their respective cache
162+
if (usersCacheEnabled()) {
163+
cache.putValue(userId || 'me', { user: JSON.stringify(user) });
164+
}
165+
if (photosCacheEnabled()) {
166+
storePhotoInCache(userId || 'me', 'users', { eTag, photo: photo });
167+
}
144168
} else if (!photo) {
145169
// get photo from graph
146170
const resource = userId ? `users/${userId}` : 'me';
@@ -165,9 +189,9 @@ export async function getUserWithPhoto(graph: IGraph, userId?: string): Promise<
165189
.get();
166190
if (response) {
167191
if (usersCacheEnabled()) {
168-
cache.putValue(userId || 'me', { user: JSON.stringify(response.content) });
192+
cache.putValue(userId || 'me', { user: JSON.stringify(response) });
169193
}
170-
user = response.content;
194+
user = response;
171195
}
172196
}
173197
person = user;

packages/mgt/src/utils/Batch.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ export class Batch implements IBatch {
164164

165165
response.id = request.id;
166166
response.index = request.index;
167+
response.headers = r.headers;
167168

168169
if (r.status !== 200) {
169170
if (r.status === 429) {

samples/examples/login-popup.html

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<!DOCTYPE html>
2+
<html>
3+
4+
<head>
5+
<script type="module" src="../../packages/mgt/dist/es6/index.js"></script>
6+
</head>
7+
8+
<body>
9+
<!-- <mgt-msal-provider client-id="af4ad92c-e141-49ac-9d86-23af45007101"></mgt-msal-provider> -->
10+
11+
<mgt-login></mgt-login>
12+
13+
<div id="loginStatus" style="padding-top: 20px">
14+
</div>
15+
16+
<script type="module">
17+
import { Providers, ProviderState, MsalProvider, LoginType } from '../../packages/mgt/dist/es6/index.js';
18+
19+
Providers.globalProvider = new MsalProvider({
20+
clientId: 'af4ad92c-e141-49ac-9d86-23af45007101',
21+
loginType: LoginType.Popup
22+
});
23+
24+
document.querySelector('mgt-login').addEventListener('loginCompleted', e => {
25+
console.log(e);
26+
document.getElementById('loginStatus').innerText = "Login completed successfully";
27+
});
28+
</script>
29+
30+
</html>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<!DOCTYPE html>
2+
<html>
3+
4+
<head>
5+
<script type="module" src="../../packages/mgt/dist/es6/index.js"></script>
6+
</head>
7+
8+
<body>
9+
<mgt-msal-provider client-id="af4ad92c-e141-49ac-9d86-23af45007101"></mgt-msal-provider>
10+
11+
<mgt-login></mgt-login>
12+
13+
<div id="loginStatus" style="padding-top: 20px">
14+
</div>
15+
16+
<script type="module">
17+
import { Providers, ProviderState } from '../../packages/mgt/dist/es6/index.js';
18+
19+
document.querySelector('mgt-login').addEventListener('loginCompleted', e => {
20+
console.log(e);
21+
document.getElementById('loginStatus').innerText = "Login completed successfully";
22+
});
23+
</script>
24+
25+
</html>

0 commit comments

Comments
 (0)