Skip to content

(DOCSP-12167): Write Phase 2 Tutorial Text (Node) #487

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

Merged
merged 5 commits into from
Sep 28, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
244 changes: 102 additions & 142 deletions source/tutorial/nodejs-cli.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ tracker command line interface (CLI) that allows users to:

- Register themselves with email and password.
- Sign in to their account with their email and password.
- View, create, modify, and delete tasks.
- Watch for changes to the Task collection and get notified in the terminal
window when a change occurs.
- View a list of projects they are a member of.
- View, create, modify, and delete tasks in projects.
- View a list of team members in their project.
- Add and remove team members to their project.

This tutorial should take around 30 minutes to complete.

Expand Down Expand Up @@ -111,10 +112,10 @@ multiple platforms. The project root for this tutorial is located in the
files.

This application has a flat project structure: all of the files are in the root
directory. In this tutorial, we'll be focusing on 4 files: ``config.js``,
``users.js``, ``tasks.js``, and ``watch.js``. The other files provide the
directory. In this tutorial, we'll be focusing on 5 files: ``config.js``,
``users.js``, ``tasks.js``, ``team.js``, ``projects.js``. The other files provide the
underlying structure for the CLI. The following table describes the role of
each file in this project:
important files in this project:

.. cssclass:: config-table
.. list-table::
Expand All @@ -133,7 +134,8 @@ each file in this project:
throughout its lifecycle and displays the initial logon screen.

* - main.js
- Displays the main menu of choices.
- Displays the main menu of choices. Users can view a list of projects
they are a member of or select a project.

* - output.js
- Responsible for displaying text in the terminal window.
Expand All @@ -149,9 +151,13 @@ each file in this project:
- Handles Realm user authentication, including logging in, registering a
new user, and logging out.

* - watch.js
- Adds a listener to a Realm collection to notify the user when a change
occurs.
* - team.js
- Handles the team member related communication between the CLI and Realm.
The methods for listing, adding, and removing team members are contained in
this file.

* - projects.js
- Handles project retrieval and listing.

D. Connect to Your MongoDB Realm App
------------------------------------
Expand All @@ -160,9 +166,7 @@ To get the app working with your backend, you first need to add your Realm App I
to the ``config.js`` file. The ``config.js`` module exports a single property,
``realmAppId``, which is currently set to "TODO":

.. code-block:: js

exports.realmAppId = "TODO";
.. literalinclude:: /tutorial/node-cli/bluehawk-gen/code/start/config.codeblock.realmAppID.js

Change this value to your Realm App ID.

Expand All @@ -172,31 +176,22 @@ Change this value to your Realm App ID.
Your App Id </get-started/find-your-app-id>` doc.

Once you have made that change, you now need to complete the code needed to
open a {+realm+}. In ``index.js``, find the ``openRealm`` function. It will look
like this:

.. code-block:: js

async function openRealm() {
const config = {
schema: [schemas.TaskSchema, schemas.UserSchema, schemas.ProjectSchema],
sync: {
user: users.getAuthedUser(),
partitionValue: "My Project",
},
};
// TODO: open a realm with these configuration settings.
}
open a {+realm+}. In ``index.js``, find the ``openRealm`` function. Replace the ``TODO``
line with a line of code that opens a {+realm+} and assigns it to the ``realm``
property. It will look like this:

Replace the ``TODO`` line with a line of code that opens a {+realm+} and assigns
it to the ``realm`` property:
.. literalinclude:: /tutorial/node-cli/bluehawk-gen/code/final/index.codeblock.openRealm.js
:emphasize-lines: 9

.. code-block:: js
Now that you have implemented the ``openRealm`` function, you will now need to
complete the code that retrieves the {+realm+}. In ``index.js``, find the
``getRealm`` function. It will look like this:

realm = new Realm(config);
.. literalinclude:: /tutorial/node-cli/bluehawk-gen/code/final/index.codeblock.getRealm.js
:emphasize-lines: 3

At this point, your app is pointing to your backend and opens a connection
to it when you start the app. However, users cannot yet log in, so let's update
to it when you start the app. However, users cannot log in yet, so let's update
that code next.

E. Enable authentication
Expand All @@ -208,47 +203,31 @@ an email address and password, and then, within a try-catch block, creates an
credential and passes it to the Realm
:js-sdk:`logIn() <Realm.App.html#logIn>` method.

