From 7ffb9e121374e980efecbbf472960ff65d4d9b09 Mon Sep 17 00:00:00 2001 From: Dara Hayes Date: Thu, 5 Dec 2019 17:05:34 +0000 Subject: [PATCH] chore: release 0.12.0 (#289) * chore: release 0.12.0 * doc: 0.12.0 version on website --- docs/ref-release-notes.md | 7 + examples/conflicts/package.json | 4 +- lerna.json | 2 +- packages/offix-cache/package.json | 2 +- packages/offix-client-boost/package.json | 6 +- packages/offix-client/package.json | 10 +- packages/offix-conflicts-client/package.json | 2 +- packages/offix-conflicts-server/package.json | 2 +- packages/offix-offline/package.json | 2 +- packages/offix-scheduler/package.json | 6 +- packages/react-offix-hooks/package.json | 4 +- .../versioned_docs/version-0.12.0/android.md | 14 + .../versioned_docs/version-0.12.0/angular.md | 16 + .../version-0.12.0/getting-started.md | 130 +++++++ .../version-0.12.0/ref-advanced.md | 37 ++ .../version-0.12.0/ref-cache.md | 158 ++++++++ .../version-0.12.0/ref-configuration.md | 36 ++ .../version-0.12.0/ref-offline.md | 192 ++++++++++ .../version-0.12.0/ref-release-notes.md | 344 ++++++++++++++++++ .../version-0.12.0-sidebars.json | 28 ++ website/versions.json | 1 + 21 files changed, 983 insertions(+), 20 deletions(-) create mode 100644 website/versioned_docs/version-0.12.0/android.md create mode 100644 website/versioned_docs/version-0.12.0/angular.md create mode 100644 website/versioned_docs/version-0.12.0/getting-started.md create mode 100644 website/versioned_docs/version-0.12.0/ref-advanced.md create mode 100644 website/versioned_docs/version-0.12.0/ref-cache.md create mode 100644 website/versioned_docs/version-0.12.0/ref-configuration.md create mode 100644 website/versioned_docs/version-0.12.0/ref-offline.md create mode 100644 website/versioned_docs/version-0.12.0/ref-release-notes.md create mode 100644 website/versioned_sidebars/version-0.12.0-sidebars.json diff --git a/docs/ref-release-notes.md b/docs/ref-release-notes.md index 0cad2e7ca..6b0e781e0 100644 --- a/docs/ref-release-notes.md +++ b/docs/ref-release-notes.md @@ -4,6 +4,13 @@ title: What is new in Offix sidebar_label: Release notes --- +# 0.12.0 + +The 0.12.0 release brings a new `offix-client-boost` package. This package is a batteries-included wrapper around the `offix-client` that brings +everything needed to get started with GraphQL quickly. This includes a cache and support for subscriptions and file uploads. + +Check out the new documentation at [offix.dev](https://offix.dev). + # 0.11.0 The 0.11.0 release simplifies the creation of offline clients. diff --git a/examples/conflicts/package.json b/examples/conflicts/package.json index de73eaae5..e9800fc9b 100644 --- a/examples/conflicts/package.json +++ b/examples/conflicts/package.json @@ -1,6 +1,6 @@ { "name": "conflict-examples", - "version": "0.11.0", + "version": "0.12.0", "private": true, "description": "", "scripts": { @@ -13,7 +13,7 @@ "express": "^4.16.4", "graphql": "0.13.2", "graphql-tools": "^4.0.3", - "offix-conflicts-server": "0.11.0" + "offix-conflicts-server": "0.12.0" }, "devDependencies": { "@types/graphql": "^14.0.3", diff --git a/lerna.json b/lerna.json index 8b3a5aa3f..f685da709 100644 --- a/lerna.json +++ b/lerna.json @@ -4,7 +4,7 @@ "examples/*", "packages/*" ], - "version": "0.11.0", + "version": "0.12.0", "command": { "publish": { "exact": true diff --git a/packages/offix-cache/package.json b/packages/offix-cache/package.json index 9399f9ea1..d63573a99 100644 --- a/packages/offix-cache/package.json +++ b/packages/offix-cache/package.json @@ -1,6 +1,6 @@ { "name": "offix-cache", - "version": "0.11.0", + "version": "0.12.0", "description": "GraphQL Mutation and Subscription Helpers", "main": "dist/index.js", "types": "types/index.d.ts", diff --git a/packages/offix-client-boost/package.json b/packages/offix-client-boost/package.json index 290e7e1a4..6e9c19db9 100644 --- a/packages/offix-client-boost/package.json +++ b/packages/offix-client-boost/package.json @@ -1,6 +1,6 @@ { "name": "offix-client-boost", - "version": "0.11.0", + "version": "0.12.0", "description": "simplifies setup and usage of offix-client", "main": "dist/index.js", "types": "types/index.d.ts", @@ -23,8 +23,8 @@ "apollo-link-retry": "2.2.15", "apollo-link-ws": "1.0.19", "apollo-upload-client": "12.1.0", - "offix-client": "0.11.0", - "offix-conflicts-client": "0.11.0", + "offix-client": "0.12.0", + "offix-conflicts-client": "0.12.0", "subscriptions-transport-ws": "0.9.16" }, "devDependencies": { diff --git a/packages/offix-client/package.json b/packages/offix-client/package.json index 1a52e5dda..6269dbeae 100644 --- a/packages/offix-client/package.json +++ b/packages/offix-client/package.json @@ -1,6 +1,6 @@ { "name": "offix-client", - "version": "0.11.0", + "version": "0.12.0", "description": "Offix GraphQL Offline Client", "main": "dist/index.js", "types": "types/index.d.ts", @@ -48,10 +48,10 @@ "apollo-link-retry": "2.2.15", "debug": "4.1.1", "idb-localstorage": "0.2.0", - "offix-cache": "0.11.0", - "offix-conflicts-client": "0.11.0", - "offix-offline": "0.11.0", - "offix-scheduler": "0.11.0" + "offix-cache": "0.12.0", + "offix-conflicts-client": "0.12.0", + "offix-offline": "0.12.0", + "offix-scheduler": "0.12.0" }, "peerDependencies": { "graphql": "^0.12.0 || ^0.13.0 || ^14.0.0" diff --git a/packages/offix-conflicts-client/package.json b/packages/offix-conflicts-client/package.json index 77f37fd0a..51a061a42 100644 --- a/packages/offix-conflicts-client/package.json +++ b/packages/offix-conflicts-client/package.json @@ -1,6 +1,6 @@ { "name": "offix-conflicts-client", - "version": "0.11.0", + "version": "0.12.0", "description": "adds client side conflict detection and resolution", "main": "dist/index.js", "types": "types/index.d.ts", diff --git a/packages/offix-conflicts-server/package.json b/packages/offix-conflicts-server/package.json index 78aa67802..fcbd6748a 100644 --- a/packages/offix-conflicts-server/package.json +++ b/packages/offix-conflicts-server/package.json @@ -1,6 +1,6 @@ { "name": "offix-conflicts-server", - "version": "0.11.0", + "version": "0.12.0", "description": "Offix GraphQL server", "main": "dist/index.js", "scripts": { diff --git a/packages/offix-offline/package.json b/packages/offix-offline/package.json index 0bd3bf20c..6fd351ed5 100644 --- a/packages/offix-offline/package.json +++ b/packages/offix-offline/package.json @@ -1,6 +1,6 @@ { "name": "offix-offline", - "version": "0.11.0", + "version": "0.12.0", "description": "Offix package that exposes network interfaces", "main": "dist/index.js", "types": "types/index.d.ts", diff --git a/packages/offix-scheduler/package.json b/packages/offix-scheduler/package.json index 82847050f..2ed98a52c 100644 --- a/packages/offix-scheduler/package.json +++ b/packages/offix-scheduler/package.json @@ -1,6 +1,6 @@ { "name": "offix-scheduler", - "version": "0.11.0", + "version": "0.12.0", "description": "", "main": "dist/index.js", "types": "types/index.d.ts", @@ -33,8 +33,8 @@ }, "dependencies": { "idb-localstorage": "0.2.0", - "offix-cache": "0.11.0", - "offix-offline": "0.11.0" + "offix-cache": "0.12.0", + "offix-offline": "0.12.0" }, "peerDependencies": { "graphql": "^0.12.0 || ^0.13.0 || ^14.0.0" diff --git a/packages/react-offix-hooks/package.json b/packages/react-offix-hooks/package.json index 327e674d5..e4b12f18b 100644 --- a/packages/react-offix-hooks/package.json +++ b/packages/react-offix-hooks/package.json @@ -1,6 +1,6 @@ { "name": "react-offix-hooks", - "version": "0.11.0", + "version": "0.12.0", "description": "Use offix-client in react hooks", "keywords": [ "offix", @@ -38,7 +38,7 @@ "dependencies": { "@types/lodash": "4.14.149", "lodash": "4.17.15", - "offix-client": "0.11.0" + "offix-client": "0.12.0" }, "peerDependencies": { "graphql": "^0.12.0 || ^0.13.0 || ^14.0.0", diff --git a/website/versioned_docs/version-0.12.0/android.md b/website/versioned_docs/version-0.12.0/android.md new file mode 100644 index 000000000..3c5575c3d --- /dev/null +++ b/website/versioned_docs/version-0.12.0/android.md @@ -0,0 +1,14 @@ +--- +id: version-0.12.0-android +title: Android +sidebar_label: Android +original_id: android +--- + +Offix supports Android platform by seprate project called Offix Android. + +Offix Android Documentation: +http://android.offix.dev + +Offix Android Github: +https://github.com/aerogear/offix-android \ No newline at end of file diff --git a/website/versioned_docs/version-0.12.0/angular.md b/website/versioned_docs/version-0.12.0/angular.md new file mode 100644 index 000000000..02889f463 --- /dev/null +++ b/website/versioned_docs/version-0.12.0/angular.md @@ -0,0 +1,16 @@ +--- +id: version-0.12.0-angular +title: Angular +sidebar_label: Angular +original_id: angular +--- + +Offix does not provide a specific API for Angular, +However, the regular `offix-client` module can be used for most use cases. + +## Example Angular Application + +We maintain an Angular based example Ionic application that showcases how to use Offix. +For more information please check: + +https://github.com/aerogear/ionic-showcase/ diff --git a/website/versioned_docs/version-0.12.0/getting-started.md b/website/versioned_docs/version-0.12.0/getting-started.md new file mode 100644 index 000000000..0443d113f --- /dev/null +++ b/website/versioned_docs/version-0.12.0/getting-started.md @@ -0,0 +1,130 @@ +--- +id: version-0.12.0-getting-started +title: Getting Started +sidebar_label: Getting Started +original_id: getting-started +--- + +Offix can be used with existing Apollo GraphQL applications as an extension to the Apollo Client. +The Offix Client provides additional methods that support various offline use cases. + +Offix supports multiple platforms by extendable javascript library and individual wrappers for +specific web and cross platform frameworks. + + +## Installing Offix Client + +Using [npm](https://www.npmjs.com/package/offix-client): + +```shell +npm install offix-client +``` + +Or [yarn](https://yarnpkg.com/en/package/offix-client): + +```shell +yarn add offix-client +``` + +## Using the Client inside your application + +To work with Offix you should create Offix client. + +> NOTE: Offix client extends Apollo Client - if you already using Apollo Client you would need to +swap it with the Offix client implementation + +```javascript +import { ApolloOfflineClient } from 'offix-client'; +import { InMemoryCache } from 'apollo-cache-inmemory'; +import { HttpLink } from "apollo-link-http"; + +const config = { + link: new HttpLink({ uri: 'http://example.com/graphql' }) + cache: new InMemoryCache() +}; + +// create the client +const client = new ApolloOfflineClient(config); + +// initialise the client +// Client needs to be installised before any other queries and mutations will happen. +// Please see platform guide to see how this can be done in React, Angular etc. +await client.init(); +``` + +The `ApolloOfflineClient` is a full `ApolloClient` but with some additional features for building offline workflows. + +**Note** `client.init` must be resolved before the application makes any queries/mutations, otherwise the cache and storage mechanisms may not work properly. + +## Offix Client Boost + +The `offix-client-boost` is a convenient way to create a client already bundled with all you nead to work with GraphQL. +Mainly an cache and [Apollo Links](https://www.apollographql.com/docs/link/) subscriptions and file uploads. +Offix Boost is recomended if you trying to build your first GraphQL application and want to have seamless experience. + +```js +import { createClient } from 'offix-client-boost' + +const config = { + httpUrl: 'http://example.com/graphql', + wsUrl: 'ws://example.com/graphql' +} + +const client = await createClient(config) +``` + +## Working with client + +Client will offer the same API as Apollo GraphQL client. +Offix will supply additional methods that help with offline experience. + +The following example shows the `client.offlineMutate()` method which support sending new mutation while the application is considered offline. + +```js +const options = { + mutation: gql` + mutation greeting($name: String!){ + greeting(name: $name) { + body + } + }`, + variables: { + name: 'hello world!' + } +}; + +client.offlineMutate(options).catch((error) => { + // we are offline - lets wait for changes + if(error.offline) { + error.watchOfflineChange().then((result) => { + console.log('mutation was completed after we came back online!', result) + }) + } +}); +``` + +When offline, an error is returned with a reference to a promise which can be used to wait for the mutation to complete. This will happen when the application comes back online. + +`async/await` can be used too. + +```js +try { + await client.offlineMutate(options) +} catch(error) { + if(error.offline) { + const result = await error.watchOfflineChange() + console.log('mutation was completed after we came back online!', result) + } +} +``` + +## Non Offline realated API + +`ApolloOfflineClient` is and extension of the Apollo GraphQL client and can be used with various web and mobile frameworks. +For basic concepts about Apollo GraphQL please refer to the documentation for your own platform. + +For React: +https://www.apollographql.com/docs/react/ + +For Angular: +https://www.apollographql.com/docs/angular/ diff --git a/website/versioned_docs/version-0.12.0/ref-advanced.md b/website/versioned_docs/version-0.12.0/ref-advanced.md new file mode 100644 index 000000000..c1e82130f --- /dev/null +++ b/website/versioned_docs/version-0.12.0/ref-advanced.md @@ -0,0 +1,37 @@ +--- +id: version-0.12.0-cookbooks +title: Cookbooks +sidebar_label: Offline Cookbook +original_id: cookbooks +--- + + +# Offix Cookbook + +This section contains multiple tips and tricks for working with Offix. + + +## Implementing Custom Network Status checks + +To use your own custom network checks, implement the [NetworkStatus](NetworkStatus.ts) + interface which provides two functions; + +```javascript + onStatusChangeListener(callback: NetworkStatusChangeCallback): void; + + isOffline(): boolean; +``` + +This interface can be used to redefine what being offline means. +For example, for some use cases developers might decide to use scheduler only when Wifi connection is available. + +## Querying Data when Offline + +To make sure that data will be available when the devices comes back online, we need to query it with the proper +`fetchPolicy` + +We recommend to always use the default fetch policy and to let Offix to control the flow. However, for advanced use cases +developers can modify their approach. + +For more information see: +https://medium.com/@wtr/data-query-patterns-for-graphql-clients-af66830531aa diff --git a/website/versioned_docs/version-0.12.0/ref-cache.md b/website/versioned_docs/version-0.12.0/ref-cache.md new file mode 100644 index 000000000..94fd58c9c --- /dev/null +++ b/website/versioned_docs/version-0.12.0/ref-cache.md @@ -0,0 +1,158 @@ +--- +id: version-0.12.0-offix-cache +title: Offix Cache +sidebar_label: Client Cache +original_id: offix-cache +--- + +The `offix-cache` package helps developers to manage some of their client's state such as cache, optimistic responses and subscriptions. + +Offix Cache capabilities are available automatically when using `client.offlineMutate`. + +## Optimistic UI + +In Apollo Client, mutation results are not applied to the UI until responses are received from the server. To provide a better user experience, an application may want to update the UI immediately. [Optimistic Responses](https://www.apollographql.com/docs/react/api/react-apollo.html#graphql-mutation-options-optimisticResponse 'Optimistic Responses') are an easy to way to achieve this goal. However, creating individual optimistic responses for each mutation in your application can introduce boilerplate code. offix-cache can automatically create optimistic responses for you to reduce this boilerplate. + +The `createOptimisticResponse` function returns an object which can be passed directly to Apollo Client's mutate function. `createOptimisticResponse` will help to build expected response object from input arguments. +if your mutation returns different values you will still need to build it manually. + +```javascript +import { createOptimisticResponse } from 'offix-cache'; + +const optimisticResponse = createOptimisticResponse({ + mutation: ADD_TASK, + variables: { some_key: 'some_value' }, + operationType: 'add', + returnType: 'Task', + idField: 'id' +}); + +apolloClient.mutate({ + mutation: ADD_TASK, + optimisticResponse: optimisticResponse +}); +``` + +## Mutation Cache Helpers + +`offix-cache` provides a mechanism to automatically update the client cache based on the result returned by a mutation. The `createMutationOptions` function returns a `MutationOptions` object compatible with Apollo Client's mutate. + +```javascript +const { createMutationOptions, CacheOperation } = require('offix-cache'); + +const mutationOptions = { + mutation: ADD_TASK, + variables: { + title: 'item title' + }, + updateQuery: { + query: GET_TASKS, + variables: { + filterBy: 'some filter' + } + }, + returnType: 'Task', + operationType: CacheOperation.ADD, + idField: 'id' +}; +``` + +We can also provide more than one query to update in the cache by providing an array to the `updateQuery` parameter: + +```javascript +const mutationOptions = { + ... + updateQuery: [ + { query: GET_TASKS, variables: {} } + ], + ... +}; +``` + +Where `mutationOptions` is either of the two objects shown above, we can then pass this object to our mutate function. + +```javascript +const options = createMutationOptions(mutationOptions); + +apolloClient.mutate(options); +``` + +> NOTE: Cache helpers currently support only GraphQL Queries that return arrays. +> For example `getUsers():[User]`. +> When working with single objects returned by Queries we usually do not need use any helper as Query will be updated automatically on every update + +## Subscription Helpers + +`offix-cache` provides a subscription cache update method helper which can generate the necessary options to be used with Apollo Client's `subscribeToMore` function. + +To use this helper, we first need to create some options. These options should take the folowing form: + +```javascript +const { CacheOperation } = require('offix-cache'); + +const options = { + subscriptionQuery: TASK_ADDED_SUBSCRIPTION, + cacheUpdateQuery: GET_TASKS, + operationType: CacheOperation.ADD +}; +``` + +This options object will be used to inform the subscription helper that for every data object received because of the `TASK_ADDED_SUBSCRIPTION` the `GET_TASKS` query should also be kept up to date in the cache. + +We can then create the required cache update functions in the following way: + +```javascript +const { createSubscriptionOptions } = require('offix-cache'); + +const subscriptionOptions = createSubscriptionOptions(options); +``` + +To use this helper we then pass this `subscriptionOptions` variable to the `subscribeToMore` function of our `ObservableQuery`. + +```javascript +const query = + apolloClient.watchQuery < + AllTasks > + { + query: GET_TASKS + }; + +query.subscribeToMore(subscriptionOptions); +``` + +The cache will now be kept up to date with automatic data deduplication being performed. + +### Multiple Subscriptions + +`offix-cache` also provides the ability to automatically call `subscribeToMore` on your `ObservableQuery`. This can be useful in a situation where you may have multiple subscriptions which can affect one single query. For example, if you have a `TaskAdded`, `TaskDeleted` and a `TaskUpdated` subscription you would need three separate `subscribeToMore` function calls. This can become tedious as your number of subscriptions grow. To combat this, we can use the `subscribeToMoreHelper` function from offix-cache to automatically handle this for us by passing it an array of subscriptions and their corresponding queries which need to be updated. + +```javascript +const { CacheOperation } = require('offix-cache'); + +const addOptions = { + subscriptionQuery: TASK_ADDED_SUBSCRIPTION, + cacheUpdateQuery: GET_TASKS, + operationType: CacheOperation.ADD +}; + +const deleteOptions = { + subscriptionQuery: TASK_DELETED_SUBSCRIPTION, + cacheUpdateQuery: GET_TASKS, + operationType: CacheOperation.DELETE +}; + +const updateOptions = { + subscriptionQuery: TASK_UPDATED_SUBSCRIPTION, + cacheUpdateQuery: GET_TASKS, + operationType: CacheOperation.REFRESH +}; + +const query = + client.watchQuery < + AllTasks > + { + query: GET_TASKS + }; + +subscribeToMoreHelper(query, [addOptions, deleteOptions, updateOptions]); +``` diff --git a/website/versioned_docs/version-0.12.0/ref-configuration.md b/website/versioned_docs/version-0.12.0/ref-configuration.md new file mode 100644 index 000000000..5c82cc918 --- /dev/null +++ b/website/versioned_docs/version-0.12.0/ref-configuration.md @@ -0,0 +1,36 @@ +--- +id: version-0.12.0-client-configuration +title: Client Configuration +sidebar_label: Client Configuration +original_id: client-configuration +--- + +## offix-client + +### `ApolloOfflineClient` + +`ApolloOfflineClient` extends the `ApolloClient` class. Check the [`ApolloClient` constructor docs](https://www.apollographql.com/docs/react/v2.5/api/apollo-client/#apolloclient) to see the options that can be passed. + +There are some additional options specific to `ApolloOfflineClient`. + +* `cacheStorage` - The [PersistentStore](https://github.com/aerogear/offix/blob/master/packages/offix-offline/src/offline/storage/PersistentStore.ts) you want your client to use for persisting the Apollo Cache (Uses indexeddb by default). +* `offlineStorage` - The [PersistentStore](https://github.com/aerogear/offix/blob/master/packages/offix-offline/src/offline/storage/PersistentStore.ts) you want your client to use for persisting offline operations in the offline queue (Uses indexeddb by default). +* `networkStatus` - [NetworkStatus](https://github.com/aerogear/offix/blob/master/packages/offix-offline/src/offline/network/NetworkStatus.ts) Interface for detecting changes in network status. (Uses browser networking APIs by default) +* `offlineQueueListener` - [ApolloOfflineQueueListener](./ref-offline.md#listening-for-events) User provided listener that contains a set of methods that are called when certain events occur in the queue. +* `conflictProvider` - [ObjectState](./ref-conflict-server.md#implementing-custom-conflict-resolution) Interface that defines how object state is progressed. This interface needs to match state provider supplied on server. +* `conflictStrategy` - [ConflictResolutionStrategy](https://github.com/aerogear/offix/blob/master/packages/offix-conflicts-client/src/strategies/ConflictResolutionStrategy.ts)interface used on the client to resolve conflicts. The [default strategy](https://github.com/aerogear/offix/blob/master/packages/offix-conflicts-client/src/strategies/strategies.ts) merges client changes onto the server changes. +* `mutationCacheUpdates` - [CacheUpdates](./ref-offline.md#global-update-functions) Cache updates functions for your mutations. Argument allows to restore optimistic responses on application restarts. +* `retryOptions` - The options to configure how failed offline mutations are retried. See [`apollo-link-retry`](https://www.apollographql.com/docs/link/links/retry/). + +## offix-client-boost + +### `createClient` + +`createClient` accepts all of the `ApolloOfflineClient` options described above as well as the ones listed below. + +* `httpUrl` (required) - The URL of the GraphQL server +* `wsUrl` (required) - The URL of the websocket endpoint for subscriptions +* `cache` - The Apollo [InMemoryCache](https://www.apollographql.com/docs/react/caching/cache-configuration/) that will be used. (creates one by default). +* `authContextProvider` - An object or an `async` function that returns an [`AuthContext`](https://github.com/aerogear/offix/blob/master/packages/offix-client-boost/src/auth/AuthContexrProvider.ts) object with authentication headers that will be passed in GraphQL requests and in the `connectionParams` of websocket connections. +* `fileUpload` - If set to `true`, GraphGL file uploads will be enabled and supported. (default is `false`) +* `websocketClientOptions` - Options for the websocket client used for subscriptions. See [subscriptions-transport-ws](https://www.npmjs.com/package/subscriptions-transport-ws) \ No newline at end of file diff --git a/website/versioned_docs/version-0.12.0/ref-offline.md b/website/versioned_docs/version-0.12.0/ref-offline.md new file mode 100644 index 000000000..527a44a08 --- /dev/null +++ b/website/versioned_docs/version-0.12.0/ref-offline.md @@ -0,0 +1,192 @@ +--- +id: version-0.12.0-offline-client +title: Offline Support +sidebar_label: Offline Client +original_id: offline-client +--- + +Offix provides first class support for performing GraphQL operations while offline. Mutations are held in a queue that is configured to hold requests while the client is offline. When the client goes offline for long periods of time they will still be able negotiate local updates with the server state thanks to powerful conflict resolution strategies. + +Offix-client offers a comprehensive set of features to perform data operations when offline. Thanks to the offline mutation store users can stage their changes to be replicated back to the server when they return online. + +Please follow chapters bellow for more information. + +## Querying local cache + +By default queries are cached based on the type and id field, and the results of performed queries are cached as well and they will be available when the client is offline. + +Because of this, when mutations that can change query results are performed, the `refetchQueries` or update options of the mutate method should be used to ensure the local cache is kept up to date. + +Offix makes your cache simple to manage, with out of the box cache helpers in `offix-cache` or by automatically wrapping these helpers in offix-client through the `OfflineClient` class. + +To use the `offlineMutate` function, we first need to create our `MutationHelperOptions` object. This is an extension of Apollo's MutationOptions. + +```javascript +const { CacheOperation } = require('offix-cache'); + +const mutationOptions = { + mutation: ADD_TASK, + variables: { + title: 'item title' + }, + updateQuery: { + query: GET_TASKS, + variables: { + filterBy: 'some filter' + } + }, + returnType: 'Task', + operationType: CacheOperation.ADD, + idField: 'id' +}; +``` + +We can also provide more than one query to update in the cache by providing an array to the `updateQuery` parameter: + +```javascript + +const mutationOptions = { + ... + updateQuery: [ + { query: GET_TASKS, variables: {} } + ] + , + ... +}; +``` + +We then simply pass this object to `offlineMutate` and our cache is automatically kept up to date. + +```javascript +client.offlineMutate(mutationOptions); +``` + +If you do not wish to use offline capabilities of offix for some mutations, use the `client.mutate` function. `client.mutate` will not react to the changes in the network state. Requests are not enqueued into offline queue. + +## Offline Workflow + +When offline `client.offlineMutate` function will return immediately after is called. +Returned promise will resolve into error (`catch` method is triggered). +Developers can detect if error is an offline error and watch for change to be replicated back to server. + +Example: + +```javascript +client.offlineMutate(...).catch((error)=> { + // 1. Detect if this was an offline error + if (error.offline){ + // 2. We can still track when offline change is going to be replicated. + error.watchOfflineChange().then(...) + } +}); +``` + +> Note: Additionally to watching individual mutations framework offers global offline listener that can be supplied when creating client. + +## Global Update Functions + +Apollo client holds all mutation parameters in memory. An offline Apollo client will continue to store mutation parameters and once online, it will restore all mutations to memory. Any Update Functions that are supplied to mutations cannot be cached by an Apollo client resulting in the loss of all optimisticResponses after a restart. Update functions supplied to mutations cannot be saved in the cache. As a result, all optimisticResponses will disappear from the application after a restart and it will only reappear when the Apollo client becomes online and successfully syncs with the server. + +To prevent the loss of all optimisticResponses after a restart, you can configure the `update` functions to restore all optimisticResponses. + +```javascript +const updateFunctions = { + // Can contain update functions from each component + ...ItemUpdates, + ...TasksUpdates +}; + +let config = { + mutationCacheUpdates: updateFunctions +}; +``` + +## Making modifications when offline + +Client provides queue that stores mutations performed when offline. +By default queue saves data in storage to be available after application restarts. +Queue will hold requests until application will come back online. + +Developers can adjust how queue will process new mutations by supplying custom `NetworkStatus` implementation. + +## Listening for Events + +It is possible to provide `offlineQueueListener` in config to be notified about offline related events: + +```javascript +client.registerOfflineEventListener({ + onOperationEnqueued(operation) { + // called when operation was placed on the queue + }, + onOperationFailure: (operation) => { + // called when the operation failed + }, + onOperationSuccess: (operation) => { + // called when the operation was fulfilled + }, + onOperationRequeued: (operation) => { + // called when an operation was loaded in from storage and placed back on the queue + // This would happen across app restarts + }, + queueCleared() { + // called when all operations are fulfilled and the queue is cleared + } +}); +``` + +Below is an example `ApolloQueueEntryOperation` object. + +```js +{ + qid: 'client:abc123' + op: { + context: { + operationName: 'createItem', + conflictBase: undefined, + idField: 'id', + returnType: 'Item' + }, + mutation: , + optimisticResponse: , + variables: + } +} +``` + +`ApolloQueueEntryOperation` objects have two top level fields: + +* `qid` - Queue ID. This ID is randomly generated and mostly used by the `OfflineQueue`. +* `op` - The operation. In `offix-client` It's of type `MutationOptions`, the options object passed into `client.offlineMutate` with some extra metadata set by `offix-client`. + +## Cache + +Apollo Offline Client strongly leverages Apollo Cache. +Please follow documentation for more information about caching in Apollo GraphQL + +https://www.apollographql.com/docs/react/advanced/caching.html + +### Querying your data + +Cache is used to hold data that can be fetched when client is offline. +To effectively work with cache users can use `cache-first` fetchPolicy +when performing queries. This policy will try to use local cache in +situations when cache was already populated with the server side data. + +```javascript +return ( + this.apollo.watchQuery < + YourType > + { + query: YOUR_QUERY, + fetchPolicy: 'cache-first' + } +); +``` + +Cache is going to be refueled by subscriptions, pooling or regular queries happening in UI. + +## Designing your types + +When designing your GraphQL schema types `id` field will be always required. +We also expect that id will be always queried back from server. +Library will perform business logic assuming that `id` field will be supplied and returned from server. Without this field some offline functionalities will not work properly. diff --git a/website/versioned_docs/version-0.12.0/ref-release-notes.md b/website/versioned_docs/version-0.12.0/ref-release-notes.md new file mode 100644 index 000000000..9520976a0 --- /dev/null +++ b/website/versioned_docs/version-0.12.0/ref-release-notes.md @@ -0,0 +1,344 @@ +--- +id: version-0.12.0-release-notes +title: What is new in Offix +sidebar_label: Release notes +original_id: release-notes +--- + +# 0.12.0 + +The 0.12.0 release brings a new `offix-client-boost` package. This package is a batteries-included wrapper around the `offix-client` that brings +everything needed to get started with GraphQL quickly. This includes a cache and support for subscriptions and file uploads. + +Check out the new documentation at [offix.dev](https://offix.dev). + +# 0.11.0 + +The 0.11.0 release simplifies the creation of offline clients. + +## Breaking Changes + +### OfflineClient replaced with ApolloOfflineClient + +The `OfflineClient` class has been replaced with `ApolloOfflineClient`. `OfflineClient` was a wrapper that would internally create an `ApolloClient` which could be accessed after initialization. The flow looked something like this. + +```js +const offlineClient = new OfflineClient(options) +const apolloClient = await offlineClient.init() +``` + +The new `ApolloOfflineClient` directly `extends` `ApolloClient` which makes initialization simpler and more flexible as all of the standard Apollo Client options are supported. + +Initialization is slightly different, mainly, and `ApolloLink` must be passed that can connect with the GraphQL server. See the example code below. + +```js +import { ApolloOfflineClient } from 'offix-client' + +const link = new HttpLink({ uri: 'http://localhost/graphql' }) + +const config = { + link, + typeDefs, + resolvers, + ...otherOptions +} + +const client = new ApolloOfflineClient(config) +await client.init() +... + +// `client` is an ApolloClient so you can call all of the methods you'd expect +client.query(...) +client.mutate(...) +client.offlineMutate(...) +``` + +# 0.10.0 + +The `0.10.0` release builds upon the `0.9.0` release and adds two new packages. `offix-scheduler` and `offix-conflicts-client` + +## offix-scheduler + +`offix-scheduler` now holds the core functionalities of the Offix project. That includes queueing, scheduling, persistence and fulfilment of offline operations. + +`offix-scheduler` can and will be used to create new clients in the future that are not based on Apollo Client. Clients we are investigating are URQL (see https://github.com/aerogear/offix/issues/235), Relay (see https://github.com/aerogear/offix/issues/236) and plain HTTP/REST. `offix-client` now uses `offix-scheduler` under the hood. + +There have been no functional changes to `offix-client` and everything should remain the same. + +## offix-conflicts-client + +The `offix-conflicts-client` package contains all the interfaces and current implementations for client side pluggable conflict detection and resolution. Right now this package is used in to deliver the conflict capabilities in `offix-client` and it could also be used by package maintainers to implement conflict capabilities in new clients also. It is not important for application developers looking to use `offix-client` + +# 0.9.0 + +The `0.9.0` release is a significant refactor of the internals of Offix and introduces a couple of breaking changes to the end user API. + +* We have a new documentation site available at [offix.dev](https://offix.dev). Special thanks to [@LakshanKarunathilake](https://github.com/LakshanKarunathilake) for the complete overhaul. +* All queueing, scheduling, persistence, and replaying of offline operations now happens outside of the Apollo Link chain. Instead we use a much more generic queueing mechanism that opens the door to great flexibility. +* It paves the way for new features in the future. Most importantly, the ability to use Offix with other GraphQL clients, or even with regular RESTful clients (and more). +* The internal architecture of Offix is drastically simplified. It is much easier to understand, maintain and test. + +With this release, `OfflineClient` behaves mostly the same way as it has before but there were a couple of breaking changes which are outlined below. + +## Background + +Previous versions of Offix relied heavily on something called [Apollo Link](https://www.apollographql.com/docs/link/overview/) which is essentially chain of "middleware" functions that can modify the behaviour and results from calls like `ApolloClient.mutate()` and `ApolloClient.query()`. Most of the underlying queueing, scheduling, persistence and replaying of offline mutations done by Offix happened inside the of the Apollo Link chain. This approach seemed like a good idea, but over time we have realised it made things difficult to maintain and it kept us limited in the features we could provide. + +## Breaking Changes + +### `client.offlineMutation` has been deprecated in favour of `client.offlineMutate` + +It didn't make sense to have a `mutate` and `offlineMutation` method. `offlineMutation` has been deprecated in favour of `offlineMutate`. `offlineMutation` can still be used, but it logs a deprecation warning to the console and it will be removed in the next release. + +**Suggestion:** Change all uses of `client.offlineMutation` to `client.offlineMutate` + +### client.mutate no longer does any offline scheduling + +A side effect of our Apollo Link architecture was that `client.mutate()` would also schedule operations while offline (as well as `client.offlineMutation`). Using `client.mutate()` for offline operations was never recommended but it was possible. This is no longer the case. + +**Suggestion:** any places where you intentionally have offline behaviour using `client.mutate()` should use `client.offlineMutate()` instead. + +### Removed `@OnlineOnly` directive + +Because `client.mutate()` does not schedule offline operations anymore, the `@OnlineOnly` directive is no longer useful and has been completely removed. + +**Suggestion:** remove all instances of the `@OnlineOnly` directive and ensure mutations that used it are called with `client.mutate()`. + +### Errors from `client.offlineMutate()` do not have `networkError` property. + +Errors originating from the Apollo Link chain are found on `error.networkError`. +This led to checks in application code such as `if (error.networkError.offline)`, +where `error.networkError` is the actual error thrown. + +This is no longer the case. Now the everything is found on the top level `error` object. +See the example below: + +```js +const options = { + mutation: gql` + mutation greeting($name: String!){ + greeting(name: $name) { + body + } + }`, + variables: { + name: 'hello world!' + } +}; + +client.offlineMutate(options).catch((error) => { + // This used to be `if (error.networkError.offline)` + if(error.offline) { + // This used to be `error.networkError.watchOfflineChange()` + error.watchOfflineChange().then(...) + } +}); +``` + +This is the same for local conflict errors: + +```js +client.offlineMutate(options).catch((error) => { + // This used to be `if (error.networkError.localConflict)` + if (error.localConflict) { + // handle local conflict + } +}); +``` + +**Suggestion:** review all code where `error.networkError.` is being accessed and change it to `error.` + +### `OfflineQueue` and `OfflineStore` are generic (TypeScript Users Only) + +The `OfflineQueue` and `OfflineStore` classes and some related interfaces have been refactored to handle generic objects. TypeScript users may experience compilation issues if their application references these types. + +New types have been added to `offix-client` for Apollo specific usage. + +**Suggestion:** Migrate the following references: + +* `OfflineQueue` becomes `ApolloOfflineQueue` +* `OfflineStore` becomes `ApolloOfflineStore` +* `OfflineQueueListener` becomes `ApolloOfflineQueueListener` +* `IResultProcessor` becomes `ApolloIResultProcessor` + +### New Arguments passed to registerOfflineEventListener functions + +`registerOfflineEventListener` registers functions that are called on events originating from the `OfflineQueue`. + +```js +client.registerOfflineEventListener({ + onOperationEnqueued(operation) { + // called when operation was placed on the queue + }, + onOperationFailure: (operation) => { + // called when the operation failed + }, + onOperationSuccess: (operation) => { + // called when the operation was fulfilled + }, + onOperationRequeued: (operation) => { + // called when an operation was loaded in from storage and placed back on the queue + // This would happen across app restarts + }, + queueCleared() { + // called when all operations are fulfilled and the queue is cleared + } +}); +``` + +In previous versions of `offix-client`, these functions had an [Apollo Operation](https://www.apollographql.com/docs/link/overview/) object passed to them. Because Offix no longer uses Apollo Link, this is no longer the case. Instead an [`ApolloQueueEntryOperation`](https://github.com/aerogear/offix/blob/b1e42936ff05a3d21c93795eadba07217bd13f23/packages/offix-client/src/apollo/ApolloOfflineClient.ts#L53) is passed. See the example object below. + +``` +{ + qid: 'client:abc123' + op: { + context: { + operationName: 'createItem', + conflictBase: undefined, + idField: 'id', + returnType: 'Item' + }, + mutation: , + optimisticResponse: , + variables: + } +} +``` + +`ApolloQueueEntryOperation` objects have two top level fields: + +* `qid` - Queue ID. This ID is randomly generated and mostly used by the `OfflineQueue`. +* `op` - The operation. In `offix-client` It's of type `MutationOptions`, the options object passed into `client.offlineMutate` with some extra metadata set by `offix-client`. + +**Suggestion:** review any code where `registerOfflineEventListener` is used and refactor the listener functions to use the new data structure being passed. + +### Operations Stored in OfflineStore are not backwards compatible + +Because of the architectural changes to Offix, the objects stored in the OfflineQueue and OfflineStore are different. Previously, the queue and storage mechanisms were based around the [Apollo Operation](https://www.apollographql.com/docs/link/overview/). + +`offix-client` now queues and stores objects based off the `MutationOptions` object. Unfortunately these changes were not backwards compatible. Existing applications using `offix-client` that have pending offline operations will not be able to load and requeue those operations after an upgrade to the latest version. The data will still exist in the store and it will be accessible manually. Developers will have to migrate the data themselves. **Data in the Apollo Cache is not affected.** We hope this issue will affect very few users if any at all. + +To ensure this doesn't happen in future, we have implemented versioning and new serialize/deserialize interfaces that will allow our storage mechanism to handle these types of upgrades/migrations. + +**Suggestions:** + +* Ensure users have no pending offline operations before administering the update. +* Manually migrate the data using custom application code. +* If it's not critical, users can re-enter the data or redo their offline operations. + +## Features + +### New Documentation Website! + +Our documentation website has been rebuilt using Docusaurus. Special thanks to [@LakshanKarunathilake](https://github.com/LakshanKarunathilake) for the complete overhaul. + +### OfflineQueue is directly accessible on the client + +`client.queue` is now directly accessible. This opens up the possibility for your application to directly see the operations in the queue. It also means you can manually call `client.queue.forwardOperations()` to execute all operations in the queue. + +### OfflineClient accepts user provided `InMemoryCache` + +It is now possible to pass your own `InMemoryCache` into `OfflineClient`. This makes it possible to configure things like cache redirects. + +```js +const cache = new InMemoryCache(yourCacheConfig); + +const offlineClient = new OfflineClient({ + httpUrl: 'https://example.com', + cache +}); +``` + +### OfflineClient enables wiping out cache using persistor interface + +`offlineClient.persitor.purge()` method will wipe entire persistence layer for Apollo cache. + +> NOTE: InMemoryCache needs to be wiped out as well along with the client. Please execute `offlineClient.cache.rest()` + +# 0.8.0 + +## Features + +### Offix React Hooks Alpha released + +Offix React Hooks provides helpers for using offix within React and React Native. +Please refer to package README for more information + +### Ability to customize Apollo Link chain + +`OffixClientConfig.terminatingLink` allows to customize client by adding additional links +for handling authentication, network requests etc. + +### New way to access Apollo Client + +`OfflineClient.apolloClient` is now public. This means `apolloClient` is directly accessible after `OfflineClient.init()`. + +## Breaking changes + +### Changes for Subscriptions and File Uploads + +Subscriptions and file uploads were removed from the main library. +Developers can still configure Subscription access directly int their application by +creating Apollo link acording to documentation and passing `OffixClientConfig.terminatingLink` + +### 0.7.1 + +#### Offline operations persist optimistic response + +Offline operations will now cache update functional and automatically apply optimistic response +`OffixClientConfig.mutationCacheUpdates` is still required to see optimistic responses after application restart. + +#### watchOfflineChange returns mutation result + +We have discovered bug where `watchOfflineChange` method from `OfflineError` was missing mutation results. +This is now fixed so your UI can instantly watch for offline chances and render when succeeded. + +### 0.7.0 + +#### Support Apollo 2.6.x + +Apollo Client 2.6.x with new typings is now supported. + +#### Extended conflict support + +New conflict implementation requires changes on both client and server. +On server we have changed conflict detection mechanism to single method. +Server side conflict resolution was removed due to the fact that we could not provide +reliable diff source without separate store. + +##### Server side implementation: + +```javascript + const conflictError = conflictHandler.checkForConflict(greeting, args); + if (conflictError) { + throw conflictError; + } +} +``` + +##### Client side implementation: + +Client side implementation now requires users to apply `returnType` to context when performing a mutation. +Conflict interface now has an additional method `mergeOccured` that will be triggered when a conflict was resolved without data loss. + +Please refer to documentation for more details. + +#### Breaking changes + +##### DataSync Config renamed + +`DataSyncConfig` interface was renamed to `OffixClientConfig`. +Please review if your configuration still conforms to the new interface. + +##### Cache Helper Interface + +Cache Helper interface now will now accept object instead of individual parameters: + +```javascript +const updateFunction = getUpdateFunction({ + mutationName, + idField, + operationType, + updateQuery +}); +``` diff --git a/website/versioned_sidebars/version-0.12.0-sidebars.json b/website/versioned_sidebars/version-0.12.0-sidebars.json new file mode 100644 index 000000000..c7404a59f --- /dev/null +++ b/website/versioned_sidebars/version-0.12.0-sidebars.json @@ -0,0 +1,28 @@ +{ + "version-0.12.0-docs": { + "Getting Started": [ + "version-0.12.0-getting-started", + "version-0.12.0-client-configuration" + ], + "OffixClient": [ + "version-0.12.0-offline-client", + "version-0.12.0-offix-cache", + "version-0.12.0-conflict-client", + "version-0.12.0-conflict-server" + ], + "Platforms": [ + "version-0.12.0-react", + "version-0.12.0-react-native", + "version-0.12.0-angular", + "version-0.12.0-ionic", + "version-0.12.0-android" + ], + "Advanced topics": [ + "version-0.12.0-cookbooks", + "version-0.12.0-offix-scheduler-introduction" + ], + "Releases": [ + "version-0.12.0-release-notes" + ] + } +} diff --git a/website/versions.json b/website/versions.json index 9e1c7d5dc..8c868efe1 100644 --- a/website/versions.json +++ b/website/versions.json @@ -1,4 +1,5 @@ [ + "0.12.0", "0.11.0", "0.10.0", "0.9.0",