relaks-django-data-source

2.0.3 • Public • Published

Relaks Django Data Source

This module lets you access a Django server from a React app that use Relaks. It's designed to work with the Django REST framework.

Installation

npm --save-dev install relaks-django-data-source

Usage

import DjangoDataSource from 'relaks-django-data-source';

let options = {
    abbreviatedFolderContents: false,
    authorizationKeyword: 'Token',
    baseURL: 'https://swapi.co/api',
    refreshInterval: 60 * 1000,
};
let dataSource = new DjangoDataSource(options);
dataSource.activate();
/* Root-level React component */
class FrontEnd extends PureComponent {
    constructor(props) {
        super(props);
        let { dataSource } = props;
        this.state = {
            database: new Database(dataSource);
        }
    }

    componentDidMount() {
        let { dataSource } = this.props;
        dataSource.addEventListener('change', this.handleDataSourceChange);
    }

    /* ... */

    handleDataSourceChange = (evt) => {
        let { dataSource } = this.props;
        let database = new Database(dataSource);
        this.setState({ database });
    }
}

Components are expected to access functionalities of the data source through a proxy object--Database in the sample code above. See the documentation of Relaks for an explanation. A default implementation is provided for reference purpose. It's recommended that you create your own.

Options

abbreviatedFolderContents

A boolean value indicating whether objects in directory listings are always different from those fetched individually. Normally, when you ask for a single object, the data source will check to see if the object can be found in a cached directory query. For example, it'll check queries on /articles/ when you request /articles/5/. This behavior would lead to erroneous results if objects in a directory listing only contains a subset of their properties. Setting abbreviatedFolderContents to true disables it completely.

You can also flag queries as abbreviated selectively.

The default value is false.

authorizationKeyword

The keyword that precedes the token in the HTTP Authorization header:

Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b

The default matches what the Django REST Framework's TokenAuthentication scheme uses. You don't need to supply this unless you have subclassed TokenAuthentication and changed the keyword to something else.

baseURL

The base URL of the remote server. It'll be added to any URL that isn't absolute.

fetchFunc

An alternative function to be used in place of the browser's built-in fetch().

forceHTTPS

Ensures HTTPS is used if baseURL starts with "https:". True by default.

refreshInterval

The amount of time, in milliseconds, to wait before rerunning data queries to ensure freshness. The data source caches all queries. When a query matches one that was performed before, the results obtained earlier will be returned immediately. If the amount of time elapsed since exceeds refreshInterval, the data source will rerun the query. If the results differ in anyway, a change event will occur.

You can also manually flag queries as out-of-date by calling invalidate().

Methods

Event listeners:

Activation

Data retrieval:

Cache invalidation:

Data modification:

Access control:

HTTP request:

addEventListener()

function addEventListener(name: string, handler: function, beginning?:boolean): void

Attach an event listener to the data source. handler will be called whenever events of type occur. When beginning is true, the listener will be place before any existing listeners. It's otherwise added at the end of the list.

Inherited from relaks-event-emitter.

removeEventListener()

function removeEventListener(name: string, handler: function): void

Detach an event listener from the data source. handler and type must match what were given to addEventListener().

Inherited from relaks-event-emitter.

waitForEvent()

async function waitForEvent(type: string): Event

Return a promise that is fulfilled when an event of the specified type occurs.

Inherited from relaks-event-emitter.

activate()

function activate(): void

Activate the data source, allowing it to fetch data from a remote server.

deactivate()

function deactivate(): void

Deactivate the data source, keeping it from performing data requests. Operations that require accessing a remote server will be on hold indefinitely, until activate() is called.

The data source will continue to return cached data after its deactivation.

fetchList()

async function fetchList(url: string, options?: object): object[]

Conceptually, fetch all objects in a directory. In actuality, the method will only return the first page of results initially (when pagination is enabled). Attached to the returned array will be a method called more(). When it's called, an additional page will be fetched and appended to the list.

This method is designed for handling large result sets with continuous scrolling (as opposed to traditional pagination).

In addition to more(), the returned array will also have the property total. It's the number of objects in the directory on the server. The standard property length gives the number of objects already retrieved.

more() and total are always present, even when pagination is not available. A call to more() does nothing when there are no more pages.

