npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@giosg/realtime-sdk

v2.5.0

Published

SDK for streaming Giosg resources in real-time

Downloads

316

Readme

RealtimeSDK

RealtimeSDK is a JavaScript library, written in TypeScript, that combines the new giosg v5 API endpoints and the channel broadcasting system together. It allows you to observe RESTful resources and resource collections, automatically updating their state in real-time. The observing is done with Observables from RxJS library.

Architecture of Realtime SDK

Core features

  • Handles all the state for remote resources and collections! UI components can directly just subscribe resources they are interested in.
  • Directly maps to the RESTful API (v5)! Everything available from the v5 APIs can be used!
  • Synchronizes trustfully real-time changes: 90% of all RESTful resources and collections support real-time changes out-of-the-box. RealtimeSDK handles all the WebSocket connections and handling of the real-time messaging. It also handles many possible race conditions!
  • Optimized to avoid unnecessary network traffic by caching the resource states! If multiple UI components are interested in the same resources, they are only fetched once!
  • Memory-efficient: only keeps in memory what is subscribed by UI components. Once completely unsubscribed, the resources are forgotten from the memory.
  • Error recovery: ensures that everything is kept up-to-date even after a network issue! Recoverable errors are not even exposed to UI components.
  • Resource manipulation synchronization: any creations, updates, and deletions are automatically reflected to the UI!
  • Optimistic updates: for the best user experience, update and delete actions are reflected to the UI immediately, before the operation is ready! Only after something goes wrong, the state is rolled back and an error is triggered.

Authentication

RealtimeSDK manages authentication of the user or visitor that is currently using the web app. The recommended way to authorize users is to use OpenID Connect (OAuth 2) authentication. Alternatively, for visitors, there is an AJAX-based authentication method (which will be replaced with a better method in the future).

Authentication with OpenID Connect (OAuth 2)

To get the RealtimeSDK to work on a web app you need to import the library, instantiate the required objects, and connect them to an OpenID Connect compatible authorization endpoint.

import { RealtimeSDK } from '@giosg/realtime-sdk';
import { OpenIDAuthorizer } from '@giosg/realtime-sdk';

// Instantiate the SDK
const sdk = new RealtimeSDK();
// Set up the authentication instance depending on the desired method
const authorizer = new OpenIdAuthorizer({
    // The OAuth authorization endpoint URL
    authUrl: 'https://service.giosg.com/identity/authorize',
    // The `client_id` as specified in OAuth
    clientId: 'chats.giosg.com',
    // Prefix for the keys when storing authentication infor to session storage
    storagePrefix: 'giosg-auth',
    // Scopes required by the app, as specified in OAuth
    scopes: [
        'openid',
        'profile',
        'email',
        /* and then any number of following: 'settings', 'reports', 'users' */
    ],
    // Wait at least half of the expiration time of the token before renewal
    minRenewalFactor: 0.5,
    // Wait at most 3/4 of the expiration time of the token before renewal
    maxRenewalFactor: 0.75,
});
// When your app starts, call this to start the authentication process and socket connections
sdk.connect(authorizer);

You can immediately start subscribing resources and collections after the SDK has been instantiated. HTTP requests and socket connections are only done after connect has been called!

When running unit tests, you should probably call setAuthentication method instead of connect. See "Authentication in tests" section for more details.

Authentication with AJAX

import { RealtimeSDK } from '@giosg/realtime-sdk';
import { AjaxAuthorizer } from '@giosg/realtime-sdk';

// Instantiate the SDK
const sdk = new RealtimeSDK();
// The ID of the organization, who owns this app and the visitor data
const orgId = 'ebd8dc9d-483c-49ad-9bb8-4a80f3e2c281'
// Set up the authentication for the SDK using a *deprecated* AJAX API endpoint
const authorizer = new AjaxAuthorizer(`/api/v5/public/orgs/${orgId}/auth`, {
    visitor_global_id: localStorage.getItem('visitor_global_id'),
    visitor_secret_id: localStorage.getItem('visitor_secret_id'),
});
// When your app starts, call this to start the authentication process and socket connections
sdk.connect(authorizer);

