Giter VIP home page Giter VIP logo

feathers-reactive's Introduction

feathers-reactive

CI Download Status

Reactive API extensions for Feathers

About

feathers-reactive adds a watch() method to services. The returned object implements all service methods as RxJS v7 observables that automatically update on real-time events.

Options

The following options are supported:

  • idField (mandatory): The id property field of your services. Depends on your service/database. Usually 'id' (SQL, Rethinkdb, …) or '_id' (MongoDB, NeDB, … ).
  • dataField (default: data): The data property field in paginated responses
  • listStrategy (default: smart): The strategy to use for streaming the data. Can be smart, always or never. Avoid using always whenever possible.
  • sorter (function(query, options) {}): A function that returns a sorting function for the given query and option including pagination and limiting. Does not need to be customized unless there is a sorting mechanism other than Feathers standard in place.
  • matcher (function(query)): A function that returns a function which returns whether an item matches the original query or not.
  • pipe (operator | operator[]) One or multiple rxjs operators of the form function(observable) => observable like you would pass them to an Observable's .pipe method. The supplied operators are applied to any Observable created by feathers-reactive. options.pipe: tap(data => console.log(data)) would log every emitted value to the console.

Application level

import feathers from '@feathersjs/feathers';
import { rx } from 'feathers-reactive';

const app = feathers().configure(rx(options));

Service level

With feathers-reactive configured on the application individual options can be set at the service level with service.rx:

// Set a different id field
app.service('todos').rx({
  idField: '_id'
});

Method call level

Each method call can also pass its own options via params.rx:

// Never update data for this method call
app.service('todos').watch({ listStrategy: 'never' }).find();

List strategies

List strategies are used to determine how a data stream behaves. Currently there are three strategies:

  • never - Returns a stream from the service promise that only emits the method call data and never updates after that
  • smart (default) - Returns a stream that smartly emits updated list data based on the services real-time events. It does not re-query any new data (but does not cover some cases in which the always strategy can be used). When using smart list strategy, an additional method reset is available to get fresh data from the server.
  • always - Re-runs the original query to always get fresh data from the server on any matching real-time event. Avoid this list strategy if possible since it will put a higher load on the server than necessary.

Usage

import {feathers} from '@feathersjs/feathers';
import memory from 'feathers-memory';
import { rx } from 'feathers-reactive';

const app = feathers()
  .configure(rx({
    idField: 'id'
  }))
  .use('/messages', memory());

const messages = app.service('messages');

messages.create({
  text: 'A test message'
}).then(() => {
  // Get a specific message with id 0. Emit the message data once it resolves
  // and every time it changes e.g. through an updated or patched event
  messages.watch().get(0).subscribe(message => console.log('My message', message));

  // Find all messages and emit a new list every time anything changes
  messages.watch().find().subscribe(messages => console.log('Message list', messages));

  setTimeout(() => {
    messages.create({ text: 'Another message' }).then(() =>
      setTimeout(() => messages.patch(0, { text: 'Updated message' }), 1000)
    );
  }, 1000);
});

Will output:

My message { text: 'A test message', id: 0 }
Message list [ { text: 'A test message', id: 0 } ]
Message list [ { text: 'A test message', id: 0 },
  { text: 'Another message', id: 1 } ]
My message { text: 'Updated message', id: 0 }
Message list [ { text: 'Updated message', id: 0 },
  { text: 'Another message', id: 1 } ]

Frameworks

Let's assume a simple Feathers Socket.io server in app.js like this:

npm install @feathersjs/feathers @feathersjs/socketio feathers-memory

import {feathers} from '@feathersjs/feathers';
import socketio from '@feathersjs/socketio';
import memory from 'feathers-memory';

const app = feathers()
  .configure(socketio())
  .use('/todos', memory());

app.on('connection', connection => app.channel('everybody').join(connection));
app.publish(() => app.channel('everybody'));

app.listen(3030).on('listening', () =>
  console.log('Feathers Socket.io server running on localhost:3030')
);

Usage

For an ES5 compatible version on the client (e.g. when using create-react-app) you can import feathers-reactive/dist/feathers-reactive. In client.js:

import io from 'socket.io-client';
import feathers from '@feathersjs/client';
import rx from 'feathers-reactive/dist/feathers-reactive';

const socket = io('http://localhost:3030');
const app = feathers()
  .configure(feathers.socketio(socket))
  .configure(rx({
    idField: 'id'
  }));

export default app;

React

A real-time ReactJS Todo application (with Bootstrap styles) can look like this (see the examples/react-todos folder for a working example);

import React, { Component } from 'react';
import client from './client';

class App extends Component {
  constructor (props) {
    super(props);
    this.state = {
      todos: [],
      text: ''
    };
  }

  componentDidMount () {
    this.todos = client.service('todos').watch()
      .find().subscribe(todos => this.setState(todos));
  }

  componentWillUnmount () {
    this.todos.unsubscribe();
  }

  updateText (ev) {
    this.setState({ text: ev.target.value });
  }

  createTodo (ev) {
    client.service('todos').create({
      text: this.state.text,
      complete: false
    });
    this.setState({ text: '' });
    ev.preventDefault();
  }

