mirror of
https://github.com/status-im/realm-js.git
synced 2025-01-12 07:14:23 +00:00
Merge branch '2.3.x' of github.com:realm/realm-js into jas/backlink-queries
This commit is contained in:
commit
2e4e0df01c
67
CHANGELOG.md
67
CHANGELOG.md
@ -1,4 +1,4 @@
|
||||
2.3.0 Release notes (2018-2-19)
|
||||
2.3.0 Release notes (2018-3-7)
|
||||
=============================================================
|
||||
### Breaking changes
|
||||
* [Sync] Sync protocol changed to version 24.
|
||||
@ -17,12 +17,69 @@
|
||||
* Added support for queries over named backlinks (#1498/#1660).
|
||||
- Example syntax: `parents.age > 25` and `parents.@count == 2`.
|
||||
* [Sync] Added `Realm.Results.subscribe()` to subscribe to partial synced Realms.
|
||||
* [Sync] Added class `Realm.Sync.Subscription` to support partial synced Realms.
|
||||
* [Sync] Added class `Realm.Sync.Subscription` and enum `Realm.Sync.SubscriptionState` to support partial synced Realms.
|
||||
* [Sync] Added an object-level permission subsystem. It is possible to grant fine-grained priviliges to users.
|
||||
* Added object-level permissions:
|
||||
- Schemas `Realm.Permissions.Realm`, `Realm.Permissions.Class`, `Realm.Permissions.Role`, `Realm.Permissions.User`, and `Realm.Permissions.Permission` to support working with permissions. These schemas can be used in user-defined Realms and schemas.
|
||||
- Permissions are enforced by the object server but connectivity is not required.
|
||||
- Method `Realm.privilges()` to compute privileges on a Realm, a Realm object schema, or a Realm object. The method returns either a `Realm.Permissions.Realm` or `Realm.Permissions.Class` object.
|
||||
- For non-synced Realms, all privileges are always granted.
|
||||
- For more details, please read the reference documentation.
|
||||
* Added `Realm.defaultSyncConfiguration()` which will return the configuration for a default synced Realm (#1688).
|
||||
* [Sync] Decrepated `Realm.Sync.setFeatureToken` (#1689).
|
||||
|
||||
### Bug fixes
|
||||
* Fixed usage of disk space preallocation which would occasionally fail on recent MacOS running with the APFS filesystem (Realm Core #3005).
|
||||
|
||||
### Internal
|
||||
* Updated to Realm Core 5.2.0.
|
||||
* Updated to Realm Sync 3.0.0-beta.9.
|
||||
* Tested against Realm Object Server 3.0.0-alpha.2.
|
||||
* Updated to Realm Core 5.4.0.
|
||||
* Updated to Realm Sync 3.0.0-rc.1.
|
||||
* Tested against Realm Object Server 3.0.0-alpha.8.
|
||||
|
||||
|
||||
2.2.14 Release notes (2018-3-5)
|
||||
=============================================================
|
||||
### Breaking changes
|
||||
* None.
|
||||
|
||||
### Enhancements
|
||||
* None.
|
||||
|
||||
### Bug fixes
|
||||
* [Sync] Fixed race condition in handling of session bootstrapping in client.
|
||||
|
||||
### Internal
|
||||
* Updated to Realm Sync 2.2.15.
|
||||
|
||||
|
||||
2.2.13 Release notes (2018-3-2)
|
||||
=============================================================
|
||||
### Breaking changes
|
||||
* None.
|
||||
|
||||
### Enhancements
|
||||
* None.
|
||||
|
||||
### Bug fixes
|
||||
* [Sync] Fixed handling of SSL certificates for the sync client.
|
||||
|
||||
### Internal
|
||||
* Updated to Realm Sync 2.2.14.
|
||||
|
||||
|
||||
2.2.12 Release notes (2018-2-23)
|
||||
=============================================================
|
||||
### Breaking changes
|
||||
* None.
|
||||
|
||||
### Enhancements
|
||||
* None.
|
||||
|
||||
### Bug fixes
|
||||
* Validate that a given type appears at most once in the schema.
|
||||
|
||||
### Internal
|
||||
* None.
|
||||
|
||||
|
||||
2.2.10 Release notes (2018-2-20)
|
||||
|
@ -1,5 +1,5 @@
|
||||
PACKAGE_NAME=realm-js
|
||||
VERSION=2.3.0-alpha.13
|
||||
REALM_CORE_VERSION=5.2.0
|
||||
REALM_SYNC_VERSION=3.0.0-beta.9
|
||||
REALM_OBJECT_SERVER_VERSION=3.0.0-alpha.2
|
||||
VERSION=2.3.0-beta.2
|
||||
REALM_CORE_VERSION=5.4.0
|
||||
REALM_SYNC_VERSION=3.0.0-rc.1
|
||||
REALM_OBJECT_SERVER_VERSION=3.0.0-alpha.8
|
||||
|
@ -21,7 +21,7 @@
|
||||
* They are created exclusively by the client and are processed by the server
|
||||
* as indicated by the status fields.
|
||||
* PermissionChange objects allow to grant and revoke permissions by setting
|
||||
* mayRead, mayWrite and mayManage accordingly.
|
||||
* mayRead, mayWrite and mayManage accordingly.
|
||||
* If any of these flags are not set, these are merged
|
||||
* with either the existing or default permissions as applicable. As a
|
||||
* side-effect this causes that the default permissions are permanently
|
||||
@ -30,7 +30,7 @@
|
||||
* ErrorCode will be updated accordingly.
|
||||
*/
|
||||
class PermissionChange {
|
||||
|
||||
|
||||
/**
|
||||
* Gets the unique identifier of this object in the Management realm.
|
||||
* @type {string}
|
||||
@ -229,3 +229,161 @@ class PermissionOfferResponse {
|
||||
*/
|
||||
get realmUrl() {}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A permission which can be applied to a Realm, Class, or specific Object.
|
||||
* Permissions are applied by adding the permission to the Realm.Permission singleton
|
||||
* object, the RealmClass.Permission object for the desired class, or to a user-defined
|
||||
* Realm.List<Realm.Permission> property on a specific Object instance. The meaning of each of
|
||||
* the properties of Permission depend on what the permission is applied to, and so are
|
||||
* left undocumented here.
|
||||
* @since 2.3.0
|
||||
*/
|
||||
class Permission {
|
||||
|
||||
/**
|
||||
* The Role which this Permission applies to. All users within the Role are
|
||||
* granted the permissions specified by the fields below any
|
||||
* objects/classes/realms which use this Permission.
|
||||
*
|
||||
* This property cannot be modified once set.
|
||||
* @type {Role}
|
||||
*/
|
||||
get role() {}
|
||||
|
||||
/**
|
||||
* Whether the user can read the object to which this Permission is attached.
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canRead() {}
|
||||
|
||||
/**
|
||||
* Whether the user can modify the object to which this Permission is attached.
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canUpdate() {}
|
||||
|
||||
/**
|
||||
* Whether the user can delete the object to which this Permission is attached.
|
||||
*
|
||||
* This property is only applicable to Permissions attached to Objects, and not
|
||||
* to Realms or Classes.
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canDelete() {}
|
||||
|
||||
/**
|
||||
* Whether the user can add or modify Permissions for the object which this
|
||||
* Permission is attached to.
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canSetPermissions() {}
|
||||
|
||||
/**
|
||||
* Whether the user can subscribe to queries for this object type.
|
||||
*
|
||||
* This property is only applicable to Permissions attached to Classes, and not
|
||||
* to Realms or Objects.
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canQuery() {}
|
||||
|
||||
/**
|
||||
* Whether the user can create new objects of the type this Permission is attached to.
|
||||
*
|
||||
* This property is only applicable to Permissions attached to Classes, and not
|
||||
* to Realms or Objects.
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canCreate() {}
|
||||
|
||||
/**
|
||||
* Whether the user can modify the schema of the Realm which this
|
||||
* Permission is attached to.
|
||||
*
|
||||
* This property is only applicable to Permissions attached to Realms, and not
|
||||
* to Realms or Objects.
|
||||
* @type {boolean}
|
||||
*/
|
||||
get canModifySchema() {}
|
||||
}
|
||||
|
||||
/**
|
||||
* A representation of a sync user within the permissions system.
|
||||
*
|
||||
* User objects are created automatically for each sync user which connects to
|
||||
* a Realm, and can also be created manually if you wish to grant permissions to a user
|
||||
* which has not yet connected to this Realm.
|
||||
* @since 2.3.0
|
||||
*/
|
||||
class User {
|
||||
/**
|
||||
* The unique Realm Object Server user ID string identifying this user. This will have
|
||||
* the same value as Realm.Sync.User.identity.
|
||||
* @type {string}
|
||||
*/
|
||||
get id() {}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Role within the permissions system.
|
||||
*
|
||||
* A Role consists of a name for the role and a list of users which are members of the role.
|
||||
* Roles are granted privileges on Realms, Classes and Objects, and in turn grant those
|
||||
* privileges to all users which are members of the role.
|
||||
* A role named "everyone" is automatically created in new Realms, and all new users which
|
||||
* connect to the Realm are automatically added to it. Any other roles you wish to use are
|
||||
* managed as normal Realm objects.
|
||||
* @since 2.3.0
|
||||
*/
|
||||
class Role {
|
||||
/**
|
||||
* The name of the Role.
|
||||
* @type {string}
|
||||
*/
|
||||
get name() {}
|
||||
|
||||
/**
|
||||
* The users which belong to the role.
|
||||
* @type {Array<Realm.Sync.Permissions.User>}
|
||||
*/
|
||||
get members() {}
|
||||
}
|
||||
|
||||
/**
|
||||
* An object which describes class-wide permissions.
|
||||
*
|
||||
* An instance of this object is automatically created in the Realm for class in your schema,
|
||||
* and should not be created manually.
|
||||
* @since 2.3.0
|
||||
*/
|
||||
class Class {
|
||||
/**
|
||||
* The name of the class which these permissions apply to.
|
||||
* @type {string}
|
||||
*/
|
||||
get class_name() {}
|
||||
|
||||
/**
|
||||
* The permissions for this class.
|
||||
* @type {Array<Realm.Sync.Permissions.Permission>}
|
||||
*/
|
||||
get permissions() {}
|
||||
}
|
||||
|
||||
/**
|
||||
* A singleton object which describes Realm-wide permissions.
|
||||
*
|
||||
* An object of this type is automatically created in the Realm for you, and more objects
|
||||
* cannot be created manually.
|
||||
* @since 2.3.0
|
||||
*/
|
||||
class Realm {
|
||||
/**
|
||||
* The permissions for the Realm.
|
||||
* @type {Array<Realm.Sync.Permission>}
|
||||
*/
|
||||
get permissions() {}
|
||||
}
|
||||
|
@ -119,12 +119,42 @@ class Realm {
|
||||
*/
|
||||
static openAsync(config, callback, progressCallback) {}
|
||||
|
||||
/**
|
||||
* Return a configuration for a default synced Realm. The server URL for the current user will be used as base for
|
||||
* the URL for the synced Realm.
|
||||
* @throws {Error} if zero or multiple users are logged in
|
||||
* @returns {Realm~Configuration} - a configuration matching a default synced Realm.
|
||||
* @since 2.3.0
|
||||
*/
|
||||
static defaultSyncConfiguration() {}
|
||||
|
||||
/**
|
||||
* Closes this Realm so it may be re-opened with a newer schema version.
|
||||
* All objects and collections from this Realm are no longer valid after calling this method.
|
||||
*/
|
||||
close() {}
|
||||
|
||||
/**
|
||||
* Returns the granted privilges.
|
||||
*
|
||||
* This combines all privileges granted on the Realm/Class/Object by all Roles which
|
||||
* the current User is a member of into the final privileges which will
|
||||
* be enforced by the server.
|
||||
*
|
||||
* The privilege calculation is done locally using cached data, and inherently may
|
||||
* be stale. It is possible that this method may indicate that an operation is
|
||||
* permitted but the server will still reject it if permission is revoked before
|
||||
* the changes have been integrated on the server.
|
||||
*
|
||||
* Non-synchronized Realms always have permission to perform all operations.
|
||||
*
|
||||
* @param {(Realm~ObjectType|Realm.Object)} arg - the object type or the object to compute priviliges from
|
||||
* @returns {Object} as the computed priviliges as properties
|
||||
* @since 2.3.0
|
||||
* @see {Realm.Permissions} for details of priviliges and roles.
|
||||
*/
|
||||
privileges(arg) {}
|
||||
|
||||
/**
|
||||
* Create a new Realm object of the given type and with the specified properties.
|
||||
* @param {Realm~ObjectType} type - The type of Realm object to create.
|
||||
|
@ -23,7 +23,7 @@ let merlots = wines.filtered('variety == $0 && vintage <= $1', 'Merlot', maxYear
|
||||
```
|
||||
|
||||
|
||||
### Relational operators
|
||||
### Conditional operators
|
||||
You can use equality comparison on all property types:
|
||||
`==` and `!=`
|
||||
|
||||
@ -35,7 +35,7 @@ Example:
|
||||
let oldContacts = realm.objects('Contact').filtered('age > 2');
|
||||
```
|
||||
|
||||
Note that for boolean properties, you should test against the expected keyword.
|
||||
Note that for boolean properties, you should test against `true` or `false`.
|
||||
|
||||
Example:
|
||||
```JS
|
||||
@ -43,7 +43,7 @@ let women = realm.objects('Contact').filtered('isMale == false');
|
||||
```
|
||||
|
||||
### String operators
|
||||
For string properties, prefix, suffix, and substring queries are supported by using the `BEGINSWITH`, `ENDSWITH`, and `CONTAINS` operators.
|
||||
For string properties, prefix, suffix, and substring queries are supported by using the `BEGINSWITH`, `ENDSWITH`, `CONTAINS` and `LIKE` operators.
|
||||
|
||||
For any string operation you can append `[c]` to the operator to make it case insensitive.
|
||||
|
||||
@ -53,6 +53,45 @@ let peopleWhoseNameContainsA = realm.objects('Contact').filtered('name CONTAINS[
|
||||
let Johns = realm.objects('Contact').filtered('name ==[c] "john"');
|
||||
```
|
||||
|
||||
You can do simple wildcard matching with `LIKE` which supports using `?` to match a single character and `*` to match zero or multiple characters.
|
||||
|
||||
Example:
|
||||
```JS
|
||||
// Matches "John" and "Johnny"
|
||||
let Johns = realm.objects('Contact').filtered('name LIKE "John*"');
|
||||
```
|
||||
|
||||
### Composition
|
||||
Use parentheses and the `&&`/`AND` and `||`/`OR` operators to compose queries. You can negate a predicate with `!`/`NOT`.
|
||||
|
||||
### Queries on collections
|
||||
|
||||
When objects contain lists you can query into them using the collection operators `ANY`, `ALL` and `NONE`.
|
||||
|
||||
Example:
|
||||
```JS
|
||||
// Find contacts with one or more teenage friends
|
||||
let teens = realm.objects('Contact').filtered('ANY friends.age < 14');
|
||||
|
||||
// Find contacts where all friends are older than 21
|
||||
let adults = realm.objects('Contact').filtered('ALL friends.age > 21');
|
||||
```
|
||||
|
||||
You can query on aggregates over properties in the lists using the aggregate operators `.@count`, `.@avg`, `.@min`, `.@max` and `.@sum`.
|
||||
|
||||
Example:
|
||||
```JS
|
||||
// Find contacts without friends
|
||||
let lonely = realm.objects('Contact').filtered('friends.@count == 0');
|
||||
|
||||
// Find contacts where the average age of their friends is above 40
|
||||
let adults = realm.objects('Contact').filtered('friends.@avg.age > 40');
|
||||
```
|
||||
|
||||
Subqueries using the `SUBQUERY` operator allows you to filter the lists across multiple parameters while querying them.
|
||||
|
||||
Example:
|
||||
```JS
|
||||
// Find contacts with friends above 21 in SF
|
||||
let teens = realm.objects('Contact').filtered('SUBQUERY(friends, $friend, $friend.age > 21 AND $friend.city = "SF").@count > 0');
|
||||
```
|
||||
|
@ -18,6 +18,8 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
const URL = require('url-parse');
|
||||
|
||||
let getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors || function(obj) {
|
||||
return Object.getOwnPropertyNames(obj).reduce(function (descriptors, name) {
|
||||
descriptors[name] = Object.getOwnPropertyDescriptor(obj, name);
|
||||
@ -149,13 +151,31 @@ module.exports = function(realmConstructor) {
|
||||
setConstructorOnPrototype(realmConstructor.Sync.User);
|
||||
setConstructorOnPrototype(realmConstructor.Sync.Session);
|
||||
|
||||
// A configuration for a default Realm
|
||||
realmConstructor.defaultSyncConfiguration = function() {
|
||||
let users = this.Sync.User.all;
|
||||
let identities = Object.keys(users);
|
||||
if (identities.length === 1) {
|
||||
let user = users[identities[0]];
|
||||
let url = new URL(user.server);
|
||||
let secure = (url.protocol === 'https:')?'s':'';
|
||||
let port = (url.port === undefined)?'9080':url.port
|
||||
let realmUrl = `realm${secure}://${url.hostname}:${port}/~/default`;
|
||||
|
||||
let config = {
|
||||
sync: {
|
||||
user,
|
||||
url: realmUrl
|
||||
}
|
||||
};
|
||||
return config;
|
||||
}
|
||||
new Error(`One and only one user should be logged in but found ${users.length} users.`);
|
||||
}
|
||||
|
||||
if (realmConstructor.Sync._setFeatureToken) {
|
||||
realmConstructor.Sync.setFeatureToken = function(featureToken) {
|
||||
if (typeof featureToken !== 'string' && !(featureToken instanceof String)) {
|
||||
throw new Error("featureToken should be a string");
|
||||
}
|
||||
|
||||
realmConstructor.Sync._setFeatureToken(featureToken.trim());
|
||||
console.log('Realm.Sync.setFeatureToken() is deprecated and you can remove any calls to it.');
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,6 +187,68 @@ module.exports = function(realmConstructor) {
|
||||
Complete: 1, // The subscription has been processed by the sync server and data is being synced to the device.
|
||||
Invalidated: 3, // The subscription has been removed.
|
||||
};
|
||||
|
||||
// Define the permission schemas as constructors so that they can be
|
||||
// passed into directly to functions which want object type names
|
||||
const permissionsSchema = Object.freeze({
|
||||
Class: function() {},
|
||||
Permission: function() {},
|
||||
Realm: function() {},
|
||||
Role: function() {},
|
||||
User: function() {},
|
||||
});
|
||||
permissionsSchema.Permission.schema = Object.freeze({
|
||||
name: '__Permission',
|
||||
properties: {
|
||||
role: '__Role',
|
||||
canRead: {type: 'bool', default: false},
|
||||
canUpdate: {type: 'bool', default: false},
|
||||
canDelete: {type: 'bool', default: false},
|
||||
canSetPermissions: {type: 'bool', default: false},
|
||||
canQuery: {type: 'bool', default: false},
|
||||
canCreate: {type: 'bool', default: false},
|
||||
canModifySchema: {type: 'bool', default: false},
|
||||
}
|
||||
});
|
||||
|
||||
permissionsSchema.User.schema = Object.freeze({
|
||||
name: '__User',
|
||||
primaryKey: 'id',
|
||||
properties: {
|
||||
id: 'string'
|
||||
}
|
||||
});
|
||||
|
||||
permissionsSchema.Role.schema = Object.freeze({
|
||||
name: '__Role',
|
||||
primaryKey: 'name',
|
||||
properties: {
|
||||
name: 'string',
|
||||
members: '__User[]'
|
||||
}
|
||||
});
|
||||
|
||||
permissionsSchema.Class.schema = Object.freeze({
|
||||
name: '__Class',
|
||||
primaryKey: 'class_name',
|
||||
properties: {
|
||||
class_name: 'string',
|
||||
permissions: '__Permission[]'
|
||||
}
|
||||
});
|
||||
|
||||
permissionsSchema.Realm.schema = Object.freeze({
|
||||
name: '__Realm',
|
||||
primaryKey: 'id',
|
||||
properties: {
|
||||
id: 'int',
|
||||
permissions: '__Permission[]'
|
||||
}
|
||||
});
|
||||
Object.defineProperty(realmConstructor, 'Permissions', {
|
||||
value: permissionsSchema,
|
||||
configurable: false
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: Remove this now useless object.
|
||||
|
65
lib/index.d.ts
vendored
65
lib/index.d.ts
vendored
@ -81,7 +81,7 @@ declare namespace Realm {
|
||||
path?: string;
|
||||
readOnly?: boolean;
|
||||
inMemory?: boolean;
|
||||
schema?: ObjectClass[] | ObjectSchema[];
|
||||
schema?: (ObjectClass | ObjectSchema)[];
|
||||
schemaVersion?: number;
|
||||
sync?: Realm.Sync.SyncConfiguration;
|
||||
deleteRealmIfMigrationNeeded?: boolean;
|
||||
@ -406,7 +406,7 @@ declare namespace Realm.Sync {
|
||||
* @see { @link https://realm.io/docs/javascript/latest/api/Realm.Sync.Subscription.html }
|
||||
*/
|
||||
class Subscription {
|
||||
readonly state: number;
|
||||
readonly state: SubscriptionState;
|
||||
readonly error: string;
|
||||
|
||||
unsubscribe(): void;
|
||||
@ -414,6 +414,14 @@ declare namespace Realm.Sync {
|
||||
removeListener(subscruptionCallback: SubscriptionNotificationCallback): void;
|
||||
}
|
||||
|
||||
enum SubscriptionState {
|
||||
Error,
|
||||
Creating,
|
||||
Pending,
|
||||
Complete,
|
||||
Invalidated,
|
||||
}
|
||||
|
||||
/**
|
||||
* AuthError
|
||||
* @see { @link https://realm.io/docs/javascript/latest/api/Realm.Sync.AuthError.html }
|
||||
@ -440,6 +448,10 @@ declare namespace Realm.Sync {
|
||||
function removeListener(regex: string, name: string, changeCallback: (changeEvent: ChangeEvent) => void): Promise<void>;
|
||||
function setLogLevel(logLevel: 'all' | 'trace' | 'debug' | 'detail' | 'info' | 'warn' | 'error' | 'fatal' | 'off'): void;
|
||||
function initiateClientReset(path: string): void;
|
||||
|
||||
/**
|
||||
* @deprecated, to be removed in future versions
|
||||
*/
|
||||
function setFeatureToken(token: string): void;
|
||||
|
||||
type Instruction = {
|
||||
@ -475,6 +487,42 @@ declare namespace Realm.Sync {
|
||||
}
|
||||
}
|
||||
|
||||
declare namespace Realm.Permissions {
|
||||
class Permission {
|
||||
static schema: ObjectSchema;
|
||||
|
||||
identity: string;
|
||||
canRead: boolean;
|
||||
canUpdate: boolean;
|
||||
canDelete: boolean;
|
||||
canSetPermissions: boolean;
|
||||
canQuery: boolean;
|
||||
canCreate: boolean;
|
||||
canModifySchema: boolean;
|
||||
}
|
||||
|
||||
class User {
|
||||
static schema: ObjectSchema;
|
||||
identity: string;
|
||||
}
|
||||
|
||||
class Role {
|
||||
static schema: ObjectSchema;
|
||||
name: string;
|
||||
members: User[];
|
||||
}
|
||||
|
||||
class Class {
|
||||
static schema: ObjectSchema;
|
||||
class_name: string;
|
||||
permissions: Permission[];
|
||||
}
|
||||
|
||||
class Realm {
|
||||
static schema: ObjectSchema;
|
||||
permissions: Permission[];
|
||||
}
|
||||
}
|
||||
|
||||
interface ProgressPromise extends Promise<Realm> {
|
||||
progress(callback: Realm.Sync.ProgressNotificationCallback): Promise<Realm>
|
||||
@ -517,11 +565,16 @@ declare class Realm {
|
||||
*/
|
||||
static openAsync(config: Realm.Configuration, callback: (error: any, realm: Realm) => void, progressCallback?: Realm.Sync.ProgressNotificationCallback): void
|
||||
|
||||
/**
|
||||
* Return a configuration for a default Realm.
|
||||
*/
|
||||
static defaultSyncConfiguration(): string;
|
||||
|
||||
/**
|
||||
* Delete the Realm file for the given configuration.
|
||||
* @param {Configuration} config
|
||||
*/
|
||||
static deleteFile(config: Realm.Configuration): void
|
||||
static deleteFile(config: Realm.Configuration): void;
|
||||
|
||||
/**
|
||||
* @param {Realm.Configuration} config?
|
||||
@ -544,7 +597,7 @@ declare class Realm {
|
||||
* @param {boolean} update?
|
||||
* @returns T
|
||||
*/
|
||||
create<T>(type: string | Realm.ObjectClass | Function, properties: T & Realm.ObjectPropsType, update?: boolean): T;
|
||||
create<T>(type: string | Realm.ObjectClass | Function, properties: T | Realm.ObjectPropsType, update?: boolean): T;
|
||||
|
||||
/**
|
||||
* @param {Realm.Object|Realm.Object[]|Realm.List<any>|Realm.Results<any>|any} object
|
||||
@ -620,6 +673,10 @@ declare class Realm {
|
||||
* @returns boolean
|
||||
*/
|
||||
compact(): boolean;
|
||||
|
||||
privileges() : Realm.Permissions.Realm;
|
||||
privileges(objectType: string | Realm.ObjectSchema | Function) : Realm.Permissions.Class;
|
||||
privileges(obj: Realm.Object) : Realm.Permissions.Class;
|
||||
}
|
||||
|
||||
declare module 'realm' {
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "realm",
|
||||
"description": "Realm is a mobile database: an alternative to SQLite and key-value stores",
|
||||
"version": "2.3.0-alpha.13",
|
||||
"version": "2.3.0-beta.2",
|
||||
"license": "Apache-2.0",
|
||||
"homepage": "https://realm.io",
|
||||
"keywords": [
|
||||
@ -37,8 +37,8 @@
|
||||
"react-native",
|
||||
"scripts",
|
||||
"src",
|
||||
"tests",
|
||||
"vendor",
|
||||
"tests",
|
||||
"binding.gyp",
|
||||
"realm.gypi",
|
||||
"target_defaults.gypi",
|
||||
@ -90,7 +90,7 @@
|
||||
"request": "^2.78.0",
|
||||
"stream-counter": "^1.0.0",
|
||||
"sync-request": "^3.0.1",
|
||||
"url-parse": "^1.1.7"
|
||||
"url-parse": "^1.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^4.0.35",
|
||||
|
118
src/js_realm.hpp
118
src/js_realm.hpp
@ -35,7 +35,6 @@
|
||||
#include "js_sync.hpp"
|
||||
#include "sync/sync_config.hpp"
|
||||
#include "sync/sync_manager.hpp"
|
||||
#include "sync/partial_sync.hpp"
|
||||
#endif
|
||||
|
||||
#include "shared_realm.hpp"
|
||||
@ -186,6 +185,7 @@ public:
|
||||
static void compact(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void delete_model(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void object_for_object_id(ContextType, ObjectType, Arguments, ReturnValue&);
|
||||
static void privileges(ContextType, ObjectType, Arguments, ReturnValue&);
|
||||
|
||||
// properties
|
||||
static void get_empty(ContextType, ObjectType, ReturnValue &);
|
||||
@ -242,6 +242,7 @@ public:
|
||||
{"close", wrap<close>},
|
||||
{"compact", wrap<compact>},
|
||||
{"deleteModel", wrap<delete_model>},
|
||||
{"privileges", wrap<privileges>},
|
||||
{"_objectForObjectId", wrap<object_for_object_id>},
|
||||
#if REALM_ENABLE_SYNC
|
||||
{"_waitForDownload", wrap<wait_for_download_completion>},
|
||||
@ -291,7 +292,8 @@ public:
|
||||
return name;
|
||||
}
|
||||
|
||||
static const ObjectSchema& validated_object_schema_for_value(ContextType ctx, const SharedRealm &realm, const ValueType &value, std::string& object_type) {
|
||||
static const ObjectSchema& validated_object_schema_for_value(ContextType ctx, const SharedRealm &realm, const ValueType &value) {
|
||||
std::string object_type;
|
||||
if (Value::is_constructor(ctx, value)) {
|
||||
FunctionType constructor = Value::to_constructor(ctx, value);
|
||||
|
||||
@ -541,7 +543,7 @@ void RealmClass<T>::constructor(ContextType ctx, ObjectType this_object, size_t
|
||||
|
||||
template<typename T>
|
||||
SharedRealm RealmClass<T>::create_shared_realm(ContextType ctx, realm::Realm::Config config, bool schema_updated,
|
||||
ObjectDefaultsMap && defaults, ConstructorMap && constructors) {
|
||||
ObjectDefaultsMap&& defaults, ConstructorMap&& constructors) {
|
||||
config.execution_context = Context<T>::get_execution_context_id(ctx);
|
||||
|
||||
SharedRealm realm;
|
||||
@ -551,9 +553,6 @@ SharedRealm RealmClass<T>::create_shared_realm(ContextType ctx, realm::Realm::Co
|
||||
catch (const RealmFileException& ex) {
|
||||
handleRealmFileException(ctx, config, ex);
|
||||
}
|
||||
catch (...) {
|
||||
throw;
|
||||
}
|
||||
|
||||
GlobalContextType global_context = Context<T>::get_global_context(ctx);
|
||||
if (!realm->m_binding_context) {
|
||||
@ -790,10 +789,8 @@ void RealmClass<T>::objects(ContextType ctx, ObjectType this_object, Arguments a
|
||||
args.validate_maximum(1);
|
||||
|
||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||
std::string object_type;
|
||||
validated_object_schema_for_value(ctx, realm, args[0], object_type);
|
||||
|
||||
return_value.set(ResultsClass<T>::create_instance(ctx, realm, object_type));
|
||||
auto& object_schema = validated_object_schema_for_value(ctx, realm, args[0]);
|
||||
return_value.set(ResultsClass<T>::create_instance(ctx, realm, object_schema.name));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@ -802,7 +799,7 @@ void RealmClass<T>::object_for_primary_key(ContextType ctx, ObjectType this_obje
|
||||
|
||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||
std::string object_type;
|
||||
auto &object_schema = validated_object_schema_for_value(ctx, realm, args[0], object_type);
|
||||
auto &object_schema = validated_object_schema_for_value(ctx, realm, args[0]);
|
||||
NativeAccessor accessor(ctx, realm, object_schema);
|
||||
auto realm_object = realm::Object::get_for_primary_key(accessor, realm, object_schema, args[1]);
|
||||
|
||||
@ -820,8 +817,7 @@ void RealmClass<T>::create(ContextType ctx, ObjectType this_object, Arguments ar
|
||||
|
||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||
realm->verify_open();
|
||||
std::string object_type;
|
||||
auto &object_schema = validated_object_schema_for_value(ctx, realm, args[0], object_type);
|
||||
auto &object_schema = validated_object_schema_for_value(ctx, realm, args[0]);
|
||||
|
||||
ObjectType object = Value::validated_to_object(ctx, args[1], "properties");
|
||||
if (Value::is_array(ctx, args[1])) {
|
||||
@ -898,7 +894,13 @@ void RealmClass<T>::delete_all(ContextType ctx, ObjectType this_object, Argument
|
||||
}
|
||||
|
||||
for (auto objectSchema : realm->schema()) {
|
||||
ObjectStore::table_for_object_type(realm->read_group(), objectSchema.name)->clear();
|
||||
auto table = ObjectStore::table_for_object_type(realm->read_group(), objectSchema.name);
|
||||
if (realm->is_partial()) {
|
||||
realm::Results(realm, *table).clear();
|
||||
}
|
||||
else {
|
||||
table->clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1002,36 +1004,6 @@ void RealmClass<T>::compact(ContextType ctx, ObjectType this_object, Arguments a
|
||||
return_value.set(realm->compact());
|
||||
}
|
||||
|
||||
#if REALM_ENABLE_SYNC
|
||||
namespace {
|
||||
|
||||
// FIXME: Sync should provide this: https://github.com/realm/realm-sync/issues/1796
|
||||
inline sync::ObjectID object_id_from_string(std::string const& string)
|
||||
{
|
||||
if (string.front() != '{' || string.back() != '}')
|
||||
throw std::invalid_argument("Invalid object ID.");
|
||||
|
||||
size_t dash_index = string.find('-');
|
||||
if (dash_index == std::string::npos)
|
||||
throw std::invalid_argument("Invalid object ID.");
|
||||
|
||||
std::string high_string = string.substr(1, dash_index - 1);
|
||||
std::string low_string = string.substr(dash_index + 1, string.size() - dash_index - 2);
|
||||
|
||||
if (high_string.size() == 0 || high_string.size() > 16 || low_string.size() == 0 || low_string.size() > 16)
|
||||
throw std::invalid_argument("Invalid object ID.");
|
||||
|
||||
auto isxdigit = static_cast<int(*)(int)>(std::isxdigit);
|
||||
if (!std::all_of(high_string.begin(), high_string.end(), isxdigit) ||
|
||||
!std::all_of(low_string.begin(), low_string.end(), isxdigit)) {
|
||||
throw std::invalid_argument("Invalid object ID.");
|
||||
}
|
||||
return sync::ObjectID(strtoull(high_string.c_str(), nullptr, 16), strtoull(low_string.c_str(), nullptr, 16));
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
#endif // REALM_ENABLE_SYNC
|
||||
|
||||
template<typename T>
|
||||
void RealmClass<T>::object_for_object_id(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue& return_value) {
|
||||
args.validate_count(2);
|
||||
@ -1041,21 +1013,67 @@ void RealmClass<T>::object_for_object_id(ContextType ctx, ObjectType this_object
|
||||
if (!sync::has_object_ids(realm->read_group()))
|
||||
throw std::logic_error("Realm._objectForObjectId() can only be used with synced Realms.");
|
||||
|
||||
std::string object_type = Value::validated_to_string(ctx, args[0]);
|
||||
validated_object_schema_for_value(ctx, realm, args[0], object_type);
|
||||
|
||||
auto& object_schema = validated_object_schema_for_value(ctx, realm, args[0]);
|
||||
std::string object_id_string = Value::validated_to_string(ctx, args[1]);
|
||||
auto object_id = object_id_from_string(object_id_string);
|
||||
auto object_id = sync::ObjectID::from_string(object_id_string);
|
||||
|
||||
const Group& group = realm->read_group();
|
||||
size_t ndx = sync::row_for_object_id(group, *ObjectStore::table_for_object_type(group, object_type), object_id);
|
||||
size_t ndx = sync::row_for_object_id(group, *ObjectStore::table_for_object_type(group, object_schema.name), object_id);
|
||||
if (ndx != realm::npos) {
|
||||
return_value.set(RealmObjectClass<T>::create_instance(ctx, realm::Object(realm, object_type, ndx)));
|
||||
return_value.set(RealmObjectClass<T>::create_instance(ctx, realm::Object(realm, object_schema.name, ndx)));
|
||||
}
|
||||
#else
|
||||
throw std::logic_error("Realm._objectForObjectId() can only be used with synced Realms.");
|
||||
#endif // REALM_ENABLE_SYNC
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RealmClass<T>::privileges(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(1);
|
||||
|
||||
using Privilege = realm::ComputedPrivileges;
|
||||
auto has_privilege = [](Privilege actual, Privilege expected) {
|
||||
return (static_cast<int>(actual) & static_cast<int>(expected)) == static_cast<int>(expected);
|
||||
};
|
||||
|
||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||
if (args.count == 0) {
|
||||
auto p = realm->get_privileges();
|
||||
ObjectType object = Object::create_empty(ctx);
|
||||
Object::set_property(ctx, object, "read", Value::from_boolean(ctx, has_privilege(p, Privilege::Read)));
|
||||
Object::set_property(ctx, object, "update", Value::from_boolean(ctx,has_privilege(p, Privilege::Update)));
|
||||
Object::set_property(ctx, object, "modifySchema", Value::from_boolean(ctx, has_privilege(p, Privilege::ModifySchema)));
|
||||
Object::set_property(ctx, object, "setPermissions", Value::from_boolean(ctx, has_privilege(p, Privilege::SetPermissions)));
|
||||
return_value.set(object);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Value::is_object(ctx, args[0])) {
|
||||
auto arg = Value::to_object(ctx, args[0]);
|
||||
if (Object::template is_instance<RealmObjectClass<T>>(ctx, arg)) {
|
||||
auto obj = get_internal<T, RealmObjectClass<T>>(arg);
|
||||
auto p = realm->get_privileges(obj->row());
|
||||
|
||||
ObjectType object = Object::create_empty(ctx);
|
||||
Object::set_property(ctx, object, "read", Value::from_boolean(ctx, has_privilege(p, Privilege::Read)));
|
||||
Object::set_property(ctx, object, "update", Value::from_boolean(ctx,has_privilege(p, Privilege::Update)));
|
||||
Object::set_property(ctx, object, "delete", Value::from_boolean(ctx,has_privilege(p, Privilege::Delete)));
|
||||
Object::set_property(ctx, object, "setPermissions", Value::from_boolean(ctx, has_privilege(p, Privilege::SetPermissions)));
|
||||
return_value.set(object);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto& object_schema = validated_object_schema_for_value(ctx, realm, args[0]);
|
||||
auto p = realm->get_privileges(object_schema.name);
|
||||
ObjectType object = Object::create_empty(ctx);
|
||||
Object::set_property(ctx, object, "read", Value::from_boolean(ctx, has_privilege(p, Privilege::Read)));
|
||||
Object::set_property(ctx, object, "update", Value::from_boolean(ctx,has_privilege(p, Privilege::Update)));
|
||||
Object::set_property(ctx, object, "create", Value::from_boolean(ctx, has_privilege(p, Privilege::Create)));
|
||||
Object::set_property(ctx, object, "subscribe", Value::from_boolean(ctx, has_privilege(p, Privilege::Query)));
|
||||
Object::set_property(ctx, object, "setPermissions", Value::from_boolean(ctx, has_privilege(p, Privilege::SetPermissions)));
|
||||
return_value.set(object);
|
||||
}
|
||||
|
||||
} // js
|
||||
} // realm
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 703c391c64f73c812358940993942338f1bc706d
|
||||
Subproject commit b250563ea1eb9f32ec7dbd76f2c6f8f1a26914cc
|
@ -37,7 +37,8 @@ module.exports = {
|
||||
}
|
||||
else if (type === 'object') {
|
||||
for (const key of Object.keys(val1)) {
|
||||
this.assertEqual(val1[key], val2[key], errorMessage, depth + 1);
|
||||
const message = errorMessage ? `${errorMessage}: ${key}` : key;
|
||||
this.assertEqual(val1[key], val2[key], message, depth + 1);
|
||||
}
|
||||
}
|
||||
else if (type === 'list') {
|
||||
|
@ -9,6 +9,8 @@ const realmName = process.argv[4];
|
||||
const realmModule = process.argv[5];
|
||||
|
||||
const Realm = require(realmModule);
|
||||
// Ensure that schemas.js gets the correct module with `require('realm')`
|
||||
require.cache[require.resolve('realm')] = require.cache[require.resolve(realmModule)];
|
||||
let schemas = require(process.argv[2]);
|
||||
|
||||
function createObjects(user) {
|
||||
|
@ -16,33 +16,33 @@
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
'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);
|
||||
});
|
||||
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 = () => {
|
||||
return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password')
|
||||
.then(user => {
|
||||
new Realm({sync: {user, url: 'realm://localhost:9080/~/test'}}).close();
|
||||
return user;
|
||||
});
|
||||
};
|
||||
const createUserWithTestRealm = () => {
|
||||
return Realm.Sync.User
|
||||
.register('http://localhost:9080', uuid(), 'password')
|
||||
.then(user => {
|
||||
new Realm({sync: {user, url: 'realm://localhost:9080/~/test'}}).close();
|
||||
return user;
|
||||
});
|
||||
};
|
||||
|
||||
return Promise.all(Array.from({length: count}, createUserWithTestRealm));
|
||||
return Promise.all(Array.from({length: count}, createUserWithTestRealm));
|
||||
}
|
||||
|
||||
function wait(t) {
|
||||
return new Promise(resolve => setTimeout(resolve, t));
|
||||
return new Promise(resolve => setTimeout(resolve, t));
|
||||
}
|
||||
|
||||
function repeatUntil(fn, predicate) {
|
||||
@ -57,66 +57,183 @@ function repeatUntil(fn, predicate) {
|
||||
return check;
|
||||
}
|
||||
|
||||
function subscribe(results) {
|
||||
const subscription = results.subscribe();
|
||||
return new Promise((resolve, reject) => {
|
||||
subscription.addListener((subscription, state) => {
|
||||
if (state == Realm.Sync.SubscriptionState.Complete) {
|
||||
resolve();
|
||||
}
|
||||
else if (state == Realm.Sync.SubscriptionState.Error) {
|
||||
reject();
|
||||
}
|
||||
});
|
||||
setTimeout(() => reject("listener never called"), 5000);
|
||||
});
|
||||
}
|
||||
|
||||
function waitForUpload(realm) {
|
||||
let session = realm.syncSession;
|
||||
return new Promise(resolve => {
|
||||
let callback = (transferred, total) => {
|
||||
if (transferred === total) {
|
||||
session.removeProgressNotification(callback);
|
||||
resolve(realm);
|
||||
}
|
||||
};
|
||||
session.addProgressNotification('upload', 'forCurrentlyOutstandingWork', callback);
|
||||
});
|
||||
}
|
||||
|
||||
function permissionForPath(permissions, path) {
|
||||
for (const permission of permissions) {
|
||||
if (permission.path == path) {
|
||||
return permission;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
testApplyAndGetGrantedPermissions() {
|
||||
return createUsersWithTestRealms(1)
|
||||
.then(([user]) => {
|
||||
return user.applyPermissions({ userId: `${user.identity}` }, `/${user.identity}/test`, 'read')
|
||||
.then(repeatUntil(() => user.getGrantedPermissions('any'),
|
||||
permissions => permissions.length > 1))
|
||||
.then(permissions => {
|
||||
TestCase.assertEqual(permissions[0].path, `/${user.identity}/test`);
|
||||
TestCase.assertEqual(permissions[0].mayRead, true);
|
||||
TestCase.assertEqual(permissions[0].mayWrite, false);
|
||||
TestCase.assertEqual(permissions[0].mayManage, false);
|
||||
});
|
||||
return createUsersWithTestRealms(1).then(([user]) => {
|
||||
const path = `/${user.identity}/test`;
|
||||
return user
|
||||
.applyPermissions({userId: `${user.identity}`},
|
||||
`/${user.identity}/test`, 'read')
|
||||
.then(repeatUntil(() => user.getGrantedPermissions('any'),
|
||||
permissions => {
|
||||
let permission = permissionForPath(permissions, path);
|
||||
return permission && !permission.mayWrite;
|
||||
}))
|
||||
.then(permissions => {
|
||||
let permission = permissionForPath(permissions, path);
|
||||
TestCase.assertDefined(permission);
|
||||
TestCase.assertEqual(permission.mayRead, true);
|
||||
TestCase.assertEqual(permission.mayWrite, false);
|
||||
TestCase.assertEqual(permission.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[2].path, `/${user1.identity}/test`);
|
||||
TestCase.assertEqual(permissions[2].mayRead, true);
|
||||
TestCase.assertEqual(permissions[2].mayWrite, false);
|
||||
TestCase.assertEqual(permissions[2].mayManage, false);
|
||||
});
|
||||
return createUsersWithTestRealms(2).then(([user1, user2]) => {
|
||||
const path = `/${user1.identity}/test`;
|
||||
return user1.offerPermissions(`/${user1.identity}/test`, 'read')
|
||||
.then(token => user2.acceptPermissionOffer(token))
|
||||
.then(realmUrl => {
|
||||
TestCase.assertEqual(realmUrl, path);
|
||||
return realmUrl;
|
||||
})
|
||||
.then(repeatUntil(() => user2.getGrantedPermissions('any'),
|
||||
permissions => permissions.length > 2
|
||||
&& permissionForPath(permissions, path)))
|
||||
.then(permissions => {
|
||||
let permission = permissionForPath(permissions, path)
|
||||
TestCase.assertDefined(permission);
|
||||
TestCase.assertEqual(permission.mayRead, true);
|
||||
TestCase.assertEqual(permission.mayWrite, false);
|
||||
TestCase.assertEqual(permission.mayManage, false);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
testInvalidatePermissionOffer() {
|
||||
let user1, user2, token;
|
||||
return createUsersWithTestRealms(2)
|
||||
.then(users => {
|
||||
user1 = users[0];
|
||||
user2 = users[1];
|
||||
return user1.offerPermissions(`/${user1.identity}/test`, 'read');
|
||||
})
|
||||
.then(t => { token = t; 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);
|
||||
}
|
||||
});
|
||||
let user1, user2, token;
|
||||
return createUsersWithTestRealms(2)
|
||||
.then(users => {
|
||||
user1 = users[0];
|
||||
user2 = users[1];
|
||||
return user1.offerPermissions(`/${user1.identity}/test`, 'read');
|
||||
})
|
||||
.then(t => {
|
||||
token = t;
|
||||
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);
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
testObjectPermissions() {
|
||||
let config = (user, url) => {
|
||||
return {
|
||||
schema: [
|
||||
Realm.Permissions.Permission,
|
||||
Realm.Permissions.User,
|
||||
Realm.Permissions.Role,
|
||||
{
|
||||
name: 'Object',
|
||||
properties: {
|
||||
value: 'int',
|
||||
permissions: '__Permission[]'
|
||||
}
|
||||
}
|
||||
],
|
||||
sync: {user, url, partial: true}
|
||||
};
|
||||
};
|
||||
let owner, otherUser
|
||||
return Realm.Sync.User
|
||||
.register('http://localhost:9080', uuid(), 'password')
|
||||
.then(user => {
|
||||
owner = user;
|
||||
new Realm({sync: {user, url: 'realm://localhost:9080/~/test'}}).close();
|
||||
return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password')
|
||||
})
|
||||
.then(user => {
|
||||
otherUser = user;
|
||||
return owner.applyPermissions({userId: otherUser.identity},
|
||||
`/${owner.identity}/test`, 'read')
|
||||
})
|
||||
.then(() => {
|
||||
let realm = new Realm(config(owner, 'realm://localhost:9080/~/test'));
|
||||
realm.write(() => {
|
||||
let user = realm.create(Realm.Permissions.User, {id: otherUser.identity})
|
||||
let role = realm.create(Realm.Permissions.Role, {name: 'reader'})
|
||||
role.members.push(user)
|
||||
|
||||
let obj1 = realm.create('Object', {value: 1});
|
||||
let obj2 = realm.create('Object', {value: 2});
|
||||
obj2.permissions.push(realm.create(Realm.Permissions.Permission,
|
||||
{role: role, canRead: true, canUpdate: false}))
|
||||
});
|
||||
return waitForUpload(realm).then(() => realm.close());
|
||||
})
|
||||
.then(() => Realm.open(config(otherUser, `realm://localhost:9080/${owner.identity}/test`)))
|
||||
.then((realm) => subscribe(realm.objects('Object')).then(() => realm))
|
||||
.then((realm) => {
|
||||
// Should have full access to the Realm as a whole
|
||||
TestCase.assertSimilar('object', realm.privileges(),
|
||||
{read: true, update: true, modifySchema: true, setPermissions: true});
|
||||
TestCase.assertSimilar('object', realm.privileges('Object'),
|
||||
{read: true, update: true, create: true, subscribe: true, setPermissions: true});
|
||||
// Verify that checking via constructor works too
|
||||
TestCase.assertSimilar('object', realm.privileges(Realm.Permissions.User),
|
||||
{read: true, update: true, create: true, subscribe: true, setPermissions: true});
|
||||
|
||||
// Should only be able to see the second object
|
||||
let results = realm.objects('Object')
|
||||
TestCase.assertEqual(results.length, 1);
|
||||
TestCase.assertEqual(results[0].value, 2);
|
||||
TestCase.assertSimilar('object', realm.privileges(results[0]),
|
||||
{read: true, update: false, delete: false, setPermissions: false});
|
||||
realm.close();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ module.exports = {
|
||||
const realm2 = new Realm({schema: [], path: testPath2});
|
||||
TestCase.assertEqual(realm2.path, defaultDir + testPath2);
|
||||
},
|
||||
|
||||
|
||||
testRealmIsClosed: function() {
|
||||
const realm = new Realm({schema: []});
|
||||
TestCase.assertFalse(realm.isClosed);
|
||||
|
@ -61,27 +61,23 @@ function copyFileToTempDir(filename) {
|
||||
return tmpFile.name;
|
||||
}
|
||||
|
||||
function runOutOfProcess(nodeJsFilePath) {
|
||||
var nodeArgs = Array.prototype.slice.call(arguments);
|
||||
function runOutOfProcess() {
|
||||
const args = Array.prototype.slice.call(arguments);
|
||||
let tmpDir = tmp.dirSync();
|
||||
let content = fs.readFileSync(nodeJsFilePath, 'utf8');
|
||||
let tmpFile = tmp.fileSync({ dir: tmpDir.name });
|
||||
fs.appendFileSync(tmpFile.fd, content, { encoding: 'utf8' });
|
||||
nodeArgs[0] = tmpFile.name;
|
||||
console.log(`runOutOfProcess : ${args.join(' ')}`);
|
||||
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) => {
|
||||
execFile(process.execPath, args, {cwd: tmpDir.name}, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
console.error("runOutOfProcess failed\n" + error);
|
||||
reject(new Error(`Running ${nodeJsFilePath} failed. error: ${error}`));
|
||||
console.error("runOutOfProcess failed\n", error, stdout, stderr);
|
||||
reject(new Error(`Running ${args[0]} failed. error: ${error}`));
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('runOutOfProcess success\n' + stdout);
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
@ -170,6 +166,33 @@ module.exports = {
|
||||
});
|
||||
},
|
||||
|
||||
testDefaultRealm() {
|
||||
if (!isNodeProccess) {
|
||||
return;
|
||||
}
|
||||
|
||||
const username = uuid();
|
||||
const realmName = 'default';
|
||||
const expectedObjectsCount = 3;
|
||||
|
||||
let user;
|
||||
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||
.then(() => Realm.Sync.User.login('http://localhost:9080', username, 'password'))
|
||||
.then(u => {
|
||||
user = u;
|
||||
return Realm.open(Realm.defaultSyncConfiguration());
|
||||
})
|
||||
.then(realm => {
|
||||
let actualObjectsCount = realm.objects('Dog').length;
|
||||
TestCase.assertEqual(actualObjectsCount, expectedObjectsCount, "Synced realm does not contain the expected objects count");
|
||||
|
||||
const session = realm.syncSession;
|
||||
TestCase.assertInstanceOf(session, Realm.Sync.Session);
|
||||
TestCase.assertEqual(session.user.identity, user.identity);
|
||||
TestCase.assertEqual(session.state, 'active');
|
||||
});
|
||||
},
|
||||
|
||||
testRealmOpenWithExistingLocalRealm() {
|
||||
if (!isNodeProccess) {
|
||||
return;
|
||||
@ -275,43 +298,40 @@ module.exports = {
|
||||
const expectedObjectsCount = 3;
|
||||
|
||||
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) => {
|
||||
const accessTokenRefreshed = this;
|
||||
let successCounter = 0;
|
||||
.then(() => Realm.Sync.User.login('http://localhost:9080', username, 'password'))
|
||||
.then(user => {
|
||||
const accessTokenRefreshed = this;
|
||||
let successCounter = 0;
|
||||
|
||||
let config = {
|
||||
sync: { user, url: `realm://localhost:9080/~/${realmName}` }
|
||||
};
|
||||
let config = {
|
||||
sync: { user, url: `realm://localhost:9080/~/${realmName}` }
|
||||
};
|
||||
return new Promise((resolve, reject) => {
|
||||
Realm.openAsync(config, (error, realm) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
let actualObjectsCount = realm.objects('Dog').length;
|
||||
TestCase.assertEqual(actualObjectsCount, expectedObjectsCount, "Synced realm does not contain the expected objects count");
|
||||
|
||||
Realm.openAsync(config, (error, realm) => {
|
||||
try {
|
||||
if (error) {
|
||||
reject(error);
|
||||
}
|
||||
let firstDog = realm.objects('Dog')[0];
|
||||
TestCase.assertTrue(({}).hasOwnProperty.call(firstDog, 'name'), "Synced realm does not have an inffered schema");
|
||||
TestCase.assertTrue(firstDog.name, "Synced realm object's property should have a value");
|
||||
TestCase.assertTrue(firstDog.name.indexOf('Lassy') !== -1, "Synced realm object's property should contain the actual written value");
|
||||
|
||||
let actualObjectsCount = realm.objects('Dog').length;
|
||||
TestCase.assertEqual(actualObjectsCount, expectedObjectsCount, "Synced realm does not contain the expected objects count");
|
||||
|
||||
let firstDog = realm.objects('Dog')[0];
|
||||
TestCase.assertTrue(({}).hasOwnProperty.call(firstDog, 'name'), "Synced realm does not have an inffered schema");
|
||||
TestCase.assertTrue(firstDog.name, "Synced realm object's property should have a value");
|
||||
TestCase.assertTrue(firstDog.name.indexOf('Lassy') !== -1, "Synced realm object's property should contain the actual written value");
|
||||
|
||||
|
||||
const session = realm.syncSession;
|
||||
TestCase.assertInstanceOf(session, Realm.Sync.Session);
|
||||
TestCase.assertEqual(session.user.identity, user.identity);
|
||||
TestCase.assertEqual(session.config.url, config.sync.url);
|
||||
TestCase.assertEqual(session.config.user.identity, config.sync.user.identity);
|
||||
TestCase.assertEqual(session.state, 'active');
|
||||
resolve();
|
||||
}
|
||||
catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
const session = realm.syncSession;
|
||||
TestCase.assertInstanceOf(session, Realm.Sync.Session);
|
||||
TestCase.assertEqual(session.user.identity, user.identity);
|
||||
TestCase.assertEqual(session.config.url, config.sync.url);
|
||||
TestCase.assertEqual(session.config.user.identity, config.sync.user.identity);
|
||||
TestCase.assertEqual(session.state, 'active');
|
||||
resolve();
|
||||
}
|
||||
catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -321,26 +341,22 @@ module.exports = {
|
||||
const username = uuid();
|
||||
const expectedObjectsCount = 3;
|
||||
|
||||
const accessTokenRefreshed = this;
|
||||
let successCounter = 0;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const accessTokenRefreshed = this;
|
||||
let successCounter = 0;
|
||||
let config = {
|
||||
schema: [{ name: 'Dog', properties: { name: 'string' } }],
|
||||
};
|
||||
|
||||
let config = {
|
||||
schema: [{ name: 'Dog', properties: { name: 'string' } }],
|
||||
};
|
||||
return Realm.open(config).then(realm => {
|
||||
realm.write(() => {
|
||||
for (let i = 1; i <= 3; i++) {
|
||||
realm.create('Dog', { name: `Lassy ${i}` });
|
||||
}
|
||||
});
|
||||
|
||||
Realm.open(config).then(realm => {
|
||||
realm.write(() => {
|
||||
for (let i = 1; i <= 3; i++) {
|
||||
realm.create('Dog', { name: `Lassy ${i}` });
|
||||
}
|
||||
});
|
||||
|
||||
let actualObjectsCount = realm.objects('Dog').length;
|
||||
TestCase.assertEqual(actualObjectsCount, expectedObjectsCount, "Local realm does not contain the expected objects count");
|
||||
resolve();
|
||||
}).catch(error => reject(error));
|
||||
let actualObjectsCount = realm.objects('Dog').length;
|
||||
TestCase.assertEqual(actualObjectsCount, expectedObjectsCount, "Local realm does not contain the expected objects count");
|
||||
});
|
||||
},
|
||||
|
||||
@ -412,37 +428,33 @@ module.exports = {
|
||||
const realmName = uuid();
|
||||
|
||||
return runOutOfProcess(__dirname + '/nested-list-helper.js', __dirname + '/schemas.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 = {
|
||||
schema: [schemas.ParentObject, schemas.NameObject],
|
||||
sync: { user, url: `realm://localhost:9080/~/${realmName}` }
|
||||
};
|
||||
Realm.open(config).then(realm => {
|
||||
let objects = realm.objects('ParentObject');
|
||||
.then(() => Realm.Sync.User.login('http://localhost:9080', username, 'password'))
|
||||
.then(user => {
|
||||
let config = {
|
||||
schema: [schemas.ParentObject, schemas.NameObject],
|
||||
sync: { user, url: `realm://localhost:9080/~/${realmName}` }
|
||||
};
|
||||
return Realm.open(config)
|
||||
}).then(realm => {
|
||||
let objects = realm.objects('ParentObject');
|
||||
|
||||
let json = JSON.stringify(objects);
|
||||
TestCase.assertEqual(json, '{"0":{"id":1,"name":{"0":{"family":"Larsen","given":{"0":"Hans","1":"Jørgen"},"prefix":{}},"1":{"family":"Hansen","given":{"0":"Ib"},"prefix":{}}}},"1":{"id":2,"name":{"0":{"family":"Petersen","given":{"0":"Gurli","1":"Margrete"},"prefix":{}}}}}');
|
||||
TestCase.assertEqual(objects.length, 2);
|
||||
TestCase.assertEqual(objects[0].name.length, 2);
|
||||
TestCase.assertEqual(objects[0].name[0].given.length, 2);
|
||||
TestCase.assertEqual(objects[0].name[0].prefix.length, 0);
|
||||
TestCase.assertEqual(objects[0].name[0].given[0], 'Hans');
|
||||
TestCase.assertEqual(objects[0].name[0].given[1], 'Jørgen')
|
||||
TestCase.assertEqual(objects[0].name[1].given.length, 1);
|
||||
TestCase.assertEqual(objects[0].name[1].given[0], 'Ib');
|
||||
TestCase.assertEqual(objects[0].name[1].prefix.length, 0);
|
||||
let json = JSON.stringify(objects);
|
||||
TestCase.assertEqual(json, '{"0":{"id":1,"name":{"0":{"family":"Larsen","given":{"0":"Hans","1":"Jørgen"},"prefix":{}},"1":{"family":"Hansen","given":{"0":"Ib"},"prefix":{}}}},"1":{"id":2,"name":{"0":{"family":"Petersen","given":{"0":"Gurli","1":"Margrete"},"prefix":{}}}}}');
|
||||
TestCase.assertEqual(objects.length, 2);
|
||||
TestCase.assertEqual(objects[0].name.length, 2);
|
||||
TestCase.assertEqual(objects[0].name[0].given.length, 2);
|
||||
TestCase.assertEqual(objects[0].name[0].prefix.length, 0);
|
||||
TestCase.assertEqual(objects[0].name[0].given[0], 'Hans');
|
||||
TestCase.assertEqual(objects[0].name[0].given[1], 'Jørgen')
|
||||
TestCase.assertEqual(objects[0].name[1].given.length, 1);
|
||||
TestCase.assertEqual(objects[0].name[1].given[0], 'Ib');
|
||||
TestCase.assertEqual(objects[0].name[1].prefix.length, 0);
|
||||
|
||||
TestCase.assertEqual(objects[1].name.length, 1);
|
||||
TestCase.assertEqual(objects[1].name[0].given.length, 2);
|
||||
TestCase.assertEqual(objects[1].name[0].prefix.length, 0);
|
||||
TestCase.assertEqual(objects[1].name[0].given[0], 'Gurli');
|
||||
TestCase.assertEqual(objects[1].name[0].given[1], 'Margrete');
|
||||
resolve();
|
||||
}).catch(() => reject());
|
||||
});
|
||||
});
|
||||
TestCase.assertEqual(objects[1].name.length, 1);
|
||||
TestCase.assertEqual(objects[1].name[0].given.length, 2);
|
||||
TestCase.assertEqual(objects[1].name[0].prefix.length, 0);
|
||||
TestCase.assertEqual(objects[1].name[0].given[0], 'Gurli');
|
||||
TestCase.assertEqual(objects[1].name[0].given[1], 'Margrete');
|
||||
});
|
||||
},
|
||||
|
||||
@ -610,60 +622,59 @@ module.exports = {
|
||||
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' } }],
|
||||
};
|
||||
.then(() => Realm.Sync.User.login('http://localhost:9080', username, 'password'))
|
||||
.then(user => {
|
||||
let config = {
|
||||
sync: {
|
||||
user,
|
||||
url: `realm://localhost:9080/~/${realmName}`
|
||||
},
|
||||
schema: [{ name: 'Dog', properties: { name: 'string' } }],
|
||||
};
|
||||
|
||||
let realm = new Realm(config);
|
||||
let unregisterFunc;
|
||||
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 writeDataFunc = () => {
|
||||
realm.write(() => {
|
||||
for (let i = 1; i <= 3; i++) {
|
||||
realm.create('Dog', { name: `Lassy ${i}` });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let syncFinished = false;
|
||||
let failOnCall = false;
|
||||
const progressCallback = (transferred, total) => {
|
||||
if (failOnCall) {
|
||||
reject(new Error("Progress callback should not be called after removeProgressNotification"));
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
syncFinished = transferred === total;
|
||||
//unregister and write some new data.
|
||||
if (syncFinished) {
|
||||
failOnCall = true;
|
||||
unregisterFunc();
|
||||
|
||||
//unregister and write some new data.
|
||||
if (syncFinished) {
|
||||
failOnCall = true;
|
||||
unregisterFunc();
|
||||
//use second callback to wait for sync finished
|
||||
realm.syncSession.addProgressNotification('upload', 'reportIndefinitely', (transferred, transferable) => {
|
||||
if (transferred === transferable) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
writeDataFunc();
|
||||
}
|
||||
};
|
||||
|
||||
//use second callback to wait for sync finished
|
||||
realm.syncSession.addProgressNotification('upload', 'reportIndefinitely', (transferred, transferable) => {
|
||||
if (transferred === transferable) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
writeDataFunc();
|
||||
}
|
||||
};
|
||||
realm.syncSession.addProgressNotification('upload', 'reportIndefinitely', progressCallback);
|
||||
|
||||
realm.syncSession.addProgressNotification('upload', 'reportIndefinitely', progressCallback);
|
||||
unregisterFunc = () => {
|
||||
realm.syncSession.removeProgressNotification(progressCallback);
|
||||
};
|
||||
|
||||
unregisterFunc = () => {
|
||||
realm.syncSession.removeProgressNotification(progressCallback);
|
||||
};
|
||||
|
||||
writeDataFunc();
|
||||
});
|
||||
writeDataFunc();
|
||||
});
|
||||
});
|
||||
},
|
||||
@ -675,36 +686,24 @@ module.exports = {
|
||||
|
||||
const username = uuid();
|
||||
const realmName = uuid();
|
||||
let progressCalled = false;
|
||||
|
||||
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' } }],
|
||||
};
|
||||
.then(() => Realm.Sync.User.login('http://localhost:9080', username, 'password'))
|
||||
.then(user => {
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
return Promise.race([
|
||||
Realm.open(config).progress((transferred, total) => { progressCalled = true; }),
|
||||
new Promise((_, reject) => setTimeout(() => reject("Progress Notifications API failed to call progress callback for Realm constructor"), 5000))
|
||||
]);
|
||||
}).then(() => TestCase.assertTrue(progressCalled));
|
||||
},
|
||||
|
||||
testProgressNotificationsForRealmOpenAsync() {
|
||||
@ -716,37 +715,36 @@ module.exports = {
|
||||
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' } }],
|
||||
};
|
||||
.then(() => 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;
|
||||
let progressCalled = false;
|
||||
|
||||
Realm.openAsync(config,
|
||||
(error, realm) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
Realm.openAsync(config,
|
||||
(error, realm) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
TestCase.assertTrue(progressCalled);
|
||||
resolve();
|
||||
},
|
||||
(transferred, total) => {
|
||||
progressCalled = true;
|
||||
});
|
||||
TestCase.assertTrue(progressCalled);
|
||||
resolve();
|
||||
},
|
||||
(transferred, total) => {
|
||||
progressCalled = true;
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
reject("Progress Notifications API failed to call progress callback for Realm constructor");
|
||||
}, 5000);
|
||||
});
|
||||
setTimeout(function() {
|
||||
reject("Progress Notifications API failed to call progress callback for Realm constructor");
|
||||
}, 5000);
|
||||
});
|
||||
});
|
||||
},
|
||||
@ -761,38 +759,37 @@ module.exports = {
|
||||
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 => {
|
||||
let config = {
|
||||
sync: {
|
||||
user: user,
|
||||
url: `realm://localhost:9080/~/${realmName}`,
|
||||
partial: true,
|
||||
error: (session, error) => console.log(error)
|
||||
},
|
||||
schema: [{ name: 'Dog', properties: { name: 'string' } }]
|
||||
};
|
||||
.then(() => Realm.Sync.User.login('http://localhost:9080', username, 'password'))
|
||||
.then(user => {
|
||||
let config = {
|
||||
sync: {
|
||||
user: user,
|
||||
url: `realm://localhost:9080/~/${realmName}`,
|
||||
partial: true,
|
||||
error: (session, error) => console.log(error)
|
||||
},
|
||||
schema: [{ name: 'Dog', properties: { name: 'string' } }]
|
||||
};
|
||||
|
||||
Realm.deleteFile(config);
|
||||
const realm = new Realm(config);
|
||||
TestCase.assertEqual(realm.objects('Dog').length, 0);
|
||||
var results = realm.objects('Dog').filtered("name == 'Lassy 1'");
|
||||
var subscription = results.subscribe();
|
||||
TestCase.assertEqual(subscription.state, Realm.Sync.SubscriptionState.Creating);
|
||||
return new Promise((resolve, reject) => {
|
||||
subscription.addListener((subscription, state) => {
|
||||
if (state == Realm.Sync.SubscriptionState.Complete) {
|
||||
TestCase.assertEqual(results.length, 1);
|
||||
TestCase.assertTrue(results[0].name === 'Lassy 1', "The object is not synced correctly");
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
setTimeout(function() {
|
||||
reject("listener never called");
|
||||
}, 5000);
|
||||
Realm.deleteFile(config);
|
||||
const realm = new Realm(config);
|
||||
TestCase.assertEqual(realm.objects('Dog').length, 0);
|
||||
var results = realm.objects('Dog').filtered("name == 'Lassy 1'");
|
||||
var subscription = results.subscribe();
|
||||
TestCase.assertEqual(subscription.state, Realm.Sync.SubscriptionState.Creating);
|
||||
return new Promise((resolve, reject) => {
|
||||
subscription.addListener((subscription, state) => {
|
||||
if (state == Realm.Sync.SubscriptionState.Complete) {
|
||||
TestCase.assertEqual(results.length, 1);
|
||||
TestCase.assertTrue(results[0].name === 'Lassy 1', "The object is not synced correctly");
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
})
|
||||
})
|
||||
setTimeout(function() {
|
||||
reject("listener never called");
|
||||
}, 5000);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
testPartialSyncAnonymous_ResultsListener() {
|
||||
@ -805,38 +802,37 @@ module.exports = {
|
||||
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 => {
|
||||
let config = {
|
||||
sync: {
|
||||
user: user,
|
||||
url: `realm://localhost:9080/~/${realmName}`,
|
||||
partial: true,
|
||||
error: (session, error) => console.log(error)
|
||||
},
|
||||
schema: [{ name: 'Dog', properties: { name: 'string' } }]
|
||||
};
|
||||
.then(() => Realm.Sync.User.login('http://localhost:9080', username, 'password'))
|
||||
.then(user => {
|
||||
let config = {
|
||||
sync: {
|
||||
user: user,
|
||||
url: `realm://localhost:9080/~/${realmName}`,
|
||||
partial: true,
|
||||
error: (session, error) => console.log(error)
|
||||
},
|
||||
schema: [{ name: 'Dog', properties: { name: 'string' } }]
|
||||
};
|
||||
|
||||
Realm.deleteFile(config);
|
||||
const realm = new Realm(config);
|
||||
TestCase.assertEqual(realm.objects('Dog').length, 0);
|
||||
var results = realm.objects('Dog').filtered("name == 'Lassy 1'");
|
||||
var subscription = results.subscribe();
|
||||
TestCase.assertEqual(subscription.state, Realm.Sync.SubscriptionState.Creating);
|
||||
return new Promise((resolve, reject) => {
|
||||
results.addListener((collection, changes) => {
|
||||
if (subscription.state === Realm.Sync.SubscriptionState.Complete) {
|
||||
TestCase.assertEqual(collection.length, 1);
|
||||
TestCase.assertTrue(collection[0].name === 'Lassy 1', "The object is not synced correctly");
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
setTimeout(function() {
|
||||
reject("listener never called");
|
||||
}, 5000);
|
||||
Realm.deleteFile(config);
|
||||
const realm = new Realm(config);
|
||||
TestCase.assertEqual(realm.objects('Dog').length, 0);
|
||||
var results = realm.objects('Dog').filtered("name == 'Lassy 1'");
|
||||
var subscription = results.subscribe();
|
||||
TestCase.assertEqual(subscription.state, Realm.Sync.SubscriptionState.Creating);
|
||||
return new Promise((resolve, reject) => {
|
||||
results.addListener((collection, changes) => {
|
||||
if (subscription.state === Realm.Sync.SubscriptionState.Complete) {
|
||||
TestCase.assertEqual(collection.length, 1);
|
||||
TestCase.assertTrue(collection[0].name === 'Lassy 1', "The object is not synced correctly");
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
})
|
||||
})
|
||||
setTimeout(function() {
|
||||
reject("listener never called");
|
||||
}, 5000);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
testPartialSyncMultipleSubscriptions() {
|
||||
@ -849,56 +845,55 @@ module.exports = {
|
||||
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 => {
|
||||
let config = {
|
||||
sync: {
|
||||
user: user,
|
||||
url: `realm://localhost:9080/~/${realmName}`,
|
||||
partial: true,
|
||||
error: (session, error) => console.log(error)
|
||||
},
|
||||
schema: [{ name: 'Dog', properties: { name: 'string' } }]
|
||||
};
|
||||
.then(() => Realm.Sync.User.login('http://localhost:9080', username, 'password'))
|
||||
.then(user => {
|
||||
let config = {
|
||||
sync: {
|
||||
user: user,
|
||||
url: `realm://localhost:9080/~/${realmName}`,
|
||||
partial: true,
|
||||
error: (session, error) => console.log(error)
|
||||
},
|
||||
schema: [{ name: 'Dog', properties: { name: 'string' } }]
|
||||
};
|
||||
|
||||
Realm.deleteFile(config);
|
||||
const realm = new Realm(config);
|
||||
TestCase.assertEqual(realm.objects('Dog').length, 0);
|
||||
var results1 = realm.objects('Dog').filtered("name == 'Lassy 1'");
|
||||
var results2 = realm.objects('Dog').filtered("name == 'Lassy 2'");
|
||||
var subscription1 = results1.subscribe();
|
||||
var subscription2 = results2.subscribe();
|
||||
Realm.deleteFile(config);
|
||||
const realm = new Realm(config);
|
||||
TestCase.assertEqual(realm.objects('Dog').length, 0);
|
||||
var results1 = realm.objects('Dog').filtered("name == 'Lassy 1'");
|
||||
var results2 = realm.objects('Dog').filtered("name == 'Lassy 2'");
|
||||
var subscription1 = results1.subscribe();
|
||||
var subscription2 = results2.subscribe();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let called1 = false;
|
||||
let called2 = false;
|
||||
results1.addListener((collection, changeset) => {
|
||||
if (subscription1.state == Realm.Sync.SubscriptionState.Complete) {
|
||||
TestCase.assertEqual(collection.length, 1);
|
||||
TestCase.assertTrue(collection[0].name === 'Lassy 1', "The object is not synced correctly");
|
||||
called1 = true;
|
||||
if (called1 && called2) {
|
||||
resolve();
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
let called1 = false;
|
||||
let called2 = false;
|
||||
results1.addListener((collection, changeset) => {
|
||||
if (subscription1.state == Realm.Sync.SubscriptionState.Complete) {
|
||||
TestCase.assertEqual(collection.length, 1);
|
||||
TestCase.assertTrue(collection[0].name === 'Lassy 1', "The object is not synced correctly");
|
||||
called1 = true;
|
||||
if (called1 && called2) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
results2.addListener((collection, changeset) => {
|
||||
if (subscription2.state == Realm.Sync.SubscriptionState.Complete) {
|
||||
TestCase.assertEqual(collection.length, 1);
|
||||
TestCase.assertTrue(collection[0].name === 'Lassy 2', "The object is not synced correctly");
|
||||
called2 = true;
|
||||
if (called1 && called2) {
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
reject("listener never called");
|
||||
}, 5000);
|
||||
}
|
||||
});
|
||||
})
|
||||
})
|
||||
results2.addListener((collection, changeset) => {
|
||||
if (subscription2.state == Realm.Sync.SubscriptionState.Complete) {
|
||||
TestCase.assertEqual(collection.length, 1);
|
||||
TestCase.assertTrue(collection[0].name === 'Lassy 2', "The object is not synced correctly");
|
||||
called2 = true;
|
||||
if (called1 && called2) {
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
reject("listener never called");
|
||||
}, 5000);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
testPartialSyncFailing() {
|
||||
@ -911,23 +906,22 @@ module.exports = {
|
||||
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 => {
|
||||
let config = {
|
||||
sync: {
|
||||
user: user,
|
||||
url: `realm://localhost:9080/~/${realmName}`,
|
||||
partial: false, // <---- calling subscribe should fail
|
||||
error: (session, error) => console.log(error)
|
||||
},
|
||||
schema: [{ name: 'Dog', properties: { name: 'string' } }]
|
||||
};
|
||||
.then(() => Realm.Sync.User.login('http://localhost:9080', username, 'password'))
|
||||
.then(user => {
|
||||
let config = {
|
||||
sync: {
|
||||
user: user,
|
||||
url: `realm://localhost:9080/~/${realmName}`,
|
||||
partial: false, // <---- calling subscribe should fail
|
||||
error: (session, error) => console.log(error)
|
||||
},
|
||||
schema: [{ name: 'Dog', properties: { name: 'string' } }]
|
||||
};
|
||||
|
||||
Realm.deleteFile(config);
|
||||
const realm = new Realm(config);
|
||||
TestCase.assertEqual(realm.objects('Dog').length, 0);
|
||||
TestCase.assertThrows(function () { var subscription = realm.objects('Dog').filtered("name == 'Lassy 1'").subscribe(); } );
|
||||
});
|
||||
Realm.deleteFile(config);
|
||||
const realm = new Realm(config);
|
||||
TestCase.assertEqual(realm.objects('Dog').length, 0);
|
||||
TestCase.assertThrows(function () { var subscription = realm.objects('Dog').filtered("name == 'Lassy 1'").subscribe(); } );
|
||||
});
|
||||
},
|
||||
|
||||
@ -941,36 +935,35 @@ module.exports = {
|
||||
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 => {
|
||||
let config = {
|
||||
sync: {
|
||||
user: user,
|
||||
url: `realm://localhost:9080/~/${realmName}`,
|
||||
partial: true,
|
||||
error: (session, error) => console.log(error)
|
||||
},
|
||||
schema: [{ name: 'Dog', properties: { name: 'string' } }]
|
||||
};
|
||||
.then(() => Realm.Sync.User.login('http://localhost:9080', username, 'password'))
|
||||
.then(user => {
|
||||
let config = {
|
||||
sync: {
|
||||
user: user,
|
||||
url: `realm://localhost:9080/~/${realmName}`,
|
||||
partial: true,
|
||||
error: (session, error) => console.log(error)
|
||||
},
|
||||
schema: [{ name: 'Dog', properties: { name: 'string' } }]
|
||||
};
|
||||
|
||||
Realm.deleteFile(config);
|
||||
const realm = new Realm(config);
|
||||
var results = realm.objects('Dog').filtered("name == 'Lassy 1'");
|
||||
var subscription = results.subscribe();
|
||||
TestCase.assertEqual(subscription.state, Realm.Sync.SubscriptionState.Creating);
|
||||
return new Promise((resolve, reject) => {
|
||||
results.addListener((collection, changes) => {
|
||||
if (subscription.state === Realm.Sync.SubscriptionState.Complete) {
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
if (subscription.state === Realm.Sync.SubscriptionState.Invalidated) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
setTimeout(function() {
|
||||
reject("listener never called");
|
||||
}, 5000);
|
||||
Realm.deleteFile(config);
|
||||
const realm = new Realm(config);
|
||||
var results = realm.objects('Dog').filtered("name == 'Lassy 1'");
|
||||
var subscription = results.subscribe();
|
||||
TestCase.assertEqual(subscription.state, Realm.Sync.SubscriptionState.Creating);
|
||||
return new Promise((resolve, reject) => {
|
||||
results.addListener((collection, changes) => {
|
||||
if (subscription.state === Realm.Sync.SubscriptionState.Complete) {
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
if (subscription.state === Realm.Sync.SubscriptionState.Invalidated) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
setTimeout(function() {
|
||||
reject("listener never called");
|
||||
}, 5000);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
2
vendor/.npmignore
vendored
2
vendor/.npmignore
vendored
@ -12,3 +12,5 @@ GCDWebServer/iOS
|
||||
GCDWebServer/Mac
|
||||
GCDWebServer/Tests
|
||||
GCDWebServer/tvOS
|
||||
|
||||
realm-*
|
||||
|
Loading…
x
Reference in New Issue
Block a user