By default, fetchList() will return as soon as it has one page of results. Specifying the option minimum forces it to wait until a certain number of objects have become available. When minimum is a negative number, that's interpreted as the difference from the total. When minimum is a string, it's expected to hold a percentage of the total. For example, 100% means the complete data set.

Options:

  • minimum - the minimum number of objects to fetch (default: any)
  • abbreviated - indicates that the objects found at url do not have all their properties and they should not be used to fulfill calls to fetchOne()
  • afterInsert - see afterInsert (default: "refresh")
  • afterUpdate - see afterUpdate (default: "refresh")
  • afterDelete - see afterDelete (default: "remove")

fetchMultiple()

async function fetchMultiple(urls: string[], options?: object): object[]

Fetch multiple objects in a single call. This is convenience method designed for handling one-to-many relations. It calls fetchOne() internally.

By default, the promise returned by fetchMultiple() is not fulfilled until every object is retrieved from the remote server. When the option minimum is specified, the promise will fulfill immediately when the number of cached objects meets the requirement. null will appear in place of an object in the array when it's uncached. When the uncached objects eventually arrive, the data source emits a change event. Subsequent calls to fetchMultiple() will then return all requested objects.

Options:

  • minimum - the minimum number of objects to fetch (default: all)

fetchOne()

async function fetchOne(url: string, options?: object): object

Fetch an object from the server. This method will check the results of calls to fetchPage() and fetchList() to see if the object in question hasn't been fetched already.

Options:

fetchPage()

async function fetchPage(url: string, page: number, options?: object): object[]

Fetch a single page of a directory listing. All objects will be returned if the server doesn't support pagination.

Options:

  • abbreviated - indicates that the objects found at url do not have all their properties and they should not be used to fulfill calls to fetchOne()
  • afterInsert - see afterInsert (default: "refresh")
  • afterUpdate - see afterUpdate (default: "refresh")
  • afterDelete - see afterDelete (default: "refresh")

invalidate()

function invalidate(time: string): boolean

Flag matching data queries performed before the specified time as out-of-date. time should be an ISO timestamp (a Date object is also acceptable). The methods returns true when queries have been invalidated and false otherwise.

The data source emits a change event when queries are invalidated.

invalidateList()

function invalidateList(url:string, options?: object): boolean

Invalidate a query performed earlier using fetchList().

invalidateMultiple()

function invalidateMultiple(urls:string[], options?: object): boolean

Invalidate multiple queries performed earlier using fetchOne().

invalidatePage()

function invalidatePage(url:string, page: number, options?: object): boolean

Invalidate a query performed earlier using fetchPage().

invalidateOne()

function invalidateOne(url:string, options?: object): boolean

Invalidate a query performed earlier using fetchOne().

deleteOne()

async function deleteOne(folderURL: string, object: object): object
async function deleteOne(object: object): object

Delete an object on the remote server. The afterDelete hooks of cached queries are invoked afterward.

When URL keys are used, folderURL can be omitted (since the object contains its own URL).

deleteMultiple()

function deleteMultiple(folderURL: string, objects: object[]): object[]
function deleteMultiple(objects: object[]): object[]

Delete multiple objects on the remote server. The afterDelete hooks of cached queries are invoked afterward.

When URL keys are used, folderURL can be omitted (since the objects contain their own URLs).

insertOne()

async function insertOne(folderURL: string, object: object): object

Insert an object into a directory on the remote server. The afterInsert hooks of cached queries are invoked afterward. The inserted object will be available through fetchOne() immediately.

insertMultiple()

async function  insertMultiple(folderURL: string, objects: object[]): object[]

Insert multiple objects into a directory on the remote server. The afterInsert hooks of cached queries are invoked afterward. The inserted objects will be available through fetchOne() immediately.

updateOne()

async function updateOne(folderURL: string, object: object): object
async function updateOne(object: object): object

Update an object on the remote server. The afterUpdate hooks of cached queries are invoked afterward.

When URL keys are used, folderURL can be omitted (since the object contains its own URL).

updateMultiple()

function updateMultiple(folderURL: string, objects: object[]): object[]
function updateMultiple(objects: object[]): object[]

Update multiple objects on the remote server. The afterUpdate hooks of cached queries are invoked afterward.