Find the following comment:

.. code-block:: js

// TODO: create new emailPassword credentials and call app.logIn(...)

And add the following code to create a ``emailPassword`` credential:

.. code-block:: js

const credentials = Realm.Credentials.emailPassword(
input.email,
input.password
);

Immediately below this, add the following code to call the
``logIn()`` method:

.. code-block:: js

const user = await app.logIn(credentials);
Find the the ``logIn`` function and add the following code to create a
``emailPassword`` credential and call the ``logIn()`` method.

.. literalinclude:: tutorial/bluehawk-gen/code/final/users.codeblock.userLogin.js
:emphasize-lines: 17, 18, 19, 22

F. Implement the CRUD methods
-----------------------------

In the ``tasks.js`` file, there are a number of functions to handle typical
In the ``tasks.js`` and ``projects.js`` files, there are a number of functions to handle typical
CRUD functionality: ``getTasks``, ``getTask``, ``createTask``, ``deleteTask``,
``editTask``, and ``changeStatus``. Each of these functions (except ``getTasks``)
prompts the user for input and then makes the appropriate call to Realm. Your
job is to implement the calls to {+service-short+}. The following list provides
guidance on how to complete this task for each function:
``editTask``, ``changeStatus``, and ``getProjects``. Each of these functions
(except ``getTasks`` and ``getProjects``) prompts the user for input and then makes
the appropriate call to Realm. Your job is to implement the calls to {+service-short+}.
The following list provides guidance on how to complete this task for each function.

In ``tasks.js``:

* ``getTasks``

To get all objects, call the :js-sdk:`objects() <Realm.html#objects>` method
and pass in the name of the collection:

.. code-block:: js

const tasks = realm.objects("Task");
.. literalinclude:: tutorial/bluehawk-gen/final/tasks.codeblock.getTasks.js
:emphasize-lines: 3


* ``getTask``
Expand All @@ -257,12 +236,8 @@ guidance on how to complete this task for each function:
we call the :js-sdk:`objectForPrimaryKey() <Realm.html#objectForPrimaryKey>`
function to get a task by its Id.

.. code-block:: js

let result = realm.objectForPrimaryKey(
"Task",
new bson.ObjectID(task.id)
);
.. literalinclude:: tutorial/bluehawk-gen/final/tasks.codeblock.getTask.js
:emphasize-lines: 11


* ``createTask``
Expand All @@ -274,16 +249,9 @@ guidance on how to complete this task for each function:
the :js-sdk:`create() <Realm.html#create>` function, passing in all of the
required properties:

.. code-block:: js

realm.write(() => {
result = realm.create("Task", {
_id: new bson.ObjectID(),
_partition: "My Project",
name: task.name,
status: task.status,
});
});
.. literalinclude:: tutorial/bluehawk-gen/final/tasks.codeblock.createTask.js
:emphasize-lines: 23, 24, 25, 26, 27, 28


.. note::

Expand All @@ -297,16 +265,12 @@ guidance on how to complete this task for each function:
* ``deleteTask``

Deleting objects must also take place within a transaction. As with modifying
an object, we'll use the write() function to handle the transaction for us,
and then call the the :js-sdk:`delete() <Realm.html#delete>` function within
it:

.. code-block:: js
an object, we'll use the write() function to handle the transaction for us.
We'll first call the ``objectForPrimaryKey`` method to get the specific we want
to delete and then the :js-sdk:`delete() <Realm.html#delete>` function on that task:

realm.write(() => {
realm.delete(task);
output.result("Task deleted.");
});
.. literalinclude:: tutorial/bluehawk-gen/code/final/tasks.codeblock.deleteTask.js
:emphasize-lines: 18, 20


* ``modifyTask``
Expand All @@ -317,19 +281,62 @@ guidance on how to complete this task for each function:
call to a Realm API to change an object. Rather, you change the *local* object
and Sync ensures the object is updated on the server.

.. code-block:: js