  updateTodo (todo, ev) {
    todo.complete = ev.target.checked;
    client.service('todos').patch(todo.id, todo);
  }

  deleteTodo (todo) {
    client.service('todos').remove(todo.id);
  }

  render () {
    const renderTodo = todo =>
      <li key={todo.id} className={`page-header checkbox ${todo.complete ? 'done' : ''}`}>
        <label>
          <input type='checkbox' onChange={this.updateTodo.bind(this, todo)}
            checked={todo.complete} />
          {todo.text}
        </label>
        <a href='javascript://' className='pull-right delete'
          onClick={this.deleteTodo.bind(this, todo)}>
          <span className='glyphicon glyphicon-remove' />
        </a>
      </li>;

    return <div className='container' id='todos'>
      <h1>Feathers real-time Todos</h1>

      <ul className='todos list-unstyled'>{this.state.todos.map(renderTodo)}</ul>
      <form role='form' className='create-todo' onSubmit={this.createTodo.bind(this)}>
        <div className='form-group'>
          <input type='text' className='form-control' name='description'
            placeholder='Add a new Todo' onChange={this.updateText.bind(this)}
            value={this.state.text} />
        </div>
        <button type='submit' className='btn btn-info col-md-12'>
          Add Todo
        </button>
      </form>
    </div>;
  }
}

export default App;

License

Copyright (c) 2023

Licensed under the MIT license.

feathers-reactive's People

Contributors

avimar avatar bitflower avatar claustres avatar corymsmith avatar daffl avatar danielang avatar ekryski avatar greenkeeper[bot] avatar greenkeeperio-bot avatar hubgit avatar j2l4e avatar jobobo21 avatar johnlindquist avatar johnny4young avatar marshallswain avatar musicformellons avatar niels-be avatar psi-4ward avatar rmanibus avatar rybaczewa avatar sean-nicholas avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

feathers-reactive's Issues

Subscriptions with $client query parameters are not reactive

Steps to reproduce

const setStore = data => {
    // set card store logic...
}

this.service.find({ query: { cardType: 'story', $client: { teamId: '12345' } } }).subscribe(setStore)

Expected behaviour

setStore() is called with result of initial find query, and should also be called upon subsequent changes to any documents associated with the query.

Actual behaviour

setCardStore() is first called with correct data, but subsequent calls result in an empty data array (rather than all relevant documents)

System configuration

Setup using a RethinkDb database.

[Docs proposal] Add more documentation about the matcher option

Currently there are three issues open #53 #56 #60 (and I believe a few that are closed as well) that all stem from the same behavior of the (matcher() from [email protected])[https://github.com/feathersjs/commons/blob/auk/src/utils.js#L146-L171] which is the default matcher function used by feathers-reactive.

The specifics of the behavior are such that many complex queries will cause the matcher to return false all the time, dropping the events rather than simply ignoring the parts of the query it doesn't understand.

I don't know if this is likely to be changed for some later release or update with Buzzard, but I think before any code change is made, providing more documentation or a warning about the matcher option might help avoid a lot of confusion, as this is a single point of configuration that can solve all of the related issues. E.g. in our code we solved this simply doing something like the following:

const { matcher, omit } = require('feathers-commons/lib/utils');

feathers()
    .configure(hooks())
    .configure(socketio(this.socket, { timeout: 10000 }))
    .configure(rx({
        idField: '_id',
        strategy: 'always',
        // fix for $nonstandardSyntax bug
        matcher: (query) => matcher(omit(query, ['$nonstandardSyntax']))
    }));

It took us almost a week to get around to figuring out that this was the only fix we needed, so I imagine there are others who will hit the same pitfall...

Transpile fails for client builds

This module requires:

"@feathersjs/commons": "^1.4.0",
"@feathersjs/feathers": "^3.1.1",

Those are untranspiled versions, which will break builds that require to run in non es6 browsers
(edge, ie11).
Please manually transpile the dependencies or require the "@feathersjs/client" version of those modules.

Related:
feathersjs/feathers#608

Build fails with create-react-app

Steps to reproduce

create-react-app my-app
cd my-app
yarn add feathers-reactive

Load feathers-reactive in one of the files, then:

yarn build

Expected behavior

The project should build successfully.

Actual behavior

feathersjs/feathers#774 is triggered, causing the build to fail with the following error:

$ react-scripts build
Creating an optimized production build...
Failed to compile.

Failed to minify the code from this file: 

 	./node_modules/@feathersjs/commons/lib/utils.js:8 

Read more here: http://bit.ly/2tRViJ9

error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
ERROR: "build-js" exited with 1.

System configuration

Tell us about the applicable parts of your setup.

Module versions

  • create-react-app 1.5.2
  • feathers-reactive 0.6.0

NodeJS version: 8

Operating System: Mac OS X

Module Loader: Webpack (via create-react-app)

listStrategy doesn't work as mentioned in readme

I'm very new to feathers and RxJS so... my mind is blown :P
But...

app.service('serviceName').rx({
    idField: '_id',
    listStrategy: 'always'
});

does not work. I figured out, that I must pass this expression: feathers.rx.strategy(Rx).always to listStrategy. Then it works as expected.