For now, there is an alternative way of authenticating the user with an AJAX call to a deprecated API endpoint. Currently, this is the only way to authenticate visitors. Do not use this method for users but use OpenID Connect (OAuth2) instead! Also note that this method only works on service.giosg.com domain!

  • When authenticating a user with cookies, you would like to use an endpoint /api/v5/auth. This is DEPRECATED in favor of OpenID Connect!
  • When authenticating a visitor, you would like to use an endpoint /api/v5/public/orgs/<organization_id>/auth. You should provide an object with the second parameter containing the visitor_global_id ("gid") and visitor_secret_id ("sgid") that are stored locally.

Observing the authentication

You usually want to do something with the "currently authenticated user". The auth method returns an Observable that emits the latest authentication information of the user.

sdk.auth().subscribe(auth => {
    console.log(`Currently authenticated with user ${auth.user_id}`);
    console.log(`The user belongs to organization ${auth.organization_id}`);
});

The Observable won't emit the authentication information until the user is successfully authenticated. The auth parameter is an object having the following attributes, based on the information received from the authorization endpoint (e.g. OAuth2):

Attribute | Description ------------------|--------------- organization_id | The UUID of the organization to which the authentication is related. For authenticated users, this is the organization of the user. For visitors, this is the organiztion to which the visitor ID (CID) relates to. user_id | If the authentication is for an authenticated user account, then is will be the UUID of the user visitor_id | If the authentication is for an anonymous visitor, then this will be the ID (CID) of the visitor expires_at | The ISO formatted date/time when the authentication will expire. SDK will attempt to renew the access token before it expires access_token | The access token string that SDK will use internally for all the HTTP requests (with Authorization: Bearer <token> HTTP header) and WebSocket connection (as token URL parameter)

See also the example how to stream resources of the authenticated user

Resources

Observe resources

The most important feature of the RealtimeSDK is that you can load the immediate state and then all the future updates for one specific resource. The stream completes only if the resource ever gets deleted.

// Start listening changes to a specific resource
sdk.streamResource('/api/v5/orgs/c0c329ce-bd7f-41e3-b79b-af2f7f9f334b')
    .subscribe(organization => {
        // Emits the resource immediately when loaded, and later
        // all the changes to the resource.
    })
;

If connection to the server breaks, the SDK will automatically reload the resource when the connection is re-established, and any missed updates will be emitted!

The stream will result in an error if the immediate load of the resource fails. However, after that the SDK will recover from connection problems automatically, reloading and emitting the resource if necessary.

Stop observing resources

SDK will automatically handle real-time channel listening under-the-hood. The client monitors changes to the resource as long as the user of SDK is being subscribed to a resource stream. Therefore, you should unsubscribe when you do not need it any more, in order to prevent unnecessary network traffic and memory load.

// Start listening changes to the resource
let resourceStream = sdk.streamResource('/api/v5/orgs/c0c329ce-bd7f-41e3-b79b-af2f7f9f334b');
let subscription = resourceStream.subscribe(organization => { /* ... */ });
// You should cancel the subscription when you are not interested in the changes any more
subscription.unsubscribe();

Observe resources of the authenticated user

In many cases you may want to use the information of the authenticated user to determine which resources are observed. A common recipe is to use the observable returned by the auth method and Observable.switchMap.

// Observe the details of the current user
sdk.auth()
    .map(auth => auth.user_id)
    .distinctUntilChanged()
    .switchMap(userId => sdk.streamResource('/api/v5/users/' + userId))
    .subscribe(user => {
        console.log("My name is " + user.full_name);
    })
;
// Observe the details of the current user's organization
sdk.auth()
    .map(auth => auth.organization_id)
    .distinctUntilChanged()
    .switchMap(orgId => sdk.streamResource('/api/v5/orgs/' + orgId))
    .subscribe(organization => {
        console.log("My organization's name is " + organization.name);
    })
;

However, there is a shortcut helper method for this named switchUser:

// Observe the details of the current user
sdk.switchUser(userId => sdk.streamResource('/api/v5/users/' + userId))
    .subscribe(user => {
        console.log("My name is " + user.full_name);
    })
;
// Observe the details of the current user's organization
sdk.switchUser((userId, orgId) => sdk.streamResource('/api/v5/orgs/' + orgId))
    .subscribe(organization => {
        console.log("My organization's name is " + organization.name);
    })
;

Using custom channel when streaming resources

In advanced use, if you want to optimize the number of channel subscriptions when streaming idividual resources, you may additionally define a custom channel from which the resource updates are listened. If not defined, this defaults to the resource URL:

// Start listening changes to a specific resource, using a channel of the parent collection
sdk.streamResource('/api/v5/users/xxxxx/chats/yyyyy', { channel: '/api/v5/users/xxxxx/chats' })
    .subscribe(chat => {
        // ...
    })
;

Load resource without observing changes

In some cases you may be interested in just the current state of the resource but not any future changes. You can do this by calling getResource method. This just makes a GET request, without listening real-time changes.

Note that this still returns an Observable that needs to be subscribed.

// Start listening changes to a specific resource
sdk.getResource('/api/v5/orgs/c0c329ce-bd7f-41e3-b79b-af2f7f9f334b')
    .subscribe(organization => {
        // Emits the resource's latest state and then completes.
        // Does not emit any further changes to the resource.
    }, error => {
        // Failed to load the resource.
    })
;

This is similar than calling Observable.first method of the Observable returned by streamResource, but it is more efficient, as it does not use real-time socket connection at all.

In this case, the subscription does not need to be manually unsubscribed, unless you want to abort the request.

Create a new resource

You can get an observable for creating new resource by calling postResource method. By subscribing to it, the resource will be POSTed to the collection. The Observable emits exactly one value: the created resource object. It then completes. In other words, this does not subscribe to any future changes of the resource.

// Create a new resource by posting to a collection
let userData = {
    first_name: "John",
    last_name: "Smith",
    /* etc... */
};
sdk.postResource('/api/v5/orgs/c0c329ce-bd7f-41e3-b79b-af2f7f9f334b/users', userData)
    .subscribe(createdUser => {
        // Emits the resource when created successfully. It then completes.
    }, error => {
        // The creation failed!
    })
;

If the creation fails, the observable results in an error.

NOTE: The HTTP request starts only after the observable is actually subscribed. This is because of the anatomy of an observable. In other words, to create a resource, even if you are not interested in the results, you need to call sdk.postResource(...).subscribe();.

Also, by default it makes a new request for each subscriber. If this behavior is not desired, you can multicast the observable, e.g by calling Observable.share.

Also note that the SDK will automatically "add" the newly created resource to the collection (see below) it was posted! Therefore, if you are streaming the collection to which te resource was POSTed, then SDK will automatically ensure that the resource is added to the collection (and therefore e.g. shown in the UI).

Update a resource

You can update an existing resource by calling putResource for a full update or patchResource for a partial update. Similar to postResource, this only emits the updated resource and then completes.

// Update a new resource by putting
let updatedUserData = {
    first_name: "John",
    last_name: "Doe",
    /* etc... */
};
sdk.putResource('/api/v5/orgs/c0c329ce-bd7f-41e3-b79b-af2f7f9f334b/users/6437bd00-5c92-4310-b4b6-1cedb35aa3c9', updatedUserData)
    .subscribe(updatedUser => {
        // Emits the resource when updated successfully. It then completes.
    }, error => {
        // The update failed!
    })
;

NOTE: As with postResource, these observables needs to be subscribed in order to start the HTTP request, and each subscription results in a new request. See the notes above.

Destroy a resource

You can destroy an existing resource by calling deleteResource for a resource endpoint. Unlike other methods, this only emits a single undefined value when successful. It does not emit any changes (because the resource does not exist any more).

// Destroy the resource
sdk.deleteResource('/api/v5/orgs/c0c329ce-bd7f-41e3-b79b-af2f7f9f334b/users/6437bd00-5c92-4310-b4b6-1cedb35aa3c9')
    .subscribe(() => {
        // Destroyed successfully!
    }, error => {
        // The destroying failed!
    })
;

NOTE: As with postResource, this observable needs to be subscribed in order to start the HTTP request, and each subscription results in a new request. See the notes above.

Collections

Observe collections

Another core feature of the Giosg SDK is that it can be used to observe collections of resources and all the changes made to it in real-time.

The simplest way to observe a collection of resources is to stream arrays of resources.

// Start listening changes to the collection
sdk.streamCollectionArray('/api/v5/orgs/c0c329ce-bd7f-41e3-b79b-af2f7f9f334b/users')
    .subscribe(users => {
        // Emits the array of ALL the resources in the collection, and then
        // all the changed arrays.
    })
;

However, you should only use streamCollectionArray method if you know that your collection is relatively small by its size. This is because all the pagination chunks needs to be loaded from the server before the complete array can be built. For large collections, let's say, more than 100 items, this can be a performance issue, and you should use the streamCollection method instead, descibed below.

