Merge pull request #1325 from realm/tg/merge-master-to-next-major
Merge latest master to next-major
This commit is contained in:
commit
e42db5aba2
|
@ -54,6 +54,7 @@
|
||||||
"type": "node",
|
"type": "node",
|
||||||
"request": "attach",
|
"request": "attach",
|
||||||
"name": "Attach to Port",
|
"name": "Attach to Port",
|
||||||
|
"protocol": "legacy",
|
||||||
"address": "localhost",
|
"address": "localhost",
|
||||||
"port": 5858
|
"port": 5858
|
||||||
}
|
}
|
||||||
|
|
47
CHANGELOG.md
47
CHANGELOG.md
|
@ -1,3 +1,48 @@
|
||||||
|
1.12.0 Release notes (2017-9-14)
|
||||||
|
=============================================================
|
||||||
|
### Breaking changes
|
||||||
|
* None
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
* Improve performance of the RPC worker for chrome debugging.
|
||||||
|
* Added Progress API `realm.syncSession.addProgressNotification` and `realm.syncSession.removeProgressNotification`
|
||||||
|
* Added additional parameter for `Realm.open` and `Realm.openAsync` for download progress notifications
|
||||||
|
* Added `Realm.deleteFile` for deleting a Realm (#363).
|
||||||
|
* Added `Realm.deleteModel` for deleting a Realm model in a migration (#573).
|
||||||
|
* Added support for in-memory Realms.
|
||||||
|
* `Realm.Sync.User.login`, `Realm.Sync.User.register`, and `Realm.Sync.User.registerWithProvider` return Promises and deprecate the callback style for them. Callbacks will continue to work for backward compatibility.
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
* Adding missing TypeScript definitions; Permissions (#1283), `setFeatureToken()`, and instructions (#1298).
|
||||||
|
* Removed `loginWithProvider` from TypeScript definition files. This API never existed and was incorrectly added.
|
||||||
|
|
||||||
|
1.11.1 Release notes (2017-9-1)
|
||||||
|
=============================================================
|
||||||
|
### Breaking changes
|
||||||
|
* None
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
* None
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
* Fix accessToken.
|
||||||
|
|
||||||
|
1.11.0 Release notes (2017-8-31)
|
||||||
|
=============================================================
|
||||||
|
### Breaking changes
|
||||||
|
* None
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
* Added methods `Realm.beginTransaction()`, `Realm.commitTransaction()`, `Realm.cancelTransaction()` to manually control write transactions.
|
||||||
|
* Added property `Realm.isInTransaction` which indicates if write transaction is in progress.
|
||||||
|
* Added `shouldCompactOnLaunch` to configuration (#507).
|
||||||
|
* Added `Realm.compact()` for manually compacting Realm files.
|
||||||
|
* Added various methods for permission management (#1204).
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
* None
|
||||||
|
|
||||||
|
|
||||||
1.10.3 Release notes (2017-8-16)
|
1.10.3 Release notes (2017-8-16)
|
||||||
=============================================================
|
=============================================================
|
||||||
### Breaking changes
|
### Breaking changes
|
||||||
|
@ -7,7 +52,7 @@
|
||||||
* None
|
* None
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
* none
|
* None
|
||||||
|
|
||||||
|
|
||||||
1.10.2 Release notes (2017-8-16)
|
1.10.2 Release notes (2017-8-16)
|
||||||
|
|
|
@ -38,3 +38,54 @@ Below are some guidelines about the format of the commit message itself:
|
||||||
Realm welcomes all contributions! The only requirement we have is that, like many other projects, we need to have a [Contributor License Agreement](https://en.wikipedia.org/wiki/Contributor_License_Agreement) (CLA) in place before we can accept any external code. Our own CLA is a modified version of the Apache Software Foundation’s CLA.
|
Realm welcomes all contributions! The only requirement we have is that, like many other projects, we need to have a [Contributor License Agreement](https://en.wikipedia.org/wiki/Contributor_License_Agreement) (CLA) in place before we can accept any external code. Our own CLA is a modified version of the Apache Software Foundation’s CLA.
|
||||||
|
|
||||||
[Please submit your CLA electronically using our Google form](https://docs.google.com/forms/d/1bVp-Wp5nmNFz9Nx-ngTmYBVWVdwTyKj4T0WtfVm0Ozs/viewform?fbzx=4154977190905366979) so we can accept your submissions. The GitHub username you file there will need to match that of your Pull Requests. If you have any questions or cannot file the CLA electronically, you can email <help@realm.io>.
|
[Please submit your CLA electronically using our Google form](https://docs.google.com/forms/d/1bVp-Wp5nmNFz9Nx-ngTmYBVWVdwTyKj4T0WtfVm0Ozs/viewform?fbzx=4154977190905366979) so we can accept your submissions. The GitHub username you file there will need to match that of your Pull Requests. If you have any questions or cannot file the CLA electronically, you can email <help@realm.io>.
|
||||||
|
|
||||||
|
### Guidelines
|
||||||
|
|
||||||
|
Adding new functionality to Realm JavaScript requires that you modify a few places in the repository. As an example, consider adding a function `crashOnStart()` to the class `Realm`. The subsections below guides you through where and what to add.
|
||||||
|
|
||||||
|
#### Add the function
|
||||||
|
|
||||||
|
First, add a prototype of function to `src/js_realm.hpp`; look for a section marked by the comment `// method`. The prototype looks like:
|
||||||
|
|
||||||
|
```
|
||||||
|
static void crashOnStart(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||||
|
```
|
||||||
|
|
||||||
|
You have to implement the function. Find a place in `src/js_realm.hpp` to add it (maybe at the end):
|
||||||
|
|
||||||
|
```
|
||||||
|
template<typename T>
|
||||||
|
void RealmClass<T>::crashOnStart(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||||
|
validate_argument_count(argc, 0); // <- the function doesn't take any arguments
|
||||||
|
|
||||||
|
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object); // <- unwrap the Realm instance
|
||||||
|
|
||||||
|
// add the actual implement ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Testing is important, and in `tests/js/realm-tests.js` you can add the tests you need.
|
||||||
|
|
||||||
|
Note: If your new API and/or test cases are not getting picked up when running the Android or iOS tests, remove the corresponding installed package from react-test-app and try again.
|
||||||
|
|
||||||
|
```
|
||||||
|
rm -rf tests/react-test-app/node_modules/realm
|
||||||
|
rm -rf tests/react-test-app/node_modules/realm-tests
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Wrap the function
|
||||||
|
|
||||||
|
In order to call the C++ implementation, the JavaScript engine has to know about the function. You must simply add it to the map of methods/functions. Find `MethodMap<T> const methods` declaration in `src/js_realm.hpp` and add your function to it:
|
||||||
|
|
||||||
|
```
|
||||||
|
{"crashOnStart", wrap<crashOnStart>},
|
||||||
|
```
|
||||||
|
|
||||||
|
#### The final details
|
||||||
|
|
||||||
|
To finish adding your new function, you will have to add your function a few places:
|
||||||
|
|
||||||
|
* In `lib/index.d.ts` you add the TypeScript declaration
|
||||||
|
* Documentation is added in `docs/realm.js`
|
||||||
|
* Add your function to `lib/browser/index.js` in order to enable it in the Chrome Debugger
|
||||||
|
* Add an entry to `CHANGELOG.md` if applicable (Breaking changes/Enhancements/Bug fixes)
|
||||||
|
|
|
@ -68,8 +68,8 @@ stage('build') {
|
||||||
macos_node_release: doMacBuild('node Release'),
|
macos_node_release: doMacBuild('node Release'),
|
||||||
//macos_realmjs_debug: doMacBuild('realmjs Debug'),
|
//macos_realmjs_debug: doMacBuild('realmjs Debug'),
|
||||||
//macos_realmjs_release: doMacBuild('realmjs Release'),
|
//macos_realmjs_release: doMacBuild('realmjs Release'),
|
||||||
macos_react_tests_debug: doReactBuild('react-tests Debug'),
|
macos_react_tests_debug: doMacBuild('react-tests Debug'),
|
||||||
macos_react_tests_release: doReactBuild('react-tests Release'),
|
macos_react_tests_release: doMacBuild('react-tests Release'),
|
||||||
macos_react_example_debug: doMacBuild('react-example Debug'),
|
macos_react_example_debug: doMacBuild('react-example Debug'),
|
||||||
macos_react_example_release: doMacBuild('react-example Release'),
|
macos_react_example_release: doMacBuild('react-example Release'),
|
||||||
//android_react_tests: doAndroidBuild('react-tests-android', {
|
//android_react_tests: doAndroidBuild('react-tests-android', {
|
||||||
|
@ -188,23 +188,9 @@ def doMacBuild(target, postStep = null) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def doReactBuild(target, postStep = null) {
|
|
||||||
return {
|
|
||||||
node('xamarin-mac') {
|
|
||||||
try {
|
|
||||||
lock("${env.NODE_NAME} iOS Simulator") {
|
|
||||||
doInside("./scripts/test.sh", target, postStep)
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
deleteDir()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def doWindowsBuild() {
|
def doWindowsBuild() {
|
||||||
return {
|
return {
|
||||||
node('windows') {
|
node('windows && nodejs') {
|
||||||
unstash 'source'
|
unstash 'source'
|
||||||
try {
|
try {
|
||||||
bat 'npm install --build-from-source=realm'
|
bat 'npm install --build-from-source=realm'
|
||||||
|
|
|
@ -26,17 +26,17 @@ The API reference is located at [realm.io/docs/javscript/latest/api](https://rea
|
||||||
- **Need help with your code?**: Look for previous questions on the [#realm tag](https://stackoverflow.com/questions/tagged/realm?sort=newest) — or [ask a new question](https://stackoverflow.com/questions/ask?tags=realm). We actively monitor and answer questions on SO!
|
- **Need help with your code?**: Look for previous questions on the [#realm tag](https://stackoverflow.com/questions/tagged/realm?sort=newest) — or [ask a new question](https://stackoverflow.com/questions/ask?tags=realm). We actively monitor and answer questions on SO!
|
||||||
- **Have a bug to report?** [Open an issue](https://github.com/realm/realm-js/issues/new). If possible, include the version of Realm, a full log, the Realm file, and a project that shows the issue.
|
- **Have a bug to report?** [Open an issue](https://github.com/realm/realm-js/issues/new). If possible, include the version of Realm, a full log, the Realm file, and a project that shows the issue.
|
||||||
- **Have a feature request?** [Open an issue](https://github.com/realm/realm-js/issues/new). Tell us what the feature should do, and why you want the feature.
|
- **Have a feature request?** [Open an issue](https://github.com/realm/realm-js/issues/new). Tell us what the feature should do, and why you want the feature.
|
||||||
- Sign up for our [**Community Newsletter**](https://www2.realm.io/l/210132/2016-12-05/fy9m) to get regular tips, learn about other use-cases and get alerted of blog posts and tutorials about Realm.
|
- Sign up for our [**Community Newsletter**](https://go.pardot.com/l/210132/2017-04-26/3j74l) to get regular tips, learn about other use-cases and get alerted of blog posts and tutorials about Realm.
|
||||||
|
|
||||||
## Building Realm
|
## Building Realm
|
||||||
|
|
||||||
In case you don't want to use the precompiled version on npm, you can build Realm yourself from source. You’ll need an Internet connection the first time you build in order to download the core library.
|
In case you don't want to use the precompiled version on npm, you can build Realm yourself from source. You’ll need an Internet connection the first time you build in order to download the core library.
|
||||||
|
|
||||||
Prerequisites:
|
Prerequisites:
|
||||||
- Node 4.0+
|
- Node: 4.0 <= version < 7.0
|
||||||
- Xcode 7.2+
|
- Xcode 7.2+
|
||||||
- Android SDK 23+
|
- Android SDK 23+
|
||||||
- Android NDK 10e+
|
- Android NDK 10e
|
||||||
|
|
||||||
First clone this repository:
|
First clone this repository:
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,14 @@ class Realm {
|
||||||
*/
|
*/
|
||||||
get schemaVersion() {}
|
get schemaVersion() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this Realm is in a write transaction.
|
||||||
|
* @type {boolean}
|
||||||
|
* @readonly
|
||||||
|
* @since 1.10.3
|
||||||
|
*/
|
||||||
|
get isInTransaction() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the sync session if this is a synced Realm
|
* Gets the sync session if this is a synced Realm
|
||||||
* @type {Session}
|
* @type {Session}
|
||||||
|
@ -87,7 +95,7 @@ class Realm {
|
||||||
* Open a realm asynchronously with a promise. If the realm is synced, it will be fully
|
* Open a realm asynchronously with a promise. If the realm is synced, it will be fully
|
||||||
* synchronized before it is available.
|
* synchronized before it is available.
|
||||||
* @param {Realm~Configuration} config
|
* @param {Realm~Configuration} config
|
||||||
* @returns {Promise} - a promise that will be resolved with the realm instance when it's available.
|
* @returns {ProgressPromise} - a promise that will be resolved with the realm instance when it's available.
|
||||||
*/
|
*/
|
||||||
static open(config) {}
|
static open(config) {}
|
||||||
|
|
||||||
|
@ -96,9 +104,10 @@ class Realm {
|
||||||
* synchronized before it is available.
|
* synchronized before it is available.
|
||||||
* @param {Realm~Configuration} config
|
* @param {Realm~Configuration} config
|
||||||
* @param {callback(error, realm)} - will be called when the realm is ready.
|
* @param {callback(error, realm)} - will be called when the realm is ready.
|
||||||
|
* @param {callback(transferred, transferable)} [progressCallback] - an optional callback for download progress notifications
|
||||||
* @throws {Error} If anything in the provided `config` is invalid.
|
* @throws {Error} If anything in the provided `config` is invalid.
|
||||||
*/
|
*/
|
||||||
static openAsync(config, callback) {}
|
static openAsync(config, callback, progressCallback) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes this Realm so it may be re-opened with a newer schema version.
|
* Closes this Realm so it may be re-opened with a newer schema version.
|
||||||
|
@ -124,6 +133,12 @@ class Realm {
|
||||||
*/
|
*/
|
||||||
delete(object) {}
|
delete(object) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a Realm model, including all of its objects.
|
||||||
|
* @param {string} name - the model name
|
||||||
|
*/
|
||||||
|
deleteModel(name) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* **WARNING:** This will delete **all** objects in the Realm!
|
* **WARNING:** This will delete **all** objects in the Realm!
|
||||||
*/
|
*/
|
||||||
|
@ -182,6 +197,40 @@ class Realm {
|
||||||
* @param {function()} callback
|
* @param {function()} callback
|
||||||
*/
|
*/
|
||||||
write(callback) {}
|
write(callback) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiate a write transaction.
|
||||||
|
* @throws {Error} When already in write transaction
|
||||||
|
*/
|
||||||
|
beginTransaction() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Commit a write transaction.
|
||||||
|
*/
|
||||||
|
commitTransaction() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel a write transaction.
|
||||||
|
*/
|
||||||
|
cancelTransaction() {}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Replaces all string columns in this Realm with a string enumeration column and compacts the
|
||||||
|
* database file.
|
||||||
|
*
|
||||||
|
* Cannot be called from a write transaction.
|
||||||
|
*
|
||||||
|
* Compaction will not occur if other `Realm` instances exist.
|
||||||
|
*
|
||||||
|
* While compaction is in progress, attempts by other threads or processes to open the database will
|
||||||
|
* wait.
|
||||||
|
*
|
||||||
|
* Be warned that resource requirements for compaction is proportional to the amount of live data in
|
||||||
|
* the database. Compaction works by writing the database contents to a temporary database file and
|
||||||
|
* then replacing the database with the temporary one.
|
||||||
|
* @returns {true} if compaction succeeds.
|
||||||
|
*/
|
||||||
|
compact() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -195,6 +244,13 @@ class Realm {
|
||||||
*/
|
*/
|
||||||
Realm.schemaVersion = function(path, encryptionKey) {};
|
Realm.schemaVersion = function(path, encryptionKey) {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the Realm file for the given configuration.
|
||||||
|
* @param {Realm~Configuration} config
|
||||||
|
* @throws {Error} If anything in the provided `config` is invalid.
|
||||||
|
*/
|
||||||
|
Realm.deleteFile = function(config) {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default path where to create and access the Realm file.
|
* The default path where to create and access the Realm file.
|
||||||
* @type {string}
|
* @type {string}
|
||||||
|
@ -213,8 +269,20 @@ Realm.defaultPath;
|
||||||
* This function takes two arguments:
|
* This function takes two arguments:
|
||||||
* - `oldRealm` - The Realm before migration is performed.
|
* - `oldRealm` - The Realm before migration is performed.
|
||||||
* - `newRealm` - The Realm that uses the latest `schema`, which should be modified as necessary.
|
* - `newRealm` - The Realm that uses the latest `schema`, which should be modified as necessary.
|
||||||
|
* @property {callback(number, number)} [shouldCompactOnLaunch] - The function called when opening
|
||||||
|
* a Realm for the first time during the life of a process to determine if it should be compacted
|
||||||
|
* before being returned to the user. The function takes two arguments:
|
||||||
|
* - `totalSize` - The total file size (data + free space)
|
||||||
|
* - `unusedSize` - The total bytes used by data in the file.
|
||||||
|
* It returns `true` to indicate that an attempt to compact the file should be made. The compaction
|
||||||
|
* will be skipped if another process is accessing it.
|
||||||
* @property {string} [path={@link Realm.defaultPath}] - The path to the file where the
|
* @property {string} [path={@link Realm.defaultPath}] - The path to the file where the
|
||||||
* Realm database should be stored.
|
* Realm database should be stored.
|
||||||
|
* @property {boolean} [inMemory=false] - Specifies if this Realm should be opened in-memory. This
|
||||||
|
* still requires a path (can be the default path) to identify the Realm so other processes can
|
||||||
|
* open the same Realm. The file will also be used as swap space if the Realm becomes bigger than
|
||||||
|
* what fits in memory, but it is not persistent and will be removed when the last instance
|
||||||
|
* is closed.
|
||||||
* @property {boolean} [readOnly=false] - Specifies if this Realm should be opened as read-only.
|
* @property {boolean} [readOnly=false] - Specifies if this Realm should be opened as read-only.
|
||||||
* @property {Array<Realm~ObjectClass|Realm~ObjectSchema>} [schema] - Specifies all the
|
* @property {Array<Realm~ObjectClass|Realm~ObjectSchema>} [schema] - Specifies all the
|
||||||
* object types in this Realm. **Required** when first creating a Realm at this `path`.
|
* object types in this Realm. **Required** when first creating a Realm at this `path`.
|
||||||
|
@ -224,6 +292,10 @@ Realm.defaultPath;
|
||||||
* child properties:
|
* child properties:
|
||||||
* - `user` - A `User` object obtained by calling `Realm.Sync.User.login`
|
* - `user` - A `User` object obtained by calling `Realm.Sync.User.login`
|
||||||
* - `url` - A `string` which contains a valid Realm Sync url
|
* - `url` - A `string` which contains a valid Realm Sync url
|
||||||
|
* - `error` - A callback function which is called in error situations
|
||||||
|
* - `validate_ssl` - Indicating if SSL certificates must be validated
|
||||||
|
* - `ssl_trust_certificate_path` - A path where to find trusted SSL certificates
|
||||||
|
* The `error` callback can take up to four optional arguments: `message`, `isFatal`, `category`, and `code`.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
85
docs/sync.js
85
docs/sync.js
|
@ -135,9 +135,10 @@ class User {
|
||||||
* @param {string} server - authentication server
|
* @param {string} server - authentication server
|
||||||
* @param {string} username
|
* @param {string} username
|
||||||
* @param {string} password
|
* @param {string} password
|
||||||
* @param {function(error, user)} callback - called with the following arguments:
|
* @param {function(error, user)} [callback] - called with the following arguments:
|
||||||
* - `error` - an Error object is provided on failure
|
* - `error` - an Error object is provided on failure
|
||||||
* - `user` - a valid User object on success
|
* - `user` - a valid User object on success
|
||||||
|
* @returns {void|Promise<User>} Returns a promise with a user if the callback was not specified
|
||||||
*/
|
*/
|
||||||
static login(server, username, password, callback) {}
|
static login(server, username, password, callback) {}
|
||||||
|
|
||||||
|
@ -148,9 +149,10 @@ class User {
|
||||||
* @param {string} options.provider - The provider type
|
* @param {string} options.provider - The provider type
|
||||||
* @param {string} options.providerToken - The access token for the given provider
|
* @param {string} options.providerToken - The access token for the given provider
|
||||||
* @param {object} [options.userInfo] - A map containing additional data required by the provider
|
* @param {object} [options.userInfo] - A map containing additional data required by the provider
|
||||||
* @param {function(error, User)} callback - called with the following arguments:
|
* @param {function(error, User)} [callback] - an optional callback called with the following arguments:
|
||||||
* - `error` - an Error object is provided on failure
|
* - `error` - an Error object is provided on failure
|
||||||
* - `user` - a valid User object on success
|
* - `user` - a valid User object on success
|
||||||
|
* @return {void|Promise<User>} Returns a promise with a user if the callback was not specified
|
||||||
*/
|
*/
|
||||||
static registerWithProvider(server, options, callback) {}
|
static registerWithProvider(server, options, callback) {}
|
||||||
|
|
||||||
|
@ -159,9 +161,10 @@ class User {
|
||||||
* @param {string} server - authentication server
|
* @param {string} server - authentication server
|
||||||
* @param {string} username
|
* @param {string} username
|
||||||
* @param {string} password
|
* @param {string} password
|
||||||
* @param {function(error, user)} callback - called with the following arguments:
|
* @param {function(error, user)} [callback] - called with the following arguments:
|
||||||
* - `error` - an Error object is provided on failure
|
* - `error` - an Error object is provided on failure
|
||||||
* - `user` - a valid User object on success
|
* - `user` - a valid User object on success
|
||||||
|
* @return {void|Promise<User>} Returns a promise with a user if the callback was not specified
|
||||||
*/
|
*/
|
||||||
static register(server, username, password, callback) {}
|
static register(server, username, password, callback) {}
|
||||||
|
|
||||||
|
@ -242,6 +245,60 @@ class User {
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
retrieveAccount(provider, username) {}
|
retrieveAccount(provider, username) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asynchronously retrieves all permissions associated with the user calling this method.
|
||||||
|
* @param {string} recipient the optional recipient of the permission. Can be either
|
||||||
|
* 'any' which is the default, or 'currentUser' or 'otherUser' if you want only permissions
|
||||||
|
* belonging to the user or *not* belonging to the user.
|
||||||
|
* @returns {Results} a queryable collection of permission objects that provides detailed
|
||||||
|
* information regarding the granted access.
|
||||||
|
* The collection is a live query similar to what you would get by callig Realm.objects,
|
||||||
|
* so the same features apply - you can listen for notifications or filter it.
|
||||||
|
*/
|
||||||
|
getGrantedPermissions(recipient) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the permissions of a Realm.
|
||||||
|
* @param {object} condition - A condition that will be used to match existing users against.
|
||||||
|
* This should be an object, containing either the key 'userId', or 'metadataKey' and 'metadataValue'.
|
||||||
|
* @param {string} realmUrl - The path to the Realm that you want to apply permissions to.
|
||||||
|
* @param {string} accessLevel - The access level you want to set: 'none', 'read', 'write' or 'admin'.
|
||||||
|
* @returns {Promise} a Promise that, upon completion, indicates that the permissions have been
|
||||||
|
* successfully applied by the server. It will be resolved with the
|
||||||
|
* {@link PermissionChange PermissionChange} object that refers to the applied permission.
|
||||||
|
*/
|
||||||
|
applyPermissions(condition, realmUrl, accessLevel) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a token that can be used for sharing a Realm.
|
||||||
|
* @param {string} realmUrl - The Realm URL whose permissions settings should be changed. Use * to change
|
||||||
|
* the permissions of all Realms managed by this user.
|
||||||
|
* @param {string} accessLevel - The access level to grant matching users. Note that the access level
|
||||||
|
* setting is additive, i.e. you cannot revoke permissions for users who previously had a higher access level.
|
||||||
|
* Can be 'read', 'write' or 'admin'.
|
||||||
|
* @param {Date} [expiresAt] - Optional expiration date of the offer. If set to null, the offer doesn't expire.
|
||||||
|
* @returns {string} - A token that can be shared with another user, e.g. via email or message and then consumed by
|
||||||
|
* User.acceptPermissionOffer to obtain permissions to a Realm.
|
||||||
|
*/
|
||||||
|
offerPermissions(realmUrl, accessLevel, expiresAt) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Consumes a token generated by {@link Realm#Sync#User#offerPermissions offerPermissions} to obtain permissions to a shared Realm.
|
||||||
|
* @param {string} token - The token, generated by User.offerPermissions
|
||||||
|
* @returns {string} The url of the Realm that the token has granted permissions to.
|
||||||
|
*/
|
||||||
|
acceptPermissionOffer(token) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidates a permission offer.
|
||||||
|
* Invalidating an offer prevents new users from consuming its token. It doesn't revoke any permissions that have
|
||||||
|
* already been granted.
|
||||||
|
* @param {string|PermissionOffer} permissionOfferOrToken - Either the token or the entire
|
||||||
|
* {@link PermissionOffer PermissionOffer} object that was generated with
|
||||||
|
* {@link Realm#Sync#User#offerPermissions offerPermissions}.
|
||||||
|
*/
|
||||||
|
invalidatePermissionOffer(permissionOfferOrToken) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -280,6 +337,28 @@ class Session {
|
||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
get state() {}
|
get state() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a progress notification callback on a session object
|
||||||
|
* @param {string} direction - The progress direction to register for.
|
||||||
|
* Can be either:
|
||||||
|
* - `download` - report download progress
|
||||||
|
* - `upload` - report upload progress
|
||||||
|
* @param {string} mode - The progress notification mode to use for the registration.
|
||||||
|
* Can be either:
|
||||||
|
* - `reportIndefinitely` - the registration will stay active until the callback is unregistered
|
||||||
|
* - `forCurrentlyOutstandingWork` - the registration will be active until only the currently transferable bytes are synced
|
||||||
|
* @param {callback(transferred, transferable)} callback - called with the following arguments:
|
||||||
|
* - `transferred` - the current number of bytes already transferred
|
||||||
|
* - `transferable` - the total number of transferable bytes (the number of bytes already transferred plus the number of bytes pending transfer)
|
||||||
|
*/
|
||||||
|
addProgressNotification(direction, mode, progressCallback) {}
|
||||||
|
|
||||||
|
/** Unregister a progress notification callback that was previously registered with addProgressNotification.
|
||||||
|
* Calling the function multiple times with the same callback is ignored.
|
||||||
|
* @param {callback(transferred, transferable)} callback - a previously registered progress callback
|
||||||
|
*/
|
||||||
|
removeProgressNotification(progressCallback) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -55,9 +55,11 @@ function setupRealm(realm, realmId) {
|
||||||
'empty',
|
'empty',
|
||||||
'path',
|
'path',
|
||||||
'readOnly',
|
'readOnly',
|
||||||
|
'inMemory',
|
||||||
'schema',
|
'schema',
|
||||||
'schemaVersion',
|
'schemaVersion',
|
||||||
'syncSession',
|
'syncSession',
|
||||||
|
'isInTransaction',
|
||||||
].forEach((name) => {
|
].forEach((name) => {
|
||||||
Object.defineProperty(realm, name, {get: util.getterForProperty(name)});
|
Object.defineProperty(realm, name, {get: util.getterForProperty(name)});
|
||||||
});
|
});
|
||||||
|
@ -81,14 +83,14 @@ export default class Realm {
|
||||||
if (typeof item == 'function') {
|
if (typeof item == 'function') {
|
||||||
let schema = item.schema;
|
let schema = item.schema;
|
||||||
if (!schema || typeof schema != 'object') {
|
if (!schema || typeof schema != 'object') {
|
||||||
throw new Error("Realm object constructor must have 'schema' property");
|
throw new Error("Realm object constructor must have a 'schema' property.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let {name, properties} = schema;
|
let {name, properties} = schema;
|
||||||
if (!name || typeof name != 'string') {
|
if (!name || typeof name != 'string') {
|
||||||
throw new Error("Realm object schema must have 'name' property");
|
throw new Error(`Failed to read ObjectSchema: name must be of type 'string', got (${typeof name})`);
|
||||||
} else if (!properties || typeof properties != 'object') {
|
} else if (!properties || typeof properties != 'object') {
|
||||||
throw new Error("Realm object schema must have 'properties' property");
|
throw new Error(`Failed to read ObjectSchema: properties must be of type 'object', got (${typeof properties})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
schemas.splice(i, 1, schema);
|
schemas.splice(i, 1, schema);
|
||||||
|
@ -130,8 +132,13 @@ util.createMethods(Realm.prototype, objectTypes.REALM, [
|
||||||
// Mutating methods:
|
// Mutating methods:
|
||||||
util.createMethods(Realm.prototype, objectTypes.REALM, [
|
util.createMethods(Realm.prototype, objectTypes.REALM, [
|
||||||
'delete',
|
'delete',
|
||||||
|
'deleteModel',
|
||||||
'deleteAll',
|
'deleteAll',
|
||||||
'write',
|
'write',
|
||||||
|
'compact',
|
||||||
|
'beginTransaction',
|
||||||
|
'commitTransaction',
|
||||||
|
'cancelTransaction',
|
||||||
], true);
|
], true);
|
||||||
|
|
||||||
const Sync = {
|
const Sync = {
|
||||||
|
@ -164,6 +171,11 @@ Object.defineProperties(Realm, {
|
||||||
return rpc.callMethod(undefined, Realm[keys.id], 'schemaVersion', Array.from(arguments));
|
return rpc.callMethod(undefined, Realm[keys.id], 'schemaVersion', Array.from(arguments));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
deleteFile: {
|
||||||
|
value: function(config) {
|
||||||
|
return rpc.callMethod(undefined, Realm[keys.id], 'deleteFile', Array.from(arguments));
|
||||||
|
}
|
||||||
|
},
|
||||||
copyBundledRealmFiles: {
|
copyBundledRealmFiles: {
|
||||||
value: function() {
|
value: function() {
|
||||||
return rpc.callMethod(undefined, Realm[keys.id], 'copyBundledRealmFiles', []);
|
return rpc.callMethod(undefined, Realm[keys.id], 'copyBundledRealmFiles', []);
|
||||||
|
@ -177,7 +189,8 @@ Object.defineProperties(Realm, {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
_waitForDownload: {
|
_waitForDownload: {
|
||||||
value: function(_config, callback) {
|
value: function(_config, sessionCallback, callback) {
|
||||||
|
sessionCallback();
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -85,5 +85,5 @@ export function typeForConstructor(realmId, constructor) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
throw new Error("Constructor was not registered in the schema for this Realm")
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,9 @@ import { keys, objectTypes } from './constants';
|
||||||
import { getterForProperty, createMethods } from './util';
|
import { getterForProperty, createMethods } from './util';
|
||||||
import { deserialize } from './rpc';
|
import { deserialize } from './rpc';
|
||||||
|
|
||||||
export default class Session { }
|
export default class Session {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Object.defineProperties(Session.prototype, {
|
Object.defineProperties(Session.prototype, {
|
||||||
url: { get: getterForProperty('url') },
|
url: { get: getterForProperty('url') },
|
||||||
|
@ -31,7 +33,9 @@ Object.defineProperties(Session.prototype, {
|
||||||
|
|
||||||
createMethods(Session.prototype, objectTypes.SESSION, [
|
createMethods(Session.prototype, objectTypes.SESSION, [
|
||||||
'_refreshAccessToken',
|
'_refreshAccessToken',
|
||||||
'_simulateError'
|
'_simulateError',
|
||||||
|
'addProgressNotification',
|
||||||
|
'removeProgressNotification'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export function createSession(realmId, info) {
|
export function createSession(realmId, info) {
|
||||||
|
|
|
@ -40,7 +40,7 @@ export function createMethod(type, name, mutates) {
|
||||||
let id = this[keys.id];
|
let id = this[keys.id];
|
||||||
|
|
||||||
if (!realmId || !id) {
|
if (!realmId || !id) {
|
||||||
throw new TypeError(name + ' method was not called a Realm object!');
|
throw new TypeError(name + ' method was not called on a Realm object!');
|
||||||
}
|
}
|
||||||
if (this[keys.type] !== type) {
|
if (this[keys.type] !== type) {
|
||||||
throw new TypeError(name + ' method was called on an object of the wrong type!');
|
throw new TypeError(name + ' method was called on an object of the wrong type!');
|
||||||
|
|
|
@ -19,7 +19,12 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function AuthError(problem) {
|
function AuthError(problem) {
|
||||||
Error.call(this, problem.title);
|
const error = Error.call(this, problem.title);
|
||||||
|
|
||||||
|
this.name = 'AuthError';
|
||||||
|
this.message = error.message;
|
||||||
|
this.stack = error.stack;
|
||||||
|
|
||||||
Object.assign(this, problem);
|
Object.assign(this, problem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,10 +43,15 @@ module.exports = function(realmConstructor) {
|
||||||
//Add async open API
|
//Add async open API
|
||||||
Object.defineProperties(realmConstructor, getOwnPropertyDescriptors({
|
Object.defineProperties(realmConstructor, getOwnPropertyDescriptors({
|
||||||
open(config) {
|
open(config) {
|
||||||
return new Promise((resolve, reject) => {
|
let syncSession;
|
||||||
realmConstructor._waitForDownload(config, (error) => {
|
let promise = new Promise((resolve, reject) => {
|
||||||
|
realmConstructor._waitForDownload(config,
|
||||||
|
(session) => {
|
||||||
|
syncSession = session;
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(error);
|
setTimeout(() => { reject(error); }, 1);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
|
@ -59,12 +64,31 @@ module.exports = function(realmConstructor) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
promise.progress = (callback) => {
|
||||||
|
if (syncSession) {
|
||||||
|
syncSession.addProgressNotification('download', 'forCurrentlyOutstandingWork', callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
};
|
||||||
|
|
||||||
|
return promise;
|
||||||
},
|
},
|
||||||
|
|
||||||
openAsync(config, callback) {
|
openAsync(config, callback, progressCallback) {
|
||||||
realmConstructor._waitForDownload(config, (error) => {
|
const message = "Realm.openAsync is now deprecated in favor of Realm.open. This function will be removed in future versions.";
|
||||||
|
(console.warn || console.log).call(console, message);
|
||||||
|
|
||||||
|
realmConstructor._waitForDownload(config,
|
||||||
|
(syncSession) => {
|
||||||
|
if (progressCallback) {
|
||||||
|
syncSession.addProgressNotification('download', 'forCurrentlyOutstandingWork', progressCallback);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
callback(error);
|
setTimeout(() => { callback(error); }, 1);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
|
@ -72,7 +96,7 @@ module.exports = function(realmConstructor) {
|
||||||
//FIXME: RN hangs here. Remove when node's makeCallback alternative is implemented
|
//FIXME: RN hangs here. Remove when node's makeCallback alternative is implemented
|
||||||
setTimeout(() => { callback(null, syncedRealm); }, 1);
|
setTimeout(() => { callback(null, syncedRealm); }, 1);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
callback(e);
|
setTimeout(() => { callback(e); }, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -107,7 +131,7 @@ module.exports = function(realmConstructor) {
|
||||||
|
|
||||||
if (realmConstructor.Sync._setFeatureToken) {
|
if (realmConstructor.Sync._setFeatureToken) {
|
||||||
realmConstructor.Sync.setFeatureToken = function(featureToken) {
|
realmConstructor.Sync.setFeatureToken = function(featureToken) {
|
||||||
if (typeof featureToken !== 'string' || !featureToken instanceof String) {
|
if (typeof featureToken !== 'string' && !(featureToken instanceof String)) {
|
||||||
throw new Error("featureToken should be a string");
|
throw new Error("featureToken should be a string");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,8 +77,10 @@ declare namespace Realm {
|
||||||
interface Configuration {
|
interface Configuration {
|
||||||
encryptionKey?: ArrayBuffer | ArrayBufferView | Int8Array;
|
encryptionKey?: ArrayBuffer | ArrayBufferView | Int8Array;
|
||||||
migration?: (oldRealm: Realm, newRealm: Realm) => void;
|
migration?: (oldRealm: Realm, newRealm: Realm) => void;
|
||||||
|
shouldCompactOnLaunch?: (totalBytes: number, usedBytes: number) => boolean;
|
||||||
path?: string;
|
path?: string;
|
||||||
readOnly?: boolean;
|
readOnly?: boolean;
|
||||||
|
inMemory?: boolean;
|
||||||
schema?: ObjectClass[] | ObjectSchema[];
|
schema?: ObjectClass[] | ObjectSchema[];
|
||||||
schemaVersion?: number;
|
schemaVersion?: number;
|
||||||
sync?: Realm.Sync.SyncConfiguration;
|
sync?: Realm.Sync.SyncConfiguration;
|
||||||
|
@ -263,22 +265,96 @@ declare namespace Realm.Sync {
|
||||||
readonly server: string;
|
readonly server: string;
|
||||||
readonly token: string;
|
readonly token: string;
|
||||||
static adminUser(adminToken: string, server?: string): User;
|
static adminUser(adminToken: string, server?: string): User;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated, to be removed in future versions
|
||||||
|
*/
|
||||||
static login(server: string, username: string, password: string, callback: (error: any, user: User) => void): void;
|
static login(server: string, username: string, password: string, callback: (error: any, user: User) => void): void;
|
||||||
static loginWithProvider(server: string, provider: string, providerToken: string, callback: (error: any, user: User) => void): void;
|
static login(server: string, username: string, password: string): Promise<Realm.Sync.User>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated, to be removed in future versions
|
||||||
|
*/
|
||||||
static register(server: string, username: string, password: string, callback: (error: any, user: User) => void): void;
|
static register(server: string, username: string, password: string, callback: (error: any, user: User) => void): void;
|
||||||
|
static register(server: string, username: string, password: string): Promise<Realm.Sync.User>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated, to be removed in versions
|
||||||
|
*/
|
||||||
static registerWithProvider(server: string, options: { provider: string, providerToken: string, userInfo: any }, callback: (error: Error | null, user: User | null) => void): void;
|
static registerWithProvider(server: string, options: { provider: string, providerToken: string, userInfo: any }, callback: (error: Error | null, user: User | null) => void): void;
|
||||||
|
static registerWithProvider(server: string, options: { provider: string, providerToken: string, userInfo: any }): Promise<Realm.Sync.User>;
|
||||||
|
|
||||||
logout(): void;
|
logout(): void;
|
||||||
openManagementRealm(): Realm;
|
openManagementRealm(): Realm;
|
||||||
retrieveAccount(provider: string, username: string): Promise<Account>;
|
retrieveAccount(provider: string, username: string): Promise<Account>;
|
||||||
|
|
||||||
|
getGrantedPermissions(recipient: 'any' | 'currentUser' | 'otherUser'): Results<Permission>;
|
||||||
|
applyPermissions(condition: PermissionCondition, realmUrl: string, accessLevel: AccessLevel): Promise<PermissionChange>;
|
||||||
|
offerPermissions(realmUrl: string, accessLevel: AccessLevel, expiresAt?: Date): Promise<string>;
|
||||||
|
acceptPermissionOffer(token: string): Promise<string>
|
||||||
|
invalidatePermissionOffer(permissionOfferOrToken: PermissionOffer | string): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PermissionCondition = {
|
||||||
|
userId: string |
|
||||||
|
{ metadataKey: string, metadataValue: string }
|
||||||
|
};
|
||||||
|
|
||||||
|
type AccessLevel = 'none' | 'read' | 'write' | 'admin';
|
||||||
|
|
||||||
|
class Permission {
|
||||||
|
readonly id: string;
|
||||||
|
readonly updatedAt: Date;
|
||||||
|
readonly userId: string;
|
||||||
|
readonly path: string;
|
||||||
|
readonly mayRead?: boolean;
|
||||||
|
readonly mayWrite?: boolean;
|
||||||
|
readonly mayManage?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
class PermissionChange {
|
||||||
|
id: string;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
statusCode?: number;
|
||||||
|
statusMessage?: string;
|
||||||
|
userId: string;
|
||||||
|
metadataKey?: string;
|
||||||
|
metadataValue?: string;
|
||||||
|
realmUrl: string;
|
||||||
|
mayRead?: boolean;
|
||||||
|
mayWrite?: boolean;
|
||||||
|
mayManage?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
class PermissionOffer {
|
||||||
|
id: string;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
statusCode?: number;
|
||||||
|
statusMessage?: string;
|
||||||
|
token?: string;
|
||||||
|
realmUrl: string;
|
||||||
|
mayRead?: boolean;
|
||||||
|
mayWrite?: boolean;
|
||||||
|
mayManage?: boolean;
|
||||||
|
expiresAt?: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ErrorCallback = (message?: string, isFatal?: boolean, category?: string, code?: number) => void;
|
||||||
|
|
||||||
interface SyncConfiguration {
|
interface SyncConfiguration {
|
||||||
user: User;
|
user: User;
|
||||||
url: string;
|
url: string;
|
||||||
validate_ssl?: boolean;
|
validate_ssl?: boolean;
|
||||||
ssl_trust_certificate_path?: string;
|
ssl_trust_certificate_path?: string;
|
||||||
|
error?: ErrorCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ProgressNotificationCallback = (transferred: number, transferable: number) => void;
|
||||||
|
type ProgressDirection = 'download' | 'upload';
|
||||||
|
type ProgressMode = 'reportIndefinitely' | 'forCurrentlyOutstandingWork';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Session
|
* Session
|
||||||
* @see { @link https://realm.io/docs/javascript/latest/api/Realm.Sync.Session.html }
|
* @see { @link https://realm.io/docs/javascript/latest/api/Realm.Sync.Session.html }
|
||||||
|
@ -288,6 +364,9 @@ declare namespace Realm.Sync {
|
||||||
readonly state: 'invalid' | 'active' | 'inactive';
|
readonly state: 'invalid' | 'active' | 'inactive';
|
||||||
readonly url: string;
|
readonly url: string;
|
||||||
readonly user: User;
|
readonly user: User;
|
||||||
|
|
||||||
|
addProgressNotification(direction: ProgressDirection, mode: ProgressMode, progressCallback: ProgressNotificationCallback): void;
|
||||||
|
removeProgressNotification(progressCallback: ProgressNotificationCallback): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -313,11 +392,16 @@ declare namespace Realm.Sync {
|
||||||
function addListener(serverURL: string, adminUser: Realm.Sync.User, regex: string, name: string, changeCallback: (changeEvent: ChangeEvent) => void): void;
|
function addListener(serverURL: string, adminUser: Realm.Sync.User, regex: string, name: string, changeCallback: (changeEvent: ChangeEvent) => void): void;
|
||||||
function removeAllListeners(name?: string): void;
|
function removeAllListeners(name?: string): void;
|
||||||
function removeListener(regex: string, name: string, changeCallback: (changeEvent: ChangeEvent) => void): void;
|
function removeListener(regex: string, name: string, changeCallback: (changeEvent: ChangeEvent) => void): void;
|
||||||
function setLogLevel(logLevel: 'error' | 'info' | 'debug'): void;
|
function setLogLevel(logLevel: 'all' | 'trace' | 'debug' | 'detail' | 'info' | 'warn' | 'error' | 'fatal' | 'off'): void;
|
||||||
|
function setFeatureToken(token: string): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated, to be removed in 2.0
|
||||||
|
*/
|
||||||
function setAccessToken(accessToken: string): void;
|
function setAccessToken(accessToken: string): void;
|
||||||
|
|
||||||
type Instruction = {
|
type Instruction = {
|
||||||
type: 'INSERT' | 'SET' | 'DELETE' | 'CLEAR' | 'LIST_SET' | 'LIST_INSERT' | 'LIST_ERASE' | 'LIST_CLEAR' | 'ADD_TYPE' | 'ADD_PROPERTIES'
|
type: 'INSERT' | 'SET' | 'DELETE' | 'CLEAR' | 'LIST_SET' | 'LIST_INSERT' | 'LIST_ERASE' | 'LIST_CLEAR' | 'ADD_TYPE' | 'ADD_PROPERTIES' | 'CHANGE_IDENTITY' | 'SWAP_IDENTITY'
|
||||||
object_type: string,
|
object_type: string,
|
||||||
identity: string,
|
identity: string,
|
||||||
values: any | undefined
|
values: any | undefined
|
||||||
|
@ -349,6 +433,11 @@ declare namespace Realm.Sync {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interface ProgressPromise extends Promise<Realm> {
|
||||||
|
progress(callback: Realm.Sync.ProgressNotificationCallback) : Promise<Realm>
|
||||||
|
}
|
||||||
|
|
||||||
declare class Realm {
|
declare class Realm {
|
||||||
static defaultPath: string;
|
static defaultPath: string;
|
||||||
|
|
||||||
|
@ -357,6 +446,7 @@ declare class Realm {
|
||||||
readonly readOnly: boolean;
|
readonly readOnly: boolean;
|
||||||
readonly schema: Realm.ObjectSchema[];
|
readonly schema: Realm.ObjectSchema[];
|
||||||
readonly schemaVersion: number;
|
readonly schemaVersion: number;
|
||||||
|
readonly isInTransaction: boolean;
|
||||||
|
|
||||||
readonly syncSession: Realm.Sync.Session | null;
|
readonly syncSession: Realm.Sync.Session | null;
|
||||||
|
|
||||||
|
@ -368,17 +458,27 @@ declare class Realm {
|
||||||
*/
|
*/
|
||||||
static schemaVersion(path: string, encryptionKey?: ArrayBuffer | ArrayBufferView): number;
|
static schemaVersion(path: string, encryptionKey?: ArrayBuffer | ArrayBufferView): number;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open a realm asynchronously with a promise. If the realm is synced, it will be fully synchronized before it is available.
|
* Open a realm asynchronously with a promise. If the realm is synced, it will be fully synchronized before it is available.
|
||||||
* @param {Configuration} config
|
* @param {Configuration} config
|
||||||
*/
|
*/
|
||||||
static open(config: Realm.Configuration): Promise<Realm>
|
static open(config: Realm.Configuration): ProgressPromise;
|
||||||
/**
|
/**
|
||||||
|
* @deprecated in favor of `Realm.open`
|
||||||
* Open a realm asynchronously with a callback. If the realm is synced, it will be fully synchronized before it is available.
|
* Open a realm asynchronously with a callback. If the realm is synced, it will be fully synchronized before it is available.
|
||||||
* @param {Configuration} config
|
* @param {Configuration} config
|
||||||
* @param {Function} callback will be called when the realm is ready.
|
* @param {Function} callback will be called when the realm is ready.
|
||||||
|
* @param {ProgressNotificationCallback} progressCallback? a progress notification callback for 'download' direction and 'forCurrentlyOutstandingWork' mode
|
||||||
*/
|
*/
|
||||||
static openAsync(config: Realm.Configuration, callback: (error: any, realm: Realm) => void): void
|
static openAsync(config: Realm.Configuration, callback: (error: any, realm: Realm) => void, progressCallback?: Realm.Sync.ProgressNotificationCallback): void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the Realm file for the given configuration.
|
||||||
|
* @param {Configuration} config
|
||||||
|
*/
|
||||||
|
static deleteFile(config: Realm.Configuration): void
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Realm.Configuration} config?
|
* @param {Realm.Configuration} config?
|
||||||
|
@ -409,6 +509,11 @@ declare class Realm {
|
||||||
*/
|
*/
|
||||||
delete(object: Realm.Object | Realm.Object[] | Realm.List<any> | Realm.Results<any> | any): void;
|
delete(object: Realm.Object | Realm.Object[] | Realm.List<any> | Realm.Results<any> | any): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns void
|
||||||
|
*/
|
||||||
|
deleteModel(name: string): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns void
|
* @returns void
|
||||||
*/
|
*/
|
||||||
|
@ -452,6 +557,26 @@ declare class Realm {
|
||||||
* @returns void
|
* @returns void
|
||||||
*/
|
*/
|
||||||
write(callback: () => void): void;
|
write(callback: () => void): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns void
|
||||||
|
*/
|
||||||
|
beginTransaction(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns void
|
||||||
|
*/
|
||||||
|
commitTransaction(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns void
|
||||||
|
*/
|
||||||
|
cancelTransaction(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns boolean
|
||||||
|
*/
|
||||||
|
compact(): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module 'realm' {
|
declare module 'realm' {
|
||||||
|
|
|
@ -28,6 +28,8 @@ module.exports = [
|
||||||
statusCode: { type: 'int', optional: true },
|
statusCode: { type: 'int', optional: true },
|
||||||
statusMessage: { type: 'string', optional: true },
|
statusMessage: { type: 'string', optional: true },
|
||||||
userId: { type: 'string' },
|
userId: { type: 'string' },
|
||||||
|
metadataKey: { type: 'string', optional: true },
|
||||||
|
metadataValue: { type: 'string', optional: true },
|
||||||
realmUrl: { type: 'string' },
|
realmUrl: { type: 'string' },
|
||||||
mayRead: { type: 'bool', optional: true },
|
mayRead: { type: 'bool', optional: true },
|
||||||
mayWrite: { type: 'bool', optional: true },
|
mayWrite: { type: 'bool', optional: true },
|
||||||
|
|
|
@ -0,0 +1,274 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Copyright 2017 Realm Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const url_parse = require('url-parse');
|
||||||
|
const managementSchema = require('./management-schema');
|
||||||
|
|
||||||
|
function generateUniqueId() {
|
||||||
|
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||||
|
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
||||||
|
return v.toString(16);
|
||||||
|
});
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
const permissionSchema = [
|
||||||
|
{
|
||||||
|
name: 'Permission',
|
||||||
|
properties: {
|
||||||
|
id: { type: 'string' },
|
||||||
|
updatedAt: { type: 'date' },
|
||||||
|
userId: { type: 'string' },
|
||||||
|
path: { type: 'string' },
|
||||||
|
mayRead: { type: 'bool' },
|
||||||
|
mayWrite: { type: 'bool' },
|
||||||
|
mayManage: { type: 'bool' },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// Symbols are not supported on RN yet, so we use this for now:
|
||||||
|
const specialPurposeRealmsKey = '_specialPurposeRealms';
|
||||||
|
|
||||||
|
function getSpecialPurposeRealm(user, realmName, schema) {
|
||||||
|
if (!user.hasOwnProperty(specialPurposeRealmsKey)) {
|
||||||
|
user[specialPurposeRealmsKey] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user[specialPurposeRealmsKey].hasOwnProperty(realmName)) {
|
||||||
|
return Promise.resolve(user[specialPurposeRealmsKey][realmName]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = url_parse(user.server);
|
||||||
|
if (url.protocol === 'http:') {
|
||||||
|
url.set('protocol', 'realm:');
|
||||||
|
} else if (url.protocol === 'https:') {
|
||||||
|
url.set('protocol', 'realms:');
|
||||||
|
} else {
|
||||||
|
throw new Error(`Unexpected user auth url: ${user.server}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
url.set('pathname', `/~/${realmName}`);
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
schema: schema,
|
||||||
|
sync: {
|
||||||
|
user,
|
||||||
|
url: url.href
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const _Realm = user.constructor._realmConstructor;
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
_Realm._waitForDownload(config, (error) => {
|
||||||
|
|
||||||
|
// FIXME: I don't understand why, but removing the following setTimeout causes the subsequent
|
||||||
|
// setTimeout call (when resolving the promise) to hang on RN iOS.
|
||||||
|
// This might be related to our general makeCallback issue: #1255.
|
||||||
|
setTimeout(() => {}, 1);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
setTimeout(() => reject(error), 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
try {
|
||||||
|
let syncedRealm = new _Realm(config);
|
||||||
|
user[specialPurposeRealmsKey][realmName] = syncedRealm;
|
||||||
|
//FIXME: RN hangs here. Remove when node's makeCallback alternative is implemented (#1255)
|
||||||
|
setTimeout(() => resolve(syncedRealm), 1);
|
||||||
|
} catch (e) {
|
||||||
|
setTimeout(() => reject(e), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createInManagementRealm(user, modelName, modelInitializer) {
|
||||||
|
return getSpecialPurposeRealm(user, '__management', managementSchema)
|
||||||
|
.then(managementRealm => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
let o;
|
||||||
|
|
||||||
|
const listener = () => {
|
||||||
|
if (!o) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusCode = o.statusCode;
|
||||||
|
if (typeof statusCode === 'number') {
|
||||||
|
managementRealm.removeListener('change', listener);
|
||||||
|
|
||||||
|
if (statusCode === 0) {
|
||||||
|
setTimeout(() => resolve(o), 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const e = new Error(o.statusMessage);
|
||||||
|
e.statusCode = statusCode;
|
||||||
|
e.managementObject = o;
|
||||||
|
setTimeout(() => reject(e), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
managementRealm.addListener('change', listener);
|
||||||
|
|
||||||
|
managementRealm.write(() => {
|
||||||
|
o = managementRealm.create(modelName, modelInitializer);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
reject(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const accessLevels = ['none', 'read', 'write', 'admin'];
|
||||||
|
const offerAccessLevels = ['read', 'write', 'admin'];
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getGrantedPermissions(recipient) {
|
||||||
|
if (recipient && ['currentUser', 'otherUser', 'any'].indexOf(recipient) === -1) {
|
||||||
|
return Promise.reject(new Error(`'${recipient}' is not a valid recipient type. Must be 'any', 'currentUser' or 'otherUser'.`));
|
||||||
|
}
|
||||||
|
|
||||||
|
return getSpecialPurposeRealm(this, '__permission', permissionSchema)
|
||||||
|
.then(permissionRealm => {
|
||||||
|
let permissions = permissionRealm.objects('Permission')
|
||||||
|
.filtered('NOT path ENDSWITH "__permission" AND NOT path ENDSWITH "__management"');
|
||||||
|
|
||||||
|
if (recipient === 'currentUser') {
|
||||||
|
permissions = permissions.filtered('userId = $0', this.identity);
|
||||||
|
}
|
||||||
|
else if (recipient === 'otherUser') {
|
||||||
|
permissions = permissions.filtered('userId != $0', this.identity);
|
||||||
|
}
|
||||||
|
return permissions;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
applyPermissions(condition, realmUrl, accessLevel) {
|
||||||
|
if (!realmUrl) {
|
||||||
|
return Promise.reject(new Error('realmUrl must be specified'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accessLevels.indexOf(accessLevel) === -1) {
|
||||||
|
return Promise.reject(new Error(`'${accessLevel}' is not a valid access level. Must be ${accessLevels.join(', ')}.`));
|
||||||
|
}
|
||||||
|
|
||||||
|
const mayRead = accessLevel === 'read' || accessLevel === 'write' || accessLevel === 'admin';
|
||||||
|
const mayWrite = accessLevel === 'write' || accessLevel === 'admin';
|
||||||
|
const mayManage = accessLevel === 'admin';
|
||||||
|
|
||||||
|
const permissionChange = {
|
||||||
|
id: generateUniqueId(),
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date(),
|
||||||
|
realmUrl,
|
||||||
|
mayRead,
|
||||||
|
mayWrite,
|
||||||
|
mayManage
|
||||||
|
};
|
||||||
|
|
||||||
|
if (condition.hasOwnProperty('userId')) {
|
||||||
|
permissionChange.userId = condition.userId;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
permissionChange.userId = '';
|
||||||
|
permissionChange.metadataKey = condition.metadataKey;
|
||||||
|
permissionChange.metadataValue = condition.metadataValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return createInManagementRealm(this, 'PermissionChange', permissionChange);
|
||||||
|
},
|
||||||
|
|
||||||
|
offerPermissions(realmUrl, accessLevel, expiresAt) {
|
||||||
|
if (!realmUrl) {
|
||||||
|
return Promise.reject(new Error('realmUrl must be specified'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offerAccessLevels.indexOf(accessLevel) === -1) {
|
||||||
|
return Promise.reject(new Error(`'${accessLevel}' is not a valid access level. Must be ${offerAccessLevels.join(', ')}.`));
|
||||||
|
}
|
||||||
|
|
||||||
|
const mayRead = true;
|
||||||
|
const mayWrite = accessLevel === 'write' || accessLevel === 'admin';
|
||||||
|
const mayManage = accessLevel === 'admin';
|
||||||
|
|
||||||
|
const permissionOffer = {
|
||||||
|
id: generateUniqueId(),
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date(),
|
||||||
|
expiresAt,
|
||||||
|
realmUrl,
|
||||||
|
mayRead,
|
||||||
|
mayWrite,
|
||||||
|
mayManage
|
||||||
|
};
|
||||||
|
|
||||||
|
return createInManagementRealm(this, 'PermissionOffer', permissionOffer)
|
||||||
|
.then(appliedOffer => appliedOffer.token);
|
||||||
|
},
|
||||||
|
|
||||||
|
acceptPermissionOffer(token) {
|
||||||
|
if (!token) {
|
||||||
|
return Promise.reject(new Error('Offer token must be specified'));
|
||||||
|
}
|
||||||
|
|
||||||
|
const permissionOfferResponse = {
|
||||||
|
id: generateUniqueId(),
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date(),
|
||||||
|
token
|
||||||
|
};
|
||||||
|
|
||||||
|
return createInManagementRealm(this, 'PermissionOfferResponse', permissionOfferResponse)
|
||||||
|
.then(appliedReponse => appliedReponse.realmUrl);
|
||||||
|
},
|
||||||
|
|
||||||
|
invalidatePermissionOffer(permissionOfferOrToken) {
|
||||||
|
return getSpecialPurposeRealm(this, '__management', managementSchema)
|
||||||
|
.then(managementRealm => {
|
||||||
|
let permissionOffer;
|
||||||
|
|
||||||
|
if (typeof permissionOfferOrToken === 'string') {
|
||||||
|
// We were given a token, not an object. Find the matching object.
|
||||||
|
const q = managementRealm.objects('PermissionOffer')
|
||||||
|
.filtered('token = $0', permissionOfferOrToken);
|
||||||
|
|
||||||
|
if (q.length === 0) {
|
||||||
|
throw new Error("No permission offers with the given token were found");
|
||||||
|
}
|
||||||
|
|
||||||
|
permissionOffer = q[0];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
permissionOffer = permissionOfferOrToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
managementRealm.write(() => {
|
||||||
|
permissionOffer.expiresAt = new Date();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const AuthError = require('./errors').AuthError;
|
const AuthError = require('./errors').AuthError;
|
||||||
|
const permissionApis = require('./permission-api');
|
||||||
|
|
||||||
function node_require(module) {
|
function node_require(module) {
|
||||||
return require(module);
|
return require(module);
|
||||||
|
@ -27,7 +28,7 @@ function node_require(module) {
|
||||||
function checkTypes(args, types) {
|
function checkTypes(args, types) {
|
||||||
args = Array.prototype.slice.call(args);
|
args = Array.prototype.slice.call(args);
|
||||||
for (var i = 0; i < types.length; ++i) {
|
for (var i = 0; i < types.length; ++i) {
|
||||||
if (typeof args[i] !== types[i]) {
|
if (args.length > i && typeof args[i] !== types[i]) {
|
||||||
throw new TypeError('param ' + i + ' must be of type ' + types[i]);
|
throw new TypeError('param ' + i + ' must be of type ' + types[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,6 +114,15 @@ function refreshAccessToken(user, localRealmPath, realmUrl) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base authentication method. It fires a JSON POST to the server parameter plus the auth url
|
||||||
|
* For example, if the server parameter is `http://myapp.com`, this url will post to `http://myapp.com/auth`
|
||||||
|
* @param {object} userConstructor
|
||||||
|
* @param {string} server the http or https server url
|
||||||
|
* @param {object} json the json to post to the auth endpoint
|
||||||
|
* @param {Function} callback an optional callback with an error and user parameter
|
||||||
|
* @returns {Promise} only returns a promise if the callback parameter was omitted
|
||||||
|
*/
|
||||||
function _authenticate(userConstructor, server, json, callback) {
|
function _authenticate(userConstructor, server, json, callback) {
|
||||||
json.app_id = '';
|
json.app_id = '';
|
||||||
const url = auth_url(server);
|
const url = auth_url(server);
|
||||||
|
@ -122,25 +132,35 @@ function _authenticate(userConstructor, server, json, callback) {
|
||||||
headers: postHeaders,
|
headers: postHeaders,
|
||||||
open_timeout: 5000
|
open_timeout: 5000
|
||||||
};
|
};
|
||||||
performFetch(url, options)
|
|
||||||
|
const promise = performFetch(url, options)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
return response.json().then((body) => callback(new AuthError(body)));
|
return response.json().then((body) => Promise.reject(new AuthError(body)));
|
||||||
} else {
|
} else {
|
||||||
return response.json().then(function (body) {
|
return response.json().then(function (body) {
|
||||||
// TODO: validate JSON
|
// TODO: validate JSON
|
||||||
const token = body.refresh_token.token;
|
const token = body.refresh_token.token;
|
||||||
const identity = body.refresh_token.token_data.identity;
|
const identity = body.refresh_token.token_data.identity;
|
||||||
const isAdmin = body.refresh_token.token_data.is_admin;
|
const isAdmin = body.refresh_token.token_data.is_admin;
|
||||||
callback(undefined, userConstructor.createUser(server, identity, token, false, isAdmin));
|
return userConstructor.createUser(server, identity, token, false, isAdmin);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
promise.then(user => {
|
||||||
|
callback(null, user);
|
||||||
})
|
})
|
||||||
.catch(callback);
|
.catch(err => {
|
||||||
|
callback(err);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
const staticMethods = {
|
||||||
static: {
|
|
||||||
get current() {
|
get current() {
|
||||||
const allUsers = this.all;
|
const allUsers = this.all;
|
||||||
const keys = Object.keys(allUsers);
|
const keys = Object.keys(allUsers);
|
||||||
|
@ -154,7 +174,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
|
|
||||||
adminUser(token, server) {
|
adminUser(token, server) {
|
||||||
checkTypes(arguments, ['string']);
|
checkTypes(arguments, ['string', 'string']);
|
||||||
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||||
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
||||||
return v.toString(16);
|
return v.toString(16);
|
||||||
|
@ -164,20 +184,34 @@ module.exports = {
|
||||||
|
|
||||||
register(server, username, password, callback) {
|
register(server, username, password, callback) {
|
||||||
checkTypes(arguments, ['string', 'string', 'string', 'function']);
|
checkTypes(arguments, ['string', 'string', 'string', 'function']);
|
||||||
_authenticate(this, server, {
|
const json = {
|
||||||
provider: 'password',
|
provider: 'password',
|
||||||
user_info: { password: password, register: true },
|
user_info: { password: password, register: true },
|
||||||
data: username
|
data: username
|
||||||
}, callback);
|
};
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
const message = "register(..., callback) is now deprecated in favor of register(): Promise<User>. This function argument will be removed in future versions.";
|
||||||
|
(console.warn || console.log).call(console, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _authenticate(this, server, json, callback);
|
||||||
},
|
},
|
||||||
|
|
||||||
login(server, username, password, callback) {
|
login(server, username, password, callback) {
|
||||||
checkTypes(arguments, ['string', 'string', 'string', 'function']);
|
checkTypes(arguments, ['string', 'string', 'string', 'function']);
|
||||||
_authenticate(this, server, {
|
const json = {
|
||||||
provider: 'password',
|
provider: 'password',
|
||||||
user_info: { password: password },
|
user_info: { password: password },
|
||||||
data: username
|
data: username
|
||||||
}, callback);
|
};
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
const message = "login(..., callback) is now deprecated in favor of login(): Promise<User>. This function argument will be removed in future versions.";
|
||||||
|
(console.warn || console.log).call(console, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _authenticate(this, server, json, callback);
|
||||||
},
|
},
|
||||||
|
|
||||||
registerWithProvider(server, options, callback) {
|
registerWithProvider(server, options, callback) {
|
||||||
|
@ -195,21 +229,27 @@ module.exports = {
|
||||||
checkTypes(arguments, ['string', 'object', 'function']);
|
checkTypes(arguments, ['string', 'object', 'function']);
|
||||||
}
|
}
|
||||||
|
|
||||||
let reqOptions = {
|
let json = {
|
||||||
provider: options.provider,
|
provider: options.provider,
|
||||||
data: options.providerToken,
|
data: options.providerToken,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (options.userInfo) {
|
if (options.userInfo) {
|
||||||
reqOptions.user_info = options.userInfo;
|
json.user_info = options.userInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
_authenticate(this, server, reqOptions, callback);
|
if (callback) {
|
||||||
|
const message = "registerWithProvider(..., callback) is now deprecated in favor of registerWithProvider(): Promise<User>. This function argument will be removed in future versions.";
|
||||||
|
(console.warn || console.log).call(console, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _authenticate(this, server, json, callback);
|
||||||
},
|
},
|
||||||
|
|
||||||
_refreshAccessToken: refreshAccessToken
|
_refreshAccessToken: refreshAccessToken
|
||||||
},
|
};
|
||||||
instance: {
|
|
||||||
|
const instanceMethods = {
|
||||||
openManagementRealm() {
|
openManagementRealm() {
|
||||||
let url = url_parse(this.server);
|
let url = url_parse(this.server);
|
||||||
if (url.protocol === 'http:') {
|
if (url.protocol === 'http:') {
|
||||||
|
@ -232,13 +272,11 @@ module.exports = {
|
||||||
},
|
},
|
||||||
retrieveAccount(provider, provider_id) {
|
retrieveAccount(provider, provider_id) {
|
||||||
checkTypes(arguments, ['string', 'string']);
|
checkTypes(arguments, ['string', 'string']);
|
||||||
|
|
||||||
const url = url_parse(this.server);
|
const url = url_parse(this.server);
|
||||||
url.set('pathname', `/api/providers/${provider}/accounts/${provider_id}`);
|
url.set('pathname', `/api/providers/${provider}/accounts/${provider_id}`);
|
||||||
const headers = {
|
const headers = {
|
||||||
Authorization: this.token
|
Authorization: this.token
|
||||||
};
|
};
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers,
|
headers,
|
||||||
|
@ -257,5 +295,12 @@ module.exports = {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
};
|
||||||
|
|
||||||
|
// Append the permission apis
|
||||||
|
Object.assign(instanceMethods, permissionApis);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
static: staticMethods,
|
||||||
|
instance: instanceMethods
|
||||||
};
|
};
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "realm",
|
"name": "realm",
|
||||||
"version": "1.10.2",
|
"version": "1.11.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
"acorn": {
|
"acorn": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.1.1.tgz",
|
||||||
"integrity": "sha512-vOk6uEMctu0vQrvuSqFdJyqj1Q0S5VTDL79qtjo+DhRr+1mmaD+tluFSCZqhvi/JUhXSzoZN2BhtstaPEeE8cw==",
|
"integrity": "sha1-U/4WERH5EquZnuiHqQoLxSgi/XU=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"acorn-jsx": {
|
"acorn-jsx": {
|
||||||
|
@ -67,7 +67,7 @@
|
||||||
"aproba": {
|
"aproba": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.2.tgz",
|
||||||
"integrity": "sha512-ZpYajIfO0j2cOFTO955KUMIKNmj6zhX8kVztMAxFsDaMwz+9Z9SV0uou2pC9HJqcfpffOsjnbrDMvkNy+9RXPw=="
|
"integrity": "sha1-RcZikJTeTpb2k+9+q3SuB5wkD8E="
|
||||||
},
|
},
|
||||||
"are-we-there-yet": {
|
"are-we-there-yet": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.4",
|
||||||
|
@ -322,7 +322,7 @@
|
||||||
"circular-json": {
|
"circular-json": {
|
||||||
"version": "0.3.3",
|
"version": "0.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz",
|
||||||
"integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==",
|
"integrity": "sha1-gVyZ6oT2gJUp0vRXkb34JxE1LWY=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"cli-cursor": {
|
"cli-cursor": {
|
||||||
|
@ -521,7 +521,7 @@
|
||||||
"es-abstract": {
|
"es-abstract": {
|
||||||
"version": "1.8.0",
|
"version": "1.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.8.0.tgz",
|
||||||
"integrity": "sha512-Cf9/h5MrXtExM20gSS55YFrGKCyPrRBjIVBtVyy8vmlsDfe0NPKMWj65tPLgzyfPuapWxh5whpXCtW4+AW5mRg==",
|
"integrity": "sha1-OwA4XoVymTK+/6kWO76hI06TKRQ=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"es-to-primitive": "1.1.1",
|
"es-to-primitive": "1.1.1",
|
||||||
|
@ -723,7 +723,7 @@
|
||||||
"esprima": {
|
"esprima": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz",
|
||||||
"integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==",
|
"integrity": "sha1-RJnt3NERDgshi6zy+n9/WfVcqAQ=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"esquery": {
|
"esquery": {
|
||||||
|
@ -800,6 +800,27 @@
|
||||||
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
|
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"fbjs": {
|
||||||
|
"version": "0.8.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.14.tgz",
|
||||||
|
"integrity": "sha1-0dviviVMNakeCfMfnNUKQLKg7Rw=",
|
||||||
|
"requires": {
|
||||||
|
"core-js": "1.2.7",
|
||||||
|
"isomorphic-fetch": "2.2.1",
|
||||||
|
"loose-envify": "1.3.1",
|
||||||
|
"object-assign": "4.1.1",
|
||||||
|
"promise": "7.3.1",
|
||||||
|
"setimmediate": "1.0.5",
|
||||||
|
"ua-parser-js": "0.7.14"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"core-js": {
|
||||||
|
"version": "1.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
|
||||||
|
"integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"fd-slicer": {
|
"fd-slicer": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz",
|
||||||
|
@ -941,7 +962,7 @@
|
||||||
"glob": {
|
"glob": {
|
||||||
"version": "7.1.2",
|
"version": "7.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
|
||||||
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
|
"integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"fs.realpath": "1.0.0",
|
"fs.realpath": "1.0.0",
|
||||||
"inflight": "1.0.6",
|
"inflight": "1.0.6",
|
||||||
|
@ -954,7 +975,7 @@
|
||||||
"globals": {
|
"globals": {
|
||||||
"version": "9.18.0",
|
"version": "9.18.0",
|
||||||
"resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
|
||||||
"integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==",
|
"integrity": "sha1-qjiWs+abSH8X4x7SFD1pqOMMLYo=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"globby": {
|
"globby": {
|
||||||
|
@ -1032,7 +1053,7 @@
|
||||||
"hosted-git-info": {
|
"hosted-git-info": {
|
||||||
"version": "2.5.0",
|
"version": "2.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz",
|
||||||
"integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==",
|
"integrity": "sha1-bWDjSzq7yDEwYsO3mO+NkBoHrzw=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"http-basic": {
|
"http-basic": {
|
||||||
|
@ -1070,7 +1091,7 @@
|
||||||
"iconv-lite": {
|
"iconv-lite": {
|
||||||
"version": "0.4.18",
|
"version": "0.4.18",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz",
|
||||||
"integrity": "sha512-sr1ZQph3UwHTR0XftSbK85OvBbxe/abLGzEnPENCQwmHf7sck8Oyu4ob3LgBxWWxRoM+QszeUyl7jbqapu2TqA=="
|
"integrity": "sha1-I9hlaxaq5nQqwpcy6o8DNqR4nPI="
|
||||||
},
|
},
|
||||||
"ignore": {
|
"ignore": {
|
||||||
"version": "3.3.3",
|
"version": "3.3.3",
|
||||||
|
@ -1249,6 +1270,15 @@
|
||||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
||||||
},
|
},
|
||||||
|
"isomorphic-fetch": {
|
||||||
|
"version": "2.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
|
||||||
|
"integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
|
||||||
|
"requires": {
|
||||||
|
"node-fetch": "1.7.2",
|
||||||
|
"whatwg-fetch": "2.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"isstream": {
|
"isstream": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
|
||||||
|
@ -1263,13 +1293,12 @@
|
||||||
"js-tokens": {
|
"js-tokens": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
|
||||||
"integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
|
"integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"js-yaml": {
|
"js-yaml": {
|
||||||
"version": "3.9.1",
|
"version": "3.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.9.1.tgz",
|
||||||
"integrity": "sha512-CbcG379L1e+mWBnLvHWWeLs8GyV/EMw862uLI3c+GxVyDHWZcjZinwuBd3iW2pgxgIlksW/1vNJa4to+RvDOww==",
|
"integrity": "sha1-CHdc69/dNZIJ8NKs04PI+GppBKA=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"argparse": "1.0.9",
|
"argparse": "1.0.9",
|
||||||
|
@ -1294,7 +1323,7 @@
|
||||||
"jsdoc": {
|
"jsdoc": {
|
||||||
"version": "3.5.4",
|
"version": "3.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.5.4.tgz",
|
||||||
"integrity": "sha512-VmTw0J+2L16IxAe0JSDSAcH0F+DbZxaj8wN1AjHtKMQU/hO0ciIl5ZE93XqrrFIbknobuqHKJCXZj6+Hk57MjA==",
|
"integrity": "sha1-zu73xLrEM1yxD/QeOg9Yk5pTRCg=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"babylon": "7.0.0-beta.19",
|
"babylon": "7.0.0-beta.19",
|
||||||
|
@ -1512,7 +1541,6 @@
|
||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz",
|
||||||
"integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=",
|
"integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"js-tokens": "3.0.2"
|
"js-tokens": "3.0.2"
|
||||||
}
|
}
|
||||||
|
@ -1539,7 +1567,7 @@
|
||||||
"minimatch": {
|
"minimatch": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
"integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"brace-expansion": "1.1.8"
|
"brace-expansion": "1.1.8"
|
||||||
}
|
}
|
||||||
|
@ -1560,7 +1588,7 @@
|
||||||
"mockery": {
|
"mockery": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/mockery/-/mockery-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/mockery/-/mockery-2.1.0.tgz",
|
||||||
"integrity": "sha512-9VkOmxKlWXoDO/h1jDZaS4lH33aWfRiJiNT/tKj+8OGzrcFDLo8d0syGdbsc3Bc4GvRXPb+NMMvojotmuGJTvA==",
|
"integrity": "sha1-WwrvH/Vk8PgTlEXhZVNseQlxNHA=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"ms": {
|
"ms": {
|
||||||
|
@ -1588,7 +1616,7 @@
|
||||||
"node-fetch": {
|
"node-fetch": {
|
||||||
"version": "1.7.2",
|
"version": "1.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.2.tgz",
|
||||||
"integrity": "sha512-xZZUq2yDhKMIn/UgG5q//IZSNLJIwW2QxS14CNH5spuiXkITM2pUitjdq58yLSaU7m4M0wBNaM2Gh/ggY4YJig==",
|
"integrity": "sha1-xU6arFfkModSM1JfPIkcQVn/79c=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"encoding": "0.1.12",
|
"encoding": "0.1.12",
|
||||||
"is-stream": "1.1.0"
|
"is-stream": "1.1.0"
|
||||||
|
@ -1632,7 +1660,7 @@
|
||||||
"normalize-package-data": {
|
"normalize-package-data": {
|
||||||
"version": "2.4.0",
|
"version": "2.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
|
||||||
"integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
|
"integrity": "sha1-EvlaMH1YNSB1oEkHuErIvpisAS8=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"hosted-git-info": "2.5.0",
|
"hosted-git-info": "2.5.0",
|
||||||
|
@ -1644,7 +1672,7 @@
|
||||||
"npmlog": {
|
"npmlog": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
|
||||||
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
|
"integrity": "sha1-CKfyqL9zRgR3mp76StXMcXq7lUs=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"are-we-there-yet": "1.1.4",
|
"are-we-there-yet": "1.1.4",
|
||||||
"console-control-strings": "1.1.0",
|
"console-control-strings": "1.1.0",
|
||||||
|
@ -1805,11 +1833,20 @@
|
||||||
"promise": {
|
"promise": {
|
||||||
"version": "7.3.1",
|
"version": "7.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
|
||||||
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
|
"integrity": "sha1-BktyYCsY+Q8pGSuLG8QY/9Hr078=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"asap": "2.0.6"
|
"asap": "2.0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"prop-types": {
|
||||||
|
"version": "15.5.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.10.tgz",
|
||||||
|
"integrity": "sha1-J5ffwxJhguOpXj37suiT3ddFYVQ=",
|
||||||
|
"requires": {
|
||||||
|
"fbjs": "0.8.14",
|
||||||
|
"loose-envify": "1.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"punycode": {
|
"punycode": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
|
||||||
|
@ -1873,7 +1910,7 @@
|
||||||
"readable-stream": {
|
"readable-stream": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
|
||||||
"integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
|
"integrity": "sha1-No8lEtefnUb9/HE0mueHi7weuVw=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"core-util-is": "1.0.2",
|
"core-util-is": "1.0.2",
|
||||||
"inherits": "2.0.3",
|
"inherits": "2.0.3",
|
||||||
|
@ -1986,7 +2023,7 @@
|
||||||
"resolve": {
|
"resolve": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz",
|
||||||
"integrity": "sha512-aW7sVKPufyHqOmyyLzg/J+8606v5nevBgaliIlV7nUpVMsDnoBGV/cbSLNjZAg9q0Cfd/+easKVKQ8vOu8fn1Q==",
|
"integrity": "sha1-p1vgHFPaJdk0qY69DkxKcxL5KoY=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"path-parse": "1.0.5"
|
"path-parse": "1.0.5"
|
||||||
|
@ -2034,18 +2071,23 @@
|
||||||
"safe-buffer": {
|
"safe-buffer": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
|
||||||
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
|
"integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM="
|
||||||
},
|
},
|
||||||
"semver": {
|
"semver": {
|
||||||
"version": "5.4.1",
|
"version": "5.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
|
||||||
"integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg=="
|
"integrity": "sha1-4FnAnYVx8FQII3M0M1BdOi8AsY4="
|
||||||
},
|
},
|
||||||
"set-blocking": {
|
"set-blocking": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||||
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
|
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
|
||||||
},
|
},
|
||||||
|
"setimmediate": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
|
||||||
|
"integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
|
||||||
|
},
|
||||||
"shelljs": {
|
"shelljs": {
|
||||||
"version": "0.7.8",
|
"version": "0.7.8",
|
||||||
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz",
|
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz",
|
||||||
|
@ -2153,7 +2195,7 @@
|
||||||
"string_decoder": {
|
"string_decoder": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
|
||||||
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
|
"integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"safe-buffer": "5.1.1"
|
"safe-buffer": "5.1.1"
|
||||||
}
|
}
|
||||||
|
@ -2237,7 +2279,7 @@
|
||||||
"string-width": {
|
"string-width": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
|
||||||
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
|
"integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"is-fullwidth-code-point": "2.0.0",
|
"is-fullwidth-code-point": "2.0.0",
|
||||||
|
@ -2372,6 +2414,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||||
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
|
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
|
||||||
},
|
},
|
||||||
|
"ua-parser-js": {
|
||||||
|
"version": "0.7.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.14.tgz",
|
||||||
|
"integrity": "sha1-EQ1T+kw/MmwSEpK76skE0uAzh8o="
|
||||||
|
},
|
||||||
"uid-number": {
|
"uid-number": {
|
||||||
"version": "0.0.6",
|
"version": "0.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz",
|
||||||
|
@ -2432,7 +2479,7 @@
|
||||||
"uuid": {
|
"uuid": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz",
|
||||||
"integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g=="
|
"integrity": "sha1-PdPT55Crwk17DToDT/q6vijrvAQ="
|
||||||
},
|
},
|
||||||
"validate-npm-package-license": {
|
"validate-npm-package-license": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
|
@ -2461,10 +2508,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"whatwg-fetch": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz",
|
||||||
|
"integrity": "sha1-nITsLc9oGH/wC8ZOEnS0QhduHIQ="
|
||||||
|
},
|
||||||
"wide-align": {
|
"wide-align": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz",
|
||||||
"integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==",
|
"integrity": "sha1-Vx4PGwYEY268DfwhsDObvjE0FxA=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"string-width": "1.0.2"
|
"string-width": "1.0.2"
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,9 +59,9 @@
|
||||||
"jsdoc": "npm install && npm run jsdoc:clean && jsdoc -u docs/tutorials -p package.json -c docs/conf.json",
|
"jsdoc": "npm install && npm run jsdoc:clean && jsdoc -u docs/tutorials -p package.json -c docs/conf.json",
|
||||||
"prenode-tests": "npm install --build-from-source=realm && cd tests && npm install",
|
"prenode-tests": "npm install --build-from-source=realm && cd tests && npm install",
|
||||||
"node-tests": "cd tests && npm run test && cd ..",
|
"node-tests": "cd tests && npm run test && cd ..",
|
||||||
"test-runner:ava": "cd tests/test-runners/ava && npm install && npm test",
|
"test-runner:ava": "cd tests/test-runners/ava && npm install --build-from-source=realm && npm test",
|
||||||
"test-runner:mocha": "cd tests/test-runners/mocha && npm install && npm test",
|
"test-runner:mocha": "cd tests/test-runners/mocha && npm install --build-from-source=realm && npm test",
|
||||||
"test-runner:jest": "cd tests/test-runners/jest && npm install && npm test",
|
"test-runner:jest": "cd tests/test-runners/jest && npm install --build-from-source=realm && npm test",
|
||||||
"test-runners": "npm run test-runner:ava && npm run test-runner:mocha && npm run test-runner:jest",
|
"test-runners": "npm run test-runner:ava && npm run test-runner:mocha && npm run test-runner:jest",
|
||||||
"isMac": "node -p \"if (process.platform == 'darwin') { process.exit(0); } else { process.exit(-1); }\"",
|
"isMac": "node -p \"if (process.platform == 'darwin') { process.exit(0); } else { process.exit(-1); }\"",
|
||||||
"testMac": "npm run isMac -s && echo this is mac || echo . ",
|
"testMac": "npm run isMac -s && echo this is mac || echo . ",
|
||||||
|
@ -86,6 +86,7 @@
|
||||||
"node-fetch": "^1.6.3",
|
"node-fetch": "^1.6.3",
|
||||||
"node-pre-gyp": "^0.6.36",
|
"node-pre-gyp": "^0.6.36",
|
||||||
"progress": "^2.0.0",
|
"progress": "^2.0.0",
|
||||||
|
"prop-types": "^15.5.10",
|
||||||
"request": "^2.78.0",
|
"request": "^2.78.0",
|
||||||
"stream-counter": "^1.0.0",
|
"stream-counter": "^1.0.0",
|
||||||
"sync-request": "^3.0.1",
|
"sync-request": "^3.0.1",
|
||||||
|
|
|
@ -137,6 +137,41 @@ def findNdkBuildFullPath() {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def checkNdkVersion(ndkBuildFullPath) {
|
||||||
|
def ndkPath = new File(ndkBuildFullPath).getParent()
|
||||||
|
def detectedNdkVersion
|
||||||
|
def releaseFile = new File(ndkPath, 'RELEASE.TXT')
|
||||||
|
def propertyFile = new File(ndkPath, 'source.properties')
|
||||||
|
if (releaseFile.isFile()) {
|
||||||
|
detectedNdkVersion = releaseFile.text.trim().split()[0].split('-')[0]
|
||||||
|
} else if (propertyFile.isFile()) {
|
||||||
|
detectedNdkVersion = getValueFromPropertiesFile(propertyFile, 'Pkg.Revision')
|
||||||
|
if (detectedNdkVersion == null) {
|
||||||
|
throw new GradleException("Failed to obtain the NDK version information from ${ndkPath}/source.properties")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new GradleException("Neither ${releaseFile.getAbsolutePath()} nor ${propertyFile.getAbsolutePath()} is a file.")
|
||||||
|
}
|
||||||
|
if (detectedNdkVersion != project.ndkVersion) {
|
||||||
|
throw new GradleException("Your NDK version: ${detectedNdkVersion}."
|
||||||
|
+ " Realm JNI must be compiled with the version ${project.ndkVersion} of NDK.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static def getValueFromPropertiesFile(File propFile, String key) {
|
||||||
|
if (!propFile.isFile() || !propFile.canRead()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
def prop = new Properties()
|
||||||
|
def reader = propFile.newReader()
|
||||||
|
try {
|
||||||
|
prop.load(reader)
|
||||||
|
} finally {
|
||||||
|
reader.close()
|
||||||
|
}
|
||||||
|
return prop.get(key)
|
||||||
|
}
|
||||||
|
|
||||||
def getNdkBuildFullPath() {
|
def getNdkBuildFullPath() {
|
||||||
def ndkBuildFullPath = findNdkBuildFullPath()
|
def ndkBuildFullPath = findNdkBuildFullPath()
|
||||||
if (ndkBuildFullPath == null) {
|
if (ndkBuildFullPath == null) {
|
||||||
|
@ -153,6 +188,9 @@ def getNdkBuildFullPath() {
|
||||||
"(On Windows, make sure you escape backslashes in local.properties or use forward slashes, e.g. C:\\\\ndk or C:/ndk rather than C:\\ndk)",
|
"(On Windows, make sure you escape backslashes in local.properties or use forward slashes, e.g. C:\\\\ndk or C:/ndk rather than C:\\ndk)",
|
||||||
null)
|
null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkNdkVersion(ndkBuildFullPath);
|
||||||
|
|
||||||
return ndkBuildFullPath
|
return ndkBuildFullPath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,3 +6,4 @@ POM_PACKAGING=aar
|
||||||
POM_DESCRIPTION=Android Realm React Native module. Realm is a mobile database: a replacement for SQLite & ORMs
|
POM_DESCRIPTION=Android Realm React Native module. Realm is a mobile database: a replacement for SQLite & ORMs
|
||||||
DEBUG_BUILD=true
|
DEBUG_BUILD=true
|
||||||
android.useDeprecatedNdk=true
|
android.useDeprecatedNdk=true
|
||||||
|
ndkVersion=r10e
|
||||||
|
|
|
@ -70,6 +70,7 @@ LOCAL_C_INCLUDES += src/object-store/external/pegtl
|
||||||
LOCAL_C_INCLUDES += vendor
|
LOCAL_C_INCLUDES += vendor
|
||||||
LOCAL_C_INCLUDES += $(JAVA_HOME)/include
|
LOCAL_C_INCLUDES += $(JAVA_HOME)/include
|
||||||
LOCAL_C_INCLUDES += $(JAVA_HOME)/include/darwin
|
LOCAL_C_INCLUDES += $(JAVA_HOME)/include/darwin
|
||||||
|
LOCAL_C_INCLUDES += $(JAVA_HOME)/include/linux
|
||||||
LOCAL_C_INCLUDES += core/include
|
LOCAL_C_INCLUDES += core/include
|
||||||
ifeq ($(strip $(BUILD_TYPE_SYNC)),1)
|
ifeq ($(strip $(BUILD_TYPE_SYNC)),1)
|
||||||
LOCAL_C_INCLUDES += src/object-store/src/sync
|
LOCAL_C_INCLUDES += src/object-store/src/sync
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import { ListView as BaseListView } from 'react-native';
|
import { ListView as BaseListView } from 'react-native';
|
||||||
|
|
||||||
function hashObjects(array) {
|
function hashObjects(array) {
|
||||||
|
@ -196,8 +197,8 @@ export default class ListView extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView.propTypes = {
|
ListView.propTypes = {
|
||||||
dataSource: React.PropTypes.instanceOf(ListViewDataSource).isRequired,
|
dataSource: PropTypes.instanceOf(ListViewDataSource).isRequired,
|
||||||
renderRow: React.PropTypes.func.isRequired,
|
renderRow: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
ListView.DataSource = ListViewDataSource;
|
ListView.DataSource = ListViewDataSource;
|
||||||
|
|
|
@ -174,7 +174,16 @@ if (!fs.existsSync(realmDir)) {
|
||||||
pipeline = pipeline.then(() => decompress(downloadedArchive, targetFolder, decompressOptions));
|
pipeline = pipeline.then(() => decompress(downloadedArchive, targetFolder, decompressOptions));
|
||||||
|
|
||||||
if (extractedFolder) {
|
if (extractedFolder) {
|
||||||
pipeline = pipeline.then(() => fs.renameSync(path.resolve(vendorDir, extractedFolder), realmDir));
|
pipeline = pipeline.then(() => {
|
||||||
|
fs.renameSync(path.resolve(vendorDir, extractedFolder), realmDir);
|
||||||
|
const libDir = path.resolve(realmDir, 'lib')
|
||||||
|
if (fs.existsSync(libDir)) {
|
||||||
|
// Remove all shared libraries as we want to just use the static ones
|
||||||
|
fs.readdirSync(libDir)
|
||||||
|
.filter(name => /\.so$/.test(name))
|
||||||
|
.forEach(name => fs.unlinkSync(path.resolve(libDir, name)));
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pipeline.catch(error => {
|
pipeline.catch(error => {
|
||||||
|
|
|
@ -61,18 +61,8 @@ VERSION_BRANCH="${RELEASE_VERSION%.*}.x"
|
||||||
git fetch origin || die 'Failed to fetch from git origin.'
|
git fetch origin || die 'Failed to fetch from git origin.'
|
||||||
[ -z "$(git rev-list origin/$BRANCH..HEAD)" ] || die 'Local commits are not pushed to origin.'
|
[ -z "$(git rev-list origin/$BRANCH..HEAD)" ] || die 'Local commits are not pushed to origin.'
|
||||||
|
|
||||||
# Run all tests that must pass before publishing.
|
|
||||||
for test in eslint jsdoc license-check react-example react-tests-android react-tests; do
|
|
||||||
for configuration in Debug Release; do
|
|
||||||
echo "RUNNING TEST: $test ($configuration)"
|
|
||||||
echo '----------------------------------------'
|
|
||||||
npm test "$test" "$configuration" || die "Test Failed: $test ($configuration)"
|
|
||||||
echo
|
|
||||||
done
|
|
||||||
done
|
|
||||||
|
|
||||||
# Double check before actually publishing.
|
# Double check before actually publishing.
|
||||||
confirm "Are you sure you want to publish $VERSION?" || die "Aborted publishing $VERSION"
|
confirm "Are you sure you want to publish $VERSION? (Did you run all tests)?" || die "Aborted publishing $VERSION"
|
||||||
|
|
||||||
# This should fail if this tag already exists.
|
# This should fail if this tag already exists.
|
||||||
git tag "v$VERSION"
|
git tag "v$VERSION"
|
||||||
|
@ -88,3 +78,5 @@ REALM_BUILD_ANDROID=1 npm publish ${PRERELEASE:+--tag $PRERELEASE}
|
||||||
# Only push the tag to GitHub if the publish was successful.
|
# Only push the tag to GitHub if the publish was successful.
|
||||||
echo "Pushing v$VERSION tag to GitHub..."
|
echo "Pushing v$VERSION tag to GitHub..."
|
||||||
git push origin "v$VERSION"
|
git push origin "v$VERSION"
|
||||||
|
|
||||||
|
echo "Done. Now, you should update the documentation!"
|
||||||
|
|
|
@ -9,7 +9,7 @@ export NPM_CONFIG_PROGRESS=false
|
||||||
TARGET=$1
|
TARGET=$1
|
||||||
CONFIGURATION=${2:-Release}
|
CONFIGURATION=${2:-Release}
|
||||||
|
|
||||||
if echo $CONFIGURATION | grep -i "^Debug$" > /dev/null ; then
|
if echo "$CONFIGURATION" | grep -i "^Debug$" > /dev/null ; then
|
||||||
CONFIGURATION="Debug"
|
CONFIGURATION="Debug"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -17,8 +17,6 @@ IOS_SIM_DEVICE=${IOS_SIM_DEVICE:-} # use preferentially, otherwise will be set a
|
||||||
ios_sim_default_device_type=${IOS_SIM_DEVICE_TYPE:-iPhone 5s}
|
ios_sim_default_device_type=${IOS_SIM_DEVICE_TYPE:-iPhone 5s}
|
||||||
ios_sim_default_ios_version=${IOS_SIM_OS:-iOS 10.1}
|
ios_sim_default_ios_version=${IOS_SIM_OS:-iOS 10.1}
|
||||||
|
|
||||||
ACCEPTED_LICENSES='MIT, ISC, BSD, Apache-2.0, BSD-2-Clause, BSD-3-Clause, WTFPL, Unlicense, (MIT AND CC-BY-3.0)'
|
|
||||||
|
|
||||||
PATH="/opt/android-sdk-linux/platform-tools:$PATH"
|
PATH="/opt/android-sdk-linux/platform-tools:$PATH"
|
||||||
SRCROOT=$(cd "$(dirname "$0")/.." && pwd)
|
SRCROOT=$(cd "$(dirname "$0")/.." && pwd)
|
||||||
XCPRETTY=$(which xcpretty || true)
|
XCPRETTY=$(which xcpretty || true)
|
||||||
|
@ -48,7 +46,11 @@ download_server() {
|
||||||
}
|
}
|
||||||
|
|
||||||
start_server() {
|
start_server() {
|
||||||
./object-server-for-testing/start-object-server.command &
|
#disabled ROS logging
|
||||||
|
./object-server-for-testing/start-object-server.command &> /dev/null &
|
||||||
|
|
||||||
|
#enabled ROS logging
|
||||||
|
#./object-server-for-testing/start-object-server.command &
|
||||||
SERVER_PID=$!
|
SERVER_PID=$!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,12 +122,17 @@ xctest() {
|
||||||
echo " done"
|
echo " done"
|
||||||
|
|
||||||
# - Run the build and test
|
# - Run the build and test
|
||||||
|
xcrun xcodebuild -scheme "$1" -configuration "$CONFIGURATION" -sdk iphonesimulator -destination id="$IOS_SIM_DEVICE" build || {
|
||||||
|
EXITCODE=$?
|
||||||
|
echo "*** Failure (exit code $EXITCODE). ***"
|
||||||
|
exit $EXITCODE
|
||||||
|
}
|
||||||
if [ -n "$XCPRETTY" ]; then
|
if [ -n "$XCPRETTY" ]; then
|
||||||
log_temp=$(mktemp build.log.XXXXXX)
|
log_temp=$(mktemp build.log.XXXXXX)
|
||||||
if [ -e "$log_temp" ]; then
|
if [ -e "$log_temp" ]; then
|
||||||
rm "$log_temp"
|
rm "$log_temp"
|
||||||
fi
|
fi
|
||||||
xcrun xcodebuild -scheme "$1" -configuration "$CONFIGURATION" -sdk iphonesimulator -destination name="iPhone 5s" build test 2>&1 | tee "$log_temp" | "$XCPRETTY" -c --no-utf --report junit --output build/reports/junit.xml || {
|
xcrun xcodebuild -scheme "$1" -configuration "$CONFIGURATION" -sdk iphonesimulator -destination name="iPhone 5s" test 2>&1 | tee "$log_temp" | "$XCPRETTY" -c --no-utf --report junit --output build/reports/junit.xml || {
|
||||||
EXITCODE=$?
|
EXITCODE=$?
|
||||||
printf "*** Xcode Failure (exit code %s). The full xcode log follows: ***\n\n" "$EXITCODE"
|
printf "*** Xcode Failure (exit code %s). The full xcode log follows: ***\n\n" "$EXITCODE"
|
||||||
cat "$log_temp"
|
cat "$log_temp"
|
||||||
|
@ -134,7 +141,7 @@ xctest() {
|
||||||
}
|
}
|
||||||
rm "$log_temp"
|
rm "$log_temp"
|
||||||
else
|
else
|
||||||
xcrun xcodebuild -scheme "$1" -configuration "$CONFIGURATION" -sdk iphonesimulator -destination id="$IOS_SIM_DEVICE" build test || {
|
xcrun xcodebuild -scheme "$1" -configuration "$CONFIGURATION" -sdk iphonesimulator -destination id="$IOS_SIM_DEVICE" test || {
|
||||||
EXITCODE=$?
|
EXITCODE=$?
|
||||||
echo "*** Failure (exit code $EXITCODE). ***"
|
echo "*** Failure (exit code $EXITCODE). ***"
|
||||||
exit $EXITCODE
|
exit $EXITCODE
|
||||||
|
@ -230,14 +237,22 @@ cleanup
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
|
|
||||||
# Use a consistent version of Node if possible.
|
# Use a consistent version of Node if possible.
|
||||||
|
if [[ -z "$(command -v nvm)" ]]; then
|
||||||
|
set +e
|
||||||
if [ -f "$NVM_DIR/nvm.sh" ]; then
|
if [ -f "$NVM_DIR/nvm.sh" ]; then
|
||||||
. "$NVM_DIR/nvm.sh"
|
. "$NVM_DIR/nvm.sh" '' || true
|
||||||
elif [ -x "$(command -v brew)" ] && [ -f "$(brew --prefix nvm)/nvm.sh" ]; then
|
elif [ -x "$(command -v brew)" ] && [ -f "$(brew --prefix nvm)/nvm.sh" ]; then
|
||||||
# we must be on mac and nvm was installed with brew
|
# we must be on mac and nvm was installed with brew
|
||||||
# TODO: change the mac slaves to use manual nvm installation
|
# TODO: change the mac slaves to use manual nvm installation
|
||||||
. "$(brew --prefix nvm)/nvm.sh"
|
. "$(brew --prefix nvm)/nvm.sh" '' || true
|
||||||
|
elif [ -f "$HOME/.nvm/nvm.sh" ]; then
|
||||||
|
. ~/.nvm/nvm.sh ''
|
||||||
|
fi
|
||||||
|
set -e
|
||||||
|
fi
|
||||||
|
if [[ "$(command -v nvm)" ]]; then
|
||||||
|
nvm install 6.5.0
|
||||||
fi
|
fi
|
||||||
[[ "$(command -v nvm)" ]] && nvm use 6.5.0 || true
|
|
||||||
|
|
||||||
# Remove cached packages
|
# Remove cached packages
|
||||||
rm -rf ~/.yarn-cache/npm-realm-*
|
rm -rf ~/.yarn-cache/npm-realm-*
|
||||||
|
@ -392,10 +407,19 @@ case "$TARGET" in
|
||||||
;;
|
;;
|
||||||
"test-runners")
|
"test-runners")
|
||||||
npm run check-environment
|
npm run check-environment
|
||||||
# Create a fake realm module that points to the source root so that test-runner tests can require('realm')
|
|
||||||
npm install --build-from-source=realm
|
|
||||||
npm run test-runners
|
npm run test-runners
|
||||||
;;
|
;;
|
||||||
|
"all")
|
||||||
|
# Run all tests that must pass before publishing.
|
||||||
|
for test in eslint license-check react-example react-tests-android react-tests; do
|
||||||
|
for configuration in Debug Release; do
|
||||||
|
echo "RUNNING TEST: $test ($configuration)"
|
||||||
|
echo '----------------------------------------'
|
||||||
|
npm test "$test" "$configuration" || die "Test Failed: $test ($configuration)"
|
||||||
|
echo
|
||||||
|
done
|
||||||
|
done
|
||||||
|
;;
|
||||||
"object-store")
|
"object-store")
|
||||||
pushd src/object-store
|
pushd src/object-store
|
||||||
cmake -DCMAKE_BUILD_TYPE="$CONFIGURATION" .
|
cmake -DCMAKE_BUILD_TYPE="$CONFIGURATION" .
|
||||||
|
|
|
@ -93,4 +93,19 @@ namespace realm {
|
||||||
s_default_realm_directory + "/*.realm.lock";
|
s_default_realm_directory + "/*.realm.lock";
|
||||||
system(cmd.c_str());
|
system(cmd.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void remove_directory(const std::string &path)
|
||||||
|
{
|
||||||
|
std::string cmd_clear_dir = "rm " + path + "/*";
|
||||||
|
system(cmd_clear_dir.c_str());
|
||||||
|
|
||||||
|
std::string cmd_rmdir = "rmdir " + path;
|
||||||
|
system(cmd_rmdir.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_file(const std::string &path)
|
||||||
|
{
|
||||||
|
std::string cmd = "rm " + path;
|
||||||
|
system(cmd.c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,37 +20,26 @@
|
||||||
|
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <exception>
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
namespace realm {
|
#include <realm/util/optional.hpp>
|
||||||
|
|
||||||
class ConcurrentDequeTimeout : public std::exception {
|
namespace realm {
|
||||||
public:
|
|
||||||
ConcurrentDequeTimeout() : std::exception() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class ConcurrentDeque {
|
class ConcurrentDeque {
|
||||||
public:
|
public:
|
||||||
T pop_front(size_t timeout = 0) {
|
T pop_back() {
|
||||||
std::unique_lock<std::mutex> lock(m_mutex);
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||||||
while (m_deque.empty()) {
|
m_condition.wait(lock, [this] { return !m_deque.empty(); });
|
||||||
wait(lock, timeout);
|
return do_pop_back();
|
||||||
}
|
|
||||||
T item = std::move(m_deque.front());
|
|
||||||
m_deque.pop_front();
|
|
||||||
return item;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
T pop_back(size_t timeout = 0) {
|
util::Optional<T> try_pop_back(size_t timeout) {
|
||||||
std::unique_lock<std::mutex> lock(m_mutex);
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||||||
while (m_deque.empty()) {
|
m_condition.wait_for(lock, std::chrono::milliseconds(timeout),
|
||||||
wait(lock, timeout);
|
[this] { return !m_deque.empty(); });
|
||||||
}
|
return m_deque.empty() ? util::none : util::make_optional(do_pop_back());
|
||||||
T item = std::move(m_deque.back());
|
|
||||||
m_deque.pop_back();
|
|
||||||
return item;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void push_front(T&& item) {
|
void push_front(T&& item) {
|
||||||
|
@ -72,19 +61,16 @@ class ConcurrentDeque {
|
||||||
return m_deque.empty();
|
return m_deque.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void wait(std::unique_lock<std::mutex> &lock, size_t timeout = 0) {
|
|
||||||
if (!timeout) {
|
|
||||||
m_condition.wait(lock);
|
|
||||||
}
|
|
||||||
else if (m_condition.wait_for(lock, std::chrono::milliseconds(timeout)) == std::cv_status::timeout) {
|
|
||||||
throw ConcurrentDequeTimeout();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::condition_variable m_condition;
|
std::condition_variable m_condition;
|
||||||
std::mutex m_mutex;
|
std::mutex m_mutex;
|
||||||
std::deque<T> m_deque;
|
std::deque<T> m_deque;
|
||||||
|
|
||||||
|
T do_pop_back() {
|
||||||
|
T item = std::move(m_deque.back());
|
||||||
|
m_deque.pop_back();
|
||||||
|
return item;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // realm
|
} // realm
|
||||||
|
|
|
@ -102,4 +102,19 @@ void remove_realm_files_from_directory(const std::string &directory)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void remove_file(const std::string &path)
|
||||||
|
{
|
||||||
|
NSFileManager *manager = [NSFileManager defaultManager];
|
||||||
|
NSString *filePath = @(path.c_str());
|
||||||
|
|
||||||
|
if (![manager removeItemAtPath:filePath error:nil]) {
|
||||||
|
throw std::runtime_error((std::string)"Failed to delete file at path " + filePath.UTF8String);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_directory(const std::string &path)
|
||||||
|
{
|
||||||
|
remove_file(path); // works for directories too
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,29 @@ using ConstructorType = void(typename T::Context, typename T::Object, size_t, co
|
||||||
template<typename T>
|
template<typename T>
|
||||||
using MethodType = void(typename T::Context, typename T::Function, typename T::Object, size_t, const typename T::Value[], ReturnValue<T> &);
|
using MethodType = void(typename T::Context, typename T::Function, typename T::Object, size_t, const typename T::Value[], ReturnValue<T> &);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct Arguments {
|
||||||
|
const typename T::Context ctx;
|
||||||
|
const size_t count;
|
||||||
|
const typename T::Value* const value;
|
||||||
|
|
||||||
|
typename T::Value operator[](size_t index) const noexcept {
|
||||||
|
if (index >= count) {
|
||||||
|
return Value<T>::from_undefined(ctx);
|
||||||
|
}
|
||||||
|
return value[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void validate_maximum(size_t max) const {
|
||||||
|
if (max < count) {
|
||||||
|
throw std::invalid_argument(util::format("Invalid arguments: at most %1 expected, but %2 supplied.", max, count));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
using ArgumentsMethodType = void(typename T::Context, typename T::Function, typename T::Object, Arguments<T>, ReturnValue<T> &);
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct PropertyType {
|
struct PropertyType {
|
||||||
using GetterType = void(typename T::Context, typename T::Object, ReturnValue<T> &);
|
using GetterType = void(typename T::Context, typename T::Object, ReturnValue<T> &);
|
||||||
|
|
|
@ -22,8 +22,6 @@
|
||||||
#include "js_realm_object.hpp"
|
#include "js_realm_object.hpp"
|
||||||
#include "js_schema.hpp"
|
#include "js_schema.hpp"
|
||||||
|
|
||||||
#include "util/format.hpp"
|
|
||||||
|
|
||||||
namespace realm {
|
namespace realm {
|
||||||
class List;
|
class List;
|
||||||
class Object;
|
class Object;
|
||||||
|
@ -65,8 +63,9 @@ public:
|
||||||
ValueType value = Object::get_property(m_ctx, object, prop_name);
|
ValueType value = Object::get_property(m_ctx, object, prop_name);
|
||||||
const auto& prop = m_object_schema.persisted_properties[prop_index];
|
const auto& prop = m_object_schema.persisted_properties[prop_index];
|
||||||
if (!Value::is_valid_for_property(m_ctx, value, prop)) {
|
if (!Value::is_valid_for_property(m_ctx, value, prop)) {
|
||||||
throw TypeErrorException(util::format("%1.%2", m_object_schema.name, prop.name),
|
throw TypeErrorException(m_object_schema.name, prop.name,
|
||||||
js_type_name_for_property_type(prop.type));
|
js_type_name_for_property_type(prop.type),
|
||||||
|
print(value));
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
@ -121,7 +120,7 @@ public:
|
||||||
void will_change(realm::Object&, realm::Property const&) { }
|
void will_change(realm::Object&, realm::Property const&) { }
|
||||||
void did_change() { }
|
void did_change() { }
|
||||||
|
|
||||||
std::string print(ValueType const&) { return "not implemented"; }
|
std::string print(ValueType const& v) { return Value::to_string(m_ctx, v); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ContextType m_ctx;
|
ContextType m_ctx;
|
||||||
|
|
296
src/js_realm.hpp
296
src/js_realm.hpp
|
@ -123,9 +123,7 @@ class RealmDelegate : public BindingContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectType realm_object = create_object<T, RealmClass<T>>(m_context, new SharedRealm(realm));
|
ObjectType realm_object = create_object<T, RealmClass<T>>(m_context, new SharedRealm(realm));
|
||||||
ValueType arguments[2];
|
ValueType arguments[] = {realm_object, Value::from_string(m_context, notification_name)};
|
||||||
arguments[0] = realm_object;
|
|
||||||
arguments[1] = Value::from_string(m_context, notification_name);
|
|
||||||
|
|
||||||
std::list<Protected<FunctionType>> notifications_copy(m_notifications);
|
std::list<Protected<FunctionType>> notifications_copy(m_notifications);
|
||||||
for (auto &callback : notifications_copy) {
|
for (auto &callback : notifications_copy) {
|
||||||
|
@ -148,6 +146,7 @@ class RealmClass : public ClassDefinition<T, SharedRealm, ObservableClass<T>> {
|
||||||
using FunctionType = typename T::Function;
|
using FunctionType = typename T::Function;
|
||||||
using ObjectType = typename T::Object;
|
using ObjectType = typename T::Object;
|
||||||
using ValueType = typename T::Value;
|
using ValueType = typename T::Value;
|
||||||
|
using Arguments = js::Arguments<T>;
|
||||||
using String = js::String<T>;
|
using String = js::String<T>;
|
||||||
using Object = js::Object<T>;
|
using Object = js::Object<T>;
|
||||||
using Value = js::Value<T>;
|
using Value = js::Value<T>;
|
||||||
|
@ -165,24 +164,31 @@ public:
|
||||||
static FunctionType create_constructor(ContextType);
|
static FunctionType create_constructor(ContextType);
|
||||||
|
|
||||||
// methods
|
// methods
|
||||||
static void objects(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
static void objects(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||||
static void object_for_primary_key(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
static void object_for_primary_key(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||||
static void create(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
static void create(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||||
static void delete_one(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
static void delete_one(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||||
static void delete_all(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
static void delete_all(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||||
static void write(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
static void write(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||||
static void add_listener(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
static void begin_transaction(ContextType, FunctionType, ObjectType, Arguments, ReturnValue&);
|
||||||
static void wait_for_download_completion(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
static void commit_transaction(ContextType, FunctionType, ObjectType, Arguments, ReturnValue&);
|
||||||
static void remove_listener(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
static void cancel_transaction(ContextType, FunctionType, ObjectType, Arguments, ReturnValue&);
|
||||||
static void remove_all_listeners(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
static void add_listener(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||||
static void close(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
static void wait_for_download_completion(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||||
|
static void remove_listener(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||||
|
static void remove_all_listeners(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||||
|
static void close(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||||
|
static void compact(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||||
|
static void delete_model(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||||
|
|
||||||
// properties
|
// properties
|
||||||
static void get_empty(ContextType, ObjectType, ReturnValue &);
|
static void get_empty(ContextType, ObjectType, ReturnValue &);
|
||||||
static void get_path(ContextType, ObjectType, ReturnValue &);
|
static void get_path(ContextType, ObjectType, ReturnValue &);
|
||||||
static void get_schema_version(ContextType, ObjectType, ReturnValue &);
|
static void get_schema_version(ContextType, ObjectType, ReturnValue &);
|
||||||
static void get_schema(ContextType, ObjectType, ReturnValue &);
|
static void get_schema(ContextType, ObjectType, ReturnValue &);
|
||||||
|
static void get_in_memory(ContextType, ObjectType, ReturnValue &);
|
||||||
static void get_read_only(ContextType, ObjectType, ReturnValue &);
|
static void get_read_only(ContextType, ObjectType, ReturnValue &);
|
||||||
|
static void get_is_in_transaction(ContextType, ObjectType, ReturnValue &);
|
||||||
#if REALM_ENABLE_SYNC
|
#if REALM_ENABLE_SYNC
|
||||||
static void get_sync_session(ContextType, ObjectType, ReturnValue &);
|
static void get_sync_session(ContextType, ObjectType, ReturnValue &);
|
||||||
#endif
|
#endif
|
||||||
|
@ -191,9 +197,10 @@ public:
|
||||||
static void constructor(ContextType, ObjectType, size_t, const ValueType[]);
|
static void constructor(ContextType, ObjectType, size_t, const ValueType[]);
|
||||||
static SharedRealm create_shared_realm(ContextType, realm::Realm::Config, bool, ObjectDefaultsMap &&, ConstructorMap &&);
|
static SharedRealm create_shared_realm(ContextType, realm::Realm::Config, bool, ObjectDefaultsMap &&, ConstructorMap &&);
|
||||||
|
|
||||||
static void schema_version(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
static void schema_version(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||||
static void clear_test_state(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
static void clear_test_state(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||||
static void copy_bundled_realm_files(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
static void copy_bundled_realm_files(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||||
|
static void delete_file(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &);
|
||||||
|
|
||||||
// static properties
|
// static properties
|
||||||
static void get_default_path(ContextType, ObjectType, ReturnValue &);
|
static void get_default_path(ContextType, ObjectType, ReturnValue &);
|
||||||
|
@ -205,6 +212,7 @@ public:
|
||||||
{"schemaVersion", wrap<schema_version>},
|
{"schemaVersion", wrap<schema_version>},
|
||||||
{"clearTestState", wrap<clear_test_state>},
|
{"clearTestState", wrap<clear_test_state>},
|
||||||
{"copyBundledRealmFiles", wrap<copy_bundled_realm_files>},
|
{"copyBundledRealmFiles", wrap<copy_bundled_realm_files>},
|
||||||
|
{"deleteFile", wrap<delete_file>},
|
||||||
{"_waitForDownload", wrap<wait_for_download_completion>},
|
{"_waitForDownload", wrap<wait_for_download_completion>},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -219,10 +227,15 @@ public:
|
||||||
{"delete", wrap<delete_one>},
|
{"delete", wrap<delete_one>},
|
||||||
{"deleteAll", wrap<delete_all>},
|
{"deleteAll", wrap<delete_all>},
|
||||||
{"write", wrap<write>},
|
{"write", wrap<write>},
|
||||||
|
{"beginTransaction", wrap<begin_transaction>},
|
||||||
|
{"commitTransaction", wrap<commit_transaction>},
|
||||||
|
{"cancelTransaction", wrap<cancel_transaction>},
|
||||||
{"addListener", wrap<add_listener>},
|
{"addListener", wrap<add_listener>},
|
||||||
{"removeListener", wrap<remove_listener>},
|
{"removeListener", wrap<remove_listener>},
|
||||||
{"removeAllListeners", wrap<remove_all_listeners>},
|
{"removeAllListeners", wrap<remove_all_listeners>},
|
||||||
{"close", wrap<close>},
|
{"close", wrap<close>},
|
||||||
|
{"compact", wrap<compact>},
|
||||||
|
{"deleteModel", wrap<delete_model>},
|
||||||
};
|
};
|
||||||
|
|
||||||
PropertyMap<T> const properties = {
|
PropertyMap<T> const properties = {
|
||||||
|
@ -230,7 +243,9 @@ public:
|
||||||
{"path", {wrap<get_path>, nullptr}},
|
{"path", {wrap<get_path>, nullptr}},
|
||||||
{"schemaVersion", {wrap<get_schema_version>, nullptr}},
|
{"schemaVersion", {wrap<get_schema_version>, nullptr}},
|
||||||
{"schema", {wrap<get_schema>, nullptr}},
|
{"schema", {wrap<get_schema>, nullptr}},
|
||||||
|
{"inMemory", {wrap<get_in_memory>, nullptr}},
|
||||||
{"readOnly", {wrap<get_read_only>, nullptr}},
|
{"readOnly", {wrap<get_read_only>, nullptr}},
|
||||||
|
{"isInTransaction", {wrap<get_is_in_transaction>, nullptr}},
|
||||||
#if REALM_ENABLE_SYNC
|
#if REALM_ENABLE_SYNC
|
||||||
{"syncSession", {wrap<get_sync_session>, nullptr}},
|
{"syncSession", {wrap<get_sync_session>, nullptr}},
|
||||||
#endif
|
#endif
|
||||||
|
@ -367,6 +382,12 @@ void RealmClass<T>::constructor(ContextType ctx, ObjectType this_object, size_t
|
||||||
config.path = js::default_path();
|
config.path = js::default_path();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const String in_memory_string = "inMemory";
|
||||||
|
ValueType in_memory_value = Object::get_property(ctx, object, in_memory_string);
|
||||||
|
if (!Value::is_undefined(ctx, in_memory_value) && Value::validated_to_boolean(ctx, in_memory_value, "inMemory")) {
|
||||||
|
config.in_memory = true;
|
||||||
|
}
|
||||||
|
|
||||||
static const String read_only_string = "readOnly";
|
static const String read_only_string = "readOnly";
|
||||||
ValueType read_only_value = Object::get_property(ctx, object, read_only_string);
|
ValueType read_only_value = Object::get_property(ctx, object, read_only_string);
|
||||||
if (!Value::is_undefined(ctx, read_only_value) && Value::validated_to_boolean(ctx, read_only_value, "readOnly")) {
|
if (!Value::is_undefined(ctx, read_only_value) && Value::validated_to_boolean(ctx, read_only_value, "readOnly")) {
|
||||||
|
@ -376,7 +397,7 @@ void RealmClass<T>::constructor(ContextType ctx, ObjectType this_object, size_t
|
||||||
static const String schema_string = "schema";
|
static const String schema_string = "schema";
|
||||||
ValueType schema_value = Object::get_property(ctx, object, schema_string);
|
ValueType schema_value = Object::get_property(ctx, object, schema_string);
|
||||||
if (!Value::is_undefined(ctx, schema_value)) {
|
if (!Value::is_undefined(ctx, schema_value)) {
|
||||||
ObjectType schema_object = Value::validated_to_object(ctx, schema_value, "schema");
|
ObjectType schema_object = Value::validated_to_array(ctx, schema_value, "schema");
|
||||||
config.schema.emplace(Schema<T>::parse_schema(ctx, schema_object, defaults, constructors));
|
config.schema.emplace(Schema<T>::parse_schema(ctx, schema_object, defaults, constructors));
|
||||||
schema_updated = true;
|
schema_updated = true;
|
||||||
}
|
}
|
||||||
|
@ -390,6 +411,28 @@ void RealmClass<T>::constructor(ContextType ctx, ObjectType this_object, size_t
|
||||||
config.schema_version = 0;
|
config.schema_version = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const String compact_on_launch_string = "shouldCompactOnLaunch";
|
||||||
|
ValueType compact_value = Object::get_property(ctx, object, compact_on_launch_string);
|
||||||
|
if (!Value::is_undefined(ctx, compact_value)) {
|
||||||
|
if (config.schema_mode == SchemaMode::ReadOnly) {
|
||||||
|
throw std::invalid_argument("Cannot set 'shouldCompactOnLaunch' when 'readOnly' is set.");
|
||||||
|
}
|
||||||
|
if (config.sync_config) {
|
||||||
|
throw std::invalid_argument("Cannot set 'shouldCompactOnLaunch' when 'sync' is set.");
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionType should_compact_on_launch_function = Value::validated_to_function(ctx, compact_value, "shouldCompactOnLaunch");
|
||||||
|
config.should_compact_on_launch_function = [=](uint64_t total_bytes, uint64_t unused_bytes) {
|
||||||
|
ValueType arguments[2] = {
|
||||||
|
Value::from_number(ctx, total_bytes),
|
||||||
|
Value::from_number(ctx, unused_bytes)
|
||||||
|
};
|
||||||
|
|
||||||
|
ValueType should_compact = Function<T>::callback(ctx, should_compact_on_launch_function, this_object, 2, arguments);
|
||||||
|
return Value::to_boolean(ctx, should_compact);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
static const String migration_string = "migration";
|
static const String migration_string = "migration";
|
||||||
ValueType migration_value = Object::get_property(ctx, object, migration_string);
|
ValueType migration_value = Object::get_property(ctx, object, migration_string);
|
||||||
if (!Value::is_undefined(ctx, migration_value)) {
|
if (!Value::is_undefined(ctx, migration_value)) {
|
||||||
|
@ -460,13 +503,13 @@ SharedRealm RealmClass<T>::create_shared_realm(ContextType ctx, realm::Realm::Co
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void RealmClass<T>::schema_version(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
void RealmClass<T>::schema_version(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||||
validate_argument_count(argc, 1, 2);
|
args.validate_maximum(2);
|
||||||
|
|
||||||
realm::Realm::Config config;
|
realm::Realm::Config config;
|
||||||
config.path = normalize_realm_path(Value::validated_to_string(ctx, arguments[0]));
|
config.path = normalize_realm_path(Value::validated_to_string(ctx, args[0]));
|
||||||
if (argc == 2) {
|
if (args.count == 2) {
|
||||||
auto encryption_key = Value::validated_to_binary(ctx, arguments[1], "encryptionKey");
|
auto encryption_key = Value::validated_to_binary(ctx, args[1], "encryptionKey");
|
||||||
config.encryption_key.assign(encryption_key.data(), encryption_key.data() + encryption_key.size());
|
config.encryption_key.assign(encryption_key.data(), encryption_key.data() + encryption_key.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -481,18 +524,59 @@ void RealmClass<T>::schema_version(ContextType ctx, FunctionType, ObjectType thi
|
||||||
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void RealmClass<T>::clear_test_state(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
void RealmClass<T>::clear_test_state(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||||
validate_argument_count(argc, 0);
|
args.validate_maximum(0);
|
||||||
|
|
||||||
js::clear_test_state();
|
js::clear_test_state();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void RealmClass<T>::copy_bundled_realm_files(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
void RealmClass<T>::copy_bundled_realm_files(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||||
validate_argument_count(argc, 0);
|
args.validate_maximum(0);
|
||||||
realm::copy_bundled_realm_files();
|
realm::copy_bundled_realm_files();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void RealmClass<T>::delete_file(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||||
|
args.validate_maximum(1);
|
||||||
|
|
||||||
|
ValueType value = args[0];
|
||||||
|
if (!Value::is_object(ctx, value)) {
|
||||||
|
throw std::runtime_error("Invalid argument, expected a Realm configuration object");
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectType object = Value::validated_to_object(ctx, value);
|
||||||
|
realm::Realm::Config config;
|
||||||
|
|
||||||
|
static const String path_string = "path";
|
||||||
|
ValueType path_value = Object::get_property(ctx, object, path_string);
|
||||||
|
if (!Value::is_undefined(ctx, path_value)) {
|
||||||
|
config.path = Value::validated_to_string(ctx, path_value, "path");
|
||||||
|
}
|
||||||
|
else if (config.path.empty()) {
|
||||||
|
config.path = js::default_path();
|
||||||
|
}
|
||||||
|
|
||||||
|
config.path = normalize_realm_path(config.path);
|
||||||
|
|
||||||
|
std::string realm_file_path = config.path;
|
||||||
|
realm::remove_file(realm_file_path);
|
||||||
|
realm::remove_file(realm_file_path + ".lock");
|
||||||
|
realm::remove_file(realm_file_path + ".note");
|
||||||
|
realm::remove_directory(realm_file_path + ".management");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void RealmClass<T>::delete_model(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||||
|
args.validate_maximum(1);
|
||||||
|
ValueType value = args[0];
|
||||||
|
|
||||||
|
SharedRealm& realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||||
|
|
||||||
|
std::string model_name = Value::validated_to_string(ctx, value, "deleteModel");
|
||||||
|
ObjectStore::delete_data_for_object(realm->read_group(), model_name);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void RealmClass<T>::get_default_path(ContextType ctx, ObjectType object, ReturnValue &return_value) {
|
void RealmClass<T>::get_default_path(ContextType ctx, ObjectType object, ReturnValue &return_value) {
|
||||||
return_value.set(realm::js::default_path());
|
return_value.set(realm::js::default_path());
|
||||||
|
@ -528,11 +612,21 @@ void RealmClass<T>::get_schema(ContextType ctx, ObjectType object, ReturnValue &
|
||||||
return_value.set(Schema<T>::object_for_schema(ctx, schema));
|
return_value.set(Schema<T>::object_for_schema(ctx, schema));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void RealmClass<T>::get_in_memory(ContextType ctx, ObjectType object, ReturnValue &return_value) {
|
||||||
|
return_value.set(get_internal<T, RealmClass<T>>(object)->get()->config().in_memory);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void RealmClass<T>::get_read_only(ContextType ctx, ObjectType object, ReturnValue &return_value) {
|
void RealmClass<T>::get_read_only(ContextType ctx, ObjectType object, ReturnValue &return_value) {
|
||||||
return_value.set(get_internal<T, RealmClass<T>>(object)->get()->config().read_only());
|
return_value.set(get_internal<T, RealmClass<T>>(object)->get()->config().read_only());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void RealmClass<T>::get_is_in_transaction(ContextType ctx, ObjectType object, ReturnValue &return_value) {
|
||||||
|
return_value.set(get_internal<T, RealmClass<T>>(object)->get()->is_in_transaction());
|
||||||
|
}
|
||||||
|
|
||||||
#if REALM_ENABLE_SYNC
|
#if REALM_ENABLE_SYNC
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void RealmClass<T>::get_sync_session(ContextType ctx, ObjectType object, ReturnValue &return_value) {
|
void RealmClass<T>::get_sync_session(ContextType ctx, ObjectType object, ReturnValue &return_value) {
|
||||||
|
@ -547,12 +641,17 @@ void RealmClass<T>::get_sync_session(ContextType ctx, ObjectType object, ReturnV
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void RealmClass<T>::wait_for_download_completion(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
void RealmClass<T>::wait_for_download_completion(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||||
validate_argument_count(argc, 2);
|
args.validate_maximum(3);
|
||||||
auto callback_function = Value::validated_to_function(ctx, arguments[1]);
|
auto callback_function = Value::validated_to_function(ctx, args[1 + (args.count == 3)]);
|
||||||
|
|
||||||
|
ValueType session_callback = Value::from_null(ctx);
|
||||||
|
if (args.count == 3) {
|
||||||
|
session_callback = Value::validated_to_function(ctx, args[1]);
|
||||||
|
}
|
||||||
|
|
||||||
#if REALM_ENABLE_SYNC
|
#if REALM_ENABLE_SYNC
|
||||||
auto config_object = Value::validated_to_object(ctx, arguments[0]);
|
auto config_object = Value::validated_to_object(ctx, args[0]);
|
||||||
ValueType sync_config_value = Object::get_property(ctx, config_object, "sync");
|
ValueType sync_config_value = Object::get_property(ctx, config_object, "sync");
|
||||||
if (!Value::is_undefined(ctx, sync_config_value)) {
|
if (!Value::is_undefined(ctx, sync_config_value)) {
|
||||||
realm::Realm::Config config;
|
realm::Realm::Config config;
|
||||||
|
@ -623,6 +722,14 @@ void RealmClass<T>::wait_for_download_completion(ContextType ctx, FunctionType,
|
||||||
std::shared_ptr<SyncUser> user = sync_config->user;
|
std::shared_ptr<SyncUser> user = sync_config->user;
|
||||||
if (user && user->state() != SyncUser::State::Error) {
|
if (user && user->state() != SyncUser::State::Error) {
|
||||||
if (auto session = user->session_for_on_disk_path(config.path)) {
|
if (auto session = user->session_for_on_disk_path(config.path)) {
|
||||||
|
if (!Value::is_null(ctx, session_callback)) {
|
||||||
|
FunctionType session_callback_func = Value::to_function(ctx, session_callback);
|
||||||
|
auto syncSession = create_object<T, SessionClass<T>>(ctx, new WeakSession(session));
|
||||||
|
ValueType callback_arguments[1];
|
||||||
|
callback_arguments[0] = syncSession;
|
||||||
|
Function<T>::callback(protected_ctx, session_callback_func, protected_this, 1, callback_arguments);
|
||||||
|
}
|
||||||
|
|
||||||
if (progressFuncDefined) {
|
if (progressFuncDefined) {
|
||||||
session->register_progress_notifier(std::move(progressFunc), SyncSession::NotifierType::download, false);
|
session->register_progress_notifier(std::move(progressFunc), SyncSession::NotifierType::download, false);
|
||||||
}
|
}
|
||||||
|
@ -651,25 +758,25 @@ void RealmClass<T>::wait_for_download_completion(ContextType ctx, FunctionType,
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void RealmClass<T>::objects(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
void RealmClass<T>::objects(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||||
validate_argument_count(argc, 1);
|
args.validate_maximum(1);
|
||||||
|
|
||||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||||
std::string object_type;
|
std::string object_type;
|
||||||
validated_object_schema_for_value(ctx, realm, arguments[0], object_type);
|
validated_object_schema_for_value(ctx, realm, args[0], object_type);
|
||||||
|
|
||||||
return_value.set(ResultsClass<T>::create_instance(ctx, realm, object_type));
|
return_value.set(ResultsClass<T>::create_instance(ctx, realm, object_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void RealmClass<T>::object_for_primary_key(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
void RealmClass<T>::object_for_primary_key(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||||
validate_argument_count(argc, 2);
|
args.validate_maximum(2);
|
||||||
|
|
||||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||||
std::string object_type;
|
std::string object_type;
|
||||||
auto &object_schema = validated_object_schema_for_value(ctx, realm, arguments[0], object_type);
|
auto &object_schema = validated_object_schema_for_value(ctx, realm, args[0], object_type);
|
||||||
NativeAccessor accessor(ctx, realm, object_schema);
|
NativeAccessor accessor(ctx, realm, object_schema);
|
||||||
auto realm_object = realm::Object::get_for_primary_key(accessor, realm, object_schema, arguments[1]);
|
auto realm_object = realm::Object::get_for_primary_key(accessor, realm, object_schema, args[1]);
|
||||||
|
|
||||||
if (realm_object.is_valid()) {
|
if (realm_object.is_valid()) {
|
||||||
return_value.set(RealmObjectClass<T>::create_instance(ctx, std::move(realm_object)));
|
return_value.set(RealmObjectClass<T>::create_instance(ctx, std::move(realm_object)));
|
||||||
|
@ -680,21 +787,22 @@ void RealmClass<T>::object_for_primary_key(ContextType ctx, FunctionType, Object
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void RealmClass<T>::create(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
void RealmClass<T>::create(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||||
validate_argument_count(argc, 2, 3);
|
args.validate_maximum(3);
|
||||||
|
|
||||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||||
|
realm->verify_open();
|
||||||
std::string object_type;
|
std::string object_type;
|
||||||
auto &object_schema = validated_object_schema_for_value(ctx, realm, arguments[0], object_type);
|
auto &object_schema = validated_object_schema_for_value(ctx, realm, args[0], object_type);
|
||||||
|
|
||||||
ObjectType object = Value::validated_to_object(ctx, arguments[1], "properties");
|
ObjectType object = Value::validated_to_object(ctx, args[1], "properties");
|
||||||
if (Value::is_array(ctx, arguments[1])) {
|
if (Value::is_array(ctx, args[1])) {
|
||||||
object = Schema<T>::dict_for_property_array(ctx, object_schema, object);
|
object = Schema<T>::dict_for_property_array(ctx, object_schema, object);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool update = false;
|
bool update = false;
|
||||||
if (argc == 3) {
|
if (args.count == 3) {
|
||||||
update = Value::validated_to_boolean(ctx, arguments[2], "update");
|
update = Value::validated_to_boolean(ctx, args[2], "update");
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeAccessor accessor(ctx, realm, object_schema);
|
NativeAccessor accessor(ctx, realm, object_schema);
|
||||||
|
@ -703,15 +811,16 @@ void RealmClass<T>::create(ContextType ctx, FunctionType, ObjectType this_object
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void RealmClass<T>::delete_one(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
void RealmClass<T>::delete_one(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||||
validate_argument_count(argc, 1);
|
args.validate_maximum(1);
|
||||||
|
|
||||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||||
|
realm->verify_open();
|
||||||
if (!realm->is_in_transaction()) {
|
if (!realm->is_in_transaction()) {
|
||||||
throw std::runtime_error("Can only delete objects within a transaction.");
|
throw std::runtime_error("Can only delete objects within a transaction.");
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectType arg = Value::validated_to_object(ctx, arguments[0]);
|
ObjectType arg = Value::validated_to_object(ctx, args[0], "object");
|
||||||
|
|
||||||
if (Object::template is_instance<RealmObjectClass<T>>(ctx, arg)) {
|
if (Object::template is_instance<RealmObjectClass<T>>(ctx, arg)) {
|
||||||
auto object = get_internal<T, RealmObjectClass<T>>(arg);
|
auto object = get_internal<T, RealmObjectClass<T>>(arg);
|
||||||
|
@ -750,10 +859,11 @@ void RealmClass<T>::delete_one(ContextType ctx, FunctionType, ObjectType this_ob
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void RealmClass<T>::delete_all(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
void RealmClass<T>::delete_all(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||||
validate_argument_count(argc, 0);
|
args.validate_maximum(0);
|
||||||
|
|
||||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||||
|
realm->verify_open();
|
||||||
|
|
||||||
if (!realm->is_in_transaction()) {
|
if (!realm->is_in_transaction()) {
|
||||||
throw std::runtime_error("Can only delete objects within a transaction.");
|
throw std::runtime_error("Can only delete objects within a transaction.");
|
||||||
|
@ -765,18 +875,18 @@ void RealmClass<T>::delete_all(ContextType ctx, FunctionType, ObjectType this_ob
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void RealmClass<T>::write(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
void RealmClass<T>::write(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||||
validate_argument_count(argc, 1);
|
args.validate_maximum(1);
|
||||||
|
|
||||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||||
FunctionType callback = Value::validated_to_function(ctx, arguments[0]);
|
FunctionType callback = Value::validated_to_function(ctx, args[0]);
|
||||||
|
|
||||||
realm->begin_transaction();
|
realm->begin_transaction();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Function<T>::call(ctx, callback, this_object, 0, nullptr);
|
Function<T>::call(ctx, callback, this_object, 0, nullptr);
|
||||||
}
|
}
|
||||||
catch (std::exception &e) {
|
catch (...) {
|
||||||
realm->cancel_transaction();
|
realm->cancel_transaction();
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
@ -785,54 +895,84 @@ void RealmClass<T>::write(ContextType ctx, FunctionType, ObjectType this_object,
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void RealmClass<T>::add_listener(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
void RealmClass<T>::begin_transaction(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||||
validate_argument_count(argc, 2);
|
args.validate_maximum(0);
|
||||||
|
|
||||||
validated_notification_name(ctx, arguments[0]);
|
|
||||||
auto callback = Value::validated_to_function(ctx, arguments[1]);
|
|
||||||
|
|
||||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||||
if (realm->is_closed()) {
|
realm->begin_transaction();
|
||||||
throw ClosedRealmException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void RealmClass<T>::commit_transaction(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||||
|
args.validate_maximum(0);
|
||||||
|
|
||||||
|
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||||
|
realm->commit_transaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void RealmClass<T>::cancel_transaction(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||||
|
args.validate_maximum(0);
|
||||||
|
|
||||||
|
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||||
|
realm->cancel_transaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void RealmClass<T>::add_listener(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||||
|
args.validate_maximum(2);
|
||||||
|
|
||||||
|
validated_notification_name(ctx, args[0]);
|
||||||
|
auto callback = Value::validated_to_function(ctx, args[1]);
|
||||||
|
|
||||||
|
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||||
|
realm->verify_open();
|
||||||
get_delegate<T>(realm.get())->add_notification(callback);
|
get_delegate<T>(realm.get())->add_notification(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void RealmClass<T>::remove_listener(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
void RealmClass<T>::remove_listener(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||||
validate_argument_count(argc, 2);
|
args.validate_maximum(2);
|
||||||
|
|
||||||
validated_notification_name(ctx, arguments[0]);
|
validated_notification_name(ctx, args[0]);
|
||||||
auto callback = Value::validated_to_function(ctx, arguments[1]);
|
auto callback = Value::validated_to_function(ctx, args[1]);
|
||||||
|
|
||||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||||
if (realm->is_closed()) {
|
realm->verify_open();
|
||||||
throw ClosedRealmException();
|
|
||||||
}
|
|
||||||
get_delegate<T>(realm.get())->remove_notification(callback);
|
get_delegate<T>(realm.get())->remove_notification(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void RealmClass<T>::remove_all_listeners(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
void RealmClass<T>::remove_all_listeners(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||||
validate_argument_count(argc, 0, 1);
|
args.validate_maximum(1);
|
||||||
if (argc) {
|
if (args.count) {
|
||||||
validated_notification_name(ctx, arguments[0]);
|
validated_notification_name(ctx, args[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||||
if (realm->is_closed()) {
|
realm->verify_open();
|
||||||
throw ClosedRealmException();
|
|
||||||
}
|
|
||||||
get_delegate<T>(realm.get())->remove_all_notifications();
|
get_delegate<T>(realm.get())->remove_all_notifications();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void RealmClass<T>::close(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
void RealmClass<T>::close(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||||
validate_argument_count(argc, 0);
|
args.validate_maximum(0);
|
||||||
|
|
||||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||||
realm->close();
|
realm->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void RealmClass<T>::compact(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||||
|
args.validate_maximum(0);
|
||||||
|
|
||||||
|
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||||
|
if (realm->is_in_transaction()) {
|
||||||
|
throw std::runtime_error("Cannot compact a Realm within a transaction.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return_value.set(realm->compact());
|
||||||
|
}
|
||||||
|
|
||||||
} // js
|
} // js
|
||||||
} // realm
|
} // realm
|
||||||
|
|
|
@ -127,12 +127,13 @@ bool RealmObjectClass<T>::set_property(ContextType ctx, ObjectType object, const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NativeAccessor<T> accessor(ctx, realm_object->realm(), realm_object->get_object_schema());
|
||||||
if (!Value::is_valid_for_property(ctx, value, *prop)) {
|
if (!Value::is_valid_for_property(ctx, value, *prop)) {
|
||||||
throw TypeErrorException(util::format("%1.%2", realm_object->get_object_schema().name, property_name),
|
throw TypeErrorException(realm_object->get_object_schema().name, property_name,
|
||||||
js_type_name_for_property_type(prop->type));
|
js_type_name_for_property_type(prop->type),
|
||||||
|
accessor.print(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeAccessor<T> accessor(ctx, realm_object->realm(), realm_object->get_object_schema());
|
|
||||||
realm_object->set_property_value(accessor, property_name, value, true);
|
realm_object->set_property_value(accessor, property_name, value, true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -179,9 +179,9 @@ ObjectSchema Schema<T>::parse_object_schema(ContextType ctx, ObjectType object_s
|
||||||
|
|
||||||
ObjectDefaults object_defaults;
|
ObjectDefaults object_defaults;
|
||||||
ObjectSchema object_schema;
|
ObjectSchema object_schema;
|
||||||
object_schema.name = Object::validated_get_string(ctx, object_schema_object, name_string);
|
object_schema.name = Object::validated_get_string(ctx, object_schema_object, name_string, "ObjectSchema");
|
||||||
|
|
||||||
ObjectType properties_object = Object::validated_get_object(ctx, object_schema_object, properties_string, "ObjectSchema must have a 'properties' object.");
|
ObjectType properties_object = Object::validated_get_object(ctx, object_schema_object, properties_string, "ObjectSchema");
|
||||||
if (Value::is_array(ctx, properties_object)) {
|
if (Value::is_array(ctx, properties_object)) {
|
||||||
uint32_t length = Object::validated_get_length(ctx, properties_object);
|
uint32_t length = Object::validated_get_length(ctx, properties_object);
|
||||||
for (uint32_t i = 0; i < length; i++) {
|
for (uint32_t i = 0; i < length; i++) {
|
||||||
|
@ -232,7 +232,8 @@ ObjectSchema Schema<T>::parse_object_schema(ContextType ctx, ObjectType object_s
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
realm::Schema Schema<T>::parse_schema(ContextType ctx, ObjectType schema_object, ObjectDefaultsMap &defaults, ConstructorMap &constructors) {
|
realm::Schema Schema<T>::parse_schema(ContextType ctx, ObjectType schema_object,
|
||||||
|
ObjectDefaultsMap &defaults, ConstructorMap &constructors) {
|
||||||
std::vector<ObjectSchema> schema;
|
std::vector<ObjectSchema> schema;
|
||||||
uint32_t length = Object::validated_get_length(ctx, schema_object);
|
uint32_t length = Object::validated_get_length(ctx, schema_object);
|
||||||
|
|
||||||
|
|
120
src/js_sync.hpp
120
src/js_sync.hpp
|
@ -160,6 +160,7 @@ class SessionClass : public ClassDefinition<T, WeakSession> {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::string const name = "Session";
|
std::string const name = "Session";
|
||||||
|
using ProgressHandler = void(uint64_t transferred_bytes, uint64_t transferrable_bytes);
|
||||||
|
|
||||||
static FunctionType create_constructor(ContextType);
|
static FunctionType create_constructor(ContextType);
|
||||||
|
|
||||||
|
@ -170,6 +171,8 @@ public:
|
||||||
|
|
||||||
static void simulate_error(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
static void simulate_error(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||||
static void refresh_access_token(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
static void refresh_access_token(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||||
|
static void add_progress_notification(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &);
|
||||||
|
static void remove_progress_notification(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &);
|
||||||
|
|
||||||
PropertyMap<T> const properties = {
|
PropertyMap<T> const properties = {
|
||||||
{"config", {wrap<get_config>, nullptr}},
|
{"config", {wrap<get_config>, nullptr}},
|
||||||
|
@ -180,7 +183,9 @@ public:
|
||||||
|
|
||||||
MethodMap<T> const methods = {
|
MethodMap<T> const methods = {
|
||||||
{"_simulateError", wrap<simulate_error>},
|
{"_simulateError", wrap<simulate_error>},
|
||||||
{"_refreshAccessToken", wrap<refresh_access_token>}
|
{"_refreshAccessToken", wrap<refresh_access_token>},
|
||||||
|
{"addProgressNotification", wrap<add_progress_notification>},
|
||||||
|
{"removeProgressNotification", wrap<remove_progress_notification>},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -307,6 +312,82 @@ void SessionClass<T>::refresh_access_token(ContextType ctx, FunctionType, Object
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void SessionClass<T>::add_progress_notification(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||||
|
validate_argument_count(argc, 3);
|
||||||
|
|
||||||
|
if (auto session = get_internal<T, SessionClass<T>>(this_object)->lock()) {
|
||||||
|
|
||||||
|
std::string direction = Value::validated_to_string(ctx, arguments[0], "direction");
|
||||||
|
std::string mode = Value::validated_to_string(ctx, arguments[1], "mode");
|
||||||
|
SyncSession::NotifierType notifierType;
|
||||||
|
if (direction == "download") {
|
||||||
|
notifierType = SyncSession::NotifierType::download;
|
||||||
|
}
|
||||||
|
else if (direction == "upload") {
|
||||||
|
notifierType = SyncSession::NotifierType::upload;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw std::invalid_argument("Invalid argument 'direction'. Only 'download' and 'upload' progress notification directions are supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_streaming = false;
|
||||||
|
if (mode == "reportIndefinitely") {
|
||||||
|
is_streaming = true;
|
||||||
|
}
|
||||||
|
else if (mode == "forCurrentlyOutstandingWork") {
|
||||||
|
is_streaming = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw std::invalid_argument("Invalid argument 'mode'. Only 'reportIndefinitely' and 'forCurrentlyOutstandingWork' progress notification modes are supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto callback_function = Value::validated_to_function(ctx, arguments[2], "callback");
|
||||||
|
|
||||||
|
Protected<FunctionType> protected_callback(ctx, callback_function);
|
||||||
|
Protected<ObjectType> protected_this(ctx, this_object);
|
||||||
|
Protected<typename T::GlobalContext> protected_ctx(Context<T>::get_global_context(ctx));
|
||||||
|
std::function<ProgressHandler> progressFunc;
|
||||||
|
|
||||||
|
EventLoopDispatcher<ProgressHandler> progress_handler([=](uint64_t transferred_bytes, uint64_t transferrable_bytes) {
|
||||||
|
HANDLESCOPE
|
||||||
|
ValueType callback_arguments[2];
|
||||||
|
callback_arguments[0] = Value::from_number(protected_ctx, transferred_bytes);
|
||||||
|
callback_arguments[1] = Value::from_number(protected_ctx, transferrable_bytes);
|
||||||
|
|
||||||
|
Function<T>::callback(protected_ctx, protected_callback, typename T::Object(), 2, callback_arguments);
|
||||||
|
});
|
||||||
|
|
||||||
|
progressFunc = std::move(progress_handler);
|
||||||
|
|
||||||
|
|
||||||
|
auto registrationToken = session->register_progress_notifier(std::move(progressFunc), notifierType, false);
|
||||||
|
|
||||||
|
auto syncSession = create_object<T, SessionClass<T>>(ctx, new WeakSession(session));
|
||||||
|
PropertyAttributes attributes = ReadOnly | DontEnum | DontDelete;
|
||||||
|
Object::set_property(ctx, callback_function, "_syncSession", syncSession, attributes);
|
||||||
|
Object::set_property(ctx, callback_function, "_registrationToken", Value::from_number(protected_ctx, registrationToken), attributes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void SessionClass<T>::remove_progress_notification(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||||
|
validate_argument_count(argc, 1);
|
||||||
|
auto callback_function = Value::validated_to_function(ctx, arguments[0], "callback");
|
||||||
|
auto syncSessionProp = Object::get_property(ctx, callback_function, "_syncSession");
|
||||||
|
if (Value::is_undefined(ctx, syncSessionProp) || Value::is_null(ctx, syncSessionProp)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto syncSession = Value::validated_to_object(ctx, syncSessionProp);
|
||||||
|
auto registrationToken = Object::get_property(ctx, callback_function, "_registrationToken");
|
||||||
|
|
||||||
|
if (auto session = get_internal<T, SessionClass<T>>(syncSession)->lock()) {
|
||||||
|
auto reg = Value::validated_to_number(ctx, registrationToken);
|
||||||
|
session->unregister_progress_notifier(reg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class SyncClass : public ClassDefinition<T, void*> {
|
class SyncClass : public ClassDefinition<T, void*> {
|
||||||
using GlobalContextType = typename T::GlobalContext;
|
using GlobalContextType = typename T::GlobalContext;
|
||||||
|
@ -328,6 +409,7 @@ public:
|
||||||
static void set_sync_log_level(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
static void set_sync_log_level(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||||
|
|
||||||
// private
|
// private
|
||||||
|
static std::function<SyncBindSessionHandler> session_bind_callback(ContextType ctx, ObjectType sync_constructor);
|
||||||
static void populate_sync_config(ContextType, ObjectType realm_constructor, ObjectType config_object, Realm::Config&);
|
static void populate_sync_config(ContextType, ObjectType realm_constructor, ObjectType config_object, Realm::Config&);
|
||||||
|
|
||||||
// static properties
|
// static properties
|
||||||
|
@ -367,6 +449,24 @@ void SyncClass<T>::set_sync_log_level(ContextType ctx, FunctionType, ObjectType
|
||||||
realm::SyncManager::shared().set_log_level(log_level_2);
|
realm::SyncManager::shared().set_log_level(log_level_2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
std::function<SyncBindSessionHandler> SyncClass<T>::session_bind_callback(ContextType ctx, ObjectType sync_constructor)
|
||||||
|
{
|
||||||
|
Protected<typename T::GlobalContext> protected_ctx(Context<T>::get_global_context(ctx));
|
||||||
|
Protected<ObjectType> protected_sync_constructor(ctx, sync_constructor);
|
||||||
|
return EventLoopDispatcher<SyncBindSessionHandler>([protected_ctx, protected_sync_constructor](const std::string& path, const realm::SyncConfig& config, std::shared_ptr<SyncSession>) {
|
||||||
|
HANDLESCOPE
|
||||||
|
ObjectType user_constructor = Object::validated_get_object(protected_ctx, protected_sync_constructor, "User");
|
||||||
|
FunctionType refreshAccessToken = Object::validated_get_function(protected_ctx, user_constructor, "_refreshAccessToken");
|
||||||
|
|
||||||
|
ValueType arguments[3];
|
||||||
|
arguments[0] = create_object<T, UserClass<T>>(protected_ctx, new SharedUser(config.user));
|
||||||
|
arguments[1] = Value::from_string(protected_ctx, path);
|
||||||
|
arguments[2] = Value::from_string(protected_ctx, config.realm_url);
|
||||||
|
Function::call(protected_ctx, refreshAccessToken, 3, arguments);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void SyncClass<T>::populate_sync_config(ContextType ctx, ObjectType realm_constructor, ObjectType config_object, Realm::Config& config)
|
void SyncClass<T>::populate_sync_config(ContextType ctx, ObjectType realm_constructor, ObjectType config_object, Realm::Config& config)
|
||||||
{
|
{
|
||||||
|
@ -376,21 +476,8 @@ void SyncClass<T>::populate_sync_config(ContextType ctx, ObjectType realm_constr
|
||||||
} else if (!Value::is_undefined(ctx, sync_config_value)) {
|
} else if (!Value::is_undefined(ctx, sync_config_value)) {
|
||||||
auto sync_config_object = Value::validated_to_object(ctx, sync_config_value);
|
auto sync_config_object = Value::validated_to_object(ctx, sync_config_value);
|
||||||
|
|
||||||
ObjectType sync_constructor = Object::validated_get_object(ctx, realm_constructor, std::string("Sync"));
|
ObjectType sync_constructor = Object::validated_get_object(ctx, realm_constructor, "Sync");
|
||||||
Protected<ObjectType> protected_sync(ctx, sync_constructor);
|
auto bind = session_bind_callback(ctx, sync_constructor);
|
||||||
Protected<typename T::GlobalContext> protected_ctx(Context<T>::get_global_context(ctx));
|
|
||||||
|
|
||||||
EventLoopDispatcher<SyncBindSessionHandler> bind([protected_ctx, protected_sync](const std::string& path, const realm::SyncConfig& config, std::shared_ptr<SyncSession>) {
|
|
||||||
HANDLESCOPE
|
|
||||||
ObjectType user_constructor = Object::validated_get_object(protected_ctx, protected_sync, std::string("User"));
|
|
||||||
FunctionType refreshAccessToken = Object::validated_get_function(protected_ctx, user_constructor, std::string("_refreshAccessToken"));
|
|
||||||
|
|
||||||
ValueType arguments[3];
|
|
||||||
arguments[0] = create_object<T, UserClass<T>>(protected_ctx, new SharedUser(config.user));
|
|
||||||
arguments[1] = Value::from_string(protected_ctx, path.c_str());
|
|
||||||
arguments[2] = Value::from_string(protected_ctx, config.realm_url.c_str());
|
|
||||||
Function::call(protected_ctx, refreshAccessToken, 3, arguments);
|
|
||||||
});
|
|
||||||
|
|
||||||
std::function<SyncSessionErrorHandler> error_handler;
|
std::function<SyncSessionErrorHandler> error_handler;
|
||||||
ValueType error_func = Object::get_property(ctx, sync_config_object, "error");
|
ValueType error_func = Object::get_property(ctx, sync_config_object, "error");
|
||||||
|
@ -440,6 +527,5 @@ void SyncClass<T>::populate_sync_config(ContextType ctx, ObjectType realm_constr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // js
|
} // js
|
||||||
} // realm
|
} // realm
|
||||||
|
|
|
@ -20,12 +20,14 @@
|
||||||
|
|
||||||
#include "execution_context_id.hpp"
|
#include "execution_context_id.hpp"
|
||||||
#include "property.hpp"
|
#include "property.hpp"
|
||||||
|
#include "util/format.hpp"
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <realm/binary_data.hpp>
|
#include <realm/binary_data.hpp>
|
||||||
|
#include <realm/string_data.hpp>
|
||||||
#include <realm/util/to_string.hpp>
|
#include <realm/util/to_string.hpp>
|
||||||
|
|
||||||
#if defined(__GNUC__) && !(defined(DEBUG) && DEBUG)
|
#if defined(__GNUC__) && !(defined(DEBUG) && DEBUG)
|
||||||
|
@ -80,18 +82,16 @@ struct Context {
|
||||||
|
|
||||||
class TypeErrorException : public std::invalid_argument {
|
class TypeErrorException : public std::invalid_argument {
|
||||||
public:
|
public:
|
||||||
std::string const& prefix() const { return m_prefix; }
|
TypeErrorException(StringData object_type, StringData property,
|
||||||
std::string const& type() const { return m_type; }
|
std::string const& type, std::string const& value)
|
||||||
|
: std::invalid_argument(util::format("%1.%2 must be of type '%3', got (%4)",
|
||||||
TypeErrorException(std::string prefix, std::string type) :
|
object_type, property, type, value))
|
||||||
std::invalid_argument(prefix + " must be of type: " + type),
|
|
||||||
m_prefix(std::move(prefix)),
|
|
||||||
m_type(std::move(type))
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
private:
|
TypeErrorException(const char *name, std::string const& type, std::string const& value)
|
||||||
std::string m_prefix;
|
: std::invalid_argument(util::format("%1 must be of type '%2', got (%3)",
|
||||||
std::string m_type;
|
name ? name : "JS value", type, value))
|
||||||
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@ -138,8 +138,7 @@ struct Value {
|
||||||
#define VALIDATED(return_t, type) \
|
#define VALIDATED(return_t, type) \
|
||||||
static return_t validated_to_##type(ContextType ctx, const ValueType &value, const char *name = nullptr) { \
|
static return_t validated_to_##type(ContextType ctx, const ValueType &value, const char *name = nullptr) { \
|
||||||
if (!is_##type(ctx, value)) { \
|
if (!is_##type(ctx, value)) { \
|
||||||
std::string prefix = name ? std::string("'") + name + "'" : "JS value"; \
|
throw TypeErrorException(name, #type, to_string(ctx, value)); \
|
||||||
throw TypeErrorException(prefix, #type); \
|
|
||||||
} \
|
} \
|
||||||
return to_##type(ctx, value); \
|
return to_##type(ctx, value); \
|
||||||
}
|
}
|
||||||
|
@ -225,7 +224,7 @@ struct Object {
|
||||||
return Value<T>::validated_to_##type(ctx, get_property(ctx, object, key), std::string(key).c_str()); \
|
return Value<T>::validated_to_##type(ctx, get_property(ctx, object, key), std::string(key).c_str()); \
|
||||||
} \
|
} \
|
||||||
catch (std::invalid_argument &e) { \
|
catch (std::invalid_argument &e) { \
|
||||||
throw message ? std::invalid_argument(message) : e; \
|
throw message ? std::invalid_argument(util::format("Failed to read %1: %2", message, e.what())) : e; \
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
static return_t validated_get_##type(ContextType ctx, const ObjectType &object, uint32_t index, const char *message = nullptr) { \
|
static return_t validated_get_##type(ContextType ctx, const ObjectType &object, uint32_t index, const char *message = nullptr) { \
|
||||||
|
@ -233,7 +232,7 @@ struct Object {
|
||||||
return Value<T>::validated_to_##type(ctx, get_property(ctx, object, index)); \
|
return Value<T>::validated_to_##type(ctx, get_property(ctx, object, index)); \
|
||||||
} \
|
} \
|
||||||
catch (std::invalid_argument &e) { \
|
catch (std::invalid_argument &e) { \
|
||||||
throw message ? std::invalid_argument(message) : e; \
|
throw message ? std::invalid_argument(util::format("Failed to read %1: %2", message, e.what())) : e; \
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,9 @@ template<typename T>
|
||||||
using ClassDefinition = js::ClassDefinition<Types, T>;
|
using ClassDefinition = js::ClassDefinition<Types, T>;
|
||||||
|
|
||||||
using ConstructorType = js::ConstructorType<Types>;
|
using ConstructorType = js::ConstructorType<Types>;
|
||||||
|
using ArgumentsMethodType = js::ArgumentsMethodType<Types>;
|
||||||
using MethodType = js::MethodType<Types>;
|
using MethodType = js::MethodType<Types>;
|
||||||
|
using Arguments = js::Arguments<Types>;
|
||||||
using PropertyType = js::PropertyType<Types>;
|
using PropertyType = js::PropertyType<Types>;
|
||||||
using IndexPropertyType = js::IndexPropertyType<Types>;
|
using IndexPropertyType = js::IndexPropertyType<Types>;
|
||||||
using StringPropertyType = js::StringPropertyType<Types>;
|
using StringPropertyType = js::StringPropertyType<Types>;
|
||||||
|
@ -369,6 +371,19 @@ JSValueRef wrap(JSContextRef ctx, JSObjectRef function, JSObjectRef this_object,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<jsc::ArgumentsMethodType F>
|
||||||
|
JSValueRef wrap(JSContextRef ctx, JSObjectRef function, JSObjectRef this_object, size_t argc, const JSValueRef arguments[], JSValueRef* exception) {
|
||||||
|
jsc::ReturnValue return_value(ctx);
|
||||||
|
try {
|
||||||
|
F(ctx, function, this_object, jsc::Arguments{ctx, argc, arguments}, return_value);
|
||||||
|
return return_value;
|
||||||
|
}
|
||||||
|
catch (std::exception &e) {
|
||||||
|
*exception = jsc::Exception::value(ctx, e);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template<jsc::PropertyType::GetterType F>
|
template<jsc::PropertyType::GetterType F>
|
||||||
JSValueRef wrap(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef* exception) {
|
JSValueRef wrap(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef* exception) {
|
||||||
jsc::ReturnValue return_value(ctx);
|
jsc::ReturnValue return_value(ctx);
|
||||||
|
|
|
@ -31,6 +31,8 @@ using ClassDefinition = js::ClassDefinition<Types, T>;
|
||||||
|
|
||||||
using ConstructorType = js::ConstructorType<Types>;
|
using ConstructorType = js::ConstructorType<Types>;
|
||||||
using MethodType = js::MethodType<Types>;
|
using MethodType = js::MethodType<Types>;
|
||||||
|
using ArgumentsMethodType = js::ArgumentsMethodType<Types>;
|
||||||
|
using Arguments = js::Arguments<Types>;
|
||||||
using PropertyType = js::PropertyType<Types>;
|
using PropertyType = js::PropertyType<Types>;
|
||||||
using IndexPropertyType = js::IndexPropertyType<Types>;
|
using IndexPropertyType = js::IndexPropertyType<Types>;
|
||||||
using StringPropertyType = js::StringPropertyType<Types>;
|
using StringPropertyType = js::StringPropertyType<Types>;
|
||||||
|
@ -308,6 +310,21 @@ void wrap(const v8::FunctionCallbackInfo<v8::Value>& info) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<node::ArgumentsMethodType F>
|
||||||
|
void wrap(const v8::FunctionCallbackInfo<v8::Value>& info) {
|
||||||
|
v8::Isolate* isolate = info.GetIsolate();
|
||||||
|
node::ReturnValue return_value(info.GetReturnValue());
|
||||||
|
auto arguments = node::get_arguments(info);
|
||||||
|
|
||||||
|
try {
|
||||||
|
F(isolate, info.Callee(), info.This(), node::Arguments{isolate, arguments.size(), arguments.data()}, return_value);
|
||||||
|
}
|
||||||
|
catch (std::exception &e) {
|
||||||
|
Nan::ThrowError(node::Exception::value(isolate, e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
template<node::PropertyType::GetterType F>
|
template<node::PropertyType::GetterType F>
|
||||||
void wrap(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
void wrap(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
||||||
v8::Isolate* isolate = info.GetIsolate();
|
v8::Isolate* isolate = info.GetIsolate();
|
||||||
|
|
|
@ -134,4 +134,55 @@ void remove_realm_files_from_directory(const std::string &dir_path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void remove_directory(const std::string &path)
|
||||||
|
{
|
||||||
|
FileSystemRequest exists_req;
|
||||||
|
if (uv_fs_stat(uv_default_loop(), &exists_req, path.c_str(), nullptr) != 0) {
|
||||||
|
if (exists_req.result == UV_ENOENT) {
|
||||||
|
// path doesn't exist, ignore
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
throw UVException(static_cast<uv_errno_t>(exists_req.result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uv_dirent_t dir_entry;
|
||||||
|
FileSystemRequest dir_scan_req;
|
||||||
|
if (uv_fs_scandir(uv_default_loop(), &dir_scan_req, path.c_str(), 0, nullptr) < 0) {
|
||||||
|
throw UVException(static_cast<uv_errno_t>(dir_scan_req.result));
|
||||||
|
}
|
||||||
|
|
||||||
|
while (uv_fs_scandir_next(&dir_scan_req, &dir_entry) != UV_EOF) {
|
||||||
|
std::string dir_entry_path = path + '/' + dir_entry.name;
|
||||||
|
FileSystemRequest delete_req;
|
||||||
|
if (uv_fs_unlink(uv_default_loop(), &delete_req, dir_entry_path.c_str(), nullptr) != 0) {
|
||||||
|
throw UVException(static_cast<uv_errno_t>(delete_req.result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSystemRequest rmdir_req;
|
||||||
|
if (uv_fs_rmdir(uv_default_loop(), &rmdir_req, path.c_str(), nullptr)) {
|
||||||
|
throw UVException(static_cast<uv_errno_t>(rmdir_req.result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void remove_file(const std::string &path)
|
||||||
|
{
|
||||||
|
FileSystemRequest exists_req;
|
||||||
|
if (uv_fs_stat(uv_default_loop(), &exists_req, path.c_str(), nullptr) != 0) {
|
||||||
|
if (exists_req.result == UV_ENOENT) {
|
||||||
|
// path doesn't exist, ignore
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
throw UVException(static_cast<uv_errno_t>(exists_req.result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSystemRequest delete_req;
|
||||||
|
if (uv_fs_unlink(uv_default_loop(), &delete_req, path.c_str(), nullptr) != 0) {
|
||||||
|
throw UVException(static_cast<uv_errno_t>(delete_req.result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // realm
|
} // realm
|
||||||
|
|
|
@ -41,4 +41,10 @@ void copy_bundled_realm_files();
|
||||||
// remove all realm files in the given directory
|
// remove all realm files in the given directory
|
||||||
void remove_realm_files_from_directory(const std::string &directory);
|
void remove_realm_files_from_directory(const std::string &directory);
|
||||||
|
|
||||||
|
// remove file at the given path
|
||||||
|
void remove_file(const std::string &path);
|
||||||
|
|
||||||
|
// remove directory at the given path
|
||||||
|
void remove_directory(const std::string &path);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
15
src/rpc.cpp
15
src/rpc.cpp
|
@ -77,17 +77,16 @@ json RPCWorker::pop_task_result() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void RPCWorker::try_run_task() {
|
void RPCWorker::try_run_task() {
|
||||||
try {
|
|
||||||
// Use a 10 millisecond timeout to keep this thread unblocked.
|
// Use a 10 millisecond timeout to keep this thread unblocked.
|
||||||
auto task = m_tasks.pop_back(10);
|
auto task = m_tasks.try_pop_back(10);
|
||||||
task();
|
if (!task) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
(*task)();
|
||||||
|
|
||||||
// Since this can be called recursively, it must be pushed to the front of the queue *after* running the task.
|
// Since this can be called recursively, it must be pushed to the front of the queue *after* running the task.
|
||||||
m_futures.push_front(task.get_future());
|
m_futures.push_front(task->get_future());
|
||||||
}
|
|
||||||
catch (ConcurrentDequeTimeout &) {
|
|
||||||
// We tried.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RPCWorker::stop() {
|
void RPCWorker::stop() {
|
||||||
|
|
|
@ -120,6 +120,23 @@ module.exports = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
assertThrowsContaining: function(func, expectedMessage) {
|
||||||
|
var caught = false;
|
||||||
|
try {
|
||||||
|
func();
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
caught = true;
|
||||||
|
if (!e.message.includes(expectedMessage)) {
|
||||||
|
throw new TestFailureError(`Expected exception "${expectedMessage}" not thrown - instead caught: "${e}"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!caught) {
|
||||||
|
throw new TestFailureError(`Expected exception "${expectedMessage}" not thrown`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
assertTrue: function(condition, errorMessage) {
|
assertTrue: function(condition, errorMessage) {
|
||||||
if (!condition) {
|
if (!condition) {
|
||||||
throw new TestFailureError(errorMessage || `Condition ${condition} expected to be true`);
|
throw new TestFailureError(errorMessage || `Condition ${condition} expected to be true`);
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
This script creates 3 new objects into a new realm. These are objects are validated to exists by the download api tests.
|
This script creates 3 new objects into a new realm. These are objects are validated to exists by the download api tests.
|
||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
console.log("download-api-helper started");
|
||||||
const username = process.argv[2];
|
const username = process.argv[2];
|
||||||
const realmName = process.argv[3];
|
const realmName = process.argv[3];
|
||||||
const realmModule = process.argv[4];
|
const realmModule = process.argv[4];
|
||||||
|
|
||||||
var Realm = require(realmModule);
|
var Realm = require(realmModule);
|
||||||
Realm.Sync.User.register('http://localhost:9080', username, 'password', (error, user) => {
|
|
||||||
if (error) {
|
function createObjects(user) {
|
||||||
console.log(error);
|
|
||||||
process.exit(-2);
|
|
||||||
} else {
|
|
||||||
const config = {
|
const config = {
|
||||||
sync: { user, url: `realm://localhost:9080/~/${realmName}`, error: err => console.log(err) },
|
sync: { user,
|
||||||
|
url: `realm://localhost:9080/~/${realmName}`,
|
||||||
|
error: err => console.log(err)
|
||||||
|
},
|
||||||
schema: [{ name: 'Dog', properties: { name: 'string' } }]
|
schema: [{ name: 'Dog', properties: { name: 'string' } }]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -29,6 +29,22 @@ Realm.Sync.User.register('http://localhost:9080', username, 'password', (error,
|
||||||
console.log("Dogs count " + realm.objects('Dog').length);
|
console.log("Dogs count " + realm.objects('Dog').length);
|
||||||
setTimeout(() => process.exit(0), 3000);
|
setTimeout(() => process.exit(0), 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Realm.Sync.User.register('http://localhost:9080', username, 'password', (error, registeredUser) => {
|
||||||
|
if (error) {
|
||||||
|
const registrationError = JSON.stringify(error);
|
||||||
|
Realm.Sync.User.login('http://localhost:9080', username, 'password', (err, loggedUser) => {
|
||||||
|
if (err) {
|
||||||
|
const loginError = JSON.stringify(err);
|
||||||
|
console.error("download-api-helper failed:\n User.register() error:\n" + err + "\n" + registrationError + "\n User.login() error:\n" + loginError);
|
||||||
|
process.exit(-2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
createObjects(loggedUser);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
createObjects(registeredUser);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,12 @@ if (!(typeof process === 'object' && process.platform === 'win32')) {
|
||||||
if (Realm.Sync) {
|
if (Realm.Sync) {
|
||||||
TESTS.UserTests = require('./user-tests');
|
TESTS.UserTests = require('./user-tests');
|
||||||
TESTS.SessionTests = require('./session-tests');
|
TESTS.SessionTests = require('./session-tests');
|
||||||
|
|
||||||
|
// FIXME: Permission tests currently fail in chrome debugging mode.
|
||||||
|
if (typeof navigator === 'undefined' ||
|
||||||
|
!/Chrome/.test(navigator.userAgent)) { // eslint-disable-line no-undef
|
||||||
|
TESTS.PermissionTests = require('./permission-tests');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function node_require(module) { return require(module); }
|
function node_require(module) { return require(module); }
|
||||||
|
|
|
@ -158,4 +158,170 @@ module.exports = {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
testDeleteModelMigration: function() {
|
||||||
|
const schema = [{
|
||||||
|
name: 'TestObject',
|
||||||
|
properties: {
|
||||||
|
prop0: 'string',
|
||||||
|
prop1: 'int',
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
var realm = new Realm({schema: schema});
|
||||||
|
|
||||||
|
realm.write(function() {
|
||||||
|
realm.create('TestObject', ['stringValue', 1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
realm.close();
|
||||||
|
|
||||||
|
realm = new Realm({schema: [], schemaVersion: 1});
|
||||||
|
TestCase.assertEqual(realm.schema.length, 0); // no models
|
||||||
|
realm.close(); // this won't delete the model
|
||||||
|
|
||||||
|
realm = new Realm({schema: schema, schemaVersion: 2});
|
||||||
|
TestCase.assertEqual(realm.objects('TestObject').length, 1); // the model objects are still there
|
||||||
|
realm.close();
|
||||||
|
|
||||||
|
// now delete the model explicitly, which should delete the objects too
|
||||||
|
realm = new Realm({schema: [], schemaVersion: 3, migration: function(oldRealm, newRealm) {
|
||||||
|
newRealm.deleteModel('TestObject');
|
||||||
|
}});
|
||||||
|
|
||||||
|
TestCase.assertEqual(realm.schema.length, 0); // no models
|
||||||
|
|
||||||
|
realm.close();
|
||||||
|
|
||||||
|
realm = new Realm({schema: schema, schemaVersion: 4});
|
||||||
|
|
||||||
|
TestCase.assertEqual(realm.objects('TestObject').length, 0);
|
||||||
|
|
||||||
|
realm.close();
|
||||||
|
},
|
||||||
|
|
||||||
|
testDeleteModelInSchema: function() {
|
||||||
|
const schema = [{
|
||||||
|
name: 'TestObject',
|
||||||
|
properties: {
|
||||||
|
prop0: 'string',
|
||||||
|
prop1: 'int',
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
var realm = new Realm({schema: schema});
|
||||||
|
|
||||||
|
realm.write(function() {
|
||||||
|
realm.create('TestObject', ['stringValue', 1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
realm.close();
|
||||||
|
|
||||||
|
|
||||||
|
// now delete the model explicitly, but it should remain as it's still in the schema
|
||||||
|
// only the rows should get deleted
|
||||||
|
realm = new Realm({schema: schema, schemaVersion: 1, migration: function(oldRealm, newRealm) {
|
||||||
|
newRealm.deleteModel('TestObject');
|
||||||
|
}});
|
||||||
|
|
||||||
|
TestCase.assertEqual(realm.schema.length, 1); // model should remain
|
||||||
|
TestCase.assertEqual(realm.objects('TestObject').length, 0); // objects should be gone
|
||||||
|
|
||||||
|
realm.close();
|
||||||
|
|
||||||
|
realm = new Realm({schema: schema, schemaVersion: 2});
|
||||||
|
TestCase.assertEqual(realm.objects('TestObject').length, 0);
|
||||||
|
|
||||||
|
realm.close();
|
||||||
|
},
|
||||||
|
|
||||||
|
testDeleteModelIgnoreNotExisting: function() {
|
||||||
|
const schema = [{
|
||||||
|
name: 'TestObject',
|
||||||
|
properties: {
|
||||||
|
prop0: 'string',
|
||||||
|
prop1: 'int',
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
var realm = new Realm({schema: schema});
|
||||||
|
|
||||||
|
realm.write(function() {
|
||||||
|
realm.create('TestObject', ['stringValue', 1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
realm.close();
|
||||||
|
|
||||||
|
// non-existing models should be ignore on delete
|
||||||
|
realm = new Realm({schema: schema, schemaVersion: 1, migration: function(oldRealm, newRealm) {
|
||||||
|
newRealm.deleteModel('NonExistingModel');
|
||||||
|
}});
|
||||||
|
|
||||||
|
realm.close();
|
||||||
|
|
||||||
|
realm = new Realm({schema: schema, schemaVersion: 2});
|
||||||
|
TestCase.assertEqual(realm.objects('TestObject').length, 1);
|
||||||
|
|
||||||
|
realm.close();
|
||||||
|
},
|
||||||
|
|
||||||
|
testDeleteModelWithRelationship: function() {
|
||||||
|
const ShipSchema = {
|
||||||
|
name: 'Ship',
|
||||||
|
properties: {
|
||||||
|
ship_name: 'string',
|
||||||
|
captain: 'Captain'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const CaptainSchema = {
|
||||||
|
name: 'Captain',
|
||||||
|
properties: {
|
||||||
|
captain_name: 'string',
|
||||||
|
ships: { type: 'linkingObjects', objectType: 'Ship', property: 'captain' }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var realm = new Realm({schema: [ShipSchema, CaptainSchema]});
|
||||||
|
|
||||||
|
realm.write(function() {
|
||||||
|
realm.create('Ship', {
|
||||||
|
ship_name: 'My Ship',
|
||||||
|
captain: {
|
||||||
|
captain_name: 'John Doe'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
TestCase.assertEqual(realm.objects('Captain').length, 1);
|
||||||
|
TestCase.assertEqual(realm.objects('Ship').length, 1);
|
||||||
|
TestCase.assertEqual(realm.objects('Ship')[0].captain.captain_name, "John Doe");
|
||||||
|
TestCase.assertEqual(realm.objects('Captain')[0].ships[0].ship_name, "My Ship");
|
||||||
|
|
||||||
|
realm.close();
|
||||||
|
|
||||||
|
realm = new Realm({schema: [ShipSchema, CaptainSchema], schemaVersion: 1, migration: function(oldRealm, newRealm) {
|
||||||
|
TestCase.assertThrows(function(e) {
|
||||||
|
// deleting a model which is target of linkingObjects results in an exception
|
||||||
|
newRealm.deleteModel('Captain');
|
||||||
|
console.log(e);
|
||||||
|
}, "Table is target of cross-table link columns");
|
||||||
|
}});
|
||||||
|
|
||||||
|
TestCase.assertEqual(realm.objects('Captain').length, 1);
|
||||||
|
TestCase.assertEqual(realm.objects('Ship').length, 1);
|
||||||
|
|
||||||
|
realm.close();
|
||||||
|
|
||||||
|
realm = new Realm({schema: [ShipSchema, CaptainSchema], schemaVersion: 2, migration: function(oldRealm, newRealm) {
|
||||||
|
// deleting a model which isn't target of linkingObjects works fine
|
||||||
|
newRealm.deleteModel('Ship');
|
||||||
|
}});
|
||||||
|
|
||||||
|
TestCase.assertEqual(realm.objects('Captain').length, 1);
|
||||||
|
TestCase.assertEqual(realm.objects('Ship').length, 0);
|
||||||
|
TestCase.assertEqual(realm.objects('Captain')[0].ships.length, 0);
|
||||||
|
|
||||||
|
realm.close();
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Copyright 2017 Realm Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Realm = require('realm');
|
||||||
|
var TestCase = require('./asserts');
|
||||||
|
|
||||||
|
function uuid() {
|
||||||
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||||
|
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
||||||
|
return v.toString(16);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createUsersWithTestRealms(count) {
|
||||||
|
const createUserWithTestRealm = username => new Promise((resolve, reject) => {
|
||||||
|
Realm.Sync.User.register('http://localhost:9080', username, 'password', (error, user) => {
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
new Realm({ sync: { user, url: 'realm://localhost:9080/~/test'}}).close();
|
||||||
|
resolve(user);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
// Generate some usernames
|
||||||
|
const usernames = new Array(count).fill(undefined).map(uuid);
|
||||||
|
|
||||||
|
// And turn them into users and realms
|
||||||
|
const userPromises = usernames.map(createUserWithTestRealm);
|
||||||
|
return Promise.all(userPromises);
|
||||||
|
}
|
||||||
|
|
||||||
|
function wait(t) {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, t));
|
||||||
|
}
|
||||||
|
|
||||||
|
function repeatUntil(fn, predicate) {
|
||||||
|
let retries = 0
|
||||||
|
function check() {
|
||||||
|
if (retries > 3) {
|
||||||
|
return Promise.reject(new Error("operation timed out"));
|
||||||
|
}
|
||||||
|
++retries;
|
||||||
|
return fn().then(x => predicate(x) ? x : wait(100).then(check));
|
||||||
|
}
|
||||||
|
return check;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
testApplyAndGetGrantedPermissions() {
|
||||||
|
return createUsersWithTestRealms(1)
|
||||||
|
.then(([user]) => {
|
||||||
|
return user.applyPermissions({ userId: '*' }, `/${user.identity}/test`, 'read')
|
||||||
|
.then(repeatUntil(() => user.getGrantedPermissions('any'),
|
||||||
|
permissions => permissions.length > 1))
|
||||||
|
.then(permissions => {
|
||||||
|
TestCase.assertEqual(permissions[1].path, `/${user.identity}/test`);
|
||||||
|
TestCase.assertEqual(permissions[1].mayRead, true);
|
||||||
|
TestCase.assertEqual(permissions[1].mayWrite, false);
|
||||||
|
TestCase.assertEqual(permissions[1].mayManage, false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
testOfferPermissions() {
|
||||||
|
return createUsersWithTestRealms(2)
|
||||||
|
.then(([user1, user2]) => {
|
||||||
|
return user1.offerPermissions(`/${user1.identity}/test`, 'read')
|
||||||
|
.then(token => user2.acceptPermissionOffer(token))
|
||||||
|
.then(realmUrl => {
|
||||||
|
TestCase.assertEqual(realmUrl, `/${user1.identity}/test`);
|
||||||
|
return realmUrl;
|
||||||
|
})
|
||||||
|
.then(repeatUntil(() => user2.getGrantedPermissions('any'),
|
||||||
|
permissions => permissions.length > 1))
|
||||||
|
.then(permissions => {
|
||||||
|
TestCase.assertEqual(permissions[1].path, `/${user1.identity}/test`);
|
||||||
|
TestCase.assertEqual(permissions[1].mayRead, true);
|
||||||
|
TestCase.assertEqual(permissions[1].mayWrite, false);
|
||||||
|
TestCase.assertEqual(permissions[1].mayManage, false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
testInvalidatePermissionOffer() {
|
||||||
|
return createUsersWithTestRealms(2)
|
||||||
|
.then(([user1, user2]) => {
|
||||||
|
user1.offerPermissions(`/${user1.identity}/test`, 'read')
|
||||||
|
.then((token) => {
|
||||||
|
return user1.invalidatePermissionOffer(token)
|
||||||
|
// Since we don't yet support notification when the invalidation has gone through,
|
||||||
|
// wait for a bit and hope the server is done processing.
|
||||||
|
.then(wait(100))
|
||||||
|
.then(user2.acceptPermissionOffer(token))
|
||||||
|
// We want the call to fail, i.e. the catch() below should be called.
|
||||||
|
.then(() => { throw new Error("User was able to accept an invalid permission offer token"); })
|
||||||
|
.catch(error => {
|
||||||
|
try {
|
||||||
|
TestCase.assertEqual(error.message, 'The permission offer is expired.');
|
||||||
|
TestCase.assertEqual(error.statusCode, 701);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
throw new Error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -421,29 +421,40 @@ module.exports = {
|
||||||
},
|
},
|
||||||
|
|
||||||
testAddListener: function() {
|
testAddListener: function() {
|
||||||
return new Promise((resolve, _reject) => {
|
if (typeof navigator !== 'undefined' && /Chrome/.test(navigator.userAgent)) { // eslint-disable-line no-undef
|
||||||
var realm = new Realm({ schema: [schemas.TestObject] });
|
// FIXME: async callbacks do not work correctly in Chrome debugging mode
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const realm = new Realm({ schema: [schemas.TestObject] });
|
||||||
realm.write(() => {
|
realm.write(() => {
|
||||||
realm.create('TestObject', { doubleCol: 1 });
|
realm.create('TestObject', { doubleCol: 1 });
|
||||||
realm.create('TestObject', { doubleCol: 2 });
|
realm.create('TestObject', { doubleCol: 2 });
|
||||||
realm.create('TestObject', { doubleCol: 3 });
|
realm.create('TestObject', { doubleCol: 3 });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let resolve, first = true;
|
||||||
|
return new Promise((r, _reject) => {
|
||||||
|
resolve = r;
|
||||||
realm.objects('TestObject').addListener((testObjects, changes) => {
|
realm.objects('TestObject').addListener((testObjects, changes) => {
|
||||||
// TODO: First notification is empty, so perform these
|
if (first) {
|
||||||
// assertions on the second call. However, there is a race condition
|
TestCase.assertEqual(testObjects.length, 3);
|
||||||
// in React Native, so find a way to do this in a robust way.
|
TestCase.assertEqual(changes.insertions.length, 0);
|
||||||
//TestCase.assertEqual(testObjects.length, 4);
|
}
|
||||||
//TestCase.assertEqual(changes.insertions.length, 1);
|
else {
|
||||||
|
TestCase.assertEqual(testObjects.length, 4);
|
||||||
|
TestCase.assertEqual(changes.insertions.length, 1);
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
|
}).then(() => {
|
||||||
|
return new Promise((r, _reject) => {
|
||||||
realm.write(() => {
|
realm.write(() => {
|
||||||
realm.create('TestObject', { doubleCol: 1 });
|
realm.create('TestObject', { doubleCol: 1 });
|
||||||
});
|
});
|
||||||
})
|
resolve = r;
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -119,6 +119,13 @@ exports.StringPrimary = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.StringOnly = {
|
||||||
|
name: 'StringOnlyObject',
|
||||||
|
properties: {
|
||||||
|
stringCol: 'string',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
exports.AllTypes = {
|
exports.AllTypes = {
|
||||||
name: 'AllTypesObject',
|
name: 'AllTypesObject',
|
||||||
primaryKey: 'primaryCol',
|
primaryKey: 'primaryCol',
|
||||||
|
|
|
@ -26,7 +26,7 @@ const Realm = require('realm');
|
||||||
const TestCase = require('./asserts');
|
const TestCase = require('./asserts');
|
||||||
|
|
||||||
const isNodeProccess = (typeof process === 'object' && process + '' === '[object process]');
|
const isNodeProccess = (typeof process === 'object' && process + '' === '[object process]');
|
||||||
console.log("isnode " + isNodeProccess + " typeof " + (typeof(process) === 'object'));
|
|
||||||
function node_require(module) {
|
function node_require(module) {
|
||||||
return require(module);
|
return require(module);
|
||||||
}
|
}
|
||||||
|
@ -84,13 +84,23 @@ function runOutOfProcess(nodeJsFilePath) {
|
||||||
fs.appendFileSync(tmpFile.fd, content, { encoding: 'utf8' });
|
fs.appendFileSync(tmpFile.fd, content, { encoding: 'utf8' });
|
||||||
nodeArgs[0] = tmpFile.name;
|
nodeArgs[0] = tmpFile.name;
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
console.log('runOutOfProcess command\n node ' + nodeArgs.join(" "));
|
||||||
const child = execFile('node', nodeArgs, { cwd: tmpDir.name }, (error, stdout, stderr) => {
|
const child = execFile('node', nodeArgs, { cwd: tmpDir.name }, (error, stdout, stderr) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(new Error(`Error executing ${nodeJsFilePath} Error: ${error}`));
|
console.error("runOutOfProcess failed\n" + error);
|
||||||
|
reject(new Error(`Running ${nodeJsFilePath} failed. error: ${error}`));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('runOutOfProcess success\n' + stdout);
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
})
|
}
|
||||||
|
catch (e) {
|
||||||
|
reject(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -150,7 +160,7 @@ module.exports = {
|
||||||
const realmName = uuid();
|
const realmName = uuid();
|
||||||
const expectedObjectsCount = 3;
|
const expectedObjectsCount = 3;
|
||||||
|
|
||||||
runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return promisifiedLogin('http://localhost:9080', username, 'password').then(user => {
|
return promisifiedLogin('http://localhost:9080', username, 'password').then(user => {
|
||||||
const accessTokenRefreshed = this;
|
const accessTokenRefreshed = this;
|
||||||
|
@ -186,9 +196,9 @@ module.exports = {
|
||||||
const realmName = uuid();
|
const realmName = uuid();
|
||||||
const expectedObjectsCount = 3;
|
const expectedObjectsCount = 3;
|
||||||
|
|
||||||
runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return promisifiedLogin('http://localhost:9080', username, 'password').then(user => {
|
return Realm.Sync.User.login('http://localhost:9080', username, 'password').then(user => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const accessTokenRefreshed = this;
|
const accessTokenRefreshed = this;
|
||||||
let successCounter = 0;
|
let successCounter = 0;
|
||||||
|
@ -239,9 +249,9 @@ module.exports = {
|
||||||
const realmName = uuid();
|
const realmName = uuid();
|
||||||
const expectedObjectsCount = 3;
|
const expectedObjectsCount = 3;
|
||||||
|
|
||||||
runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return promisifiedLogin('http://localhost:9080', username, 'password').then(user => {
|
return Realm.Sync.User.login('http://localhost:9080', username, 'password').then(user => {
|
||||||
const accessTokenRefreshed = this;
|
const accessTokenRefreshed = this;
|
||||||
let successCounter = 0;
|
let successCounter = 0;
|
||||||
let progressNotificationCalled = false;
|
let progressNotificationCalled = false;
|
||||||
|
@ -275,9 +285,9 @@ module.exports = {
|
||||||
const realmName = uuid();
|
const realmName = uuid();
|
||||||
const expectedObjectsCount = 3;
|
const expectedObjectsCount = 3;
|
||||||
|
|
||||||
runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return promisifiedLogin('http://localhost:9080', username, 'password').then(user => {
|
return Realm.Sync.User.login('http://localhost:9080', username, 'password').then(user => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let progressNotificationCalled = false;
|
let progressNotificationCalled = false;
|
||||||
let config = {
|
let config = {
|
||||||
|
@ -322,9 +332,9 @@ module.exports = {
|
||||||
const realmName = uuid();
|
const realmName = uuid();
|
||||||
const expectedObjectsCount = 3;
|
const expectedObjectsCount = 3;
|
||||||
|
|
||||||
runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return promisifiedLogin('http://localhost:9080', username, 'password').then(user => {
|
return Realm.Sync.User.login('http://localhost:9080', username, 'password').then(user => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const accessTokenRefreshed = this;
|
const accessTokenRefreshed = this;
|
||||||
let successCounter = 0;
|
let successCounter = 0;
|
||||||
|
@ -429,7 +439,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
|
|
||||||
testErrorHandling() {
|
testErrorHandling() {
|
||||||
return promisifiedRegister('http://localhost:9080', uuid(), 'password').then(user => {
|
return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password').then(user => {
|
||||||
return new Promise((resolve, _reject) => {
|
return new Promise((resolve, _reject) => {
|
||||||
const config = { sync: { user, url: 'realm://localhost:9080/~/myrealm' } };
|
const config = { sync: { user, url: 'realm://localhost:9080/~/myrealm' } };
|
||||||
config.sync.error = (sender, error) => {
|
config.sync.error = (sender, error) => {
|
||||||
|
@ -449,5 +459,191 @@ module.exports = {
|
||||||
session._simulateError(123, 'simulated error');
|
session._simulateError(123, 'simulated error');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
testProgressNotificationsForRealmConstructor() {
|
||||||
|
if (!isNodeProccess) {
|
||||||
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const username = uuid();
|
||||||
|
const realmName = uuid();
|
||||||
|
|
||||||
|
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||||
|
.then(() => {
|
||||||
|
return Realm.Sync.User.login('http://localhost:9080', username, 'password').then(user => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let config = {
|
||||||
|
sync: {
|
||||||
|
user,
|
||||||
|
url: `realm://localhost:9080/~/${realmName}`
|
||||||
|
},
|
||||||
|
schema: [{ name: 'Dog', properties: { name: 'string' } }],
|
||||||
|
};
|
||||||
|
|
||||||
|
let realm = new Realm(config);
|
||||||
|
const progressCallback = (transferred, total) => {
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
realm.syncSession.addProgressNotification('download', 'reportIndefinitely', progressCallback);
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
reject("Progress Notifications API failed to call progress callback for Realm constructor");
|
||||||
|
}, 5000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
testProgressNotificationsUnregisterForRealmConstructor() {
|
||||||
|
if (!isNodeProccess) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
const username = uuid();
|
||||||
|
const realmName = uuid();
|
||||||
|
|
||||||
|
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||||
|
.then(() => {
|
||||||
|
return Realm.Sync.User.login('http://localhost:9080', username, 'password').then(user => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let config = {
|
||||||
|
sync: {
|
||||||
|
user,
|
||||||
|
url: `realm://localhost:9080/~/${realmName}`
|
||||||
|
},
|
||||||
|
schema: [{ name: 'Dog', properties: { name: 'string' } }],
|
||||||
|
};
|
||||||
|
|
||||||
|
let realm = new Realm(config);
|
||||||
|
let unregisterFunc;
|
||||||
|
|
||||||
|
let writeDataFunc = () => {
|
||||||
|
realm.write(() => {
|
||||||
|
for (let i = 1; i <= 3; i++) {
|
||||||
|
realm.create('Dog', { name: `Lassy ${i}` });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let syncFinished = false;
|
||||||
|
let failOnCall = false;
|
||||||
|
const progressCallback = (transferred, total) => {
|
||||||
|
if (failOnCall) {
|
||||||
|
reject(new Error("Progress callback should not be called after removeProgressNotification"));
|
||||||
|
}
|
||||||
|
|
||||||
|
syncFinished = transferred === total;
|
||||||
|
|
||||||
|
//unregister and write some new data.
|
||||||
|
if (syncFinished) {
|
||||||
|
failOnCall = true;
|
||||||
|
unregisterFunc();
|
||||||
|
|
||||||
|
//use second callback to wait for sync finished
|
||||||
|
realm.syncSession.addProgressNotification('upload', 'reportIndefinitely', (x, y) => {
|
||||||
|
if (x === y) {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
writeDataFunc();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
realm.syncSession.addProgressNotification('upload', 'reportIndefinitely', progressCallback);
|
||||||
|
|
||||||
|
unregisterFunc = () => {
|
||||||
|
realm.syncSession.removeProgressNotification(progressCallback);
|
||||||
|
};
|
||||||
|
|
||||||
|
writeDataFunc();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
testProgressNotificationsForRealmOpen2() {
|
||||||
|
if (!isNodeProccess) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
const username = uuid();
|
||||||
|
const realmName = uuid();
|
||||||
|
|
||||||
|
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||||
|
.then(() => {
|
||||||
|
return Realm.Sync.User.login('http://localhost:9080', username, 'password').then(user => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let config = {
|
||||||
|
sync: {
|
||||||
|
user,
|
||||||
|
url: `realm://localhost:9080/~/${realmName}`
|
||||||
|
},
|
||||||
|
schema: [{ name: 'Dog', properties: { name: 'string' } }],
|
||||||
|
};
|
||||||
|
|
||||||
|
let progressCalled = false;
|
||||||
|
Realm.open(config)
|
||||||
|
.progress((transferred, total) => {
|
||||||
|
progressCalled = true;
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
TestCase.assertTrue(progressCalled);
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.catch((e) => reject(e));
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
reject("Progress Notifications API failed to call progress callback for Realm constructor");
|
||||||
|
}, 5000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
testProgressNotificationsForRealmOpenAsync2() {
|
||||||
|
if (!isNodeProccess) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
const username = uuid();
|
||||||
|
const realmName = uuid();
|
||||||
|
|
||||||
|
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||||
|
.then(() => {
|
||||||
|
return Realm.Sync.User.login('http://localhost:9080', username, 'password').then(user => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let config = {
|
||||||
|
sync: {
|
||||||
|
user,
|
||||||
|
url: `realm://localhost:9080/~/${realmName}`
|
||||||
|
},
|
||||||
|
schema: [{ name: 'Dog', properties: { name: 'string' } }],
|
||||||
|
};
|
||||||
|
|
||||||
|
let progressCalled = false;
|
||||||
|
|
||||||
|
Realm.openAsync(config,
|
||||||
|
(error, realm) => {
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestCase.assertTrue(progressCalled);
|
||||||
|
resolve();
|
||||||
|
},
|
||||||
|
(transferred, total) => {
|
||||||
|
progressCalled = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
reject("Progress Notifications API failed to call progress callback for Realm constructor");
|
||||||
|
}, 5000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,10 +10,12 @@
|
||||||
"needle": "^1.3.0",
|
"needle": "^1.3.0",
|
||||||
"terminate": "^1.0.8",
|
"terminate": "^1.0.8",
|
||||||
"tmp": "^0.0.30",
|
"tmp": "^0.0.30",
|
||||||
"url-parse": "^1.1.7"
|
"url-parse": "^1.1.7",
|
||||||
|
"typescript": "^2.5.2"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "jasmine spec/unit_tests.js",
|
"check-typescript" : "tsc --noEmit --alwaysStrict ./../lib/index.d.ts",
|
||||||
"test-sync-integration": "jasmine spec/sync_integration_tests.js"
|
"test": "npm run check-typescript && jasmine spec/unit_tests.js",
|
||||||
|
"test-sync-integration": "npm run check-typescript && jasmine spec/sync_integration_tests.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ async function runTests() {
|
||||||
await runTest(suiteName, testName);
|
await runTest(suiteName, testName);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
itemTest.ele('error', {'message': ''}, e.message);
|
itemTest.ele('error', {'message': e.message, 'stacktrace': e.stack}, e.toString());
|
||||||
nbrFailures++;
|
nbrFailures++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,6 +168,7 @@ extern NSMutableArray *RCTGetModuleClasses(void);
|
||||||
+ (void)waitForCondition:(BOOL *)condition description:(NSString *)description {
|
+ (void)waitForCondition:(BOOL *)condition description:(NSString *)description {
|
||||||
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
|
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
|
||||||
NSDate *timeout = [NSDate dateWithTimeIntervalSinceNow:30.0];
|
NSDate *timeout = [NSDate dateWithTimeIntervalSinceNow:30.0];
|
||||||
|
RCTBridge *bridge = [self currentBridge];
|
||||||
|
|
||||||
while (!*condition) {
|
while (!*condition) {
|
||||||
if ([timeout timeIntervalSinceNow] < 0) {
|
if ([timeout timeIntervalSinceNow] < 0) {
|
||||||
|
@ -180,6 +181,7 @@ extern NSMutableArray *RCTGetModuleClasses(void);
|
||||||
[runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
|
[runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
|
||||||
[runLoop runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
|
[runLoop runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
|
||||||
[NSThread sleepForTimeInterval:0.01]; // Bad things may happen without some sleep.
|
[NSThread sleepForTimeInterval:0.01]; // Bad things may happen without some sleep.
|
||||||
|
[bridge.eventDispatcher sendAppEventWithName:@"realm-dummy" body:nil]; // Ensure RN has an event loop running
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
const isNodeProccess = (typeof process === 'object' && process + '' === '[object process]');
|
||||||
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
@ -28,7 +29,11 @@ const Realm = require('realm');
|
||||||
const RealmTests = require('../js');
|
const RealmTests = require('../js');
|
||||||
|
|
||||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000;
|
jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000;
|
||||||
const isDebuggerAttached = typeof v8debug === 'object';
|
let isDebuggerAttached = typeof v8debug === 'object';
|
||||||
|
if (!isDebuggerAttached && isNodeProccess) {
|
||||||
|
isDebuggerAttached = /--debug|--inspect/.test(process.execArgv.join(' '));
|
||||||
|
}
|
||||||
|
|
||||||
if (isDebuggerAttached) {
|
if (isDebuggerAttached) {
|
||||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 3000000;
|
jasmine.DEFAULT_TIMEOUT_INTERVAL = 3000000;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue