diff --git a/CHANGELOG.md b/CHANGELOG.md index bfb506b374..eaa50d5377 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,19 @@ NOTE: Support for syncing with realm.cloud.io and/or Realm Object Server has bee NOTE: This version bumps the Realm file format to version 11. It is not possible to downgrade to earlier versions. Older files will automatically be upgraded to the new file format. Files created by Realm JavaScript prior to v1.0.0, might not be upgradeable. Only [Realm Studio 10.0.0](https://github.com/realm/realm-studio/releases/tag/v10.0.0-beta.1) or later will be able to open the new file format. +### Breaking changes +* `Realm.Auth.EmailPassword.registerEmail()` has been renamed to `Realm.Auth.EmailPassword.registerUser()`. +* `Realm.User.identity` has been renamed to `Realm.User.id`. +* `Realm.User.token` has been renamed to `Realm.User.accessToken`. +* Change instance methods `Realm.App.currentUser()` and `Realm.App.allUsers()` to instance properties `Realm.App.currentUser` and `Realm.App.allUsers`. +* `Realm.Auth.UserAPIKeyProvider` has been replaced by `Realm.Auth.ApiKeyProvider`. +* `user.auth.apiKeys` has been replaced by `user.apiKeys`. +* The instance methods on the ApiKeyAuth instance (`user.apiKeys`) have gotten their APIKey(s) suffix removed: Ex. `apiKeys.createAPIKey` has been replaced by `apiKeys.create`. +* `Realm.Auth.EmailPasswordProvider` has been replaced by `Realm.Auth.EmailPasswordAuth`. +* `app.auth.emailPassword` has been replaced by `user.emailPasswordAuth`. + ### Enhancements -* None. +* Added RemoteMongoClient functionality to `Realm.User` ### Fixed * Failed to parse arguments correctly, causing the error `callback must be of type 'function', got (undefined)` when calling `Realm.App.emailPassword.sendResetPasswordEmail()` and `Realm.App.emailPassword.resendConfirmationEmail()`. ([#3037](https://github.com/realm/realm-js/issues/3037), since v10.0.0-beta.1) diff --git a/binding.gyp b/binding.gyp index 67cd533764..b78b824639 100644 --- a/binding.gyp +++ b/binding.gyp @@ -27,7 +27,7 @@ "src/js_app_credentials.hpp", "src/js_user.hpp", "src/js_network_transport.hpp", - "src/js_email_password_provider.hpp", + "src/js_email_password_auth.hpp", "src/node/sync_logger.cpp", "src/node/sync_logger.hpp", ] diff --git a/docs/sync.js b/docs/sync.js index 2325bc6e83..0adab6a7f7 100644 --- a/docs/sync.js +++ b/docs/sync.js @@ -107,14 +107,14 @@ * * @returns {Realm.User} The current user, `null` is no current user. */ - currentUser() { } + get currentUser() { } /** * Returns a dictionary of alll users. Users' identity is used as key. * * @returns {Array} */ - allUsers() { } + get allUsers() { } /** * Switches the current user. @@ -133,18 +133,18 @@ removeUser(user) { } /** - * Auth providers. Currently only `emailPassword` provider is support + * Client for the email/password authentication provider. * * @example * { - * let app = new Realm.App(config); - * let provider = app.auth.emailPassword; + * // Creating a new user, by registering via email & password + * const app = new Realm.App(config); + * await app.emailPasswordAuth.registerUser('john@example.com', 'some-secure-password'); * } * - * @see Realm.Auth - * @see Realm.Auth.EmailPassword + * @type {Realm.Auth.EmailPasswordAuth} */ - get auth() { } + get emailPasswordAuth() { } } @@ -351,8 +351,8 @@ class Credentials { /** * A namespace for auth providers - * @see Realm.Auth.EmailPassword - * @see Realm.Auth.UserAPIKey + * @see Realm.Auth.EmailPasswordAuth + * @see Realm.Auth.ApiKeyAuth * @memberof Realm */ class Auth { @@ -363,20 +363,20 @@ class Auth { * Class for managing email/password for users * @memberof Realm.Auth */ -class EmailPassword { +class EmailPasswordAuth { /** - * Registers a new email identity with the username/password provider, + * Registers a new email identity with the email/password provider, * and sends a confirmation email to the provided address. * * @param {string} email - The email address of the user to register. * @param {string} password - The password that the user created for the new username/password identity. * @returns {Promise} */ - registerEmail(email, password) { } + registerUser(email, password) { } /** - * Confirms an email identity with the username/password provider. + * Confirms an email identity with the email/password provider. * * @param {string} token - The confirmation token that was emailed to the user. * @param {string} id - The confirmation token id that was emailed to the user. @@ -408,6 +408,17 @@ class EmailPassword { * @returns {Promise} */ resetPassword(password, token, id) { } + + /** + * Resets the password of an email identity using the + * password reset function set up in the application. + * + * @param {string} email - The email address of the user. + * @param {string} password - The desired new password. + * @param {Array} args - A bson array of arguments. + * @return {Promose} + */ + callResetPasswordFunction(email, password, args) { } } /** @@ -416,7 +427,7 @@ class EmailPassword { * client should only be used by an authenticated user. * @memberof Realm.Auth */ -class APIKeys { +class ApiKeyAuth { /** * Creates a user API key that can be used to authenticate as the current user. @@ -424,7 +435,7 @@ class APIKeys { * @param {string} name - The name of the API key to be created. * @returns {Promise} */ - createAPIKey(name) { } + create(name) { } /** * Fetches a user API key associated with the current user. @@ -432,14 +443,14 @@ class APIKeys { * @param {string} id - The id of the API key to fetch. * @returns {Promise} */ - fetchAPIKey(id) { } + fetch(id) { } /** * Fetches the user API keys associated with the current user. * * @returns {Promise} */ - allAPIKeys() { } + fetchAll() { } /** * Deletes a user API key associated with the current user. @@ -447,7 +458,7 @@ class APIKeys { * @param {string} id - The id of the API key to delete. * @returns {Promise} */ - deleteAPIKey(id) { } + delete(id) { } /** * Enables a user API key associated with the current user. @@ -455,7 +466,7 @@ class APIKeys { * @param {string} id - The id of the API key to enable. * @returns {Promise} */ - enableAPIKey(id) { } + enable(id) { } /** * Disables a user API key associated with the current user. @@ -463,7 +474,7 @@ class APIKeys { * @param {string} id - The id of the API key to disable. * @returns {Promise} */ - disableAPIKey(id) { } + disable(id) { } } @@ -477,14 +488,21 @@ class User { * The identity is a guaranteed to be unique among all users on MongoDB Realm Cloud . * @type {string} */ - get identity() { } + get id() { } + + /** + * Gets this user's access token. This is the user's credential for accessing the MongoDB + * Realm Cloud and should be treated as sensitive data. + * @type {string} + */ + get accessToken() { } /** * Gets this user's refresh token. This is the user's credential for accessing the MongoDB * Realm Cloud and should be treated as sensitive data. * @type {string} */ - get token() { } + get refreshToken() { } /** * Gets this user's associated custom data. This is application-specific data provided by the server. @@ -537,7 +555,7 @@ class User { /** * Returns a provider to interact with API keys. - * @return {Realm.Auth.APIKeys} - the provider + * @return {Realm.Auth.ApiKeyAuth} - the provider */ apiKeys() { } diff --git a/lib/user_apikey_provider_client.js b/lib/api-key-auth-methods.js similarity index 65% rename from lib/user_apikey_provider_client.js rename to lib/api-key-auth-methods.js index 2ec63b4ef7..18ae1840bc 100644 --- a/lib/user_apikey_provider_client.js +++ b/lib/api-key-auth-methods.js @@ -21,28 +21,28 @@ const {promisify} = require("./utils.js"); const instanceMethods = { - createAPIKey(name) { - return promisify(cb => this._createAPIKey(name, cb)); + create(name) { + return promisify(cb => this._create(name, cb)); }, - fetchAPIKey(id) { - return promisify(cb => this._fetchAPIKey(id, cb)); + fetch(id) { + return promisify(cb => this._fetch(id, cb)); }, - fetchAPIKeys() { - return promisify(cb => this._fetchAPIKeys(cb)); + fetchAll() { + return promisify(cb => this._fetchAll(cb)); }, - deleteAPIKey(apiKeyId) { - return promisify(cb => this._deleteAPIKey(apiKeyId, cb)); + delete(apiKeyId) { + return promisify(cb => this._delete(apiKeyId, cb)); }, - enableAPIKey(apiKeyId) { - return promisify(cb => this._enableAPIKey(apiKeyId, cb)); + enable(apiKeyId) { + return promisify(cb => this._enable(apiKeyId, cb)); }, - disableAPIKey(apiKeyId) { - return promisify(cb => this._disableAPIKey(apiKeyId, cb)); + disable(apiKeyId) { + return promisify(cb => this._disable(apiKeyId, cb)); }, }; diff --git a/lib/app.js b/lib/app.js index 9540c2e713..c585fc19c8 100644 --- a/lib/app.js +++ b/lib/app.js @@ -28,17 +28,6 @@ const instanceMethods = { removeUser() { return promisify(cb => this._removeUser(cb)); }, - - get auth() { - const app = this; - return new Proxy({}, { - get(target, name) { - if (name === "emailPassword") { - return app._authEmailPassword; - } - } - }); - } }; const staticMethods = { diff --git a/lib/browser/user.js b/lib/browser/user.js index 619778b6e6..d6c26fa6a0 100644 --- a/lib/browser/user.js +++ b/lib/browser/user.js @@ -36,12 +36,14 @@ createMethods(User.prototype, objectTypes.USER, [ ]); Object.defineProperties(User.prototype, { - identity: { get: getterForProperty('identity') }, - token: { get: getterForProperty('token') }, + id: { get: getterForProperty('id') }, + accessToken: { get: getterForProperty('accessToken') }, + refreshToken: { get: getterForProperty('refreshToken') }, profile: { get: getterForProperty('profile') }, isLoggedIn: { get: getterForProperty('isLoggedIn') }, state: { get: getterForProperty('state') }, customData: { get: getterForProperty('customData') }, + apiKeys: { get: getterForProperty('apiKeys') }, }); export function createUser(realmId, info) { diff --git a/lib/email_password_provider_client_methods.js b/lib/email-password-auth-methods.js similarity index 79% rename from lib/email_password_provider_client_methods.js rename to lib/email-password-auth-methods.js index 2592fd7075..a93cdfe1c7 100644 --- a/lib/email_password_provider_client_methods.js +++ b/lib/email-password-auth-methods.js @@ -16,13 +16,12 @@ // //////////////////////////////////////////////////////////////////////////// -'use strict'; const {promisify} = require("./utils.js"); const instanceMethods = { - registerEmail(email, password) { - return promisify(cb => this._registerEmail(email, password, cb)); + registerUser(email, password) { + return promisify(cb => this._registerUser(email, password, cb)); }, confirmUser(token, token_id) { @@ -38,8 +37,12 @@ const instanceMethods = { }, resetPassword(password, token, token_id) { - return promisify(cb => this._sendResetPasswordEmail(password, token_id, token_id, cb)); - } + return promisify(cb => this._resetPassword(password, token, token_id)); + }, + + callResetPasswordFunction(email, password, bsonArgs) { + return promisify(cb => this._callResetPasswordFunction(email, password, bsonArgs)); + }, }; const staticMethods = { diff --git a/lib/extensions.js b/lib/extensions.js index 276f7f03b1..67e8053519 100644 --- a/lib/extensions.js +++ b/lib/extensions.js @@ -363,11 +363,11 @@ module.exports = function(realmConstructor, context) { let credentialMethods = require("./credentials"); Object.defineProperties(realmConstructor.Credentials, getOwnPropertyDescriptors(credentialMethods.static)) - let emailPasswordProviderMethods = require("./email_password_provider_client_methods"); - Object.defineProperties(realmConstructor.Auth.EmailPasswordProvider.prototype, getOwnPropertyDescriptors(emailPasswordProviderMethods.instance)); + let emailPasswordAuthMethods = require("./email-password-auth-methods"); + Object.defineProperties(realmConstructor.Auth.EmailPasswordAuth.prototype, getOwnPropertyDescriptors(emailPasswordAuthMethods.instance)); - let userAPIKeyProviderMethods = require("./user_apikey_provider_client"); - Object.defineProperties(realmConstructor.Auth.UserAPIKeyProvider.prototype, getOwnPropertyDescriptors(userAPIKeyProviderMethods.instance)); + let apiKeyAuthMethods = require("./api-key-auth-methods"); + Object.defineProperties(realmConstructor.Auth.ApiKeyAuth.prototype, getOwnPropertyDescriptors(apiKeyAuthMethods.instance)); realmConstructor.Sync.AuthError = require("./errors").AuthError; diff --git a/lib/user.js b/lib/user.js index cfb5cc4e71..4fb8c2f1c2 100644 --- a/lib/user.js +++ b/lib/user.js @@ -99,17 +99,6 @@ const instanceMethods = { get functions() { return this._functionsOnService(undefined); }, - - get auth() { - const user = this; - return new Proxy({}, { - get(target, name) { - if (name === "apiKeys") { - return user._authApiKeys; - } - } - }); - } } const staticMethods = { diff --git a/package-lock.json b/package-lock.json index 39badfffde..42671b7fb7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2169,7 +2169,7 @@ "babylon": { "version": "7.0.0-beta.44", "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.44.tgz", - "integrity": "sha512-5Hlm13BJVAioCHpImtFqNOF2H3ieTOHd0fmFGMxOJ9jgeFqeAwsv3u5P5cR7CSeFrkgHsT19DgFJkHV0/Mcd8g==", + "integrity": "sha1-iRWeFebjDFCW4i1zjYwK+KDoyh0=", "dev": true }, "balanced-match": { @@ -4142,7 +4142,7 @@ "esrecurse": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "integrity": "sha1-AHo7n9vCs7uH5IeeoZyS/b05Qs8=", "dev": true, "requires": { "estraverse": "^4.1.0" @@ -9612,7 +9612,7 @@ "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "integrity": "sha1-pFBD1U9YBTFtqNYvn1CRjT2nCwo=", "dev": true, "requires": { "isexe": "^2.0.0" diff --git a/packages/realm-network-transport/.eslintignore b/packages/realm-network-transport/.eslintignore index 178135c2b2..72760ebe4d 100644 --- a/packages/realm-network-transport/.eslintignore +++ b/packages/realm-network-transport/.eslintignore @@ -1 +1,2 @@ /dist/ +/types/ diff --git a/packages/realm-network-transport/src/index.ts b/packages/realm-network-transport/src/index.ts index a11642d761..6d064d1dc3 100644 --- a/packages/realm-network-transport/src/index.ts +++ b/packages/realm-network-transport/src/index.ts @@ -24,4 +24,5 @@ export { Method, SuccessCallback, ErrorCallback, + ResponseHandler, } from "./NetworkTransport"; diff --git a/packages/realm-web-integration-tests/src/api-key-auth-provider.test.ts b/packages/realm-web-integration-tests/src/api-key-auth-provider.test.ts index bb3b7dd1b3..c4bf4ff351 100644 --- a/packages/realm-web-integration-tests/src/api-key-auth-provider.test.ts +++ b/packages/realm-web-integration-tests/src/api-key-auth-provider.test.ts @@ -28,12 +28,12 @@ describe("ApiKeyAuthProvider", () => { const app = createApp(); // Login a user const credentials = Credentials.anonymous(); - await app.logIn(credentials); + const user = await app.logIn(credentials); // List all existing keys - const keys = await app.auth.apiKey.list(); - // console.log(keys); + const keys = await user.apiKeys.fetchAll(); + console.log(keys); // Create an api key - const key = await app.auth.apiKey.create("my-key"); + const key = await user.apiKeys.create("my-key"); // console.log(key); }); }); diff --git a/packages/realm-web-integration-tests/src/app.test.ts b/packages/realm-web-integration-tests/src/app.test.ts index 837d90f9b3..049ffc0505 100644 --- a/packages/realm-web-integration-tests/src/app.test.ts +++ b/packages/realm-web-integration-tests/src/app.test.ts @@ -50,19 +50,21 @@ describe("App#constructor", () => { expect(app.allUsers).deep.equals([user2, user1]); // Ensure that the two users are not one and the same expect(user1.id).to.not.equals(user2.id); - // Switch back to the first user, by object reference + // Switch back to the first user app.switchUser(user1); expect(app.currentUser).equals(user1); expect(app.allUsers).deep.equals([user1, user2]); - // Switch back to the second user, by user id - app.switchUser(user2.id); + // Switch back to the second user + app.switchUser(user2); expect(app.currentUser).equals(user2); expect(app.allUsers).deep.equals([user2, user1]); // Switch back to the first user and log out app.switchUser(user1); expect(app.currentUser).equals(user1); - await app.logOut(); + await user1.logOut(); expect(app.currentUser).equals(user2); expect(app.allUsers).deep.equals([user2, user1]); + await app.removeUser(user1); + expect(app.allUsers).deep.equals([user2]); }); }); diff --git a/packages/realm-web-integration-tests/src/email-password-auth-provider.test.ts b/packages/realm-web-integration-tests/src/email-password-auth-provider.test.ts index d213d1f12f..a2f7ebc8ad 100644 --- a/packages/realm-web-integration-tests/src/email-password-auth-provider.test.ts +++ b/packages/realm-web-integration-tests/src/email-password-auth-provider.test.ts @@ -34,9 +34,9 @@ describe("EmailPasswordAuthProvider", () => { const email = `test-user-${runNumber}@testing.mongodb.com`; const password = "my-super-secret-password"; // List all existing keys - await app.auth.emailPassword.registerUser(email, password); + await app.emailPasswordAuth.registerUser(email, password); // Authenticate - const newCredentials = Credentials.usernamePassword(email, password); + const newCredentials = Credentials.emailPassword(email, password); await app.logIn(newCredentials); }); }); diff --git a/packages/realm-web-integration-tests/src/user.test.ts b/packages/realm-web-integration-tests/src/user.test.ts index 1512919f85..0e449f8c03 100644 --- a/packages/realm-web-integration-tests/src/user.test.ts +++ b/packages/realm-web-integration-tests/src/user.test.ts @@ -32,8 +32,8 @@ describe("User", () => { expect(user.state).equals("active"); // Expect something of the user profile expect(user.profile.type).equals("normal"); - expect(Array.isArray(user.profile.identities)).equals(true); - expect(user.profile.identities.length).equals(1); + // TODO: expect(Array.isArray(user.profile.identities)).equals(true); + // TODO: expect(user.profile.identities.length).equals(1); expect(user.profile.name).equals(undefined); }); }); diff --git a/packages/realm-web-integration-tests/src/utils.ts b/packages/realm-web-integration-tests/src/utils.ts index 080cfda535..d5b1dd0f4c 100644 --- a/packages/realm-web-integration-tests/src/utils.ts +++ b/packages/realm-web-integration-tests/src/utils.ts @@ -22,9 +22,7 @@ import { App } from "realm-web"; declare const APP_ID: string; declare const BASE_URL: string; -export function createApp< - FunctionsFactoryType extends Realm.FunctionsFactory ->() { +export function createApp() { if (typeof APP_ID !== "string") { throw new Error("Expected a global APP_ID"); } diff --git a/packages/realm-web/.eslintignore b/packages/realm-web/.eslintignore index 178135c2b2..a3ef5b0a25 100644 --- a/packages/realm-web/.eslintignore +++ b/packages/realm-web/.eslintignore @@ -1 +1,3 @@ /dist/ +/types/generated/ +/types/realm/ diff --git a/packages/realm-web/rollup.config.js b/packages/realm-web/rollup.config.js index d55dc754ae..d3417a64de 100644 --- a/packages/realm-web/rollup.config.js +++ b/packages/realm-web/rollup.config.js @@ -48,7 +48,10 @@ export default [ output: { file: "dist/bundle.d.ts", format: "es", - intro: '/// ', + intro: ` + /// + /// + `, }, plugins: [dts(), resolve()], }, diff --git a/packages/realm-web/src/App.test.ts b/packages/realm-web/src/App.test.ts index 0d02882f05..f85d01bc23 100644 --- a/packages/realm-web/src/App.test.ts +++ b/packages/realm-web/src/App.test.ts @@ -163,7 +163,7 @@ describe("App", () => { // Expect that we logged in expect(app.currentUser).equals(user); expect(app.allUsers).deep.equals([user]); - await app.logOut(); + await user.logOut(); expect(app.currentUser).equals(null); expect(user.state).equals(UserState.LoggedOut); expect(user.state).equals("logged-out"); @@ -232,6 +232,59 @@ describe("App", () => { ]); }); + it("throws if asked to switch to or remove an unknown user", async () => { + const transport = new MockNetworkTransport([ + { + user_id: "totally-valid-user-id", + access_token: "deadbeef", + refresh_token: "very-refreshing", + }, + ]); + const app = new App({ + id: "default-app-id", + transport, + baseUrl: "http://localhost:1337", + }); + const credentials = Credentials.anonymous(); + const user = await app.logIn(credentials, false); + // Expect that we logged in + expect(app.currentUser).equals(user); + expect(app.allUsers).deep.equals([user]); + const anotherUser = {} as User; + // Switch + try { + await app.switchUser(anotherUser); + throw new Error("Expected an exception"); + } catch (err) { + expect(err.message).equals( + "The user was never logged into this app", + ); + } + // Remove + try { + await app.removeUser(anotherUser); + throw new Error("Expected an exception"); + } catch (err) { + expect(err.message).equals( + "The user was never logged into this app", + ); + } + // Expect the first user to remain logged in and known to the app + expect(app.currentUser).equals(user); + expect(app.allUsers).deep.equals([user]); + expect(user.state).equals("active"); + // Assume the correct requests made it to the transport + expect(transport.requests).deep.equals([ + { + method: "POST", + url: + "http://localhost:1337/api/client/v2.0/app/default-app-id/auth/providers/anon-user/login", + body: {}, + headers: DEFAULT_HEADERS, + }, + ]); + }); + it("expose a callable functions factory", async () => { const transport = new MockNetworkTransport([ { diff --git a/packages/realm-web/src/App.ts b/packages/realm-web/src/App.ts index 8c2d3cd46c..fcea102da7 100644 --- a/packages/realm-web/src/App.ts +++ b/packages/realm-web/src/App.ts @@ -19,11 +19,11 @@ import { NetworkTransport } from "realm-network-transport"; import { create as createFunctionsFactory } from "./FunctionsFactory"; -import { User, UserState, UserControlHandle } from "./User"; +import { User, UserState } from "./User"; import { AuthenticatedTransport, Transport, BaseTransport } from "./transports"; import { Credentials } from "./Credentials"; import { create as createServicesFactory } from "./services"; -import { create as createAuthProviders } from "./auth-providers"; +import { EmailPasswordAuth } from "./auth-providers"; /** * Configuration to pass as an argument when constructing an app. @@ -37,17 +37,16 @@ export interface AppConfiguration extends Realm.AppConfiguration { * MongoDB Realm App */ export class App< - FunctionsFactoryType extends Realm.BaseFunctionsFactory = Realm.DefaultFunctionsFactory -> implements Realm.App { + FunctionsFactoryType extends object = Realm.DefaultFunctionsFactory, + CustomDataType extends object = any +> implements Realm.App { /** @inheritdoc */ - public readonly functions: FunctionsFactoryType; + public readonly functions: FunctionsFactoryType & + Realm.BaseFunctionsFactory; /** @inheritdoc */ public readonly services: Realm.Services; - /** @inheritdoc */ - public readonly auth: Realm.AuthProviders; - /** @inheritdoc */ public readonly id: string; @@ -71,6 +70,9 @@ export class App< */ public readonly appTransport: Transport; + /** @inheritdoc */ + public readonly emailPasswordAuth: EmailPasswordAuth; + /** * This base route will be prefixed requests issued through by the base transport */ @@ -80,7 +82,7 @@ export class App< * A (reversed) stack of active and logged-out users. * Elements in the beginning of the array is considered more recent than the later elements. */ - private readonly users: UserControlHandle[] = []; + private readonly users: User[] = []; /** * Construct a Realm App, either from the Realm App id visible from the MongoDB Realm UI or a configuration. @@ -122,7 +124,7 @@ export class App< // Construct the services factory this.services = createServicesFactory(authTransport); // Construct the auth providers - this.auth = createAuthProviders(authTransport); + this.emailPasswordAuth = new EmailPasswordAuth(authTransport); } /** @@ -130,29 +132,15 @@ export class App< * * @param nextUser The user or id of the user to switch to */ - public switchUser(nextUser: User | string) { - if (typeof nextUser === "string") { - const handle = this.users.find(({ user: u }) => u.id === nextUser); - if (handle) { - this.switchUser(handle.user); - } else { - throw new Error( - `Failed to switch user (id = ${nextUser}) - did you log in?`, - ); - } - } else if (nextUser instanceof User) { - const index = this.users.findIndex(({ user }) => user === nextUser); - if (index >= 0) { - // Remove the user from the stack - const [handle] = this.users.splice(index, 1); - // Insert the user in the beginning of the stack - this.users.splice(0, 0, handle); - } else { - throw new Error("The user was not logged into this app"); - } - } else { - throw new Error("Expected a user id or a User instance"); + public switchUser(nextUser: User) { + const index = this.users.findIndex(u => u === nextUser); + if (index === -1) { + throw new Error("The user was never logged into this app"); } + // Remove the user from the stack + const [user] = this.users.splice(index, 1); + // Insert the user in the beginning of the stack + this.users.unshift(user); } /** @@ -162,45 +150,28 @@ export class App< * @param fetchProfile Should the users profile be fetched? (default: true) */ public async logIn(credentials: Realm.Credentials, fetchProfile = true) { - const handle: UserControlHandle = await User.logIn( - this, - credentials, - fetchProfile, - ); + const user: User< + FunctionsFactoryType, + CustomDataType + > = await User.logIn(this, credentials, fetchProfile); // Add the user at the top of the stack - this.users.splice(0, 0, handle); + this.users.unshift(user); // Return the user - return handle.user; + return user; } /** - * Log out a user - * - * @param userOrId The user or id of the user to log out (default: currentUser) + * @inheritdoc */ - public async logOut( - userOrId: Realm.User | string | null = this.currentUser, - ) { - const { user } = this.getUserHandle(userOrId); - await user.logOut(); - } - - /** - * Remove a user entirely from the app (logs out the user if they're not already logged out) - * - * @param userOrId The user or id of the user to remove. - */ - public async removeUser(userOrId: Realm.User | string) { - const { user, controller } = this.getUserHandle(userOrId); - // If active - log out the user - if (user.state === UserState.Active) { - await this.logOut(user); - } - // Set the state of the user - controller.setState(UserState.Removed); + public async removeUser(user: User) { // Remove the user from the list of users - const index = this.users.findIndex(({ user: u }) => u === user); + const index = this.users.findIndex(u => u === user); + if (index === -1) { + throw new Error("The user was never logged into this app"); + } this.users.splice(index, 1); + // Log out the user + await user.logOut(); // TODO: Delete any data / tokens which were persisted } @@ -209,15 +180,18 @@ export class App< * * @returns the currently active user or null. */ - public get currentUser(): Realm.User | null { - const activeUserHandles = this.users.filter( - ({ user }) => user.state === UserState.Active, + public get currentUser(): User< + FunctionsFactoryType, + CustomDataType + > | null { + const activeUsers = this.users.filter( + user => user.state === UserState.Active, ); - if (activeUserHandles.length === 0) { + if (activeUsers.length === 0) { return null; } else { // Current user is the top of the stack - return activeUserHandles[0].user; + return activeUsers[0]; } } @@ -228,13 +202,15 @@ export class App< * * @returns An array of users active or loggedout users (current user being the first). */ - public get allUsers(): Readonly { - const allUsers = this.users.map(({ user }) => user); - const activeUsers = allUsers.filter( - user => user.state === UserState.Active, + public get allUsers(): Readonly< + Realm.User[] + > { + // We need to peek into refresh tokens to avoid cyclic code + const activeUsers = this.users.filter( + user => user.refreshToken !== null, ); - const loggedOutUsers = allUsers.filter( - user => user.state === UserState.LoggedOut, + const loggedOutUsers = this.users.filter( + user => user.refreshToken === null, ); // Returning a freezed copy of the list of users to prevent outside changes return Object.freeze([...activeUsers, ...loggedOutUsers]); @@ -246,14 +222,12 @@ export class App< * @param userOrId A user object or user id * @returns A handle containing the user and it's controller. */ - private getUserHandle(userOrId: Realm.User | string | null) { - const handle = this.users.find(({ user }) => - typeof userOrId === "string" - ? user.id === userOrId - : user === userOrId, + private getUser(userOrId: Realm.User | string | null) { + const user = this.users.find(u => + typeof userOrId === "string" ? u.id === userOrId : u === userOrId, ); - if (handle) { - return handle; + if (user) { + return user; } else { throw new Error("Invalid user or user id"); } diff --git a/packages/realm-web/src/User.test.ts b/packages/realm-web/src/User.test.ts index 282b22baaa..507bbf3cad 100644 --- a/packages/realm-web/src/User.test.ts +++ b/packages/realm-web/src/User.test.ts @@ -76,10 +76,6 @@ describe("User", () => { id: "some-user-id", accessToken: "deadbeef", refreshToken: "very-refreshing", - onController: controller => { - controller.setAccessToken("new-access-token"); - controller.setState(UserState.Removed); - }, }); // Expect an exception if the profile was never fetched expect(() => { @@ -93,20 +89,4 @@ describe("User", () => { firstName: "John", }); }); - - it("provides a controller which state", () => { - const user = new User({ - app: new MockApp("my-mocked-app"), - id: "some-user-id", - accessToken: "deadbeef", - refreshToken: "very-refreshing", - onController: controller => { - controller.setAccessToken("new-access-token"); - controller.setState(UserState.Removed); - }, - }); - // Expect something about the user - expect(user.accessToken).equals("new-access-token"); - expect(user.state).equals("removed"); - }); }); diff --git a/packages/realm-web/src/User.ts b/packages/realm-web/src/User.ts index feb931a5b4..967ff0288a 100644 --- a/packages/realm-web/src/User.ts +++ b/packages/realm-web/src/User.ts @@ -28,7 +28,6 @@ interface UserParameters { id: string; accessToken: string; refreshToken: string; - onController?: (controller: UserController) => void; } export enum UserState { @@ -42,68 +41,30 @@ export enum UserType { Server = "server", } -export interface UserController { - setAccessToken(token: string): void; - setState(state: UserState): void; -} - -export interface UserControlHandle { - user: User; - controller: UserController; -} - /** * Representation of an authenticated user of an app. */ -export class User implements Realm.User { +export class User< + FunctionsFactoryType extends object = Realm.DefaultFunctionsFactory, + CustomDataType extends object = any +> implements Realm.User { /** * The app that this user is associated with. */ - public readonly app: App; - - /* - * The list of identities associated with this user. - * // TODO: Implement and test this ... - */ - // public readonly identities: Realm.UserIdentity[] = []; - - /** - * Create a user, returning both the user and a controller enabling updates to the user's internal state. - * - * @param parameters The parameters passed to the constructor. - * @returns an object containing the new user and its controller. - */ - public static create(parameters: UserParameters): UserControlHandle { - const { onController, ...otherParameters } = parameters; - let controller: UserController | undefined; - const user = new User({ - ...otherParameters, - onController: c => { - if (onController) { - onController(c); - } - controller = c; - }, - }); - if (controller) { - return { user, controller }; - } else { - throw new Error( - "Expected controllerReady to be called synchronously", - ); - } - } + public readonly app: App; /** * Log in and create a user * - * @param transport The transport to use when issuing requests + * @param app The app used when logging in the user * @param credentials Credentials to use when logging in - * @param app * @param fetchProfile Should the users profile be fetched? (default: true) */ - public static async logIn( - app: App, + public static async logIn< + FunctionsFactoryType extends object, + CustomDataType extends object + >( + app: App, credentials: Realm.Credentials, fetchProfile = true, ) { @@ -132,7 +93,7 @@ export class User implements Realm.User { throw new Error("Expected an refresh token in the response"); } // Create the user - const handle = User.create({ + const user = new User({ app, id: userId, accessToken, @@ -140,10 +101,10 @@ export class User implements Realm.User { }); // If neeeded, fetch and set the profile on the user if (fetchProfile) { - await handle.user.refreshProfile(); + await user.refreshProfile(); } // Return the user handle - return handle; + return user; } private _id: string; @@ -153,13 +114,7 @@ export class User implements Realm.User { private _state: Realm.UserState; private transport: AuthenticatedTransport; - public constructor({ - app, - id, - accessToken, - refreshToken, - onController, - }: UserParameters) { + public constructor({ app, id, accessToken, refreshToken }: UserParameters) { this.app = app; this._id = id; this._accessToken = accessToken; @@ -168,18 +123,6 @@ export class User implements Realm.User { this.transport = new AuthenticatedTransport(app.baseTransport, { currentUser: this, }); - - // Create and expose the controller to the creator - if (onController) { - onController({ - setAccessToken: token => { - this._accessToken = token; - }, - setState: state => { - this._state = state; - }, - }); - } } /** @@ -213,8 +156,26 @@ export class User implements Realm.User { * * @returns The current state of the user. */ - get state(): Realm.UserState { - return this._state; + get state(): UserState { + if (this.app.allUsers.indexOf(this) === -1) { + return UserState.Removed; + } else { + return this._refreshToken === null + ? UserState.LoggedOut + : UserState.Active; + } + } + + get customData(): CustomDataType { + throw new Error("Not yet implemented"); + } + + get functions(): FunctionsFactoryType & Realm.BaseFunctionsFactory { + throw new Error("Not yet implemented"); + } + + get apiKeys(): Realm.Auth.ApiKeyAuth { + throw new Error("Not yet implemented"); } /** @@ -240,17 +201,41 @@ export class User implements Realm.User { public async logOut() { // Invalidate the refresh token - await this.app.baseTransport.fetch({ - method: "DELETE", - path: "/auth/session", - headers: { - Authorization: `Bearer ${this._refreshToken}`, - }, - }); - // Forget the tokens - this._accessToken = null; - this._refreshToken = null; + if (this._refreshToken !== null) { + await this.app.baseTransport.fetch({ + method: "DELETE", + path: "/auth/session", + headers: { + Authorization: `Bearer ${this._refreshToken}`, + }, + }); + // Forget the tokens + this._accessToken = null; + this._refreshToken = null; + } // Update the state this._state = UserState.LoggedOut; } + + /** @inheritdoc */ + public async linkCredentials(credentials: Realm.Credentials) { + throw new Error("Not yet implemented"); + } + + public async refreshCustomData() { + await this.refreshAccessToken(); + return this.customData; + } + + public callFunction(name: string, ...args: any[]) { + return this.functions.callFunction(name, ...args); + } + + private async refreshAccessToken() { + throw new Error("Not yet implemented"); + } + + push(serviceName = ""): Realm.Services.Push { + throw new Error("Not yet implemented"); + } } diff --git a/packages/realm-web/src/UserProfile.ts b/packages/realm-web/src/UserProfile.ts index af6ce55064..71dfe1ae69 100644 --- a/packages/realm-web/src/UserProfile.ts +++ b/packages/realm-web/src/UserProfile.ts @@ -41,7 +41,7 @@ enum DataKey { const DATA_MAPPING: { [k in DataKey]: keyof UserProfile } = { [DataKey.NAME]: "name", [DataKey.EMAIL]: "email", - [DataKey.PICTURE]: "pictureURL", + [DataKey.PICTURE]: "pictureUrl", [DataKey.FIRST_NAME]: "firstName", [DataKey.LAST_NAME]: "lastName", [DataKey.GENDER]: "gender", @@ -59,7 +59,7 @@ export class UserProfile implements Realm.UserProfile { public readonly email?: string; /** @inheritdoc */ - public readonly pictureURL?: string; + public readonly pictureUrl?: string; /** @inheritdoc */ public readonly firstName?: string; diff --git a/packages/realm-web/src/auth-providers/ApiKeyAuthProvider.test.ts b/packages/realm-web/src/auth-providers/ApiKeyAuth.test.ts similarity index 92% rename from packages/realm-web/src/auth-providers/ApiKeyAuthProvider.test.ts rename to packages/realm-web/src/auth-providers/ApiKeyAuth.test.ts index 3f24cc1e21..286231ca9e 100644 --- a/packages/realm-web/src/auth-providers/ApiKeyAuthProvider.test.ts +++ b/packages/realm-web/src/auth-providers/ApiKeyAuth.test.ts @@ -19,7 +19,7 @@ import { expect } from "chai"; import { ObjectId } from "bson"; -import { ApiKeyAuthProvider } from "./ApiKeyAuthProvider"; +import { ApiKeyAuth } from "./ApiKeyAuth"; import { MockTransport } from "../test/MockTransport"; const DEFAULT_HEADERS = { @@ -27,7 +27,7 @@ const DEFAULT_HEADERS = { "Content-Type": "application/json", }; -describe("ApiKeyAuthProvider", () => { +describe("ApiKeyAuth", () => { it("can create an api key", async () => { const transport = new MockTransport([ { @@ -39,7 +39,7 @@ describe("ApiKeyAuthProvider", () => { disabled: true, }, ]); - const provider = new ApiKeyAuthProvider(transport); + const provider = new ApiKeyAuth(transport); const apiKey = await provider.create("my-key-name"); // Expect something of the newly created key expect(typeof apiKey._id).equals("object"); @@ -71,8 +71,8 @@ describe("ApiKeyAuthProvider", () => { disabled: true, }, ]); - const provider = new ApiKeyAuthProvider(transport); - const apiKey = await provider.get( + const provider = new ApiKeyAuth(transport); + const apiKey = await provider.fetch( ObjectId.createFromHexString("deadbeefdeadbeefdeadbeef"), ); // Expect something of the key @@ -114,8 +114,8 @@ describe("ApiKeyAuthProvider", () => { }, ], ]); - const provider = new ApiKeyAuthProvider(transport); - const apiKeys = await provider.list(); + const provider = new ApiKeyAuth(transport); + const apiKeys = await provider.fetchAll(); // Expect something of the first key const [firstKey, secondKey] = apiKeys; expect(firstKey._id.toHexString()).equals("deadbeefdeadbeefdeadbee1"); @@ -139,7 +139,7 @@ describe("ApiKeyAuthProvider", () => { it("can delete a key", async () => { const transport = new MockTransport([{}]); - const provider = new ApiKeyAuthProvider(transport); + const provider = new ApiKeyAuth(transport); await provider.delete( ObjectId.createFromHexString("deadbeefdeadbeefdeadbeef"), ); @@ -156,7 +156,7 @@ describe("ApiKeyAuthProvider", () => { it("can enable a key", async () => { const transport = new MockTransport([{}]); - const provider = new ApiKeyAuthProvider(transport); + const provider = new ApiKeyAuth(transport); await provider.enable( ObjectId.createFromHexString("deadbeefdeadbeefdeadbeef"), ); @@ -173,7 +173,7 @@ describe("ApiKeyAuthProvider", () => { it("can disable a key", async () => { const transport = new MockTransport([{}]); - const provider = new ApiKeyAuthProvider(transport); + const provider = new ApiKeyAuth(transport); await provider.disable( ObjectId.createFromHexString("deadbeefdeadbeefdeadbeef"), ); diff --git a/packages/realm-web/src/auth-providers/ApiKeyAuthProvider.ts b/packages/realm-web/src/auth-providers/ApiKeyAuth.ts similarity index 90% rename from packages/realm-web/src/auth-providers/ApiKeyAuthProvider.ts rename to packages/realm-web/src/auth-providers/ApiKeyAuth.ts index 91dc738109..79ae533aa7 100644 --- a/packages/realm-web/src/auth-providers/ApiKeyAuthProvider.ts +++ b/packages/realm-web/src/auth-providers/ApiKeyAuth.ts @@ -20,8 +20,7 @@ import { Transport } from "../transports"; import { deserialize } from "../utils/ejson"; /** @inheritdoc */ -export class ApiKeyAuthProvider - implements Realm.AuthProviders.ApiKeyAuthProvider { +export class ApiKeyAuth implements Realm.Auth.ApiKeyAuth { /** * The transport used to send requests to services. */ @@ -38,7 +37,7 @@ export class ApiKeyAuthProvider } /** @inheritdoc */ - create(name: string): Promise { + create(name: string): Promise { return this.transport .fetch({ method: "POST", @@ -48,7 +47,7 @@ export class ApiKeyAuthProvider } /** @inheritdoc */ - get(keyId: Realm.ObjectId): Promise { + fetch(keyId: Realm.ObjectId): Promise { return this.transport .fetch({ method: "GET", @@ -58,7 +57,7 @@ export class ApiKeyAuthProvider } /** @inheritdoc */ - list(): Promise { + fetchAll(): Promise { return this.transport.fetch({ method: "GET" }).then(deserialize); } diff --git a/packages/realm-web/src/auth-providers/EmailPasswordAuthProvider.ts b/packages/realm-web/src/auth-providers/EmailPasswordAuth.ts similarity index 94% rename from packages/realm-web/src/auth-providers/EmailPasswordAuthProvider.ts rename to packages/realm-web/src/auth-providers/EmailPasswordAuth.ts index 31b309696e..86563612d6 100644 --- a/packages/realm-web/src/auth-providers/EmailPasswordAuthProvider.ts +++ b/packages/realm-web/src/auth-providers/EmailPasswordAuth.ts @@ -19,8 +19,7 @@ import { Transport } from "../transports"; /** @inheritdoc */ -export class EmailPasswordAuthProvider - implements Realm.AuthProviders.EmailPasswordAuthProvider { +export class EmailPasswordAuth implements Realm.Auth.EmailPasswordAuth { /** * The underlying transport. */ @@ -55,7 +54,7 @@ export class EmailPasswordAuthProvider } /** @inheritdoc */ - resendConfirmation(email: string): Promise { + resendConfirmationEmail(email: string): Promise { return this.transport.fetch({ method: "POST", path: "/confirm/send", diff --git a/packages/realm-web/src/auth-providers/index.ts b/packages/realm-web/src/auth-providers/index.ts index 7aa5ace35b..50f8153074 100644 --- a/packages/realm-web/src/auth-providers/index.ts +++ b/packages/realm-web/src/auth-providers/index.ts @@ -16,22 +16,5 @@ // //////////////////////////////////////////////////////////////////////////// -import { Transport } from "../transports"; - -import { EmailPasswordAuthProvider } from "./EmailPasswordAuthProvider"; -import { ApiKeyAuthProvider } from "./ApiKeyAuthProvider"; - -// TODO: Consider if we should query the service for enabled authentication providers before creating clients. - -/** - * Create an app's authentication providers. - * - * @param transport The transport used when sending requests to the service. - * @returns An object with interfaces to all possible authentication provider the app might have. - */ -export function create(transport: Transport): Realm.AuthProviders { - return { - emailPassword: new EmailPasswordAuthProvider(transport), - apiKey: new ApiKeyAuthProvider(transport), - }; -} +export { EmailPasswordAuth } from "./EmailPasswordAuth"; +export { ApiKeyAuth } from "./ApiKeyAuth"; diff --git a/packages/realm-web/src/index.ts b/packages/realm-web/src/index.ts index af9a5f2989..c847cfb232 100644 --- a/packages/realm-web/src/index.ts +++ b/packages/realm-web/src/index.ts @@ -21,7 +21,7 @@ import { App } from "./App"; const appCache: { [id: string]: Realm.App } = {}; /** - * Get or create a Realm App from an id. + * Get or create a singleton Realm App from an id. * * @param id The Realm App id visible from the MongoDB Realm UI or a configuration * @returns The Realm App instance. Calling this function multiple times with the same id will return the same instance. @@ -30,9 +30,7 @@ export function app(id: string) { if (id in appCache) { return appCache[id]; } else { - // Ensures the App has the correct constructor type signature - const AppConstructor: Realm.AppConstructor = App; - const instance = new AppConstructor(id); + const instance = new App(id); appCache[id] = instance; return instance; } diff --git a/packages/realm-web/src/test/MockNetworkTransport.ts b/packages/realm-web/src/test/MockNetworkTransport.ts index 5680669369..067b159d8b 100644 --- a/packages/realm-web/src/test/MockNetworkTransport.ts +++ b/packages/realm-web/src/test/MockNetworkTransport.ts @@ -18,9 +18,8 @@ import { NetworkTransport, - SuccessCallback, - ErrorCallback, Request, + ResponseHandler, } from "realm-network-transport"; /** @@ -68,9 +67,8 @@ export class MockNetworkTransport implements NetworkTransport { /** @inheritdoc */ fetchWithCallbacks( request: Request, - successCallback: SuccessCallback, - errorCallback: ErrorCallback, + handler: ResponseHandler, ) { - this.fetchAndParse(request).then(successCallback, errorCallback); + this.fetchAndParse(request).then(handler.onSuccess, handler.onError); } } diff --git a/packages/realm-web/src/transports/AuthenticatedTransport.ts b/packages/realm-web/src/transports/AuthenticatedTransport.ts index 4805f2b76e..ae278446a9 100644 --- a/packages/realm-web/src/transports/AuthenticatedTransport.ts +++ b/packages/realm-web/src/transports/AuthenticatedTransport.ts @@ -26,7 +26,7 @@ interface UserContext { /** * The currently active user */ - currentUser: Realm.User | null; + currentUser: Realm.User | null; } /** diff --git a/packages/realm-web/tsconfig.build.json b/packages/realm-web/tsconfig.build.json index 46e3c95f2d..5c5b57a385 100644 --- a/packages/realm-web/tsconfig.build.json +++ b/packages/realm-web/tsconfig.build.json @@ -9,6 +9,7 @@ }, "exclude": [ "src/**/*.test.ts", - "src/test/" + "src/test/", + "types/" ] } \ No newline at end of file diff --git a/packages/realm-web/tsconfig.json b/packages/realm-web/tsconfig.json index 202667d67d..9432b90650 100644 --- a/packages/realm-web/tsconfig.json +++ b/packages/realm-web/tsconfig.json @@ -24,5 +24,6 @@ }, "include": [ "src/**/*.ts" - ] + ], + "exclude": [] } \ No newline at end of file diff --git a/src/RealmJS.xcodeproj/project.pbxproj b/src/RealmJS.xcodeproj/project.pbxproj index 367fad5352..fb9fe63fe2 100644 --- a/src/RealmJS.xcodeproj/project.pbxproj +++ b/src/RealmJS.xcodeproj/project.pbxproj @@ -185,11 +185,11 @@ 3FCE2A991F58BE3600D4855B /* feature_checks.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = feature_checks.hpp; path = "object-store/src/feature_checks.hpp"; sourceTree = SOURCE_ROOT; }; 42BD57A824640A94008679D5 /* js_app.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_app.hpp; sourceTree = ""; }; 42BD57A924640A94008679D5 /* js_app_credentials.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_app_credentials.hpp; sourceTree = ""; }; - 42BD57AA24640A94008679D5 /* js_email_password_provider.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_email_password_provider.hpp; sourceTree = ""; }; + 42BD57AA24640A94008679D5 /* js_email_password_auth.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_email_password_auth.hpp; sourceTree = ""; }; 42BD57AB24640A94008679D5 /* js_user.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_user.hpp; sourceTree = ""; }; 42BD57AC24640A94008679D5 /* js_network_transport.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_network_transport.hpp; sourceTree = ""; }; 42BD57AD24640A95008679D5 /* js_sync_util.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_sync_util.hpp; sourceTree = ""; }; - 42BD57AE24640A95008679D5 /* js_user_apikey_provider.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_user_apikey_provider.hpp; sourceTree = ""; }; + 42BD57AE24640A95008679D5 /* js_api_key_auth.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_api_key_auth.hpp; sourceTree = ""; }; 42BD57AF24640AA8008679D5 /* keypath_helpers.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = keypath_helpers.hpp; path = src/keypath_helpers.hpp; sourceTree = ""; }; 42BD57B024640AE4008679D5 /* generic_network_transport.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = generic_network_transport.hpp; path = src/sync/generic_network_transport.hpp; sourceTree = ""; }; 42BD57B124640AE4008679D5 /* app_credentials.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = app_credentials.hpp; path = src/sync/app_credentials.hpp; sourceTree = ""; }; @@ -360,9 +360,9 @@ 42BD57A824640A94008679D5 /* js_app.hpp */, 42BD57AC24640A94008679D5 /* js_network_transport.hpp */, 42BD57AD24640A95008679D5 /* js_sync_util.hpp */, - 42BD57AE24640A95008679D5 /* js_user_apikey_provider.hpp */, + 42BD57AE24640A95008679D5 /* js_api_key_auth.hpp */, 42BD57AB24640A94008679D5 /* js_user.hpp */, - 42BD57AA24640A94008679D5 /* js_email_password_provider.hpp */, + 42BD57AA24640A94008679D5 /* js_email_password_auth.hpp */, F62A35131C18E6E2004A917D /* iOS */, F6874A441CAD2ACD00EEEE36 /* JSC */, F6BCCFDF1C83809A00FE31AE /* lib */, diff --git a/src/js_user_apikey_provider.hpp b/src/js_api_key_auth.hpp similarity index 78% rename from src/js_user_apikey_provider.hpp rename to src/js_api_key_auth.hpp index b5e4183f90..7bc2d18b85 100644 --- a/src/js_user_apikey_provider.hpp +++ b/src/js_api_key_auth.hpp @@ -29,19 +29,19 @@ using SharedUser = std::shared_ptr; using SharedApp = std::shared_ptr; template -class UserAPIKeyProviderClient : public app::App::UserAPIKeyProviderClient { +class ApiKeyAuth : public app::App::UserAPIKeyProviderClient { public: - UserAPIKeyProviderClient(app::App::UserAPIKeyProviderClient client, SharedUser user) : app::App::UserAPIKeyProviderClient(client), m_user(std::move(user)) {} - UserAPIKeyProviderClient(UserAPIKeyProviderClient &&) = default; + ApiKeyAuth(app::App::UserAPIKeyProviderClient client, SharedUser user) : app::App::UserAPIKeyProviderClient(client), m_user(std::move(user)) {} + ApiKeyAuth(ApiKeyAuth &&) = default; - UserAPIKeyProviderClient& operator=(UserAPIKeyProviderClient &&) = default; - UserAPIKeyProviderClient& operator=(UserAPIKeyProviderClient const&) = default; + ApiKeyAuth& operator=(ApiKeyAuth &&) = default; + ApiKeyAuth& operator=(ApiKeyAuth const&) = default; SharedUser m_user; }; template -class UserAPIKeyProviderClientClass : public ClassDefinition> { +class ApiKeyAuthClass : public ClassDefinition> { using GlobalContextType = typename T::GlobalContext; using ContextType = typename T::Context; using FunctionType = typename T::Function; @@ -55,7 +55,7 @@ class UserAPIKeyProviderClientClass : public ClassDefinition; public: - std::string const name = "UserAPIKeyProviderClient"; + std::string const name = "ApiKeyAuth"; static FunctionType create_constructor(ContextType); static ObjectType create_instance(ContextType, SharedApp, SharedUser); @@ -65,30 +65,30 @@ class UserAPIKeyProviderClientClass : public ClassDefinition const methods = { - {"_createAPIKey", wrap}, - {"_fetchAPIKey", wrap}, - {"_fetchAPIKeys", wrap}, - {"_deleteAPIKey", wrap}, - {"_enableAPIKey", wrap}, - {"_disableAPIKey", wrap}, + {"_create", wrap}, + {"_fetch", wrap}, + {"_fetchAll", wrap}, + {"_delete", wrap}, + {"_enable", wrap}, + {"_disable", wrap}, }; }; template -inline typename T::Function UserAPIKeyProviderClientClass::create_constructor(ContextType ctx) { - FunctionType constructor = ObjectWrap>::create_constructor(ctx); +inline typename T::Function ApiKeyAuthClass::create_constructor(ContextType ctx) { + FunctionType constructor = ObjectWrap>::create_constructor(ctx); return constructor; } template -typename T::Object UserAPIKeyProviderClientClass::create_instance(ContextType ctx, SharedApp app, SharedUser user) { - return create_object>(ctx, new UserAPIKeyProviderClient(app->provider_client(), user)); +typename T::Object ApiKeyAuthClass::create_instance(ContextType ctx, SharedApp app, SharedUser user) { + return create_object>(ctx, new ApiKeyAuth(app->provider_client(), user)); } template @@ -107,10 +107,10 @@ typename T::Object make_api_key(typename T::Context ctx, util::Optional -void UserAPIKeyProviderClientClass::create_api_key(ContextType ctx, ObjectType this_object, Arguments& args, ReturnValue& return_value) { +void ApiKeyAuthClass::create_api_key(ContextType ctx, ObjectType this_object, Arguments& args, ReturnValue& return_value) { args.validate_count(2); - auto& client = *get_internal>(ctx, this_object); + auto& client = *get_internal>(ctx, this_object); auto name = Value::validated_to_string(ctx, args[0], "name"); auto callback = Value::validated_to_function(ctx, args[1], "callback"); @@ -144,10 +144,10 @@ void UserAPIKeyProviderClientClass::create_api_key(ContextType ctx, ObjectTyp } template -void UserAPIKeyProviderClientClass::fetch_api_key(ContextType ctx, ObjectType this_object, Arguments& args, ReturnValue& return_value) { +void ApiKeyAuthClass::fetch_api_key(ContextType ctx, ObjectType this_object, Arguments& args, ReturnValue& return_value) { args.validate_count(2); - auto& client = *get_internal>(ctx, this_object); + auto& client = *get_internal>(ctx, this_object); auto id = Value::validated_to_object_id(ctx, args[0], "id"); auto callback = Value::validated_to_function(ctx, args[1], "callback"); @@ -181,10 +181,10 @@ void UserAPIKeyProviderClientClass::fetch_api_key(ContextType ctx, ObjectType } template -void UserAPIKeyProviderClientClass::fetch_api_keys(ContextType ctx, ObjectType this_object, Arguments& args, ReturnValue& return_value) { +void ApiKeyAuthClass::fetch_all_api_keys(ContextType ctx, ObjectType this_object, Arguments& args, ReturnValue& return_value) { args.validate_count(1); - auto& client = *get_internal>(ctx, this_object); + auto& client = *get_internal>(ctx, this_object); auto callback = Value::validated_to_function(ctx, args[0], "callback"); Protected protected_ctx(Context::get_global_context(ctx)); @@ -255,10 +255,10 @@ app::App::UserAPIKey to_api_key(typename T::Context ctx, typename T::Object api_ } template -void UserAPIKeyProviderClientClass::delete_api_key(ContextType ctx, ObjectType this_object, Arguments& args, ReturnValue& return_value) { +void ApiKeyAuthClass::delete_api_key(ContextType ctx, ObjectType this_object, Arguments& args, ReturnValue& return_value) { args.validate_count(2); - auto& client = *get_internal>(ctx, this_object); + auto& client = *get_internal>(ctx, this_object); auto api_key_id = Value::validated_to_object_id(ctx, args[0], "API key id"); auto callback = Value::validated_to_function(ctx, args[1], "callback"); @@ -267,10 +267,10 @@ void UserAPIKeyProviderClientClass::delete_api_key(ContextType ctx, ObjectTyp } template -void UserAPIKeyProviderClientClass::enable_api_key(ContextType ctx, ObjectType this_object, Arguments& args, ReturnValue& return_value) { +void ApiKeyAuthClass::enable_api_key(ContextType ctx, ObjectType this_object, Arguments& args, ReturnValue& return_value) { args.validate_count(2); - auto& client = *get_internal>(ctx, this_object); + auto& client = *get_internal>(ctx, this_object); auto api_key_id = Value::validated_to_object_id(ctx, args[0], "API key id"); auto callback = Value::validated_to_function(ctx, args[1], "callback"); @@ -279,10 +279,10 @@ void UserAPIKeyProviderClientClass::enable_api_key(ContextType ctx, ObjectTyp } template -void UserAPIKeyProviderClientClass::disable_api_key(ContextType ctx, ObjectType this_object, Arguments& args, ReturnValue& return_value) { +void ApiKeyAuthClass::disable_api_key(ContextType ctx, ObjectType this_object, Arguments& args, ReturnValue& return_value) { args.validate_count(2); - auto& client = *get_internal>(ctx, this_object); + auto& client = *get_internal>(ctx, this_object); auto api_key_id = Value::validated_to_object_id(ctx, args[0], "API key id"); auto callback = Value::validated_to_function(ctx, args[1], "callback"); diff --git a/src/js_app.hpp b/src/js_app.hpp index 0f23769f64..16fb920eaf 100644 --- a/src/js_app.hpp +++ b/src/js_app.hpp @@ -29,7 +29,7 @@ #include "js_user.hpp" #include "js_app_credentials.hpp" #include "js_network_transport.hpp" -#include "js_email_password_provider.hpp" +#include "js_email_password_auth.hpp" using SharedApp = std::shared_ptr; using SharedUser = std::shared_ptr; @@ -60,23 +60,23 @@ class AppClass : public ClassDefinition { static FunctionType create_constructor(ContextType); static void get_app_id(ContextType, ObjectType, ReturnValue &); - static void get_auth_email_password(ContextType, ObjectType, ReturnValue &); + static void get_email_password_auth(ContextType, ObjectType, ReturnValue &); + static void get_current_user(ContextType, ObjectType, ReturnValue &); + static void get_all_users(ContextType, ObjectType, ReturnValue &); PropertyMap const properties = { {"id", {wrap, nullptr}}, - {"_authEmailPassword", {wrap, nullptr}}, + {"emailPasswordAuth", {wrap, nullptr}}, + {"currentUser", {wrap, nullptr}}, + {"allUsers", {wrap, nullptr}} }; static void login(ContextType, ObjectType, Arguments&, ReturnValue&); - static void all_users(ContextType, ObjectType, Arguments&, ReturnValue&); - static void current_user(ContextType, ObjectType, Arguments&, ReturnValue&); static void switch_user(ContextType, ObjectType, Arguments&, ReturnValue&); static void remove_user(ContextType, ObjectType, Arguments&, ReturnValue&); MethodMap const methods = { {"_login", wrap}, - {"allUsers", wrap}, - {"currentUser", wrap}, {"switchUser", wrap}, {"_removeUser", wrap}, }; @@ -222,9 +222,7 @@ void AppClass::login(ContextType ctx, ObjectType this_object, Arguments &args } template -void AppClass::all_users(ContextType ctx, ObjectType this_object, Arguments& args, ReturnValue& return_value) { - args.validate_count(0); - +void AppClass::get_all_users(ContextType ctx, ObjectType this_object, ReturnValue& return_value) { auto app = *get_internal>(ctx, this_object); auto users = Object::create_empty(ctx); @@ -236,9 +234,7 @@ void AppClass::all_users(ContextType ctx, ObjectType this_object, Arguments& } template -void AppClass::current_user(ContextType ctx, ObjectType this_object, Arguments& args, ReturnValue& return_value) { - args.validate_count(0); - +void AppClass::get_current_user(ContextType ctx, ObjectType this_object, ReturnValue& return_value) { auto app = *get_internal>(ctx, this_object); auto user = app->current_user(); if (user) { @@ -296,9 +292,9 @@ void AppClass::remove_user(ContextType ctx, ObjectType this_object, Arguments } template -void AppClass::get_auth_email_password(ContextType ctx, ObjectType this_object, ReturnValue &return_value) { +void AppClass::get_email_password_auth(ContextType ctx, ObjectType this_object, ReturnValue &return_value) { auto app = *get_internal>(ctx, this_object); - return_value.set(EmailPasswordProviderClientClass::create_instance(ctx, app)); + return_value.set(EmailPasswordAuthClass::create_instance(ctx, app)); } } diff --git a/src/js_email_password_provider.hpp b/src/js_email_password_auth.hpp similarity index 56% rename from src/js_email_password_provider.hpp rename to src/js_email_password_auth.hpp index f3931d6279..1172a6c578 100644 --- a/src/js_email_password_provider.hpp +++ b/src/js_email_password_auth.hpp @@ -27,7 +27,7 @@ namespace realm { namespace js { template -class EmailPasswordProviderClientClass : public ClassDefinition { +class EmailPasswordAuthClass : public ClassDefinition { using GlobalContextType = typename T::GlobalContext; using ContextType = typename T::Context; using FunctionType = typename T::Function; @@ -41,7 +41,7 @@ class EmailPasswordProviderClientClass : public ClassDefinition; public: - std::string const name = "EmailPasswordProviderClient"; + std::string const name = "EmailPasswordAuth"; static FunctionType create_constructor(ContextType); static ObjectType create_instance(ContextType, SharedApp); @@ -49,37 +49,40 @@ class EmailPasswordProviderClientClass : public ClassDefinition const properties = { }; - static void register_email(ContextType, ObjectType, Arguments&, ReturnValue&); + static void register_user(ContextType, ObjectType, Arguments&, ReturnValue&); static void confirm_user(ContextType, ObjectType, Arguments&, ReturnValue&); static void resend_confirmation_email(ContextType, ObjectType, Arguments&, ReturnValue&); static void send_reset_password_email(ContextType, ObjectType, Arguments&, ReturnValue&); static void reset_password(ContextType, ObjectType, Arguments&, ReturnValue&); + static void call_reset_password_function(ContextType, ObjectType, Arguments&, ReturnValue&); MethodMap const methods = { - {"_registerEmail", wrap}, + {"_registerUser", wrap}, {"_confirmUser", wrap}, {"_resendConfirmationEmail", wrap}, {"_sendResetPasswordEmail", wrap}, - {"_resetPassword", wrap} + {"_resetPassword", wrap}, + {"_callResetPasswordFunction", wrap}, + }; }; template -inline typename T::Function EmailPasswordProviderClientClass::create_constructor(ContextType ctx) { - FunctionType constructor = ObjectWrap>::create_constructor(ctx); +inline typename T::Function EmailPasswordAuthClass::create_constructor(ContextType ctx) { + FunctionType constructor = ObjectWrap>::create_constructor(ctx); return constructor; } template -typename T::Object EmailPasswordProviderClientClass::create_instance(ContextType ctx, SharedApp app) { - return create_object>(ctx, new app::App::UsernamePasswordProviderClient(app->provider_client())); +typename T::Object EmailPasswordAuthClass::create_instance(ContextType ctx, SharedApp app) { + return create_object>(ctx, new app::App::UsernamePasswordProviderClient(app->provider_client())); } template -void EmailPasswordProviderClientClass::register_email(ContextType ctx, ObjectType this_object, Arguments& args, ReturnValue& return_value) { +void EmailPasswordAuthClass::register_user(ContextType ctx, ObjectType this_object, Arguments& args, ReturnValue& return_value) { args.validate_count(3); - auto& client = *get_internal>(ctx, this_object); + auto& client = *get_internal>(ctx, this_object); auto email = Value::validated_to_string(ctx, args[0], "email"); auto password = Value::validated_to_string(ctx, args[1], "password"); @@ -89,10 +92,10 @@ void EmailPasswordProviderClientClass::register_email(ContextType ctx, Object } template -void EmailPasswordProviderClientClass::confirm_user(ContextType ctx, ObjectType this_object, Arguments& args, ReturnValue& return_value) { +void EmailPasswordAuthClass::confirm_user(ContextType ctx, ObjectType this_object, Arguments& args, ReturnValue& return_value) { args.validate_count(3); - auto& client = *get_internal>(ctx, this_object); + auto& client = *get_internal>(ctx, this_object); auto token = Value::validated_to_string(ctx, args[0], "token"); auto token_id = Value::validated_to_string(ctx, args[1], "token_id"); @@ -102,10 +105,10 @@ void EmailPasswordProviderClientClass::confirm_user(ContextType ctx, ObjectTy } template -void EmailPasswordProviderClientClass::resend_confirmation_email(ContextType ctx, ObjectType this_object, Arguments& args, ReturnValue& return_value) { +void EmailPasswordAuthClass::resend_confirmation_email(ContextType ctx, ObjectType this_object, Arguments& args, ReturnValue& return_value) { args.validate_count(2); - auto& client = *get_internal>(ctx, this_object); + auto& client = *get_internal>(ctx, this_object); auto email = Value::validated_to_string(ctx, args[0], "email"); auto callback = Value::validated_to_function(ctx, args[1], "callback"); @@ -114,10 +117,10 @@ void EmailPasswordProviderClientClass::resend_confirmation_email(ContextType } template -void EmailPasswordProviderClientClass::send_reset_password_email(ContextType ctx, ObjectType this_object, Arguments& args, ReturnValue& return_value) { +void EmailPasswordAuthClass::send_reset_password_email(ContextType ctx, ObjectType this_object, Arguments& args, ReturnValue& return_value) { args.validate_count(2); - auto& client = *get_internal>(ctx, this_object); + auto& client = *get_internal>(ctx, this_object); auto email = Value::validated_to_string(ctx, args[0], "email"); auto callback = Value::validated_to_function(ctx, args[1], "callback"); @@ -126,10 +129,10 @@ void EmailPasswordProviderClientClass::send_reset_password_email(ContextType } template -void EmailPasswordProviderClientClass::reset_password(ContextType ctx, ObjectType this_object, Arguments& args, ReturnValue& return_value) { +void EmailPasswordAuthClass::reset_password(ContextType ctx, ObjectType this_object, Arguments& args, ReturnValue& return_value) { args.validate_count(4); - auto& client = *get_internal>(ctx, this_object); + auto& client = *get_internal>(ctx, this_object); auto password = Value::validated_to_string(ctx, args[0], "password"); auto token = Value::validated_to_string(ctx, args[1], "token"); @@ -139,5 +142,26 @@ void EmailPasswordProviderClientClass::reset_password(ContextType ctx, Object client.reset_password(password, token, token_id, make_callback_handler(ctx, this_object, callback)); } +template +void EmailPasswordAuthClass::call_reset_password_function(ContextType ctx, ObjectType this_object, Arguments& args, ReturnValue& return_value) { + args.validate_count(4); + + auto& client = *get_internal>(ctx, this_object); + + auto email = Value::validated_to_string(ctx, args[0], "email"); + auto password = Value::validated_to_string(ctx, args[1], "password"); + auto call_args_js = Value::validated_to_array(ctx, args[1], "args"); + auto callback = Value::validated_to_function(ctx, args[3], "callback"); + + bson::BsonArray call_args_bson; + uint32_t length = Object::validated_get_length(ctx, call_args_js); + for (uint32_t index = 0; index < length; ++index) { + auto obj = Object::get_property(ctx, call_args_js, index); + call_args_bson.push_back(Value::to_bson(ctx, obj)); + } + + client.call_reset_password_function(email, password, call_args_bson, make_callback_handler(ctx, this_object, callback)); +} + } } diff --git a/src/js_realm.hpp b/src/js_realm.hpp index 1bb1482efe..06bd7c1e13 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -33,8 +33,8 @@ #include "js_app.hpp" #include "js_auth.hpp" #include "js_app_credentials.hpp" -#include "js_email_password_provider.hpp" -#include "js_user_apikey_provider.hpp" +#include "js_email_password_auth.hpp" +#include "js_api_key_auth.hpp" #include "sync/async_open_task.hpp" #include "sync/sync_config.hpp" #include "sync/sync_manager.hpp" @@ -467,11 +467,11 @@ inline typename T::Function RealmClass::create_constructor(ContextType ctx) { FunctionType auth_constructor = AuthClass::create_constructor(ctx); Object::set_property(ctx, realm_constructor, "Auth", auth_constructor, attributes); - FunctionType email_password_provider_client_constructor = EmailPasswordProviderClientClass::create_constructor(ctx); - Object::set_property(ctx, auth_constructor, "EmailPasswordProvider", email_password_provider_client_constructor, attributes); + FunctionType email_password_provider_client_constructor = EmailPasswordAuthClass::create_constructor(ctx); + Object::set_property(ctx, auth_constructor, "EmailPasswordAuth", email_password_provider_client_constructor, attributes); - FunctionType user_apikey_provider_client_constructor = UserAPIKeyProviderClientClass::create_constructor(ctx); - Object::set_property(ctx, auth_constructor, "UserAPIKeyProvider", user_apikey_provider_client_constructor, attributes); + FunctionType user_apikey_provider_client_constructor = ApiKeyAuthClass::create_constructor(ctx); + Object::set_property(ctx, auth_constructor, "ApiKeyAuth", user_apikey_provider_client_constructor, attributes); #endif if (getenv("REALM_DISABLE_SYNC_TO_DISK")) { diff --git a/src/js_user.hpp b/src/js_user.hpp index b1d326e784..1bbe8dd4ea 100644 --- a/src/js_user.hpp +++ b/src/js_user.hpp @@ -22,7 +22,7 @@ #include "js_collection.hpp" #include "js_sync_util.hpp" #include "js_app_credentials.hpp" -#include "js_user_apikey_provider.hpp" +#include "js_api_key_auth.hpp" #include "sync/sync_config.hpp" #include "sync/sync_manager.hpp" @@ -70,22 +70,24 @@ class UserClass : public ClassDefinition> { static FunctionType create_constructor(ContextType); static ObjectType create_instance(ContextType, SharedUser, SharedApp); - static void get_identity(ContextType, ObjectType, ReturnValue &); - static void get_token(ContextType, ObjectType, ReturnValue &); + static void get_id(ContextType, ObjectType, ReturnValue &); + static void get_access_token(ContextType, ObjectType, ReturnValue &); + static void get_refresh_token(ContextType, ObjectType, ReturnValue &); static void get_profile(ContextType, ObjectType, ReturnValue &); static void is_logged_in(ContextType, ObjectType, ReturnValue &); static void get_state(ContextType, ObjectType, ReturnValue &); static void get_custom_data(ContextType, ObjectType, ReturnValue &); - static void get_auth_api_keys(ContextType, ObjectType, ReturnValue &); + static void get_api_keys(ContextType, ObjectType, ReturnValue &); PropertyMap const properties = { - {"identity", {wrap, nullptr}}, - {"token", {wrap, nullptr}}, + {"id", {wrap, nullptr}}, + {"accessToken", {wrap, nullptr}}, + {"refreshToken", {wrap, nullptr}}, {"profile", {wrap, nullptr}}, {"isLoggedIn", {wrap, nullptr}}, {"state", {wrap, nullptr}}, {"customData", {wrap, nullptr}}, - {"_authApiKeys", {wrap, nullptr}}, + {"apiKeys", {wrap, nullptr}}, }; MethodMap const static_methods = { @@ -123,17 +125,24 @@ typename T::Object UserClass::create_instance(ContextType ctx, SharedUser use } template -void UserClass::get_identity(ContextType ctx, ObjectType object, ReturnValue &return_value) { - std::string identity = get_internal>(ctx, object)->get()->identity(); - return_value.set(identity); +void UserClass::get_id(ContextType ctx, ObjectType object, ReturnValue &return_value) { + std::string id = get_internal>(ctx, object)->get()->identity(); + return_value.set(id); } template -void UserClass::get_token(ContextType ctx, ObjectType object, ReturnValue &return_value) { +void UserClass::get_access_token(ContextType ctx, ObjectType object, ReturnValue &return_value) { + std::string token = get_internal>(ctx, object)->get()->access_token(); + return_value.set(token); +} + +template +void UserClass::get_refresh_token(ContextType ctx, ObjectType object, ReturnValue &return_value) { std::string token = get_internal>(ctx, object)->get()->refresh_token(); return_value.set(token); } + template void UserClass::is_logged_in(ContextType ctx, ObjectType object, ReturnValue &return_value) { auto logged_in = get_internal>(ctx, object)->get()->is_logged_in(); @@ -298,9 +307,9 @@ void UserClass::call_function(ContextType ctx, ObjectType this_object, Argume } template -void UserClass::get_auth_api_keys(ContextType ctx, ObjectType this_object, ReturnValue &return_value) { +void UserClass::get_api_keys(ContextType ctx, ObjectType this_object, ReturnValue &return_value) { auto user = get_internal>(ctx, this_object); - return_value.set(UserAPIKeyProviderClientClass::create_instance(ctx, user->m_app, *user)); + return_value.set(ApiKeyAuthClass::create_instance(ctx, user->m_app, *user)); } template diff --git a/src/rpc.cpp b/src/rpc.cpp index 3c01019a57..29cda179ae 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp @@ -754,7 +754,7 @@ json RPCServer::serialize_json_value(JSValueRef js_value) { else if (jsc::Object::is_instance>(m_context, js_object)) { auto user = jsc::Object::get_internal>(m_context, js_object); json user_dict { - {"identity", user->get()->identity()}, + {"id", user->get()->identity()}, }; return { {"type", RealmObjectTypesUser}, diff --git a/tests/js/app-tests.js b/tests/js/app-tests.js index 0ecebce33c..f7787fdcf2 100644 --- a/tests/js/app-tests.js +++ b/tests/js/app-tests.js @@ -123,30 +123,30 @@ module.exports = { async testLogoutAndAllUsers() { let app = new Realm.App(config); let credentials = Realm.Credentials.anonymous(); - let users = app.allUsers(); + let users = app.allUsers; const nUsers = Object.keys(users).length; let user = await app.logIn(credentials); - users = app.allUsers(); + users = app.allUsers; TestCase.assertEqual(Object.keys(users).length, nUsers + 1) await user.logOut(); - users = app.allUsers(); + users = app.allUsers; TestCase.assertEqual(Object.keys(users).length, nUsers); }, async testCurrentUser() { let app = new Realm.App(config); - TestCase.assertNull(app.currentUser()); + TestCase.assertNull(app.currentUser); let credentials = Realm.Credentials.anonymous(); let user1 = await app.logIn(credentials); - let user2 = app.currentUser(); - TestCase.assertEqual(user1.identity, user2.identity); + let user2 = app.currentUser; + TestCase.assertEqual(user1.id, user2.id); await user1.logOut(); - TestCase.assertNull(app.currentUser()); + TestCase.assertNull(app.currentUser); }, async testMongoDBRealmSync() { diff --git a/tests/js/session-tests.js b/tests/js/session-tests.js index 5febcbdd2a..8b98adc39a 100644 --- a/tests/js/session-tests.js +++ b/tests/js/session-tests.js @@ -168,9 +168,9 @@ module.exports = { const session = realm.syncSession; TestCase.assertInstanceOf(session, Realm.Sync.Session); - TestCase.assertEqual(session.user.identity, user.identity); + TestCase.assertEqual(session.user.id, user.id); TestCase.assertEqual(session.config.url, config.sync.url); - TestCase.assertEqual(session.config.user.identity, config.sync.user.identity); + TestCase.assertEqual(session.config.user.id, config.sync.user.id); TestCase.assertEqual(session.state, 'active'); return user.logOut(); }); @@ -208,9 +208,9 @@ module.exports = { const session = realm.syncSession; TestCase.assertInstanceOf(session, Realm.Sync.Session); - TestCase.assertEqual(session.user.identity, user.identity); + TestCase.assertEqual(session.user.id, user.id); TestCase.assertEqual(session.config.url, config.sync.url); - TestCase.assertEqual(session.config.user.identity, config.sync.user.identity); + TestCase.assertEqual(session.config.user.id, config.sync.user.id); TestCase.assertEqual(session.state, 'active'); realm.close() }); @@ -482,11 +482,11 @@ module.exports = { realm1.close(); // delete Realm on server - let encodedPath = encodeURIComponent(`${user.identity}/myrealm`); + let encodedPath = encodeURIComponent(`${user.id}/myrealm`); let url = new URL(`/realms/files/${encodedPath}`, user.server); let options = { headers: { - Authorization: `${user.token}`, + Authorization: `${user.accessToken}`, 'Content-Type': 'application/json', }, method: 'DELETE', diff --git a/tests/js/user-tests.js b/tests/js/user-tests.js index 0598de306e..2771e495bf 100644 --- a/tests/js/user-tests.js +++ b/tests/js/user-tests.js @@ -42,16 +42,17 @@ if (isNodeProcess) { function assertIsUser(user) { TestCase.assertDefined(user); TestCase.assertType(user, 'object'); - TestCase.assertType(user.token, 'string'); - TestCase.assertType(user.identity, 'string'); + TestCase.assertType(user.accessToken, 'string'); + TestCase.assertType(user.refreshToken, 'string'); + TestCase.assertType(user.id, 'string'); TestCase.assertType(user.customData, 'object'); TestCase.assertInstanceOf(user, Realm.User); } function assertIsSameUser(value, user) { assertIsUser(value); - TestCase.assertEqual(value.token, user.token); - TestCase.assertEqual(value.identity, user.identity); + TestCase.assertEqual(value.accessToken, user.accessToken); + TestCase.assertEqual(value.id, user.id); } function assertIsError(error, message) { @@ -91,21 +92,21 @@ function randomNonVerifiableEmail() { async function registerAndLogInEmailUser(app) { const validEmail = randomVerifiableEmail(); const validPassword = "test1234567890"; - await app.auth.emailPassword.registerEmail(validEmail, validPassword); + await app.emailPasswordAuth.registerUser(validEmail, validPassword); let user = await app.logIn(Realm.Credentials.emailPassword(validEmail, validPassword)) assertIsUser(user); - assertIsSameUser(user, app.currentUser()); + assertIsSameUser(user, app.currentUser); return user; } async function logOutExistingUsers(app) { - let users = app.allUsers(); - Object.keys(app.allUsers()).forEach(async id => await users[id].logOut()); + let users = app.allUsers; + Object.keys(app.allUsers).forEach(async id => await users[id].logOut()); } module.exports = { - // tests also logIn() and currentUser() + // tests also logIn() and currentUser async testLogout() { let app = new Realm.App(appConfig); await logOutExistingUsers(app); @@ -113,10 +114,10 @@ module.exports = { let user = await app.logIn(credentials); assertIsUser(user); - assertIsSameUser(user, app.currentUser()); + assertIsSameUser(user, app.currentUser); await user.logOut(); // Is now logged out. - TestCase.assertNull(app.currentUser()); + TestCase.assertNull(app.currentUser); }, testEmailPasswordMissingUsername() { @@ -140,10 +141,10 @@ module.exports = { }); }, - async testEmailPasswordProvider() { + async testEmailPasswordAuth() { let app = new Realm.App(appConfig); - let provider = app.auth.emailPassword; - TestCase.assertTrue(provider instanceof Realm.Auth.EmailPasswordProvider); + let provider = app.emailPasswordAuth; + TestCase.assertTrue(provider instanceof Realm.Auth.EmailPasswordAuth); }, async testRegisterAutoVerifyEmailPassword() { @@ -157,7 +158,7 @@ module.exports = { let credentials = Realm.Credentials.emailPassword(invalidEmail, invalidPassword); let err = await TestCase.assertThrowsAsync(async() => app.logIn(credentials)); TestCase.assertEqual(err.message, "invalid username/password"); // this user does not exist yet - err = await TestCase.assertThrowsAsync(async() => app.auth.emailPassword.registerEmail(invalidEmail, invalidPassword)); + err = await TestCase.assertThrowsAsync(async() => app.emailPasswordAuth.registerUser(invalidEmail, invalidPassword)); TestCase.assertEqual(err.message, "password must be between 6 and 128 characters"); err = await TestCase.assertThrowsAsync(async() => app.logIn(credentials)); TestCase.assertEqual(err.message, "invalid username/password"); // this user did not register @@ -166,7 +167,7 @@ module.exports = { let credentials = Realm.Credentials.emailPassword(invalidEmail, validPassword); let err = await TestCase.assertThrowsAsync(async() => app.logIn(credentials)); TestCase.assertEqual(err.message, "invalid username/password"); // this user does not exist yet - err = await TestCase.assertThrowsAsync(async() => app.auth.emailPassword.registerEmail(invalidEmail, validPassword)); + err = await TestCase.assertThrowsAsync(async() => app.emailPasswordAuth.registerUser(invalidEmail, validPassword)); TestCase.assertEqual(err.message, `failed to confirm user ${invalidEmail}`); err = await TestCase.assertThrowsAsync(async() => app.logIn(credentials)); TestCase.assertEqual(err.message, "invalid username/password"); // this user did not register @@ -175,7 +176,7 @@ module.exports = { let credentials = Realm.Credentials.emailPassword(validEmail, invalidPassword); let err = await TestCase.assertThrowsAsync(async() => app.logIn(credentials)); TestCase.assertEqual(err.message, "invalid username/password"); // this user does not exist yet - err = await TestCase.assertThrowsAsync(async() => app.auth.emailPassword.registerEmail(validEmail, invalidPassword)); + err = await TestCase.assertThrowsAsync(async() => app.emailPasswordAuth.registerUser(validEmail, invalidPassword)); TestCase.assertEqual(err.message, "password must be between 6 and 128 characters"); err = await TestCase.assertThrowsAsync(async() => app.logIn(credentials)); TestCase.assertEqual(err.message, "invalid username/password"); // this user did not register @@ -184,21 +185,25 @@ module.exports = { let credentials = Realm.Credentials.emailPassword(validEmail, validPassword); let err = await TestCase.assertThrowsAsync(async() => app.logIn(credentials)); TestCase.assertEqual(err.message, "invalid username/password"); // this user does not exist yet - await app.auth.emailPassword.registerEmail(validEmail, validPassword); + await app.emailPasswordAuth.registerUser(validEmail, validPassword); let user = await app.logIn(credentials) assertIsUser(user); - assertIsSameUser(user, app.currentUser()); + assertIsSameUser(user, app.currentUser); await user.logOut(); } }, - async testUserAPIKeyProvider() { + async testApiKeyAuth() { let app = new Realm.App(appConfig); let credentials = Realm.Credentials.anonymous(); let user = await app.logIn(credentials); - let provider = user.auth.apiKeys; - TestCase.assertTrue(provider instanceof Realm.Auth.UserAPIKeyProvider); + TestCase.assertTrue(user.apiKeys instanceof Realm.Auth.ApiKeyAuth); + + // TODO: Fix this to not respond with a 403 + // const keys = await user.apiKeys.fetchAll(); + // TestCase.assertTrue(Array.isArray(keys)); + await user.logOut(); }, @@ -258,41 +263,41 @@ module.exports = { let app = new Realm.App(appConfig); await logOutExistingUsers(app); - let all = app.allUsers(); + let all = app.allUsers; TestCase.assertArrayLength(Object.keys(all), 0, "Noone to begin with"); let credentials = Realm.Credentials.anonymous(); let user1 = await app.logIn(credentials); - all = app.allUsers(); + all = app.allUsers; TestCase.assertArrayLength(Object.keys(all), 1, "One user"); - assertIsSameUser(all[user1.identity], user1); + assertIsSameUser(all[user1.id], user1); let user2 = await app.logIn(Realm.Credentials.anonymous()); - all = app.allUsers(); + all = app.allUsers; TestCase.assertArrayLength(Object.keys(all), 1, "still one user"); // NOTE: the list of users is in latest-first order. - assertIsSameUser(all[user2.identity], user2); - assertIsSameUser(all[user1.identity], user1); + assertIsSameUser(all[user2.id], user2); + assertIsSameUser(all[user1.id], user1); await user2.logOut(); // logs out the shared anonymous session - all = app.allUsers(); + all = app.allUsers; TestCase.assertArrayLength(Object.keys(all), 0, "All gone"); }, async testCurrentWithAnonymous() { let app = new Realm.App(appConfig); await logOutExistingUsers(app); - TestCase.assertNull(app.currentUser()); + TestCase.assertNull(app.currentUser); let firstUser = await app.logIn(Realm.Credentials.anonymous()); - assertIsSameUser(app.currentUser(), firstUser); + assertIsSameUser(app.currentUser, firstUser); let secondUser = await app.logIn(Realm.Credentials.anonymous()); // the most recently logged in user is considered current TestCase.assertTrue(firstUser.isLoggedIn); TestCase.assertTrue(secondUser.isLoggedIn); - assertIsSameUser(app.currentUser(), secondUser); + assertIsSameUser(app.currentUser, secondUser); secondUser.logOut(); // since anonymous user sessions are shared, firstUser is logged out as well - TestCase.assertNull(app.currentUser()); + TestCase.assertNull(app.currentUser); TestCase.assertFalse(firstUser.isLoggedIn); TestCase.assertFalse(secondUser.isLoggedIn); }, @@ -300,47 +305,47 @@ module.exports = { async testCurrentWithEmail() { let app = new Realm.App(appConfig); await logOutExistingUsers(app); - TestCase.assertNull(app.currentUser()); + TestCase.assertNull(app.currentUser); let firstUser = await registerAndLogInEmailUser(app); - assertIsSameUser(app.currentUser(), firstUser); + assertIsSameUser(app.currentUser, firstUser); let secondUser = await registerAndLogInEmailUser(app); - assertIsSameUser(app.currentUser(), secondUser); // the most recently logged in user is considered current + assertIsSameUser(app.currentUser, secondUser); // the most recently logged in user is considered current await secondUser.logOut(); - assertIsSameUser(app.currentUser(), firstUser); // auto change back to another logged in user + assertIsSameUser(app.currentUser, firstUser); // auto change back to another logged in user await firstUser.logOut(); - TestCase.assertNull(app.currentUser()); + TestCase.assertNull(app.currentUser); }, async testAllWithEmail() { let app = new Realm.App(appConfig); await logOutExistingUsers(app); - let all = app.allUsers(); + let all = app.allUsers; TestCase.assertArrayLength(Object.keys(all), 0, "Noone to begin with"); let credentials = Realm.Credentials.anonymous(); let user1 = await registerAndLogInEmailUser(app); - all = app.allUsers(); + all = app.allUsers; TestCase.assertArrayLength(Object.keys(all), 1, "One user"); - assertIsSameUser(all[user1.identity], user1); + assertIsSameUser(all[user1.id], user1); let user2 = await registerAndLogInEmailUser(app); - all = app.allUsers(); + all = app.allUsers; TestCase.assertArrayLength(Object.keys(all), 2, "Two users"); // NOTE: the list of users is in latest-first order. - assertIsSameUser(all[user2.identity], user2); - assertIsSameUser(all[user1.identity], user1); + assertIsSameUser(all[user2.id], user2); + assertIsSameUser(all[user1.id], user1); await user2.logOut(); - all = app.allUsers(); - assertIsSameUser(all[user2.identity], user2); - assertIsSameUser(all[user1.identity], user1); + all = app.allUsers; + assertIsSameUser(all[user2.id], user2); + assertIsSameUser(all[user1.id], user1); TestCase.assertFalse(user2.isLoggedIn); TestCase.assertTrue(user1.isLoggedIn); TestCase.assertArrayLength(Object.keys(all), 2, "still holds references to both users"); await user1.logOut(); - all = app.allUsers(); + all = app.allUsers; TestCase.assertFalse(user1.isLoggedIn); TestCase.assertFalse(user2.isLoggedIn); TestCase.assertArrayLength(Object.keys(all), 2, "still holds references to both users"); // FIXME: is this actually expected? diff --git a/tsconfig.json b/tsconfig.json index 2e13f7d3db..19e9f9f115 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,5 +3,8 @@ "strict": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true - } -} + }, + "exclude": [ + "packages" + ] +} \ No newline at end of file diff --git a/types/app.d.ts b/types/app.d.ts index 52068d8e9f..108d1027c9 100644 --- a/types/app.d.ts +++ b/types/app.d.ts @@ -23,10 +23,6 @@ /// declare namespace Realm { - // See https://stackoverflow.com/a/51114250 on why we're importing the BSON types like this - type ObjectId = import("bson").ObjectId; - type Binary = import("bson").Binary; - namespace Credentials { /** * Payload sent when authenticating using the [Email/Password Provider](https://docs.mongodb.com/stitch/authentication/userpass/). @@ -110,19 +106,17 @@ declare namespace Realm { // TODO: Add providerCapabilities? } - /** - * The constructor of MongoDB Realm App. - */ - type AppConstructor = new < - FunctionsFactoryType extends object = DefaultFunctionsFactory - >( - idOrConfiguration: string | AppConfiguration, - ) => App; - /** * A MongoDB Realm App. */ - class App { + class App { + /** + * Construct a MongoDB Realm App. + * + * @param idOrConfiguration The id string or configuration for the app. + */ + constructor(idOrConfiguration: string | AppConfiguration); + /** * */ @@ -144,36 +138,38 @@ declare namespace Realm { services: Realm.Services; /** - * Perform operations on an app's authentication providers. + * Perform operations related to the email/password auth provider. */ - auth: Realm.AuthProviders; + emailPasswordAuth: Realm.Auth.EmailPasswordAuth; /** * The last user to log in or being switched to. */ - readonly currentUser: Realm.User | null; + readonly currentUser: User | null; /** * All authenticated users. */ - readonly allUsers: Readonly; + readonly allUsers: Readonly[]>; /** * Log in a user using a specific credential * * @param credentials the credentials to use when logging in */ - logIn(credentials: Credentials): Promise; + logIn(credentials: Credentials): Promise>; /** - * Log out the currently authenticated user and clear any persisted authentication information. + * Switch current user, from an instance of `User` or the string id of the user. */ - logOut(): Promise; + switchUser(user: User): void; /** - * Switch current user, from an instance of `Realm.User` or the string id of the user. + * Logs out and removes a user from the app. + * + * @returns A promise that resolves once the user has been logged out and removed from the app. */ - switchUser(user: User | string): void; + removeUser(user: User): Promise; } /** @@ -194,11 +190,14 @@ declare namespace Realm { /** * Representation of an authenticated user of an app. */ - interface User { + class User< + FunctionsFactoryType extends object = DefaultFunctionsFactory, + CustomDataType extends object = any + > { /** * The automatically-generated internal ID of the user. */ - id: string; + readonly id: string; /** * The state of the user. @@ -206,22 +205,89 @@ declare namespace Realm { readonly state: UserState; // TODO: Populate the list of identities - // identities: UserIdentity[]; + // readonly identities: UserIdentity[]; /** * The access token used when requesting a new access token. */ - accessToken: string | null; + readonly accessToken: string | null; /** * The refresh token used when requesting a new access token. */ - refreshToken: string | null; + readonly refreshToken: string | null; + + /** + * You can store arbitrary data about your application users in a MongoDB collection and configure MongoDB Realm to automatically expose each user’s data in a field of their user object. + * For example, you might store a user’s preferred language, date of birth, or their local timezone. + * + * If this value has not been configured, it will be empty. + */ + readonly customData: CustomDataType; /** * A profile containing additional information about the user. */ readonly profile: UserProfile; + + /** + * Use this to call functions defined by the MongoDB Realm app, as this user. + */ + readonly functions: FunctionsFactoryType & BaseFunctionsFactory; + + /** + * Perform operations related to the API-key auth provider. + */ + readonly apiKeys: Realm.Auth.ApiKeyAuth; + + /** + * Log out the user. + * + * @returns A promise that resolves once the user has been logged out of the app. + */ + logOut(): Promise; + + /** + * Link the user with an identity represented by another set of credentials. + * + * @param credentials The credentials to use when linking. + */ + linkCredentials(credentials: Credentials): Promise; + + /** + * Call a remote MongoDB Realm function by its name. + * Note: Consider using `functions[name]()` instead of calling this method. + * + * @example + * // These are all equivalent: + * await user.callFunction("doThing", [a1, a2, a3]); + * await user.functions.doThing(a1, a2, a3); + * await user.functions["doThing"](a1, a2, a3); + * + * @example + * // The methods returned from the functions object are bound, which is why it's okay to store the function in a variable before calling it: + * const doThing = user.functions.doThing; + * await doThing(a1); + * await doThing(a2); + * + * @param name Name of the function + * @param args Arguments passed to the function + */ + callFunction(name: string, ...args: any[]): Promise; + + /** + * Refresh the access token and derive custom data from it. + * + * @returns The newly fetched custom data. + */ + refreshCustomData(): Promise; + + /** + * Use the Push service to enable sending push messages to this user via Firebase Cloud Messaging (FCM). + * + * @returns An service client with methods to register and deregister the device on the user. + */ + push(serviceName: string): Realm.Services.Push; } /** @@ -236,13 +302,21 @@ declare namespace Realm { Removed = "removed", } + /** + * The type of a user. + */ + enum UserType { + /** A normal end-user created this user */ + Normal = "normal", + /** The user was created by the server */ + Server = "server", + } + // TODO: Implement storing these identities on the user - /* interface UserIdentity { userId: string; providerType: string; } - */ /** * An extended profile with detailed information about the user. @@ -261,7 +335,7 @@ declare namespace Realm { /** * A URL referencing a picture associated with the user. */ - pictureURL?: string; + pictureUrl?: string; /** * The users first name. @@ -299,7 +373,7 @@ declare namespace Realm { * The type of user * // TODO: Determine the meaning of the different possibilities. */ - userType: "server" | "normal"; + type: UserType; } /** @@ -312,7 +386,7 @@ declare namespace Realm { */ interface BaseFunctionsFactory { /** - * Call a remote MongoDB Realm function by it's name. + * Call a remote MongoDB Realm function by its name. * Consider using `functions[name]()` instead of calling this method. * * @param name Name of the function diff --git a/types/auth-providers.d.ts b/types/auth-providers.d.ts index 3d17a6eacb..6bac741575 100644 --- a/types/auth-providers.d.ts +++ b/types/auth-providers.d.ts @@ -22,16 +22,16 @@ declare namespace Realm { */ interface AuthProviders { /** Authentication provider where users identify using email and password. */ - emailPassword: Realm.AuthProviders.EmailPasswordAuthProvider; + emailPassword: Realm.Auth.EmailPasswordAuth; /** Authentication provider where users identify using an API-key. */ - apiKey: Realm.AuthProviders.ApiKeyAuthProvider; + apiKey: Realm.Auth.ApiKeyAuth; } - namespace AuthProviders { + namespace Auth { /** * Authentication provider where users identify using email and password. */ - interface EmailPasswordAuthProvider { + class EmailPasswordAuth { /** * Register a new user. * @@ -53,7 +53,7 @@ declare namespace Realm { * * @param email the email associated to resend the confirmation to. */ - resendConfirmation(email: string): Promise; + resendConfirmationEmail(email: string): Promise; /** * Complete resetting the password @@ -92,7 +92,7 @@ declare namespace Realm { /** * The representation of an API-key stored in the service. */ - interface ApiKey { + type ApiKey = { /** * The internal identifier of the key. */ @@ -112,12 +112,12 @@ declare namespace Realm { * When disabled, the key cannot authenticate. */ disabled: boolean; - } + }; /** * Authentication provider where users identify using an API-key. */ - interface ApiKeyAuthProvider { + class ApiKeyAuth { /** * Creates an API key that can be used to authenticate as the current user. * @@ -130,12 +130,12 @@ declare namespace Realm { * * @param keyId the id of the API key to fetch. */ - get(keyId: ObjectId): Promise; + fetch(keyId: ObjectId): Promise; /** * Fetches the API keys associated with the current user. */ - list(): Promise; + fetchAll(): Promise; /** * Deletes an API key associated with the current user. diff --git a/types/bson.d.ts b/types/bson.d.ts new file mode 100644 index 0000000000..56886bc218 --- /dev/null +++ b/types/bson.d.ts @@ -0,0 +1,23 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2020 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. +// +//////////////////////////////////////////////////////////////////////////// + +declare namespace Realm { + // See https://stackoverflow.com/a/51114250 on why we're importing the BSON types like this + type ObjectId = import("bson").ObjectId; + type Binary = import("bson").Binary; +} diff --git a/types/index.d.ts b/types/index.d.ts index a5db04744f..d0357d4dbe 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -19,6 +19,7 @@ // TypeScript Version: 2.3.2 // With great contributions to @akim95 on github +/// /// declare namespace Realm { @@ -328,89 +329,10 @@ declare namespace Realm { readonly prototype: Results; }; - interface UserProfile { - name?: string; - email?: string; - pictureUrl?: string; - firstName?: string; - lastName?: string; - gender?: string; - birthday?: string; - minAge?: string; - maxAge?: string; - } - - class User { - readonly identity: string; - readonly token: string; - readonly isLoggedIn: boolean; - readonly state: UserState; - readonly customData: Object; - readonly profile: UserProfile; - - /** - * Convenience wrapper around call_function(name, [args]). - * - * @example - * // These are all equivalent: - * await user.call_function("do_thing", [a1, a2, a3]); - * await user.functions.do_thing(a1, a2, a3); - * await user.functions["do_thing"](a1, a2, a3); - * - * @example - * // It it legal to store the functions as first-class values: - * const do_thing = user.functions.do_thing; - * await do_thing(a1); - * await do_thing(a2); - */ - readonly functions: { - [name: string] : (...args: any[]) => Promise - }; - - logOut(): Promise; - linkCredentials(credentials: Credentials): Promise; - callFunction(name: string, args: any[]): Promise; - refreshCustomData(): Promise; - push(serviceName: string): { - register(token: string): Promise, - deregister(): Promise, - }; - - readonly apiKeys: Realm.Auth.APIKeys; - } - - namespace Auth { - class APIKeys { - createAPIKey(name: string): Promise; - fetchAPIKey(id: string): Promise; - allAPIKeys(): Promise>; - deleteAPIKey(id: string): Promise; - enableAPIKey(id: string): Promise; - disableAPIKey(id: string): Promise; - } - - class EmailPassword { - registerEmail(email: string, password: string): Promise; - confirmUser(token: string, id: string): Promise; - resendConfirmationEmail(email: string): Promise; - sendResetPasswordEmail(email: string): Promise; - resetPassword(password: string, token: string, id: string): Promise; - } - } - interface UserMap { [identity: string]: User } - //TODO: This clashes with app.d.ts. Remove or refactor - // class App { - // logIn(credentials: Credentials): Promise; - // allUsers(): UserMap; - // currentUser(): User | null; - // switchUser(user: User): void; - // removeUser(user: User): Promise; - // } - interface SyncError { name: string; message: string; diff --git a/types/services.d.ts b/types/services.d.ts index f3ce8fb19d..119b626fe8 100644 --- a/types/services.d.ts +++ b/types/services.d.ts @@ -378,7 +378,7 @@ declare namespace Realm { */ get( url: string, - options: HTTP.RequestOptions, + options?: HTTP.RequestOptions, ): Promise; /** @@ -390,7 +390,7 @@ declare namespace Realm { */ post( url: string, - options: HTTP.RequestOptions, + options?: HTTP.RequestOptions, ): Promise; /** @@ -402,7 +402,7 @@ declare namespace Realm { */ put( url: string, - options: HTTP.RequestOptions, + options?: HTTP.RequestOptions, ): Promise; /** @@ -414,7 +414,7 @@ declare namespace Realm { */ delete( url: string, - options: HTTP.RequestOptions, + options?: HTTP.RequestOptions, ): Promise; /** @@ -426,7 +426,7 @@ declare namespace Realm { */ head( url: string, - options: HTTP.RequestOptions, + options?: HTTP.RequestOptions, ): Promise; /** @@ -438,7 +438,7 @@ declare namespace Realm { */ patch( url: string, - options: HTTP.RequestOptions, + options?: HTTP.RequestOptions, ): Promise; } @@ -515,5 +515,22 @@ declare namespace Realm { body: Binary; } } + + /** + * Use the Push service to enable sending push messages to this user via Firebase Cloud Messaging (FCM). + */ + interface Push { + /** + * Register this device with the user. + * + * @param token A Firebase Cloud Messaging (FCM) token, retrieved via the firebase SDK. + */ + register(token: string): Promise, + + /** + * Deregister this device with the user, to disable sending messages to this device. + */ + deregister(): Promise, + } } }