Code Monkey home page Code Monkey logo

simpleddp's Introduction

npm version Build Status Dependency Status devDependency Status

SimpleDDP 🥚

The aim of this library is to simplify the process of working with Meteor.js server over DDP protocol using external JS environments (like Node.js, Cordova, Ionic, ReactNative, etc).

It is battle tested 🏰 in production and ready to use 🔨.

If you like this project ⭐ is always welcome.

Important

SimpleDDP is written in ES6 and uses modern features like promises. Though its precompiled with Babel, your js environment must support ES6 features. So if you are planning to use SimpleDDP be sure that your js environment supports ES6 features or include polyfills yourself (like Babel Polyfill).

Project uses semantic versioning 2.0.0.

DDP (protocol) specification.

Install

npm install simpleddp --save

Plugins

Example

First of all you need WebSocket implementation for your node app. We will use isomorphic-ws package for this since it works on the client and serverside.

npm install isomorphic-ws ws --save

Import/require simpleDDP.

const simpleDDP = require("simpleddp"); // nodejs
const ws = require("isomorphic-ws");

or

import simpleDDP from 'simpleDDP'; // ES6
import ws from 'isomorphic-ws';

Now you should make a new simpleDDP instance.

let opts = {
    endpoint: "ws://someserver.com/websocket",
    SocketConstructor: ws,
    reconnectInterval: 5000
};
const server = new simpleDDP(opts);

Connection is not going to be established immediately after you create a simpleDDP instance. If you need to check your connection simply use server.connected property which is true if you are connected to the server, otherwise it's false.

You can also add some events for connection status.

server.on('connected', () => {
    // do something
});

server.on('disconnected', () => {
    // for example show alert to user
});

server.on('error', (e) => {
    // global errors from server
});

As an alternative you can use a async/await style (or then(...)).

(async ()=>{
  await server.connect();
  // connection is ready here
})();

The next thing we are going to do is subscribing to some publications.

let userSub = server.subscribe("user_pub");
let otherSub = server.subscribe("other_pub",'param1',2); // you can specify arguments for subscription

(async ()=>{
  await userSub.ready();
  let nextSub = server.subscribe("next_pub"); // subscribing after userSub is ready
  await nextSub.ready();
  //all subs are ready here
})();

You can fetch all things you've subscribed for using server.collection method. Also you can get reactive data sources (plain js objects which will be automatically updated if something changes on the server).

(async ()=>{

  // call some method
  await server.call('somemethod');

  let userSub = server.subscribe("user",userId);
  await userSub.ready();

  // get non-reactive user object
  let user = server.collection('users').filter(user=>user.id==userId).fetch()[0];

  // get reactive user object
  let userReactiveCursor = server.collection('users').filter(user=>user.id==userId).reactive().one();
  let userReactiveObject = userReactiveCursor.data();

  // observing the changes
  server.collection('users').filter(user=>user.id==userId).onChange(({prev,next})=>{
    console.log('previus user data',state.prev);
    console.log('next user data',state.next);
  });

  // observing changes in reactive data source
  userReactiveCursor.onChange((newData)=>{
    console.log('new user state', newData);
  });

  let participantsSub = server.subscribe("participants");

  await participantsSub.ready();

  let reactiveCollection = server.collection('participants').reactive();

  // reactive reduce
  let reducedReactive = reactiveCollection.reduce((acc,val,i,arr)=>{
    if (i<arr.length-1)  {
      return acc + val.age;
    } else {
      return (acc + val.age)/arr.length;
    }
  },0);

  // reactive mean age of all participants
  let meanAge = reducedReactive.data();

  // observing changes in reactive data source
  userReactiveCursor.onChange((newData)=>{
    console.log('new user state', newData);
  });
})();

simpleddp's People

Contributors

dependabot[bot] avatar fantostisch avatar gregivy avatar hems 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

simpleddp's Issues

Is there any solutions or workaround to connect with Android?

Description:
If I have a website using your solution, how can I send or receive data on Android?

Expected behavior:
I was hoping that you release a new version for Android.

Observed behavior:
I observed that okHttp and ktor have provide websocket solution but those doesn't support subscribe.

Impact:
High

Environment:
Test

Browser DDP call promise never resolves