When URL keys are used, folderURL can be omitted (since the objects contain their own URLs).

authenticate()

async function authenticate(loginURL: string, credentials: object, allowURLs?: string[]): boolean

Gain elevated access to a Django server. loginURL is the REST endpoint for logging in. credentials is an object containng username (or email) and password. allowURLs is the list of URLs that will become accessible. This parameter is designed for apps that access multiple servers. It can typically be omitted. Its default value is [ "/" ], meaning all resources under baseURL will become accessible.

The fulfillment value of this method is true when the login process succeeds. If it fails, the promise will be rejected.

An authorization event occurs when the data source acquire an authorization token. This give you a chance to save the token for use in a future session.

authorize()

async function authorize(token: string, allowURLs?: string[], fresh?: boolean): boolean

Provide a Django authorization token to the data source, likely one that was saved from an earlier authentication. allowURLs indicates the scope of access. It can typically be omitted. fresh indicates whether the token was just obtained from the remote server. If omitted, evt.fresh will be false in the subsequent authorization event. Your code can then elect not to save the token again.

The method will return a promise that immediately resolves to false if token is empty or if the data source has previously encountered a 401 or 403 error using that token.

cancelAuthentication()

function cancelAuthentication(allowURLs?: string[]): void

Tell the data source to stop waiting for authentication. Operations that had encountered the HTTP status code 401 will then fail.

cancelAuthorization()

function cancelAuthorization(denyURLs?: string[]): void

Remove authorization to certain URLs. This can be used to force the user to authenticate again before a sensitive area of your app can be accessed.

This method does not trigger any event.

isAuthorized()

function isAuthorized(url?: string): boolean

Return true if the data source has an authorization token for the identicated location. url can be omitted, in which case it defaults to "/".

revokeAuthorization()

function revokeAuthorization(logoutURL: string, denyURLs?: string[]): void

Log out from a Django server. logoutURL is the REST endpoint for logging out. denyURLs is the list of URLs from which authorization will be removed. It can typically be omitted.

An deauthorization event will occur afterward. This give you a chance to remove a saved authorization token.

A change event will also occur after the data source removed cached queries.

delete()

async function delete(url: string): null

Low-level function that performs an HTTP DELETE operation.

get()

async function get(url: string): object

Low-level function that performs an HTTP GET operation.

post()

async function post(url: string, object: object): object

Low-level function that performs an HTTP POST operation.

put()

async function put(url: string, object: object): object

Low-level function that performs an HTTP PUT operation.

Hooks

afterDelete

When objects are deleted using deleteOne() or deleteMultiple(), a query's afterDelete hook is invoked so that cached results are updated.

For queries performed through fetchOne(), the hook function has the form:

function afterDeleteHook(object: object, deletedObject: object): object

object is the cached object before, while deletedObject is a copy of the same object. If the function returns an object, that'll be the result when the query is run again. If it returns true, then the query is removed from the cache (the default behavior). If it returns false, then the object will continue to be available despite its disappearance on the server.

For queries performed through fetchPage() or fetchList, the hook function has the form:

function afterDeleteHook(objects: objects[], deletedObjects: objects[]): objects[]

objects are the currently cached objects, while deletedObjects is a list of deleted objects. The function should return a new array not containing the deleted objects (the default behavior). If there's no change, it should return false. If it returns true, the query will be refreshed.

A string can be used to specify a predefined hook function. The possible values are "refresh", "ignore", "remove".

afterInsert

When new objects are inserted into a directory using insertOne() or insertMultiple(), a query's afterInsert hook is invoked so that cached results are updated. Only queries performed through fetchPage() or fetchList() have this hook.

The hook function has the following form:

function afterInserHook(objects: object[], newObjects: object[]): objects[]

objects are the currently cached objects, while newObjects is a list of newly created objects. The function should return a new array with the new objects inserted at the correct positions. If there's no change (because none of the new objects actually matches the query), the function should return false. If the function return true, the query will be invalidated.

A string can be used to specify a predefined hook function. The possible values are "refresh", "ignore", "unshift", and "push".

afterUpdate

When existing objects are modified using updateOne() or updateMultiple(), a query's afterUpdate hook is invoked so that cached results are updated. It's also invoked when the data source fetches a more recent copy of an object. Consider the following scenario:

  1. fetchOne() retrieves object A from the server
  2. Object A is modified on the server side
  3. fetchList() retrieves object A, B, C, and D from the server

