@fluidframework/odsp-driver 2.0.0 → 2.0.2
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/api-report/odsp-driver.alpha.api.md +1 -1
- package/dist/createFile.d.ts +3 -3
- package/dist/createFile.d.ts.map +1 -1
- package/dist/createFile.js +11 -9
- package/dist/createFile.js.map +1 -1
- package/dist/createNewContainerOnExistingFile.d.ts +1 -1
- package/dist/createNewContainerOnExistingFile.d.ts.map +1 -1
- package/dist/createNewContainerOnExistingFile.js +2 -2
- package/dist/createNewContainerOnExistingFile.js.map +1 -1
- package/dist/createNewUtils.d.ts +1 -1
- package/dist/createNewUtils.d.ts.map +1 -1
- package/dist/createNewUtils.js +22 -17
- package/dist/createNewUtils.js.map +1 -1
- package/dist/fetchSnapshot.d.ts +6 -9
- package/dist/fetchSnapshot.d.ts.map +1 -1
- package/dist/fetchSnapshot.js +22 -21
- package/dist/fetchSnapshot.js.map +1 -1
- package/dist/getFileLink.js +13 -8
- package/dist/getFileLink.js.map +1 -1
- package/dist/getUrlAndHeadersWithAuth.d.ts +2 -5
- package/dist/getUrlAndHeadersWithAuth.d.ts.map +1 -1
- package/dist/getUrlAndHeadersWithAuth.js +7 -28
- package/dist/getUrlAndHeadersWithAuth.js.map +1 -1
- package/dist/odspDelayLoadedDeltaStream.d.ts +3 -4
- package/dist/odspDelayLoadedDeltaStream.d.ts.map +1 -1
- package/dist/odspDelayLoadedDeltaStream.js +4 -5
- package/dist/odspDelayLoadedDeltaStream.js.map +1 -1
- package/dist/odspDeltaStorageService.d.ts +2 -2
- package/dist/odspDeltaStorageService.d.ts.map +1 -1
- package/dist/odspDeltaStorageService.js +9 -8
- package/dist/odspDeltaStorageService.js.map +1 -1
- package/dist/odspDocumentService.d.ts +4 -4
- package/dist/odspDocumentService.d.ts.map +1 -1
- package/dist/odspDocumentService.js +9 -9
- package/dist/odspDocumentService.js.map +1 -1
- package/dist/odspDocumentServiceFactoryCore.d.ts.map +1 -1
- package/dist/odspDocumentServiceFactoryCore.js +6 -4
- package/dist/odspDocumentServiceFactoryCore.js.map +1 -1
- package/dist/odspDocumentStorageManager.d.ts +2 -2
- package/dist/odspDocumentStorageManager.d.ts.map +1 -1
- package/dist/odspDocumentStorageManager.js +25 -19
- package/dist/odspDocumentStorageManager.js.map +1 -1
- package/dist/odspSummaryUploadManager.d.ts +2 -3
- package/dist/odspSummaryUploadManager.d.ts.map +1 -1
- package/dist/odspSummaryUploadManager.js +6 -5
- package/dist/odspSummaryUploadManager.js.map +1 -1
- package/dist/odspUtils.d.ts +11 -3
- package/dist/odspUtils.d.ts.map +1 -1
- package/dist/odspUtils.js +10 -6
- package/dist/odspUtils.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/prefetchLatestSnapshot.d.ts +1 -1
- package/dist/prefetchLatestSnapshot.d.ts.map +1 -1
- package/dist/prefetchLatestSnapshot.js +5 -5
- package/dist/prefetchLatestSnapshot.js.map +1 -1
- package/dist/vroom.d.ts +2 -2
- package/dist/vroom.d.ts.map +1 -1
- package/dist/vroom.js +7 -9
- package/dist/vroom.js.map +1 -1
- package/lib/createFile.d.ts +3 -3
- package/lib/createFile.d.ts.map +1 -1
- package/lib/createFile.js +12 -10
- package/lib/createFile.js.map +1 -1
- package/lib/createNewContainerOnExistingFile.d.ts +1 -1
- package/lib/createNewContainerOnExistingFile.d.ts.map +1 -1
- package/lib/createNewContainerOnExistingFile.js +2 -2
- package/lib/createNewContainerOnExistingFile.js.map +1 -1
- package/lib/createNewUtils.d.ts +1 -1
- package/lib/createNewUtils.d.ts.map +1 -1
- package/lib/createNewUtils.js +23 -18
- package/lib/createNewUtils.js.map +1 -1
- package/lib/fetchSnapshot.d.ts +6 -9
- package/lib/fetchSnapshot.d.ts.map +1 -1
- package/lib/fetchSnapshot.js +23 -22
- package/lib/fetchSnapshot.js.map +1 -1
- package/lib/getFileLink.js +14 -9
- package/lib/getFileLink.js.map +1 -1
- package/lib/getUrlAndHeadersWithAuth.d.ts +2 -5
- package/lib/getUrlAndHeadersWithAuth.d.ts.map +1 -1
- package/lib/getUrlAndHeadersWithAuth.js +5 -26
- package/lib/getUrlAndHeadersWithAuth.js.map +1 -1
- package/lib/odspDelayLoadedDeltaStream.d.ts +3 -4
- package/lib/odspDelayLoadedDeltaStream.d.ts.map +1 -1
- package/lib/odspDelayLoadedDeltaStream.js +4 -5
- package/lib/odspDelayLoadedDeltaStream.js.map +1 -1
- package/lib/odspDeltaStorageService.d.ts +2 -2
- package/lib/odspDeltaStorageService.d.ts.map +1 -1
- package/lib/odspDeltaStorageService.js +9 -8
- package/lib/odspDeltaStorageService.js.map +1 -1
- package/lib/odspDocumentService.d.ts +4 -4
- package/lib/odspDocumentService.d.ts.map +1 -1
- package/lib/odspDocumentService.js +9 -9
- package/lib/odspDocumentService.js.map +1 -1
- package/lib/odspDocumentServiceFactoryCore.d.ts.map +1 -1
- package/lib/odspDocumentServiceFactoryCore.js +6 -4
- package/lib/odspDocumentServiceFactoryCore.js.map +1 -1
- package/lib/odspDocumentStorageManager.d.ts +2 -2
- package/lib/odspDocumentStorageManager.d.ts.map +1 -1
- package/lib/odspDocumentStorageManager.js +26 -20
- package/lib/odspDocumentStorageManager.js.map +1 -1
- package/lib/odspSummaryUploadManager.d.ts +2 -3
- package/lib/odspSummaryUploadManager.d.ts.map +1 -1
- package/lib/odspSummaryUploadManager.js +7 -6
- package/lib/odspSummaryUploadManager.js.map +1 -1
- package/lib/odspUtils.d.ts +11 -3
- package/lib/odspUtils.d.ts.map +1 -1
- package/lib/odspUtils.js +11 -7
- package/lib/odspUtils.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/prefetchLatestSnapshot.d.ts +1 -1
- package/lib/prefetchLatestSnapshot.d.ts.map +1 -1
- package/lib/prefetchLatestSnapshot.js +5 -5
- package/lib/prefetchLatestSnapshot.js.map +1 -1
- package/lib/vroom.d.ts +2 -2
- package/lib/vroom.d.ts.map +1 -1
- package/lib/vroom.js +7 -9
- package/lib/vroom.js.map +1 -1
- package/package.json +11 -11
- package/src/createFile.ts +15 -21
- package/src/createNewContainerOnExistingFile.ts +2 -2
- package/src/createNewUtils.ts +32 -24
- package/src/fetchSnapshot.ts +35 -34
- package/src/getFileLink.ts +26 -20
- package/src/getUrlAndHeadersWithAuth.ts +6 -31
- package/src/odspDelayLoadedDeltaStream.ts +4 -5
- package/src/odspDeltaStorageService.ts +11 -7
- package/src/odspDocumentService.ts +8 -8
- package/src/odspDocumentServiceFactoryCore.ts +5 -3
- package/src/odspDocumentStorageManager.ts +36 -33
- package/src/odspSummaryUploadManager.ts +9 -9
- package/src/odspUtils.ts +19 -6
- package/src/packageVersion.ts +1 -1
- package/src/prefetchLatestSnapshot.ts +9 -5
- package/src/vroom.ts +11 -11
|
@@ -31,7 +31,7 @@ import { getWithRetryForTokenRefresh } from "./odspUtils.js";
|
|
|
31
31
|
export class OdspDeltaStorageService {
|
|
32
32
|
constructor(
|
|
33
33
|
private readonly deltaFeedUrl: string,
|
|
34
|
-
private readonly
|
|
34
|
+
private readonly getAuthHeader: InstrumentedStorageTokenFetcher,
|
|
35
35
|
private readonly epochTracker: EpochTracker,
|
|
36
36
|
private readonly logger: ITelemetryLoggerExt,
|
|
37
37
|
) {}
|
|
@@ -52,9 +52,13 @@ export class OdspDeltaStorageService {
|
|
|
52
52
|
): Promise<IDeltasFetchResult> {
|
|
53
53
|
return getWithRetryForTokenRefresh(async (options) => {
|
|
54
54
|
// Note - this call ends up in getSocketStorageDiscovery() and can refresh token
|
|
55
|
-
// Thus it needs to be done before we call
|
|
56
|
-
const
|
|
57
|
-
const
|
|
55
|
+
// Thus it needs to be done before we call getAuthHeader() to reduce extra calls
|
|
56
|
+
const url = this.buildUrl(from, to);
|
|
57
|
+
const method = "POST";
|
|
58
|
+
const authHeader = await this.getAuthHeader(
|
|
59
|
+
{ ...options, request: { url, method } },
|
|
60
|
+
"DeltaStorage",
|
|
61
|
+
);
|
|
58
62
|
|
|
59
63
|
return PerformanceEvent.timedExecAsync(
|
|
60
64
|
this.logger,
|
|
@@ -69,7 +73,7 @@ export class OdspDeltaStorageService {
|
|
|
69
73
|
async (event) => {
|
|
70
74
|
const formBoundary = uuid();
|
|
71
75
|
let postBody = `--${formBoundary}\r\n`;
|
|
72
|
-
postBody += `Authorization:
|
|
76
|
+
postBody += `Authorization: ${authHeader}\r\n`;
|
|
73
77
|
postBody += `X-HTTP-Method-Override: GET\r\n`;
|
|
74
78
|
|
|
75
79
|
postBody += `_post: 1\r\n`;
|
|
@@ -88,11 +92,11 @@ export class OdspDeltaStorageService {
|
|
|
88
92
|
|
|
89
93
|
const response =
|
|
90
94
|
await this.epochTracker.fetchAndParseAsJSON<IDeltaStorageGetResponse>(
|
|
91
|
-
|
|
95
|
+
url,
|
|
92
96
|
{
|
|
93
97
|
headers,
|
|
94
98
|
body: postBody,
|
|
95
|
-
method
|
|
99
|
+
method,
|
|
96
100
|
signal: abort.signal,
|
|
97
101
|
},
|
|
98
102
|
"ops",
|
|
@@ -64,7 +64,7 @@ export class OdspDocumentService
|
|
|
64
64
|
* Creates a new OdspDocumentService instance.
|
|
65
65
|
*
|
|
66
66
|
* @param resolvedUrl - resolved url identifying document that will be managed by returned service instance.
|
|
67
|
-
* @param
|
|
67
|
+
* @param getAuthHeader - function that can provide the Authentication header value. This is is also referred to as
|
|
68
68
|
* the "Vroom" token in SPO.
|
|
69
69
|
* @param getWebsocketToken - function that can provide a token for accessing the web socket. This is also referred
|
|
70
70
|
* to as the "Push" token in SPO. If undefined then websocket token is expected to be returned with joinSession
|
|
@@ -77,7 +77,7 @@ export class OdspDocumentService
|
|
|
77
77
|
*/
|
|
78
78
|
public static async create(
|
|
79
79
|
resolvedUrl: IResolvedUrl,
|
|
80
|
-
|
|
80
|
+
getAuthHeader: InstrumentedStorageTokenFetcher,
|
|
81
81
|
// eslint-disable-next-line @rushstack/no-new-null
|
|
82
82
|
getWebsocketToken: ((options: TokenFetchOptions) => Promise<string | null>) | undefined,
|
|
83
83
|
logger: ITelemetryLoggerExt,
|
|
@@ -89,7 +89,7 @@ export class OdspDocumentService
|
|
|
89
89
|
): Promise<IDocumentService> {
|
|
90
90
|
return new OdspDocumentService(
|
|
91
91
|
getOdspResolvedUrl(resolvedUrl),
|
|
92
|
-
|
|
92
|
+
getAuthHeader,
|
|
93
93
|
getWebsocketToken,
|
|
94
94
|
logger,
|
|
95
95
|
cache,
|
|
@@ -110,7 +110,7 @@ export class OdspDocumentService
|
|
|
110
110
|
|
|
111
111
|
/**
|
|
112
112
|
* @param odspResolvedUrl - resolved url identifying document that will be managed by this service instance.
|
|
113
|
-
* @param
|
|
113
|
+
* @param getAuthHeader - function that can provide the Authentication header value. This is is also referred to as
|
|
114
114
|
* the "Vroom" token in SPO.
|
|
115
115
|
* @param getWebsocketToken - function that can provide a token for accessing the web socket. This is also referred
|
|
116
116
|
* to as the "Push" token in SPO. If undefined then websocket token is expected to be returned with joinSession
|
|
@@ -124,7 +124,7 @@ export class OdspDocumentService
|
|
|
124
124
|
*/
|
|
125
125
|
private constructor(
|
|
126
126
|
public readonly odspResolvedUrl: IOdspResolvedUrl,
|
|
127
|
-
private readonly
|
|
127
|
+
private readonly getAuthHeader: InstrumentedStorageTokenFetcher,
|
|
128
128
|
private readonly getWebsocketToken:
|
|
129
129
|
| ((options: TokenFetchOptions) => Promise<string | null>)
|
|
130
130
|
| undefined,
|
|
@@ -175,7 +175,7 @@ export class OdspDocumentService
|
|
|
175
175
|
if (!this.storageManager) {
|
|
176
176
|
this.storageManager = new OdspDocumentStorageService(
|
|
177
177
|
this.odspResolvedUrl,
|
|
178
|
-
this.
|
|
178
|
+
this.getAuthHeader,
|
|
179
179
|
this.mc.logger,
|
|
180
180
|
true,
|
|
181
181
|
this.cache,
|
|
@@ -208,7 +208,7 @@ export class OdspDocumentService
|
|
|
208
208
|
const snapshotOps = this.storageManager?.ops ?? [];
|
|
209
209
|
const service = new OdspDeltaStorageService(
|
|
210
210
|
this.odspResolvedUrl.endpoints.deltaStorageUrl,
|
|
211
|
-
this.
|
|
211
|
+
this.getAuthHeader,
|
|
212
212
|
this.epochTracker,
|
|
213
213
|
this.mc.logger,
|
|
214
214
|
);
|
|
@@ -285,7 +285,7 @@ export class OdspDocumentService
|
|
|
285
285
|
this.odspDelayLoadedDeltaStream = new module.OdspDelayLoadedDeltaStream(
|
|
286
286
|
this.odspResolvedUrl,
|
|
287
287
|
this._policies,
|
|
288
|
-
this.
|
|
288
|
+
this.getAuthHeader,
|
|
289
289
|
this.getWebsocketToken,
|
|
290
290
|
this.mc,
|
|
291
291
|
this.cache,
|
|
@@ -167,7 +167,7 @@ export class OdspDocumentServiceFactoryCore
|
|
|
167
167
|
this.hostPolicy.enableSingleRequestForShareLinkWithCreate,
|
|
168
168
|
},
|
|
169
169
|
async (event) => {
|
|
170
|
-
const
|
|
170
|
+
const getAuthHeader = toInstrumentedOdspStorageTokenFetcher(
|
|
171
171
|
odspLogger,
|
|
172
172
|
resolvedUrlData,
|
|
173
173
|
this.getStorageToken,
|
|
@@ -188,7 +188,7 @@ export class OdspDocumentServiceFactoryCore
|
|
|
188
188
|
});
|
|
189
189
|
const _odspResolvedUrl = isNewFileInfo(fileInfo)
|
|
190
190
|
? await module.createNewFluidFile(
|
|
191
|
-
|
|
191
|
+
getAuthHeader,
|
|
192
192
|
fileInfo,
|
|
193
193
|
odspLogger,
|
|
194
194
|
createNewSummary,
|
|
@@ -200,7 +200,7 @@ export class OdspDocumentServiceFactoryCore
|
|
|
200
200
|
this.hostPolicy.enableSingleRequestForShareLinkWithCreate,
|
|
201
201
|
)
|
|
202
202
|
: await module.createNewContainerOnExistingFile(
|
|
203
|
-
|
|
203
|
+
getAuthHeader,
|
|
204
204
|
fileInfo,
|
|
205
205
|
odspLogger,
|
|
206
206
|
createNewSummary,
|
|
@@ -300,11 +300,13 @@ export class OdspDocumentServiceFactoryCore
|
|
|
300
300
|
this.getWebsocketToken === undefined
|
|
301
301
|
? undefined
|
|
302
302
|
: async (options: TokenFetchOptions): Promise<string | null> =>
|
|
303
|
+
// websocket expects a plain token
|
|
303
304
|
toInstrumentedOdspTokenFetcher(
|
|
304
305
|
extLogger,
|
|
305
306
|
resolvedUrlData,
|
|
306
307
|
this.getWebsocketToken!,
|
|
307
308
|
false /* throwOnNullToken */,
|
|
309
|
+
true /* returnPlainToken */,
|
|
308
310
|
)(options, "GetWebsocketToken");
|
|
309
311
|
|
|
310
312
|
return OdspDocumentService.create(
|
|
@@ -51,7 +51,7 @@ import {
|
|
|
51
51
|
fetchSnapshot,
|
|
52
52
|
fetchSnapshotWithRedeem,
|
|
53
53
|
} from "./fetchSnapshot.js";
|
|
54
|
-
import {
|
|
54
|
+
import { getHeadersWithAuth } from "./getUrlAndHeadersWithAuth.js";
|
|
55
55
|
import { IOdspCache, IPrefetchSnapshotContents } from "./odspCache.js";
|
|
56
56
|
import { FlushResult } from "./odspDocumentDeltaConnection.js";
|
|
57
57
|
import { OdspDocumentStorageServiceBase } from "./odspDocumentStorageServiceBase.js";
|
|
@@ -63,6 +63,7 @@ import {
|
|
|
63
63
|
isInstanceOfISnapshot,
|
|
64
64
|
isSnapshotFetchForLoadingGroup,
|
|
65
65
|
useLegacyFlowWithoutGroupsForSnapshotFetch,
|
|
66
|
+
type TokenFetchOptionsEx,
|
|
66
67
|
} from "./odspUtils.js";
|
|
67
68
|
import { pkgVersion as driverVersion } from "./packageVersion.js";
|
|
68
69
|
|
|
@@ -98,7 +99,7 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
|
|
|
98
99
|
|
|
99
100
|
constructor(
|
|
100
101
|
private readonly odspResolvedUrl: IOdspResolvedUrl,
|
|
101
|
-
private readonly
|
|
102
|
+
private readonly getAuthHeader: InstrumentedStorageTokenFetcher,
|
|
102
103
|
private readonly logger: ITelemetryLoggerExt,
|
|
103
104
|
private readonly fetchFullSnapshot: boolean,
|
|
104
105
|
private readonly cache: IOdspCache,
|
|
@@ -124,12 +125,13 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
|
|
|
124
125
|
this.checkAttachmentPOSTUrl();
|
|
125
126
|
|
|
126
127
|
const response = await getWithRetryForTokenRefresh(async (options) => {
|
|
127
|
-
const
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
128
|
+
const url = `${this.attachmentPOSTUrl}/content`;
|
|
129
|
+
const method = "POST";
|
|
130
|
+
const authHeader = await this.getAuthHeader(
|
|
131
|
+
{ ...options, request: { url, method } },
|
|
132
|
+
"CreateBlob",
|
|
132
133
|
);
|
|
134
|
+
const headers = getHeadersWithAuth(authHeader);
|
|
133
135
|
headers["Content-Type"] = "application/octet-stream";
|
|
134
136
|
|
|
135
137
|
return PerformanceEvent.timedExecAsync(
|
|
@@ -146,7 +148,7 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
|
|
|
146
148
|
{
|
|
147
149
|
body: file,
|
|
148
150
|
headers,
|
|
149
|
-
method
|
|
151
|
+
method,
|
|
150
152
|
},
|
|
151
153
|
"createBlob",
|
|
152
154
|
),
|
|
@@ -170,13 +172,13 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
|
|
|
170
172
|
this.checkAttachmentGETUrl();
|
|
171
173
|
|
|
172
174
|
const blob = await getWithRetryForTokenRefresh(async (options) => {
|
|
173
|
-
const
|
|
174
|
-
const
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
!!this.hostPolicy.sessionOptions?.forceAccessTokenViaAuthorizationHeader,
|
|
175
|
+
const url = `${this.attachmentGETUrl}/${encodeURIComponent(blobId)}/content`;
|
|
176
|
+
const method = "GET";
|
|
177
|
+
const authHeader = await this.getAuthHeader(
|
|
178
|
+
{ ...options, request: { url, method } },
|
|
179
|
+
"GetBlob",
|
|
179
180
|
);
|
|
181
|
+
const headers = getHeadersWithAuth(authHeader);
|
|
180
182
|
|
|
181
183
|
return PerformanceEvent.timedExecAsync(
|
|
182
184
|
this.logger,
|
|
@@ -483,12 +485,13 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
|
|
|
483
485
|
}
|
|
484
486
|
|
|
485
487
|
return getWithRetryForTokenRefresh(async (options) => {
|
|
486
|
-
const
|
|
487
|
-
const
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
488
|
+
const url = `${this.snapshotUrl}/versions?top=${count}`;
|
|
489
|
+
const method = "GET";
|
|
490
|
+
const storageToken = await this.getAuthHeader(
|
|
491
|
+
{ ...options, request: { url, method } },
|
|
492
|
+
"GetVersions",
|
|
491
493
|
);
|
|
494
|
+
const headers = getHeadersWithAuth(storageToken);
|
|
492
495
|
|
|
493
496
|
// Fetch the latest snapshot versions for the document
|
|
494
497
|
const response = await PerformanceEvent.timedExecAsync(
|
|
@@ -604,14 +607,16 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
|
|
|
604
607
|
|
|
605
608
|
const snapshotDownloader = async (
|
|
606
609
|
finalOdspResolvedUrl: IOdspResolvedUrl,
|
|
607
|
-
|
|
610
|
+
tokenFetcher: InstrumentedStorageTokenFetcher,
|
|
611
|
+
tokenFetchOptions: TokenFetchOptionsEx,
|
|
608
612
|
loadingGroupId: string[] | undefined,
|
|
609
613
|
options: ISnapshotOptions | undefined,
|
|
610
614
|
controller?: AbortController,
|
|
611
615
|
): Promise<ISnapshotRequestAndResponseOptions> => {
|
|
612
616
|
return downloadSnapshot(
|
|
613
617
|
finalOdspResolvedUrl,
|
|
614
|
-
|
|
618
|
+
tokenFetcher,
|
|
619
|
+
tokenFetchOptions,
|
|
615
620
|
loadingGroupId,
|
|
616
621
|
options,
|
|
617
622
|
this.snapshotFormatFetchType,
|
|
@@ -631,7 +636,7 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
|
|
|
631
636
|
try {
|
|
632
637
|
const odspSnapshot = await fetchSnapshotWithRedeem(
|
|
633
638
|
this.odspResolvedUrl,
|
|
634
|
-
this.
|
|
639
|
+
this.getAuthHeader,
|
|
635
640
|
snapshotOptions,
|
|
636
641
|
!!this.hostPolicy.sessionOptions?.forceAccessTokenViaAuthorizationHeader,
|
|
637
642
|
this.logger,
|
|
@@ -673,7 +678,7 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
|
|
|
673
678
|
};
|
|
674
679
|
const odspSnapshot = await fetchSnapshotWithRedeem(
|
|
675
680
|
this.odspResolvedUrl,
|
|
676
|
-
this.
|
|
681
|
+
this.getAuthHeader,
|
|
677
682
|
snapshotOptionsWithoutBlobs,
|
|
678
683
|
!!this.hostPolicy.sessionOptions?.forceAccessTokenViaAuthorizationHeader,
|
|
679
684
|
this.logger,
|
|
@@ -767,10 +772,9 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
|
|
|
767
772
|
});
|
|
768
773
|
this.odspSummaryUploadManager = new module.OdspSummaryUploadManager(
|
|
769
774
|
this.odspResolvedUrl.endpoints.snapshotStorageUrl,
|
|
770
|
-
this.
|
|
775
|
+
this.getAuthHeader,
|
|
771
776
|
this.logger,
|
|
772
777
|
this.epochTracker,
|
|
773
|
-
!!this.hostPolicy.sessionOptions?.forceAccessTokenViaAuthorizationHeader,
|
|
774
778
|
this.relayServiceTenantAndSessionId,
|
|
775
779
|
);
|
|
776
780
|
return this.odspSummaryUploadManager;
|
|
@@ -811,15 +815,15 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
|
|
|
811
815
|
scenarioName?: string,
|
|
812
816
|
): Promise<ISnapshotTree | undefined> {
|
|
813
817
|
return getWithRetryForTokenRefresh(async (options) => {
|
|
814
|
-
const
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
818
|
+
const snapshotDownloader = async (url: string): Promise<IOdspResponse<unknown>> => {
|
|
819
|
+
const authHeader = await this.getAuthHeader(
|
|
820
|
+
{ ...options, request: { url, method: "GET" } },
|
|
821
|
+
"ReadCommit",
|
|
822
|
+
);
|
|
823
|
+
const headers = getHeadersWithAuth(authHeader);
|
|
820
824
|
return this.epochTracker.fetchAndParseAsJSON(
|
|
821
825
|
url,
|
|
822
|
-
|
|
826
|
+
{ headers },
|
|
823
827
|
"snapshotTree",
|
|
824
828
|
undefined,
|
|
825
829
|
scenarioName,
|
|
@@ -827,7 +831,6 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
|
|
|
827
831
|
};
|
|
828
832
|
const snapshot = await fetchSnapshot(
|
|
829
833
|
this.snapshotUrl!,
|
|
830
|
-
storageToken,
|
|
831
834
|
id,
|
|
832
835
|
this.fetchFullSnapshot,
|
|
833
836
|
!!this.hostPolicy.sessionOptions?.forceAccessTokenViaAuthorizationHeader,
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
OdspSummaryTreeValue,
|
|
29
29
|
} from "./contracts.js";
|
|
30
30
|
import { EpochTracker } from "./epochTracker.js";
|
|
31
|
-
import {
|
|
31
|
+
import { getHeadersWithAuth } from "./getUrlAndHeadersWithAuth.js";
|
|
32
32
|
import { getWithRetryForTokenRefresh } from "./odspUtils.js";
|
|
33
33
|
|
|
34
34
|
/**
|
|
@@ -42,10 +42,9 @@ export class OdspSummaryUploadManager {
|
|
|
42
42
|
|
|
43
43
|
constructor(
|
|
44
44
|
private readonly snapshotUrl: string,
|
|
45
|
-
private readonly
|
|
45
|
+
private readonly getAuthHeader: InstrumentedStorageTokenFetcher,
|
|
46
46
|
logger: ITelemetryLoggerExt,
|
|
47
47
|
private readonly epochTracker: EpochTracker,
|
|
48
|
-
private readonly forceAccessTokenViaAuthorizationHeader: boolean,
|
|
49
48
|
private readonly relayServiceTenantAndSessionId: () => string | undefined,
|
|
50
49
|
) {
|
|
51
50
|
this.mc = loggerToMonitoringContext(logger);
|
|
@@ -102,13 +101,14 @@ export class OdspSummaryUploadManager {
|
|
|
102
101
|
};
|
|
103
102
|
|
|
104
103
|
return getWithRetryForTokenRefresh(async (options) => {
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
this.forceAccessTokenViaAuthorizationHeader,
|
|
104
|
+
const url = `${this.snapshotUrl}/snapshot`;
|
|
105
|
+
const method = "POST";
|
|
106
|
+
const authHeader = await this.getAuthHeader(
|
|
107
|
+
{ ...options, request: { url, method } },
|
|
108
|
+
"WriteSummaryTree",
|
|
111
109
|
);
|
|
110
|
+
|
|
111
|
+
const headers = getHeadersWithAuth(authHeader);
|
|
112
112
|
headers["Content-Type"] = "application/json";
|
|
113
113
|
const relayServiceTenantAndSessionId = this.relayServiceTenantAndSessionId();
|
|
114
114
|
// This would be undefined in case of summary is uploaded in detached container with attachment
|
package/src/odspUtils.ts
CHANGED
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
InstrumentedStorageTokenFetcher,
|
|
32
32
|
InstrumentedTokenFetcher,
|
|
33
33
|
OdspErrorTypes,
|
|
34
|
+
authHeaderFromTokenResponse,
|
|
34
35
|
OdspResourceTokenFetchOptions,
|
|
35
36
|
TokenFetchOptions,
|
|
36
37
|
TokenFetcher,
|
|
@@ -64,7 +65,14 @@ export interface IOdspResponse<T> {
|
|
|
64
65
|
duration: number;
|
|
65
66
|
}
|
|
66
67
|
|
|
67
|
-
|
|
68
|
+
/**
|
|
69
|
+
* This interface captures the portion of TokenFetchOptions required for refreshing tokens
|
|
70
|
+
* It is controlled by logic in getWithRetryForTokenRefresh to specify what is the required refresh behavior
|
|
71
|
+
*/
|
|
72
|
+
export interface TokenFetchOptionsEx {
|
|
73
|
+
refresh: boolean;
|
|
74
|
+
claims?: string;
|
|
75
|
+
tenantId?: string;
|
|
68
76
|
/**
|
|
69
77
|
* The previous error we hit in {@link getWithRetryForTokenRefresh}.
|
|
70
78
|
*/
|
|
@@ -354,7 +362,8 @@ export function toInstrumentedOdspStorageTokenFetcher(
|
|
|
354
362
|
logger,
|
|
355
363
|
resolvedUrlParts,
|
|
356
364
|
tokenFetcher,
|
|
357
|
-
true, // throwOnNullToken
|
|
365
|
+
true, // throwOnNullToken
|
|
366
|
+
false, // returnPlainToken
|
|
358
367
|
);
|
|
359
368
|
// Drop undefined from signature - we can do it safely due to throwOnNullToken == true above
|
|
360
369
|
return res as InstrumentedStorageTokenFetcher;
|
|
@@ -364,12 +373,14 @@ export function toInstrumentedOdspStorageTokenFetcher(
|
|
|
364
373
|
* Returns a function that can be used to fetch storage or websocket token.
|
|
365
374
|
* There are scenarios where websocket token is not required / present (consumer stack and ordering service token),
|
|
366
375
|
* thus it could return null. Use toInstrumentedOdspStorageTokenFetcher if you deal with storage token.
|
|
376
|
+
* @param returnPlainToken - When true, tokenResponse.token is returned. When false, tokenResponse.authorizationHeader is returned or an authorization header value is created based on tokenResponse.token
|
|
367
377
|
*/
|
|
368
378
|
export function toInstrumentedOdspTokenFetcher(
|
|
369
379
|
logger: ITelemetryLoggerExt,
|
|
370
380
|
resolvedUrlParts: IOdspUrlParts,
|
|
371
381
|
tokenFetcher: TokenFetcher<OdspResourceTokenFetchOptions>,
|
|
372
382
|
throwOnNullToken: boolean,
|
|
383
|
+
returnPlainToken: boolean,
|
|
373
384
|
): InstrumentedTokenFetcher {
|
|
374
385
|
return async (
|
|
375
386
|
options: TokenFetchOptions,
|
|
@@ -394,7 +405,9 @@ export function toInstrumentedOdspTokenFetcher(
|
|
|
394
405
|
...resolvedUrlParts,
|
|
395
406
|
}).then(
|
|
396
407
|
(tokenResponse) => {
|
|
397
|
-
const
|
|
408
|
+
const returnVal = returnPlainToken
|
|
409
|
+
? tokenFromResponse(tokenResponse)
|
|
410
|
+
: authHeaderFromTokenResponse(tokenResponse);
|
|
398
411
|
// This event alone generates so many events that is materially impacts cost of telemetry
|
|
399
412
|
// Thus do not report end event when it comes back quickly.
|
|
400
413
|
// Note that most of the hosts do not report if result is comming from cache or not,
|
|
@@ -403,10 +416,10 @@ export function toInstrumentedOdspTokenFetcher(
|
|
|
403
416
|
if (alwaysRecordTokenFetchTelemetry || event.duration >= 32) {
|
|
404
417
|
event.end({
|
|
405
418
|
fromCache: isTokenFromCache(tokenResponse),
|
|
406
|
-
isNull:
|
|
419
|
+
isNull: returnVal === null,
|
|
407
420
|
});
|
|
408
421
|
}
|
|
409
|
-
if (
|
|
422
|
+
if (returnVal === null && throwOnNullToken) {
|
|
410
423
|
throw new NonRetryableError(
|
|
411
424
|
// pre-0.58 error message: Token is null for ${name} call
|
|
412
425
|
`The Host-provided token fetcher returned null`,
|
|
@@ -414,7 +427,7 @@ export function toInstrumentedOdspTokenFetcher(
|
|
|
414
427
|
{ method: name, driverVersion },
|
|
415
428
|
);
|
|
416
429
|
}
|
|
417
|
-
return
|
|
430
|
+
return returnVal;
|
|
418
431
|
},
|
|
419
432
|
(error) => {
|
|
420
433
|
// There is an important but unofficial contract here where token providers can set canRetry: true
|
package/src/packageVersion.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
OdspResourceTokenFetchOptions,
|
|
16
16
|
TokenFetcher,
|
|
17
17
|
getKeyForCacheEntry,
|
|
18
|
+
type InstrumentedStorageTokenFetcher,
|
|
18
19
|
} from "@fluidframework/odsp-driver-definitions/internal";
|
|
19
20
|
import {
|
|
20
21
|
PerformanceEvent,
|
|
@@ -35,6 +36,7 @@ import {
|
|
|
35
36
|
createOdspLogger,
|
|
36
37
|
getOdspResolvedUrl,
|
|
37
38
|
toInstrumentedOdspStorageTokenFetcher,
|
|
39
|
+
type TokenFetchOptionsEx,
|
|
38
40
|
} from "./odspUtils.js";
|
|
39
41
|
|
|
40
42
|
/**
|
|
@@ -45,7 +47,7 @@ import {
|
|
|
45
47
|
* @param getStorageToken - function that can provide the storage token for a given site. This is
|
|
46
48
|
* is also referred to as the "VROOM" token in SPO.
|
|
47
49
|
* @param persistedCache - Cache to store the fetched snapshot.
|
|
48
|
-
* @param forceAccessTokenViaAuthorizationHeader -
|
|
50
|
+
* @param forceAccessTokenViaAuthorizationHeader - @deprecated Not used, true value always used instead. Whether to force passing given token via authorization header.
|
|
49
51
|
* @param logger - Logger to have telemetry events.
|
|
50
52
|
* @param hostSnapshotFetchOptions - Options to fetch the snapshot if any. Otherwise default will be used.
|
|
51
53
|
* @param enableRedeemFallback - True to have the sharing link redeem fallback in case the Trees Latest/Redeem
|
|
@@ -86,7 +88,7 @@ export async function prefetchLatestSnapshot(
|
|
|
86
88
|
driveId: odspResolvedUrl.driveId,
|
|
87
89
|
itemId: odspResolvedUrl.itemId,
|
|
88
90
|
};
|
|
89
|
-
const
|
|
91
|
+
const getAuthHeader = toInstrumentedOdspStorageTokenFetcher(
|
|
90
92
|
odspLogger,
|
|
91
93
|
resolvedUrlData,
|
|
92
94
|
getStorageToken,
|
|
@@ -94,14 +96,16 @@ export async function prefetchLatestSnapshot(
|
|
|
94
96
|
|
|
95
97
|
const snapshotDownloader = async (
|
|
96
98
|
finalOdspResolvedUrl: IOdspResolvedUrl,
|
|
97
|
-
|
|
99
|
+
storageTokenFetcher: InstrumentedStorageTokenFetcher,
|
|
100
|
+
tokenFetchOptions: TokenFetchOptionsEx,
|
|
98
101
|
loadingGroupId: string[] | undefined,
|
|
99
102
|
snapshotOptions: ISnapshotOptions | undefined,
|
|
100
103
|
controller?: AbortController,
|
|
101
104
|
): Promise<ISnapshotRequestAndResponseOptions> => {
|
|
102
105
|
return downloadSnapshot(
|
|
103
106
|
finalOdspResolvedUrl,
|
|
104
|
-
|
|
107
|
+
storageTokenFetcher,
|
|
108
|
+
tokenFetchOptions,
|
|
105
109
|
loadingGroupId,
|
|
106
110
|
snapshotOptions,
|
|
107
111
|
undefined,
|
|
@@ -135,7 +139,7 @@ export async function prefetchLatestSnapshot(
|
|
|
135
139
|
);
|
|
136
140
|
await fetchSnapshotWithRedeem(
|
|
137
141
|
odspResolvedUrl,
|
|
138
|
-
|
|
142
|
+
getAuthHeader,
|
|
139
143
|
hostSnapshotFetchOptions,
|
|
140
144
|
forceAccessTokenViaAuthorizationHeader,
|
|
141
145
|
odspLogger,
|
package/src/vroom.ts
CHANGED
|
@@ -31,7 +31,7 @@ interface IJoinSessionBody {
|
|
|
31
31
|
* @param path - The API path that is relevant to this request
|
|
32
32
|
* @param method - The type of request, such as GET or POST
|
|
33
33
|
* @param logger - A logger to use for this request
|
|
34
|
-
* @param
|
|
34
|
+
* @param getAuthHeader - A function that is able to provide the access token for this request
|
|
35
35
|
* @param epochTracker - fetch wrapper which incorporates epoch logic around joinSession call
|
|
36
36
|
* @param requestSocketToken - flag indicating whether joinSession is expected to return access token
|
|
37
37
|
* which is used when establishing websocket connection with collab session backend service.
|
|
@@ -44,17 +44,21 @@ interface IJoinSessionBody {
|
|
|
44
44
|
export async function fetchJoinSession(
|
|
45
45
|
urlParts: IOdspUrlParts,
|
|
46
46
|
path: string,
|
|
47
|
-
method:
|
|
47
|
+
method: "GET" | "POST",
|
|
48
48
|
logger: ITelemetryLoggerExt,
|
|
49
|
-
|
|
49
|
+
getAuthHeader: InstrumentedStorageTokenFetcher,
|
|
50
50
|
epochTracker: EpochTracker,
|
|
51
51
|
requestSocketToken: boolean,
|
|
52
52
|
options: TokenFetchOptionsEx,
|
|
53
53
|
disableJoinSessionRefresh: boolean | undefined,
|
|
54
54
|
isRefreshingJoinSession: boolean,
|
|
55
|
-
guestDisplayName?: string,
|
|
56
55
|
): Promise<ISocketStorageDiscovery> {
|
|
57
|
-
const
|
|
56
|
+
const apiRoot = getApiRoot(new URL(urlParts.siteUrl));
|
|
57
|
+
const url = `${apiRoot}/drives/${urlParts.driveId}/items/${urlParts.itemId}/${path}?ump=1`;
|
|
58
|
+
const authHeader = await getAuthHeader(
|
|
59
|
+
{ ...options, request: { url, method } },
|
|
60
|
+
"JoinSession",
|
|
61
|
+
);
|
|
58
62
|
|
|
59
63
|
const tokenRefreshProps = options.refresh
|
|
60
64
|
? { hasClaims: !!options.claims, hasTenantId: !!options.tenantId }
|
|
@@ -75,10 +79,9 @@ export async function fetchJoinSession(
|
|
|
75
79
|
...tokenRefreshProps,
|
|
76
80
|
},
|
|
77
81
|
async (event) => {
|
|
78
|
-
const apiRoot = getApiRoot(new URL(urlParts.siteUrl));
|
|
79
82
|
const formBoundary = uuid();
|
|
80
83
|
let postBody = `--${formBoundary}\r\n`;
|
|
81
|
-
postBody += `Authorization:
|
|
84
|
+
postBody += `Authorization: ${authHeader}\r\n`;
|
|
82
85
|
postBody += `X-HTTP-Method-Override: POST\r\n`;
|
|
83
86
|
postBody += `Content-Type: application/json\r\n`;
|
|
84
87
|
if (!disableJoinSessionRefresh) {
|
|
@@ -90,9 +93,6 @@ export async function fetchJoinSession(
|
|
|
90
93
|
const body: IJoinSessionBody = {
|
|
91
94
|
requestSocketToken: true,
|
|
92
95
|
};
|
|
93
|
-
if (guestDisplayName !== undefined) {
|
|
94
|
-
body.guestDisplayName = guestDisplayName;
|
|
95
|
-
}
|
|
96
96
|
postBody += `\r\n${JSON.stringify(body)}\r\n`;
|
|
97
97
|
}
|
|
98
98
|
postBody += `\r\n--${formBoundary}--`;
|
|
@@ -103,7 +103,7 @@ export async function fetchJoinSession(
|
|
|
103
103
|
const response = await runWithRetry(
|
|
104
104
|
async () =>
|
|
105
105
|
epochTracker.fetchAndParseAsJSON<ISocketStorageDiscovery>(
|
|
106
|
-
|
|
106
|
+
url,
|
|
107
107
|
{ method, headers, body: postBody },
|
|
108
108
|
"joinSession",
|
|
109
109
|
true,
|