Good work on the package and plugins architecture ( :

Currently i tried:

  • login ( worked perfectly )
  • subscriptions ( worked perfectly )

But funny enough, when i tried to do a simple call to some method the returned promise never resolves.

I investigated a bit of the source code ( specifically the apply method ) and turns out:

  • methodId value is 0
  • onMethodResult is never called

Then basically the promise hangs forever and never resolves.

I have some Meteor "native code" using that same Meteor.method and it works normally, so i can confirm it's not a problem on the meteor side.

I'll let you know if I find any other clues about what might be causing that problem.

On change being triggered with remove on start subscription start

Im using simpleddp on a react-native application, I start the subscriptions with an onChange every time the app is opened, i'm having a strange behavior that when I start the subscriptions I receive N onChange events of removed, that are followed by the same N events but for added, so basically its removing and adding the same content every time I open the app, I tried multiple things, such as for example, remove all subs, changeListeners and even calling clearData before starting the subscriptions, but I do still receive the on change events, any idea what might be causing it?

Empty data on fetch, and onChange does not work either

Sorry for bad english.

I am using this package on my meteor app, and not being able to get the result. I have created a react function component and used this lib.

here's the code:

import React, { useState, useEffect } from "react";

const simpleDDP = require("simpleddp");
import ws from 'isomorphic-ws';

export default function TestPage(props) {
    let ddpServer = null;
    let ddpSubs = [];

    useEffect(() => {
        reactiveDataComputation();

        return () => {
            if (ddpServer) {
                ddpServer.disconnect();
                ddpServer = null
            }
            if (ddpSubs && ddpSubs.length > 0) {
                ddpSubs.forEach(s => {
                    s.remove();
                });
                ddpSubs = [];
            }
        };
    });

    const reactiveDataComputation = async () => {
        // removing old ddpServer
        if (ddpServer) {
            ddpServer.disconnect();
            ddpServer = null;
        }
        // removing old ddpSubs
        if (ddpSubs && ddpSubs.length > 0) {
            ddpSubs.forEach(s => {
                s.remove();
            });
            ddpSubs = [];
        }

        let opts = {
            endpoint: 'ws://localhost:4000/websocket',
            SocketConstructor: ws,
            reconnectInterval: 5000
        };

        ddpServer = new simpleDDP(opts);

        (async () => {
            await ddpServer.connect();
            // connection is ready here

            const sub = ddpServer.subscribe("tasks.goalTasks", 848);
            await sub.ready();

            console.log('ready ', sub.isReady());
            if (sub.isReady()) { 
                //all subs are ready here
                console.log(ddpServer.collection('tasks').fetch()) // returns empty array always
                ddpSubs.push(sub);
            }
        })();

        ddpServer.on('connected', () => {
            console.log('connected') // connected

            ddpServer.collection('tasks').onChange((state) => {
                console.log(state) // no logs
            });
        });
    }

    return (
        <div>Test Page</div>
    )
}

here's the pub i am subscribing to:

Meteor.publish('tasks.goalTasks', function (goal_id) {
  return Tasks.find({ goal_id: goal_id });
});

Can anyone help me with this?

is polling (DISABLE_WEBSOCKETS) supported?

Hi,

struggling with React Native and connecting to a specific Meteor server and enabling DDP/Websockets.

Wondering If Websockets are supported wondering and if the fall back to polling is supported?

If there's a better place to post questions, please let me know where?

I have tried to connect the the server but don't seem to be getting any good error message back to help me debug this. The best I got was 400/bad request when using React Native Meteor... so trying to come up with a workaround for that.

Any help appreciated

thanks

How to set header?

Hi,

how to set headers to a simpleddp client like

let opts = { endpoint: "ws://hard.homedoudou.fr/websocket", SocketConstructor: ws, reconnectInterval: 5000, headers: { 'User-Agent': 'nabaztag/0.1' } };

Thanks

Session disconnected and loginSessionLost event is not called

Hello @Gregivy , it happens that I have a monitoring application and I am getting a weird behavior, since devices (users) are disconnected after the 3rd or 5th day. In some cases, they're reconnected automatically (this is good) but in other cases they remain offline.

image

I am using socialize:user-presence package to monitor sessions of the devices. This code let me know if a device is connected or disconnected:

UserPresence.onSessionConnected(function(connection, userId) {
	console.log('user-connected: ', userId, ' with IP address: ', connection.clientAddress, 'and connection id: ', connection.id);
});

UserPresence.onSessionDisconnected(function(connection, userId) {
	console.log('user-disconnected: ', userId, ' with IP address: ', connection.clientAddress, 'and connection id: ', connection.id);
});

Notes:

Server:

Client:

  • Simple DDP Client
  • Asus Tinker Boards (SBC) with Debian OS
  • Node 12/14
  • It is worth mentioning that these devices at the moment do not make calls to endpoints (methods and publications), only the time in which they remain with online status is being measured.

I have tested that bahavior on a Windows machine (as a client) and I got the same issue. When the PC is not used for a while (until it gets suspended), I receive in the server a log of disconnected client while in the client doesn't update the connectivity status.

Front-end of web admin is using Vue since they are real users who operate the system.

Meteor.loggingIn() equivalent

I am testing your package as a replacement for react-native-meteor and was wondering is there is a replacement for the Meteor.loggingIn() method? I used this check in combination with Meteor.user() to know if the user was logged in or not.

if( !Meteor.loggingIn() ) {
   if (Meteor.user()) {
    //user is logged in
  } else {
   //user is a guest and not logged in
  }
}

How can I get the same pattern with your package?

Thanks.

Use it in Meteor create --bare

I just recently read about Meteor create --bare which removes minomongo from Meteor client.
Now I really want to use simpleddp and have it results save on my hook state management library so that I will not depend anymore on minimongo...

Hope you can show me a very simple example or show me to a right directions.

Thanks and great job on simpleddp.

This.userId server side

How do you send the token to the server, so that you can access this.userId in the server

[Feature request] Set custom timeout for an specific server.call()

Hi, it would be great if you can add to the call method an extra parameter to specify the timeout. This help us to override the default timeout for an specific request since it is very used when you have certain meteor methods which take long time (such as download files, send emails, invoke http requests to an api, etc).

Custom Login Handler

Has anyone tried using a custom login handler with the function registerLoginHandler in the server and calling the method with simpleDDP?

I keep getting a 404 method not found error

Handling "connection error"

Currently when the webserver is down for maintenance i get the following error on my Browser console.

socket.js:64 WebSocket connection to 'ws://[...]/websocket' failed: Error during WebSocket handshake: Unexpected response code: 503

Is that a way of gracefully handling connection errors so they don't end up in the Browser Console and the application can deal with them programmatically, for instance showing an UI element.

what about the performance of the collection if data became large?

@Gregivy This is the best DDP client I have see so far!

I just noticed that it support client collection. I think you should emphasize this feature in the git topic. :)

