@webex/internal-plugin-avatar 3.0.0-beta.2 → 3.0.0-beta.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -3
- package/dist/avatar-url-batcher.js +5 -17
- package/dist/avatar-url-batcher.js.map +1 -1
- package/dist/avatar-url-store.js +4 -31
- package/dist/avatar-url-store.js.map +1 -1
- package/dist/avatar.js +8 -32
- package/dist/avatar.js.map +1 -1
- package/dist/config.js +0 -7
- package/dist/config.js.map +1 -1
- package/dist/index.js +1 -9
- package/dist/index.js.map +1 -1
- package/package.json +12 -12
- package/src/avatar-url-batcher.js +40 -31
- package/src/avatar-url-store.js +1 -1
- package/src/avatar.js +52 -41
- package/src/config.js +2 -2
- package/src/index.js +1 -1
- package/test/integration/spec/avatar.js +15 -14
- package/test/unit/spec/avatar-url-batcher.js +24 -15
- package/test/unit/spec/avatar-url-store.js +110 -58
- package/test/unit/spec/avatar.js +1453 -1217
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webex/internal-plugin-avatar",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.21",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Joe Fuhrman <jofuhrma@cisco.com>",
|
|
@@ -21,20 +21,20 @@
|
|
|
21
21
|
]
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@webex/test-helper-chai": "3.0.0-beta.
|
|
25
|
-
"@webex/test-helper-file": "3.0.0-beta.
|
|
26
|
-
"@webex/test-helper-mock-webex": "3.0.0-beta.
|
|
27
|
-
"@webex/test-helper-test-users": "3.0.0-beta.
|
|
24
|
+
"@webex/test-helper-chai": "3.0.0-beta.21",
|
|
25
|
+
"@webex/test-helper-file": "3.0.0-beta.21",
|
|
26
|
+
"@webex/test-helper-mock-webex": "3.0.0-beta.21",
|
|
27
|
+
"@webex/test-helper-test-users": "3.0.0-beta.21",
|
|
28
28
|
"sinon": "^9.2.4"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@webex/common": "3.0.0-beta.
|
|
32
|
-
"@webex/common-timers": "3.0.0-beta.
|
|
33
|
-
"@webex/helper-image": "3.0.0-beta.
|
|
34
|
-
"@webex/internal-plugin-avatar": "3.0.0-beta.
|
|
35
|
-
"@webex/internal-plugin-device": "3.0.0-beta.
|
|
36
|
-
"@webex/internal-plugin-user": "3.0.0-beta.
|
|
37
|
-
"@webex/webex-core": "3.0.0-beta.
|
|
31
|
+
"@webex/common": "3.0.0-beta.21",
|
|
32
|
+
"@webex/common-timers": "3.0.0-beta.21",
|
|
33
|
+
"@webex/helper-image": "3.0.0-beta.21",
|
|
34
|
+
"@webex/internal-plugin-avatar": "3.0.0-beta.21",
|
|
35
|
+
"@webex/internal-plugin-device": "3.0.0-beta.21",
|
|
36
|
+
"@webex/internal-plugin-user": "3.0.0-beta.21",
|
|
37
|
+
"@webex/webex-core": "3.0.0-beta.21",
|
|
38
38
|
"lodash": "^4.17.21"
|
|
39
39
|
}
|
|
40
40
|
}
|
|
@@ -10,17 +10,21 @@ const AvatarUrlBatcher = Batcher.extend({
|
|
|
10
10
|
|
|
11
11
|
handleHttpSuccess(res) {
|
|
12
12
|
// eslint-disable-next-line arrow-body-style
|
|
13
|
-
return Promise.all(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
13
|
+
return Promise.all(
|
|
14
|
+
res.options.body.map((req) => {
|
|
15
|
+
return Promise.all(
|
|
16
|
+
req.sizes.map((size) => {
|
|
17
|
+
const response = (res.body[req.uuid] && res.body[req.uuid][size]) || undefined;
|
|
18
|
+
|
|
19
|
+
return this.acceptItem({
|
|
20
|
+
response,
|
|
21
|
+
uuid: req.uuid,
|
|
22
|
+
size,
|
|
23
|
+
});
|
|
24
|
+
})
|
|
25
|
+
);
|
|
26
|
+
})
|
|
27
|
+
);
|
|
24
28
|
},
|
|
25
29
|
|
|
26
30
|
handleHttpError(reason) {
|
|
@@ -28,15 +32,21 @@ const AvatarUrlBatcher = Batcher.extend({
|
|
|
28
32
|
|
|
29
33
|
// avoid multiple => on same line
|
|
30
34
|
// eslint-disable-next-line arrow-body-style
|
|
31
|
-
return Promise.all(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
return Promise.all(
|
|
36
|
+
reason.options.body.map((item) => {
|
|
37
|
+
return Promise.all(
|
|
38
|
+
item.sizes.map((size) =>
|
|
39
|
+
this.getDeferredForRequest({
|
|
40
|
+
uuid: item.uuid,
|
|
41
|
+
size,
|
|
42
|
+
})
|
|
43
|
+
// I don't see a better way to do this than with an additional nesting
|
|
44
|
+
// eslint-disable-next-line max-nested-callbacks
|
|
45
|
+
.then((defer) => defer.reject(msg instanceof Error ? msg : new Error(msg)))
|
|
46
|
+
)
|
|
47
|
+
);
|
|
35
48
|
})
|
|
36
|
-
|
|
37
|
-
// eslint-disable-next-line max-nested-callbacks
|
|
38
|
-
.then((defer) => defer.reject(msg instanceof Error ? msg : new Error(msg)))));
|
|
39
|
-
}));
|
|
49
|
+
);
|
|
40
50
|
},
|
|
41
51
|
|
|
42
52
|
didItemFail(item) {
|
|
@@ -52,20 +62,20 @@ const AvatarUrlBatcher = Batcher.extend({
|
|
|
52
62
|
},
|
|
53
63
|
|
|
54
64
|
handleItemFailure(item) {
|
|
55
|
-
return this.getDeferredForRequest(item)
|
|
56
|
-
.
|
|
57
|
-
|
|
58
|
-
});
|
|
65
|
+
return this.getDeferredForRequest(item).then((defer) => {
|
|
66
|
+
defer.reject(new Error(item.response || 'Failed to retrieve avatar'));
|
|
67
|
+
});
|
|
59
68
|
},
|
|
60
69
|
|
|
61
70
|
handleItemSuccess(item) {
|
|
62
|
-
return this.getDeferredForResponse(item)
|
|
63
|
-
|
|
71
|
+
return this.getDeferredForResponse(item).then((defer) =>
|
|
72
|
+
defer.resolve({
|
|
64
73
|
hasDefaultAvatar: item.response.defaultAvatar,
|
|
65
74
|
uuid: item.uuid,
|
|
66
75
|
size: item.size,
|
|
67
|
-
url: item.response.url
|
|
68
|
-
})
|
|
76
|
+
url: item.response.url,
|
|
77
|
+
})
|
|
78
|
+
);
|
|
69
79
|
},
|
|
70
80
|
|
|
71
81
|
fingerprintRequest(item) {
|
|
@@ -94,7 +104,7 @@ const AvatarUrlBatcher = Batcher.extend({
|
|
|
94
104
|
map.forEach((value, key) => {
|
|
95
105
|
payload.push({
|
|
96
106
|
uuid: key,
|
|
97
|
-
sizes: uniq(value)
|
|
107
|
+
sizes: uniq(value),
|
|
98
108
|
});
|
|
99
109
|
});
|
|
100
110
|
|
|
@@ -106,10 +116,9 @@ const AvatarUrlBatcher = Batcher.extend({
|
|
|
106
116
|
method: 'POST',
|
|
107
117
|
api: 'avatar',
|
|
108
118
|
resource: 'profiles/urls',
|
|
109
|
-
body: payload
|
|
119
|
+
body: payload,
|
|
110
120
|
});
|
|
111
|
-
}
|
|
112
|
-
|
|
121
|
+
},
|
|
113
122
|
});
|
|
114
123
|
|
|
115
124
|
export default AvatarUrlBatcher;
|
package/src/avatar-url-store.js
CHANGED
|
@@ -102,7 +102,7 @@ export default class AvatarUrlStore {
|
|
|
102
102
|
* @returns {Promise<true>}
|
|
103
103
|
*/
|
|
104
104
|
remove(item) {
|
|
105
|
-
const sizes = item.size && [item.size] || [40, 50, 80, 110, 135, 192, 640, 1600];
|
|
105
|
+
const sizes = (item.size && [item.size]) || [40, 50, 80, 110, 135, 192, 640, 1600];
|
|
106
106
|
|
|
107
107
|
sizes.forEach((one) => urlByUuid.get(this).delete(`${item.uuid} - ${one}`));
|
|
108
108
|
|
package/src/avatar.js
CHANGED
|
@@ -14,7 +14,7 @@ const Avatar = WebexPlugin.extend({
|
|
|
14
14
|
namespace: 'Avatar',
|
|
15
15
|
|
|
16
16
|
children: {
|
|
17
|
-
batcher: AvatarUrlBatcher
|
|
17
|
+
batcher: AvatarUrlBatcher,
|
|
18
18
|
},
|
|
19
19
|
|
|
20
20
|
session: {
|
|
@@ -22,19 +22,24 @@ const Avatar = WebexPlugin.extend({
|
|
|
22
22
|
default() {
|
|
23
23
|
return new AvatarUrlStore();
|
|
24
24
|
},
|
|
25
|
-
type: 'any'
|
|
25
|
+
type: 'any',
|
|
26
26
|
},
|
|
27
27
|
enableThumbnails: {
|
|
28
28
|
default: true,
|
|
29
|
-
type: 'boolean'
|
|
30
|
-
}
|
|
29
|
+
type: 'boolean',
|
|
30
|
+
},
|
|
31
31
|
},
|
|
32
32
|
|
|
33
33
|
@oneFlight({keyFactory: (uuid) => uuid})
|
|
34
34
|
_fetchAllAvatarUrlSizes(uuid, options) {
|
|
35
35
|
// fetch all possible sizes of avatar and store in cache
|
|
36
|
-
return Promise.all(
|
|
37
|
-
.
|
|
36
|
+
return Promise.all(
|
|
37
|
+
this.config.sizes.map((size) =>
|
|
38
|
+
this.batcher
|
|
39
|
+
.request({uuid, size})
|
|
40
|
+
.then((item) => this.store.add(defaults({cacheControl: options.cacheControl}, item)))
|
|
41
|
+
)
|
|
42
|
+
);
|
|
38
43
|
},
|
|
39
44
|
|
|
40
45
|
/**
|
|
@@ -48,14 +53,17 @@ const Avatar = WebexPlugin.extend({
|
|
|
48
53
|
*/
|
|
49
54
|
@oneFlight({keyFactory: (uuid, options) => uuid + String(options && options.size)})
|
|
50
55
|
_fetchAvatarUrl(uuid, options) {
|
|
51
|
-
return this.store.get({uuid, size: options.size})
|
|
52
|
-
|
|
56
|
+
return this.store.get({uuid, size: options.size}).catch(() =>
|
|
57
|
+
Promise.all([
|
|
53
58
|
this._fetchAllAvatarUrlSizes(uuid, options),
|
|
54
59
|
// just in case options.size does not fall into the predefined values above
|
|
55
|
-
this.batcher.request({uuid, size: options.size})
|
|
60
|
+
this.batcher.request({uuid, size: options.size}),
|
|
56
61
|
])
|
|
57
62
|
// eslint-disable-next-line no-unused-vars
|
|
58
|
-
.then(([ignore, item]) =>
|
|
63
|
+
.then(([ignore, item]) =>
|
|
64
|
+
this.store.add(defaults({cacheControl: options.cacheControl}, item))
|
|
65
|
+
)
|
|
66
|
+
);
|
|
59
67
|
},
|
|
60
68
|
|
|
61
69
|
/**
|
|
@@ -70,15 +78,16 @@ const Avatar = WebexPlugin.extend({
|
|
|
70
78
|
*/
|
|
71
79
|
retrieveAvatarUrl(user, options) {
|
|
72
80
|
if (!user) {
|
|
73
|
-
return Promise.reject(new Error('
|
|
81
|
+
return Promise.reject(new Error("'user' is a required parameter"));
|
|
74
82
|
}
|
|
75
83
|
|
|
76
84
|
options = defaults(options, {
|
|
77
85
|
cacheControl: this.config.cacheControl,
|
|
78
|
-
size: this.config.defaultAvatarSize
|
|
86
|
+
size: this.config.defaultAvatarSize,
|
|
79
87
|
});
|
|
80
88
|
|
|
81
|
-
return this.webex.internal.user
|
|
89
|
+
return this.webex.internal.user
|
|
90
|
+
.asUUID(user)
|
|
82
91
|
.then((uuid) => this._fetchAvatarUrl(uuid, options))
|
|
83
92
|
.then((item) => {
|
|
84
93
|
if (options.hideDefaultAvatar) {
|
|
@@ -97,41 +106,43 @@ const Avatar = WebexPlugin.extend({
|
|
|
97
106
|
*/
|
|
98
107
|
setAvatar(file) {
|
|
99
108
|
return detectFileType(file, this.logger)
|
|
100
|
-
.then((type) =>
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
109
|
+
.then((type) =>
|
|
110
|
+
processImage({
|
|
111
|
+
file,
|
|
112
|
+
type,
|
|
113
|
+
thumbnailMaxWidth: this.config.thumbnailMaxWidth,
|
|
114
|
+
thumbnailMaxHeight: this.config.thumbnailMaxHeight,
|
|
115
|
+
enableThumbnails: this.enableThumbnails,
|
|
116
|
+
logger: this.logger,
|
|
117
|
+
isAvatar: true,
|
|
118
|
+
})
|
|
119
|
+
)
|
|
120
|
+
.then((processedImage) =>
|
|
121
|
+
this.upload({
|
|
122
|
+
api: 'avatar',
|
|
123
|
+
resource: `profile/${this.webex.internal.device.userId}/session`,
|
|
124
|
+
file: processedImage[0],
|
|
125
|
+
phases: {
|
|
126
|
+
upload: {
|
|
127
|
+
$uri: (session) => session.url,
|
|
128
|
+
},
|
|
129
|
+
finalize: {
|
|
130
|
+
method: 'PUT',
|
|
131
|
+
api: 'avatar',
|
|
132
|
+
$resource: (session) =>
|
|
133
|
+
// eslint-disable-next-line max-len
|
|
134
|
+
`profile/${this.webex.internal.device.userId}/session/${session.id}`,
|
|
135
|
+
},
|
|
116
136
|
},
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
api: 'avatar',
|
|
120
|
-
$resource: (session) =>
|
|
121
|
-
// eslint-disable-next-line max-len
|
|
122
|
-
`profile/${this.webex.internal.device.userId}/session/${session.id}`
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}))
|
|
137
|
+
})
|
|
138
|
+
)
|
|
126
139
|
.then((res) => {
|
|
127
140
|
// invalidate user's cached avatar
|
|
128
141
|
this.store.remove({uuid: this.webex.internal.device.userId});
|
|
129
142
|
|
|
130
143
|
return res.url;
|
|
131
144
|
});
|
|
132
|
-
}
|
|
133
|
-
|
|
145
|
+
},
|
|
134
146
|
});
|
|
135
147
|
|
|
136
|
-
|
|
137
148
|
export default Avatar;
|
package/src/config.js
CHANGED
package/src/index.js
CHANGED
|
@@ -13,34 +13,35 @@ import testUsers from '@webex/test-helper-test-users';
|
|
|
13
13
|
describe('plugin-avatar', () => {
|
|
14
14
|
let mccoy, webex, spock;
|
|
15
15
|
|
|
16
|
-
before('create users', () =>
|
|
17
|
-
.then((users) => {
|
|
16
|
+
before('create users', () =>
|
|
17
|
+
testUsers.create({count: 2}).then((users) => {
|
|
18
18
|
[spock, mccoy] = users;
|
|
19
19
|
|
|
20
20
|
webex = new WebexCore({
|
|
21
21
|
credentials: {
|
|
22
|
-
authorization: spock.token
|
|
23
|
-
}
|
|
22
|
+
authorization: spock.token,
|
|
23
|
+
},
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
mccoy.webex = new WebexCore({
|
|
27
27
|
credentials: {
|
|
28
|
-
authorization: mccoy.token
|
|
29
|
-
}
|
|
28
|
+
authorization: mccoy.token,
|
|
29
|
+
},
|
|
30
30
|
});
|
|
31
|
-
})
|
|
31
|
+
})
|
|
32
|
+
);
|
|
32
33
|
|
|
33
|
-
before('register with wdm', () =>
|
|
34
|
-
webex.internal.device.register(),
|
|
35
|
-
|
|
36
|
-
]));
|
|
34
|
+
before('register with wdm', () =>
|
|
35
|
+
Promise.all([webex.internal.device.register(), mccoy.webex.internal.device.register()])
|
|
36
|
+
);
|
|
37
37
|
|
|
38
38
|
let sampleImageSmallOnePng = 'sample-image-small-one.png';
|
|
39
39
|
|
|
40
|
-
before(() =>
|
|
41
|
-
.then((file) => {
|
|
40
|
+
before(() =>
|
|
41
|
+
fh.fetch(sampleImageSmallOnePng).then((file) => {
|
|
42
42
|
sampleImageSmallOnePng = file;
|
|
43
|
-
})
|
|
43
|
+
})
|
|
44
|
+
);
|
|
44
45
|
|
|
45
46
|
// describe('#setAvatar()', () => {
|
|
46
47
|
// it('sets a user\'s avatar', () => webex.internal.avatar.setAvatar(sampleImageSmallOnePng)
|
|
@@ -14,17 +14,21 @@ describe('plugin-avatar', () => {
|
|
|
14
14
|
beforeEach(() => {
|
|
15
15
|
webex = new MockWebex({
|
|
16
16
|
children: {
|
|
17
|
-
avatar: Avatar
|
|
18
|
-
}
|
|
17
|
+
avatar: Avatar,
|
|
18
|
+
},
|
|
19
19
|
});
|
|
20
20
|
batcher = webex.internal.avatar.batcher;
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
describe('#fingerprints', () => {
|
|
24
|
-
it(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
it("fingerprintRequest returns 'uuid-size'", () =>
|
|
25
|
+
batcher
|
|
26
|
+
.fingerprintRequest({uuid: 'uuid1', size: 80})
|
|
27
|
+
.then((result) => assert.deepEqual(result, 'uuid1-80')));
|
|
28
|
+
it("fingerprintResponse returns 'uuid-size'", () =>
|
|
29
|
+
batcher
|
|
30
|
+
.fingerprintRequest({uuid: 'uuid1', size: 80})
|
|
31
|
+
.then((result) => assert.deepEqual(result, 'uuid1-80')));
|
|
28
32
|
});
|
|
29
33
|
|
|
30
34
|
describe('#submitHttpRequest()', () => {
|
|
@@ -32,7 +36,7 @@ describe('plugin-avatar', () => {
|
|
|
32
36
|
method: 'POST',
|
|
33
37
|
api: 'avatar',
|
|
34
38
|
resource: 'profiles/urls',
|
|
35
|
-
body: 'foo'
|
|
39
|
+
body: 'foo',
|
|
36
40
|
};
|
|
37
41
|
|
|
38
42
|
it('calls webex.request with expected params', () => {
|
|
@@ -41,7 +45,8 @@ describe('plugin-avatar', () => {
|
|
|
41
45
|
};
|
|
42
46
|
|
|
43
47
|
// webex.requestPromise = Promise.resolve(mockRequest);
|
|
44
|
-
return batcher
|
|
48
|
+
return batcher
|
|
49
|
+
.submitHttpRequest(mockRequest.body)
|
|
45
50
|
.then((req) => assert.deepEqual(req, mockRequest));
|
|
46
51
|
});
|
|
47
52
|
});
|
|
@@ -54,7 +59,7 @@ describe('plugin-avatar', () => {
|
|
|
54
59
|
warn = batcher.logger.warn;
|
|
55
60
|
loggerWarned = false;
|
|
56
61
|
batcher.logger.warn = (msg) => {
|
|
57
|
-
loggerWarned =
|
|
62
|
+
loggerWarned = msg === 'Avatar: substituted size "256" for "80"';
|
|
58
63
|
};
|
|
59
64
|
});
|
|
60
65
|
|
|
@@ -62,14 +67,18 @@ describe('plugin-avatar', () => {
|
|
|
62
67
|
batcher.logger.warn = warn;
|
|
63
68
|
});
|
|
64
69
|
|
|
65
|
-
it('returns true if no response in item', () =>
|
|
66
|
-
.then((res) => assert.isTrue(res)));
|
|
70
|
+
it('returns true if no response in item', () =>
|
|
71
|
+
batcher.didItemFail({}).then((res) => assert.isTrue(res)));
|
|
67
72
|
|
|
68
|
-
it('returns false, warns reqested size does not equal response size', () =>
|
|
69
|
-
|
|
73
|
+
it('returns false, warns reqested size does not equal response size', () =>
|
|
74
|
+
batcher
|
|
75
|
+
.didItemFail({size: 80, response: {size: 256}})
|
|
76
|
+
.then((res) => assert.isFalse(res && loggerWarned)));
|
|
70
77
|
|
|
71
|
-
it('returns false no warning', () =>
|
|
72
|
-
|
|
78
|
+
it('returns false no warning', () =>
|
|
79
|
+
batcher
|
|
80
|
+
.didItemFail({size: 80, response: {size: 80}})
|
|
81
|
+
.then((res) => assert.isFalse(res && !loggerWarned)));
|
|
73
82
|
});
|
|
74
83
|
});
|
|
75
84
|
});
|