Skip to content

Commit

Permalink
Additions to custom user data (#6221)
Browse files Browse the repository at this point in the history
  • Loading branch information
elle-j authored and takameyer committed Oct 31, 2023
1 parent ee5a2e5 commit 2fc2ffd
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 23 deletions.
23 changes: 18 additions & 5 deletions examples/rn-connection-and-error/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ It specifically addresses the following points:
* With invalid password
* With non-existent email
* Listening (and triggering) when a user gets logged out.
* Listening (and triggering) when a user's tokens are refreshed.
* Listening (and triggering) when a user's tokens and custom data are refreshed.
* Listening (and triggering) when the underlying sync session:
* Tries to connect
* Gets connected
Expand Down Expand Up @@ -201,17 +201,21 @@ To sync data used in this app you must first:

### Add an Atlas Function

> If you set up your App Services App [via a CLI](#via-a-cli-recommended), you can **skip this step** as the function should already be defined for you.
> If you set up your App Services App [via a CLI](#via-a-cli-recommended), you can **skip this step** as these functions should already be defined for you.
We will add a function for forcing a client reset. The function is solely used for demo purposes and should not be used in production.
We will add a function for forcing a client reset. The function is solely used for demo purposes and should not be used in production. We will also add a function to be run on user creation that adds fields to the user's [custom user data](https://www.mongodb.com/docs/atlas/app-services/users/custom-metadata/) document.

To set this up via the App Services UI:

1. [Define a function](https://www.mongodb.com/docs/atlas/app-services/functions/#define-a-function) with the following configurations:
1. [Define two functions](https://www.mongodb.com/docs/atlas/app-services/functions/#define-a-function) with the following configurations:
* Function name: `triggerClientReset`
* Authentication: `System`
* Private: `false`
* Code: See [backend function](./backend/functions/triggerClientReset.js)
* Function name: `onUserCreation`
* Authentication: `Application Authentication`
* Private: `false`
* Code: See [backend function](./backend/functions/onUserCreation.js)

### Install Dependencies

Expand Down Expand Up @@ -253,7 +257,16 @@ npm run android

> If you set up your App Services App [via a CLI](#via-a-cli-recommended), you can **skip this step** as the permissions should already be defined for you.
After running the client app for the first time, [check the rules](https://www.mongodb.com/docs/atlas/app-services/rules/roles/#define-roles---permissions) for the collections in the App Services UI and make sure all collections have `readAndWriteAll` permissions (see [corresponding json](./backend/data_sources/mongodb-atlas/sync/Product/rules.json)).
After running the client app for the first time, [modify the rules](https://www.mongodb.com/docs/atlas/app-services/rules/roles/#define-roles---permissions) for the collections in the App Services UI.

* Collections: `Kiosk`, `Product`, `Store`
* Permissions: `readAndWriteAll` (see [corresponding json](./backend/data_sources/mongodb-atlas/sync/Product/rules.json))
* Explanation:
* All users will be able to read and write to the above collections.
* Collection: `Users`
* Permissions: `ThisUser` (see [corresponding json](./backend/data_sources/mongodb-atlas/AuthExample/Users/rules.json))
* Explanation:
* Users who have registered each have a `Users` document as custom user data. A user will be able to read and write to their own document (i.e. when `Users.user_id === <App User ID>`), but not anyone else's (see [Secure Custom User Data](https://www.mongodb.com/docs/atlas/app-services/users/custom-metadata/#secure-custom-user-data)).

> To learn more and see examples of permissions depending on a certain use case, see [Device Sync Permissions Guide](https://www.mongodb.com/docs/atlas/app-services/sync/app-builder/device-sync-permissions-guide/#std-label-flexible-sync-permissions-guide) and [Data Access Role Examples](https://www.mongodb.com/docs/atlas/app-services/rules/examples/).
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"collection": "Users",
"database": "AuthExample",
"roles": [
{
"name": "ThisUser",
"apply_when": {
"user_id": "%%user.id%%"
},
"document_filters": {
"write": {},
"read": {}
},
"read": true,
"write": true,
"insert": false,
"delete": false,
"search": false
}
],
"filters": []
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
{
"name": "onUserCreation",
"private": false,
"run_as_system": true,
"disable_arg_logs": true
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ exports = async function onUserCreation(user) {
const customUserDataCollection = context.services.get("mongodb-atlas").db("AuthExample").collection("Users");
try {
await customUserDataCollection.insertOne({
// Save the user's account ID to your configured user_id_field
// Save the user's account ID to your configured user_id field.
user_id: user.id,
// Store any other user data you want
// Store any other user data you want.
team: "service",
});
} catch (e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import {useCallback, useEffect, useState} from 'react';
import {BSON, ConnectionState, UserState} from 'realm';
import {useRealm, useUser} from '@realm/react';
import {useApp, useRealm, useUser} from '@realm/react';

import {Store} from '../models/Store';
import {logger} from '../utils/logger';
Expand All @@ -37,6 +37,7 @@ let mostRecentAccessToken: string | null = null;
* You can also add a listener to the App (via `useApp()`).
*/
export function useDemoSyncTriggers() {
const app = useApp();
const realm = useRealm();
const currentUser = useUser();
const [isConnected, setIsConnected] = useState(true);
Expand Down Expand Up @@ -147,6 +148,18 @@ export function useDemoSyncTriggers() {
await currentUser.refreshCustomData();
}, [currentUser]);

/**
* Trigger the user event listener by removing the user from the app.
*/
const deleteUser = useCallback(() => {
// TODO: Update to use only `deleteUser`.
// We currently call both `deleteUser` (deletes from server and client) and
// `removeUser` (deletes from client) due to a bug in `deleteUser` where the
// `currentUser` is not updated to `null`.
app.deleteUser(currentUser);
app.removeUser(currentUser);
}, [app, currentUser]);

useEffect(() => {
/**
* The user listener - Will be invoked on various user related events including
Expand Down Expand Up @@ -196,5 +209,6 @@ export function useDemoSyncTriggers() {
triggerSyncError,
triggerClientReset,
refreshAccessToken,
deleteUser,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import React from 'react';
import {Alert, FlatList, StyleSheet, Text, View} from 'react-native';
import {useAuth, useApp, useUser} from '@realm/react';
import {useAuth, useUser} from '@realm/react';

import {Button} from '../components/Button';
import {KioskItem} from '../components/KioskItem';
Expand All @@ -27,6 +27,18 @@ import {fonts} from '../styles/fonts';
import {useDemoSyncTriggers} from '../hooks/useDemoSyncTriggers';
import {useStore} from '../providers/StoreProvider';

/**
* The properties used as custom user data.
*
* @note
* Our backend function `onUserCreation()` adds these fields
* when the user registers.
*/
type CustomUserData = {
user_id: string;
team: string;
};

/**
* Screen for showing the kiosks and products in the store,
* as well as buttons for triggering various listeners.
Expand All @@ -41,10 +53,10 @@ export function StoreScreen() {
triggerSyncError,
triggerClientReset,
refreshAccessToken,
deleteUser,
} = useDemoSyncTriggers();
const {logOut} = useAuth();
const user = useUser();
const app = useApp();
const user = useUser<{}, CustomUserData, {}>();

return (
<View style={styles.container}>
Expand Down Expand Up @@ -72,6 +84,11 @@ export function StoreScreen() {
/>
</View>
<View style={styles.triggers}>
<View style={styles.status}>
<Text style={styles.statusText}>
Team: {user.customData.team}
</Text>
</View>
<View style={styles.status}>
<Text style={styles.statusText}>
Status: {isConnected ? 'Connected 🟢' : 'Not connected 🔴'}
Expand All @@ -82,11 +99,6 @@ export function StoreScreen() {
text={isConnected ? 'Disconnect' : 'Connect'}
/>
</View>
<View style={styles.status}>
<Text style={styles.statusText}>
Team: {user.customData?.team || '-'}
</Text>
</View>
<View style={styles.triggerButtons}>
<Button
extraStyles={[styles.button]}
Expand Down Expand Up @@ -119,10 +131,7 @@ export function StoreScreen() {
/>
<Button
extraStyles={[styles.button]}
onPress={() => {
app.deleteUser(user);
app.removeUser(user);
}}
onPress={deleteUser}
text="Delete User"
/>
<Button
Expand Down
2 changes: 0 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 2fc2ffd

Please sign in to comment.