By the way

// Always fetch fresh data for this method call
app.service('todos').find({ listStrategy: 'always' });

should be

// Always fetch fresh data for this method call
app.service('todos').find({rx: { listStrategy: 'always' }});

Correct?

Socket reconnect. How to?

I'm trying to figure out how to handle socket reconnection. It seems like feathers reactive doesn't handle this reconnections.

Shouldn't that be the case? The only solution i can think of is listening on the socket "reconnected" event and recreate all the observables, but it seems very hacky.

Update rxjs to 5.5

RxJS 5.5 with lettable operators is out.
The way we import operators now patches Observable.prototype creating blind dependencies. Moving to lettables would avoid that.
5.5 still allows the prototype operators, so this would not impose a breaking change per se.
However applications that rely on an operator that feathers-reactive imported without importing it on their own, will break. This is easy to debug and fix though.

data.sort is not a function

Can anyone advise what would cause Feathers to throw the error in utils.js

I just can't see what I've done wrong, and I only get the error once, any repeat requests that are the same don't give the error.

Uncaught TypeError: data.sort is not a function
    at MapSubscriber.eval [as project] (utils.js:77)
    at MapSubscriber._next (map.js:79)
    at MapSubscriber.Subscriber.next (Subscriber.js:92)
    at eval (PromiseObservable.js:68)
    at ZoneDelegate.invoke (zone.js:388)
    at Object.onInvoke (core.js:4733)
    at ZoneDelegate.invoke (zone.js:387)
    at Zone.run (zone.js:138)
    at eval (zone.js:858)
    at ZoneDelegate.invokeTask (zone.js:421)

How does the sorter work?

It's not really clear from the documentation how the sorter option should be used. I'm currently having issues with the sort order after making a change to a list of items (i.e the sort order isn't the same as the original request) and wondering if I need to be using this option to rectify the problem.

Consuming service with server-side pagination & infinite loading

(Posting here following discussion on feathersjs.slack.com)

Given a service which paginates server side, what would be the appropriate way to consume it, in the following scenario:

We want to create an infinite scrolling list, which updates in real-time, as resources are created/updated/removed.

Currently, using something like

myService.find().subscribe(response => console.log(response));

initially yields the response from the server, say:

{
  data: [
    { id: '1111' },
    { id: '2b2b' },
    { id: '5aef' },
    { id: '3333' },
    { id: '4444' }
],
  limit: 5,
  skip: 0,
  total: 5
}

If we then create a resource with an id of '5555' (say by hitting the server with Postman), the same observable yields again:

{
  data: [
    { id: '1111' },
    { id: '2b2b' },
    { id: '3333' },
    { id: '4444' },
    { id: '5555' }
  ],
  limit: 5,
  skip: 0,
  total: 6
}

Notice the results are now sorted by id.

If we create another resource, with id of '6666', the observable yields:

{
  data: [
    { id: '1111' },
    { id: '2b2b' },
    { id: '3333' },
    { id: '4444' },
    { id: '5555' }
  ],
  limit: 5,
  skip: 0,
  total: 7
}

So the observable is sorting by id, and enforcing the $limit, which we didn't pass to it (it was in the server's response). This behaviour is not ideal for implementing an infinite list. There seems to be no good pattern for consuming such a service like this.

@daffl (on Slack):

I think there are two things:

  • Allow non paginated reactive lists (right now they are truncated based on $limit)
  • Have a code sample for creating an infinite loading observable

Strategy updates: `passive` and `active`

Currently, the smart strategy removes entries when they get deleted and tries to add them to lists (in the right spot) if they match the query.

There are some edge cases that currently are not covered:

  • If the entry is smaller or larger it will get added to the beginning or the end of the list respectively. This might not necessary be the case (e.g. when you $skip and $limit there is no certain way of determining if it is in the list or comes at a position after/before)
  • If an entry gets deleted, it gets removed from reactive lists. If you set $limit they can end up smaller than what you would expect.

