Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Additions to custom user data #6221

Merged
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,29 @@
{
"collection": "Users",
"database": "AuthExample",
"roles": [
{
"name": "ThisUser",
"apply_when": {
"roles": [
{
"name": "ThisUser",
"apply_when": {
"user_id": "%%user.id%%"
},
"insert": false,
"read": true,
"write": true,
"search": false,
"delete": false
}
],
"filters": []
},
"document_filters": {
"write": {},
"read": {}
}
}
]
}
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
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes here and some in StoreScreen.tsx (i.e. moving the responsibility of deleting the user to this file) are the same as in the previous PR. But since this one will be merged after, the previous changes probably would have been overwritten had it not been changed here as well.

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>
Comment on lines +87 to +91
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Just moved this above the "Connected" text.

But also, removed the ? after customData as it will always return a record.

<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.

Loading