Clone this repo:
  1. 1507c1d fix(test): reflect the TestRunner interface change by YoungSeok Yoon · 19 hours ago master
  2. 2af5776 feat(agent): waiting for the conversation information by YoungSeok Yoon · 9 days ago
  3. 558ef23 feat(input): refocus message input after submit by YoungSeok Yoon · 9 days ago
  4. 5bf68a0 Add Dependency opinion to relationship by David Murphy · 3 weeks ago
  5. 7d09ab9 fix(conversation_list): fix the new conversation handling logic by YoungSeok Yoon · 10 days ago

Chat

Status: Experimental

Structure

This repo contains code for running a Fuchsia specific set of Chat modules.

  • agents: Fuchsia agents (background services) using Modular APIs.
    • content_provider: The chat content provider agent which communicates with the firebase DB and the Ledger instance.
  • modules: Fuchsia application code using Modular APIs.
    • conversation: UI module for displaying chat messages for a conversatoin.
    • conversation_list: UI module for displaying the list of conversations.
    • story: The primary entry point for the full Chat experience.
  • services: FIDL service definitions.

Development

Setup

This repo is already part of the default jiri manifest.

Follow the instructions for setting up a fresh Fuchsia checkout. Once you have the jiri tool installed and have imported the default manifest and updated return to these instructions.

It is recommended you set up the Fuchsia environment helpers in scripts/env.sh:

source scripts/env.sh

Workflow

There are Makefile tasks setup to help simplify common development tasks. Use make help to see what they are.

When you have changes you are ready to see in action you can build with:

make build

Once the system has been built you will need to run a bootserver to get it over to a connected Acer. You can use the env.sh helper to move the build from your host to the target device with:

freboot

Once that is done (it takes a while) you can run the application with:

make run

You can run on a connected android device with:

make flutter-run

Optional: In another terminal you can tail the logs

${FUCHSIA_DIR}/out/build-magenta/tools/loglistener

Firebase DB Setup

The chat modules use the Firebase Realtime Database to send and receive chat messages between users. This section describes what needs to be configured before running the chat modules correctly.

Setup

  1. Create a new Firebase project via the console.
  • Navigate to Authentication and enable Google sign-in.
  • Under “Whitelist client IDs from external projects” add the client ID for an existing OAuth application. This will allow an extra scope to be added for that app's sign-in flow enabling Google authentication and authorization for this project.
  1. Setup the security rules for realtime database.
  • Navigate to Database -> RULES from the Firebase console.

  • Set the security rules as follows:

    {
      "rules": {
        "emails": {
          "$encodedEmail": {
            ".read": "$encodedEmail == auth.token.email.toLowerCase().replace('.', '&2E')",
            ".write": "auth != null",
          }
        },
      }
    }
    

    This will ensure that users can send any messages to any signed up users and the messages can only be read by the designated recipients.

  1. Add the Firebase project information to the configuration file, so that your chat module may send messages to other users.
  • Manually add the following values to //apps/modules/common/config.json.
    • "chat_firebase_api_key": <web_api_key>
    • "chat_firebase_project_id": <firebase_project_id>
    • These two value can be found from your Firebase project console. Navigate to the Gear icon (upper-left side) -> Project settings.
  • After adding these values, build fuchsia again, or manually copy the config.json file onto the fuchsia device at /system/data/modules/config.json.
  • NOTE: This implies that only the chat users using the same Firebase project can talk to each other.

Running Tests

Chat Agent Tests

To run the chat agent tests, build fuchsia with --modules boot_test_modular option and boot into fuchsia using a target device or a QEMU instance.

$ fset --modules boot_test_modular
$ fbuild
$ frun <options> # when using QEMU.

Once the fuchsia device is booted, run the following command from the host.

$ $FUCHSIA_DIR/apps/test_runner/src/run_test \
  --test_file=$FUCHSIA_DIR/apps/modules/chat/tests/chat_tests.json

When the test passes, the resulting output should look like this:

Running chat_content_provider_test ..
./loglistener: listening on [::]33337 for device same-undo-rail-gains
.. pass

In case something fails, the script will output fail, and also print out the fuchsia console messages with more detailed error logs.

Fore more details, refer to the test_runner documentation.

Firebase DB Authentication using REST APIs

(NOTE: You can ignore this section if you're only interested in running the chat app.)

There is not a Fuchsia compatible Firebase package for Dart. The following is a description of raw REST calls that the ChatContentProvider agent is doing behind the scenes to enable message transport via Firebase Realtime DB.

User authentication is managed via an existing project's login flow. For that flow to obtain to correct credentials it will need to be configured with an additional scope: “https://www.googleapis.com/auth/plus.login”.

When a user authenticates a JWT is returned in the response along with the traditional OAuth tokens. This special token, id_token will be used in the following call to the Google Identity Toolkit API. The following variables are required to proceed:

    export FIREBASE_KEY="<web_api_key as above>"
    export FIREBASE_URL="https://<your_firebase_project_id>.firebaseio.com"
    export GOOGLE_ID_TOKEN="<JWT id_token from separate OAuth process>"

The following request is required for new users of the Firebase project (identitytoolkit#VerifyAssertionResponse):

curl -Li -X POST \
  -H "accept: application/json" \
  -H "content-type: application/json" \
  -d "{ \"postBody\": \"id_token=${GOOGLE_ID_TOKEN}&providerId=google.com\", \"requestUri\": \"http://localhost\", \"returnIdpCredential\": true, \"returnSecureToken\": true}" \
  https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyAssertion?key=$FIREBASE_KEY

This call authenticates the Google API authenticated user with the Firebase project. The JSON body of the response will have this schema. Among the returned values, some useful ones include:

  • "localId": The Firebase User ID (UID) associated with this user.
  • "email" : Primary email address for this user.
  • "idToken": Auth token to be used in any subsequent REST API calls to the Firebase DB.
    (Not to be confused with oauthIdToken value.)

Note that this new idToken is different from the original GOOGLE_ID_TOKEN we used to make this call.

    export FIREBASE_USER_ID="<'localId' value from the response>"
    export FIREBASE_AUTH_TOKEN="<'idToken' value from the response>"

To grab the user's profile information use $FIREBASE_AUTH_TOKEN with identitytoolkit#GetAccountInfo:

curl -Li -X POST \
  -H "accept: application/json" \
  -H "content-type: application/json" \
  -d "{ \"idToken\": \"${FIREBASE_AUTH_TOKEN}\" }" \
  https://www.googleapis.com/identitytoolkit/v3/relyingparty/getAccountInfo?key=$FIREBASE_KEY

This will return some useful profile data.

Authorization

From here the database can be managed via the Firebase CLI or Firebase Console's web UI for defining schemas etc. For example you can create a database rule for users where only they can access their own data:

{
  "rules": {
    ".read": "auth != null",
    ".write": "auth != null",

    "users": {
      "$uid": {
        ".read": "$uid === auth.uid",
        ".write": "$uid === auth.uid"
      }
    }
  }
}

Once configured correctly publish the schema to the project with the Firebase CLI or the Firebase Console. From here you can start working with records.

Records

View the user's record.

curl -Li \
  -H "accept: application/json" \
  $FIREBASE_URL/users/$FIREBASE_USER_ID.json?auth=$FIREBASE_AUTH_TOKEN

Update the record.

curl -Li -X PUT \
  -H "accept: application/json" \
  -H "content-type: application/json" \
  -d "{ \"uid\": \"${FIREBASE_USER_ID}\", \"username\": \"John Doe\" }" \
  $FIREBASE_URL/users/$FIREBASE_USER_ID.json?auth=$FIREBASE_AUTH_TOKEN

Stream updates.

curl -Li \
  -H "accept: text/event-stream" \
  $FIREBASE_URL/users/$FIREBASE_USER_ID.json?auth=$FIREBASE_AUTH_TOKEN