@app-connect/core 1.7.27 → 1.7.30

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/handlers/auth.js CHANGED
@@ -34,6 +34,8 @@ async function onOAuthCallback({ platform, hostname, tokenUrl, query, hashedRcEx
34
34
  if (!oauthInfo) {
35
35
  oauthInfo = await platformModule.getOauthInfo({ tokenUrl, hostname, rcAccountId: query.rcAccountId, proxyId, proxyConfig, userEmail, isFromMCP });
36
36
  }
37
+ const resolvedHostname = oauthInfo?.hostname ?? hostname;
38
+ const resolvedTokenUrl = oauthInfo?.accessTokenUri ?? tokenUrl;
37
39
  if (oauthInfo.failMessage) {
38
40
  return {
39
41
  userInfo: null,
@@ -53,7 +55,7 @@ async function onOAuthCallback({ platform, hostname, tokenUrl, query, hashedRcEx
53
55
  const oauthApp = oauth.getOAuthApp(oauthInfo);
54
56
  const { accessToken, refreshToken, expires, data } = await oauthApp.code.getToken(callbackUri, overridingOAuthOption);
55
57
  const authHeader = `Bearer ${accessToken}`;
56
- const { successful, platformUserInfo, returnMessage } = await platformModule.getUserInfo({ authHeader, tokenUrl, apiUrl, hostname, platform, username, callbackUri, query, proxyId, proxyConfig, userEmail, data });
58
+ const { successful, platformUserInfo, returnMessage } = await platformModule.getUserInfo({ authHeader, tokenUrl: resolvedTokenUrl, apiUrl, hostname: resolvedHostname, platform, username, callbackUri, query, proxyId, proxyConfig, userEmail, data });
57
59
 
58
60
  if (successful) {
59
61
  let userInfo = null;
@@ -61,10 +63,10 @@ async function onOAuthCallback({ platform, hostname, tokenUrl, query, hashedRcEx
61
63
  userInfo = await saveUserInfo({
62
64
  platformUserInfo,
63
65
  platform,
64
- tokenUrl,
66
+ tokenUrl: resolvedTokenUrl,
65
67
  apiUrl,
66
68
  username,
67
- hostname: platformUserInfo?.overridingHostname ? platformUserInfo.overridingHostname : hostname,
69
+ hostname: platformUserInfo?.overridingHostname ? platformUserInfo.overridingHostname : resolvedHostname,
68
70
  accessToken,
69
71
  refreshToken,
70
72
  tokenExpiry: isNaN(expires) ? null : expires,
package/index.js CHANGED
@@ -3,7 +3,6 @@ const cors = require('cors')
3
3
  const bodyParser = require('body-parser');
4
4
  require('body-parser-xml')(bodyParser);
5
5
  const dynamoose = require('dynamoose');
6
- const Sequelize = require('sequelize');
7
6
  const { DynamoDB } = require('@aws-sdk/client-dynamodb');
8
7
  const axios = require('axios');
9
8
  const { UserModel } = require('./models/userModel');
@@ -36,10 +35,6 @@ const s3ErrorLogReport = require('./lib/s3ErrorLogReport');
36
35
  const pluginCore = require('./handlers/plugin');
37
36
  const { handleDatabaseError } = require('./lib/errorHandler');
38
37
  const { updateAuthSession } = require('./lib/authSession');
39
- const {
40
- migrateCallLogsExtensionNumberSqlite,
41
- sqliteCallLogsPkIncludesExtension,
42
- } = require('./lib/migrateCallLogsSchema');
43
38
  const managedAuthCore = require('./handlers/managedAuth');
44
39
  const managedOAuthCore = require('./handlers/managedOAuth');
45
40
 
@@ -1,21 +1,60 @@
1
1
  const { Op } = require('sequelize');
2
2
  const { CacheModel } = require('../models/cacheModel');
3
+ const { AccountDataModel } = require('../models/accountDataModel');
3
4
  const logger = require('./logger');
4
5
 
6
+ const ACCOUNT_CONTACT_DATA_KEY_PATTERN = 'contact-%';
7
+ const ACCOUNT_CONTACT_DATA_RETENTION_MONTHS = 3;
8
+
5
9
  async function clearExpiredCache({ now = new Date() } = {}) {
6
- const deletedCount = await CacheModel.destroy({
10
+ const cacheDeletedCount = await clearExpiredCacheRows({ now });
11
+ const accountContactDataDeletedCount = await clearExpiredAccountContactData({ now });
12
+ const deletedCount = cacheDeletedCount + accountContactDataDeletedCount;
13
+
14
+ logger.info('Expired cache cleanup completed', {
15
+ deletedCount,
16
+ cacheDeletedCount,
17
+ accountContactDataDeletedCount
18
+ });
19
+
20
+ return deletedCount;
21
+ }
22
+
23
+ function getAccountContactDataCutoffDate({ now, retentionMonths }) {
24
+ const cutoffDate = new Date(now.getTime());
25
+ cutoffDate.setMonth(cutoffDate.getMonth() - retentionMonths);
26
+ return cutoffDate;
27
+ }
28
+
29
+ async function clearExpiredCacheRows({ now }) {
30
+ return CacheModel.destroy({
7
31
  where: {
8
32
  expiry: {
9
33
  [Op.lte]: now
10
34
  }
11
35
  }
12
36
  });
37
+ }
13
38
 
14
- logger.info('Expired cache cleanup completed', { deletedCount });
39
+ async function clearExpiredAccountContactData({
40
+ now = new Date(),
41
+ retentionMonths = ACCOUNT_CONTACT_DATA_RETENTION_MONTHS
42
+ } = {}) {
43
+ const cutoffDate = getAccountContactDataCutoffDate({ now, retentionMonths });
15
44
 
16
- return deletedCount;
45
+ return AccountDataModel.destroy({
46
+ where: {
47
+ dataKey: {
48
+ [Op.like]: ACCOUNT_CONTACT_DATA_KEY_PATTERN
49
+ },
50
+ createdAt: {
51
+ [Op.lt]: cutoffDate
52
+ }
53
+ }
54
+ });
17
55
  }
18
56
 
19
57
  module.exports = {
58
+ clearExpiredAccountContactData,
20
59
  clearExpiredCache
21
60
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@app-connect/core",
3
- "version": "1.7.27",
3
+ "version": "1.7.30",
4
4
  "description": "RingCentral App Connect Core",
5
5
  "main": "index.js",
6
6
  "repository": {
package/releaseNotes.json CHANGED
@@ -1,4 +1,24 @@
1
1
  {
2
+ "1.7.30": {
3
+ "global": [
4
+ {
5
+ "type": "Fix",
6
+ "description": "Shared SMS for sites"
7
+ },
8
+ {
9
+ "type": "Fix",
10
+ "description": "Conditional rendering of contact association fields"
11
+ }
12
+ ]
13
+ },
14
+ "1.7.29": {
15
+ "global": [
16
+ {
17
+ "type": "New",
18
+ "description": "Conditional rendering of logging form fields by contact type"
19
+ }
20
+ ]
21
+ },
2
22
  "1.7.27": {
3
23
  "global": [
4
24
  {
@@ -1,9 +1,11 @@
1
1
  const { CacheModel } = require('../../models/cacheModel');
2
- const { clearExpiredCache } = require('../../lib/cacheCleanup');
2
+ const { AccountDataModel } = require('../../models/accountDataModel');
3
+ const { clearExpiredAccountContactData, clearExpiredCache } = require('../../lib/cacheCleanup');
3
4
 
4
5
  describe('cacheCleanup', () => {
5
6
  beforeEach(async () => {
6
7
  await CacheModel.destroy({ where: {} });
8
+ await AccountDataModel.destroy({ where: {} });
7
9
  });
8
10
 
9
11
  test('deletes only cache records whose expiry has passed', async () => {
@@ -39,4 +41,87 @@ describe('cacheCleanup', () => {
39
41
  expect(await CacheModel.findByPk('active-cache')).not.toBeNull();
40
42
  expect(await CacheModel.findByPk('cache-without-expiry')).not.toBeNull();
41
43
  });
44
+
45
+ test('deletes only account contact data older than three months', async () => {
46
+ const now = new Date('2026-06-08T00:00:00.000Z');
47
+
48
+ await AccountDataModel.bulkCreate([
49
+ {
50
+ rcAccountId: 'account-1',
51
+ platformName: 'test-platform',
52
+ dataKey: 'contact-+1111111111',
53
+ data: [{ id: 'old-contact' }],
54
+ createdAt: new Date('2026-03-07T23:59:59.000Z'),
55
+ updatedAt: new Date('2026-03-07T23:59:59.000Z')
56
+ },
57
+ {
58
+ rcAccountId: 'account-1',
59
+ platformName: 'test-platform',
60
+ dataKey: 'contact-+2222222222',
61
+ data: [{ id: 'exact-cutoff-contact' }],
62
+ createdAt: new Date('2026-03-08T00:00:00.000Z'),
63
+ updatedAt: new Date('2026-03-08T00:00:00.000Z')
64
+ },
65
+ {
66
+ rcAccountId: 'account-1',
67
+ platformName: 'test-platform',
68
+ dataKey: 'contact-+3333333333',
69
+ data: [{ id: 'active-contact' }],
70
+ createdAt: new Date('2026-03-08T00:00:01.000Z'),
71
+ updatedAt: new Date('2026-03-08T00:00:01.000Z')
72
+ },
73
+ {
74
+ rcAccountId: 'account-1',
75
+ platformName: 'test-platform',
76
+ dataKey: 'pluginData',
77
+ data: { jwtToken: 'plugin-token' },
78
+ createdAt: new Date('2026-03-07T23:59:59.000Z'),
79
+ updatedAt: new Date('2026-03-07T23:59:59.000Z')
80
+ }
81
+ ]);
82
+
83
+ const deletedCount = await clearExpiredAccountContactData({ now });
84
+
85
+ expect(deletedCount).toBe(1);
86
+ expect(await AccountDataModel.findOne({
87
+ where: { rcAccountId: 'account-1', platformName: 'test-platform', dataKey: 'contact-+1111111111' }
88
+ })).toBeNull();
89
+ expect(await AccountDataModel.findOne({
90
+ where: { rcAccountId: 'account-1', platformName: 'test-platform', dataKey: 'contact-+2222222222' }
91
+ })).not.toBeNull();
92
+ expect(await AccountDataModel.findOne({
93
+ where: { rcAccountId: 'account-1', platformName: 'test-platform', dataKey: 'contact-+3333333333' }
94
+ })).not.toBeNull();
95
+ expect(await AccountDataModel.findOne({
96
+ where: { rcAccountId: 'account-1', platformName: 'test-platform', dataKey: 'pluginData' }
97
+ })).not.toBeNull();
98
+ });
99
+
100
+ test('clearExpiredCache includes expired account contact data cleanup', async () => {
101
+ const now = new Date('2026-06-08T00:00:00.000Z');
102
+
103
+ await CacheModel.create({
104
+ id: 'expired-cache',
105
+ userId: 'user-1',
106
+ cacheKey: 'auth-session',
107
+ status: 'expired',
108
+ expiry: new Date('2026-06-07T23:59:59.000Z')
109
+ });
110
+ await AccountDataModel.create({
111
+ rcAccountId: 'account-1',
112
+ platformName: 'test-platform',
113
+ dataKey: 'contact-+1111111111',
114
+ data: [{ id: 'old-contact' }],
115
+ createdAt: new Date('2026-03-07T23:59:59.000Z'),
116
+ updatedAt: new Date('2026-03-07T23:59:59.000Z')
117
+ });
118
+
119
+ const deletedCount = await clearExpiredCache({ now });
120
+
121
+ expect(deletedCount).toBe(2);
122
+ expect(await CacheModel.findByPk('expired-cache')).toBeNull();
123
+ expect(await AccountDataModel.findOne({
124
+ where: { rcAccountId: 'account-1', platformName: 'test-platform', dataKey: 'contact-+1111111111' }
125
+ })).toBeNull();
126
+ });
42
127
  });