@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 +5 -3
- package/index.js +0 -5
- package/lib/cacheCleanup.js +42 -3
- package/package.json +1 -1
- package/releaseNotes.json +20 -0
- package/test/lib/cacheCleanup.test.js +86 -1
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 :
|
|
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
|
|
package/lib/cacheCleanup.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
|
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
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 {
|
|
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
|
});
|