diff --git a/clients/projectshare/src/ProjectShareClient.ts b/clients/projectshare/src/ProjectShareClient.ts index a97b41767923..bd317609d396 100644 --- a/clients/projectshare/src/ProjectShareClient.ts +++ b/clients/projectshare/src/ProjectShareClient.ts @@ -524,7 +524,7 @@ export class ProjectShareClient extends WsgClient { try { const accessurl = file.accessUrl !== undefined ? file.accessUrl : ""; await this.uploadBlob(requestContext, accessurl, data); - return this.updateFileExistsProperty(requestContext, contextId, file.wsgId); + return await this.updateFileExistsProperty(requestContext, contextId, file.wsgId); } catch (err) { throw err; } diff --git a/common/changes/@bentley/backend-itwin-client/transformer-await-returned-in-try_2021-07-06-11-52.json b/common/changes/@bentley/backend-itwin-client/transformer-await-returned-in-try_2021-07-06-11-52.json new file mode 100644 index 000000000000..42d2c4093a0d --- /dev/null +++ b/common/changes/@bentley/backend-itwin-client/transformer-await-returned-in-try_2021-07-06-11-52.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/backend-itwin-client", + "comment": "async order change in BlobDownloader", + "type": "none" + } + ], + "packageName": "@bentley/backend-itwin-client", + "email": "MichaelBelousov@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/bentleyjs-core/transformer-await-returned-in-try_2021-07-06-11-52.json b/common/changes/@bentley/bentleyjs-core/transformer-await-returned-in-try_2021-07-06-11-52.json new file mode 100644 index 000000000000..708693b197bd --- /dev/null +++ b/common/changes/@bentley/bentleyjs-core/transformer-await-returned-in-try_2021-07-06-11-52.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/bentleyjs-core", + "comment": "async return value rather than fulfilled promise", + "type": "none" + } + ], + "packageName": "@bentley/bentleyjs-core", + "email": "MichaelBelousov@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/eslint-plugin/transformer-await-returned-in-try_2021-07-06-11-52.json b/common/changes/@bentley/eslint-plugin/transformer-await-returned-in-try_2021-07-06-11-52.json new file mode 100644 index 000000000000..3743e046b796 --- /dev/null +++ b/common/changes/@bentley/eslint-plugin/transformer-await-returned-in-try_2021-07-06-11-52.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/eslint-plugin", + "comment": "enforce use of typed return-await alternative with try/catch handling", + "type": "none" + } + ], + "packageName": "@bentley/eslint-plugin", + "email": "MichaelBelousov@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-backend/transformer-await-returned-in-try_2021-07-06-11-52.json b/common/changes/@bentley/imodeljs-backend/transformer-await-returned-in-try_2021-07-06-11-52.json new file mode 100644 index 000000000000..ad347e1749d4 --- /dev/null +++ b/common/changes/@bentley/imodeljs-backend/transformer-await-returned-in-try_2021-07-06-11-52.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-backend", + "comment": "await returned promise to ensure finally block doesn't intercept it", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-backend", + "email": "MichaelBelousov@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-common/transformer-await-returned-in-try_2021-07-06-11-52.json b/common/changes/@bentley/imodeljs-common/transformer-await-returned-in-try_2021-07-06-11-52.json new file mode 100644 index 000000000000..ebaf3a5bcd8b --- /dev/null +++ b/common/changes/@bentley/imodeljs-common/transformer-await-returned-in-try_2021-07-06-11-52.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-common", + "comment": "", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-common", + "email": "MichaelBelousov@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/imodeljs-frontend/transformer-await-returned-in-try_2021-07-06-11-52.json b/common/changes/@bentley/imodeljs-frontend/transformer-await-returned-in-try_2021-07-06-11-52.json new file mode 100644 index 000000000000..77e7416bb687 --- /dev/null +++ b/common/changes/@bentley/imodeljs-frontend/transformer-await-returned-in-try_2021-07-06-11-52.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/imodeljs-frontend", + "comment": "internal async order change", + "type": "none" + } + ], + "packageName": "@bentley/imodeljs-frontend", + "email": "MichaelBelousov@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@bentley/projectshare-client/transformer-await-returned-in-try_2021-07-06-11-52.json b/common/changes/@bentley/projectshare-client/transformer-await-returned-in-try_2021-07-06-11-52.json new file mode 100644 index 000000000000..85d2864bfdf3 --- /dev/null +++ b/common/changes/@bentley/projectshare-client/transformer-await-returned-in-try_2021-07-06-11-52.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@bentley/projectshare-client", + "comment": "async order change in uploadContentInFile", + "type": "none" + } + ], + "packageName": "@bentley/projectshare-client", + "email": "MichaelBelousov@users.noreply.github.com" +} \ No newline at end of file diff --git a/core/backend-itwin-client/src/imodelhub/BlobDownloader.ts b/core/backend-itwin-client/src/imodelhub/BlobDownloader.ts index 407878df273a..84324dcdaa28 100644 --- a/core/backend-itwin-client/src/imodelhub/BlobDownloader.ts +++ b/core/backend-itwin-client/src/imodelhub/BlobDownloader.ts @@ -592,7 +592,7 @@ export class BlobDownloader { release(); } }); - return session.ready; + return await session.ready; } finally { unlock(); } diff --git a/core/backend/src/IModelTransformer.ts b/core/backend/src/IModelTransformer.ts index 800bd5c0fd12..d6de80843609 100644 --- a/core/backend/src/IModelTransformer.ts +++ b/core/backend/src/IModelTransformer.ts @@ -773,6 +773,11 @@ export class IModelTransformer extends IModelExportHandler { IModelJsFs.writeFileSync(schemaPath, await schema.toXmlString()); } + // pending PR https://github.com/typescript-eslint/typescript-eslint/pull/3601 fixes the rule @typescript-eslint/return-await + // to work in try/catch syntax in functions that contain a nested function + // until that merges and we upgrade our dependency, the callback cannot be defined inside the method it is used + private makeAbsolute = (s: string) => path.join(this._schemaExportDir, s); + /** Cause all schemas to be exported from the source iModel and imported into the target iModel. * @note For performance reasons, it is recommended that [IModelDb.saveChanges]($backend) be called after `processSchemas` is complete. * It is more efficient to process *data* changes after the schema changes have been saved. @@ -786,8 +791,8 @@ export class IModelTransformer extends IModelExportHandler { const exportedSchemaFiles = IModelJsFs.readdirSync(this._schemaExportDir); if (exportedSchemaFiles.length === 0) return; - const schemaFullPaths = exportedSchemaFiles.map((s) => path.join(this._schemaExportDir, s)); - return this.targetDb.importSchemas(requestContext, schemaFullPaths); + const schemaFullPaths = exportedSchemaFiles.map(this.makeAbsolute); + return await this.targetDb.importSchemas(requestContext, schemaFullPaths); } finally { requestContext.enter(); IModelJsFs.removeSync(this._schemaExportDir); diff --git a/core/backend/src/test/standalone/IModelTransformer.test.ts b/core/backend/src/test/standalone/IModelTransformer.test.ts index a3f97b436f74..6d6f65d62c7e 100644 --- a/core/backend/src/test/standalone/IModelTransformer.test.ts +++ b/core/backend/src/test/standalone/IModelTransformer.test.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { assert } from "chai"; import * as path from "path"; +import * as sinon from "sinon"; import { DbResult, Id64, Id64String, Logger, LogLevel } from "@bentley/bentleyjs-core"; import { Point3d, Range3d, StandardViewIndex, Transform, YawPitchRollAngles } from "@bentley/geometry-core"; import { @@ -958,4 +959,37 @@ describe("IModelTransformer", () => { targetDb.close(); }); + it("processSchemas should wait for the schema import to finish to delete the export directory", async () => { + const reqCtx = new BackendRequestContext(); + const cloneTestSchema100 = path.join(KnownTestLocations.assetsDir, "CloneTest.01.00.00.ecschema.xml"); + const sourceDbPath = IModelTestUtils.prepareOutputFile("IModelTransformer", "FinallyFirstTest.bim"); + const sourceDb = SnapshotDb.createEmpty(sourceDbPath, { rootSubject: { name: "FinallyFirstTest" } }); + await sourceDb.importSchemas(reqCtx, [cloneTestSchema100]); + sourceDb.saveChanges(); + + const targetDbPath = IModelTestUtils.prepareOutputFile("IModelTransformer", "FinallyFirstTestOut.bim"); + const targetDb = SnapshotDb.createEmpty(targetDbPath, { rootSubject: { name: "FinallyFirstTest" } }); + const transformer = new IModelTransformer(sourceDb, targetDb); + + const importSchemasResolved = sinon.spy(); + let importSchemasPromise: Promise; + + sinon.replace(targetDb, "importSchemas", sinon.fake(async () => { + importSchemasPromise = new Promise((resolve) => setImmediate(() => { + importSchemasResolved(); + resolve(undefined); + })); + return importSchemasPromise; + })); + + const removeSyncSpy = sinon.spy(IModelJsFs, "removeSync"); + + await transformer.processSchemas(reqCtx); + assert(removeSyncSpy.calledAfter(importSchemasResolved)); + + sinon.restore(); + sourceDb.close(); + targetDb.close(); + }); + }); diff --git a/core/bentley/src/OneAtATimeAction.ts b/core/bentley/src/OneAtATimeAction.ts index 60ac0ad2708f..a9eed9fbb504 100644 --- a/core/bentley/src/OneAtATimeAction.ts +++ b/core/bentley/src/OneAtATimeAction.ts @@ -83,8 +83,7 @@ export class OneAtATimeAction { } try { - await promise; - return promise; + return await promise; } finally { // do all of this whether promise was fulfilled or rejected this._active = this._pending; // see if there's a pending request waiting diff --git a/core/common/src/rpc/core/RpcInvocation.ts b/core/common/src/rpc/core/RpcInvocation.ts index 25b0e514e4dd..984940f57a42 100644 --- a/core/common/src/rpc/core/RpcInvocation.ts +++ b/core/common/src/rpc/core/RpcInvocation.ts @@ -130,6 +130,8 @@ export class RpcInvocation { (impl as any)[CURRENT_INVOCATION] = this; const op = this.lookupOperationFunction(impl); + // @typescript-eslint/return-await doesn't agree with awaiting values that *might* be a promise + // eslint-disable-next-line @typescript-eslint/return-await return await op.call(impl, ...parameters); } catch (error) { return this.reject(error); diff --git a/core/frontend/src/ContextRealityModelState.ts b/core/frontend/src/ContextRealityModelState.ts index 7358caef51e6..db9926b235d9 100644 --- a/core/frontend/src/ContextRealityModelState.ts +++ b/core/frontend/src/ContextRealityModelState.ts @@ -28,7 +28,7 @@ async function getAccessToken(): Promise { return undefined; // Not signed in try { - return IModelApp.authorizationClient.getAccessToken(); + return await IModelApp.authorizationClient.getAccessToken(); } catch (_) { return undefined; } diff --git a/core/frontend/src/test/render/webgl/FrameBuffer.test.ts b/core/frontend/src/test/render/webgl/FrameBuffer.test.ts index fbfbe8e6671a..ba5cec347259 100644 --- a/core/frontend/src/test/render/webgl/FrameBuffer.test.ts +++ b/core/frontend/src/test/render/webgl/FrameBuffer.test.ts @@ -14,9 +14,9 @@ import { TextureHandle } from "../../../render/webgl/Texture"; import { System } from "../../../render/webgl/System"; describe("FrameBuffer tests", () => { - // eslint-disable-next-line no-return-await + // eslint-disable-next-line @typescript-eslint/return-await before(async () => await IModelApp.startup()); - // eslint-disable-next-line no-return-await + // eslint-disable-next-line @typescript-eslint/return-await after(async () => await IModelApp.shutdown()); it("should produce and bind a valid framebuffer with single color attachment", () => { diff --git a/core/frontend/src/tile/map/ImageryTileTree.ts b/core/frontend/src/tile/map/ImageryTileTree.ts index 9dffa3e63f92..a818d448bea8 100644 --- a/core/frontend/src/tile/map/ImageryTileTree.ts +++ b/core/frontend/src/tile/map/ImageryTileTree.ts @@ -222,7 +222,7 @@ class ImageryTileLoader extends RealityTileLoader { try { const textureParams = new RenderTexture.Params(undefined, RenderTexture.Type.FilteredTileSection); - return imageElementFromImageSource(imageSource) + return await imageElementFromImageSource(imageSource) .then((image) => isCanceled() ? undefined : system.createTextureFromImage(image, ImageSourceFormat.Png === imageSource.format, iModel, textureParams)) .catch((_) => undefined); } catch (e) { diff --git a/test-apps/display-test-app/src/frontend/openStandaloneIModel.ts b/test-apps/display-test-app/src/frontend/openStandaloneIModel.ts index 67edcfb660cf..3be1719d2a29 100644 --- a/test-apps/display-test-app/src/frontend/openStandaloneIModel.ts +++ b/test-apps/display-test-app/src/frontend/openStandaloneIModel.ts @@ -8,7 +8,7 @@ import { BriefcaseConnection, IModelConnection, SnapshotConnection } from "@bent export async function openStandaloneIModel(filename: string, writable: boolean,): Promise { try { - return BriefcaseConnection.openStandalone(filename, writable ? OpenMode.ReadWrite : OpenMode.Readonly, { key: filename }); + return await BriefcaseConnection.openStandalone(filename, writable ? OpenMode.ReadWrite : OpenMode.Readonly, { key: filename }); } catch (err) { if (writable && err instanceof IModelError && err.errorNumber === IModelStatus.ReadOnly) return SnapshotConnection.openFile(filename); diff --git a/tools/eslint-plugin/dist/configs/imodeljs-recommended.js b/tools/eslint-plugin/dist/configs/imodeljs-recommended.js index 3757e32588ee..a6d527f9e391 100644 --- a/tools/eslint-plugin/dist/configs/imodeljs-recommended.js +++ b/tools/eslint-plugin/dist/configs/imodeljs-recommended.js @@ -161,6 +161,7 @@ module.exports = { "allow": ["T", "args"] } ], + "@typescript-eslint/return-await": "error", "@typescript-eslint/no-this-alias": "error", "@typescript-eslint/no-use-before-define": "off", "@typescript-eslint/no-unnecessary-type-assertion": "error", @@ -283,7 +284,7 @@ module.exports = { } ], "no-restricted-syntax": ["error", { selector: "TSEnumDeclaration[const=true]", message: "const enums are not allowed" }], - "no-return-await": "error", + "no-return-await": "off", // using @typescript-eslint/return-await instead "no-shadow": "off", // using @typescript-eslint/no-shadow instead "no-sparse-arrays": "error", "no-template-curly-in-string": "error",