joshblour
joshblour

Reputation: 1034

Admin on rest - implementing aor-realtime

I'm having a real hard time understanding how to implement aor-realtime (trying to use it with firebase; reads only, no write).

The first place I get stuck: This library generates a saga, right? How do I connect that with a restClient/resource? I have a few custom sagas that alert me on errors, but there is a main restClient/resource backing those. Those sagas just handles some side-effects. In this case, I just don't understand what the role of the client is, and how it interacts with the generated saga (or visa-versa)

The other question is with persistence: Updates stream in and the initial set of records is not loaded in one go. Should I be calling observer.next() with each update? or cache the updated records and call next() with the entire collection to-date.

Here's my current attempt at doing the later, but I'm still lost with how to connect it to my Admin/Resource.

import realtimeSaga from 'aor-realtime';
import { client, getToken } from '../firebase';
import { union } from 'lodash'

let cachedToken 
const observeRequest = path => (type, resource, params) => {
    // Filtering so that only chats are updated in real time
    if (resource !== 'chat') return;

    let results = {}
    let ids = []

    return {
        subscribe(observer) {
            let databaseRef = client.database().ref(path).orderByChild('at')

            let events = [ 'child_added', 'child_changed' ]

            events.forEach(e => {
                databaseRef.on(e, ({ key, val }) => {
                    results[key] = val()
                    ids = union([ key ], ids)
                    observer.next(ids.map(id => results[id]))
                })  
            })          
            const subscription = {
                unsubscribe() {
                    // Clean up after ourselves
                    databaseRef.off()
                    results = {}
                    ids = []
                    // Notify the saga that we cleaned up everything
                    observer.complete();
                }
            };

            return subscription;
        },
    };
};

export default path => realtimeSaga(observeRequest(path));

Upvotes: 0

Views: 148

Answers (2)

joshblour
joshblour

Reputation: 1034

Here's the solution I came up with, with guidance by @gildas:

import realtimeSaga from "aor-realtime";
import { client } from "../../../clients/firebase";
import { union } from "lodash";
const observeRequest = path => {      
  return (type, resource, params) => {
    // Filtering so that only chats are updated in real time
    if (resource !== "chats") return;

    let results = {}
    let ids = []

    const updateItem = res => {
      results[res.key] = { ...res.val(), id: res.key }
      ids = Object.keys(results).sort((a, b) => results[b].at - results[a].at)
    }

    return {
      subscribe(observer) {
        const { page, perPage } = params.pagination
        const offset = perPage * (page - 1)

        const databaseRef = client
            .database()
            .ref(path)
            .orderByChild("at")
            .limitToLast(offset + perPage)

        const notify = () => observer.next({ data: ids.slice(offset, offset + perPage).map(e => results[e]), total: ids.length + 1 })

        databaseRef.once('value', snapshot => {
          snapshot.forEach(updateItem)
          notify()
        })

        databaseRef.on('child_changed', res => {
          updateItem(res)
          notify()    
        })


        const subscription = {
          unsubscribe() {
            // Clean up after ourselves
            databaseRef.off();
            // Notify the saga that we cleaned up everything
            observer.complete();
          }
        };

        return subscription;
      }
    };
  }
};

export default path => realtimeSaga(observeRequest(path));

Upvotes: 1

Gildas Garcia
Gildas Garcia

Reputation: 7066

How do I connect that with a restClient/resource?

Just add the created saga to the custom sagas of your Admin component.

About the restClient, if you need it in your observer, then pass it the function which return your observer as you did with path. That's actually how it's done in the readme.

Should I be calling observer.next() with each update? or cache the updated records and call next() with the entire collection to-date.

It depends on the type parameter which is one of the admin-on-rest fetch types:

  • CRUD_GET_LIST: you should return the entire collection, updated
  • CRUD_GET_ONE: you should return the resource specified in params (which should contains its id)

Upvotes: 1

Related Questions