@app-connect/core 1.7.8 → 1.7.10

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/lib/oauth.js CHANGED
@@ -117,8 +117,6 @@ async function checkAndRefreshAccessToken(oauthApp, user, tokenLockTimeout = 20)
117
117
  }
118
118
  catch (e) {
119
119
  console.log('token refreshing failed', e.stack)
120
- }
121
- finally {
122
120
  if (newLock) {
123
121
  await newLock.delete();
124
122
  }
@@ -0,0 +1,34 @@
1
+ const Sequelize = require('sequelize');
2
+ const { sequelize } = require('./sequelize');
3
+
4
+ // Model for account data with composite primary key
5
+ exports.AccountDataModel = sequelize.define('accountData', {
6
+ rcAccountId: {
7
+ type: Sequelize.STRING,
8
+ primaryKey: true,
9
+ },
10
+ platformName: {
11
+ type: Sequelize.STRING,
12
+ primaryKey: true,
13
+ },
14
+ dataKey: {
15
+ type: Sequelize.STRING,
16
+ primaryKey: true,
17
+ },
18
+ data: {
19
+ type: Sequelize.JSON,
20
+ }
21
+ });
22
+
23
+ exports.getOrRefreshAccountData = async function getOrRefreshAccountData({ rcAccountId, platformName, dataKey, forceRefresh, fetchFn }) {
24
+ const existing = await exports.AccountDataModel.findOne({ where: { rcAccountId, platformName, dataKey } });
25
+ if (existing && !forceRefresh) return existing.data;
26
+
27
+ const fresh = await fetchFn();
28
+ if (existing) {
29
+ await existing.update({ data: fresh });
30
+ } else {
31
+ await exports.AccountDataModel.create({ rcAccountId, platformName, dataKey, data: fresh });
32
+ }
33
+ return fresh;
34
+ }
package/package.json CHANGED
@@ -1,69 +1,70 @@
1
- {
2
- "name": "@app-connect/core",
3
- "version": "1.7.8",
4
- "description": "RingCentral App Connect Core",
5
- "main": "index.js",
6
- "repository": {
7
- "type": "git",
8
- "url": "git+https://github.com/ringcentral/rc-unified-crm-extension.git"
9
- },
10
- "keywords": [
11
- "RingCentral",
12
- "App Connect"
13
- ],
14
- "author": "RingCentral Labs",
15
- "license": "MIT",
16
- "peerDependencies": {
17
- "axios": "^1.12.2",
18
- "express": "^4.21.2",
19
- "moment": "^2.29.4",
20
- "moment-timezone": "^0.5.39",
21
- "pg": "^8.8.0",
22
- "sequelize": "^6.29.0"
23
- },
24
- "dependencies": {
25
- "@aws-sdk/client-dynamodb": "^3.751.0",
26
- "@aws-sdk/client-s3": "^3.947.0",
27
- "@aws-sdk/s3-request-presigner": "^3.947.0",
28
- "body-parser": "^1.20.3",
29
- "client-oauth2": "^4.3.3",
30
- "cors": "^2.8.5",
31
- "country-state-city": "^3.2.1",
32
- "dotenv": "^16.0.3",
33
- "dynamoose": "^4.0.3",
34
- "jsonwebtoken": "^9.0.0",
35
- "mixpanel": "^0.18.0",
36
- "shortid": "^2.2.17",
37
- "tz-lookup": "^6.1.25",
38
- "ua-parser-js": "^1.0.38"
39
- },
40
- "scripts": {
41
- "test": "jest",
42
- "test:watch": "jest --watch",
43
- "test:coverage": "jest --coverage",
44
- "test:ci": "jest --ci --coverage --watchAll=false"
45
- },
46
- "devDependencies": {
47
- "@eslint/js": "^9.22.0",
48
- "@octokit/rest": "^19.0.5",
49
- "axios": "^1.12.2",
50
- "eslint": "^9.22.0",
51
- "express": "^4.21.2",
52
- "globals": "^16.0.0",
53
- "jest": "^29.3.1",
54
- "moment": "^2.29.4",
55
- "moment-timezone": "^0.5.39",
56
- "nock": "^13.2.9",
57
- "pg": "^8.8.0",
58
- "sequelize": "^6.29.0",
59
- "sqlite3": "^5.1.2",
60
- "supertest": "^6.3.1"
61
- },
62
- "overrides": {
63
- "js-object-utilities": "2.2.1"
64
- },
65
- "bugs": {
66
- "url": "https://github.com/ringcentral/rc-unified-crm-extension/issues"
67
- },
68
- "homepage": "https://github.com/ringcentral/rc-unified-crm-extension#readme"
69
- }
1
+ {
2
+ "name": "@app-connect/core",
3
+ "version": "1.7.10",
4
+ "description": "RingCentral App Connect Core",
5
+ "main": "index.js",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/ringcentral/rc-unified-crm-extension.git"
9
+ },
10
+ "keywords": [
11
+ "RingCentral",
12
+ "App Connect"
13
+ ],
14
+ "author": "RingCentral Labs",
15
+ "license": "MIT",
16
+ "peerDependencies": {
17
+ "axios": "^1.12.2",
18
+ "express": "^4.22.1",
19
+ "moment": "^2.29.4",
20
+ "moment-timezone": "^0.5.39",
21
+ "pg": "^8.8.0",
22
+ "sequelize": "^6.29.0"
23
+ },
24
+ "dependencies": {
25
+ "@aws-sdk/client-dynamodb": "^3.751.0",
26
+ "@aws-sdk/client-s3": "^3.947.0",
27
+ "@aws-sdk/s3-request-presigner": "^3.947.0",
28
+ "body-parser": "^1.20.4",
29
+ "body-parser-xml": "^2.0.5",
30
+ "client-oauth2": "^4.3.3",
31
+ "cors": "^2.8.5",
32
+ "country-state-city": "^3.2.1",
33
+ "dotenv": "^16.0.3",
34
+ "dynamoose": "^4.0.3",
35
+ "jsonwebtoken": "^9.0.0",
36
+ "mixpanel": "^0.18.0",
37
+ "shortid": "^2.2.17",
38
+ "tz-lookup": "^6.1.25",
39
+ "ua-parser-js": "^1.0.38"
40
+ },
41
+ "scripts": {
42
+ "test": "jest",
43
+ "test:watch": "jest --watch",
44
+ "test:coverage": "jest --coverage",
45
+ "test:ci": "jest --ci --coverage --watchAll=false"
46
+ },
47
+ "devDependencies": {
48
+ "@eslint/js": "^9.22.0",
49
+ "@octokit/rest": "^19.0.5",
50
+ "axios": "^1.12.2",
51
+ "eslint": "^9.22.0",
52
+ "express": "^4.22.1",
53
+ "globals": "^16.0.0",
54
+ "jest": "^29.3.1",
55
+ "moment": "^2.29.4",
56
+ "moment-timezone": "^0.5.39",
57
+ "nock": "^13.2.9",
58
+ "pg": "^8.8.0",
59
+ "sequelize": "^6.29.0",
60
+ "sqlite3": "^5.1.2",
61
+ "supertest": "^6.3.1"
62
+ },
63
+ "overrides": {
64
+ "js-object-utilities": "2.2.1"
65
+ },
66
+ "bugs": {
67
+ "url": "https://github.com/ringcentral/rc-unified-crm-extension/issues"
68
+ },
69
+ "homepage": "https://github.com/ringcentral/rc-unified-crm-extension#readme"
70
+ }
package/releaseNotes.json CHANGED
@@ -1,4 +1,28 @@
1
1
  {
2
+ "1.7.10": {
3
+ "global": [
4
+ {
5
+ "type": "Fix",
6
+ "description": "Upon completing warm transfer, it opens contact page for a second time"
7
+ },
8
+ {
9
+ "type": "New",
10
+ "description": "RingCX RingSense call logging event support for Server-side logging"
11
+ }
12
+ ]
13
+ },
14
+ "1.7.9": {
15
+ "global": [
16
+ {
17
+ "type": "Better",
18
+ "description": "Contact match speed optimized"
19
+ },
20
+ {
21
+ "type": "Better",
22
+ "description": "Minor improvements on calldown list"
23
+ }
24
+ ]
25
+ },
2
26
  "1.7.8": {
3
27
  "global": [
4
28
  {
@@ -268,4 +268,149 @@ describe('ConnectorRegistry Interface Registration with Composition', () => {
268
268
  expect(composedConnector.getAuthType).toBeDefined();
269
269
  expect(await composedConnector.getAuthType()).toBe('apiKey');
270
270
  });
271
+
272
+ test('should set and get default manifest', () => {
273
+ const defaultManifest = {
274
+ name: 'Default CRM',
275
+ version: '1.0.0',
276
+ features: ['call_logging', 'contact_sync']
277
+ };
278
+
279
+ connectorRegistry.setDefaultManifest(defaultManifest);
280
+
281
+ // Get manifest with fallback should return default
282
+ const manifest = connectorRegistry.getManifest('nonExistentPlatform', true);
283
+ expect(manifest).toEqual(defaultManifest);
284
+ });
285
+
286
+ test('should throw error when getting manifest without fallback and platform not found', () => {
287
+ expect(() => {
288
+ connectorRegistry.getManifest('nonExistentPlatform', false);
289
+ }).toThrow('Manifest not found for platform: nonExistentPlatform');
290
+ });
291
+
292
+ test('should throw error when getting manifest with fallback but no default set', () => {
293
+ connectorRegistry.manifests.clear();
294
+ expect(() => {
295
+ connectorRegistry.getManifest('nonExistentPlatform', true);
296
+ }).toThrow('Manifest not found for platform: nonExistentPlatform');
297
+ });
298
+
299
+ test('should register connector with manifest', () => {
300
+ const mockConnector = {
301
+ getAuthType: () => 'apiKey',
302
+ createCallLog: jest.fn(),
303
+ updateCallLog: jest.fn()
304
+ };
305
+ const manifest = {
306
+ name: 'Test CRM',
307
+ version: '2.0.0',
308
+ authType: 'oauth'
309
+ };
310
+
311
+ connectorRegistry.registerConnector('testPlatformWithManifest', mockConnector, manifest);
312
+
313
+ const retrievedManifest = connectorRegistry.getManifest('testPlatformWithManifest');
314
+ expect(retrievedManifest).toEqual(manifest);
315
+ });
316
+
317
+ test('should set and get release notes', () => {
318
+ const releaseNotes = {
319
+ version: '1.5.0',
320
+ date: '2024-01-15',
321
+ changes: ['Bug fixes', 'New features']
322
+ };
323
+
324
+ connectorRegistry.setReleaseNotes(releaseNotes);
325
+
326
+ // getReleaseNotes currently returns the same object regardless of platform
327
+ const notes = connectorRegistry.getReleaseNotes('anyPlatform');
328
+ expect(notes).toEqual(releaseNotes);
329
+ });
330
+
331
+ test('should get registered platforms', () => {
332
+ const connector1 = {
333
+ getAuthType: () => 'apiKey',
334
+ createCallLog: jest.fn(),
335
+ updateCallLog: jest.fn()
336
+ };
337
+ const connector2 = {
338
+ getAuthType: () => 'oauth',
339
+ createCallLog: jest.fn(),
340
+ updateCallLog: jest.fn()
341
+ };
342
+
343
+ connectorRegistry.registerConnector('platform1', connector1);
344
+ connectorRegistry.registerConnector('platform2', connector2);
345
+
346
+ const platforms = connectorRegistry.getRegisteredPlatforms();
347
+ expect(platforms).toContain('platform1');
348
+ expect(platforms).toContain('platform2');
349
+ expect(platforms).toHaveLength(2);
350
+ });
351
+
352
+ test('should check if platform is registered', () => {
353
+ const mockConnector = {
354
+ getAuthType: () => 'apiKey',
355
+ createCallLog: jest.fn(),
356
+ updateCallLog: jest.fn()
357
+ };
358
+
359
+ connectorRegistry.registerConnector('registeredPlatform', mockConnector);
360
+
361
+ expect(connectorRegistry.isRegistered('registeredPlatform')).toBe(true);
362
+ expect(connectorRegistry.isRegistered('unregisteredPlatform')).toBe(false);
363
+ });
364
+
365
+ test('should throw error for original connector when not found', () => {
366
+ expect(() => {
367
+ connectorRegistry.getOriginalConnector('nonExistentPlatform');
368
+ }).toThrow('Connector not found for platform: nonExistentPlatform');
369
+ });
370
+
371
+ test('should validate connector interface with missing required methods', () => {
372
+ const incompleteConnector = {
373
+ getAuthType: () => 'apiKey',
374
+ createCallLog: jest.fn()
375
+ // Missing updateCallLog
376
+ };
377
+
378
+ expect(() => {
379
+ connectorRegistry.registerConnector('incompletePlatform', incompleteConnector);
380
+ }).toThrow('Connector incompletePlatform missing required method: updateCallLog');
381
+ });
382
+
383
+ test('should return proxy connector when platform not found but proxy exists', () => {
384
+ const proxyConnector = {
385
+ getAuthType: () => 'proxy',
386
+ createCallLog: jest.fn(),
387
+ updateCallLog: jest.fn(),
388
+ proxy: true
389
+ };
390
+
391
+ connectorRegistry.registerConnector('proxy', proxyConnector);
392
+
393
+ const connector = connectorRegistry.getConnector('unknownPlatformWithProxy');
394
+ expect(connector).toBe(proxyConnector);
395
+ expect(connector.proxy).toBe(true);
396
+ });
397
+
398
+ test('should handle getAuthType error in getConnectorCapabilities', async () => {
399
+ const mockConnector = {
400
+ getAuthType: jest.fn().mockRejectedValue(new Error('Auth type error')),
401
+ createCallLog: jest.fn(),
402
+ updateCallLog: jest.fn()
403
+ };
404
+
405
+ connectorRegistry.registerConnector('errorPlatform', mockConnector);
406
+
407
+ const capabilities = await connectorRegistry.getConnectorCapabilities('errorPlatform');
408
+ expect(capabilities.authType).toBe('unknown');
409
+ });
410
+
411
+ test('should handle unregistering non-existent interface gracefully', () => {
412
+ // This should not throw
413
+ connectorRegistry.unregisterConnectorInterface('nonExistentPlatform', 'nonExistentInterface');
414
+ expect(connectorRegistry.hasPlatformInterface('nonExistentPlatform', 'nonExistentInterface')).toBe(false);
415
+ });
271
416
  });