dojo-stores
This library provides a data store, and several mixins built using dojo-compose and TypeScript. The mixins provide additional functionality and APIs that can be added to the base store dynamically.
WARNING This is alpha software. It is not yet production ready, so you should use at your own risk.
Features
Storage
The underlying Storage
interface provides the basic CRUD functionality, and is leveraged to provide the Store
interface, which is the interface intended to be consumed. This means that the basic createStore
factory, which defaults to using the provided createInMemoryStorage
can be repurposed to interact with any storage medium by providing an object implementing the simpler Storage
interface at instantiation.
;
Store
The Store
interface provides basic CRUD operations, methods to retrieve records, and methods to create IDs.
getids: string | string: Promise<T>;
Retrieves store items associated with the provided ID or IDs. Will return undefined for any items that don't exist in the store.
identifyitems: T | T: string;
Returns the IDs for the passed in items. By default the store will look for an id
property on an item, if another property should be used, the idProperty
can be specified when creating the store. The idFunction
property can be provided if a more complicated, composite ID needs to be generated.
createId: Promise<string>;
Generates a new ID. The default implementation of createId
involves incrementing an internally stored integer every time it is called.
additems: T | T, options?: O: StoreObservable<T, U>;
Adds the item(s) to the store, failing if they already exist, unless the rejectOverwrite
property is set to false
. For the default store implementation rejectOverwrite
is the only option used by the store.
putitems: T | T, options?: O: StoreObservable<T, U>;
Adds or overwrites the specified items in the store. If overwrites should not be allowed, rejectOverwrite
should be set to true
in the provided options.
patchupdates: PatchArgument<T>, options?: O: StoreObservable<T, U>;
Updates the item(s) indicated by PatchArgument in place in the store. The Patch
interface is based on the JSON Patch spec, and can be serialized(not fully tested) to a format compliant with an HTTP patch request
delete
Delete the item(s) with the provided IDs from the store
fetch: Promise<T>;
Returns a promise that will resolve to all of the data in the store
fetchquery: Query<T, U>: Promise<U>;
Returns a promise to the data matching the provided Query
in the store.
Basic Usage
store.fetch.then;store.delete'1'.then;store.delete.then;store.add;store.put;// These won't compile, because they don't match// the item type. The item type was inferred by the data argument// in the createStore initialization options, but it can also be// specified explicitly(i.e. createStore<TypeOfData, CrudOptions>();)// store.put({ id: '5', value: '' });// store.add('5');store.patch;store.fetch.then;
Store Observable
The return type of the CRUD methods on the Store
interface is a StoreObservable
. This type extends Promise
. then
returns the final results of the operation to the callback provided if it is successful, and passes any errors that occurred to the error callback otherwise.
But it is also observable. By default any subscribers will get exactly one UpdateResults
object or an error before being completed.
The built in store will only populate the type
and successfulData
properties, but this provides an extension point for store implementations to provide more details about the results of the operation, report results incrementally, or allow for the operation to be retried in the case of recoverable errors(e.g. data conflicts or network errors).
createObservableStoreMixin
This store provides an API for observing the store itself, or specific items within the store.
Observing the store
When observing the whole store, an initial update will be received that contains the current data in the store in the afterAll
property, and subsequent updates will represent the changes in the store since the last update.
If the fetchAroundUpdates
property is set to true
in the options when creating the store, then the data in the store will be kept up to date with the underlying storage, and any updates will represent the latest data from the storage. If fetchAroundUpdates
is false
or not specified, then the local data will be modified in place according to the updates indicated by the StoreDelta
, but it may become out of sync with the underlying storage. In this case, when fetching manually, the local data will be synced again. An update is sent after a fetch
, and the update following a fetch may contain new items in the afterAll
property that are not represented by the updates
, adds
, and deletes
of the StoreDelta
when fetchAroundUpdates
is false
and the store is out of sync with its storage.
Example usage
; ; observableStore.observe.subscribe; observableStore.observe'itemId'.subscribe, undefined, ; observableStore.observe.subscribe, undefined, ;
createQueryTransformMixin
This mixin provides the ability to filter, sort, select a range of, transform, or query for items using a custom query function.
The result of querying or transforming will be a read only QueryTransformResult
.
The QueryTransformResult
can be further queried or transformed, as well as observed, and has a reference to the source store if updates need to be performed on the original data.
The observation API for the QueryTransformResult
is very similar to the store's with a few changes.
MappedQueryTransformResult
Unless the transform method is called without an idTransform, the result of any queries to a store with the createQueryTransformMixin will be a MappedQueryTransformResult
, which includes additional data in its StoreDelta
updates, and provides a track()
method. The StoreDelta
interface is extended by the TrackedStoreDelta
interface which the MappedQueryTransformResult
provides to observers. This augments the interface by providing data indicating the current and previous indices of items that have been moved within, added to, or removed from, the view represented by the MappedQueryTransformResult
. Unlike updates
, deletes
, and adds
, these properties are not related to specific operations, but instead just represent changes in the position of items within the collection.
As with the observable store mixin, the locally tracked data in a MappedQueryTransformResult
has the potential to become out of sync with the underlying storage. If the source ObservableStore
has fetchAroundUpdates
set to true, then any query transform results produced from it will only send up to date information to observers. If the source is not fetching around updates, the track()
method provided as part of the MappedQueryTransformResult
interface can be used to create a copy of a QueryTransformResult
that will fetch after any updates from its source to make sure it has the latest data. track()
produces a TrackedQueryTransformResult
, which has a release()
method that provides a new, non-tracked query transform result.
When transform()
is called without an idTransform
, the resulting QueryTransformResult
has no way of determining the ID of a transformed item, and so it cannot tell whether changes from the source store represent updates or additions, and cannot keep an index to easily track the position of items within the store. As a result, a QueryTransformResult
created this way will not contain positional information in its updates to observers, and cannot be tracked.
Example Usage
; ;; ;filteredView.fetch.then; filteredView.observe.subscribe;
If the observer starts observing after the initial add is already resolved, the first update they receive will be the subsequent update
in this example. Here the first update is provided to an observer that subscribes synchronously with the initialization of the store, but the initial add happens asynchronously and so is not yet resolved. For a tracked collection, the first update will contain the data form a fetch
to the source.
How do I contribute?
We appreciate your interest! Please see the Dojo 2 Meta Repository for the Contributing Guidelines and Style Guide.
Testing
Test cases MUST be written using Intern using the Object test interface and Assert assertion interface.
90% branch coverage MUST be provided for all code submitted to this repository, as reported by istanbul’s combined coverage results for all supported platforms.
To test locally in node run:
grunt test
To test against browsers with a local selenium server run:
grunt test:local
To test against BrowserStack or Sauce Labs run:
grunt test:browserstack
or
grunt test:saucelabs
Licensing information
TODO: If third-party code was used to write this library, make a list of project names and licenses here
© 2004–2016 Dojo Foundation & contributors. New BSD license.