Observe collection resource streams

For large arrays, it is strongly recommended to use lower-level streamCollection method instead. Instead of arrays, it emits higher-order observables, that is, "observables of observables". The emitted "inner" observables emits all the resources in the collection, in order.

// Start listening changes to the collection
sdk.streamCollection('/api/v5/orgs/c0c329ce-bd7f-41e3-b79b-af2f7f9f334b/users')
    .subscribe(userStream => {
        // Emits an Observable for the users in the collection, according to their current
        // state in the collection. If the contents of the collection is changed, then
        // another Observable is emitted.
    })
;

Here's an example, how to observe max. 10 manager users, using an endpoint that lists all the users.

// Observe the first 10 manager users from the users collection
sdk.streamCollection('/api/v5/orgs/c0c329ce-bd7f-41e3-b79b-af2f7f9f334b/users')
    .switchMap(userStream => {
        // Take the 10 first resources matching the giving criteria, ignoring the rest
        return userStream
            // Include only if the user is a manager
            .filter(user => user.is_manager)
            // Only max. 10 managers.
            // SDK only loads the minimum number of required page chunks from the server.
            .take(10)
            // Emit as an array
            .toArray()
        ;
    })
    .subscribe(firstManagers => {
        // Emits the first 10 manager users as an array, and then
        // a new array whenever the collection changes.
    })
;

The collection pagination chunks are loaded lazily on-demand. That is, the pages are only actually loaded from the server if there are any subscribers interested in their contents! For example, you can use Observable.take to take only the given number of first resources from the collection. The latter pages are not loaded from the server at all, making the operation fast even for very large collections.

Please be careful when working with higher-order observables. It is likely that the underlying collection sometimes changes before a "inner" Observable is completed, making it obsolete. For example, a resource is added to the collection before all the page chunks are loaded from the server. Luckily, Observable has handly methods to manage inner observables! For example, you can use Observable.switchMap which is useful to only use the latest emitted inner stream of resources.

Stop observing collection changes

As with any stream returned by the SDK, you should unsubscribe your subscription when you are not interest in the changes any more!

// Start listening changes to the collection
let collectionStream = sdk.streamCollection('/api/v5/orgs/c0c329ce-bd7f-41e3-b79b-af2f7f9f334b/users');
// Subscribe to the stream
let subscription = collectionStream.subscribe(userStream => { /* ... */ });
// You should cancel the subscription when you are not interested in the changes any more
subscription.unsubscribe();

Load a collection without observing changes

// Returns an observable for the current contents of the collection
sdk.getCollection('/api/v5/orgs/c0c329ce-bd7f-41e3-b79b-af2f7f9f334b/users')
    .subscribe(user => {
        // Calls this for EACH RESOURCE in the collection and then completes!
    }, error => {
        // Loading one of the collection page loads failed!
    })
;

Sometimes you just want to get all the resources in the collection without observing the changes to the content. In this case you can use getCollection method that just retrieves the current resources in the collection as a single observable. It emits the items until the end of the collection is reached, and then completes.

The main difference to earlier methods is that the observable emits each resource from the current state of the collection. This is roughly equivalent to: sdk.streamCollection(...).first().mergeAll() but is more efficient, because it does not use the real-time socket connection at all.

Ordering collections

// Observe the collection in a specific order
let collectionStream = sdk.streamCollection(
        '/api/v5/orgs/c0c329ce-bd7f-41e3-b79b-af2f7f9f334b/users',
        {ordering: '-created_at'}
    )
    // Convert to arrays of max. 10 most recently created users
    .switchMap(userStream => userStream.take(10).toArray())
    .subscribe(recentlyCreatedUsers => {
        // Emits max. 10 most recently created users as an array,
        // and then a new array whenever a new user is created!
    })
;

The streamCollection, streamCollectionArray and getCollection methods accepts additional options. You can use the ordering option to define the order of the resources in the collection. The option must be supported by the HTTP endpoint.

As with the HTTP endpoint, you may use the - prefix to reverse the order. You can also separate secondary sorting criterias with commas (,).

Filtering collections

All the collection streaming and loading methods described above (streamCollection, streamCollectionArray and getCollection) support filtering options:

// Start listening changes to the collection
sdk.streamCollectionArray(
        '/api/v5/orgs/c0c329ce-bd7f-41e3-b79b-af2f7f9f334b/users',
        { is_manager: true }
    )
    .subscribe(managerUsers => {
        // Emits the array of filtered resources in the collection
    })
;

This will load the resources from the following URL:

/api/v5/orgs/c0c329ce-bd7f-41e3-b79b-af2f7f9f334b/users?is_manager=true

For the best performance, it is recommended that the backend supports the filtering GET parameters. However, the filtering will be also done client-side, in case the backend does not support the filtering parameter.

You may provide multiple filtering criterias, in which case all of them must match.

The following filtering types are supported:

Type | Description --------|------------ string | Added to the URL request as URI-encoded GET parameter integer | Added to the URL request as stringified GET parameter boolean | Added to the URL reqeust as ether true or false GET parameter

Note that currently, for simplicity, only exact value filtering is supported with URL GET parameters. Negations or comparisons may be added in the future. You may perform any custom filtering on the client-side. Also, no "or" condition in currently supported.

Observing resource additions

In some cases you may only be interested in resources that are added to a collection. For example, you may want to play a sound or show a small notification whenever a new chat message is received, but you would like to ignore any initial chat messages for this purpose.

In these cases, you can use streamCollectionAdditions helper method:

// Start listening additions to the collections
sdk.streamCollectionAdditions(
        '/api/v5/users/xxxxx/chats/yyyyy/messages',
        { ordering: '-created_at' }
    )
    .subscribe(newMessage => {
        // Called for each chat message that is added to the collection
        // while this collection is being subscribed!
    })
;

Please note the following characteristics of the returned observable:

  • It only emits resources that are added to the collection during the subscription. Any resources existing in the collection when being subscribed are omitted.
  • It never completes, but it fails if the loading of resources fail.
  • This completely ignores any updates and removals of resources. So, the resources in the collection should be "immutable" as possible.
  • If the resource is removed and re-added to a collection later, it will be emitted multiple times. You can avoid this by using, for example, .distinctKey("id"), but consider memory usage.
  • IMPORTANT: Internally, this needs to observe to the whole collection like streamCollection does, and therefore you can (and should) provide the same options, including filtering criteria. The ordering option will affect the order in which any "simulatenously" added resources are emitted. If the ordering does not matter and you are streaming the same collection elsewhere with any specific ordering, using the same ordering with this method will preserve a bunch of unnecessary HTTP requests!

Testing and debugging

Authentication in tests

In unit tests you probably do not want to call connect method of RealtimeSDK instance, as this tries to redirect the browser or make an AJAX request to the authentication endpoint and create an actual Websocket connection. Instead, you may initialize the SDK with a mock authentication object with setAuthentication method like this:

import { RealtimeSDK } from '@giosg/realtime-sdk';
const sdk = new RealtimeSDK();
// Set mock authentication for the SDK
sdk.setAuthentication({
    access_token: "<MY_ACCESS_TOKEN>",
    user_id: "b8aca5a7-4c73-4de1-bcf4-d3614029db1f",
    organization_id: "4ee0c512-7fb5-4b00-bfec-a6c6cd7f94e3",
});

It also accepts an Observable that emits authentication objects (to mimic changes in the authentication state):

import { Subject } from 'rxjs/Subject';
// ...
// Set up authentication from an Observable of authentication objects
let mockAuth = new Subject();
sdk.setAuthentication(mockAuth);
mockAuth.next({ access_token: "<FIRST_ACCESS_TOKEN>", /* ... */ });
mockAuth.next({ access_token: "<SECOND_ACCESS_TOKEN>", /* ... */ });

Mocking resources

The has a bunch of methods that allows to to mock the resources and collections. This means than whenever a certain resource or collection is streamed, the "fake" objects are emitted instead.

This is useful for testing components that use SDK, as you do not have to fake HTTP requests or socket traffic at all! You can also use these methods runtime for debugging.

Mock to always return a certain value:

// Mock the resource
sdk.mockResource("/api/v5/users/xxxx", {
    id: "xxxx",
    first_name: "John",
    last_name: "Smith",
    // ...
});

It also allows you to mock with an Observable. By using a Subject, you can fake changes to a resource:

// Fake the changes to the resource
let fakeStream = new Subject<Resource>();
sdk.mockResource("/api/v5/users/xxxx", fakeStream);
// ...
fakeStream.next({
    id: "xxxx",
    is_online: false,
    // ...
});
// ...
fakeStream.next({
    id: "xxxx",
    is_online: true,
    // ...
});