I have had a glance of the source code, I think it just store data in memory, right? so What's the performance if the data became large?

why not use minimongo like meteor does?

Regards

Redux-Saga

What's the best way to integrate subscriptions and methods with Redux-Saga ?

WebpackError: Unsafe builtin usage https.get

We're using simple-ddp in a webpack app, and we started seeing this warning from webpack:

warn WebpackError: Unsafe builtin usage https.get
    at website/node_modules/ws/lib/websocket.js:566:1
    at website/node_modules/ws/lib/websocket.js:71:1
    at website/node_modules/simpleddp-core/lib/socket.js:64:1
    at website/node_modules/simpleddp-core/lib/ddp.js:134:1
    at website/node_modules/simpleddp-core/lib/ddp.js:124:1
    at website/node_modules/simpleddp/lib/simpleddp.js:68:1

Not sure if something even needs to be done though.

ddpCollection.reactive() doesn't accept { sort }

Hello, and thanks for a great library!

Although the documentation implies that the following should be possible:

server.collection('widgets').reactive({ sort: mySortFunc })

It appears that the sort function is being ignored. After investigating and running some traces on the code, it looks like it's not being set at all. The constructor of ddpReactiveCollection doesn't process the sort property.

This isn't a major problem since I can rewrite the code above as follows:

server.collection('widgets').reactive().sort(mySortFunc)

...and it works as expected. But either the documentation should probably be updated, or the code fixed to work as documented.

Struggling to get data from ddp endpoint

Hi There,

