7.0.0 • Published 4 years ago

extended-ds-client v7.0.0

Weekly downloads
6
License
MIT
Repository
github
Last release
4 years ago

Extended Deepstream client

Promise based deepstream client. It's basically the deepstream.io-client-js with basic calls promisified, plus some extra methods. All methods are added as polyfills.

Install

npm i -S extended-ds-client

Overview

Creating a client through this package will give you additional methods on the client object, leaving everything from the default client untouched (getRecord, getList etc).

These are the additional functions:

In case of rejection on any of these functions, the rejected argument is always an instance of Error.

There is also a utility function to import from this module:

Tunneling export of CONSTANTS & MERGE_STRATEGIES (so that you don't also have to import deepstream.io-client-js for these).

Example 1

import getClient from 'extended-ds-client';

const client = getClient('localhost:6020');

client.p
  .login({})
  .then(data => {
    console.log('Successful login.', data);
  })
  .catch(data => {
    console.log('Login failed.', data);
  });

const users = {};
client.record.p.getList('users').then(list => {
  // With the list of entries, map each entry to a record promise and
  // wait for all to get finished:
  Promise.all(list.getEntries().map(path => client.record.p.getRecord(path))).then(records =>
    records.forEach(record => {
      // Now save all data etc
      const user = record.get();
      users[user.id] = user;
    }),
  );
});

// Default functions still work
client.record.getList('article/x35b/comments').whenReady(l => {
  // ...
});

Example 2

import getClient, { CONSTANTS, ds } from 'extended-ds-client';

ds.client = getClient('localhost:6020'); // use singleton feature

ds.client.loginP({}); // using alias pattern (instead of p.login)

console.log(CONSTANTS); // MERGE_STRATEGIES is also available as import

Example 3

// Given Example 2 file is imported together with this one

import { ds } from 'extended-ds-client';

// And why not use async-await now...

async function fetchUsers() {
  const l = await ds.client.record.p.getExistingList('users');
  const records = await Promise.all(
    l.getEntries().map(path => ds.client.record.p.getExistingRecord(path)),
  );
  const users = {};
  records.forEach(r => {
    const user = r.get();
    users[user.id] = user;
  });
  return users;
}

getClient

Default export from this module is the function getClient to create a client (just like deepstream() in original client). In the same way it also accepts an options object.

const client = getClient('localhost:6020', {
  datasetRecordFullPaths: false,
  datasetRecordIdKey: 'rid',
  splitChar: '.',
});
OptionTypeDefaultDescription
datasetRecordFullPathsbooleantrueFull paths in lists (or only id) for datasetRecord methods.
datasetRecordIdKeystring'id'ID key for dataset records.
splitCharstring'/'Splitting character in paths (for dataset records).

Promisification API

The promises will be resolved with the same argument(s) as the default client callbacks would get (or the whenReady when applicable). See the deepstream JS client documentation.

p.login

Alias: loginP

Straightforward promisification of login. See Example 1 above.

Note: Default login still works, you can still do the standard line with callback: client.login({}, success => console.log(success));

record.p.getRecord

Alias: record.getRecordP

Promisification of record.getRecord.

client.record.p.getRecord(name)
  .then(dsRecord => ...)
  .catch(error => ...);

record.p.getList

Alias: record.getListP

Promisification of record.getList.

client.record.p.getList(name)
  .then(dsList => ...)
  .catch(error => ...);

record.p.setData

Alias: record.setDataP

Promisification of record.setData.

client.record.p.setData(name, path, data)
  .then(() => ...)
  .catch(error => ...);

record.p.snapshot

Alias: record.snapshotP

Promisification of record.snapshot.

client.record.p.snapshot(name)
  .then(record => ...)
  .catch(error => ...);

record.p.has

Alias: record.hasP

Promisification of record.has, but will reject if it does not exist (and on error of course).

A second optional argument makes the check inverted -- rejecting on existing record. (This can be handy to make sure an id is free).

client.record.p.has(name)
  .then(() => ...)
  .catch(error => ...);

await client.record.p.has('record1');
// if we get here, record1 is a non-taken record id

rpc.p.make

Alias: rpc.makeP

Promisification of rpc.make.

client.rpc.p.make(name, data)
  .then(result => ...)
  .catch(error => ...);

Additional API functions

record.p.getExistingRecord

Alias: record.getExistingRecordP

Additional method that does a .has-check before .getRecord to get a record handler without implicit record creation (Compare with snapshot that also fails if the record does not exist, but returns the actual record instead of a record handler). It rejects the promise if the record does not exist.

client.record.p.getExistingRecord(name)
  .then(dsRecord => ...)
  .catch(error => ...);

record.p.getExistingList

Alias: record.getExistingListP

Like p.getExistingRecord above, but for List.

client.record.p.getExistingList(name)
  .then(dsList => ...)
  .catch(error => ...);

record.p.deleteRecord

Alias: record.deleteRecordP

Will resolve when the delete event is emitted (avoiding the race condition risk).

client.record.p.deleteRecord(arg)
  .then(() => ...)
  .catch(error => ...);
ArgumentTypeDefaultDescription
argstring/ObjectThe path to the record OR a DS Record object.

record.p.deleteList

Alias: record.deleteListP

Like p.deleteRecord above, but for List.

client.record.p.deleteList(arg)
  .then(() => ...)
  .catch(error => ...);
ArgumentTypeDefaultDescription
argstring/ObjectThe path to the list OR a DS List object.

record.p.addToList

Alias: record.addToListP

Add an entry or multiple entries to an existing list, without duplicates.

client.record.p.addToList('books', 'selfish-gene')
  .then(dsList => ...)
  .catch(error => ...);
ArgumentTypeDefaultDescription
listPathstringThe name/path of the record.
idstring/ArrayEntry(ies) to add.

record.p.removeFromList

Alias: record.removeFromListP

Remove an entry or multiple entries from an existing list.

client.record.p.removeFromList('books', 'selfish-gene')
  .then(dsList => ...)
  .catch(error => ...);
ArgumentTypeDefaultDescription
listPathstringThe name/path of the record.
idstring/ArrayEntry(ies) to add.

record.p.updateExistingRecord

Alias: record.updateExistingRecordP

Update a record, choosing from one of multiple update modes:

  • shallow: Default mode. Overwrite each property at the base level.
  • overwrite: Replace the whole record.
  • deep: Deep merge of the updates into record (using lodash.merge).
  • deepConcat: Deep merge, but concatenate arrays with only simple values.
  • deepConcatAll: Merge like deepConcat, but concatenate ALL arrays.
  • deepIgnore: Deep merge, but leave unchanged if some value in updates is '%IGNORE%'.
  • deepConcatIgnore: Merge like deepConcat, but skip values like deepIgnore.
  • removeKeys: With updates as an array, remove corresponding keys in record.

Two additional array arguments, lockedKeys and protectedKeys, makes it possible to lock or protect given keys, regardless of update mode. Locked keys won't be altered and protected ones won't be removed.

client.record.p.updateExistingRecord('record1', { a: 1, data: { b: 2 }})
  .then(() => ...)
  .catch(error => ...);

client.record.p.updateExistingRecord(
  'records/r-x',
  { data: { configs: [{ items: ['toBeConcatenated'] }] } },
  'deepConcat',
)
  .then(() => ...)
  .catch(error => ...);

client.record.p.updateExistingRecord(
  'record1',
  { data: { confs: ['%IGNORE%', { items: ['replacingFirstItem'] }] }, id: 'xb24' },
  'deepIgnore',
  ['id']
)
  .then(() => ...)
  .catch(error => ...);
ArgumentTypeDefaultDescription
namestringThe name/path of the record.
updatesObject/ArrayThe updates. Array for mode = removeKeys
modestring"shallow"Update mode, see details above.
lockedKeysArrayKeys which values can't be altered.
protectedKeysArrayKeys which can't be removed.

record.p.getDatasetRecord

Alias: record.getDatasetRecordP

In case you often end up with the structure of having a list of some type of records as the "parent" of those records. For example a list of all bookings at bookings and the bookings at bookings/room-19, bookings/room-27 etc.

On resolve you get back both the deepstream list handle and record handle.

The options described in getClient above will influence how this function operates.

client.record.p.getDatasetRecord('bookings', 'room-27').then(([dsList, dsRecord]) => {
  const booking = dsRecord.get();
  console.log(dsList.getEntries());
  console.log(booking.time, '-', booking.customer);
});
ArgumentTypeDefaultDescription
listPathstringThe path to the list.
recordIdstringThe ID of the record.
initiationObjectIf created, initiate record with this.

record.p.deleteDatasetRecord

Alias: record.deleteDatasetRecordP

Removes both a record and its entry in the list, as created with getDatasetRecord.

client.record.p.deleteDatasetRecord('bookings', 'room-27').then(dsList => {
  console.log('List of records after delete:', dsList.getEntries());
});
ArgumentTypeDefaultDescription
listPathstringThe path to the list.
recordIdstringThe ID of the record.

Utility functions

These are not extensions of the client object, but freely importable functions.

addEntry

An alternative way to add entries to a deepstream list, that prevents duplicates.

See also above method record.p.addToList that utilizes this one.

import { addEntry } from 'extended-ds-client';

client.record.p.getExistingList('books')
  .then(dsList => addEntry(dsList, 'selfish-gene'));
  .catch(error => ...);
ArgumentTypeDefaultDescription
listObjectA DS List object.
strstringThe entry to add.

Licence

MIT

Change log

6.0

  • Removed \*ListedRecord in favor of new getDatasetRecord & deleteDatasetRecord
  • Name changes of options
    • listedRecordFullPaths -> datasetRecordFullPaths
    • listedRecordIdKey -> datasetRecordIdKey
  • Added a very versatile updateExistingRecord method
  • record.p.has now rejects on non-existent record

5.0

  • Added getListedRecord that returns list & record handles
    • Will create both list & record if non-existent
    • Consistent with original getList/getRecord
  • setListedRecord now only returns the record id
  • Added method deleteListedRecord
  • addToList & removeFromList now also accepts multiple entries
  • Options added that controls how *listedRecord operates
    • datasetRecordFullPaths
    • datasetRecordIdKey
    • splitChar

4.0

  • New primary naming / method access, using p as scope
    • Keeping old naming as aliases
  • Full test coverage
  • Much smaller footprint in node_modules
  • Dropping unofficial tenant extension
  • Dropping deprecated methods
  • Improved documentation with more examples/code

3.0

  • Methods added as polyfills
  • New method addToList
  • Re-exporting of deepstream client constants

Please create an Issue in github if you feel something is missing!

7.0.0

4 years ago

6.1.6

6 years ago

6.1.5

6 years ago

6.1.4

6 years ago

6.1.3

6 years ago

6.1.2

6 years ago

6.1.1

6 years ago

6.1.0

6 years ago

6.0.0

6 years ago

5.1.5

6 years ago

5.1.4

7 years ago

5.1.3

7 years ago

5.1.2

7 years ago

5.1.1

7 years ago

5.1.0

7 years ago

5.0.0

7 years ago

4.0.1

7 years ago

4.0.0

7 years ago

3.0.0

7 years ago

2.2.4

7 years ago

2.2.3

7 years ago

2.2.2

7 years ago

2.2.1

7 years ago

2.2.0

7 years ago

2.1.8

7 years ago

2.1.6

7 years ago

2.1.5

7 years ago

2.1.4

7 years ago

2.1.3

7 years ago

2.1.2

7 years ago

2.1.1

7 years ago

2.1.0

7 years ago

2.0.8

7 years ago

2.0.7

7 years ago

2.0.6

7 years ago

2.0.5

7 years ago

2.0.4

7 years ago

2.0.3

7 years ago

2.0.2

7 years ago

2.0.1

7 years ago

2.0.0

7 years ago

1.0.10

7 years ago

1.0.9

7 years ago

1.0.8

7 years ago

1.0.7

7 years ago

1.0.6

7 years ago

1.0.5

7 years ago

1.0.4

7 years ago

1.0.3

7 years ago

1.0.2

7 years ago

1.0.0

7 years ago

0.1.0

7 years ago