Skip to content

Commit

Permalink
Fix outbox processing (#34)
Browse files Browse the repository at this point in the history
* Change outbox logic

* Refactor

* Handle missing optimisticResponse

* Fix typo. Add comment

* Add changelog entry
  • Loading branch information
manueliglesias authored Feb 5, 2018
1 parent 1c1f738 commit 4b9d774
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 92 deletions.
5 changes: 4 additions & 1 deletion packages/aws-appsync/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# Changelog

### vNext
- Handle missing optimisticResponse [PR#34](https://github.com/awslabs/aws-mobile-appsync-sdk-js/pull/34)

### 1.0.7
- Make offline support optional [PR#33](https://github.com/awslabs/aws-mobile-appsync-sdk-js/pull/33)
- Make offline support optional [PR#33](https://github.com/awslabs/aws-mobile-appsync-sdk-js/pull/33)
7 changes: 6 additions & 1 deletion packages/aws-appsync/src/cache/offline-cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ export default class MyCache extends InMemoryCache {

this.store.dispatch(writeThunk(data));
}

reset() {
this.store.dispatch(writeThunk({}));

return super.reset();
}
};

const writeThunk = (payload) => (dispatch) => {
Expand All @@ -63,7 +69,6 @@ export const reducer = () => ({
switch (type) {
case WRITE_ACTION:
return {
...state,
...normCache
};
default:
Expand Down
40 changes: 17 additions & 23 deletions packages/aws-appsync/src/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,37 +91,31 @@ class AWSAppSyncClient extends ApolloClient {
* @param {MutationOptions} options
* @returns {Promise<FetchResult>}
*/
mutate(options, extraOpts = {}) {
const { mutation, variables: mutationVariables, optimisticResponse, context: origContext = {} } = options;
const { AASContext: { ...origASAContext = {} } = {} } = origContext;

const operationDefinition = getOperationDefinition(mutation);
const varsInOperation = variablesInOperation(operationDefinition);
const variables = Array.from(varsInOperation).reduce((obj, key) => {
obj[key] = mutationVariables[key];
return obj;
}, {});

// refetchQueries left out intentionally when !doIt so we don't run them twice
const { refetchQueries, ...otherOptions } = options;
const { doIt } = origASAContext;
mutate(options) {
const { refetchQueries, context: origContext = {}, ...otherOptions } = options;
const { AASContext: { doIt = false, ...restAASContext } = {} } = origContext;

const context = {
...origContext,
AASContext: {
...origASAContext,
mutation,
variables,
optimisticResponse,
refetchQueries,
},
doIt,
...restAASContext,
...(!doIt ? { refetchQueries } : {}),
}
};

return super.mutate({
const { optimisticResponse, variables } = otherOptions;
const data = optimisticResponse &&
(typeof optimisticResponse === 'function' ? { ...optimisticResponse(variables) } : optimisticResponse);

const newOptions = {
...otherOptions,
refetchQueries: doIt ? refetchQueries : undefined,
optimisticResponse: data,
...(doIt ? { refetchQueries } : {}),
context,
});
}

return super.mutate(newOptions);
}

};
Expand Down
138 changes: 71 additions & 67 deletions packages/aws-appsync/src/link/offline-link.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
* KIND, express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
import { readQueryFromStore, defaultNormalizedCacheFactory } from "apollo-cache-inmemory";
import { ApolloLink, Observable } from "apollo-link";
import { ApolloLink, Observable, Operation } from "apollo-link";
import { getOperationDefinition, getOperationName } from "apollo-utilities";
import { Store } from 'redux';
import { Store } from "redux";

import { NORMALIZED_CACHE_KEY } from "../cache";

Expand Down Expand Up @@ -46,17 +46,25 @@ export class OfflineLink extends ApolloLink {
}

if (isMutation) {
const data = processMutation(operation, this.store);
const { cache, optimisticResponse, AASContext: { doIt = false } = {} } = operation.getContext();

// If we got data, it is the optimisticResponse, we send it to the observer
// Otherwise, we allow the mutation to continue in the link chain
if (data) {
observer.next({ data });
observer.complete();
if (!doIt) {
if (!optimisticResponse) {
console.warn('An optimisticResponse was not provided, it is required when using offline capabilities.');

return () => null;
} else {
// console.log('Processing mutation');
if (!online) {
throw new Error('Missing optimisticResponse while offline.');
}

// offline muation without optimistic response is processed immediately
} else {
const data = enqueueMutation(operation, this.store, observer);

observer.next({ data });
observer.complete();

return () => null;
}
}
}

Expand All @@ -73,6 +81,11 @@ export class OfflineLink extends ApolloLink {
}
}

/**
*
* @param {Operation} operation
* @param {Store} theStore
*/
const processOfflineQuery = (operation, theStore) => {
const { [NORMALIZED_CACHE_KEY]: normalizedCache = {} } = theStore.getState();
const { query, variables } = operation;
Expand All @@ -88,39 +101,56 @@ const processOfflineQuery = (operation, theStore) => {
return data;
}

const processMutation = (operation, theStore) => {
const { AASContext } = operation.getContext();
const { mutation, variables, optimisticResponse, refetchQueries, doIt } = AASContext;

if (doIt) {
return;
}

const data = optimisticResponse ?
typeof optimisticResponse === 'function' ?
{ ...optimisticResponse(variables) } :
optimisticResponse
: null;

// console.log('Queuing mutation');
theStore.dispatch({
type: 'SOME_ACTION',
payload: {},
meta: {
offline: {
effect: {
mutation,
variables,
refetchQueries,
doIt: true,
},
commit: { type: 'SOME_ACTION_COMMIT', meta: null },
rollback: { type: 'SOME_ACTION_ROLLBACK', meta: null },
/**
*
* @param {Operation} operation
* @param {Store} theStore
*/
const enqueueMutation = (operation, theStore, observer) => {
const { query: mutation, variables, update } = operation;
const { cache, optimisticResponse, AASContext: { doIt = false, refetchQueries } = {} } = operation.getContext();

setImmediate(() => {
theStore.dispatch({
type: 'SOME_ACTION',
payload: {},
meta: {
offline: {
effect: {
mutation,
variables,
refetchQueries,
update,
optimisticResponse,
},
commit: { type: 'SOME_ACTION_COMMIT', meta: null },
rollback: { type: 'SOME_ACTION_ROLLBACK', meta: null },
}
}
}
});
});

return data;
return optimisticResponse;
}

/**
*
* @param {*} client
* @param {*} effect
* @param {*} action
*/
export const offlineEffect = (client, effect, action) => {
const doIt = true;
const { ...otherOptions } = effect;

const context = { AASContext: { doIt } };

const options = {
...otherOptions,
context,
};

return client.mutate(options);
}

export const reducer = () => ({
Expand All @@ -145,32 +175,6 @@ export const reducer = () => ({
}
});

/**
*
* @param {*} client
* @param {*} effect
* @param {*} action
*/
export const offlineEffect = (client, effect, action) => {
const { type } = action;
const { mutation, variables, refetchQueries, doIt } = effect;

const context = {
AASContext: {
doIt,
},
};

const options = {
mutation,
variables,
refetchQueries,
context,
};

return client.mutate(options);
}

export const discard = (fn = () => null) => (error, action, retries) => {
const { graphQLErrors = [] } = error;
const conditionalCheck = graphQLErrors.find(err => err.errorType === 'DynamoDB:ConditionalCheckFailedException');
Expand Down

0 comments on commit 4b9d774

Please sign in to comment.