If you wanted to cover those edge cases you would use the always strategy which is less than ideal (ultimately I'm not sure it should exist at all).

This is where I think it makes sense to introduce two new list strategies (and remove the smart strategy):

Passive

The passive strategy will update exiting entries in the list (and re-order accordingly) and remove deleted entries but otherwise not modify it (new or updated entries that match the query will not be added).

Active

The active strategy will request a window slightly larger than $limit to be aware of its surrounding entries. That way it will be able to determine for any matching entry where it fits in. If the window gets too small (e.g. because entries were removed from the list) it will re-request a new list.

Feature Request: Browserify feathers-rx for angular2

Hi feathers team,
I'd like to use the rx-extension also in the browser (in an angular2 application). Currently there is no possibility as it seems to me.
I already tried to use browserify in the same way as the feathers-client does, but sadly without a usable result. Does someone know how to achieve this?

watch function is not working on all methods

I am using

  • feathersjs/feathers: ^3.1.3
  • feathersjs/socketio-client: ^1.1.0
  • "feathers-reactive": "^0.6.0",
  • "rxjs": "^5.5.6",
  • "socket.io-client": "^2.0.4"

Whenever I try to create, patch, remove and update data, the watch function does not respond anything.

Here is my client setup:

import io from 'socket.io-client';
import feathers from '@feathersjs/feathers';
import socketio from '@feathersjs/socketio-client';
import auth from '@feathersjs/authentication-client';
import rx from 'feathers-reactive';
import RxJS from 'rxjs';

const options = {
  idField: '_id',
  listStrategy: 'always'
};
const socket = io('http://localhost:3000', {
  transports: ['websocket'],
  forceNew: true
});
const client = feathers();

client.configure(socketio(socket))
      .configure(auth({ storage: AsyncStorage }))
      .configure(rx(options));

client.service('todos').watch().find().subscribe(todos => {
      console.log(todos); // This does nothing
});

The only time the .watch() works is if you reload the app because it is inside componentDidMount(); other than that it does not return anything even if you create, patch, remove or update.

Please advise thank you.

EventEmitter memory leak detected

As reported here feathersjs-ecosystem/client#236

In my Angular app I've started to get the following error in certain circumstances and not sure why.

feathers.js:2685 (node) warning: possible EventEmitter memory leak detected. 11 listeners added. Use emitter.setMaxListeners() to increase limit.

My page has next and prev links that allow me to navigate to the next gallery but after the 11th navigation it gives the error. In my component below I'm just subscribing to the route change to then fetch the next or previous item

export class GalleryDetailComponent implements OnInit, OnDestroy {
  gallery$: Observable<any>;
  constructor(private service: ApiService, private route: ActivatedRoute) {
    service.setup('gallery');
  }

  ngOnInit() {
    this.getGallery();
  }

  getGallery() {
    this.gallery$ = this.route.params.pipe(
      switchMap((params: any) => {
        return this.service.get(params['slug']);
      })
    );
  }

  ngOnDestroy() {}
}

and this is my feathers service and api service.

@Injectable()
export class FeathersService {
  private _feathers: any;
  private _rest: any;
  constructor(public http: HttpClient) {
    this._rest = rest(environment.host);
    this._feathers = feathers();
    this._feathers.configure(
      authentication({
        storageKey: 'auth-token',
        storage: new CookieStorage({
          path: '/',
          expires: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000)
        })
      })
    );
    this._feathers.configure(
      this._rest.angularHttpClient(this.http, { HttpHeaders: HttpHeaders })
    );
    this._feathers.configure(
      feathersRx({
        listStrategy: 'always',
        idField: '_id'
      })
    );
  }

  // expose services
  public service(name: string) {
    return this._feathers.service(name);
  }
}

@Injectable()
export class ApiService {
  private service: any;
  constructor(private feathers: FeathersService) {}

  setup(endpoint: string) {
    this.service = this.feathers.service(endpoint);
  }

  get(id: number | string, query?: any) {
    return this.service.watch({ strategy: 'always' }).get(id, { query: query });
  }
}

Cannot find module 'feathers' when using @feathersjs/client

Steps to reproduce

(First please check that this issue is not already solved as described
here
)

  • Tell us what broke. The more detailed the better.
  • If you can, please create a simple example that reproduces the issue and link to a gist, jsbin, repo, etc.

Expected behavior

Tell us what should happen

Actual behavior

Tell us what happens instead

System configuration

Tell us about the applicable parts of your setup.

