Skip to content

Commit 34b50d5

Browse files
authored
fix: Fixed bug when making API calls from page handlers in installed apps (#205)
1 parent 9bbae29 commit 34b50d5

8 files changed

+93
-45
lines changed

Diff for: jest.config.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ module.exports = {
66
collectCoverageFrom: ['lib/**/*.js'],
77
coverageReporters: ['json', 'text'],
88
testEnvironment: 'node',
9-
testPathIgnorePatterns: ['test/data'],
9+
testPathIgnorePatterns: ['test/data', 'test/utilities'],
1010
testMatch: ['**/test/**/*.[jt]s?(x)'],
1111
setupFiles: ['<rootDir>/config/jest.setup.js']
1212
}

Diff for: lib/util/smart-app-context.d.ts

+2
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ export interface SmartAppContext {
8787
* Retrieve the tokens of the installed instance from the token store and return a new, authenticated
8888
* SmartAppContext. This method is typically used to allow API calls to be made from the handlers
8989
* of CONFIGURATION/PAGE lifecycle events.
90+
* @deprecated This method will be removed at some point after the platform has been changed to include
91+
* valid tokens in CONFIGURATION events as it does for all other lifecycle events.
9092
*/
9193
retrieveTokens(): Promise<SmartAppContext>
9294

Diff for: lib/util/smart-app-context.js

+8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict'
22

33
const i18n = require('i18n')
4+
const {Mutex} = require('async-mutex')
45
const {
56
SmartThingsClient,
67
BearerTokenAuthenticator,
@@ -146,6 +147,10 @@ module.exports = class SmartAppContext {
146147
}
147148
}
148149

150+
/**
151+
* @deprecated This method will be removed at some point after the platform has been changed to include
152+
* valid tokens in CONFIGURATION events as it does for all other lifecycle events.
153+
*/
149154
async retrieveTokens() {
150155
const {app} = this
151156
if (app._contextStore) {
@@ -154,11 +159,14 @@ module.exports = class SmartAppContext {
154159
this.locationId = data.locationId
155160
this.authToken = data.authToken
156161
this.refreshToken = data.refreshToken
162+
this.apiMutex = new Mutex()
163+
157164
const authenticator = new SequentialRefreshTokenAuthenticator(
158165
this.authToken,
159166
new TokenStore(this.installedAppId, app._contextStore, app._clientId, app._clientSecret),
160167
this.apiMutex
161168
)
169+
162170
const config = {
163171
locationId: this.locationId,
164172
installedAppId: this.installedAppId

Diff for: package-lock.json

+9-9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
},
3535
"homepage": "https://github.com/SmartThingsCommunity/smartapp-sdk-nodejs#readme",
3636
"dependencies": {
37-
"@smartthings/core-sdk": "^1.7.0",
37+
"@smartthings/core-sdk": "^1.9.0",
3838
"@types/aws-lambda": "^8.10.51",
3939
"@types/i18n": "^0.8.6",
4040
"async-mutex": "^0.1.4",

Diff for: test/unit/smartapp-context-spec.js

+1-34
Original file line numberDiff line numberDiff line change
@@ -4,40 +4,7 @@ const {
44
SequentialRefreshTokenAuthenticator} = require('@smartthings/core-sdk')
55
const SmartApp = require('../../lib/smart-app')
66
const SmartAppContext = require('../../lib/util/smart-app-context')
7-
8-
class ContextStore {
9-
constructor() {
10-
this.contexts = {}
11-
}
12-
13-
get(installedAppId) {
14-
return new Promise(resolve => {
15-
resolve(this.contexts[installedAppId])
16-
})
17-
}
18-
19-
put(params) {
20-
this.contexts[params.installedAppId] = params
21-
return new Promise(resolve => {
22-
resolve()
23-
})
24-
}
25-
26-
update(installedAppId, params) {
27-
this.contexts[params.installedAppId].authToken = params.authToken
28-
this.contexts[params.installedAppId].refreshToken = params.refreshToken
29-
return new Promise(resolve => {
30-
resolve()
31-
})
32-
}
33-
34-
delete(installedAppId) {
35-
this.contexts[installedAppId] = null
36-
return new Promise(resolve => {
37-
resolve()
38-
})
39-
}
40-
}
7+
const ContextStore = require('../utilities/context-store')
418

429
describe('smartapp-context-spec', () => {
4310
let app

Diff for: test/unit/smartapp-spec.js

+32
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const SmartApp = require('../../lib/smart-app')
2+
const ContextStore = require('../utilities/context-store')
23

34
describe('smartapp-spec', () => {
45
let app
@@ -66,4 +67,35 @@ describe('smartapp-spec', () => {
6667

6768
logSpy.mockClear()
6869
})
70+
71+
it('should construct mutext for page event', async () => {
72+
let mutex
73+
app.contextStore(new ContextStore())
74+
app.page('mainPage', (context, _) => {
75+
mutex = context.apiMutex
76+
})
77+
78+
const pageEvent = {
79+
lifecycle: 'CONFIGURATION',
80+
executionId: '00000000-0000-0000-0000-000000000000',
81+
locale: 'en',
82+
version: '0.1.0',
83+
client: {
84+
os: 'ios',
85+
version: '0.0.0',
86+
language: 'fr'
87+
},
88+
configurationData: {
89+
installedAppId: '00000000-0000-0000-0000-000000000000',
90+
phase: 'PAGE',
91+
pageId: 'mainPage',
92+
previousPageId: '',
93+
config: {}
94+
},
95+
settings: {}
96+
}
97+
98+
await expect(app.handleMockCallback(pageEvent)).resolves.not.toThrow()
99+
expect(mutex).toBeDefined()
100+
})
69101
})

Diff for: test/utilities/context-store.js

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
class ContextStore {
2+
constructor() {
3+
this.contexts = {
4+
'00000000-0000-0000-0000-000000000000': {
5+
locationId: 'e9a56178-3518-49f3-b944-a25ac941c3bd'
6+
}
7+
}
8+
}
9+
10+
get(installedAppId) {
11+
return new Promise(resolve => {
12+
resolve(this.contexts[installedAppId])
13+
})
14+
}
15+
16+
put(params) {
17+
this.contexts[params.installedAppId] = params
18+
return new Promise(resolve => {
19+
resolve()
20+
})
21+
}
22+
23+
update(installedAppId, params) {
24+
this.contexts[params.installedAppId].authToken = params.authToken
25+
this.contexts[params.installedAppId].refreshToken = params.refreshToken
26+
return new Promise(resolve => {
27+
resolve()
28+
})
29+
}
30+
31+
delete(installedAppId) {
32+
this.contexts[installedAppId] = null
33+
return new Promise(resolve => {
34+
resolve()
35+
})
36+
}
37+
}
38+
39+
module.exports = ContextStore

0 commit comments

Comments
 (0)