You can reset the mocking for the resource, which re-enables retrieving the resource via HTTP and listening changes via socket:

// Reset the mocking for the resource
sdk.mockResource("/api/v5/users/xxxx", null);

Mocking collections

Mock to always return a certain array of resources:

sdk.mockCollectionArray("/api/v5/users", [
    { /* User 1 */ },
    { /* User 2 */ },
    { /* User 3 */ },
]);

You can fake changes to the collection by mocking with an Observable of arrays, for example, by using a Subject. It also allows you to mock with an Observable. By using a Subject, you can fake changes to a resource:

let fakeStream = new Subject<Resource[]>();
sdk.mockCollection("/api/v5/users", fakeStream);
// ...
fakeStream.next([
    { /* User 1 */ },
    { /* User 2 */ },
]);
// ...
fakeStream.next([
    { /* User 1 */ },
    { /* User 2 */ },
    { /* User 3 */ },
]);

Mocking resource actions

You can also mock the responses to POST, PUT, PATCH and DELETE actions done to resources via RealtimeSDK. For POST, PUT and PATCH, you can set a static response object.

// Mock POST response
sdk.mockResourcePost("/api/v5/users", {
    id: "xxxx",
    first_name: "John",
    last_name: "Smith",
    // ...
});
// Mock PUT response
sdk.mockResourcePut("/api/v5/users/xxxx", {
    id: "xxxx",
    first_name: "John",
    last_name: "Smith",
    // ...
});
// Mock PATCH response
sdk.mockResourcePatch("/api/v5/users/xxxx", {
    id: "xxxx",
    first_name: "John",
    last_name: "Smith",
    // ...
});

Alternatively, for faking asyncronous responses, you can provide an Observable, such as a Subject. This applies any of the action mocking methods:

// Mock asyncronous POST response
let fakeResponse = new Subject<Resource>();
sdk.mockResourcePost("/api/v5/users/xxxx", fakeResponse);
// ...
fakeResponse.next({
    id: "xxxx",
    first_name: "John",
    last_name: "Smith",
    // ...
});
fakeResponse.complete();

In tests, you can also provide a function as the mock. The function will be called with the payload and the access token. Its return value, which can be an object or an Observable, will be handled similar to previous examples.

// Test that the code POSTs a new resource
let postHandler = jasmine.createSpy('postHandler').and.returnValue({
    id: "xxxx",
    first_name: "John",
    last_name: "Smith",
    // ...
});
sdk.mockResourcePost("/api/v5/users", postHandler);
// ...
expect(postHandler).toHaveBeenCalledWith({ /* POSTed object */ }, "<ACCESS_TOKEN>");

Test mode

By default, SDK will only fake resources and collections that are specifically mocked with the mocking methods. Any other resources and collections will be loaded remotely. In tests, however, this is not usually what you want.

You can prevent the SDK to make any actual HTTP requests or socket traffic by switching it to the test mode. For example, in Jasmine tests you can do this:

// Disallow actual network traffic during the test
beforeEach(() => sdk.startTestMode());
afterEach(() => sdk.stopTestMode());

This is recommended to be done in each test that uses an SDK.

Optimistic resource altering

RealtimeSDK supports optimistic resource updates and deletions.

By default, any updates and removals are applied immediately to any related resource or collection stream! The actual operation is then run in background. If the operation fails, then the optimistic changes are rolled back.

NOTE: The support for optimistic additions could be added later.

When using putResource, patchResource or deleteResource, you may optionally define any optimistic side-effects yourself.

sdk.putResource(
    `/api/v5/users/${userId}`,
    {
        first_name: "John",
        last_name: "Smith",
        // ...
    },
    {
        // Which other resources should be updated?
        optimisticUpdates: [{
            resourceUrl: `/api/v5/orgs/${organizationId}/users/${userId}`,
            resource: {
                full_name: "John Smith",
            }
        }],
        // Which resources should be removed from their collections?
        optimisticRemovals: [`/api/v5/orgs/${organizationId}/cool_guys/${userId}`],
    }
).subscribe();

Publishing

To publish newer version of this package to npm ypu need to:

  • update package version in package.json
  • run npm install to install all dependencies and update package-lock.json
  • login with npm login --scope=@giosg to our organization
  • publish with npm publish -–access=public