Module versions (especially the part that's not working):

NodeJS version:

Operating System:

Browser Version:

React Native Version:

Module Loader:

find() with query

Most of the examples use find() without a specific query.
when using find(query) do the reactive event listeners take into account the original query?

Impossible to type

I'm still slowly (but surely) working my way through and typing some core Feathers libraries. Unfortunately, I'm at an impasse with feathers-reactive. There's just no good way to statically type a function that can return both a Promise and an Observable. At best, you treat the return type as generic, but that gets messy very quickly.

While I was thinking of possible solutions, it occurred to me that the fact that this can't easily be described with a type system maybe means that it shouldn't be that way. Magically returning both a Promise and Observable with promisify is arguably not ideal from an API design standpoint.

I think one possible solution would be to use the existing method registered on the service output and have that be the exclusive thing that transform the return type, like:

service.rx().find({}).subscribe

Alternatively, the service method calls themselves could change, like

service.findReactive({})

though this is maybe a bit confusing to developers.

Any thoughts on this?

Merge doesn't work?

Hi I tried the merge option on all levels (application, service and method), but it is never triggered.

.configure(rx(RxJS, {
        merge : function(current, data) {
            console.log(current, data);
        }
    }))
const userService = app.service('users').rx({ merge : function(current, data) {
    console.log(current, data);
}})
userService
    .find({
        merge : function(current, data) {
            console.log(current, data);
        }
}).subscribe(users => dispatch(receive_users(users.data))

Am I doing something wrong?

Several issues on 'update'

Say I have a 'messages' service with 3 existing messages. I query those via .find() and subscribe:

{
  "total": 3,
  "limit": 5,
  "skip": 0,
  "data": [
    {
      "text": "read me!",
      "id": 0
    },
    {
      "text": "read me!",
      "id": 1
    },
    {
      "text": "read me!",
      "id": 2
    }
  ]
}

I now change one of the messages (id 1) using cURL. The Observable emits the following object:

{
  "total": 0,
  "limit": 5,
  "skip": 0,
  "data": [
    {
      "text": "read me!",
      "id": 0
    },
    {
      "text": "read me!",
      "id": 1
    },
    {
      "text": "I changed",
      "id": "1"
    },
    {
      "text": "read me!",
      "id": 2
    }
  ]
}

Several things are unexpected:

  • the outdated object isn't removed.
  • total changed to 0. Expected 3 (or 4 in case of not removed item). not sure about this, couldn't reproduce
  • id of the new object is of type string. Expected number.

Caching is broken with rxjs 5.5.0

With rxjs <= 5.4.3 cached observables are cleaned up when there are no subscribers left as it uses shareReplay.
Apparently this isn't the desired behavior of shareReplay, thus it was changed in this commit: ReactiveX/rxjs@accbcd0

We need to either use the old shareReplay operator:

function _oldStyleShareReplay (bufferSize, windowTime, scheduler) {
  let subject;

  const connectable = multicast(function shareReplaySubjectFactory () {
    if (this._isComplete) {
      return subject;
    } else {
      return (subject = new ReplaySubject(bufferSize, windowTime, scheduler));
    }
  });
  return (source) => refCount()(connectable(source));
}

or generally address how caching and cleanup are done and use a different strategy to accomplish sane observable reuse.

Get idField from service.id if available

feathers-service-tests guarantees id to be present on commonly used (i.e. official) services.
Reading the appropriate value for idField from service.id would reduce boilerplate, especially when you have both id and _id services. Any thoughts on this?

List not updating when using fastjoin

I have the following mongoose model

  const orderComment = new Schema(
    {
      comment: 'string',
      order: {
        type: ObjectId,
        ref: 'order'
      },
      line_item: {
        type: ObjectId,
        ref: 'lineItem'
      },
      owner: {
        type: ObjectId,
        ref: 'users'
      }
    },
    {
      timestamps: true
    }
  );

and I'm populating the line_item using fastjoin i.e

  after: {
    all: [fastJoin(resolver)],
    find: [],
    get: [],
    create: [],
    update: [],
    patch: [],
    remove: []
  },

  joins: {
    line_item: () => async (post, context) =>
      !post.line_item
        ? null
        : (post.line_item = await context._loaders.items._id.load(
            post.line_item
          ))
  }

How ever when I create a new comment with the following data the list doesn't automatically update

{
  comment: 'my comment';
  line_item: '5a7ef92cfadfee10fb85d306';
  order: '5a7ef92bfadfee10fb85d305';
}

if I exclude the line_item property or remove the population is does work, so I assume it's because what I'm posting is formatted different to the list, but no idea how you get around this.

find() shouldn't be executed before subscribing

Since the Observable is generated fromPromise, its logic is executed on creation rather than on subscription.

Thus automatic retries or automatic subscription after auth are cumbersome at best.

I'm not familiar with how feathers' plugin system works, so please bear with me. I managed to hack feathers-reactive to return a cold observable when find() is called. However I didn't really dive into how the listStrategy works. Reactivity seems to be broken.

Unfortunately, this approach breaks "promisification". Is there anything to it besides backward compatibility?

list.js now basically looks like this:

export default function(Rx, events, options) {
  return function (params = {}) {
    const query = Object.assign({}, params.query);

    const __super = this._super;

    const obs = Rx.Observable.create(observer => {
      const _observer = observer;

      __super.apply(this, arguments)
        .then(res => {
          _observer.next(res);
          _observer.complete();
        })
        .catch(e => _observer.error(e));
    });

    // Hack because feathers-query-params deletes stuff from `params.query`
    // Will be fixed in the next version
    params.query = query;

    options = Object.assign(options, this._rx, params.rx);

    return options.listStrategy.call(this, obs, events, options, args);
}

This enables me to do stuff like this:

authed$:Subject<boolean> = new Subject<boolean>();

this.feathers.service('items')
      .find()
      .delayWhen(
        () => Observable.of(0), // no emission delay
        this.authed$ // delay subscription until this.authed$ emits true
      )
      .subscribe(res => console.log(res));

Not authenticated, yet? Doesn't matter:

feathers.authenticate({
      type      : 'local',
      'email'   : email,
      'password': password
    })
      .then(() => this.authed$.next(true))
      .catch(error => console.error('Error authenticating!', error));

React todo example not working

Example on https://github.com/feathersjs/feathers-reactive/tree/master/examples/react-todos
it is not working

Error:

steal.js:6363 Error loading "client" at http://localhost:3030/client.js
http://localhost:3030/client.js:55:7: Unexpected token < undefined(anonymous function) 
@ steal.js:6363tryCatchReject 
@ steal.js:1188runContinuation1 
@ steal.js:1147Rejected.when 
@ steal.js:968Pending.run 
@ steal.js:826Scheduler._drain 
@ steal.js:102Scheduler.drain 
@ steal.js:67run 
@ steal.js:273

Add support for params and query object streams

Basically, if one of the arguments (or the params.query) is an observable we should do what was suggested in #39 and mergeMap them into a single stream:

const service = app.service('messages');
const query = new RxJS.BehaviorSubject({ $skip: 0 });

const find = query.mergeMap(query => service.find({ query }));

find.subscribe(data => console.log('Data is', data));

query.next({ $skip: 10 });

feathers-reactive not responding to create event with query applied in .find() (possibly related to #62)

Steps to reproduce

const assignjobs = empNumber => {
  const jobassign = app.service("assign");
  jobassign
    .watch({ listStrategy: "always" })
    .find({
//create event triggers without query parameters
      query: {
        empnumber: empNumber
      }
    })
    .subscribe(jobs => {
      console.log(jobs);
      store.dispatch(rtupdate(jobs, "assignedjobs"));
    });
};

Expected behavior

On create event the redux dispatch should trigger.
If I remove the query in find the create event triggers an update.

Actual behavior

Every other event in crud triggers correctly except "create".

System configuration

/* This is in index that gets imported to the react method above
export const app = feathers()
.configure(socketio(socket))
.configure(reactive({ idField: "id" })); */
On the feathers side I am using sequelize as the orm.

Module versions (especially the part that's not working):
Feathers server is version 2.2.2
feathers-reactive on client is 0.5.3
NodeJS version:
v8.6.0

Operating System:
Ubuntu 16.04 lts
Browser Version:
chromium 61.0.3163

Use TC39 proposal for Symbol.observable to turn resources into Observables

See this (+ polyfill).

The idea is that instead of monkey patching resources into Observables, we should just implement Symbol.observable on them and create an observable by passing them into Observable.from.

For example,

import {Observable} from 'rxjs';
import app from './app'; // some feathers app

const messages = app.service('messages');

const messages$ = Observable.from(messages.find()); // messages.find() has a Symbol.observable member

messages$
  .switchMap(/* whatever */)
  // etc. ...

This removes the direct dependency on RxJS and moves toward the proposed standard. See the implementation in redux.

Link to outdated docs confuses about project state

The first sentence in the project description links to an outdated ("legacy") part of the feathers documentation - this is very confusing. Is this a part of the project that is not maintained any more? Is it not compatible with the rest of feathers code? Will it be compatible with future developments? Too many questions that arise from that single url to deprecated docs...

The whole ecosystems looks very fragmented already - it would be a great help and much easier to follow these developments if creators tried to keep the project docs consistent.

Thank you very much for your attention!

Subscription doesn't report updates when used with buzzard

Steps to reproduce

Upgrade the sample todo app to uses FeathersJS v3 on the front end and back end.

Expected behavior

It should work normally. Any updates should reflect on the page in real time.

Actual behavior

No updates reflect on the page until the next page reload.

System configuration

Tell us about the applicable parts of your setup.

Module versions

  • create-react-app 1.5.2
  • feathers-cli 3.6.1
  • @feathersjs/feathers 3.1.3
  • @feathersjs/client 3.4.2
  • feathers-reactive 0.6.0

NodeJS version: 8

Operating System: Mac OS X

Browser Version: Google Chrome 64.0.3282.167

React Native Version: n/a

Module Loader: Webpack (via create-react-app)

Is there a way to update query params?

Is it possible to update / alter query params after subscribing? The reason why I'm asking is that I'm working on a data table and it should be possible to change sorting persistently. Currently I'm just refetching the data, however on a create or update it goes back to the original defined sorting.

I guess the proper way is to unsubscribe and then subscribe again with the new query params?

Outstanding issues

  • patching did not update the list
  • limiting only works with pagination
  • paginated metadata should be updated
  • re-fetch when list size < $limit value (on the client)
  • caching
  • Make Rx an option (no hard dependency)
  • investigate replaying events (time travel)

Official query syntax for $gt, $gte does not work

Steps to reproduce

I got a feathers-reactive service and i want to query some objects that have dates (named start in the example) that are greater then now:

this.someService.find({ query: { $sort: { start: 1 /* sorting works */ }, start: { $gte: moment().startOf('day').toDate() /* this breaks the updates */ } }, rx: { listStrategy: 'always' /* doesn't help */ } }); });

Expected behavior

I'm expecting updates to my subscription

Actual behavior

Initial query works, no updates (created ...).
Because of the initial query is working fine and because without the $gt on "start" the updates also work i think the issue is in feathers-reactive

System configuration

"feathers": "^2.1.3",
"feathers-hooks": "^2.0.1",
"feathers-reactive": "github:feathersjs/feathers-reactive",
"feathers-rest": "^1.7.3",
"feathers-socketio": "^2.0.0",

smart strategy + matcher don't filter initial find

app.configure(rx(RxJS, { listStrategy: 'smart' }));
app.service('meetings').rx({
  idField: '_id',
  matcher: query => item => item.onOff === 'on'
});
meetings.find().subscribe(meetings => { console.log(meetings); })

This prints all items when subscribe is called the first time, rather than filtering them. I don't know if this is intentional, but meetings.find({ query: { onOff: 'on' }}) would filter them first.

That initial list gets filtered properly on the first patch event (and I assume the update event also),
but it will not be filtered if the first event is:

  • create an item that satisfies the matcher.
  • remove an item that satisfied the matcher (I assume same if matcher not satisfied).

React example missing componentWillUnmount

Not unsubscribing can lead to setting the state of a unmounted component.

I think this should be changed to:

componentDidMount() {
  this.observer = todos.find().subscribe(todos => this.setState({ todos }));
},

componentWillUnmount() {
  this.observer.unsubscribe();
},

requests performed multiple times

Related to #20.

Queries are now sent to the server at least twice. It's seems to happen due to the listStrategy subscribing multiple times.

I'm afraid I don't get how feathers' plugin system actually works.
I have no clue, what this and _super in list.js actually refer to.
So I wasn't able to get to the bottom of this at all.

Error: The RxJS instance does not seem to provide an `Observable` type.

This might be because I'm trying to use buzzard but thought I'd check as not had this issue on other projects. In my angular service I have

import { Injectable } from '@angular/core';
import * as feathers from '@feathersjs/client';
import * as feathersRx from 'feathers-reactive';
import * as io from 'socket.io-client';
import * as socketio from '@feathersjs/socketio-client';
import * as authentication from '@feathersjs/authentication-client';
import { environment } from '@env/environment';
import { CookieStorage } from 'cookie-storage';

@Injectable()
export class FeathersService {
  private _feathers: any;
  private _socket: any;
  constructor() {
    this._socket = io(environment.host);
    this._feathers = feathers();
    this._feathers.configure(socketio(this._socket));
    this._feathers.configure(
      feathersRx({
        idField: '_id'
      })
    );

    this._feathers.configure(
      authentication({
        storageKey: 'feathers-token',
        storage: new CookieStorage({
          path: '/',
          expires: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000)
        })
      })
    );
  }
}

but this is giving an error of

core.js:1350 ERROR Error: Uncaught (in promise): Error: The RxJS instance does not seem to provide an Observable type.
Error: The RxJS instance does not seem to provide an Observable type.
at FeathersRx (index.js:33)
at new FeathersService (feathers.service.ts:18)
at _createClass (core.js:10620)
at createProviderInstance$1 (core.js:10596)
at resolveNgModuleDep (core.js:10581)
at NgModuleRef
.get (core.js:11806)
at resolveDep (core.js:12302)
at createClass (core.js:12166)
at createDirectiveInstance (core.js:12011)
at createViewNodes (core.js:13449)
at FeathersRx (index.js:33)
at new FeathersService (feathers.service.ts:18)
at _createClass (core.js:10620)
at createProviderInstance$1 (core.js:10596)
at resolveNgModuleDep (core.js:10581)
at NgModuleRef
.get (core.js:11806)
at resolveDep (core.js:12302)
at createClass (core.js:12166)
at createDirectiveInstance (core.js:12011)
at createViewNodes (core.js:13449)
at resolvePromise (zone.js:824)
at eval (zone.js:876)
at ZoneDelegate.invokeTask (zone.js:425)
at Object.onInvokeTask (core.js:4620)
at ZoneDelegate.invokeTask (zone.js:424)
at Zone.runTask (zone.js:192)
at drainMicroTaskQueue (zone.js:602)
at

if I remove feathers reactive the error goes away.

Diff state

Hi,

I started using feathers-reactive with vuex and in a standard CRUD scenario everything works fine.
Now i m trying to make some canvas annotations (annotation is the feathers crud service) real time. In the subscribe i get the whole list of annotations whenever they change, but it is not very efficient to draw all of them again in the canvas for each change.
Is there a way to only get the diff list between 2 subscribes ?

patch subscribe fires twice

Im changing my angular app from using the http client to feathers reactive and sockets but seeing behaviour that I can't understand.

In my service I have the following

  update(id: number | string, data: any) {
    return this.feathers
      .service(this.endpoint)
      .watch()
      .patch(id, data);
  }

and in my component I have

  updateItem(data: any) {
    this.service.update(this.id, data).subscribe(resp => {
      console.log('navigate');
    });
  }

But doing that causes navigate to fire twice. The equivalent using angular http only fires once. i.e

  update(id: number | string, data: any) {
    return this.http
      .patch(`${this.host}/${this.endpoint}/${id}`, data)
      .map(res => res.json());
  }

"strategy=always" does not trigger brand new request

How could I trigger brand new request every time I use service.find? strategy=always does not work, and listStrategy=always does not work either. There is still no http request in my browser console. So what's the difference between strategy and listStrategy? (I see them both in README.md). Which one fulfill my purpose? Following is my code:

// feathers.service.ts
this.app = feathers()
  .configure(reactive({
    idField: '_id',
    listStrategy: 'always',
    strategy: 'always'
  }))
  .configure(rest(HOST).angularHttpClient(httpClient, {HttpHeaders}))
  .configure(hooks())
  .configure(authentication({path: '/login', storage: window.localStorage}))   
// role.component.ts
getRoles() {
  const Role = this.feathersService.app.service('roles')
  const log = console.log
  Role
    .watch({listStrategy: 'always', strategy: 'always'})
    .find()
    .subscribe(log,log)
}

Mixin of watch function into feathers service not working

Steps to reproduce

app = feathers();
app.configure(feathersRx({ idField: '_id' }));
console.log(app.service('messages')) // No property watch() here!

System configuration

Browser Version:
Firefox Version 55 (Working on Chromium for me)

I've down some research and the problem seems to be related to the way Uberprotos implements mixin() and the fact that FF defines Object.prototype.watch().

Subscriptions with $contains and $search are not reactive

Steps to reproduce

Subscribe to a service, using a $search query:

app.service('/service').find({ query: { firstName: { $search: '^james' } } }).subscribe(callback)

Expected behavior

The client receives updates when a document is added/removed/changed that matches the query

Actual behavior

The client does not receive the update

Upon calling the initial registration of the subscription, all data is returned as expected. But, the subscription isn't reactive — so we don't receive any more data.

Cannot find module 'feathers-reactive'.

Hi guys,

I'm trying to use feathers reactive in a angular cli project, all other feather modules are imported fine, except from feathers-reactive.

I keep getting Cannot find module 'feathers-reactive'., when I'm compiling.
I'm using latest version of feathers-reactive and I'm using angular/cli": "1.0.0-beta.32.3

I can see that the feathers-reactive folder is correctly in node_modules.

Is there any other way to import it? I'm doing this currently: import * as reactive from 'feathers-reactive';

Best regards
Patrick

Problems with React Native

I'm having this error while running this plugin with a React Native app (Android emulator or device):
captura de pantalla 2016-05-28 a las 16 37 14

omit: function omit(obj) {
    var result = _extends({}, obj);

    for (var _len = arguments.length, keys = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
      keys[_key - 1] = arguments[_key];
    }

    var _iteratorNormalCompletion = true;
    var _didIteratorError = false;
    var _iteratorError = undefined;

    try {
      // Here
      for (var _iterator = keys[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
        var key = _step.value;

        delete result[key];
      }
    } catch (err) {
      // ...

Original utils.js file comes from feathers-commons: https://github.com/feathersjs/feathers-commons/blob/master/src/utils.js#L27

Running in the iOS emulator works great, but it seems to not work on a real device.

I made a project to show the error (also tried older versions of RN and Rxjs).
https://github.com/mrpatiwi/reactNativeRxjs

Using feathers reactive with rxjs 5.5.x breaks angular production builds (build optimizing)

I installed the newest version of feathers-reactive and i tried the version from the rxjs5.5 branch.
I got rxjs 5.5.2 installed. When i try a development build in angular (ionic 3 in my case) everything works. When i try a production build it doesn't work with feathers reactive.

The error happens in "angular-devkit/build-optimizer". If i start a production build without build optimizer it works, too.

The error i'm getting in the production build:

Uncaught Error: Cannot find module 'tslib'
    at o (vendor.js:1)
    at vendor.js:1
    at Object.28.../Observable (vendor.js:1)
    at o (vendor.js:1)
    at vendor.js:1
    at Object.37../FromEventObservable (vendor.js:1)
    at o (vendor.js:1)
    at vendor.js:1
    at Object.181../list (vendor.js:1)
    at o (vendor.js:1)

@j2L4e Have you tested your branch with build optimizing?

previous data lost when patch/remove with listStrategy='smart'

I am not sure if I used it in a wrong way or this behavior is intentional:

app = feathers()
.configure(socketio(io('....'))
.configure(reactive(RxJS, {listStrategy: 'smart'}))

cards = app.service('cards')
cards.find().subscribe(data => console.log('a: ', data))

window.test= => cards.patch('xxx', {text:"new"})

Before running test(), the console shows an array of 10 items fetched from database; but after test() it has only one item left, that is the one been patched. The console looks like:

a: [Object, Object, Object, Object, Object, Object, Object, Object, Object, Object]
run()
a: [Object]

Same problem with update and remove. create is OK.

Is this a bug or I misunderstood what this package is for?

subscribing to query with $populate does not see any updates even with listStrategy=always

Steps to reproduce

e.g.

.find({query: {
   $populate: [
      {path: 'pets'}
   ]
}})
.subscribe(...

Service with listStrategy=always

If I comment out the

Expected behavior

whenever a new document is added (via api), the subscribed query should fire with the updated results.

Actual behavior

Nothing happens. the subscribe does not fire with the updated query.

Deleting a document works fine.

System configuration

Tell us about the applicable parts of your setup.

Module versions (especially the part that's not working):

feathers-2.1.2
feathers-reactive-0.4.1
feathers-mongoose-5.1.0
feathers-socketio-2.0.0

NodeJS version:
v6.10.2

Operating System:
ubuntu 16.04.2 LTS

Browser Version:
Chrome-58.0.3029.96 (64-bit)

React Native Version:
rxjs-5.4.0
Module Loader:
Webpack-2.5.1

Observable.throw() is not subscribable

Steps to reproduce

(First please check that this issue is not already solved as described
here
)

  • [ x] Tell us what broke. The more detailed the better.
  • If you can, please create a simple example that reproduces the issue and link to a gist, jsbin, repo, etc.
service.create(data)
  .catch(err => Observable.throw(err))
  .subscribe(x=>x, err=>err)

Expected behavior

Observable.throw() should be subscribed

Actual behavior

It throws error that subscribe is not a function of Observable.throw()

System configuration

rxjs: 5.2.0
feathers-reactive: 0.4.1

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.