I am struggling to get data from a ddp enpoint. I have had success with other ddp clients but I would like to use this one as it seems to be more active.

Given the following code.

const simpleDDP = require("simpleddp"); // nodejs
const ws = require("isomorphic-ws");

let opts = {
  endpoint: "wss://livetiming.alkamelsystems.com/websocket",
  SocketConstructor: ws,
};

const server = new simpleDDP(opts);

(async ()=>{
  let livetimingSub = server.subscribe("livetimingFeed", "imsa");
  await livetimingSub.ready();
  let data = server.collection('feeds').fetch()
  console.log(data)
})()

The resultant data variable is an empty array, I can however see that the feeds collection is returning data, I just cannot seem to access using the package.

image

Kind Regards
Ryan

Why isomorphic-ws?

Why not use the regular ws package instead?
I can't seem to find out what the isomorphic package does differently anywhere, and you use the regular ws in all of the tests. Does it matter what I use? And if not can I use other socket interfaces like uWebSocket?

Use simple-DDP in the browser

I'm planning to use this library in the browser, through a Gatsby website.

Do you still need a SocketConstructor, like isomorphic-ws? And if so, any light-weight library that would be optimal for that use case?

server.on not working

Hi @Gregivy , great work in this package, thank you.

I have this setup:

// meteor.js
import SimpleDdp from 'simpleddp';
import ws from 'isomorphic-ws';

const {simpleDDPLogin} = require('simpleddp-plugin-login');
const SERVER_URL = 'wss://xxx.sssssss.com/websocket';
const opts = {
  SocketConstructor: ws,
  autoReconnect: true,
  endpoint: SERVER_URL,
  reconnectInterval: 5000,
};

const server = new SimpleDdp(opts,[simpleDDPLogin]);

export default {
  SERVER_URL,
  SERVER_URL_HTTP: SERVER_URL
    .replace('ws://','http://')
    .replace('wss://','https://')
    .replace('/websocket',''),
  server,
};

and invoke the server on the main component like below. Everything works as expected, except the "server.on" events.

import simpleddp from "simpleddp";
import {config} from './meteor'

class App extends Component<IProps,IState> {
  private server: simpleddp;  

  constructor(props) {
    super(props);
    this.server = config.server;
  }

  public async componentDidMount()  {
    this.server.connect().then(() => {
        console.log('this gets logged!');
    }

    this.server.on('connected',()=>{
      console.log('this does not get logged');
    });

  }
  (...)
}

I can't get any of the "server.on" events to work. Am I doing something wrong? Cheers

[React Native] Cannot read property 'findIndex' of undefined

Hello!

I'm trying to migrate my react native app from react-native-meteor to this library. Calling meteor methods via call works fine. Unfortunately I cannot subscribe to collection changes.

Every time collection changes I'm receiving an error:

Simulator Screen Shot - iPhone X - 2019-04-25 at 15 08 18

Subscription:

    let opts = {
       endpoint: props.url,
       SocketConstructor: WebSocket,
       reconnectInterval: props.reconnectInterval
    }
    this.meteorClient = new SimpleDDP(opts)
    let sub = this.meteorClient.sub('stream-room-messages', argsArr)

    sub.ready().then(() => {
      console.log(`Subscribed to ${eventName}`)
      sub.start()
      console.log(`${eventName} subscription is ON: ${sub.isOn()}`)
    })

Getting collection:

    this.meteorClient.collection('stream-room-messages').reactive()

    subReactiveCursor.onChange(newData => {
      console.log(`new ${collectionName} data`, newData)
    })