Query 1's afterUpdate hook will be invoked because query 3 has obtained a newer copy of object A. Since the default hook in this case is "replace", query 1 will now also yield the newer copy.

For queries performed through fetchOne(), the hook function has the form:

function afterUpdateHook(object: object, newObject: object): object

object is the cached object before, while newObject is the object returned by the server after the save operation. If the function returns an object, then that becomes the cached object. If it returns false, that means no change occurred. If it returns true, the query is invalidated.

For queries performed through fetchPage() or fetchList, the hook function has the form:

function afterUpdateHook(objects: object[], newObjects: object[]): object[]

objects are the currently cached objects, while newObjects is a list of modified objects. The function should return a new array with old objects replaced by new ones. If there's no change, the function should return false. If the function return true, the query will be invalidated.

The function might need to sort the objects if changes can impact their order.

A string can be used to specify a predefined hook function. The possible values are "refresh", "ignore", "replace".

Predefined hook functions

  • "ignore" - return false to indicate there's no need to change the cached results
  • "push" - place new objects at the end of the list
  • "refresh" - return true to request refreshing of the query
  • "remove" - remove deleted objects from the list
  • "replace" - replace currently cached objects with the updated copy from the server
  • "unshift" - place new objects at the beginning of the list

Events

authentication

An authentication event is emitted when the server responds to a request with the HTTP status code 401 ("Unauthorized").

Default action:

Wait for authentication to occur then try again.

Properties:

  • url - the URL that triggered the 401 response
  • defaultPrevented - whether preventDefault() was called
  • propagationStopped - whether stopImmediatePropagation() was called
  • target - the data source
  • type - "authentication"

Methods:

  • postponeDefault(promise: Promise) - wait for given promise to be fulfilled and fail the operation if the fulfillment value is false
  • preventDefault() - fail the operation that trigger the authentication request
  • stopImmediatePropagation() - stop other listeners from receiving the event

authorization

An authorization event is emitted when the data source receives an authorization token, either from the server after a call to authenticate() or from your app via authorize(). A change event will follow if the default action is not prevented.

Default action:

Allow operations waiting for authentication to proceed.

Properties:

  • allowURLs - a list of URLs that have become accessible
  • fresh - whether the token was freshly issued by the server
  • token - the authorization token
  • defaultPrevented - whether preventDefault() was called
  • propagationStopped - whether stopImmediatePropagation() was called
  • target - the data source
  • type - "authorization"

Methods:

  • postponeDefault(promise: Promise) - postpone the decision on whether to proceed until the given promise is fulfilled
  • preventDefault() - stop pending operations from proceeding despite authorization having been granted
  • stopImmediatePropagation() - stop other listeners from receiving the event

Generally, there's no reason to prevent the default behavior from happen. Doing so only makes sense if the app has failEd to gain some other necessary authorization.

change

A change event is emitted whenever rerunning queries might yield new result sets. It's also emitted after the data source has gained or lost autorization.

Properties:

  • propagationStopped - whether stopImmediatePropagation() was called
  • target - the data source
  • type - "change"

Methods:

  • stopImmediatePropagation() - stop other listeners from receiving the event

deauthorization

A deauthorization event is emitted after revokeAuthorization() has logged out from a Django server. A change event will follow even if the default action is prevented.

Default action:

Clear cache queries.

Properties:

  • denyURLs - a list of URLs that might no longer be accessible
  • defaultPrevented - whether preventDefault() was called
  • propagationStopped - whether stopImmediatePropagation() was called
  • target - the data source
  • type - "deauthorization"

Methods:

  • postponeDefault(promise: Promise) - postpone the clearing of cached queries until the given promise is fulfilled
  • preventDefault() - prevent clearing of cached queries
  • stopImmediatePropagation() - stop other listeners from receiving the event

Examples

License

This project is licensed under the MIT License - see the LICENSE file for details

Readme

Keywords

Package Sidebar

Install

npm i relaks-django-data-source

Weekly Downloads

0

Version

2.0.3

License

MIT

Unpacked Size

314 kB

Total Files

10

Last publish

Collaborators

  • cleong