realm.write(() => {
task = realm.objectForPrimaryKey("Task", new bson.ObjectID(answers.id));
task[answers.key] = answers.value;
});
.. literalinclude:: /tutorial/node-cli/bluehawk-gen/code/final/tasks.codeblock.modifyTask.js
:emphasize-lines: 6, 7


.. note::

To learn more about Realm Sync, see :doc:`Sync Overview </sync/overview>`.

In ``projects.js``:

* ``getProjects``

As defined by our data model, ``projects`` are an embedded object of the ``users`` object.
To get all projects the user is a part of, we'll have to use the ``objectForPrimaryKey``
method to get the current user and then access the current user's ``memberOf`` property.

.. literalinclude:: /tutorial/node-cli/bluehawk-gen/final/projects.codeblock.getProjects.js
:emphasize-lines: 4, 5

G. Use Realm Functions
----------------------

In the ``team.js`` file, there are functions that rely on :ref:`{+service-short+}
functions </functions>`. {+service-short+} functions allow you to execute server-side
logic for your client applications. Each of the following functions require you to
implement the calls to {+service-short+}.

* ``getTeamMembers``

To get all team members, call the ``getMyTeamMembers`` {+service-short+} function
using the ``User.functions`` method.

.. literalinclude:: /tutorial/node-cli/bluehawk-gen/final/team.codeblock.getTeamMembers.js
:emphasize-lines: 4

G. Run and Test
* ``addTeamMember``

This function prompts the user for the email of the new team member. You will need
to call the ``addTeamMember`` {+service-short+} function and pass it the ``email``
parameter.

.. literalinclude:: /tutorial/node-cli/bluehawk-gen/final/team.codeblock.addTeamMember.js
:emphasize-lines: 12

* ``removeTeamMember``

This functions prompts the user for the email of the team member they would like to
remove from their project. You will need to call the ``removeTeamMember``
{+service-short+} function and pass it the ``email`` parameter.

.. literalinclude:: /tutorial/node-cli/bluehawk-gen/final/team.codeblock.removeTeamMember.js
:emphasize-lines: 15



H. Run and Test
---------------

Once you have completed the code, you should run the app and check functionality.
Expand Down Expand Up @@ -357,56 +364,9 @@ Once you have completed the code, you should run the app and check functionality

.. admonition:: Reminder

If something isn't working for you, you can check out the ``main`` branch of
If something isn't working for you, you can check out the ``final`` branch of
this repo to compare your code with our finished solution.

H. Implement the Collection Listener
------------------------------------

A very handy feature in Realm is the ability to add a listener to a collection.
The listener notifies your app when a change occurs in the collection. For our
CLI, we want to listen for changes on the ``Tasks`` collection, and when one
occurs, display a notification in the console window. To do this:

1. In your text editor, open the ``watch.js`` file.

#. Our internal ``listener`` function is a callback function that takes two
parameters: the collection of tasks and a ``changes`` object that is sent
from Realm. The ``changes`` object has the following properties:

* ``changes.deletions`` returns the index of the deleted item *before*
the deletion.

* ``changes.insertions`` returns the index of the new object.

* ``changes.modifications`` returns the index of the modified object.

#. In the ``watchForChanges`` function, you will get all of the objects in
the **Tasks** collection (just as we did in the ``getTasks`` function),
and then call the :js-sdk:`addListener() <Realm.Collection.html#addListener>`
method. Under the ``TODO`` comment, add the following code:

.. code-block:: js

const tasks = realm.objects("Task");
tasks.addListener(listener);

With this code update, you can now re-run the app and watch for changes.

1. In the main menu, choose "Watch for changes". You will see that the UI
stays in a "waiting" state.

2. Open a second terminal window and run the app again. In this second window,
try creating, modifying, and deleting tasks. Each time you perform an action,
the first terminal window will update with a notification.

.. note::

If you want to continue to work in the same process and have the change
notifications appear inline, you can modify the code
in ``main.js``, within the ``case Choices.WatchForChanges`` handler. The
inline code comment explains the change you can make.

What's Next?
------------

Expand Down
2 changes: 2 additions & 0 deletions tutorial/node-cli/config.js
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
// :code-block-start: realmAppID
exports.realmAppId = "TODO";
// :code-block-end:
Loading