   subReactiveCursor.data()

BTW onChange never called.

Reactive subscribe args?

I got an issue with using reactive args on subscribe, for now I restart subscribe and set new args .

computed: {
    employeeId() {
      return this.$store.state.app.employeeId
    },
    watch: {
      employeeId(id) {
        this.onSaleSub(id)
      }
    },
    created() {
      // Subscribe to user
      this.saleSub = this.$server.subscribe('sales', null)
    },
    methods: {
      async onSaleSub(employeeId) {
        // Restart sub & set new args
        await this.saleSub.restart([{ employeeId }])
        // Wait till sub ready
        await this.saleSub.ready()
        // Find data cursor
        const dataCursor = this.$server.collection('app_sales').reactive()
        // On data cursor changing
        dataCursor.onChange((data) => {
          // Run multiple time x
          //  How can I make it run once whenever data change ?
          console.log('user data change: ', data)
        })

        console.log('subscriptionId :', this.saleSub.subscriptionId)
        // subscriptionId  2 , 4, 8, ... always change every time sub restart 
      }
    }

How can I update subscribe args correctly ?

Handle Offline or Inaccessible Server

My issue is similar to #18 and #21

I have a react-native app and I connect to my development server like so:

let options = {
  endpoint: 'ws://<server address>:3000/websocket',
  SocketConstructor: ws,
  reconnectInterval: 5000,
  autoConnect: false,
  autoReconnect: false,
  maxTimeout: 3000
};

export const server = new simpleddp(options, [simpleDDPLogin]);
import { server } from 'some file'

connectToServer = () => {
  server
    .connect()
    .then(() => {
      console.log('server connected');  //never gets called
    })
    .catch(er => console.log('connection error', er));  //never gets called
}

If the Meteor server is running, everything works. If I stop the server, and attempt to load the App, the console.log() statements never get called.

I also tied the following:

connectToServer = async () => {
  console.log('connect to server next');
  try {
  await server.connect()
  console.log('CONNECTED?', server.connected); //never gets called
  } catch (error) {
     console.log('connection error:  ', error); //never gets called
   }
};

// some place else
connectToServer();

None of the server.on('error', ...) events are called as well.

How can I check if the server is available and connected?

Thanks.

uncaught error when "nosub" message is received

Today I noticed this error when trying to run a page which needs user login and for some weird reason it's getting the nosub message.

this is yielding the following error:
image

When investigating further ( i added a breakpoint on google chrome ) turns out that the "m" argument don't have an ".error" property when getting the "nosub" message:
image

I believe the error comes from "await ...ready()":

https://github.com/Gregivy/simpleddp/blob/master/src/classes/ddpSubscription.js#L114

I'm running meteor 1.10.1

server.on('event')

Hi, me again...

i found this for {msg: 'result'}

server.on('result', (m) => {
  console.log(m);
});
//{ msg: 'result', id: '0' }

but is there a more global function for all events i could use this way?

server.on('event', (e) => {
  console.log(e);
  if(e.msg === 'command'){
     console.log(e.job)
  }
});
//{ msg: 'command', job: 'turnOnLight' }

excuse me if it's not the right way to do it

otherwise I can communicate with "result" but it's dirty ...

thanks :)

Receiving Empty Date Objects in Collections

Hello,

Thanks for the great library. I have recently started using it in my Meteor/Vue and I see a weird issue. For some reason all the date fields in collection objects like createdAt, updatedAt are being received as empty objects although they are present in the database/collection in the Meteor Server.

My code to access them is shown below

let self = this
//Subscribe
let connectsSub = this.api.sub("project-connects", this.$store.getters['project/getCurrentProjectId'], this.subsOptions])
connectsSub.ready().then(function () {
        //Get Reactive Collection
	let reactiveConnectCollection = self.api.collection('list_users').reactive();
	let connects = reactiveConnectCollection.data()
	lodash.each(connects, (con) => {
		// THIS IS ALWAYS EMPTY
		console.log(con.createdAt)
	})
	self.rows = connects
})

Please let me know what I am doing wrong here?

Regards

isReady() returns false right after "await ready()" promise

On my current test i have a simple publication which publishes 3 notifications

// server pub
Meteor.publish('unread.notifications', () => {
   return Notifications.find({})
}

And that subscription on the client

// client
  const notificationsUnreadSub = server.subscribe("notifications.unread");
  await notificationsUnreadSub.ready();

  const notificationsCursor = server.collection('notifications')
    .reactive()
    
  console.log("is ready ->", notificationsUnreadSub.isReady())
  console.log("data ->", notificationsCursor.data())

And this is my console output:

is ready -> false
unreadNotifications.js:14 data -> (3) [{…}, {…}, {…}]

The unexpected thing is that my console log says "is ready: false" right after the subscription but my ".data()" call returns the only 3 subscriptions that exist.

Is there a reason for the "isReady()" to not be true right after waiting for the promise?

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.