@itwin/core-mobile 3.0.0-dev.100
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +263 -0
- package/LICENSE.md +9 -0
- package/README.md +11 -0
- package/lib/cjs/MobileBackend.d.ts +7 -0
- package/lib/cjs/MobileBackend.d.ts.map +1 -0
- package/lib/cjs/MobileBackend.js +24 -0
- package/lib/cjs/MobileBackend.js.map +1 -0
- package/lib/cjs/MobileFrontend.d.ts +5 -0
- package/lib/cjs/MobileFrontend.d.ts.map +1 -0
- package/lib/cjs/MobileFrontend.js +21 -0
- package/lib/cjs/MobileFrontend.js.map +1 -0
- package/lib/cjs/__DOC_ONLY__.d.ts +9 -0
- package/lib/cjs/__DOC_ONLY__.d.ts.map +1 -0
- package/lib/cjs/__DOC_ONLY__.js +28 -0
- package/lib/cjs/__DOC_ONLY__.js.map +1 -0
- package/lib/cjs/backend/AndroidHost.d.ts +11 -0
- package/lib/cjs/backend/AndroidHost.d.ts.map +1 -0
- package/lib/cjs/backend/AndroidHost.js +23 -0
- package/lib/cjs/backend/AndroidHost.js.map +1 -0
- package/lib/cjs/backend/MobileAuthorizationBackend.d.ts +23 -0
- package/lib/cjs/backend/MobileAuthorizationBackend.d.ts.map +1 -0
- package/lib/cjs/backend/MobileAuthorizationBackend.js +78 -0
- package/lib/cjs/backend/MobileAuthorizationBackend.js.map +1 -0
- package/lib/cjs/backend/MobileFileHandler.d.ts +88 -0
- package/lib/cjs/backend/MobileFileHandler.d.ts.map +1 -0
- package/lib/cjs/backend/MobileFileHandler.js +233 -0
- package/lib/cjs/backend/MobileFileHandler.js.map +1 -0
- package/lib/cjs/backend/MobileHost.d.ts +78 -0
- package/lib/cjs/backend/MobileHost.d.ts.map +1 -0
- package/lib/cjs/backend/MobileHost.js +126 -0
- package/lib/cjs/backend/MobileHost.js.map +1 -0
- package/lib/cjs/backend/MobileRpcServer.d.ts +19 -0
- package/lib/cjs/backend/MobileRpcServer.d.ts.map +1 -0
- package/lib/cjs/backend/MobileRpcServer.js +136 -0
- package/lib/cjs/backend/MobileRpcServer.js.map +1 -0
- package/lib/cjs/backend/iOSHost.d.ts +11 -0
- package/lib/cjs/backend/iOSHost.d.ts.map +1 -0
- package/lib/cjs/backend/iOSHost.js +23 -0
- package/lib/cjs/backend/iOSHost.js.map +1 -0
- package/lib/cjs/common/MobileAppProps.d.ts +35 -0
- package/lib/cjs/common/MobileAppProps.d.ts.map +1 -0
- package/lib/cjs/common/MobileAppProps.js +29 -0
- package/lib/cjs/common/MobileAppProps.js.map +1 -0
- package/lib/cjs/common/MobileEventLoop.d.ts +12 -0
- package/lib/cjs/common/MobileEventLoop.d.ts.map +1 -0
- package/lib/cjs/common/MobileEventLoop.js +31 -0
- package/lib/cjs/common/MobileEventLoop.js.map +1 -0
- package/lib/cjs/common/MobileIpc.d.ts +18 -0
- package/lib/cjs/common/MobileIpc.d.ts.map +1 -0
- package/lib/cjs/common/MobileIpc.js +71 -0
- package/lib/cjs/common/MobileIpc.js.map +1 -0
- package/lib/cjs/common/MobilePush.d.ts +21 -0
- package/lib/cjs/common/MobilePush.d.ts.map +1 -0
- package/lib/cjs/common/MobilePush.js +53 -0
- package/lib/cjs/common/MobilePush.js.map +1 -0
- package/lib/cjs/common/MobileRpcManager.d.ts +41 -0
- package/lib/cjs/common/MobileRpcManager.d.ts.map +1 -0
- package/lib/cjs/common/MobileRpcManager.js +110 -0
- package/lib/cjs/common/MobileRpcManager.js.map +1 -0
- package/lib/cjs/common/MobileRpcProtocol.d.ts +59 -0
- package/lib/cjs/common/MobileRpcProtocol.d.ts.map +1 -0
- package/lib/cjs/common/MobileRpcProtocol.js +279 -0
- package/lib/cjs/common/MobileRpcProtocol.js.map +1 -0
- package/lib/cjs/common/MobileRpcRequest.d.ts +21 -0
- package/lib/cjs/common/MobileRpcRequest.d.ts.map +1 -0
- package/lib/cjs/common/MobileRpcRequest.js +48 -0
- package/lib/cjs/common/MobileRpcRequest.js.map +1 -0
- package/lib/cjs/frontend/AndroidApp.d.ts +14 -0
- package/lib/cjs/frontend/AndroidApp.d.ts.map +1 -0
- package/lib/cjs/frontend/AndroidApp.js +17 -0
- package/lib/cjs/frontend/AndroidApp.js.map +1 -0
- package/lib/cjs/frontend/IOSApp.d.ts +10 -0
- package/lib/cjs/frontend/IOSApp.d.ts.map +1 -0
- package/lib/cjs/frontend/IOSApp.js +21 -0
- package/lib/cjs/frontend/IOSApp.js.map +1 -0
- package/lib/cjs/frontend/MobileApp.d.ts +20 -0
- package/lib/cjs/frontend/MobileApp.d.ts.map +1 -0
- package/lib/cjs/frontend/MobileApp.js +57 -0
- package/lib/cjs/frontend/MobileApp.js.map +1 -0
- package/lib/cjs/test/ios/MobilePlatform.test.d.ts +2 -0
- package/lib/cjs/test/ios/MobilePlatform.test.d.ts.map +1 -0
- package/lib/cjs/test/ios/MobilePlatform.test.js +85 -0
- package/lib/cjs/test/ios/MobilePlatform.test.js.map +1 -0
- package/package.json +76 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/** @packageDocumentation
|
|
2
|
+
* @module iModelHub
|
|
3
|
+
*/
|
|
4
|
+
/// <reference types="node" />
|
|
5
|
+
import * as https from "https";
|
|
6
|
+
import { AccessToken } from "@itwin/core-bentley";
|
|
7
|
+
import { CancelRequest, FileHandler, ProgressCallback } from "@bentley/itwin-client";
|
|
8
|
+
/**
|
|
9
|
+
* Provides methods to work with the file system and azure storage. An instance of this class has to be provided to [[IModelClient]] for file upload/download methods to work.
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
export declare class MobileFileHandler implements FileHandler {
|
|
13
|
+
/** @internal */
|
|
14
|
+
agent?: https.Agent;
|
|
15
|
+
/**
|
|
16
|
+
* Constructor for MobileFileHandler.
|
|
17
|
+
*/
|
|
18
|
+
constructor();
|
|
19
|
+
/** Create a directory, recursively setting up the path as necessary. */
|
|
20
|
+
private static makeDirectoryRecursive;
|
|
21
|
+
/**
|
|
22
|
+
* Make url safe for logging by removing sensitive information
|
|
23
|
+
* @param url input url that will be strip of search and query parameters and replace them by ... for security reason
|
|
24
|
+
*/
|
|
25
|
+
private static getSafeUrlForLogging;
|
|
26
|
+
/**
|
|
27
|
+
* Check if sas url has expired
|
|
28
|
+
* @param download sas url for download
|
|
29
|
+
* @param futureSeconds should be valid in future for given seconds.
|
|
30
|
+
*/
|
|
31
|
+
static isUrlExpired(downloadUrl: string, futureSeconds?: number): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Download a file from AzureBlobStorage for iModelHub. Creates the directory containing the file if necessary. If there is an error in the operation, incomplete file is deleted from disk.
|
|
34
|
+
* @param downloadUrl URL to download file from.
|
|
35
|
+
* @param downloadToPathname Pathname to download the file to.
|
|
36
|
+
* @param fileSize Size of the file that's being downloaded.
|
|
37
|
+
* @param progressCallback Callback for tracking progress.
|
|
38
|
+
* @throws [[IModelHubClientError]] with [IModelHubStatus.UndefinedArgumentError]($bentley) if one of the arguments is undefined or empty.
|
|
39
|
+
*/
|
|
40
|
+
downloadFile(_accessToken: AccessToken, downloadUrl: string, downloadToPathname: string, fileSize?: number, progressCallback?: ProgressCallback, cancelRequest?: CancelRequest): Promise<void>;
|
|
41
|
+
/** Get encoded block id from its number. */
|
|
42
|
+
private getBlockId;
|
|
43
|
+
private uploadChunk;
|
|
44
|
+
/**
|
|
45
|
+
* Upload a file to AzureBlobStorage for iModelHub.
|
|
46
|
+
* @param uploadUrl URL to upload the file to.
|
|
47
|
+
* @param uploadFromPathname Pathname to upload the file from.
|
|
48
|
+
* @param progressCallback Callback for tracking progress.
|
|
49
|
+
* @throws [[IModelHubClientError]] with [IModelHubStatus.UndefinedArgumentError]($bentley) if one of the arguments is undefined or empty.
|
|
50
|
+
*/
|
|
51
|
+
uploadFile(accessToken: AccessToken, uploadUrlString: string, uploadFromPathname: string, progressCallback?: ProgressCallback): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Get size of a file.
|
|
54
|
+
* @param filePath Path of the file.
|
|
55
|
+
* @returns Size of the file.
|
|
56
|
+
*/
|
|
57
|
+
getFileSize(filePath: string): number;
|
|
58
|
+
/**
|
|
59
|
+
* Check if path is a directory.
|
|
60
|
+
* @param filePath Path of the file.
|
|
61
|
+
* @returns True if path is directory.
|
|
62
|
+
*/
|
|
63
|
+
isDirectory(filePath: string): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Check if path exists.
|
|
66
|
+
* @param filePath Path of the file.
|
|
67
|
+
* @returns True if path exists.
|
|
68
|
+
*/
|
|
69
|
+
exists(filePath: string): boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Deletes file.
|
|
72
|
+
* @param filePath Path of the file.
|
|
73
|
+
*/
|
|
74
|
+
unlink(filePath: string): void;
|
|
75
|
+
/**
|
|
76
|
+
* Get file name from the path.
|
|
77
|
+
* @param filePath Path of the file.
|
|
78
|
+
* @returns File name.
|
|
79
|
+
*/
|
|
80
|
+
basename(filePath: string): string;
|
|
81
|
+
/**
|
|
82
|
+
* Join multiple strings into a single path.
|
|
83
|
+
* @param paths Strings to join.
|
|
84
|
+
* @returns Joined path.
|
|
85
|
+
*/
|
|
86
|
+
join(...paths: string[]): string;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=MobileFileHandler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MobileFileHandler.d.ts","sourceRoot":"","sources":["../../../src/backend/MobileFileHandler.ts"],"names":[],"mappings":"AAIA;;GAEG;;AAGH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAE,WAAW,EAAU,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EACL,aAAa,EAAkB,WAAW,EAAE,gBAAgB,EAE7D,MAAM,uBAAuB,CAAC;AAW/B;;;GAGG;AACH,qBAAa,iBAAkB,YAAW,WAAW;IACnD,gBAAgB;IACT,KAAK,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC;IAE3B;;OAEG;;IAIH,wEAAwE;IACxE,OAAO,CAAC,MAAM,CAAC,sBAAsB;IAQrC;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,oBAAoB;IASnC;;;;OAIG;WACW,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO;IAehF;;;;;;;OAOG;IACU,YAAY,CAAC,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,gBAAgB,EAAE,aAAa,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAkC3M,4CAA4C;IAC5C,OAAO,CAAC,UAAU;YAIJ,WAAW;IA0BzB;;;;;;OAMG;IACU,UAAU,CAAC,WAAW,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IA4C1J;;;;OAIG;IACI,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAI5C;;;;OAIG;IACI,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAI7C;;;;OAIG;IACI,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAIxC;;;OAGG;IACI,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAIrC;;;;OAIG;IACI,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAIzC;;;;OAIG;IACI,IAAI,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM;CAGxC"}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*---------------------------------------------------------------------------------------------
|
|
3
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
4
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
5
|
+
*--------------------------------------------------------------------------------------------*/
|
|
6
|
+
/** @packageDocumentation
|
|
7
|
+
* @module iModelHub
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.MobileFileHandler = void 0;
|
|
11
|
+
const fs = require("fs");
|
|
12
|
+
const path = require("path");
|
|
13
|
+
const core_bentley_1 = require("@itwin/core-bentley");
|
|
14
|
+
const itwin_client_1 = require("@bentley/itwin-client");
|
|
15
|
+
const MobileHost_1 = require("./MobileHost");
|
|
16
|
+
const js_base64_1 = require("js-base64");
|
|
17
|
+
const loggerCategory = "mobile.filehandler";
|
|
18
|
+
const defined = (argumentName, argument, allowEmpty = false) => {
|
|
19
|
+
if (argument === undefined || argument === null || (argument === "" && !allowEmpty))
|
|
20
|
+
throw Error(`Argument ${argumentName} is null or undefined`);
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Provides methods to work with the file system and azure storage. An instance of this class has to be provided to [[IModelClient]] for file upload/download methods to work.
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
class MobileFileHandler {
|
|
27
|
+
/**
|
|
28
|
+
* Constructor for MobileFileHandler.
|
|
29
|
+
*/
|
|
30
|
+
constructor() {
|
|
31
|
+
}
|
|
32
|
+
/** Create a directory, recursively setting up the path as necessary. */
|
|
33
|
+
static makeDirectoryRecursive(dirPath) {
|
|
34
|
+
if (fs.existsSync(dirPath))
|
|
35
|
+
return;
|
|
36
|
+
MobileFileHandler.makeDirectoryRecursive(path.dirname(dirPath));
|
|
37
|
+
fs.mkdirSync(dirPath);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Make url safe for logging by removing sensitive information
|
|
41
|
+
* @param url input url that will be strip of search and query parameters and replace them by ... for security reason
|
|
42
|
+
*/
|
|
43
|
+
static getSafeUrlForLogging(url) {
|
|
44
|
+
const safeToLogDownloadUrl = new URL(url);
|
|
45
|
+
if (safeToLogDownloadUrl.search && safeToLogDownloadUrl.search.length > 0)
|
|
46
|
+
safeToLogDownloadUrl.search = "...";
|
|
47
|
+
if (safeToLogDownloadUrl.hash && safeToLogDownloadUrl.hash.length > 0)
|
|
48
|
+
safeToLogDownloadUrl.hash = "...";
|
|
49
|
+
return safeToLogDownloadUrl.toString();
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Check if sas url has expired
|
|
53
|
+
* @param download sas url for download
|
|
54
|
+
* @param futureSeconds should be valid in future for given seconds.
|
|
55
|
+
*/
|
|
56
|
+
static isUrlExpired(downloadUrl, futureSeconds) {
|
|
57
|
+
const sasUrl = new URL(downloadUrl);
|
|
58
|
+
const se = sasUrl.searchParams.get("se");
|
|
59
|
+
if (se) {
|
|
60
|
+
const expiryUTC = new Date(se);
|
|
61
|
+
const now = new Date();
|
|
62
|
+
const currentUTC = new Date(now.toUTCString());
|
|
63
|
+
if (futureSeconds) {
|
|
64
|
+
currentUTC.setSeconds(futureSeconds + currentUTC.getSeconds());
|
|
65
|
+
}
|
|
66
|
+
return expiryUTC <= currentUTC;
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Download a file from AzureBlobStorage for iModelHub. Creates the directory containing the file if necessary. If there is an error in the operation, incomplete file is deleted from disk.
|
|
72
|
+
* @param downloadUrl URL to download file from.
|
|
73
|
+
* @param downloadToPathname Pathname to download the file to.
|
|
74
|
+
* @param fileSize Size of the file that's being downloaded.
|
|
75
|
+
* @param progressCallback Callback for tracking progress.
|
|
76
|
+
* @throws [[IModelHubClientError]] with [IModelHubStatus.UndefinedArgumentError]($bentley) if one of the arguments is undefined or empty.
|
|
77
|
+
*/
|
|
78
|
+
async downloadFile(_accessToken, downloadUrl, downloadToPathname, fileSize, progressCallback, cancelRequest) {
|
|
79
|
+
// strip search and hash parameters from download Url for logging purpose
|
|
80
|
+
const safeToLogUrl = MobileFileHandler.getSafeUrlForLogging(downloadUrl);
|
|
81
|
+
core_bentley_1.Logger.logInfo(loggerCategory, `Downloading file from ${safeToLogUrl}`);
|
|
82
|
+
defined("downloadUrl", downloadUrl);
|
|
83
|
+
defined("downloadToPathname", downloadToPathname);
|
|
84
|
+
if (MobileFileHandler.isUrlExpired(downloadUrl)) {
|
|
85
|
+
core_bentley_1.Logger.logError(loggerCategory, `Sas url has expired ${safeToLogUrl}`);
|
|
86
|
+
throw new itwin_client_1.SasUrlExpired(403, "Download URL has expired");
|
|
87
|
+
}
|
|
88
|
+
if (fs.existsSync(downloadToPathname))
|
|
89
|
+
fs.unlinkSync(downloadToPathname);
|
|
90
|
+
MobileFileHandler.makeDirectoryRecursive(path.dirname(downloadToPathname));
|
|
91
|
+
try {
|
|
92
|
+
await MobileHost_1.MobileHost.downloadFile(downloadUrl, downloadToPathname, progressCallback, cancelRequest);
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
if (fs.existsSync(downloadToPathname))
|
|
96
|
+
fs.unlinkSync(downloadToPathname); // Just in case there was a partial download, delete the file
|
|
97
|
+
if (!(err instanceof itwin_client_1.UserCancelledError))
|
|
98
|
+
core_bentley_1.Logger.logError(loggerCategory, `Error downloading file`);
|
|
99
|
+
throw err;
|
|
100
|
+
}
|
|
101
|
+
if (fileSize && fs.existsSync(downloadToPathname)) {
|
|
102
|
+
if (fs.lstatSync(downloadToPathname).size !== fileSize) {
|
|
103
|
+
fs.unlinkSync(downloadToPathname);
|
|
104
|
+
core_bentley_1.Logger.logError(loggerCategory, `Downloaded file is of incorrect size ${safeToLogUrl}`);
|
|
105
|
+
throw new itwin_client_1.DownloadFailed(403, "Download failed. Expected filesize does not match");
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
core_bentley_1.Logger.logTrace(loggerCategory, `Downloaded file from ${safeToLogUrl}`);
|
|
109
|
+
}
|
|
110
|
+
/** Get encoded block id from its number. */
|
|
111
|
+
getBlockId(blockId) {
|
|
112
|
+
return js_base64_1.Base64.encode(blockId.toString(16).padStart(5, "0"));
|
|
113
|
+
}
|
|
114
|
+
async uploadChunk(_accessToken, uploadUrlString, fileDescriptor, blockId, callback) {
|
|
115
|
+
const chunkSize = 4 * 1024 * 1024;
|
|
116
|
+
let buffer = Buffer.alloc(chunkSize);
|
|
117
|
+
const bytesRead = fs.readSync(fileDescriptor, buffer, 0, chunkSize, chunkSize * blockId);
|
|
118
|
+
buffer = buffer.slice(0, bytesRead);
|
|
119
|
+
const options = {
|
|
120
|
+
method: "PUT",
|
|
121
|
+
headers: {
|
|
122
|
+
"x-ms-blob-type": "BlockBlob",
|
|
123
|
+
"Content-Type": "application/octet-stream",
|
|
124
|
+
"Content-Length": buffer.length, // eslint-disable-line @typescript-eslint/naming-convention
|
|
125
|
+
},
|
|
126
|
+
body: buffer,
|
|
127
|
+
progressCallback: callback,
|
|
128
|
+
agent: this.agent,
|
|
129
|
+
timeout: {
|
|
130
|
+
deadline: 60000,
|
|
131
|
+
response: 60000,
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
const uploadUrl = `${uploadUrlString}&comp=block&blockid=${this.getBlockId(blockId)}`;
|
|
135
|
+
await (0, itwin_client_1.request)(uploadUrl, options);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Upload a file to AzureBlobStorage for iModelHub.
|
|
139
|
+
* @param uploadUrl URL to upload the file to.
|
|
140
|
+
* @param uploadFromPathname Pathname to upload the file from.
|
|
141
|
+
* @param progressCallback Callback for tracking progress.
|
|
142
|
+
* @throws [[IModelHubClientError]] with [IModelHubStatus.UndefinedArgumentError]($bentley) if one of the arguments is undefined or empty.
|
|
143
|
+
*/
|
|
144
|
+
async uploadFile(accessToken, uploadUrlString, uploadFromPathname, progressCallback) {
|
|
145
|
+
const safeToLogUrl = MobileFileHandler.getSafeUrlForLogging(uploadUrlString);
|
|
146
|
+
core_bentley_1.Logger.logTrace(loggerCategory, `Uploading file to ${safeToLogUrl}`);
|
|
147
|
+
defined("uploadUrlString", uploadUrlString);
|
|
148
|
+
defined("uploadFromPathname", uploadFromPathname);
|
|
149
|
+
const fileSize = this.getFileSize(uploadFromPathname);
|
|
150
|
+
const file = fs.openSync(uploadFromPathname, "r");
|
|
151
|
+
const chunkSize = 4 * 1024 * 1024;
|
|
152
|
+
try {
|
|
153
|
+
let blockList = '<?xml version=\"1.0\" encoding=\"utf-8\"?><BlockList>';
|
|
154
|
+
let i = 0;
|
|
155
|
+
const callback = (progress) => {
|
|
156
|
+
const uploaded = i * chunkSize + progress.loaded;
|
|
157
|
+
progressCallback({ loaded: uploaded, percent: uploaded / fileSize, total: fileSize });
|
|
158
|
+
};
|
|
159
|
+
for (; i * chunkSize < fileSize; ++i) {
|
|
160
|
+
await this.uploadChunk(accessToken, uploadUrlString, file, i, progressCallback ? callback : undefined);
|
|
161
|
+
blockList += `<Latest>${this.getBlockId(i)}</Latest>`;
|
|
162
|
+
}
|
|
163
|
+
blockList += "</BlockList>";
|
|
164
|
+
const options = {
|
|
165
|
+
method: "PUT",
|
|
166
|
+
headers: {
|
|
167
|
+
"Content-Type": "application/xml",
|
|
168
|
+
"Content-Length": blockList.length, // eslint-disable-line @typescript-eslint/naming-convention
|
|
169
|
+
},
|
|
170
|
+
body: blockList,
|
|
171
|
+
agent: this.agent,
|
|
172
|
+
timeout: {
|
|
173
|
+
response: 5000,
|
|
174
|
+
deadline: 60000,
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
const uploadUrl = `${uploadUrlString}&comp=blocklist`;
|
|
178
|
+
await (0, itwin_client_1.request)(uploadUrl, options);
|
|
179
|
+
}
|
|
180
|
+
finally {
|
|
181
|
+
fs.closeSync(file);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Get size of a file.
|
|
186
|
+
* @param filePath Path of the file.
|
|
187
|
+
* @returns Size of the file.
|
|
188
|
+
*/
|
|
189
|
+
getFileSize(filePath) {
|
|
190
|
+
return fs.statSync(filePath).size;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Check if path is a directory.
|
|
194
|
+
* @param filePath Path of the file.
|
|
195
|
+
* @returns True if path is directory.
|
|
196
|
+
*/
|
|
197
|
+
isDirectory(filePath) {
|
|
198
|
+
return fs.statSync(filePath).isDirectory();
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Check if path exists.
|
|
202
|
+
* @param filePath Path of the file.
|
|
203
|
+
* @returns True if path exists.
|
|
204
|
+
*/
|
|
205
|
+
exists(filePath) {
|
|
206
|
+
return fs.existsSync(filePath);
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Deletes file.
|
|
210
|
+
* @param filePath Path of the file.
|
|
211
|
+
*/
|
|
212
|
+
unlink(filePath) {
|
|
213
|
+
fs.unlinkSync(filePath);
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Get file name from the path.
|
|
217
|
+
* @param filePath Path of the file.
|
|
218
|
+
* @returns File name.
|
|
219
|
+
*/
|
|
220
|
+
basename(filePath) {
|
|
221
|
+
return path.basename(filePath);
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Join multiple strings into a single path.
|
|
225
|
+
* @param paths Strings to join.
|
|
226
|
+
* @returns Joined path.
|
|
227
|
+
*/
|
|
228
|
+
join(...paths) {
|
|
229
|
+
return path.join(...paths);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
exports.MobileFileHandler = MobileFileHandler;
|
|
233
|
+
//# sourceMappingURL=MobileFileHandler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MobileFileHandler.js","sourceRoot":"","sources":["../../../src/backend/MobileFileHandler.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n/** @packageDocumentation\r\n * @module iModelHub\r\n */\r\n\r\nimport * as fs from \"fs\";\r\nimport * as https from \"https\";\r\nimport * as path from \"path\";\r\nimport { AccessToken, Logger } from \"@itwin/core-bentley\";\r\nimport {\r\n CancelRequest, DownloadFailed, FileHandler, ProgressCallback, ProgressInfo, request, RequestOptions, SasUrlExpired,\r\n UserCancelledError,\r\n} from \"@bentley/itwin-client\";\r\nimport { MobileHost } from \"./MobileHost\";\r\nimport { Base64 } from \"js-base64\";\r\n\r\nconst loggerCategory: string = \"mobile.filehandler\";\r\n\r\nconst defined = (argumentName: string, argument?: any, allowEmpty: boolean = false) => {\r\n if (argument === undefined || argument === null || (argument === \"\" && !allowEmpty))\r\n throw Error(`Argument ${argumentName} is null or undefined`);\r\n};\r\n\r\n/**\r\n * Provides methods to work with the file system and azure storage. An instance of this class has to be provided to [[IModelClient]] for file upload/download methods to work.\r\n * @internal\r\n */\r\nexport class MobileFileHandler implements FileHandler {\r\n /** @internal */\r\n public agent?: https.Agent;\r\n\r\n /**\r\n * Constructor for MobileFileHandler.\r\n */\r\n constructor() {\r\n }\r\n\r\n /** Create a directory, recursively setting up the path as necessary. */\r\n private static makeDirectoryRecursive(dirPath: string) {\r\n if (fs.existsSync(dirPath))\r\n return;\r\n\r\n MobileFileHandler.makeDirectoryRecursive(path.dirname(dirPath));\r\n fs.mkdirSync(dirPath);\r\n }\r\n\r\n /**\r\n * Make url safe for logging by removing sensitive information\r\n * @param url input url that will be strip of search and query parameters and replace them by ... for security reason\r\n */\r\n private static getSafeUrlForLogging(url: string): string {\r\n const safeToLogDownloadUrl = new URL(url);\r\n if (safeToLogDownloadUrl.search && safeToLogDownloadUrl.search.length > 0)\r\n safeToLogDownloadUrl.search = \"...\";\r\n if (safeToLogDownloadUrl.hash && safeToLogDownloadUrl.hash.length > 0)\r\n safeToLogDownloadUrl.hash = \"...\";\r\n return safeToLogDownloadUrl.toString();\r\n }\r\n\r\n /**\r\n * Check if sas url has expired\r\n * @param download sas url for download\r\n * @param futureSeconds should be valid in future for given seconds.\r\n */\r\n public static isUrlExpired(downloadUrl: string, futureSeconds?: number): boolean {\r\n const sasUrl = new URL(downloadUrl);\r\n const se = sasUrl.searchParams.get(\"se\");\r\n if (se) {\r\n const expiryUTC = new Date(se);\r\n const now = new Date();\r\n const currentUTC = new Date(now.toUTCString());\r\n if (futureSeconds) {\r\n currentUTC.setSeconds(futureSeconds + currentUTC.getSeconds());\r\n }\r\n return expiryUTC <= currentUTC;\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * Download a file from AzureBlobStorage for iModelHub. Creates the directory containing the file if necessary. If there is an error in the operation, incomplete file is deleted from disk.\r\n * @param downloadUrl URL to download file from.\r\n * @param downloadToPathname Pathname to download the file to.\r\n * @param fileSize Size of the file that's being downloaded.\r\n * @param progressCallback Callback for tracking progress.\r\n * @throws [[IModelHubClientError]] with [IModelHubStatus.UndefinedArgumentError]($bentley) if one of the arguments is undefined or empty.\r\n */\r\n public async downloadFile(_accessToken: AccessToken, downloadUrl: string, downloadToPathname: string, fileSize?: number, progressCallback?: ProgressCallback, cancelRequest?: CancelRequest): Promise<void> {\r\n // strip search and hash parameters from download Url for logging purpose\r\n const safeToLogUrl = MobileFileHandler.getSafeUrlForLogging(downloadUrl);\r\n Logger.logInfo(loggerCategory, `Downloading file from ${safeToLogUrl}`);\r\n\r\n defined(\"downloadUrl\", downloadUrl);\r\n defined(\"downloadToPathname\", downloadToPathname);\r\n if (MobileFileHandler.isUrlExpired(downloadUrl)) {\r\n Logger.logError(loggerCategory, `Sas url has expired ${safeToLogUrl}`);\r\n throw new SasUrlExpired(403, \"Download URL has expired\");\r\n }\r\n if (fs.existsSync(downloadToPathname))\r\n fs.unlinkSync(downloadToPathname);\r\n\r\n MobileFileHandler.makeDirectoryRecursive(path.dirname(downloadToPathname));\r\n try {\r\n await MobileHost.downloadFile(downloadUrl, downloadToPathname, progressCallback, cancelRequest);\r\n } catch (err) {\r\n if (fs.existsSync(downloadToPathname))\r\n fs.unlinkSync(downloadToPathname); // Just in case there was a partial download, delete the file\r\n\r\n if (!(err instanceof UserCancelledError))\r\n Logger.logError(loggerCategory, `Error downloading file`);\r\n throw err;\r\n }\r\n if (fileSize && fs.existsSync(downloadToPathname)) {\r\n if (fs.lstatSync(downloadToPathname).size !== fileSize) {\r\n fs.unlinkSync(downloadToPathname);\r\n Logger.logError(loggerCategory, `Downloaded file is of incorrect size ${safeToLogUrl}`);\r\n throw new DownloadFailed(403, \"Download failed. Expected filesize does not match\");\r\n }\r\n }\r\n Logger.logTrace(loggerCategory, `Downloaded file from ${safeToLogUrl}`);\r\n }\r\n /** Get encoded block id from its number. */\r\n private getBlockId(blockId: number) {\r\n return Base64.encode(blockId.toString(16).padStart(5, \"0\"));\r\n }\r\n\r\n private async uploadChunk(_accessToken: AccessToken, uploadUrlString: string, fileDescriptor: number, blockId: number, callback?: ProgressCallback) {\r\n const chunkSize = 4 * 1024 * 1024;\r\n let buffer = Buffer.alloc(chunkSize);\r\n const bytesRead = fs.readSync(fileDescriptor, buffer, 0, chunkSize, chunkSize * blockId);\r\n buffer = buffer.slice(0, bytesRead);\r\n\r\n const options: RequestOptions = {\r\n method: \"PUT\",\r\n headers: {\r\n \"x-ms-blob-type\": \"BlockBlob\",\r\n \"Content-Type\": \"application/octet-stream\", // eslint-disable-line @typescript-eslint/naming-convention\r\n \"Content-Length\": buffer.length, // eslint-disable-line @typescript-eslint/naming-convention\r\n },\r\n body: buffer,\r\n progressCallback: callback,\r\n agent: this.agent,\r\n timeout: {\r\n deadline: 60000,\r\n response: 60000,\r\n },\r\n };\r\n\r\n const uploadUrl = `${uploadUrlString}&comp=block&blockid=${this.getBlockId(blockId)}`;\r\n await request(uploadUrl, options);\r\n }\r\n\r\n /**\r\n * Upload a file to AzureBlobStorage for iModelHub.\r\n * @param uploadUrl URL to upload the file to.\r\n * @param uploadFromPathname Pathname to upload the file from.\r\n * @param progressCallback Callback for tracking progress.\r\n * @throws [[IModelHubClientError]] with [IModelHubStatus.UndefinedArgumentError]($bentley) if one of the arguments is undefined or empty.\r\n */\r\n public async uploadFile(accessToken: AccessToken, uploadUrlString: string, uploadFromPathname: string, progressCallback?: ProgressCallback): Promise<void> {\r\n const safeToLogUrl = MobileFileHandler.getSafeUrlForLogging(uploadUrlString);\r\n Logger.logTrace(loggerCategory, `Uploading file to ${safeToLogUrl}`);\r\n defined(\"uploadUrlString\", uploadUrlString);\r\n defined(\"uploadFromPathname\", uploadFromPathname);\r\n\r\n const fileSize = this.getFileSize(uploadFromPathname);\r\n const file = fs.openSync(uploadFromPathname, \"r\");\r\n const chunkSize = 4 * 1024 * 1024;\r\n\r\n try {\r\n let blockList = '<?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\"?><BlockList>';\r\n let i = 0;\r\n const callback: ProgressCallback = (progress: ProgressInfo) => {\r\n const uploaded = i * chunkSize + progress.loaded;\r\n progressCallback!({ loaded: uploaded, percent: uploaded / fileSize, total: fileSize });\r\n };\r\n for (; i * chunkSize < fileSize; ++i) {\r\n await this.uploadChunk(accessToken, uploadUrlString, file, i, progressCallback ? callback : undefined);\r\n blockList += `<Latest>${this.getBlockId(i)}</Latest>`;\r\n }\r\n blockList += \"</BlockList>\";\r\n\r\n const options: RequestOptions = {\r\n method: \"PUT\",\r\n headers: {\r\n \"Content-Type\": \"application/xml\", // eslint-disable-line @typescript-eslint/naming-convention\r\n \"Content-Length\": blockList.length, // eslint-disable-line @typescript-eslint/naming-convention\r\n },\r\n body: blockList,\r\n agent: this.agent,\r\n timeout: {\r\n response: 5000,\r\n deadline: 60000,\r\n },\r\n };\r\n\r\n const uploadUrl = `${uploadUrlString}&comp=blocklist`;\r\n await request(uploadUrl, options);\r\n } finally {\r\n fs.closeSync(file);\r\n }\r\n }\r\n\r\n /**\r\n * Get size of a file.\r\n * @param filePath Path of the file.\r\n * @returns Size of the file.\r\n */\r\n public getFileSize(filePath: string): number {\r\n return fs.statSync(filePath).size;\r\n }\r\n\r\n /**\r\n * Check if path is a directory.\r\n * @param filePath Path of the file.\r\n * @returns True if path is directory.\r\n */\r\n public isDirectory(filePath: string): boolean {\r\n return fs.statSync(filePath).isDirectory();\r\n }\r\n\r\n /**\r\n * Check if path exists.\r\n * @param filePath Path of the file.\r\n * @returns True if path exists.\r\n */\r\n public exists(filePath: string): boolean {\r\n return fs.existsSync(filePath);\r\n }\r\n\r\n /**\r\n * Deletes file.\r\n * @param filePath Path of the file.\r\n */\r\n public unlink(filePath: string): void {\r\n fs.unlinkSync(filePath);\r\n }\r\n\r\n /**\r\n * Get file name from the path.\r\n * @param filePath Path of the file.\r\n * @returns File name.\r\n */\r\n public basename(filePath: string): string {\r\n return path.basename(filePath);\r\n }\r\n\r\n /**\r\n * Join multiple strings into a single path.\r\n * @param paths Strings to join.\r\n * @returns Joined path.\r\n */\r\n public join(...paths: string[]): string {\r\n return path.join(...paths);\r\n }\r\n}\r\n"]}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { BeEvent } from "@itwin/core-bentley";
|
|
2
|
+
import { NativeHostOpts } from "@itwin/core-backend";
|
|
3
|
+
import { NativeAppAuthorizationConfiguration, RpcInterfaceDefinition } from "@itwin/core-common";
|
|
4
|
+
import { CancelRequest, ProgressCallback } from "@bentley/itwin-client";
|
|
5
|
+
import { BatteryState, DeviceEvents, Orientation } from "../common/MobileAppProps";
|
|
6
|
+
import { MobileAuthorizationBackend } from "./MobileAuthorizationBackend";
|
|
7
|
+
/** @beta */
|
|
8
|
+
export declare type MobileCompletionCallback = (downloadUrl: string, downloadFileUrl: string, cancelled: boolean, err?: string) => void;
|
|
9
|
+
/** @beta */
|
|
10
|
+
export declare type MobileProgressCallback = (bytesWritten: number, totalBytesWritten: number, totalBytesExpectedToWrite: number) => void;
|
|
11
|
+
/** @beta */
|
|
12
|
+
export declare type MobileCancelCallback = () => boolean;
|
|
13
|
+
/** @beta */
|
|
14
|
+
export interface DownloadTask {
|
|
15
|
+
url: string;
|
|
16
|
+
downloadPath: string;
|
|
17
|
+
isDetached: boolean;
|
|
18
|
+
isRunning: boolean;
|
|
19
|
+
totalBytes?: number;
|
|
20
|
+
doneBytes?: number;
|
|
21
|
+
cancelId?: number;
|
|
22
|
+
isBackground?: boolean;
|
|
23
|
+
cancel?: MobileCancelCallback;
|
|
24
|
+
toBackground: () => boolean;
|
|
25
|
+
toForeground: () => boolean;
|
|
26
|
+
}
|
|
27
|
+
/** @beta */
|
|
28
|
+
export declare abstract class MobileDevice {
|
|
29
|
+
emit(eventName: DeviceEvents, ...args: any[]): void;
|
|
30
|
+
abstract getOrientation(): Orientation;
|
|
31
|
+
abstract getBatteryState(): BatteryState;
|
|
32
|
+
abstract getBatteryLevel(): number;
|
|
33
|
+
abstract createDownloadTask(downloadUrl: string, isBackground: boolean, downloadTo: string, completion: MobileCompletionCallback, progress?: MobileProgressCallback): number;
|
|
34
|
+
abstract cancelDownloadTask(cancelId: number): boolean;
|
|
35
|
+
abstract getDownloadTasks(): DownloadTask[];
|
|
36
|
+
abstract resumeDownloadInForeground(requestId: number): boolean;
|
|
37
|
+
abstract resumeDownloadInBackground(requestId: number): boolean;
|
|
38
|
+
abstract reconnect(connection: number): void;
|
|
39
|
+
abstract authSignIn(callback: (err?: string) => void): void;
|
|
40
|
+
abstract authSignOut(callback: (err?: string) => void): void;
|
|
41
|
+
abstract authGetAccessToken(callback: (accessToken?: string, err?: string) => void): void;
|
|
42
|
+
authInit(_config: NativeAppAuthorizationConfiguration, callback: (err?: string) => void): void;
|
|
43
|
+
abstract authStateChanged(accessToken?: string, err?: string): void;
|
|
44
|
+
}
|
|
45
|
+
/** @beta */
|
|
46
|
+
export interface MobileHostOpts extends NativeHostOpts {
|
|
47
|
+
mobileHost?: {
|
|
48
|
+
device?: MobileDevice;
|
|
49
|
+
/** list of RPC interface definitions to register */
|
|
50
|
+
rpcInterfaces?: RpcInterfaceDefinition[];
|
|
51
|
+
/** if present, [[NativeHost.authorizationClient]] will be set to an instance of NativeAppAuthorizationBackend and will be initialized. */
|
|
52
|
+
authConfig?: NativeAppAuthorizationConfiguration;
|
|
53
|
+
/** if true, do not attempt to initialize AuthorizationClient on startup */
|
|
54
|
+
noInitializeAuthClient?: boolean;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* @beta
|
|
59
|
+
*/
|
|
60
|
+
export declare class MobileHost {
|
|
61
|
+
private static _device?;
|
|
62
|
+
static get device(): MobileDevice;
|
|
63
|
+
static readonly onMemoryWarning: BeEvent<import("@itwin/core-bentley").Listener>;
|
|
64
|
+
static readonly onOrientationChanged: BeEvent<import("@itwin/core-bentley").Listener>;
|
|
65
|
+
static readonly onEnterForeground: BeEvent<import("@itwin/core-bentley").Listener>;
|
|
66
|
+
static readonly onEnterBackground: BeEvent<import("@itwin/core-bentley").Listener>;
|
|
67
|
+
static readonly onWillTerminate: BeEvent<import("@itwin/core-bentley").Listener>;
|
|
68
|
+
/** @internal */
|
|
69
|
+
static get authorization(): MobileAuthorizationBackend;
|
|
70
|
+
/** @internal */
|
|
71
|
+
static reconnect(connection: number): void;
|
|
72
|
+
/** @internal */
|
|
73
|
+
static downloadFile(downloadUrl: string, downloadTo: string, progress?: ProgressCallback, cancelRequest?: CancelRequest): Promise<void>;
|
|
74
|
+
static get isValid(): boolean;
|
|
75
|
+
/** Start the backend of a mobile app. */
|
|
76
|
+
static startup(opt?: MobileHostOpts): Promise<void>;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=MobileHost.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MobileHost.d.ts","sourceRoot":"","sources":["../../../src/backend/MobileHost.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*---------------------------------------------------------------------------------------------
|
|
3
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
4
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
5
|
+
*--------------------------------------------------------------------------------------------*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.MobileHost = exports.MobileDevice = void 0;
|
|
8
|
+
const core_bentley_1 = require("@itwin/core-bentley");
|
|
9
|
+
const core_backend_1 = require("@itwin/core-backend");
|
|
10
|
+
const core_common_1 = require("@itwin/core-common");
|
|
11
|
+
const itwin_client_1 = require("@bentley/itwin-client");
|
|
12
|
+
const presentation_common_1 = require("@itwin/presentation-common");
|
|
13
|
+
const MobileAppProps_1 = require("../common/MobileAppProps");
|
|
14
|
+
const MobileRpcManager_1 = require("../common/MobileRpcManager");
|
|
15
|
+
const MobileAuthorizationBackend_1 = require("./MobileAuthorizationBackend");
|
|
16
|
+
const MobileRpcServer_1 = require("./MobileRpcServer");
|
|
17
|
+
/** @beta */
|
|
18
|
+
class MobileDevice {
|
|
19
|
+
emit(eventName, ...args) {
|
|
20
|
+
switch (eventName) {
|
|
21
|
+
case "memoryWarning":
|
|
22
|
+
MobileHost.onMemoryWarning.raiseEvent(...args);
|
|
23
|
+
break;
|
|
24
|
+
case "orientationChanged":
|
|
25
|
+
MobileHost.onOrientationChanged.raiseEvent(...args);
|
|
26
|
+
break;
|
|
27
|
+
case "enterForeground":
|
|
28
|
+
MobileHost.onEnterForeground.raiseEvent(...args);
|
|
29
|
+
break;
|
|
30
|
+
case "enterBackground":
|
|
31
|
+
MobileHost.onEnterBackground.raiseEvent(...args);
|
|
32
|
+
break;
|
|
33
|
+
case "willTerminate":
|
|
34
|
+
MobileHost.onWillTerminate.raiseEvent(...args);
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
authInit(_config, callback) { callback(); }
|
|
39
|
+
}
|
|
40
|
+
exports.MobileDevice = MobileDevice;
|
|
41
|
+
class MobileAppHandler extends core_backend_1.IpcHandler {
|
|
42
|
+
get channelName() { return MobileAppProps_1.mobileAppChannel; }
|
|
43
|
+
async reconnect(connection) {
|
|
44
|
+
MobileHost.reconnect(connection);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* @beta
|
|
49
|
+
*/
|
|
50
|
+
class MobileHost {
|
|
51
|
+
static get device() { return this._device; }
|
|
52
|
+
/** @internal */
|
|
53
|
+
static get authorization() { return core_backend_1.IModelHost.authorizationClient; }
|
|
54
|
+
/** @internal */
|
|
55
|
+
static reconnect(connection) {
|
|
56
|
+
this.device.reconnect(connection);
|
|
57
|
+
}
|
|
58
|
+
/** @internal */
|
|
59
|
+
static async downloadFile(downloadUrl, downloadTo, progress, cancelRequest) {
|
|
60
|
+
return new Promise((resolve, reject) => {
|
|
61
|
+
let progressCb;
|
|
62
|
+
let lastReportedOn = Date.now();
|
|
63
|
+
const minTimeBeforeReportingProgress = 1000;
|
|
64
|
+
if (progress) {
|
|
65
|
+
progressCb = (_bytesWritten, totalBytesWritten, totalBytesExpectedToWrite) => {
|
|
66
|
+
const currentTime = Date.now();
|
|
67
|
+
const timeSinceLastEvent = currentTime - lastReportedOn;
|
|
68
|
+
// report all event for last 5 Mbs so we never miss 100% progress event
|
|
69
|
+
const lastEvent = (totalBytesExpectedToWrite - totalBytesWritten) < 1024 * 1024 * 5;
|
|
70
|
+
if (timeSinceLastEvent < minTimeBeforeReportingProgress && !lastEvent)
|
|
71
|
+
return;
|
|
72
|
+
lastReportedOn = currentTime;
|
|
73
|
+
const percent = Number((100 * (totalBytesWritten / totalBytesExpectedToWrite)).toFixed(2));
|
|
74
|
+
progress({ total: totalBytesExpectedToWrite, loaded: totalBytesWritten, percent });
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const requestId = this.device.createDownloadTask(downloadUrl, false, downloadTo, (_downloadUrl, _downloadFileUrl, cancelled, err) => {
|
|
78
|
+
if (cancelled)
|
|
79
|
+
reject(new itwin_client_1.UserCancelledError(core_bentley_1.BriefcaseStatus.DownloadCancelled, "User cancelled download"));
|
|
80
|
+
else if (err)
|
|
81
|
+
reject(new itwin_client_1.DownloadFailed(400, "Download failed"));
|
|
82
|
+
else
|
|
83
|
+
resolve();
|
|
84
|
+
}, progressCb);
|
|
85
|
+
if (cancelRequest) {
|
|
86
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
87
|
+
cancelRequest.cancel = () => { return this.device.cancelDownloadTask(requestId); };
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
static get isValid() { return undefined !== this._device; }
|
|
92
|
+
/** Start the backend of a mobile app. */
|
|
93
|
+
static async startup(opt) {
|
|
94
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
95
|
+
if (!this.isValid) {
|
|
96
|
+
this._device = (_b = (_a = opt === null || opt === void 0 ? void 0 : opt.mobileHost) === null || _a === void 0 ? void 0 : _a.device) !== null && _b !== void 0 ? _b : new MobileDevice();
|
|
97
|
+
// set global device interface.
|
|
98
|
+
global.__iTwinJsNativeBridge = this._device;
|
|
99
|
+
// following will provide impl for device specific api.
|
|
100
|
+
(0, MobileRpcServer_1.setupMobileRpc)();
|
|
101
|
+
}
|
|
102
|
+
await core_backend_1.NativeHost.startup(opt);
|
|
103
|
+
if (core_backend_1.IpcHost.isValid)
|
|
104
|
+
MobileAppHandler.register();
|
|
105
|
+
const rpcInterfaces = (_d = (_c = opt === null || opt === void 0 ? void 0 : opt.mobileHost) === null || _c === void 0 ? void 0 : _c.rpcInterfaces) !== null && _d !== void 0 ? _d : [
|
|
106
|
+
core_common_1.IModelReadRpcInterface,
|
|
107
|
+
core_common_1.IModelTileRpcInterface,
|
|
108
|
+
core_common_1.SnapshotIModelRpcInterface,
|
|
109
|
+
presentation_common_1.PresentationRpcInterface,
|
|
110
|
+
];
|
|
111
|
+
MobileRpcManager_1.MobileRpcManager.initializeImpl(rpcInterfaces);
|
|
112
|
+
const authorizationBackend = new MobileAuthorizationBackend_1.MobileAuthorizationBackend((_e = opt === null || opt === void 0 ? void 0 : opt.mobileHost) === null || _e === void 0 ? void 0 : _e.authConfig);
|
|
113
|
+
const connectivityStatus = core_backend_1.NativeHost.checkInternetConnectivity();
|
|
114
|
+
if (((_f = opt === null || opt === void 0 ? void 0 : opt.mobileHost) === null || _f === void 0 ? void 0 : _f.authConfig) && true !== ((_g = opt === null || opt === void 0 ? void 0 : opt.mobileHost) === null || _g === void 0 ? void 0 : _g.noInitializeAuthClient) && connectivityStatus === core_common_1.InternetConnectivityStatus.Online) {
|
|
115
|
+
await authorizationBackend.initialize((_h = opt === null || opt === void 0 ? void 0 : opt.mobileHost) === null || _h === void 0 ? void 0 : _h.authConfig);
|
|
116
|
+
}
|
|
117
|
+
core_backend_1.IModelHost.authorizationClient = authorizationBackend;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
exports.MobileHost = MobileHost;
|
|
121
|
+
MobileHost.onMemoryWarning = new core_bentley_1.BeEvent();
|
|
122
|
+
MobileHost.onOrientationChanged = new core_bentley_1.BeEvent();
|
|
123
|
+
MobileHost.onEnterForeground = new core_bentley_1.BeEvent();
|
|
124
|
+
MobileHost.onEnterBackground = new core_bentley_1.BeEvent();
|
|
125
|
+
MobileHost.onWillTerminate = new core_bentley_1.BeEvent();
|
|
126
|
+
//# sourceMappingURL=MobileHost.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MobileHost.js","sourceRoot":"","sources":["../../../src/backend/MobileHost.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n\r\nimport { BeEvent, BriefcaseStatus } from \"@itwin/core-bentley\";\r\nimport { IModelHost, IpcHandler, IpcHost, NativeHost, NativeHostOpts } from \"@itwin/core-backend\";\r\nimport {\r\n IModelReadRpcInterface, IModelTileRpcInterface, InternetConnectivityStatus, NativeAppAuthorizationConfiguration, RpcInterfaceDefinition,\r\n SnapshotIModelRpcInterface,\r\n} from \"@itwin/core-common\";\r\nimport { CancelRequest, DownloadFailed, ProgressCallback, UserCancelledError } from \"@bentley/itwin-client\";\r\nimport { PresentationRpcInterface } from \"@itwin/presentation-common\";\r\nimport { BatteryState, DeviceEvents, mobileAppChannel, MobileAppFunctions, Orientation } from \"../common/MobileAppProps\";\r\nimport { MobileRpcManager } from \"../common/MobileRpcManager\";\r\nimport { MobileAuthorizationBackend } from \"./MobileAuthorizationBackend\";\r\nimport { setupMobileRpc } from \"./MobileRpcServer\";\r\n\r\n/** @beta */\r\nexport type MobileCompletionCallback = (downloadUrl: string, downloadFileUrl: string, cancelled: boolean, err?: string) => void;\r\n/** @beta */\r\nexport type MobileProgressCallback = (bytesWritten: number, totalBytesWritten: number, totalBytesExpectedToWrite: number) => void;\r\n/** @beta */\r\nexport type MobileCancelCallback = () => boolean;\r\n\r\n/** @beta */\r\nexport interface DownloadTask {\r\n url: string;\r\n downloadPath: string;\r\n isDetached: boolean;\r\n isRunning: boolean;\r\n totalBytes?: number;\r\n doneBytes?: number;\r\n cancelId?: number;\r\n isBackground?: boolean;\r\n cancel?: MobileCancelCallback;\r\n toBackground: () => boolean;\r\n toForeground: () => boolean;\r\n}\r\n\r\n/** @beta */\r\nexport abstract class MobileDevice {\r\n public emit(eventName: DeviceEvents, ...args: any[]) {\r\n switch (eventName) {\r\n case \"memoryWarning\":\r\n MobileHost.onMemoryWarning.raiseEvent(...args); break;\r\n case \"orientationChanged\":\r\n MobileHost.onOrientationChanged.raiseEvent(...args); break;\r\n case \"enterForeground\":\r\n MobileHost.onEnterForeground.raiseEvent(...args); break;\r\n case \"enterBackground\":\r\n MobileHost.onEnterBackground.raiseEvent(...args); break;\r\n case \"willTerminate\":\r\n MobileHost.onWillTerminate.raiseEvent(...args); break;\r\n }\r\n }\r\n\r\n public abstract getOrientation(): Orientation;\r\n public abstract getBatteryState(): BatteryState;\r\n public abstract getBatteryLevel(): number;\r\n public abstract createDownloadTask(downloadUrl: string, isBackground: boolean, downloadTo: string, completion: MobileCompletionCallback, progress?: MobileProgressCallback): number;\r\n public abstract cancelDownloadTask(cancelId: number): boolean;\r\n public abstract getDownloadTasks(): DownloadTask[];\r\n public abstract resumeDownloadInForeground(requestId: number): boolean;\r\n public abstract resumeDownloadInBackground(requestId: number): boolean;\r\n public abstract reconnect(connection: number): void;\r\n public abstract authSignIn(callback: (err?: string) => void): void;\r\n public abstract authSignOut(callback: (err?: string) => void): void;\r\n public abstract authGetAccessToken(callback: (accessToken?: string, err?: string) => void): void;\r\n public authInit(_config: NativeAppAuthorizationConfiguration, callback: (err?: string) => void): void { callback(); }\r\n public abstract authStateChanged(accessToken?: string, err?: string): void;\r\n}\r\n\r\nclass MobileAppHandler extends IpcHandler implements MobileAppFunctions {\r\n public get channelName() { return mobileAppChannel; }\r\n public async reconnect(connection: number) {\r\n MobileHost.reconnect(connection);\r\n }\r\n}\r\n\r\n/** @beta */\r\nexport interface MobileHostOpts extends NativeHostOpts {\r\n mobileHost?: {\r\n device?: MobileDevice;\r\n /** list of RPC interface definitions to register */\r\n rpcInterfaces?: RpcInterfaceDefinition[];\r\n /** if present, [[NativeHost.authorizationClient]] will be set to an instance of NativeAppAuthorizationBackend and will be initialized. */\r\n authConfig?: NativeAppAuthorizationConfiguration;\r\n /** if true, do not attempt to initialize AuthorizationClient on startup */\r\n noInitializeAuthClient?: boolean;\r\n };\r\n}\r\n\r\n/**\r\n * @beta\r\n */\r\nexport class MobileHost {\r\n private static _device?: MobileDevice;\r\n public static get device() { return this._device!; }\r\n public static readonly onMemoryWarning = new BeEvent();\r\n public static readonly onOrientationChanged = new BeEvent();\r\n public static readonly onEnterForeground = new BeEvent();\r\n public static readonly onEnterBackground = new BeEvent();\r\n public static readonly onWillTerminate = new BeEvent();\r\n\r\n /** @internal */\r\n public static get authorization() { return IModelHost.authorizationClient as MobileAuthorizationBackend; }\r\n\r\n /** @internal */\r\n public static reconnect(connection: number) {\r\n this.device.reconnect(connection);\r\n }\r\n\r\n /** @internal */\r\n public static async downloadFile(downloadUrl: string, downloadTo: string, progress?: ProgressCallback, cancelRequest?: CancelRequest): Promise<void> {\r\n return new Promise<void>((resolve, reject) => {\r\n\r\n let progressCb: MobileProgressCallback | undefined;\r\n let lastReportedOn = Date.now();\r\n const minTimeBeforeReportingProgress = 1000;\r\n if (progress) {\r\n progressCb = (_bytesWritten: number, totalBytesWritten: number, totalBytesExpectedToWrite: number) => {\r\n const currentTime = Date.now();\r\n const timeSinceLastEvent = currentTime - lastReportedOn;\r\n // report all event for last 5 Mbs so we never miss 100% progress event\r\n const lastEvent = (totalBytesExpectedToWrite - totalBytesWritten) < 1024 * 1024 * 5;\r\n if (timeSinceLastEvent < minTimeBeforeReportingProgress && !lastEvent)\r\n return;\r\n\r\n lastReportedOn = currentTime;\r\n const percent = Number((100 * (totalBytesWritten / totalBytesExpectedToWrite)).toFixed(2));\r\n progress({ total: totalBytesExpectedToWrite, loaded: totalBytesWritten, percent });\r\n };\r\n }\r\n const requestId = this.device.createDownloadTask(downloadUrl, false, downloadTo, (_downloadUrl: string, _downloadFileUrl: string, cancelled: boolean, err?: string) => {\r\n if (cancelled)\r\n reject(new UserCancelledError(BriefcaseStatus.DownloadCancelled, \"User cancelled download\"));\r\n else if (err)\r\n reject(new DownloadFailed(400, \"Download failed\"));\r\n else\r\n resolve();\r\n }, progressCb);\r\n if (cancelRequest) {\r\n // eslint-disable-next-line @typescript-eslint/unbound-method\r\n cancelRequest.cancel = () => { return this.device.cancelDownloadTask(requestId); };\r\n }\r\n });\r\n }\r\n\r\n public static get isValid() { return undefined !== this._device; }\r\n\r\n /** Start the backend of a mobile app. */\r\n public static async startup(opt?: MobileHostOpts): Promise<void> {\r\n if (!this.isValid) {\r\n this._device = opt?.mobileHost?.device ?? new (MobileDevice as any)();\r\n // set global device interface.\r\n (global as any).__iTwinJsNativeBridge = this._device;\r\n // following will provide impl for device specific api.\r\n setupMobileRpc();\r\n }\r\n\r\n await NativeHost.startup(opt);\r\n if (IpcHost.isValid)\r\n MobileAppHandler.register();\r\n\r\n const rpcInterfaces = opt?.mobileHost?.rpcInterfaces ?? [\r\n IModelReadRpcInterface,\r\n IModelTileRpcInterface,\r\n SnapshotIModelRpcInterface,\r\n PresentationRpcInterface,\r\n ];\r\n\r\n MobileRpcManager.initializeImpl(rpcInterfaces);\r\n\r\n const authorizationBackend = new MobileAuthorizationBackend(opt?.mobileHost?.authConfig);\r\n const connectivityStatus = NativeHost.checkInternetConnectivity();\r\n if (opt?.mobileHost?.authConfig && true !== opt?.mobileHost?.noInitializeAuthClient && connectivityStatus === InternetConnectivityStatus.Online) {\r\n await authorizationBackend.initialize(opt?.mobileHost?.authConfig);\r\n }\r\n IModelHost.authorizationClient = authorizationBackend;\r\n }\r\n}\r\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { MobileRpcGateway } from "../common/MobileRpcProtocol";
|
|
2
|
+
export declare class MobileRpcServer {
|
|
3
|
+
private static _nextId;
|
|
4
|
+
static interop: MobileRpcGateway;
|
|
5
|
+
private _server;
|
|
6
|
+
private _connection;
|
|
7
|
+
private _port;
|
|
8
|
+
private _connectionId;
|
|
9
|
+
private _pingTimer;
|
|
10
|
+
constructor();
|
|
11
|
+
private _onListening;
|
|
12
|
+
private _notifyListening;
|
|
13
|
+
private _onConnection;
|
|
14
|
+
private _createSender;
|
|
15
|
+
private _onConnectionMessage;
|
|
16
|
+
dispose(): void;
|
|
17
|
+
}
|
|
18
|
+
export declare function setupMobileRpc(): void;
|
|
19
|
+
//# sourceMappingURL=MobileRpcServer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MobileRpcServer.d.ts","sourceRoot":"","sources":["../../../src/backend/MobileRpcServer.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,gBAAgB,EAAqB,MAAM,6BAA6B,CAAC;AAYlF,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAC,OAAO,CAAM;IAE5B,OAAc,OAAO,EAAE,gBAAgB,CAMrC;IAEF,OAAO,CAAC,OAAO,CAAY;IAC3B,OAAO,CAAC,WAAW,CAAiB;IACpC,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,UAAU,CAAiB;;IAoBnC,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,aAAa;IAiBrB,OAAO,CAAC,oBAAoB;IAiBrB,OAAO;CAUf;AAKD,wBAAgB,cAAc,SAiC7B"}
|