sharetribe-flex-sdk 1.14.0
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/.circleci/config.yml +22 -0
- package/.eslintignore +3 -0
- package/.eslintrc.js +19 -0
- package/CHANGELOG.md +231 -0
- package/LICENSE +201 -0
- package/README.md +76 -0
- package/build/sharetribe-flex-sdk-node.js +13592 -0
- package/build/sharetribe-flex-sdk-web.js +1 -0
- package/docs/README.md +20 -0
- package/docs/authentication.md +179 -0
- package/docs/calling-the-api.md +217 -0
- package/docs/configurations.md +95 -0
- package/docs/developing-sdk.md +131 -0
- package/docs/docpress.json +14 -0
- package/docs/features.md +14 -0
- package/docs/keep-alive.md +48 -0
- package/docs/object-query-parameters.md +36 -0
- package/docs/scripts.js +41 -0
- package/docs/serializing-types-to-json.md +40 -0
- package/docs/sharing-session-between-client-and-server.md +19 -0
- package/docs/styles.css +95 -0
- package/docs/token-store.md +114 -0
- package/docs/try-it-in-browser.md +32 -0
- package/docs/try-it-in-the-playground.md +153 -0
- package/docs/types.md +27 -0
- package/docs/writing-your-own-token-store.md +29 -0
- package/docs/your-own-types.md +61 -0
- package/examples/README.md +5 -0
- package/examples/getting-started-browser/README.md +22 -0
- package/examples/getting-started-browser/index.html +89 -0
- package/examples/getting-started-browser/index.js +156 -0
- package/examples/getting-started-browser/screenshots/screenshot1.png +0 -0
- package/examples/getting-started-browser/screenshots/screenshot2.png +0 -0
- package/examples/getting-started-node/README.md +23 -0
- package/examples/getting-started-node/index.js +139 -0
- package/examples/getting-started-node/screenshots/screenshot.png +0 -0
- package/package.json +83 -0
- package/playground.js +295 -0
- package/src/browser_cookie_store.js +26 -0
- package/src/context_runner.js +151 -0
- package/src/context_runner.test.js +185 -0
- package/src/detect.js +11 -0
- package/src/express_cookie_store.js +57 -0
- package/src/fake/adapter.js +130 -0
- package/src/fake/api.js +137 -0
- package/src/fake/auth.js +84 -0
- package/src/fake/token_store.js +231 -0
- package/src/index.js +25 -0
- package/src/interceptors/.eslintrc.js +5 -0
- package/src/interceptors/add_auth_header.js +32 -0
- package/src/interceptors/add_auth_header.test.js +50 -0
- package/src/interceptors/add_auth_token_response.js +16 -0
- package/src/interceptors/add_client_id_to_params.js +12 -0
- package/src/interceptors/add_client_secret_to_params.js +15 -0
- package/src/interceptors/add_grant_type_to_params.js +23 -0
- package/src/interceptors/add_idp_client_id_to_params.js +17 -0
- package/src/interceptors/add_idp_id_to_params.js +17 -0
- package/src/interceptors/add_idp_token_to_params.js +17 -0
- package/src/interceptors/add_scope_to_params.js +18 -0
- package/src/interceptors/add_subject_token_to_params.js +18 -0
- package/src/interceptors/add_token_exchange_grant_type_to_params.js +12 -0
- package/src/interceptors/auth_info.js +50 -0
- package/src/interceptors/clear_token_after_revoke.js +45 -0
- package/src/interceptors/default_params.js +12 -0
- package/src/interceptors/fetch_auth_token_from_api.js +33 -0
- package/src/interceptors/fetch_auth_token_from_store.js +27 -0
- package/src/interceptors/fetch_refresh_token_for_revoke.js +24 -0
- package/src/interceptors/multipart_request.js +35 -0
- package/src/interceptors/retry_with_anon_token.js +58 -0
- package/src/interceptors/retry_with_refresh_token.js +70 -0
- package/src/interceptors/save_token.js +20 -0
- package/src/interceptors/transit_request.js +27 -0
- package/src/interceptors/transit_request.test.js +58 -0
- package/src/interceptors/transit_response.js +27 -0
- package/src/memory_store.js +19 -0
- package/src/params_serializer.js +65 -0
- package/src/params_serializer.test.js +58 -0
- package/src/sdk.js +894 -0
- package/src/sdk.test.js +908 -0
- package/src/serializer.js +279 -0
- package/src/serializer.test.js +229 -0
- package/src/token_store.js +15 -0
- package/src/types.js +108 -0
- package/src/types.test.js +75 -0
- package/src/utils.js +68 -0
- package/src/utils.test.js +85 -0
- package/webpack.config.babel.js +47 -0
package/src/sdk.test.js
ADDED
|
@@ -0,0 +1,908 @@
|
|
|
1
|
+
/* eslint camelcase: "off" */
|
|
2
|
+
import _ from 'lodash';
|
|
3
|
+
import { UUID, LatLng } from './types';
|
|
4
|
+
import createAdapter from './fake/adapter';
|
|
5
|
+
import SharetribeSdk from './sdk';
|
|
6
|
+
import memoryStore from './memory_store';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
Helper to improve error messages.
|
|
10
|
+
|
|
11
|
+
Includes the `response` in the error message if
|
|
12
|
+
`response` exists.
|
|
13
|
+
*/
|
|
14
|
+
const report = responsePromise =>
|
|
15
|
+
responsePromise.catch(error => {
|
|
16
|
+
if (error.response) {
|
|
17
|
+
// eslint-disable-next-line no-param-reassign
|
|
18
|
+
error.message = `${error.message}. Response: ${JSON.stringify(error.response)}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
throw error;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
Helper to create SDK instance for tests with default configurations.
|
|
26
|
+
|
|
27
|
+
Pass additional configurations in `config` param to override defaults.
|
|
28
|
+
|
|
29
|
+
Returns a map that contains all the instances that might be useful for
|
|
30
|
+
tests, i.e. sdk, sdkTokenStore and adapter.
|
|
31
|
+
*/
|
|
32
|
+
const createSdk = (config = {}) => {
|
|
33
|
+
const defaults = {
|
|
34
|
+
clientId: '08ec69f6-d37e-414d-83eb-324e94afddf0',
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Extract adapter and token store here so that they can be passed to SDK
|
|
38
|
+
// constructor and included in the returned object
|
|
39
|
+
const { adapter: configAdapter, tokenStore: configTokenStore, ...restConfig } = config;
|
|
40
|
+
const adapter = configAdapter || createAdapter();
|
|
41
|
+
const sdkTokenStore = configTokenStore || memoryStore();
|
|
42
|
+
|
|
43
|
+
const sdk = new SharetribeSdk({
|
|
44
|
+
...defaults,
|
|
45
|
+
...restConfig,
|
|
46
|
+
tokenStore: sdkTokenStore,
|
|
47
|
+
adapter: adapter.adapterFn,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
sdkTokenStore,
|
|
52
|
+
adapter,
|
|
53
|
+
sdk,
|
|
54
|
+
adapterTokenStore: adapter.tokenStore,
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
describe('new SharetribeSdk', () => {
|
|
59
|
+
it('validates presence of clientId', () => {
|
|
60
|
+
expect(() => new SharetribeSdk()).toThrowError('clientId must be provided');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('validates presence of baseUrl', () => {
|
|
64
|
+
expect(
|
|
65
|
+
() =>
|
|
66
|
+
new SharetribeSdk({
|
|
67
|
+
clientId: '08ec69f6-d37e-414d-83eb-324e94afddf0',
|
|
68
|
+
baseUrl: null,
|
|
69
|
+
})
|
|
70
|
+
).toThrowError('baseUrl must be provided');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('uses default baseUrl, if none is set', () => {
|
|
74
|
+
const adapter = createAdapter((config, resolve) => {
|
|
75
|
+
// Fake adapter that echoes the URL
|
|
76
|
+
resolve({ data: { baseURL: config.baseURL } });
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const sdk = new SharetribeSdk({
|
|
80
|
+
clientId: '08ec69f6-d37e-414d-83eb-324e94afddf0',
|
|
81
|
+
adapter: adapter.adapterFn,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return sdk.login().then(res => {
|
|
85
|
+
expect(res.data.baseURL).toMatch(/^https:\/\/flex-api.sharetribe.com/);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('calls users endpoint with query params', () => {
|
|
90
|
+
const { sdk } = createSdk();
|
|
91
|
+
|
|
92
|
+
return report(
|
|
93
|
+
sdk.users.show({ id: '0e0b60fe-d9a2-11e6-bf26-cec0c932ce01' }).then(res => {
|
|
94
|
+
const resource = res.data.data;
|
|
95
|
+
const attrs = resource.attributes;
|
|
96
|
+
|
|
97
|
+
expect(resource.id).toEqual(new UUID('0e0b60fe-d9a2-11e6-bf26-cec0c932ce01'));
|
|
98
|
+
expect(attrs).toEqual(
|
|
99
|
+
expect.objectContaining({
|
|
100
|
+
email: 'user@sharetribe.com',
|
|
101
|
+
description: 'A team member',
|
|
102
|
+
})
|
|
103
|
+
);
|
|
104
|
+
})
|
|
105
|
+
);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('calls marketplace endpoint with query params', () => {
|
|
109
|
+
const { sdk } = createSdk();
|
|
110
|
+
|
|
111
|
+
return sdk.marketplace.show({ id: '0e0b60fe-d9a2-11e6-bf26-cec0c932ce01' }).then(res => {
|
|
112
|
+
const resource = res.data.data;
|
|
113
|
+
const attrs = resource.attributes;
|
|
114
|
+
|
|
115
|
+
expect(resource.id).toEqual(new UUID('0e0b60fe-d9a2-11e6-bf26-cec0c932ce01'));
|
|
116
|
+
expect(attrs).toEqual(
|
|
117
|
+
expect.objectContaining({
|
|
118
|
+
name: 'Awesome skies.',
|
|
119
|
+
description: 'Meet and greet with fanatical sky divers.',
|
|
120
|
+
})
|
|
121
|
+
);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('calls listing search with query params', () => {
|
|
126
|
+
const { sdk } = createSdk();
|
|
127
|
+
|
|
128
|
+
return sdk.listings
|
|
129
|
+
.search({
|
|
130
|
+
id: new UUID('0e0b60fe-d9a2-11e6-bf26-cec0c932ce01'),
|
|
131
|
+
origin: new LatLng(40.0, -70.0),
|
|
132
|
+
})
|
|
133
|
+
.then(res => {
|
|
134
|
+
const { data } = res.data;
|
|
135
|
+
|
|
136
|
+
expect(data).toHaveLength(2);
|
|
137
|
+
expect(data[0].attributes.description).toEqual('27-speed Hybrid. Fully functional.');
|
|
138
|
+
expect(data[0].attributes.geolocation instanceof LatLng).toEqual(true);
|
|
139
|
+
expect(data[0].attributes.geolocation).toEqual(new LatLng(40.64542, -74.08508));
|
|
140
|
+
expect(data[1].attributes.description).toEqual(
|
|
141
|
+
'Goes together perfectly with a latte and a bow tie.'
|
|
142
|
+
);
|
|
143
|
+
expect(data[1].attributes.geolocation instanceof LatLng).toEqual(true);
|
|
144
|
+
expect(data[1].attributes.geolocation).toEqual(new LatLng(40.64542, -74.08508));
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('allows user to pass custom read handlers', () => {
|
|
149
|
+
class MyUuid {
|
|
150
|
+
constructor(uuid) {
|
|
151
|
+
this.myUuid = uuid;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const handlers = [
|
|
156
|
+
{
|
|
157
|
+
sdkType: UUID,
|
|
158
|
+
appType: MyUuid,
|
|
159
|
+
reader: v => new MyUuid(v.uuid), // reader fn type: UUID -> MyUuid
|
|
160
|
+
},
|
|
161
|
+
];
|
|
162
|
+
|
|
163
|
+
const { sdk } = createSdk({
|
|
164
|
+
typeHandlers: handlers,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
return sdk.marketplace.show({ id: '0e0b60fe-d9a2-11e6-bf26-cec0c932ce01' }).then(res => {
|
|
168
|
+
const resource = res.data.data;
|
|
169
|
+
const attrs = resource.attributes;
|
|
170
|
+
|
|
171
|
+
expect(resource.id).toEqual(new MyUuid('0e0b60fe-d9a2-11e6-bf26-cec0c932ce01'));
|
|
172
|
+
expect(attrs).toEqual(
|
|
173
|
+
expect.objectContaining({
|
|
174
|
+
name: 'Awesome skies.',
|
|
175
|
+
description: 'Meet and greet with fanatical sky divers.',
|
|
176
|
+
})
|
|
177
|
+
);
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('[DEPRECATED, uses keys that are renamed] allows user to pass custom read handlers', () => {
|
|
182
|
+
class MyUuid {
|
|
183
|
+
constructor(uuid) {
|
|
184
|
+
this.myUuid = uuid;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const handlers = [
|
|
189
|
+
{
|
|
190
|
+
type: UUID,
|
|
191
|
+
customType: MyUuid,
|
|
192
|
+
reader: v => new MyUuid(v.uuid), // reader fn type: UUID -> MyUuid
|
|
193
|
+
},
|
|
194
|
+
];
|
|
195
|
+
|
|
196
|
+
const { sdk } = createSdk({
|
|
197
|
+
typeHandlers: handlers,
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
return sdk.marketplace.show({ id: '0e0b60fe-d9a2-11e6-bf26-cec0c932ce01' }).then(res => {
|
|
201
|
+
const resource = res.data.data;
|
|
202
|
+
const attrs = resource.attributes;
|
|
203
|
+
|
|
204
|
+
expect(resource.id).toEqual(new MyUuid('0e0b60fe-d9a2-11e6-bf26-cec0c932ce01'));
|
|
205
|
+
expect(attrs).toEqual(
|
|
206
|
+
expect.objectContaining({
|
|
207
|
+
name: 'Awesome skies.',
|
|
208
|
+
description: 'Meet and greet with fanatical sky divers.',
|
|
209
|
+
})
|
|
210
|
+
);
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('reads auth token from store and includes it in request headers', () => {
|
|
215
|
+
const { sdk, sdkTokenStore, adapterTokenStore } = createSdk({
|
|
216
|
+
// The Fake server doesn't know this clientId. However, the request passes because
|
|
217
|
+
// the access_token is in the store
|
|
218
|
+
clientId: 'daaf8871-4723-45b8-bc97-9e335f46966d',
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
const anonToken = adapterTokenStore.createAnonToken();
|
|
222
|
+
|
|
223
|
+
sdkTokenStore.setToken(anonToken);
|
|
224
|
+
|
|
225
|
+
return sdk.marketplace.show({ id: '0e0b60fe-d9a2-11e6-bf26-cec0c932ce01' }).then(res => {
|
|
226
|
+
const resource = res.data.data;
|
|
227
|
+
const attrs = resource.attributes;
|
|
228
|
+
|
|
229
|
+
expect(resource.id).toEqual(new UUID('0e0b60fe-d9a2-11e6-bf26-cec0c932ce01'));
|
|
230
|
+
expect(attrs).toEqual(
|
|
231
|
+
expect.objectContaining({
|
|
232
|
+
name: 'Awesome skies.',
|
|
233
|
+
description: 'Meet and greet with fanatical sky divers.',
|
|
234
|
+
})
|
|
235
|
+
);
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('stores the auth token to the store', () => {
|
|
240
|
+
const { sdk, sdkTokenStore } = createSdk();
|
|
241
|
+
|
|
242
|
+
return sdk.marketplace.show({ id: '0e0b60fe-d9a2-11e6-bf26-cec0c932ce01' }).then(res => {
|
|
243
|
+
const resource = res.data.data;
|
|
244
|
+
const attrs = resource.attributes;
|
|
245
|
+
const token = sdkTokenStore.getToken();
|
|
246
|
+
|
|
247
|
+
expect(resource.id).toEqual(new UUID('0e0b60fe-d9a2-11e6-bf26-cec0c932ce01'));
|
|
248
|
+
expect(attrs).toEqual(
|
|
249
|
+
expect.objectContaining({
|
|
250
|
+
name: 'Awesome skies.',
|
|
251
|
+
description: 'Meet and greet with fanatical sky divers.',
|
|
252
|
+
})
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
expect(token.access_token).toEqual('anonymous-access-1');
|
|
256
|
+
expect(token.token_type).toEqual('bearer');
|
|
257
|
+
expect(token.expires_in).toEqual(86400);
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it('stores auth token after login', () => {
|
|
262
|
+
const { sdk, sdkTokenStore } = createSdk();
|
|
263
|
+
|
|
264
|
+
// First we get the anonymous token
|
|
265
|
+
return report(
|
|
266
|
+
sdk.marketplace.show({ id: '0e0b60fe-d9a2-11e6-bf26-cec0c932ce01' }).then(() => {
|
|
267
|
+
expect(sdkTokenStore.getToken().access_token).toEqual('anonymous-access-1');
|
|
268
|
+
|
|
269
|
+
// After login, the anonymous token will be overriden
|
|
270
|
+
return sdk
|
|
271
|
+
.login({
|
|
272
|
+
username: 'joe.dunphy@example.com',
|
|
273
|
+
password: 'secret-joe',
|
|
274
|
+
})
|
|
275
|
+
.then(() => {
|
|
276
|
+
expect(sdkTokenStore.getToken().access_token).toEqual(
|
|
277
|
+
'joe.dunphy@example.com-access-1'
|
|
278
|
+
);
|
|
279
|
+
});
|
|
280
|
+
})
|
|
281
|
+
);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it('refreshes login token', () => {
|
|
285
|
+
const { sdk, sdkTokenStore, adapterTokenStore } = createSdk();
|
|
286
|
+
|
|
287
|
+
// First, login
|
|
288
|
+
return report(
|
|
289
|
+
sdk.login({ username: 'joe.dunphy@example.com', password: 'secret-joe' }).then(() => {
|
|
290
|
+
const { access_token } = sdkTokenStore.getToken();
|
|
291
|
+
expect(access_token).toEqual('joe.dunphy@example.com-access-1');
|
|
292
|
+
|
|
293
|
+
adapterTokenStore.expireAccessToken(access_token);
|
|
294
|
+
|
|
295
|
+
return sdk.marketplace.show({ id: '0e0b60fe-d9a2-11e6-bf26-cec0c932ce01' }).then(res => {
|
|
296
|
+
expect(sdkTokenStore.getToken().access_token).toEqual('joe.dunphy@example.com-access-2');
|
|
297
|
+
|
|
298
|
+
const resource = res.data.data;
|
|
299
|
+
const attrs = resource.attributes;
|
|
300
|
+
|
|
301
|
+
expect(resource.id).toEqual(new UUID('0e0b60fe-d9a2-11e6-bf26-cec0c932ce01'));
|
|
302
|
+
expect(attrs).toEqual(
|
|
303
|
+
expect.objectContaining({
|
|
304
|
+
name: 'Awesome skies.',
|
|
305
|
+
description: 'Meet and greet with fanatical sky divers.',
|
|
306
|
+
})
|
|
307
|
+
);
|
|
308
|
+
});
|
|
309
|
+
})
|
|
310
|
+
);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
it('refreshes anonymous token', () => {
|
|
314
|
+
const { sdk, sdkTokenStore, adapterTokenStore } = createSdk();
|
|
315
|
+
|
|
316
|
+
// First we get the anonymous token
|
|
317
|
+
return report(
|
|
318
|
+
sdk.marketplace.show({ id: '0e0b60fe-d9a2-11e6-bf26-cec0c932ce01' }).then(() => {
|
|
319
|
+
const { access_token } = sdkTokenStore.getToken();
|
|
320
|
+
expect(access_token).toEqual('anonymous-access-1');
|
|
321
|
+
|
|
322
|
+
adapterTokenStore.expireAccessToken(access_token);
|
|
323
|
+
|
|
324
|
+
return sdk.marketplace.show({ id: '0e0b60fe-d9a2-11e6-bf26-cec0c932ce01' }).then(res => {
|
|
325
|
+
expect(sdkTokenStore.getToken().access_token).toEqual('anonymous-access-2');
|
|
326
|
+
|
|
327
|
+
const resource = res.data.data;
|
|
328
|
+
const attrs = resource.attributes;
|
|
329
|
+
|
|
330
|
+
expect(resource.id).toEqual(new UUID('0e0b60fe-d9a2-11e6-bf26-cec0c932ce01'));
|
|
331
|
+
expect(attrs).toEqual(
|
|
332
|
+
expect.objectContaining({
|
|
333
|
+
name: 'Awesome skies.',
|
|
334
|
+
description: 'Meet and greet with fanatical sky divers.',
|
|
335
|
+
})
|
|
336
|
+
);
|
|
337
|
+
});
|
|
338
|
+
})
|
|
339
|
+
);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it('logs in with an authorization code', () => {
|
|
343
|
+
const { sdk, sdkTokenStore } = createSdk();
|
|
344
|
+
|
|
345
|
+
return sdk.login({ code: 'flex-authorization-code' }).then(() => {
|
|
346
|
+
const { access_token, refresh_token } = sdkTokenStore.getToken();
|
|
347
|
+
expect(access_token).toEqual('joe.dunphy@example.com-access-1');
|
|
348
|
+
expect(refresh_token).toEqual('joe.dunphy@example.com-refresh-1');
|
|
349
|
+
});
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it('logs in with idp token', () => {
|
|
353
|
+
const { sdk, sdkTokenStore } = createSdk({
|
|
354
|
+
clientSecret: '8af2bf99c380b3a303ab90ae4012c8cd8f69d309',
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
return sdk
|
|
358
|
+
.loginWithIdp({
|
|
359
|
+
idpId: 'facebook',
|
|
360
|
+
idpClientId: 'idp-client-id',
|
|
361
|
+
idpToken: 'idp-token',
|
|
362
|
+
})
|
|
363
|
+
.then(() => {
|
|
364
|
+
const { access_token, refresh_token } = sdkTokenStore.getToken();
|
|
365
|
+
expect(access_token).toEqual('joe.dunphy@example.com-access-1');
|
|
366
|
+
expect(refresh_token).toEqual('joe.dunphy@example.com-refresh-1');
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
it('revokes token (a.k.a logout)', () => {
|
|
371
|
+
const { sdk, sdkTokenStore } = createSdk();
|
|
372
|
+
|
|
373
|
+
// First, login
|
|
374
|
+
return report(
|
|
375
|
+
sdk.login({ username: 'joe.dunphy@example.com', password: 'secret-joe' }).then(() => {
|
|
376
|
+
expect(sdkTokenStore.getToken().access_token).toEqual('joe.dunphy@example.com-access-1');
|
|
377
|
+
|
|
378
|
+
// Revoke token
|
|
379
|
+
return sdk.logout().then(res => {
|
|
380
|
+
expect(res.data.action).toEqual('revoked');
|
|
381
|
+
|
|
382
|
+
expect(sdkTokenStore.getToken()).toEqual(null);
|
|
383
|
+
|
|
384
|
+
return sdk.marketplace.show({ id: '0e0b60fe-d9a2-11e6-bf26-cec0c932ce01' }).then(() => {
|
|
385
|
+
expect(sdkTokenStore.getToken().access_token).toEqual('anonymous-access-1');
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
})
|
|
389
|
+
);
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it('refreshes token before revoke', () => {
|
|
393
|
+
const { sdk, sdkTokenStore, adapterTokenStore } = createSdk();
|
|
394
|
+
|
|
395
|
+
// First, login
|
|
396
|
+
return report(
|
|
397
|
+
sdk.login({ username: 'joe.dunphy@example.com', password: 'secret-joe' }).then(() => {
|
|
398
|
+
const { access_token } = sdkTokenStore.getToken();
|
|
399
|
+
expect(access_token).toEqual('joe.dunphy@example.com-access-1');
|
|
400
|
+
|
|
401
|
+
adapterTokenStore.expireAccessToken(access_token);
|
|
402
|
+
|
|
403
|
+
// Revoke token
|
|
404
|
+
return sdk.logout().then(res => {
|
|
405
|
+
expect(res.data.action).toEqual('revoked');
|
|
406
|
+
|
|
407
|
+
expect(sdkTokenStore.getToken()).toEqual(null);
|
|
408
|
+
|
|
409
|
+
return sdk.marketplace.show({ id: '0e0b60fe-d9a2-11e6-bf26-cec0c932ce01' }).then(() => {
|
|
410
|
+
expect(sdkTokenStore.getToken().access_token).toEqual('anonymous-access-1');
|
|
411
|
+
});
|
|
412
|
+
});
|
|
413
|
+
})
|
|
414
|
+
);
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
it('refreshes token after unsuccessful revoke, but if the refresh fails because of 401, return OK.', () => {
|
|
418
|
+
const { sdk, sdkTokenStore, adapterTokenStore } = createSdk();
|
|
419
|
+
|
|
420
|
+
// First, login
|
|
421
|
+
return report(
|
|
422
|
+
sdk.login({ username: 'joe.dunphy@example.com', password: 'secret-joe' }).then(() => {
|
|
423
|
+
const { access_token, refresh_token } = sdkTokenStore.getToken();
|
|
424
|
+
expect(access_token).toEqual('joe.dunphy@example.com-access-1');
|
|
425
|
+
|
|
426
|
+
adapterTokenStore.expireAccessToken(access_token);
|
|
427
|
+
adapterTokenStore.revokeRefreshToken(refresh_token);
|
|
428
|
+
|
|
429
|
+
// Revoke token
|
|
430
|
+
return sdk.logout().then(() => {
|
|
431
|
+
expect(sdkTokenStore.getToken()).toEqual(null);
|
|
432
|
+
|
|
433
|
+
return sdk.marketplace.show({ id: '0e0b60fe-d9a2-11e6-bf26-cec0c932ce01' }).then(() => {
|
|
434
|
+
expect(sdkTokenStore.getToken().access_token).toEqual('anonymous-access-1');
|
|
435
|
+
});
|
|
436
|
+
});
|
|
437
|
+
})
|
|
438
|
+
);
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
it('refreshes token after unsuccessful revoke, but if the refresh fails because of network error, fail.', () => {
|
|
442
|
+
const { sdk, sdkTokenStore, adapterTokenStore, adapter } = createSdk();
|
|
443
|
+
|
|
444
|
+
// Two requests passes (login and first revoke try), but after that the server goes down
|
|
445
|
+
adapter.offlineAfter(2);
|
|
446
|
+
|
|
447
|
+
// First, login
|
|
448
|
+
return report(
|
|
449
|
+
sdk.login({ username: 'joe.dunphy@example.com', password: 'secret-joe' }).then(() => {
|
|
450
|
+
const { access_token, refresh_token } = sdkTokenStore.getToken();
|
|
451
|
+
expect(access_token).toEqual('joe.dunphy@example.com-access-1');
|
|
452
|
+
|
|
453
|
+
adapterTokenStore.expireAccessToken(access_token);
|
|
454
|
+
adapterTokenStore.revokeRefreshToken(refresh_token);
|
|
455
|
+
|
|
456
|
+
// Revoke token
|
|
457
|
+
return sdk
|
|
458
|
+
.logout()
|
|
459
|
+
.then(() => {
|
|
460
|
+
// Should not pass
|
|
461
|
+
expect(true).toEqual(false);
|
|
462
|
+
})
|
|
463
|
+
.catch(() => {
|
|
464
|
+
expect(sdkTokenStore.getToken().access_token).toEqual(access_token);
|
|
465
|
+
expect(sdkTokenStore.getToken().refresh_token).toEqual(refresh_token);
|
|
466
|
+
});
|
|
467
|
+
})
|
|
468
|
+
);
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
it('encodes new listing post body to Transit', () => {
|
|
472
|
+
const { sdk, adapter } = createSdk();
|
|
473
|
+
|
|
474
|
+
const testData = {
|
|
475
|
+
title: 'A new hope',
|
|
476
|
+
description: 'Our Nth listing!',
|
|
477
|
+
address: 'Bulevardi 14, Helsinki, Finland',
|
|
478
|
+
geolocation: new LatLng(10.152, 15.375),
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
const transitEncoded =
|
|
482
|
+
'["^ ","~:title","A new hope","~:description","Our Nth listing!","~:address","Bulevardi 14, Helsinki, Finland","~:geolocation",["~#geo",[10.152,15.375]]]';
|
|
483
|
+
|
|
484
|
+
return report(
|
|
485
|
+
sdk
|
|
486
|
+
.login({ username: 'joe.dunphy@example.com', password: 'secret-joe' })
|
|
487
|
+
.then(() => sdk.ownListings.create(testData))
|
|
488
|
+
.then(() => {
|
|
489
|
+
const req = _.last(adapter.requests);
|
|
490
|
+
expect(req.data).toEqual(transitEncoded);
|
|
491
|
+
expect(req.headers).toEqual(
|
|
492
|
+
expect.objectContaining({
|
|
493
|
+
'Content-Type': 'application/transit+json',
|
|
494
|
+
})
|
|
495
|
+
);
|
|
496
|
+
})
|
|
497
|
+
);
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
it('encodes new listing post body to Transit, using type appTypes', () => {
|
|
501
|
+
class MyLatLng {
|
|
502
|
+
constructor(lat, lng) {
|
|
503
|
+
this.val = [lat, lng];
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
const handlers = [
|
|
508
|
+
{
|
|
509
|
+
sdkType: LatLng,
|
|
510
|
+
appType: MyLatLng,
|
|
511
|
+
writer: v => new LatLng(v.val[0], v.val[1]),
|
|
512
|
+
},
|
|
513
|
+
];
|
|
514
|
+
|
|
515
|
+
const { sdk, adapter } = createSdk({ typeHandlers: handlers });
|
|
516
|
+
|
|
517
|
+
const testData = {
|
|
518
|
+
title: 'A new hope',
|
|
519
|
+
description: 'Our Nth listing!',
|
|
520
|
+
address: 'Bulevardi 14, Helsinki, Finland',
|
|
521
|
+
geolocation: new MyLatLng(10.152, 15.375),
|
|
522
|
+
};
|
|
523
|
+
|
|
524
|
+
const transitEncoded =
|
|
525
|
+
'["^ ","~:title","A new hope","~:description","Our Nth listing!","~:address","Bulevardi 14, Helsinki, Finland","~:geolocation",["~#geo",[10.152,15.375]]]';
|
|
526
|
+
|
|
527
|
+
return report(
|
|
528
|
+
sdk
|
|
529
|
+
.login({ username: 'joe.dunphy@example.com', password: 'secret-joe' })
|
|
530
|
+
.then(() => sdk.ownListings.create(testData))
|
|
531
|
+
.then(() => {
|
|
532
|
+
const req = _.last(adapter.requests);
|
|
533
|
+
expect(req.data).toEqual(transitEncoded);
|
|
534
|
+
expect(req.headers).toEqual(
|
|
535
|
+
expect.objectContaining({
|
|
536
|
+
'Content-Type': 'application/transit+json',
|
|
537
|
+
})
|
|
538
|
+
);
|
|
539
|
+
})
|
|
540
|
+
);
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
it('encodes new listing post body to Transit, using canHandle fn', () => {
|
|
544
|
+
const handlers = [
|
|
545
|
+
{
|
|
546
|
+
sdkType: LatLng,
|
|
547
|
+
canHandle: v => v[0] === '__my_lat_lng_type',
|
|
548
|
+
writer: v => new LatLng(v[1], v[2]),
|
|
549
|
+
},
|
|
550
|
+
];
|
|
551
|
+
|
|
552
|
+
const { sdk, adapter } = createSdk({ typeHandlers: handlers });
|
|
553
|
+
|
|
554
|
+
const testData = {
|
|
555
|
+
title: 'A new hope',
|
|
556
|
+
description: 'Our Nth listing!',
|
|
557
|
+
address: 'Bulevardi 14, Helsinki, Finland',
|
|
558
|
+
geolocation: ['__my_lat_lng_type', 10.152, 15.375],
|
|
559
|
+
};
|
|
560
|
+
|
|
561
|
+
const transitEncoded =
|
|
562
|
+
'["^ ","~:title","A new hope","~:description","Our Nth listing!","~:address","Bulevardi 14, Helsinki, Finland","~:geolocation",["~#geo",[10.152,15.375]]]';
|
|
563
|
+
|
|
564
|
+
return report(
|
|
565
|
+
sdk
|
|
566
|
+
.login({ username: 'joe.dunphy@example.com', password: 'secret-joe' })
|
|
567
|
+
.then(() => sdk.ownListings.create(testData))
|
|
568
|
+
.then(() => {
|
|
569
|
+
const req = _.last(adapter.requests);
|
|
570
|
+
expect(req.data).toEqual(transitEncoded);
|
|
571
|
+
expect(req.headers).toEqual(
|
|
572
|
+
expect.objectContaining({
|
|
573
|
+
'Content-Type': 'application/transit+json',
|
|
574
|
+
})
|
|
575
|
+
);
|
|
576
|
+
})
|
|
577
|
+
);
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
it('encodes new listing post body to Transit JSON Verbose', () => {
|
|
581
|
+
const { sdk, adapter } = createSdk({ transitVerbose: true });
|
|
582
|
+
|
|
583
|
+
const testData = {
|
|
584
|
+
title: 'A new hope',
|
|
585
|
+
description: 'Our Nth listing!',
|
|
586
|
+
address: 'Bulevardi 14, Helsinki, Finland',
|
|
587
|
+
geolocation: new LatLng(10.152, 15.375),
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
const transitEncoded =
|
|
591
|
+
'{"~:title":"A new hope","~:description":"Our Nth listing!","~:address":"Bulevardi 14, Helsinki, Finland","~:geolocation":{"~#geo":[10.152,15.375]}}';
|
|
592
|
+
|
|
593
|
+
return report(
|
|
594
|
+
sdk
|
|
595
|
+
.login({ username: 'joe.dunphy@example.com', password: 'secret-joe' })
|
|
596
|
+
.then(() => sdk.ownListings.create(testData))
|
|
597
|
+
.then(() => {
|
|
598
|
+
const req = _.last(adapter.requests);
|
|
599
|
+
expect(req.data).toEqual(transitEncoded);
|
|
600
|
+
expect(req.headers).toEqual(
|
|
601
|
+
expect.objectContaining({
|
|
602
|
+
'Content-Type': 'application/transit+json',
|
|
603
|
+
})
|
|
604
|
+
);
|
|
605
|
+
})
|
|
606
|
+
);
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
it('requests the server to send back Transit JSON Verbose', () => {
|
|
610
|
+
const { sdk, adapter } = createSdk({ transitVerbose: true });
|
|
611
|
+
|
|
612
|
+
return report(
|
|
613
|
+
sdk.marketplace.show({ id: '0e0b60fe-d9a2-11e6-bf26-cec0c932ce01' }).then(() => {
|
|
614
|
+
const req = _.last(adapter.requests);
|
|
615
|
+
expect(req.headers).toEqual(
|
|
616
|
+
expect.objectContaining({
|
|
617
|
+
'X-Transit-Verbose': 'true',
|
|
618
|
+
Accept: 'application/transit+json',
|
|
619
|
+
})
|
|
620
|
+
);
|
|
621
|
+
})
|
|
622
|
+
);
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
it('does not double encode in case we need to retry with fresh token', () => {
|
|
626
|
+
const { sdk, sdkTokenStore, adapterTokenStore, adapter } = createSdk();
|
|
627
|
+
|
|
628
|
+
const testData = {
|
|
629
|
+
title: 'A new hope',
|
|
630
|
+
description: 'Our Nth listing!',
|
|
631
|
+
address: 'Bulevardi 14, Helsinki, Finland',
|
|
632
|
+
geolocation: new LatLng(10.152, 15.375),
|
|
633
|
+
};
|
|
634
|
+
|
|
635
|
+
const transitEncoded =
|
|
636
|
+
'["^ ","~:title","A new hope","~:description","Our Nth listing!","~:address","Bulevardi 14, Helsinki, Finland","~:geolocation",["~#geo",[10.152,15.375]]]';
|
|
637
|
+
|
|
638
|
+
return report(
|
|
639
|
+
sdk.login({ username: 'joe.dunphy@example.com', password: 'secret-joe' }).then(() => {
|
|
640
|
+
const { access_token } = sdkTokenStore.getToken();
|
|
641
|
+
adapterTokenStore.expireAccessToken(access_token);
|
|
642
|
+
|
|
643
|
+
return sdk.ownListings.create(testData).then(() => {
|
|
644
|
+
const req = _.last(adapter.requests);
|
|
645
|
+
expect(req.data).toEqual(transitEncoded);
|
|
646
|
+
expect(req.headers).toEqual(
|
|
647
|
+
expect.objectContaining({
|
|
648
|
+
'Content-Type': 'application/transit+json',
|
|
649
|
+
})
|
|
650
|
+
);
|
|
651
|
+
});
|
|
652
|
+
})
|
|
653
|
+
);
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
describe('authInfo', () => {
|
|
657
|
+
it('returns authentication information', () => {
|
|
658
|
+
const { sdk } = createSdk();
|
|
659
|
+
|
|
660
|
+
return report(
|
|
661
|
+
sdk
|
|
662
|
+
.authInfo()
|
|
663
|
+
.then(authInfo => {
|
|
664
|
+
// No auth info yet.
|
|
665
|
+
expect(authInfo.grantType).toBeUndefined();
|
|
666
|
+
expect(authInfo.isAnonymous).toBeUndefined();
|
|
667
|
+
expect(authInfo.scopes).toBeUndefined();
|
|
668
|
+
})
|
|
669
|
+
.then(() =>
|
|
670
|
+
sdk.marketplace
|
|
671
|
+
.show()
|
|
672
|
+
.then(sdk.authInfo)
|
|
673
|
+
.then(authInfo => {
|
|
674
|
+
// Anonymous token
|
|
675
|
+
expect(authInfo.grantType).toEqual('client_credentials');
|
|
676
|
+
expect(authInfo.isAnonymous).toEqual(true);
|
|
677
|
+
expect(authInfo.scopes).toEqual(['public-read']);
|
|
678
|
+
})
|
|
679
|
+
)
|
|
680
|
+
.then(() =>
|
|
681
|
+
sdk
|
|
682
|
+
.login({
|
|
683
|
+
username: 'joe.dunphy@example.com',
|
|
684
|
+
password: 'secret-joe',
|
|
685
|
+
})
|
|
686
|
+
.then(sdk.authInfo)
|
|
687
|
+
.then(authInfo => {
|
|
688
|
+
// Login token
|
|
689
|
+
expect(authInfo.grantType).toEqual('refresh_token');
|
|
690
|
+
expect(authInfo.isAnonymous).toEqual(false);
|
|
691
|
+
expect(authInfo.scopes).toEqual(['user']);
|
|
692
|
+
})
|
|
693
|
+
)
|
|
694
|
+
.then(() =>
|
|
695
|
+
sdk
|
|
696
|
+
.logout()
|
|
697
|
+
.then(sdk.authInfo)
|
|
698
|
+
.then(authInfo => {
|
|
699
|
+
// Logout
|
|
700
|
+
expect(authInfo.grantType).toBeUndefined();
|
|
701
|
+
expect(authInfo.isAnonymous).toBeUndefined();
|
|
702
|
+
expect(authInfo.scopes).toBeUndefined();
|
|
703
|
+
})
|
|
704
|
+
)
|
|
705
|
+
.then(() =>
|
|
706
|
+
sdk
|
|
707
|
+
.logout()
|
|
708
|
+
.then(sdk.authInfo)
|
|
709
|
+
.then(authInfo => {
|
|
710
|
+
// Logging out already logged out user does nothing
|
|
711
|
+
expect(authInfo.grantType).toBeUndefined();
|
|
712
|
+
expect(authInfo.isAnonymous).toBeUndefined();
|
|
713
|
+
expect(authInfo.scopes).toBeUndefined();
|
|
714
|
+
})
|
|
715
|
+
)
|
|
716
|
+
);
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
it('supports anonymous tokens without scope attribute', () => {
|
|
720
|
+
const { sdk, sdkTokenStore, adapterTokenStore } = createSdk();
|
|
721
|
+
const anonToken = adapterTokenStore.createAnonToken();
|
|
722
|
+
const { scope, ...rest } = anonToken;
|
|
723
|
+
sdkTokenStore.setToken({ ...rest });
|
|
724
|
+
|
|
725
|
+
return report(
|
|
726
|
+
sdk.authInfo().then(authInfo => {
|
|
727
|
+
expect(authInfo.grantType).toEqual('client_credentials');
|
|
728
|
+
expect(authInfo.isAnonymous).toEqual(true);
|
|
729
|
+
expect(authInfo.scopes).toBeUndefined();
|
|
730
|
+
})
|
|
731
|
+
);
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
it('supports access tokens without scope attribute', () => {
|
|
735
|
+
const { sdk, sdkTokenStore, adapterTokenStore } = createSdk();
|
|
736
|
+
const accessToken = adapterTokenStore.createTokenWithCredentials(
|
|
737
|
+
'joe.dunphy@example.com',
|
|
738
|
+
'secret-joe'
|
|
739
|
+
);
|
|
740
|
+
const { scope, ...rest } = accessToken;
|
|
741
|
+
sdkTokenStore.setToken({ ...rest });
|
|
742
|
+
|
|
743
|
+
return report(
|
|
744
|
+
sdk.authInfo().then(authInfo => {
|
|
745
|
+
expect(authInfo.grantType).toEqual('refresh_token');
|
|
746
|
+
expect(authInfo.isAnonymous).toEqual(false);
|
|
747
|
+
expect(authInfo.scopes).toBeUndefined();
|
|
748
|
+
})
|
|
749
|
+
);
|
|
750
|
+
});
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
it('allows sending query params in POST request (such as `expand=true`)', () => {
|
|
754
|
+
const { sdk } = createSdk();
|
|
755
|
+
|
|
756
|
+
const params = {
|
|
757
|
+
title: 'Pelago bike',
|
|
758
|
+
description: 'City bike for city hipster!',
|
|
759
|
+
address: 'Bulevardi 14, 00200 Helsinki, Finland',
|
|
760
|
+
geolocation: new LatLng(40.0, 73.0),
|
|
761
|
+
};
|
|
762
|
+
|
|
763
|
+
return report(
|
|
764
|
+
sdk
|
|
765
|
+
.login({ username: 'joe.dunphy@example.com', password: 'secret-joe' })
|
|
766
|
+
.then(() => sdk.ownListings.create(params))
|
|
767
|
+
.then(res => {
|
|
768
|
+
const { data } = res.data;
|
|
769
|
+
const attrs = data.attributes;
|
|
770
|
+
|
|
771
|
+
expect(data).toEqual(
|
|
772
|
+
expect.objectContaining({
|
|
773
|
+
id: expect.any(UUID),
|
|
774
|
+
type: 'ownListing',
|
|
775
|
+
})
|
|
776
|
+
);
|
|
777
|
+
expect(attrs).toBeUndefined();
|
|
778
|
+
})
|
|
779
|
+
.then(() => sdk.ownListings.create(params, { expand: true }))
|
|
780
|
+
.then(res => {
|
|
781
|
+
const { data } = res.data;
|
|
782
|
+
const attrs = data.attributes;
|
|
783
|
+
|
|
784
|
+
expect(data).toEqual(
|
|
785
|
+
expect.objectContaining({
|
|
786
|
+
id: expect.any(UUID),
|
|
787
|
+
type: 'ownListing',
|
|
788
|
+
})
|
|
789
|
+
);
|
|
790
|
+
expect(attrs).toBeDefined();
|
|
791
|
+
})
|
|
792
|
+
);
|
|
793
|
+
});
|
|
794
|
+
|
|
795
|
+
it('returns error in expected error format, data as plain text', () => {
|
|
796
|
+
const { sdk } = createSdk();
|
|
797
|
+
|
|
798
|
+
return report(
|
|
799
|
+
sdk
|
|
800
|
+
.login({ username: 'wrong username', password: 'wrong password' })
|
|
801
|
+
.then(() => {
|
|
802
|
+
// Fail
|
|
803
|
+
expect(true).toEqual(false);
|
|
804
|
+
})
|
|
805
|
+
.catch(e => {
|
|
806
|
+
expect(e).toBeInstanceOf(Error);
|
|
807
|
+
expect(e).toEqual(
|
|
808
|
+
expect.objectContaining({
|
|
809
|
+
status: 401,
|
|
810
|
+
statusText: 'Unauthorized',
|
|
811
|
+
data: 'Unauthorized',
|
|
812
|
+
})
|
|
813
|
+
);
|
|
814
|
+
return Promise.resolve();
|
|
815
|
+
})
|
|
816
|
+
);
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
it('returns error in expected error format, data as an object', () => {
|
|
820
|
+
const { sdk } = createSdk();
|
|
821
|
+
|
|
822
|
+
return report(
|
|
823
|
+
sdk
|
|
824
|
+
.login({ username: 'joe.dunphy@example.com', password: 'secret-joe' })
|
|
825
|
+
.then(() => sdk.ownListings.create())
|
|
826
|
+
.then(() => {
|
|
827
|
+
// Fail
|
|
828
|
+
expect(true).toEqual(false);
|
|
829
|
+
})
|
|
830
|
+
.catch(e => {
|
|
831
|
+
expect(e).toBeInstanceOf(Error);
|
|
832
|
+
expect(e).toEqual(
|
|
833
|
+
expect.objectContaining({
|
|
834
|
+
status: 400,
|
|
835
|
+
statusText: 'Bad Request',
|
|
836
|
+
data: expect.objectContaining({
|
|
837
|
+
errors: [
|
|
838
|
+
expect.objectContaining({
|
|
839
|
+
id: expect.any(UUID),
|
|
840
|
+
status: 400,
|
|
841
|
+
code: 'bad-request',
|
|
842
|
+
title: 'Bad request',
|
|
843
|
+
details: {
|
|
844
|
+
error: {
|
|
845
|
+
'body-params': {
|
|
846
|
+
title: 'missing-required-key',
|
|
847
|
+
description: 'missing-required-key',
|
|
848
|
+
address: 'missing-required-key',
|
|
849
|
+
geolocation: 'missing-required-key',
|
|
850
|
+
},
|
|
851
|
+
},
|
|
852
|
+
},
|
|
853
|
+
}),
|
|
854
|
+
],
|
|
855
|
+
}),
|
|
856
|
+
})
|
|
857
|
+
);
|
|
858
|
+
return Promise.resolve();
|
|
859
|
+
})
|
|
860
|
+
);
|
|
861
|
+
});
|
|
862
|
+
});
|
|
863
|
+
|
|
864
|
+
describe('exchangeToken', () => {
|
|
865
|
+
it('returns a trusted token on exchange', () => {
|
|
866
|
+
const { sdk, sdkTokenStore, adapter } = createSdk();
|
|
867
|
+
|
|
868
|
+
sdk.login({ username: 'joe.dunphy@example.com', password: 'secret-joe' }).then(() => {
|
|
869
|
+
const stdToken = sdkTokenStore.getToken();
|
|
870
|
+
expect(stdToken.access_token).toEqual('joe.dunphy@example.com-access-1');
|
|
871
|
+
|
|
872
|
+
const subjectTokenStore = memoryStore();
|
|
873
|
+
subjectTokenStore.setToken(stdToken);
|
|
874
|
+
|
|
875
|
+
const { sdk: subjectSdk } = createSdk({
|
|
876
|
+
clientSecret: '8af2bf99c380b3a303ab90ae4012c8cd8f69d309',
|
|
877
|
+
tokenStore: subjectTokenStore,
|
|
878
|
+
adapter,
|
|
879
|
+
});
|
|
880
|
+
|
|
881
|
+
subjectSdk.exchangeToken().then(res => {
|
|
882
|
+
expect(res.data.scope).toEqual('trusted:user');
|
|
883
|
+
});
|
|
884
|
+
});
|
|
885
|
+
});
|
|
886
|
+
|
|
887
|
+
it('does not store a trusted token on exchange', () => {
|
|
888
|
+
const { sdk, sdkTokenStore, adapter } = createSdk();
|
|
889
|
+
|
|
890
|
+
sdk.login({ username: 'joe.dunphy@example.com', password: 'secret-joe' }).then(() => {
|
|
891
|
+
const stdToken = sdkTokenStore.getToken();
|
|
892
|
+
expect(stdToken.access_token).toEqual('joe.dunphy@example.com-access-1');
|
|
893
|
+
|
|
894
|
+
const subjectTokenStore = memoryStore();
|
|
895
|
+
subjectTokenStore.setToken(stdToken);
|
|
896
|
+
|
|
897
|
+
const { sdk: subjectSdk, sdkTokenStore: subjectSdkTokenStore } = createSdk({
|
|
898
|
+
clientSecret: '8af2bf99c380b3a303ab90ae4012c8cd8f69d309',
|
|
899
|
+
tokenStore: subjectTokenStore,
|
|
900
|
+
adapter,
|
|
901
|
+
});
|
|
902
|
+
|
|
903
|
+
subjectSdk.exchangeToken().then(() => {
|
|
904
|
+
expect(subjectSdkTokenStore.getToken().scope).toEqual('user');
|
|
905
|
+
});
|
|
906
|
+
});
|
|
907
|
+
});
|
|
908
|
+
});
|