Skip to content

Commit

Permalink
Merge branch 'webex:next' into poc/plugin-encryption
Browse files Browse the repository at this point in the history
  • Loading branch information
bhabalan authored Dec 6, 2024
2 parents 3494db7 + d545259 commit 3d6aec2
Show file tree
Hide file tree
Showing 39 changed files with 2,005 additions and 782 deletions.
2 changes: 1 addition & 1 deletion docs/samples/browser-plugin-meetings/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ function collectMeetings() {

createMeetingSelectElm.addEventListener('change', (event) => {
if (event.target.value === 'CONVERSATION_URL') {
createMeetingActionElm.innerText = 'Create Adhoc Meeting';
createMeetingActionElm.innerText = 'Create Adhoc Meeting using conversation URL (INTERNAL-USE ONLY)';
}
else {
createMeetingActionElm.innerText = 'Create Meeting';
Expand Down
4 changes: 2 additions & 2 deletions docs/samples/calling/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ <h2 class="collapsible">Authentication</h2>
<legend>Advanced Settings</legend>

<p class="text-color" style="margin-top: 0; padding: 0;">
Following options allow to set the type of registration, service domain (e.g. cisco.webex.com), server region (e.g. east) and the country (e.g. us).
Following options allow to set the type of registration, service domain (only needed for contactcenter - rtw.prod-us1.rtmsprod.net ), server region (e.g. US-EAST) and the country (e.g. US).
<br><br>
<strong>Note:</strong> Please update these before <mark>Initialize Calling</mark> if want to use different values.
<strong>Note:</strong> Please set these fields before <mark>Initialize Calling</mark> to customize the registration behavior.
</p>

<div class="u-mv">
Expand Down
77 changes: 71 additions & 6 deletions packages/@webex/internal-plugin-device/src/device.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import {deprecated, oneFlight} from '@webex/common';
import {persist, waitForValue, WebexPlugin} from '@webex/webex-core';
import {safeSetTimeout} from '@webex/common-timers';
import {orderBy} from 'lodash';

import METRICS from './metrics';
import {FEATURE_COLLECTION_NAMES, DEVICE_EVENT_REGISTRATION_SUCCESS} from './constants';
Expand Down Expand Up @@ -439,6 +440,74 @@ const Device = WebexPlugin.extend({
});
});
},
/**
* Fetches the web devices and deletes the third of them which are not recent devices in use
* @returns {Promise<void, Error>}
*/
deleteDevices() {
// Fetch devices with a GET request
return this.request({
method: 'GET',
service: 'wdm',
resource: 'devices',
})
.then((response) => {
const {devices} = response.body;

const {deviceType} = this._getBody();

// Filter devices of type deviceType
const webDevices = devices.filter((item) => item.deviceType === deviceType);

const sortedDevices = orderBy(webDevices, [(item) => new Date(item.modificationTime)]);

// If there are more than two devices, delete the last third
if (sortedDevices.length > 2) {
const totalItems = sortedDevices.length;
const countToDelete = Math.ceil(totalItems / 3);
const urlsToDelete = sortedDevices.slice(0, countToDelete).map((item) => item.url);

return Promise.race(
urlsToDelete.map((url) => {
return this.request({
uri: url,
method: 'DELETE',
});
})
);
}

return Promise.resolve();
})
.catch((error) => {
this.logger.error('Failed to retrieve devices:', error);

return Promise.reject(error);
});
},

/**
* Registers and when fails deletes devices
*/
@oneFlight
@waitForValue('@')
register(deviceRegistrationOptions = {}) {
return this._registerInternal(deviceRegistrationOptions).catch((error) => {
if (error?.body?.message === 'User has excessive device registrations') {
return this.deleteDevices().then(() => {
return this._registerInternal(deviceRegistrationOptions);
});
}
throw error;
});
},

_getBody() {
return {
...(this.config.defaults.body ? this.config.defaults.body : {}),
...(this.config.body ? this.config.body : {}),
};
},

/**
* Register or refresh a device depending on the current device state. Device
Expand All @@ -451,7 +520,7 @@ const Device = WebexPlugin.extend({
*/
@oneFlight
@waitForValue('@')
register(deviceRegistrationOptions = {}) {
_registerInternal(deviceRegistrationOptions = {}) {
this.logger.info('device: registering');

this.webex.internal.newMetrics.callDiagnosticMetrics.setDeviceInfo(this);
Expand All @@ -466,10 +535,7 @@ const Device = WebexPlugin.extend({
}

// Merge body configurations, overriding defaults.
const body = {
...(this.config.defaults.body ? this.config.defaults.body : {}),
...(this.config.body ? this.config.body : {}),
};
const body = this._getBody();

// Merge header configurations, overriding defaults.
const headers = {
Expand Down Expand Up @@ -527,7 +593,6 @@ const Device = WebexPlugin.extend({
});
});
},

/**
* Unregister the current registered device if available. Unregistering a
* device utilizes the services plugin to send the request to the **WDM**
Expand Down
96 changes: 96 additions & 0 deletions packages/@webex/internal-plugin-device/test/unit/spec/device.js
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,68 @@ describe('plugin-device', () => {
});
});

describe('deleteDevices()', () => {
const setup = (deviceType) => {
device.config.defaults = {body: {deviceType}};
};
['WEB', 'WEBCLIENT'].forEach(deviceType => {
it(`should delete correct number of devices for ${deviceType}`, async () => {
setup(deviceType);
const response = {
body: {
devices: [
{url: 'url3', modificationTime: '2023-10-03T10:00:00Z', deviceType},
{url: 'url4', modificationTime: '2023-10-04T10:00:00Z', deviceType: 'notweb'},
{url: 'url1', modificationTime: '2023-10-01T10:00:00Z', deviceType},
{url: 'url2', modificationTime: '2023-10-02T10:00:00Z', deviceType},
{url: 'url5', modificationTime: '2023-10-00T10:00:00Z', deviceType},
{url: 'url6', modificationTime: '2023-09-50T10:00:00Z', deviceType},
{url: 'url7', modificationTime: '2023-09-30T10:00:00Z', deviceType},
{url: 'url8', modificationTime: '2023-08-30T10:00:00Z', deviceType},
]
}
};
const requestStub = sinon.stub(device, 'request');
requestStub.withArgs(sinon.match({method: 'GET'})).resolves(response);
requestStub.withArgs(sinon.match({method: 'DELETE'})).resolves();

await device.deleteDevices();

const expectedDeletions = ['url8', 'url7', 'url1'];

expectedDeletions.forEach(url => {
assert(requestStub.calledWith(sinon.match({uri: url, method: 'DELETE'})));
});

const notDeletedUrls = ['url2', 'url3', 'url5', 'url6', 'url4'];
notDeletedUrls.forEach(url => {
assert(requestStub.neverCalledWith(sinon.match({uri: url, method: 'DELETE'})));
});
});});

it('does not delete when there are just 2 devices', async () => {
setup('WEB');
const response = {
body: {
devices: [
{url: 'url1', modificationTime: '2023-10-01T10:00:00Z', deviceType: 'WEB'},
{url: 'url2', modificationTime: '2023-10-02T10:00:00Z', deviceType: 'WEB'},
]
}
};

const requestStub = sinon.stub(device, 'request');
requestStub.withArgs(sinon.match({method: 'GET'})).resolves(response);
requestStub.withArgs(sinon.match({method: 'DELETE'})).resolves();

await device.deleteDevices();
const notDeletedUrls = ['url1', 'url2'];
notDeletedUrls.forEach(url => {
assert(requestStub.neverCalledWith(sinon.match({uri: url, method: 'DELETE'})));
});
});
});

describe('#register()', () => {
const setup = (config = {}) => {
webex.internal.metrics.submitClientMetrics = sinon.stub();
Expand Down Expand Up @@ -386,6 +448,40 @@ describe('plugin-device', () => {
});
});

it('calls delete devices when errors with User has excessive device registrations', async () => {
setup();
const deleteDeviceSpy = sinon.stub(device, 'deleteDevices').callsFake(() => Promise.resolve());
const registerStub = sinon.stub(device, '_registerInternal');

registerStub.onFirstCall().rejects({body: {message: 'User has excessive device registrations'}});
registerStub.onSecondCall().callsFake(() => Promise.resolve({exampleKey: 'example response value',}));

const result = await device.register();

assert.calledOnce(deleteDeviceSpy);

assert.equal(registerStub.callCount, 2);

assert.deepEqual(result, {exampleKey: 'example response value'});
});

it('does not call delete devices when some other error', async () => {
setup();

const deleteDeviceSpy = sinon.stub(device, 'deleteDevices').callsFake(() => Promise.resolve());
const registerStub = sinon.stub(device, '_registerInternal').rejects(new Error('some error'));

try {
await device.register({deleteFlag: true});
} catch (error) {
assert.notCalled(deleteDeviceSpy);

assert.equal(registerStub.callCount, 1);

assert.match(error.message, /some error/, 'Expected error message not matched');
}
});

it('checks that submitInternalEvent gets called with internal.register.device.response on error', async () => {
setup();
sinon.stub(device, 'canRegister').callsFake(() => Promise.resolve());
Expand Down
1 change: 1 addition & 0 deletions packages/@webex/internal-plugin-support/src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ export default {
appType: '',
appVersion: '',
languageCode: '',
incrementalLogs: false,
},
};
4 changes: 4 additions & 0 deletions packages/@webex/internal-plugin-support/src/support.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ const Support = WebexPlugin.extend({
return this.webex.upload(options);
})
.then((body) => {
if (this.config.incrementalLogs) {
this.webex.logger.clearBuffers();
}

if (userId && !body.userId) {
body.userId = userId;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/@webex/media-helpers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"deploy:npm": "yarn npm publish"
},
"dependencies": {
"@webex/internal-media-core": "2.11.3",
"@webex/internal-media-core": "2.12.2",
"@webex/ts-events": "^1.1.0",
"@webex/web-media-effects": "2.19.0"
},
Expand Down
Loading

0 comments on commit 3d6aec2

Please sign in to comment.