@fluid-tools/fetch-tool 2.4.0-297027 → 2.4.0-299374

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.
@@ -1 +1 @@
1
- {"version":3,"file":"fluidFetchSharePoint.d.ts","sourceRoot":"","sources":["../src/fluidFetchSharePoint.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EACN,mBAAmB,EACnB,oBAAoB,EACpB,cAAc,EAMd,MAAM,4CAA4C,CAAC;AASpD,eAAO,MAAM,qBAAqB,EAAE,mBAUnC,CAAC;AAEF,wBAAsB,cAAc,CAAC,CAAC,EACrC,QAAQ,EAAE,CAAC,eAAe,EAAE,oBAAoB,KAAK,OAAO,CAAC,CAAC,CAAC,EAC/D,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,mBAAmB,EACjC,gBAAgB,UAAQ,GACtB,OAAO,CAAC,CAAC,CAAC,CAwCZ;AA6BD,wBAAsB,kBAAkB,CACvC,MAAM,EAAE,MAAM,EACd,kBAAkB,EAAE,MAAM,EAC1B,OAAO,EAAE,OAAO,6BAoChB;AAED,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,2BAOxF"}
1
+ {"version":3,"file":"fluidFetchSharePoint.d.ts","sourceRoot":"","sources":["../src/fluidFetchSharePoint.ts"],"names":[],"mappings":"AAAA;;;GAGG;AASH,OAAO,EACN,mBAAmB,EACnB,oBAAoB,EACpB,cAAc,EAMd,MAAM,4CAA4C,CAAC;AASpD,eAAO,MAAM,qBAAqB,EAAE,mBAUnC,CAAC;AAaF,wBAAsB,cAAc,CAAC,CAAC,EACrC,QAAQ,EAAE,CAAC,eAAe,EAAE,oBAAoB,KAAK,OAAO,CAAC,CAAC,CAAC,EAC/D,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,mBAAmB,EACjC,gBAAgB,UAAQ,GACtB,OAAO,CAAC,CAAC,CAAC,CAmDZ;AA6BD,wBAAsB,kBAAkB,CACvC,MAAM,EAAE,MAAM,EACd,kBAAkB,EAAE,MAAM,EAC1B,OAAO,EAAE,OAAO,6BAoChB;AAED,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,2BAOxF"}
@@ -23,8 +23,18 @@ exports.fetchToolClientConfig = {
23
23
  return clientId;
24
24
  },
25
25
  };
