@adobe/helix-onedrive-support 6.2.2 → 7.1.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/CHANGELOG.md +29 -0
- package/package.json +5 -4
- package/src/OneDrive.d.ts +27 -3
- package/src/OneDrive.js +185 -68
- package/src/OneDriveMock.js +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,32 @@
|
|
|
1
|
+
# [7.1.0](https://github.com/adobe/helix-onedrive-support/compare/v7.0.1...v7.1.0) (2022-04-01)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* speed up fuzzyItem lookup ([#259](https://github.com/adobe/helix-onedrive-support/issues/259)) ([5963fa6](https://github.com/adobe/helix-onedrive-support/commit/5963fa6389329d0cf737f64724688593b9291923)), closes [#258](https://github.com/adobe/helix-onedrive-support/issues/258)
|
|
7
|
+
|
|
8
|
+
## [7.0.1](https://github.com/adobe/helix-onedrive-support/compare/v7.0.0...v7.0.1) (2022-03-23)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* detect *-my.sharepoint.com links ([#256](https://github.com/adobe/helix-onedrive-support/issues/256)) ([001f57d](https://github.com/adobe/helix-onedrive-support/commit/001f57d7d73fd760df86c5a18ac5f27bcf9f0cf5))
|
|
14
|
+
* trigger release ([c8826a3](https://github.com/adobe/helix-onedrive-support/commit/c8826a38c69279593338b34918b2c305912225b1))
|
|
15
|
+
|
|
16
|
+
# [7.0.0](https://github.com/adobe/helix-onedrive-support/compare/v6.2.2...v7.0.0) (2022-03-23)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Features
|
|
20
|
+
|
|
21
|
+
* add transparent tenant resolution ([dc59dbf](https://github.com/adobe/helix-onedrive-support/commit/dc59dbfc53d767593b82c845c753da3885560852))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### BREAKING CHANGES
|
|
25
|
+
|
|
26
|
+
* API slightly refactored
|
|
27
|
+
- authorityUrl is now method: `getAuthorityIUrl`
|
|
28
|
+
- new method: `setAccessToken`
|
|
29
|
+
|
|
1
30
|
## [6.2.2](https://github.com/adobe/helix-onedrive-support/compare/v6.2.1...v6.2.2) (2022-03-20)
|
|
2
31
|
|
|
3
32
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/helix-onedrive-support",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.1.0",
|
|
4
4
|
"description": "Helix OneDrive Support",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "src/index.d.ts",
|
|
@@ -24,18 +24,19 @@
|
|
|
24
24
|
"homepage": "https://github.com/adobe/helix-onedrive-support#readme",
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@adobe/helix-fetch": "3.0.7",
|
|
27
|
-
"adal-node": "https://github.com/adobe-rnd/azure-activedirectory-library-for-nodejs.git#adobe"
|
|
27
|
+
"adal-node": "https://github.com/adobe-rnd/azure-activedirectory-library-for-nodejs.git#adobe",
|
|
28
|
+
"jose": "4.6.0"
|
|
28
29
|
},
|
|
29
30
|
"devDependencies": {
|
|
30
31
|
"@adobe/eslint-config-helix": "1.3.2",
|
|
31
32
|
"@semantic-release/changelog": "6.0.1",
|
|
32
33
|
"@semantic-release/git": "10.0.1",
|
|
33
|
-
"ajv": "8.
|
|
34
|
+
"ajv": "8.11.0",
|
|
34
35
|
"codecov": "3.8.3",
|
|
35
36
|
"commitizen": "4.2.4",
|
|
36
37
|
"cz-conventional-changelog": "3.3.0",
|
|
37
38
|
"dotenv": "16.0.0",
|
|
38
|
-
"eslint": "8.
|
|
39
|
+
"eslint": "8.12.0",
|
|
39
40
|
"eslint-plugin-header": "3.1.1",
|
|
40
41
|
"eslint-plugin-import": "2.25.4",
|
|
41
42
|
"jsdoc-to-markdown": "7.1.1",
|
package/src/OneDrive.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export declare interface OneDriveOptions {
|
|
|
25
25
|
refreshToken?: string;
|
|
26
26
|
log?: Logger;
|
|
27
27
|
tenant?: string;
|
|
28
|
+
resource?: string;
|
|
28
29
|
username?: string;
|
|
29
30
|
password?: string;
|
|
30
31
|
|
|
@@ -44,6 +45,18 @@ export declare interface OneDriveOptions {
|
|
|
44
45
|
* Note that the cache is only used, if the `noShareLinkCache` flag is `falsy`
|
|
45
46
|
*/
|
|
46
47
|
shareLinkCache?: Map<string, DriveItem>,
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Disables the cache for the tenant lookup.
|
|
51
|
+
* @default process.env.HELIX_ONEDRIVE_NO_TENANT_CACHE
|
|
52
|
+
*/
|
|
53
|
+
noTenantCache?: boolean;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Map to use for the tenant lookup cache. If empty, a module-global cache will be used.
|
|
57
|
+
* Note that the cache is only used, if the `noTenantCache` flag is `falsy`
|
|
58
|
+
*/
|
|
59
|
+
tenantCache?: Map<string, DriveItem>,
|
|
47
60
|
}
|
|
48
61
|
|
|
49
62
|
export declare interface GraphResult {
|
|
@@ -145,7 +158,7 @@ export declare class OneDrive extends EventEmitter {
|
|
|
145
158
|
/**
|
|
146
159
|
* the authority url for login.
|
|
147
160
|
*/
|
|
148
|
-
|
|
161
|
+
getAuthorityUrl(): string;
|
|
149
162
|
|
|
150
163
|
/**
|
|
151
164
|
* Adds entries to the token cache
|
|
@@ -161,6 +174,14 @@ export declare class OneDrive extends EventEmitter {
|
|
|
161
174
|
*/
|
|
162
175
|
login(onCode: Function): Promise<TokenResponse>;
|
|
163
176
|
|
|
177
|
+
/**
|
|
178
|
+
* Sets the access token to use for all requests. if the token is a valid JWT token,
|
|
179
|
+
* its `tid` claim is used a tenant (if no tenant is already set).
|
|
180
|
+
*
|
|
181
|
+
* @param {string} bearerToken
|
|
182
|
+
*/
|
|
183
|
+
setAccessToken(bearerToken);
|
|
184
|
+
|
|
164
185
|
getAccessToken(autoRefresh: boolean): Promise<TokenResponse>;
|
|
165
186
|
|
|
166
187
|
createLoginUrl(): string;
|
|
@@ -173,6 +194,8 @@ export declare class OneDrive extends EventEmitter {
|
|
|
173
194
|
|
|
174
195
|
me(): Promise<GraphResult>;
|
|
175
196
|
|
|
197
|
+
initTenantFromShareLink(sharingUrl: string|URL): Promise<void>;
|
|
198
|
+
|
|
176
199
|
resolveShareLink(sharingUrl: string|URL): Promise<GraphResult>;
|
|
177
200
|
|
|
178
201
|
/**
|
|
@@ -204,16 +227,17 @@ export declare class OneDrive extends EventEmitter {
|
|
|
204
227
|
* - replace all non-alphanumeric characters with a dash
|
|
205
228
|
* - remove all consecutive dashes
|
|
206
229
|
* - remove all leading and trailing dashes
|
|
207
|
-
* - extensions are ignored, if the given path doesn't have one
|
|
230
|
+
* - extensions are ignored, if the given path doesn't have one or if ignoreExtension is true
|
|
208
231
|
*
|
|
209
232
|
* The result is an array of drive items that match the given path. They are ordered by the edit
|
|
210
233
|
* distance to the original name and then alphanumerically.
|
|
211
234
|
*
|
|
212
235
|
* @param folderItem
|
|
213
236
|
* @param relPath
|
|
237
|
+
* @param ignoreExtension
|
|
214
238
|
* @returns {Promise<DriveItem[]>}
|
|
215
239
|
*/
|
|
216
|
-
fuzzyGetDriveItem(folderItem: DriveItem, relPath?: string): Promise<DriveItem[]>;
|
|
240
|
+
fuzzyGetDriveItem(folderItem: DriveItem, relPath?: string, ignoreExtension?: boolean): Promise<DriveItem[]>;
|
|
217
241
|
|
|
218
242
|
downloadDriveItem(driveItem: DriveItem): Promise<GraphResult>;
|
|
219
243
|
|
package/src/OneDrive.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
// eslint-disable-next-line max-classes-per-file
|
|
14
14
|
const EventEmitter = require('events');
|
|
15
15
|
const { promisify } = require('util');
|
|
16
|
+
const jose = require('jose');
|
|
16
17
|
const { AuthenticationContext, MemoryCache } = require('adal-node');
|
|
17
18
|
const { fetch, reset } = require('@adobe/helix-fetch').keepAliveNoCache({ userAgent: 'helix-fetch' });
|
|
18
19
|
|
|
@@ -24,7 +25,7 @@ const SharePointSite = require('./SharePointSite.js');
|
|
|
24
25
|
|
|
25
26
|
const AZ_AUTHORITY_HOST_URL = 'https://login.windows.net';
|
|
26
27
|
const AZ_DEFAULT_RESOURCE = 'https://graph.microsoft.com'; // '00000002-0000-0000-c000-000000000000'; ??
|
|
27
|
-
const
|
|
28
|
+
const AZ_COMMON_TENANT = 'common';
|
|
28
29
|
|
|
29
30
|
/**
|
|
30
31
|
* the maximum subscription time in milliseconds
|
|
@@ -37,27 +38,23 @@ const MAX_SUBSCRIPTION_EXPIRATION_TIME = 4230 * 60 * 1000;
|
|
|
37
38
|
|
|
38
39
|
/**
|
|
39
40
|
* map that caches share item data. key is a sharing url, the value a drive item.
|
|
40
|
-
* @type {Map<string,
|
|
41
|
+
* @type {Map<string, string>}
|
|
41
42
|
* @private
|
|
42
43
|
*/
|
|
43
44
|
const globalShareLinkCache = new Map();
|
|
44
45
|
|
|
46
|
+
/**
|
|
47
|
+
* map that caches the tenant ids
|
|
48
|
+
* @type {Map<string, string>}
|
|
49
|
+
*/
|
|
50
|
+
const globalTenantCache = new Map();
|
|
51
|
+
|
|
45
52
|
/**
|
|
46
53
|
* Helper class that facilitates accessing one drive.
|
|
47
54
|
*/
|
|
48
55
|
class OneDrive extends EventEmitter {
|
|
49
56
|
/**
|
|
50
57
|
* @param {OneDriveOptions} opts Options
|
|
51
|
-
* @param {string} opts.clientId The client id of the app
|
|
52
|
-
* @param {string} [opts.clientSecret] The client secret of the app
|
|
53
|
-
* @param {string} [opts.refreshToken] The refresh token.
|
|
54
|
-
* @param {string} [opts.accessToken] The access token.
|
|
55
|
-
* @param {string} [opts.username] Username for username/password authentication.
|
|
56
|
-
* @param {string} [opts.password] Password for username/password authentication.
|
|
57
|
-
* @param {number} [opts.expiresOn] Expiration time.
|
|
58
|
-
* @param {Logger} [opts.log] A logger.
|
|
59
|
-
* @param {boolean} [opts.localAuthCache] Whether to use local auth cache
|
|
60
|
-
* @param {string} [opts.resource] Azure resource to authenticate against. defaults to MS Graph.
|
|
61
58
|
*/
|
|
62
59
|
constructor(opts) {
|
|
63
60
|
super(opts);
|
|
@@ -67,53 +64,116 @@ class OneDrive extends EventEmitter {
|
|
|
67
64
|
this.username = opts.username || '';
|
|
68
65
|
this.password = opts.password || '';
|
|
69
66
|
this._log = opts.log || console;
|
|
70
|
-
this.tenant = opts.tenant
|
|
67
|
+
this.tenant = opts.tenant;
|
|
71
68
|
this.resource = opts.resource || AZ_DEFAULT_RESOURCE;
|
|
69
|
+
this.localAuthCache = opts.localAuthCache;
|
|
72
70
|
|
|
73
71
|
if (!opts.noShareLinkCache && !process.env.HELIX_ONEDRIVE_NO_SHARE_LINK_CACHE) {
|
|
72
|
+
/** @type {Map<string, string>} */
|
|
74
73
|
this.shareLinkCache = opts.shareLinkCache || globalShareLinkCache;
|
|
75
74
|
}
|
|
75
|
+
if (!opts.noTenantCache && !process.env.HELIX_ONEDRIVE_NO_TENANT_CACHE) {
|
|
76
|
+
/** @type {Map<string, string>} */
|
|
77
|
+
this.tenantCache = opts.tenantCache || globalTenantCache;
|
|
78
|
+
}
|
|
76
79
|
|
|
77
80
|
if (!this.clientId) {
|
|
78
81
|
throw new Error('Missing clientId.');
|
|
79
82
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Return the auth context
|
|
87
|
+
* @returns {AuthenticationContext}
|
|
88
|
+
*/
|
|
89
|
+
async getAuthContext() {
|
|
90
|
+
if (!this.authContext) {
|
|
91
|
+
this.authContext = new AuthenticationContext(
|
|
92
|
+
this.getAuthorityUrl(),
|
|
93
|
+
undefined,
|
|
94
|
+
this.localAuthCache ? new MemoryCache() : undefined,
|
|
95
|
+
);
|
|
96
|
+
[
|
|
97
|
+
'acquireUserCode',
|
|
98
|
+
'acquireToken',
|
|
99
|
+
'acquireTokenWithDeviceCode',
|
|
100
|
+
'acquireTokenWithRefreshToken',
|
|
101
|
+
'acquireTokenWithUsernamePassword',
|
|
102
|
+
'acquireTokenWithClientCredentials',
|
|
103
|
+
].forEach((m) => {
|
|
104
|
+
this.authContext[m] = promisify(this.authContext[m].bind(this.authContext));
|
|
105
|
+
});
|
|
106
|
+
const { cache } = this.authContext;
|
|
107
|
+
if (this.localAuthCache) {
|
|
108
|
+
const originalAdd = cache.add;
|
|
109
|
+
cache.add = (entries, cb) => {
|
|
110
|
+
originalAdd.call(cache, entries, (...args) => {
|
|
111
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
112
|
+
this.emit('tokens', cache._entries);
|
|
113
|
+
cb(...args);
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
const originalRemove = cache.remove;
|
|
117
|
+
cache.remove = (entries, cb) => {
|
|
118
|
+
originalRemove.call(cache, entries, (...args) => {
|
|
119
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
120
|
+
this.emit('tokens', cache._entries);
|
|
121
|
+
cb(...args);
|
|
122
|
+
});
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
cache.add.promise = promisify(cache.add.bind(cache));
|
|
126
|
+
cache.remove.promise = promisify(cache.remove.bind(cache));
|
|
127
|
+
cache.find.promise = promisify(cache.find.bind(cache));
|
|
128
|
+
}
|
|
129
|
+
return this.authContext;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async resolveTenant(tenantHost) {
|
|
133
|
+
const { log } = this;
|
|
134
|
+
const configUrl = `https://login.windows.net/${tenantHost}.onmicrosoft.com/.well-known/openid-configuration`;
|
|
135
|
+
const res = await fetch(configUrl);
|
|
136
|
+
if (!res.ok) {
|
|
137
|
+
log.info(`error fetching openid-configuration for ${tenantHost}: ${res.status}. Fallback to 'common'`);
|
|
138
|
+
return AZ_COMMON_TENANT;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const { issuer } = await res.json();
|
|
142
|
+
if (!issuer) {
|
|
143
|
+
log.info(`unable to extract tenant from openid-configuration for ${tenantHost}: no 'issuer'. Fallback to 'common'`);
|
|
144
|
+
return AZ_COMMON_TENANT;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// eslint-disable-next-line prefer-destructuring
|
|
148
|
+
const tenant = new URL(issuer).pathname.split('/')[1];
|
|
149
|
+
log.info(`fetched tenant information from for ${tenantHost}: ${tenant}`);
|
|
150
|
+
return tenant;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async initTenantFromShareLink(sharingUrl) {
|
|
154
|
+
if (this.tenant) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const { log } = this;
|
|
158
|
+
const url = sharingUrl instanceof URL
|
|
159
|
+
? sharingUrl
|
|
160
|
+
: new URL(sharingUrl);
|
|
161
|
+
let [tenantHost] = url.hostname.split('.');
|
|
162
|
+
// special case: `xxxx-my.sharepoint.com`
|
|
163
|
+
if (url.hostname.endsWith('-my.sharepoint.com')) {
|
|
164
|
+
tenantHost = tenantHost.substring(0, tenantHost.length - 3);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (this.tenantCache) {
|
|
168
|
+
this.tenant = this.tenantCache.get(tenantHost);
|
|
169
|
+
}
|
|
170
|
+
if (!this.tenant) {
|
|
171
|
+
this.tenant = await this.resolveTenant(tenantHost);
|
|
172
|
+
if (this.tenantCache) {
|
|
173
|
+
this.tenantCache.set(tenantHost, this.tenant);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
log.info(`using tenant ${this.tenant} for ${tenantHost} from ${sharingUrl}`);
|
|
117
177
|
}
|
|
118
178
|
|
|
119
179
|
/**
|
|
@@ -130,7 +190,10 @@ class OneDrive extends EventEmitter {
|
|
|
130
190
|
return this._log;
|
|
131
191
|
}
|
|
132
192
|
|
|
133
|
-
|
|
193
|
+
getAuthorityUrl() {
|
|
194
|
+
if (!this.tenant) {
|
|
195
|
+
throw new Error('unable to compute authority url. no tenant.');
|
|
196
|
+
}
|
|
134
197
|
return `${AZ_AUTHORITY_HOST_URL}/${this.tenant}`;
|
|
135
198
|
}
|
|
136
199
|
|
|
@@ -139,7 +202,7 @@ class OneDrive extends EventEmitter {
|
|
|
139
202
|
*/
|
|
140
203
|
get authenticated() {
|
|
141
204
|
// eslint-disable-next-line no-underscore-dangle
|
|
142
|
-
return this.authContext
|
|
205
|
+
return this.authContext?.cache._entries.length > 0;
|
|
143
206
|
}
|
|
144
207
|
|
|
145
208
|
/**
|
|
@@ -148,7 +211,7 @@ class OneDrive extends EventEmitter {
|
|
|
148
211
|
* @return this;
|
|
149
212
|
*/
|
|
150
213
|
async loadTokenCache(entries) {
|
|
151
|
-
return this.
|
|
214
|
+
return (await this.getAuthContext()).cache.add.promise(entries);
|
|
152
215
|
}
|
|
153
216
|
|
|
154
217
|
/**
|
|
@@ -158,7 +221,8 @@ class OneDrive extends EventEmitter {
|
|
|
158
221
|
* @returns {Promise<TokenResponse>}
|
|
159
222
|
*/
|
|
160
223
|
async login(onCode) {
|
|
161
|
-
const { log
|
|
224
|
+
const { log } = this;
|
|
225
|
+
const context = await this.getAuthContext();
|
|
162
226
|
|
|
163
227
|
let code;
|
|
164
228
|
try {
|
|
@@ -182,9 +246,35 @@ class OneDrive extends EventEmitter {
|
|
|
182
246
|
}
|
|
183
247
|
|
|
184
248
|
/**
|
|
249
|
+
* Sets the access token to use for all requests. if the token is a valid JWT token,
|
|
250
|
+
* its `tid` claim is used a tenant (if no tenant is already set).
|
|
251
|
+
*
|
|
252
|
+
* @param {string} bearerToken
|
|
185
253
|
*/
|
|
186
|
-
|
|
187
|
-
const { log
|
|
254
|
+
setAccessToken(bearerToken) {
|
|
255
|
+
const { log } = this;
|
|
256
|
+
this.accessToken = {
|
|
257
|
+
accessToken: bearerToken,
|
|
258
|
+
};
|
|
259
|
+
if (!this.tenant) {
|
|
260
|
+
try {
|
|
261
|
+
const { tid } = jose.decodeJwt(bearerToken);
|
|
262
|
+
if (tid) {
|
|
263
|
+
log.info(`using tenant from access token: ${tid}`);
|
|
264
|
+
this.tenant = tid;
|
|
265
|
+
}
|
|
266
|
+
} catch (e) {
|
|
267
|
+
log.warn(`unable to decode access token: ${e.message}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
this.accessToken.tenantId = this.tenant;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
*/
|
|
275
|
+
async fetchAccessToken() {
|
|
276
|
+
const { log } = this;
|
|
277
|
+
const context = await this.getAuthContext();
|
|
188
278
|
try {
|
|
189
279
|
return await context.acquireToken(this.resource, this.username, this.clientId);
|
|
190
280
|
} catch (e) {
|
|
@@ -231,10 +321,17 @@ class OneDrive extends EventEmitter {
|
|
|
231
321
|
}
|
|
232
322
|
}
|
|
233
323
|
|
|
324
|
+
async getAccessToken() {
|
|
325
|
+
if (!this.accessToken) {
|
|
326
|
+
this.accessToken = await this.fetchAccessToken();
|
|
327
|
+
}
|
|
328
|
+
return this.accessToken;
|
|
329
|
+
}
|
|
330
|
+
|
|
234
331
|
/**
|
|
235
332
|
*/
|
|
236
333
|
createLoginUrl(redirectUri, state) {
|
|
237
|
-
return `${this.
|
|
334
|
+
return `${this.getAuthorityUrl()}/oauth2/authorize?response_type=code&scope=/.default&client_id=${this.clientId}&redirect_uri=${redirectUri}&state=${state}&resource=${this.resource}`;
|
|
238
335
|
}
|
|
239
336
|
|
|
240
337
|
async augmentAndCacheResponse(response) {
|
|
@@ -259,7 +356,8 @@ class OneDrive extends EventEmitter {
|
|
|
259
356
|
/**
|
|
260
357
|
*/
|
|
261
358
|
async acquireToken(redirectUri, code) {
|
|
262
|
-
const { log
|
|
359
|
+
const { log } = this;
|
|
360
|
+
const context = await this.getAuthContext();
|
|
263
361
|
try {
|
|
264
362
|
const resp = await context.acquireTokenWithAuthorizationCode(
|
|
265
363
|
code,
|
|
@@ -342,6 +440,7 @@ class OneDrive extends EventEmitter {
|
|
|
342
440
|
/**
|
|
343
441
|
*/
|
|
344
442
|
async resolveShareLink(sharingUrl) {
|
|
443
|
+
await this.initTenantFromShareLink(sharingUrl);
|
|
345
444
|
const link = OneDrive.encodeSharingUrl(sharingUrl);
|
|
346
445
|
this.log.debug(`resolving sharelink ${sharingUrl} (${link})`);
|
|
347
446
|
try {
|
|
@@ -368,6 +467,7 @@ class OneDrive extends EventEmitter {
|
|
|
368
467
|
if (driveItem) {
|
|
369
468
|
return driveItem;
|
|
370
469
|
}
|
|
470
|
+
await this.initTenantFromShareLink(sharingUrl);
|
|
371
471
|
if (this.shareLinkCache) {
|
|
372
472
|
driveItem = this.shareLinkCache.get(sharingUrl);
|
|
373
473
|
}
|
|
@@ -402,22 +502,39 @@ class OneDrive extends EventEmitter {
|
|
|
402
502
|
* - convert to lower case
|
|
403
503
|
* - replace all non-alphanumeric characters with a dash
|
|
404
504
|
* - remove all consecutive dashes
|
|
405
|
-
* - extensions are ignored, if the given path doesn't have one
|
|
505
|
+
* - extensions are ignored, if the given path doesn't have one or if ignoreExtension is true
|
|
406
506
|
*
|
|
407
507
|
* The result is an array of drive items that match the given path. They are ordered by the edit
|
|
408
508
|
* distance to the original name and then alphanumerically.
|
|
409
509
|
*
|
|
410
510
|
* @param {DriveItem} folderItem
|
|
411
|
-
* @param {string} relPath
|
|
511
|
+
* @param {string} [relPath = '']
|
|
512
|
+
* @param {boolean} [ignoreExtension = false]
|
|
412
513
|
* @returns {Promise<DriveItem[]>}
|
|
413
514
|
*/
|
|
414
|
-
async fuzzyGetDriveItem(folderItem, relPath = '') {
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
const ret = await this.getDriveItem(folderItem, '', false);
|
|
418
|
-
// todo: add extra extension
|
|
419
|
-
return [ret.value];
|
|
515
|
+
async fuzzyGetDriveItem(folderItem, relPath = '', ignoreExtension = false) {
|
|
516
|
+
if (relPath && !relPath.startsWith('/')) {
|
|
517
|
+
throw new Error('relPath must be empty or start with /');
|
|
420
518
|
}
|
|
519
|
+
|
|
520
|
+
// first try to get item directly
|
|
521
|
+
try {
|
|
522
|
+
const ret = await this.getDriveItem(folderItem, relPath, false);
|
|
523
|
+
if (relPath) {
|
|
524
|
+
// eslint-disable-next-line prefer-destructuring
|
|
525
|
+
ret.extension = splitByExtension(relPath)[1];
|
|
526
|
+
}
|
|
527
|
+
this.log.info(`fetched drive item directly: /drives/${folderItem.parentReference.driveId}/items/${folderItem.id}:${relPath}`);
|
|
528
|
+
return [ret];
|
|
529
|
+
} catch (e) {
|
|
530
|
+
this.log.info(`fetched drive item directly failed: /drives/${folderItem.parentReference.driveId}/items/${folderItem.id}:${relPath} (${e.statusCode})`);
|
|
531
|
+
// if no 404 or no relPath, propagate error
|
|
532
|
+
if (e.statusCode !== 404 || !relPath) {
|
|
533
|
+
throw e;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
const idx = relPath.lastIndexOf('/');
|
|
421
538
|
const folderRelPath = relPath.substring(0, idx);
|
|
422
539
|
const name = relPath.substring(idx + 1);
|
|
423
540
|
const [baseName, ext] = splitByExtension(name);
|
|
@@ -441,7 +558,7 @@ class OneDrive extends EventEmitter {
|
|
|
441
558
|
}
|
|
442
559
|
} while (query.$skiptoken);
|
|
443
560
|
|
|
444
|
-
this.log.
|
|
561
|
+
this.log.info(`loaded ${fileList.length} children from /drives/${folderItem.parentReference.driveId}/items/${folderItem.id}:${relPath}`);
|
|
445
562
|
const items = fileList.filter((item) => {
|
|
446
563
|
if (!item.file) {
|
|
447
564
|
return false;
|
|
@@ -450,7 +567,7 @@ class OneDrive extends EventEmitter {
|
|
|
450
567
|
// remember extension
|
|
451
568
|
// eslint-disable-next-line no-param-reassign
|
|
452
569
|
item.extension = itemExt;
|
|
453
|
-
if (ext && ext !== itemExt) {
|
|
570
|
+
if (ext && ext !== itemExt && !ignoreExtension) {
|
|
454
571
|
// only match extension if given via relPath
|
|
455
572
|
return false;
|
|
456
573
|
}
|
package/src/OneDriveMock.js
CHANGED
|
@@ -239,6 +239,9 @@ class OneDriveMock extends OneDrive {
|
|
|
239
239
|
const url = new URL(`https://dummy.org${uri}`);
|
|
240
240
|
if (url.pathname in this.driveItems) {
|
|
241
241
|
const result = this.driveItems[url.pathname];
|
|
242
|
+
if (result instanceof Error) {
|
|
243
|
+
throw result;
|
|
244
|
+
}
|
|
242
245
|
if (!Array.isArray(result.value)) {
|
|
243
246
|
return result;
|
|
244
247
|
}
|