26
+ // Local token cache for resolveWrapper.
27
+ // @azure/identity-cache-persistence does not behave well in response to large numbers of parallel requests, which can happen for documents
28
+ // with lots of blobs. We work around this for now by including a simple in-memory cache.
29
+ // See more information here:
30
+ // https://github.com/Azure/azure-sdk-for-js/issues/31307
31
+ const tokensByServer = new Map();
32
+ // If the persisted cache has multiple accounts, InteractiveBrowserCredential ignores it unless it is passed an explicit authentication record.
33
+ // We keep the auth record around for a single run in memory, so that at worst we only have to authenticate once per server/user.
34
+ const authRecordPerServer = new Map();
26
35
  async function resolveWrapper(callback, server, clientConfig, forceTokenReauth = false) {
27
36
  try {
37
+ const authenticationRecord = authRecordPerServer.get(server);
28
38
  const credential = new identity_1.InteractiveBrowserCredential({
29
39
  clientId: exports.fetchToolClientConfig.clientId,
30
40
  tenantId: (0, internal_2.getAadTenant)(server),
@@ -38,19 +48,30 @@ async function resolveWrapper(callback, server, clientConfig, forceTokenReauth =
38
48
  // In that case, a simple workaround is to delete the cache that @azure/identity uses before running the tool.
39
49
  // See docs on `tokenCachePersistenceOptions.name` for information on where this cache is stored.
40
50
  loginHint: fluidFetchArgs_js_1.loginHint,
51
+ authenticationRecord,
41
52
  tokenCachePersistenceOptions: {
42
53
  enabled: true,
43
54
  name: "fetch-tool",
44
55
  },
45
56
  });
46
57
  const scope = (0, internal_2.getOdspScope)(server);
47
- const { token } = await credential.getToken(scope);
58
+ if (authenticationRecord === undefined) {
59
+ // Cache this authentication record for subsequent token requests.
60
+ authRecordPerServer.set(server, await credential.authenticate(scope));
61
+ }
62
+ let cachedToken = tokensByServer.get(server);
63
+ if (cachedToken === undefined || forceTokenReauth) {
64
+ const result = await credential.getToken(scope);
65
+ cachedToken = result.token;
66
+ tokensByServer.set(server, cachedToken);
67
+ }
48
68
  return await callback({
49
- accessToken: token,
69
+ accessToken: cachedToken,
50
70
  refreshTokenFn: async () => {
51
71
  await credential.authenticate(scope);
52
- const result = await credential.getToken(scope);
53
- return result.token;
72
+ const { token } = await credential.getToken(scope);
73
+ tokensByServer.set(server, token);
74
+ return token;
54
75
  },
55
76
  });
56
77
  }
@@ -1 +1 @@
1
- {"version":3,"file":"fluidFetchSharePoint.js","sourceRoot":"","sources":["../src/fluidFetchSharePoint.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { InteractiveBrowserCredential, useIdentityPlugin } from \"@azure/identity\";\nimport { cachePersistencePlugin } from \"@azure/identity-cache-persistence\";\nimport { DriverErrorTypes } from \"@fluidframework/driver-definitions/internal\";\nimport {\n\tIPublicClientConfig,\n\tIOdspAuthRequestInfo,\n\tIOdspDriveItem,\n\tgetChildrenByDriveItem,\n\tgetDriveItemByServerRelativePath,\n\tgetDriveItemFromDriveAndItem,\n\tgetAadTenant,\n\tgetOdspScope,\n} from \"@fluidframework/odsp-doclib-utils/internal\";\n\nimport { loginHint } from \"./fluidFetchArgs.js\";\n\n// Note: the following page may be helpful for debugging auth issues:\n// https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/identity/identity/TROUBLESHOOTING.md\n// See e.g. the section on setting 'AZURE_LOG_LEVEL'.\nuseIdentityPlugin(cachePersistencePlugin);\n\nexport const fetchToolClientConfig: IPublicClientConfig = {\n\tget clientId(): string {\n\t\tconst clientId = process.env.fetch__tool__clientId;\n\t\tif (clientId === undefined) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Client ID environment variable not set: fetch__tool__clientId. Use the getkeys tool to populate it.\",\n\t\t\t);\n\t\t}\n\t\treturn clientId;\n\t},\n};\n\nexport async function resolveWrapper<T>(\n\tcallback: (authRequestInfo: IOdspAuthRequestInfo) => Promise<T>,\n\tserver: string,\n\tclientConfig: IPublicClientConfig,\n\tforceTokenReauth = false,\n): Promise<T> {\n\ttry {\n\t\tconst credential = new InteractiveBrowserCredential({\n\t\t\tclientId: fetchToolClientConfig.clientId,\n\t\t\ttenantId: getAadTenant(server),\n\t\t\t// NOTE: fetch-tool flows using multiple sets of user credentials haven't been well-tested.\n\t\t\t// Some of the @azure/identity docs suggest we may need to manage authentication records and choose\n\t\t\t// which one to use explicitly here if we have such scenarios.\n\t\t\t// If we start doing this, it may be worth considering using disableAutomaticAuthentication here so we\n\t\t\t// have better control over when interactive auth may be triggered.\n\t\t\t// For now, fetch-tool doesn't work against personal accounts anyway so the only flow that might necessitate this\n\t\t\t// would be grabbing documents using several identities (e.g. test accounts we use for stress testing).\n\t\t\t// In that case, a simple workaround is to delete the cache that @azure/identity uses before running the tool.\n\t\t\t// See docs on `tokenCachePersistenceOptions.name` for information on where this cache is stored.\n\t\t\tloginHint,\n\t\t\ttokenCachePersistenceOptions: {\n\t\t\t\tenabled: true,\n\t\t\t\tname: \"fetch-tool\",\n\t\t\t},\n\t\t});\n\n\t\tconst scope = getOdspScope(server);\n\n\t\tconst { token } = await credential.getToken(scope);\n\n\t\treturn await callback({\n\t\t\taccessToken: token,\n\t\t\trefreshTokenFn: async () => {\n\t\t\t\tawait credential.authenticate(scope);\n\t\t\t\tconst result = await credential.getToken(scope);\n\t\t\t\treturn result.token;\n\t\t\t},\n\t\t});\n\t} catch (e: any) {\n\t\tif (e.errorType === DriverErrorTypes.authorizationError && !forceTokenReauth) {\n\t\t\t// Re-auth\n\t\t\treturn resolveWrapper<T>(callback, server, clientConfig, true);\n\t\t}\n\t\tthrow e;\n\t}\n}\n\nasync function resolveDriveItemByServerRelativePath(\n\tserver: string,\n\tserverRelativePath: string,\n\tclientConfig: IPublicClientConfig,\n) {\n\treturn resolveWrapper<IOdspDriveItem>(\n\t\t// eslint-disable-next-line @typescript-eslint/promise-function-async\n\t\t(authRequestInfo) =>\n\t\t\tgetDriveItemByServerRelativePath(server, serverRelativePath, authRequestInfo, false),\n\t\tserver,\n\t\tclientConfig,\n\t);\n}\n\nasync function resolveChildrenByDriveItem(\n\tserver: string,\n\tfolderDriveItem: IOdspDriveItem,\n\tclientConfig: IPublicClientConfig,\n) {\n\treturn resolveWrapper<IOdspDriveItem[]>(\n\t\t// eslint-disable-next-line @typescript-eslint/promise-function-async\n\t\t(authRequestInfo) => getChildrenByDriveItem(folderDriveItem, server, authRequestInfo),\n\t\tserver,\n\t\tclientConfig,\n\t);\n}\n\nexport async function getSharepointFiles(\n\tserver: string,\n\tserverRelativePath: string,\n\trecurse: boolean,\n) {\n\tconst fileInfo = await resolveDriveItemByServerRelativePath(\n\t\tserver,\n\t\tserverRelativePath,\n\t\tfetchToolClientConfig,\n\t);\n\tconsole.log(fileInfo);\n\tconst pendingFolder: { path: string; folder: IOdspDriveItem }[] = [];\n\tconst files: IOdspDriveItem[] = [];\n\tif (fileInfo.isFolder) {\n\t\tpendingFolder.push({ path: serverRelativePath, folder: fileInfo });\n\t} else {\n\t\tfiles.push(fileInfo);\n\t}\n\n\t// eslint-disable-next-line no-constant-condition\n\twhile (true) {\n\t\tconst folderInfo = pendingFolder.shift();\n\t\tif (!folderInfo) {\n\t\t\tbreak;\n\t\t}\n\t\tconst { path, folder } = folderInfo;\n\t\tconst children = await resolveChildrenByDriveItem(server, folder, fetchToolClientConfig);\n\t\tfor (const child of children) {\n\t\t\tconst childPath = `${path}/${child.name}`;\n\t\t\tif (child.isFolder) {\n\t\t\t\tif (recurse) {\n\t\t\t\t\tpendingFolder.push({ path: childPath, folder: child });\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfiles.push(child);\n\t\t\t}\n\t\t}\n\t}\n\treturn files;\n}\n\nexport async function getSingleSharePointFile(server: string, drive: string, item: string) {\n\treturn resolveWrapper<IOdspDriveItem>(\n\t\t// eslint-disable-next-line @typescript-eslint/promise-function-async\n\t\t(authRequestInfo) => getDriveItemFromDriveAndItem(server, drive, item, authRequestInfo),\n\t\tserver,\n\t\tfetchToolClientConfig,\n\t);\n}\n"]}
1
+ {"version":3,"file":"fluidFetchSharePoint.js","sourceRoot":"","sources":["../src/fluidFetchSharePoint.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,8CAIyB;AACzB,kFAA2E;AAC3E,0EAA+E;AAC/E,yEASoD;AAEpD,2DAAgD;AAEhD,qEAAqE;AACrE,+FAA+F;AAC/F,qDAAqD;AACrD,IAAA,4BAAiB,EAAC,mDAAsB,CAAC,CAAC;AAE7B,QAAA,qBAAqB,GAAwB;IACzD,IAAI,QAAQ;QACX,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC;QACnD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CACd,qGAAqG,CACrG,CAAC;QACH,CAAC;QACD,OAAO,QAAQ,CAAC;IACjB,CAAC;CACD,CAAC;AAEF,wCAAwC;AACxC,2IAA2I;AAC3I,yFAAyF;AACzF,6BAA6B;AAC7B,yDAAyD;AACzD,MAAM,cAAc,GAAG,IAAI,GAAG,EAAkB,CAAC;AAEjD,+IAA+I;AAC/I,iIAAiI;AACjI,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAA4C,CAAC;AAEzE,KAAK,UAAU,cAAc,CACnC,QAA+D,EAC/D,MAAc,EACd,YAAiC,EACjC,gBAAgB,GAAG,KAAK;IAExB,IAAI,CAAC;QACJ,MAAM,oBAAoB,GAAG,mBAAmB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC7D,MAAM,UAAU,GAAG,IAAI,uCAA4B,CAAC;YACnD,QAAQ,EAAE,6BAAqB,CAAC,QAAQ;YACxC,QAAQ,EAAE,IAAA,uBAAY,EAAC,MAAM,CAAC;YAC9B,2FAA2F;YAC3F,mGAAmG;YACnG,8DAA8D;YAC9D,sGAAsG;YACtG,mEAAmE;YACnE,iHAAiH;YACjH,uGAAuG;YACvG,8GAA8G;YAC9G,iGAAiG;YACjG,SAAS,EAAT,6BAAS;YACT,oBAAoB;YACpB,4BAA4B,EAAE;gBAC7B,OAAO,EAAE,IAAI;gBACb,IAAI,EAAE,YAAY;aAClB;SACD,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,IAAA,uBAAY,EAAC,MAAM,CAAC,CAAC;QACnC,IAAI,oBAAoB,KAAK,SAAS,EAAE,CAAC;YACxC,kEAAkE;YAClE,mBAAmB,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,UAAU,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;QACvE,CAAC;QACD,IAAI,WAAW,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC7C,IAAI,WAAW,KAAK,SAAS,IAAI,gBAAgB,EAAE,CAAC;YACnD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YAChD,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC;YAC3B,cAAc,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QACzC,CAAC;QAED,OAAO,MAAM,QAAQ,CAAC;YACrB,WAAW,EAAE,WAAW;YACxB,cAAc,EAAE,KAAK,IAAI,EAAE;gBAC1B,MAAM,UAAU,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;gBACrC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;gBACnD,cAAc,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;gBAClC,OAAO,KAAK,CAAC;YACd,CAAC;SACD,CAAC,CAAC;IACJ,CAAC;IAAC,OAAO,CAAM,EAAE,CAAC;QACjB,IAAI,CAAC,CAAC,SAAS,KAAK,2BAAgB,CAAC,kBAAkB,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC9E,UAAU;YACV,OAAO,cAAc,CAAI,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC;QAChE,CAAC;QACD,MAAM,CAAC,CAAC;IACT,CAAC;AACF,CAAC;AAxDD,wCAwDC;AAED,KAAK,UAAU,oCAAoC,CAClD,MAAc,EACd,kBAA0B,EAC1B,YAAiC;IAEjC,OAAO,cAAc;IACpB,qEAAqE;IACrE,CAAC,eAAe,EAAE,EAAE,CACnB,IAAA,2CAAgC,EAAC,MAAM,EAAE,kBAAkB,EAAE,eAAe,EAAE,KAAK,CAAC,EACrF,MAAM,EACN,YAAY,CACZ,CAAC;AACH,CAAC;AAED,KAAK,UAAU,0BAA0B,CACxC,MAAc,EACd,eAA+B,EAC/B,YAAiC;IAEjC,OAAO,cAAc;IACpB,qEAAqE;IACrE,CAAC,eAAe,EAAE,EAAE,CAAC,IAAA,iCAAsB,EAAC,eAAe,EAAE,MAAM,EAAE,eAAe,CAAC,EACrF,MAAM,EACN,YAAY,CACZ,CAAC;AACH,CAAC;AAEM,KAAK,UAAU,kBAAkB,CACvC,MAAc,EACd,kBAA0B,EAC1B,OAAgB;IAEhB,MAAM,QAAQ,GAAG,MAAM,oCAAoC,CAC1D,MAAM,EACN,kBAAkB,EAClB,6BAAqB,CACrB,CAAC;IACF,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACtB,MAAM,aAAa,GAA+C,EAAE,CAAC;IACrE,MAAM,KAAK,GAAqB,EAAE,CAAC;IACnC,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;QACvB,aAAa,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;IACpE,CAAC;SAAM,CAAC;QACP,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACtB,CAAC;IAED,iDAAiD;IACjD,OAAO,IAAI,EAAE,CAAC;QACb,MAAM,UAAU,GAAG,aAAa,CAAC,KAAK,EAAE,CAAC;QACzC,IAAI,CAAC,UAAU,EAAE,CAAC;YACjB,MAAM;QACP,CAAC;QACD,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC;QACpC,MAAM,QAAQ,GAAG,MAAM,0BAA0B,CAAC,MAAM,EAAE,MAAM,EAAE,6BAAqB,CAAC,CAAC;QACzF,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;YAC9B,MAAM,SAAS,GAAG,GAAG,IAAI,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;YAC1C,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;gBACpB,IAAI,OAAO,EAAE,CAAC;oBACb,aAAa,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;gBACxD,CAAC;YACF,CAAC;iBAAM,CAAC;gBACP,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnB,CAAC;QACF,CAAC;IACF,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAvCD,gDAuCC;AAEM,KAAK,UAAU,uBAAuB,CAAC,MAAc,EAAE,KAAa,EAAE,IAAY;IACxF,OAAO,cAAc;IACpB,qEAAqE;IACrE,CAAC,eAAe,EAAE,EAAE,CAAC,IAAA,uCAA4B,EAAC,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,eAAe,CAAC,EACvF,MAAM,EACN,6BAAqB,CACrB,CAAC;AACH,CAAC;AAPD,0DAOC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport {\n\tInteractiveBrowserCredential,\n\tuseIdentityPlugin,\n\ttype AuthenticationRecord,\n} from \"@azure/identity\";\nimport { cachePersistencePlugin } from \"@azure/identity-cache-persistence\";\nimport { DriverErrorTypes } from \"@fluidframework/driver-definitions/internal\";\nimport {\n\tIPublicClientConfig,\n\tIOdspAuthRequestInfo,\n\tIOdspDriveItem,\n\tgetChildrenByDriveItem,\n\tgetDriveItemByServerRelativePath,\n\tgetDriveItemFromDriveAndItem,\n\tgetAadTenant,\n\tgetOdspScope,\n} from \"@fluidframework/odsp-doclib-utils/internal\";\n\nimport { loginHint } from \"./fluidFetchArgs.js\";\n\n// Note: the following page may be helpful for debugging auth issues:\n// https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/identity/identity/TROUBLESHOOTING.md\n// See e.g. the section on setting 'AZURE_LOG_LEVEL'.\nuseIdentityPlugin(cachePersistencePlugin);\n\nexport const fetchToolClientConfig: IPublicClientConfig = {\n\tget clientId(): string {\n\t\tconst clientId = process.env.fetch__tool__clientId;\n\t\tif (clientId === undefined) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Client ID environment variable not set: fetch__tool__clientId. Use the getkeys tool to populate it.\",\n\t\t\t);\n\t\t}\n\t\treturn clientId;\n\t},\n};\n\n// Local token cache for resolveWrapper.\n// @azure/identity-cache-persistence does not behave well in response to large numbers of parallel requests, which can happen for documents\n// with lots of blobs. We work around this for now by including a simple in-memory cache.\n// See more information here:\n// https://github.com/Azure/azure-sdk-for-js/issues/31307\nconst tokensByServer = new Map<string, string>();\n\n// If the persisted cache has multiple accounts, InteractiveBrowserCredential ignores it unless it is passed an explicit authentication record.\n// We keep the auth record around for a single run in memory, so that at worst we only have to authenticate once per server/user.\nconst authRecordPerServer = new Map<string, AuthenticationRecord | undefined>();\n\nexport async function resolveWrapper<T>(\n\tcallback: (authRequestInfo: IOdspAuthRequestInfo) => Promise<T>,\n\tserver: string,\n\tclientConfig: IPublicClientConfig,\n\tforceTokenReauth = false,\n): Promise<T> {\n\ttry {\n\t\tconst authenticationRecord = authRecordPerServer.get(server);\n\t\tconst credential = new InteractiveBrowserCredential({\n\t\t\tclientId: fetchToolClientConfig.clientId,\n\t\t\ttenantId: getAadTenant(server),\n\t\t\t// NOTE: fetch-tool flows using multiple sets of user credentials haven't been well-tested.\n\t\t\t// Some of the @azure/identity docs suggest we may need to manage authentication records and choose\n\t\t\t// which one to use explicitly here if we have such scenarios.\n\t\t\t// If we start doing this, it may be worth considering using disableAutomaticAuthentication here so we\n\t\t\t// have better control over when interactive auth may be triggered.\n\t\t\t// For now, fetch-tool doesn't work against personal accounts anyway so the only flow that might necessitate this\n\t\t\t// would be grabbing documents using several identities (e.g. test accounts we use for stress testing).\n\t\t\t// In that case, a simple workaround is to delete the cache that @azure/identity uses before running the tool.\n\t\t\t// See docs on `tokenCachePersistenceOptions.name` for information on where this cache is stored.\n\t\t\tloginHint,\n\t\t\tauthenticationRecord,\n\t\t\ttokenCachePersistenceOptions: {\n\t\t\t\tenabled: true,\n\t\t\t\tname: \"fetch-tool\",\n\t\t\t},\n\t\t});\n\n\t\tconst scope = getOdspScope(server);\n\t\tif (authenticationRecord === undefined) {\n\t\t\t// Cache this authentication record for subsequent token requests.\n\t\t\tauthRecordPerServer.set(server, await credential.authenticate(scope));\n\t\t}\n\t\tlet cachedToken = tokensByServer.get(server);\n\t\tif (cachedToken === undefined || forceTokenReauth) {\n\t\t\tconst result = await credential.getToken(scope);\n\t\t\tcachedToken = result.token;\n\t\t\ttokensByServer.set(server, cachedToken);\n\t\t}\n\n\t\treturn await callback({\n\t\t\taccessToken: cachedToken,\n\t\t\trefreshTokenFn: async () => {\n\t\t\t\tawait credential.authenticate(scope);\n\t\t\t\tconst { token } = await credential.getToken(scope);\n\t\t\t\ttokensByServer.set(server, token);\n\t\t\t\treturn token;\n\t\t\t},\n\t\t});\n\t} catch (e: any) {\n\t\tif (e.errorType === DriverErrorTypes.authorizationError && !forceTokenReauth) {\n\t\t\t// Re-auth\n\t\t\treturn resolveWrapper<T>(callback, server, clientConfig, true);\n\t\t}\n\t\tthrow e;\n\t}\n}\n\nasync function resolveDriveItemByServerRelativePath(\n\tserver: string,\n\tserverRelativePath: string,\n\tclientConfig: IPublicClientConfig,\n) {\n\treturn resolveWrapper<IOdspDriveItem>(\n\t\t// eslint-disable-next-line @typescript-eslint/promise-function-async\n\t\t(authRequestInfo) =>\n\t\t\tgetDriveItemByServerRelativePath(server, serverRelativePath, authRequestInfo, false),\n\t\tserver,\n\t\tclientConfig,\n\t);\n}\n\nasync function resolveChildrenByDriveItem(\n\tserver: string,\n\tfolderDriveItem: IOdspDriveItem,\n\tclientConfig: IPublicClientConfig,\n) {\n\treturn resolveWrapper<IOdspDriveItem[]>(\n\t\t// eslint-disable-next-line @typescript-eslint/promise-function-async\n\t\t(authRequestInfo) => getChildrenByDriveItem(folderDriveItem, server, authRequestInfo),\n\t\tserver,\n\t\tclientConfig,\n\t);\n}\n\nexport async function getSharepointFiles(\n\tserver: string,\n\tserverRelativePath: string,\n\trecurse: boolean,\n) {\n\tconst fileInfo = await resolveDriveItemByServerRelativePath(\n\t\tserver,\n\t\tserverRelativePath,\n\t\tfetchToolClientConfig,\n\t);\n\tconsole.log(fileInfo);\n\tconst pendingFolder: { path: string; folder: IOdspDriveItem }[] = [];\n\tconst files: IOdspDriveItem[] = [];\n\tif (fileInfo.isFolder) {\n\t\tpendingFolder.push({ path: serverRelativePath, folder: fileInfo });\n\t} else {\n\t\tfiles.push(fileInfo);\n\t}\n\n\t// eslint-disable-next-line no-constant-condition\n\twhile (true) {\n\t\tconst folderInfo = pendingFolder.shift();\n\t\tif (!folderInfo) {\n\t\t\tbreak;\n\t\t}\n\t\tconst { path, folder } = folderInfo;\n\t\tconst children = await resolveChildrenByDriveItem(server, folder, fetchToolClientConfig);\n\t\tfor (const child of children) {\n\t\t\tconst childPath = `${path}/${child.name}`;\n\t\t\tif (child.isFolder) {\n\t\t\t\tif (recurse) {\n\t\t\t\t\tpendingFolder.push({ path: childPath, folder: child });\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfiles.push(child);\n\t\t\t}\n\t\t}\n\t}\n\treturn files;\n}\n\nexport async function getSingleSharePointFile(server: string, drive: string, item: string) {\n\treturn resolveWrapper<IOdspDriveItem>(\n\t\t// eslint-disable-next-line @typescript-eslint/promise-function-async\n\t\t(authRequestInfo) => getDriveItemFromDriveAndItem(server, drive, item, authRequestInfo),\n\t\tserver,\n\t\tfetchToolClientConfig,\n\t);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"fluidFetchSharePoint.d.ts","sourceRoot":"","sources":["../src/fluidFetchSharePoint.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EACN,mBAAmB,EACnB,oBAAoB,EACpB,cAAc,EAMd,MAAM,4CAA4C,CAAC;AASpD,eAAO,MAAM,qBAAqB,EAAE,mBAUnC,CAAC;AAEF,wBAAsB,cAAc,CAAC,CAAC,EACrC,QAAQ,EAAE,CAAC,eAAe,EAAE,oBAAoB,KAAK,OAAO,CAAC,CAAC,CAAC,EAC/D,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,mBAAmB,EACjC,gBAAgB,UAAQ,GACtB,OAAO,CAAC,CAAC,CAAC,CAwCZ;AA6BD,wBAAsB,kBAAkB,CACvC,MAAM,EAAE,MAAM,EACd,kBAAkB,EAAE,MAAM,EAC1B,OAAO,EAAE,OAAO,6BAoChB;AAED,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,2BAOxF"}
1
+ {"version":3,"file":"fluidFetchSharePoint.d.ts","sourceRoot":"","sources":["../src/fluidFetchSharePoint.ts"],"names":[],"mappings":"AAAA;;;GAGG;AASH,OAAO,EACN,mBAAmB,EACnB,oBAAoB,EACpB,cAAc,EAMd,MAAM,4CAA4C,CAAC;AASpD,eAAO,MAAM,qBAAqB,EAAE,mBAUnC,CAAC;AAaF,wBAAsB,cAAc,CAAC,CAAC,EACrC,QAAQ,EAAE,CAAC,eAAe,EAAE,oBAAoB,KAAK,OAAO,CAAC,CAAC,CAAC,EAC/D,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,mBAAmB,EACjC,gBAAgB,UAAQ,GACtB,OAAO,CAAC,CAAC,CAAC,CAmDZ;AA6BD,wBAAsB,kBAAkB,CACvC,MAAM,EAAE,MAAM,EACd,kBAAkB,EAAE,MAAM,EAC1B,OAAO,EAAE,OAAO,6BAoChB;AAED,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,2BAOxF"}
@@ -2,7 +2,7 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
- import { InteractiveBrowserCredential, useIdentityPlugin } from "@azure/identity";
5
+ import { InteractiveBrowserCredential, useIdentityPlugin, } from "@azure/identity";
6
6
  import { cachePersistencePlugin } from "@azure/identity-cache-persistence";
7
7
  import { DriverErrorTypes } from "@fluidframework/driver-definitions/internal";
8
8
  import { getChildrenByDriveItem, getDriveItemByServerRelativePath, getDriveItemFromDriveAndItem, getAadTenant, getOdspScope, } from "@fluidframework/odsp-doclib-utils/internal";
@@ -20,8 +20,18 @@ export const fetchToolClientConfig = {
20
20
  return clientId;
21
21
  },
22
22
  };
23
+ // Local token cache for resolveWrapper.
24
+ // @azure/identity-cache-persistence does not behave well in response to large numbers of parallel requests, which can happen for documents
25
+ // with lots of blobs. We work around this for now by including a simple in-memory cache.
26
+ // See more information here:
27
+ // https://github.com/Azure/azure-sdk-for-js/issues/31307
28
+ const tokensByServer = new Map();
29
+ // If the persisted cache has multiple accounts, InteractiveBrowserCredential ignores it unless it is passed an explicit authentication record.
30
+ // We keep the auth record around for a single run in memory, so that at worst we only have to authenticate once per server/user.
31
+ const authRecordPerServer = new Map();
23
32
  export async function resolveWrapper(callback, server, clientConfig, forceTokenReauth = false) {
24
33
  try {
34
+ const authenticationRecord = authRecordPerServer.get(server);
25
35
  const credential = new InteractiveBrowserCredential({
26
36
  clientId: fetchToolClientConfig.clientId,
27
37
  tenantId: getAadTenant(server),
@@ -35,19 +45,30 @@ export async function resolveWrapper(callback, server, clientConfig, forceTokenR
35
45
  // In that case, a simple workaround is to delete the cache that @azure/identity uses before running the tool.
36
46
  // See docs on `tokenCachePersistenceOptions.name` for information on where this cache is stored.
37
47
  loginHint,
48
+ authenticationRecord,
38
49
  tokenCachePersistenceOptions: {
39
50
  enabled: true,
40
51
  name: "fetch-tool",
41
52
  },
42
53
  });
43
54
  const scope = getOdspScope(server);
44
- const { token } = await credential.getToken(scope);
55
+ if (authenticationRecord === undefined) {
56
+ // Cache this authentication record for subsequent token requests.
57
+ authRecordPerServer.set(server, await credential.authenticate(scope));
58
+ }
59
+ let cachedToken = tokensByServer.get(server);
60
+ if (cachedToken === undefined || forceTokenReauth) {
61
+ const result = await credential.getToken(scope);
62
+ cachedToken = result.token;
63
+ tokensByServer.set(server, cachedToken);
64
+ }
45
65
  return await callback({
46
- accessToken: token,
66
+ accessToken: cachedToken,
47
67
  refreshTokenFn: async () => {
48
68
  await credential.authenticate(scope);
49
- const result = await credential.getToken(scope);
50
- return result.token;
69
+ const { token } = await credential.getToken(scope);
70
+ tokensByServer.set(server, token);
71
+ return token;
51
72
  },
52
73
  });
53
74
  }
@@ -1 +1 @@
1
- {"version":3,"file":"fluidFetchSharePoint.js","sourceRoot":"","sources":["../src/fluidFetchSharePoint.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { InteractiveBrowserCredential, useIdentityPlugin } from \"@azure/identity\";\nimport { cachePersistencePlugin } from \"@azure/identity-cache-persistence\";\nimport { DriverErrorTypes } from \"@fluidframework/driver-definitions/internal\";\nimport {\n\tIPublicClientConfig,\n\tIOdspAuthRequestInfo,\n\tIOdspDriveItem,\n\tgetChildrenByDriveItem,\n\tgetDriveItemByServerRelativePath,\n\tgetDriveItemFromDriveAndItem,\n\tgetAadTenant,\n\tgetOdspScope,\n} from \"@fluidframework/odsp-doclib-utils/internal\";\n\nimport { loginHint } from \"./fluidFetchArgs.js\";\n\n// Note: the following page may be helpful for debugging auth issues:\n// https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/identity/identity/TROUBLESHOOTING.md\n// See e.g. the section on setting 'AZURE_LOG_LEVEL'.\nuseIdentityPlugin(cachePersistencePlugin);\n\nexport const fetchToolClientConfig: IPublicClientConfig = {\n\tget clientId(): string {\n\t\tconst clientId = process.env.fetch__tool__clientId;\n\t\tif (clientId === undefined) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Client ID environment variable not set: fetch__tool__clientId. Use the getkeys tool to populate it.\",\n\t\t\t);\n\t\t}\n\t\treturn clientId;\n\t},\n};\n\nexport async function resolveWrapper<T>(\n\tcallback: (authRequestInfo: IOdspAuthRequestInfo) => Promise<T>,\n\tserver: string,\n\tclientConfig: IPublicClientConfig,\n\tforceTokenReauth = false,\n): Promise<T> {\n\ttry {\n\t\tconst credential = new InteractiveBrowserCredential({\n\t\t\tclientId: fetchToolClientConfig.clientId,\n\t\t\ttenantId: getAadTenant(server),\n\t\t\t// NOTE: fetch-tool flows using multiple sets of user credentials haven't been well-tested.\n\t\t\t// Some of the @azure/identity docs suggest we may need to manage authentication records and choose\n\t\t\t// which one to use explicitly here if we have such scenarios.\n\t\t\t// If we start doing this, it may be worth considering using disableAutomaticAuthentication here so we\n\t\t\t// have better control over when interactive auth may be triggered.\n\t\t\t// For now, fetch-tool doesn't work against personal accounts anyway so the only flow that might necessitate this\n\t\t\t// would be grabbing documents using several identities (e.g. test accounts we use for stress testing).\n\t\t\t// In that case, a simple workaround is to delete the cache that @azure/identity uses before running the tool.\n\t\t\t// See docs on `tokenCachePersistenceOptions.name` for information on where this cache is stored.\n\t\t\tloginHint,\n\t\t\ttokenCachePersistenceOptions: {\n\t\t\t\tenabled: true,\n\t\t\t\tname: \"fetch-tool\",\n\t\t\t},\n\t\t});\n\n\t\tconst scope = getOdspScope(server);\n\n\t\tconst { token } = await credential.getToken(scope);\n\n\t\treturn await callback({\n\t\t\taccessToken: token,\n\t\t\trefreshTokenFn: async () => {\n\t\t\t\tawait credential.authenticate(scope);\n\t\t\t\tconst result = await credential.getToken(scope);\n\t\t\t\treturn result.token;\n\t\t\t},\n\t\t});\n\t} catch (e: any) {\n\t\tif (e.errorType === DriverErrorTypes.authorizationError && !forceTokenReauth) {\n\t\t\t// Re-auth\n\t\t\treturn resolveWrapper<T>(callback, server, clientConfig, true);\n\t\t}\n\t\tthrow e;\n\t}\n}\n\nasync function resolveDriveItemByServerRelativePath(\n\tserver: string,\n\tserverRelativePath: string,\n\tclientConfig: IPublicClientConfig,\n) {\n\treturn resolveWrapper<IOdspDriveItem>(\n\t\t// eslint-disable-next-line @typescript-eslint/promise-function-async\n\t\t(authRequestInfo) =>\n\t\t\tgetDriveItemByServerRelativePath(server, serverRelativePath, authRequestInfo, false),\n\t\tserver,\n\t\tclientConfig,\n\t);\n}\n\nasync function resolveChildrenByDriveItem(\n\tserver: string,\n\tfolderDriveItem: IOdspDriveItem,\n\tclientConfig: IPublicClientConfig,\n) {\n\treturn resolveWrapper<IOdspDriveItem[]>(\n\t\t// eslint-disable-next-line @typescript-eslint/promise-function-async\n\t\t(authRequestInfo) => getChildrenByDriveItem(folderDriveItem, server, authRequestInfo),\n\t\tserver,\n\t\tclientConfig,\n\t);\n}\n\nexport async function getSharepointFiles(\n\tserver: string,\n\tserverRelativePath: string,\n\trecurse: boolean,\n) {\n\tconst fileInfo = await resolveDriveItemByServerRelativePath(\n\t\tserver,\n\t\tserverRelativePath,\n\t\tfetchToolClientConfig,\n\t);\n\tconsole.log(fileInfo);\n\tconst pendingFolder: { path: string; folder: IOdspDriveItem }[] = [];\n\tconst files: IOdspDriveItem[] = [];\n\tif (fileInfo.isFolder) {\n\t\tpendingFolder.push({ path: serverRelativePath, folder: fileInfo });\n\t} else {\n\t\tfiles.push(fileInfo);\n\t}\n\n\t// eslint-disable-next-line no-constant-condition\n\twhile (true) {\n\t\tconst folderInfo = pendingFolder.shift();\n\t\tif (!folderInfo) {\n\t\t\tbreak;\n\t\t}\n\t\tconst { path, folder } = folderInfo;\n\t\tconst children = await resolveChildrenByDriveItem(server, folder, fetchToolClientConfig);\n\t\tfor (const child of children) {\n\t\t\tconst childPath = `${path}/${child.name}`;\n\t\t\tif (child.isFolder) {\n\t\t\t\tif (recurse) {\n\t\t\t\t\tpendingFolder.push({ path: childPath, folder: child });\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfiles.push(child);\n\t\t\t}\n\t\t}\n\t}\n\treturn files;\n}\n\nexport async function getSingleSharePointFile(server: string, drive: string, item: string) {\n\treturn resolveWrapper<IOdspDriveItem>(\n\t\t// eslint-disable-next-line @typescript-eslint/promise-function-async\n\t\t(authRequestInfo) => getDriveItemFromDriveAndItem(server, drive, item, authRequestInfo),\n\t\tserver,\n\t\tfetchToolClientConfig,\n\t);\n}\n"]}
1
+ {"version":3,"file":"fluidFetchSharePoint.js","sourceRoot":"","sources":["../src/fluidFetchSharePoint.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport {\n\tInteractiveBrowserCredential,\n\tuseIdentityPlugin,\n\ttype AuthenticationRecord,\n} from \"@azure/identity\";\nimport { cachePersistencePlugin } from \"@azure/identity-cache-persistence\";\nimport { DriverErrorTypes } from \"@fluidframework/driver-definitions/internal\";\nimport {\n\tIPublicClientConfig,\n\tIOdspAuthRequestInfo,\n\tIOdspDriveItem,\n\tgetChildrenByDriveItem,\n\tgetDriveItemByServerRelativePath,\n\tgetDriveItemFromDriveAndItem,\n\tgetAadTenant,\n\tgetOdspScope,\n} from \"@fluidframework/odsp-doclib-utils/internal\";\n\nimport { loginHint } from \"./fluidFetchArgs.js\";\n\n// Note: the following page may be helpful for debugging auth issues:\n// https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/identity/identity/TROUBLESHOOTING.md\n// See e.g. the section on setting 'AZURE_LOG_LEVEL'.\nuseIdentityPlugin(cachePersistencePlugin);\n\nexport const fetchToolClientConfig: IPublicClientConfig = {\n\tget clientId(): string {\n\t\tconst clientId = process.env.fetch__tool__clientId;\n\t\tif (clientId === undefined) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Client ID environment variable not set: fetch__tool__clientId. Use the getkeys tool to populate it.\",\n\t\t\t);\n\t\t}\n\t\treturn clientId;\n\t},\n};\n\n// Local token cache for resolveWrapper.\n// @azure/identity-cache-persistence does not behave well in response to large numbers of parallel requests, which can happen for documents\n// with lots of blobs. We work around this for now by including a simple in-memory cache.\n// See more information here:\n// https://github.com/Azure/azure-sdk-for-js/issues/31307\nconst tokensByServer = new Map<string, string>();\n\n// If the persisted cache has multiple accounts, InteractiveBrowserCredential ignores it unless it is passed an explicit authentication record.\n// We keep the auth record around for a single run in memory, so that at worst we only have to authenticate once per server/user.\nconst authRecordPerServer = new Map<string, AuthenticationRecord | undefined>();\n\nexport async function resolveWrapper<T>(\n\tcallback: (authRequestInfo: IOdspAuthRequestInfo) => Promise<T>,\n\tserver: string,\n\tclientConfig: IPublicClientConfig,\n\tforceTokenReauth = false,\n): Promise<T> {\n\ttry {\n\t\tconst authenticationRecord = authRecordPerServer.get(server);\n\t\tconst credential = new InteractiveBrowserCredential({\n\t\t\tclientId: fetchToolClientConfig.clientId,\n\t\t\ttenantId: getAadTenant(server),\n\t\t\t// NOTE: fetch-tool flows using multiple sets of user credentials haven't been well-tested.\n\t\t\t// Some of the @azure/identity docs suggest we may need to manage authentication records and choose\n\t\t\t// which one to use explicitly here if we have such scenarios.\n\t\t\t// If we start doing this, it may be worth considering using disableAutomaticAuthentication here so we\n\t\t\t// have better control over when interactive auth may be triggered.\n\t\t\t// For now, fetch-tool doesn't work against personal accounts anyway so the only flow that might necessitate this\n\t\t\t// would be grabbing documents using several identities (e.g. test accounts we use for stress testing).\n\t\t\t// In that case, a simple workaround is to delete the cache that @azure/identity uses before running the tool.\n\t\t\t// See docs on `tokenCachePersistenceOptions.name` for information on where this cache is stored.\n\t\t\tloginHint,\n\t\t\tauthenticationRecord,\n\t\t\ttokenCachePersistenceOptions: {\n\t\t\t\tenabled: true,\n\t\t\t\tname: \"fetch-tool\",\n\t\t\t},\n\t\t});\n\n\t\tconst scope = getOdspScope(server);\n\t\tif (authenticationRecord === undefined) {\n\t\t\t// Cache this authentication record for subsequent token requests.\n\t\t\tauthRecordPerServer.set(server, await credential.authenticate(scope));\n\t\t}\n\t\tlet cachedToken = tokensByServer.get(server);\n\t\tif (cachedToken === undefined || forceTokenReauth) {\n\t\t\tconst result = await credential.getToken(scope);\n\t\t\tcachedToken = result.token;\n\t\t\ttokensByServer.set(server, cachedToken);\n\t\t}\n\n\t\treturn await callback({\n\t\t\taccessToken: cachedToken,\n\t\t\trefreshTokenFn: async () => {\n\t\t\t\tawait credential.authenticate(scope);\n\t\t\t\tconst { token } = await credential.getToken(scope);\n\t\t\t\ttokensByServer.set(server, token);\n\t\t\t\treturn token;\n\t\t\t},\n\t\t});\n\t} catch (e: any) {\n\t\tif (e.errorType === DriverErrorTypes.authorizationError && !forceTokenReauth) {\n\t\t\t// Re-auth\n\t\t\treturn resolveWrapper<T>(callback, server, clientConfig, true);\n\t\t}\n\t\tthrow e;\n\t}\n}\n\nasync function resolveDriveItemByServerRelativePath(\n\tserver: string,\n\tserverRelativePath: string,\n\tclientConfig: IPublicClientConfig,\n) {\n\treturn resolveWrapper<IOdspDriveItem>(\n\t\t// eslint-disable-next-line @typescript-eslint/promise-function-async\n\t\t(authRequestInfo) =>\n\t\t\tgetDriveItemByServerRelativePath(server, serverRelativePath, authRequestInfo, false),\n\t\tserver,\n\t\tclientConfig,\n\t);\n}\n\nasync function resolveChildrenByDriveItem(\n\tserver: string,\n\tfolderDriveItem: IOdspDriveItem,\n\tclientConfig: IPublicClientConfig,\n) {\n\treturn resolveWrapper<IOdspDriveItem[]>(\n\t\t// eslint-disable-next-line @typescript-eslint/promise-function-async\n\t\t(authRequestInfo) => getChildrenByDriveItem(folderDriveItem, server, authRequestInfo),\n\t\tserver,\n\t\tclientConfig,\n\t);\n}\n\nexport async function getSharepointFiles(\n\tserver: string,\n\tserverRelativePath: string,\n\trecurse: boolean,\n) {\n\tconst fileInfo = await resolveDriveItemByServerRelativePath(\n\t\tserver,\n\t\tserverRelativePath,\n\t\tfetchToolClientConfig,\n\t);\n\tconsole.log(fileInfo);\n\tconst pendingFolder: { path: string; folder: IOdspDriveItem }[] = [];\n\tconst files: IOdspDriveItem[] = [];\n\tif (fileInfo.isFolder) {\n\t\tpendingFolder.push({ path: serverRelativePath, folder: fileInfo });\n\t} else {\n\t\tfiles.push(fileInfo);\n\t}\n\n\t// eslint-disable-next-line no-constant-condition\n\twhile (true) {\n\t\tconst folderInfo = pendingFolder.shift();\n\t\tif (!folderInfo) {\n\t\t\tbreak;\n\t\t}\n\t\tconst { path, folder } = folderInfo;\n\t\tconst children = await resolveChildrenByDriveItem(server, folder, fetchToolClientConfig);\n\t\tfor (const child of children) {\n\t\t\tconst childPath = `${path}/${child.name}`;\n\t\t\tif (child.isFolder) {\n\t\t\t\tif (recurse) {\n\t\t\t\t\tpendingFolder.push({ path: childPath, folder: child });\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfiles.push(child);\n\t\t\t}\n\t\t}\n\t}\n\treturn files;\n}\n\nexport async function getSingleSharePointFile(server: string, drive: string, item: string) {\n\treturn resolveWrapper<IOdspDriveItem>(\n\t\t// eslint-disable-next-line @typescript-eslint/promise-function-async\n\t\t(authRequestInfo) => getDriveItemFromDriveAndItem(server, drive, item, authRequestInfo),\n\t\tserver,\n\t\tfetchToolClientConfig,\n\t);\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluid-tools/fetch-tool",
3
- "version": "2.4.0-297027",
3
+ "version": "2.4.0-299374",
4
4
  "description": "Console tool to fetch Fluid data from relay service",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -17,27 +17,27 @@
17
17
  "dependencies": {
18
18
  "@azure/identity": "^4.2.0",
19
19
  "@azure/identity-cache-persistence": "^1.1.0",
20
- "@fluid-internal/client-utils": "2.4.0-297027",
21
- "@fluidframework/container-runtime": "2.4.0-297027",
22
- "@fluidframework/core-interfaces": "2.4.0-297027",
23
- "@fluidframework/core-utils": "2.4.0-297027",
24
- "@fluidframework/datastore": "2.4.0-297027",
25
- "@fluidframework/driver-definitions": "2.4.0-297027",
26
- "@fluidframework/odsp-doclib-utils": "2.4.0-297027",
27
- "@fluidframework/odsp-driver": "2.4.0-297027",
28
- "@fluidframework/odsp-driver-definitions": "2.4.0-297027",
29
- "@fluidframework/odsp-urlresolver": "2.4.0-297027",
30
- "@fluidframework/routerlicious-driver": "2.4.0-297027",
31
- "@fluidframework/routerlicious-urlresolver": "2.4.0-297027",
32
- "@fluidframework/runtime-definitions": "2.4.0-297027",
33
- "@fluidframework/tool-utils": "2.4.0-297027"
20
+ "@fluid-internal/client-utils": "2.4.0-299374",
21
+ "@fluidframework/container-runtime": "2.4.0-299374",
22
+ "@fluidframework/core-interfaces": "2.4.0-299374",
23
+ "@fluidframework/core-utils": "2.4.0-299374",
24
+ "@fluidframework/datastore": "2.4.0-299374",
25
+ "@fluidframework/driver-definitions": "2.4.0-299374",
26
+ "@fluidframework/odsp-doclib-utils": "2.4.0-299374",
27
+ "@fluidframework/odsp-driver": "2.4.0-299374",
28
+ "@fluidframework/odsp-driver-definitions": "2.4.0-299374",
29
+ "@fluidframework/odsp-urlresolver": "2.4.0-299374",
30
+ "@fluidframework/routerlicious-driver": "2.4.0-299374",
31
+ "@fluidframework/routerlicious-urlresolver": "2.4.0-299374",
32
+ "@fluidframework/runtime-definitions": "2.4.0-299374",
33
+ "@fluidframework/tool-utils": "2.4.0-299374"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@biomejs/biome": "~1.8.3",
37
- "@fluid-tools/build-cli": "^0.46.0",
38
- "@fluid-tools/fetch-tool-previous": "npm:@fluid-tools/fetch-tool@2.3.0",
37
+ "@fluid-tools/build-cli": "^0.48.0",
38
+ "@fluid-tools/fetch-tool-previous": "npm:@fluid-tools/fetch-tool@~2.3.0",
39
39
  "@fluidframework/build-common": "^2.0.3",
40
- "@fluidframework/build-tools": "^0.46.0",
40
+ "@fluidframework/build-tools": "^0.48.0",
41
41
  "@fluidframework/eslint-config-fluid": "^5.4.0",
42
42
  "@types/node": "^18.19.0",
43
43
  "copyfiles": "^2.4.1",
@@ -3,7 +3,11 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { InteractiveBrowserCredential, useIdentityPlugin } from "@azure/identity";
6
+ import {
7
+ InteractiveBrowserCredential,
8
+ useIdentityPlugin,
9
+ type AuthenticationRecord,
10
+ } from "@azure/identity";
7
11
  import { cachePersistencePlugin } from "@azure/identity-cache-persistence";
8
12
  import { DriverErrorTypes } from "@fluidframework/driver-definitions/internal";
9
13
  import {
@@ -36,6 +40,17 @@ export const fetchToolClientConfig: IPublicClientConfig = {
36
40
  },
37
41
  };
38
42
 
43
+ // Local token cache for resolveWrapper.
44
+ // @azure/identity-cache-persistence does not behave well in response to large numbers of parallel requests, which can happen for documents
45
+ // with lots of blobs. We work around this for now by including a simple in-memory cache.
46
+ // See more information here:
47
+ // https://github.com/Azure/azure-sdk-for-js/issues/31307
48
+ const tokensByServer = new Map<string, string>();
49
+
50
+ // If the persisted cache has multiple accounts, InteractiveBrowserCredential ignores it unless it is passed an explicit authentication record.
51
+ // We keep the auth record around for a single run in memory, so that at worst we only have to authenticate once per server/user.
52
+ const authRecordPerServer = new Map<string, AuthenticationRecord | undefined>();
53
+
39
54
  export async function resolveWrapper<T>(
40
55
  callback: (authRequestInfo: IOdspAuthRequestInfo) => Promise<T>,
41
56
  server: string,
@@ -43,6 +58,7 @@ export async function resolveWrapper<T>(
43
58
  forceTokenReauth = false,
44
59
  ): Promise<T> {
45
60
  try {
61
+ const authenticationRecord = authRecordPerServer.get(server);
46
62
  const credential = new InteractiveBrowserCredential({
47
63
  clientId: fetchToolClientConfig.clientId,
48
64
  tenantId: getAadTenant(server),
@@ -56,6 +72,7 @@ export async function resolveWrapper<T>(
56
72
  // In that case, a simple workaround is to delete the cache that @azure/identity uses before running the tool.
57
73
  // See docs on `tokenCachePersistenceOptions.name` for information on where this cache is stored.
58
74
  loginHint,
75
+ authenticationRecord,
59
76
  tokenCachePersistenceOptions: {
60
77
  enabled: true,
61
78
  name: "fetch-tool",
@@ -63,15 +80,24 @@ export async function resolveWrapper<T>(
63
80
  });
64
81
 
65
82
  const scope = getOdspScope(server);
66
-
67
- const { token } = await credential.getToken(scope);
83
+ if (authenticationRecord === undefined) {
84
+ // Cache this authentication record for subsequent token requests.
85
+ authRecordPerServer.set(server, await credential.authenticate(scope));
86
+ }
87
+ let cachedToken = tokensByServer.get(server);
88
+ if (cachedToken === undefined || forceTokenReauth) {
89
+ const result = await credential.getToken(scope);
90
+ cachedToken = result.token;
91
+ tokensByServer.set(server, cachedToken);
92
+ }
68
93
 
69
94
  return await callback({
70
- accessToken: token,
95
+ accessToken: cachedToken,
71
96
  refreshTokenFn: async () => {
72
97
  await credential.authenticate(scope);
73
- const result = await credential.getToken(scope);
74
- return result.token;
98
+ const { token } = await credential.getToken(scope);
99
+ tokensByServer.set(server, token);
100
+ return token;
75
101
  },
76
102
  });
77
103
  } catch (e: any) {