Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make effect delay and network detection configurables #388

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ node_modules
lib
*.tgz
lerna-debug.log
.npmrc
16 changes: 16 additions & 0 deletions packages/aws-appsync-react/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

<a name="1.2.7"></a>
## [1.2.7](https://github.com/awslabs/aws-mobile-appsync-sdk-js/compare/aws-appsync-react@1.2.6...aws-appsync-react@1.2.7) (2019-03-12)




**Note:** Version bump only for package aws-appsync-react

<a name="1.2.6"></a>
## [1.2.6](https://github.com/awslabs/aws-mobile-appsync-sdk-js/compare/aws-appsync-react@1.2.5...aws-appsync-react@1.2.6) (2019-01-11)




**Note:** Version bump only for package aws-appsync-react

<a name="1.2.5"></a>
## [1.2.5](https://github.com/awslabs/aws-mobile-appsync-sdk-js/compare/aws-appsync-react@1.2.4...aws-appsync-react@1.2.5) (2018-12-12)

Expand Down
4 changes: 2 additions & 2 deletions packages/aws-appsync-react/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "aws-appsync-react",
"version": "1.2.5",
"version": "1.2.7",
"main": "lib/index.js",
"license": "SEE LICENSE IN LICENSE",
"description": "AWS Mobile AppSync SDK for JavaScript - React and React Native components",
Expand All @@ -27,7 +27,7 @@
"devDependencies": {
"@types/graphql": "0.12.4",
"@types/react": "^16.0.25",
"aws-appsync": "^1.7.0",
"aws-appsync": "^1.7.2",
"react": "^16.1.1",
"react-apollo": "^2.1.9",
"react-native": "^0.50.3",
Expand Down
27 changes: 27 additions & 0 deletions packages/aws-appsync/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,33 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

<a name="1.7.2"></a>
## [1.7.2](https://github.com/awslabs/aws-mobile-appsync-sdk-js/compare/aws-appsync@1.7.1...aws-appsync@1.7.2) (2019-03-12)


### Bug Fixes

* **auth:** Remove temporary variables (starting with '@@') before signing ([#347](https://github.com/awslabs/aws-mobile-appsync-sdk-js/issues/347)) ([920b47d](https://github.com/awslabs/aws-mobile-appsync-sdk-js/commit/920b47d)), closes [#354](https://github.com/awslabs/aws-mobile-appsync-sdk-js/issues/354)
* **deltaSync:** Make sure query manager is initialized ([13a2dec](https://github.com/awslabs/aws-mobile-appsync-sdk-js/commit/13a2dec)), closes [#893237](https://github.com/awslabs/aws-mobile-appsync-sdk-js/issues/893237)




<a name="1.7.1"></a>
## [1.7.1](https://github.com/awslabs/aws-mobile-appsync-sdk-js/compare/aws-appsync@1.7.0...aws-appsync@1.7.1) (2019-01-11)


### Bug Fixes

* **deltasync:** Fix error when baseQuery is not specified ([#320](https://github.com/awslabs/aws-mobile-appsync-sdk-js/issues/320)) ([8dbe01d](https://github.com/awslabs/aws-mobile-appsync-sdk-js/commit/8dbe01d))
* **offline-helpers:** Offline helpers types and fix issue with different options ([#329](https://github.com/awslabs/aws-mobile-appsync-sdk-js/issues/329)) ([42ac50a](https://github.com/awslabs/aws-mobile-appsync-sdk-js/commit/42ac50a))
* **offline-helpers:** Preserve order of elements in update cache operation type ([#325](https://github.com/awslabs/aws-mobile-appsync-sdk-js/issues/325)) ([5b49946](https://github.com/awslabs/aws-mobile-appsync-sdk-js/commit/5b49946))
* **subscriptions:** Do not retry mqtt disconnections ([#319](https://github.com/awslabs/aws-mobile-appsync-sdk-js/issues/319)) ([b933379](https://github.com/awslabs/aws-mobile-appsync-sdk-js/commit/b933379))
* **subscriptions:** Guard against errors: null response in subscription handshake ([#337](https://github.com/awslabs/aws-mobile-appsync-sdk-js/issues/337)) ([a2035d0](https://github.com/awslabs/aws-mobile-appsync-sdk-js/commit/a2035d0))




<a name="1.7.0"></a>
# [1.7.0](https://github.com/awslabs/aws-mobile-appsync-sdk-js/compare/aws-appsync@1.6.0...aws-appsync@1.7.0) (2018-12-12)

Expand Down
4 changes: 2 additions & 2 deletions packages/aws-appsync/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "aws-appsync",
"version": "1.7.0",
"version": "1.7.2",
"main": "lib/index.js",
"license": "SEE LICENSE IN LICENSE",
"description": "AWS Mobile AppSync SDK for JavaScript",
Expand Down Expand Up @@ -28,7 +28,7 @@
"apollo-link-retry": "2.2.5",
"aws-sdk": "2.329.0",
"debug": "2.6.9",
"graphql": "^0.11.7",
"graphql": "0.13.0",
"redux": "^3.7.2",
"redux-thunk": "^2.2.0",
"setimmediate": "^1.0.5",
Expand Down
18 changes: 13 additions & 5 deletions packages/aws-appsync/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
ComplexObjectLink,
AUTH_TYPE
} from './link';
import { createStore } from './store';
import { createStore, OfflineStatusChangeCallbackCreator } from './store';
import { ApolloCache } from 'apollo-cache';
import { AuthOptions } from './link/auth-link';
import { Credentials, CredentialsOptions } from 'aws-sdk/lib/credentials';
Expand Down Expand Up @@ -77,13 +77,15 @@ export const createAppSyncLink = ({
complexObjectsCredentials,
resultsFetcherLink = createHttpLink({ uri: url }),
conflictResolver,
getDelay
}: {
url: string,
region: string,
auth: AuthOptions,
complexObjectsCredentials: CredentialsGetter,
resultsFetcherLink?: ApolloLink,
conflictResolver?: ConflictResolver,
getDelay?: (retries: number)=> number
}) => {
const link = ApolloLink.from([
createLinkWithStore((store) => new OfflineLink(store)),
Expand All @@ -92,7 +94,7 @@ export const createAppSyncLink = ({
createRetryLink(ApolloLink.from([
createAuthLink({ url, region, auth }),
createSubscriptionHandshakeLink(url, resultsFetcherLink)
]))
]), getDelay)
].filter(Boolean));

return link;
Expand Down Expand Up @@ -124,7 +126,7 @@ const createLinkWithStore = (createLinkFunc = (store: Store<OfflineCacheType>) =
});
}

type CredentialsGetter = () => (Credentials | CredentialsOptions | null) | Credentials | CredentialsOptions | null;
type CredentialsGetter = () => (Credentials | CredentialsOptions | Promise<Credentials> | Promise<CredentialsOptions> | null) | Credentials | CredentialsOptions | Promise<Credentials> | Promise<CredentialsOptions> | null;

export interface AWSAppSyncClientOptions {
url: string,
Expand All @@ -141,6 +143,8 @@ export interface OfflineConfig {
storage?: any,
callback?: OfflineCallback,
storeCacheRootMutation?: boolean,
detectNetwork?: OfflineStatusChangeCallbackCreator,
getDelay?: (retries:number)=>number
};

// TODO: type defs
Expand Down Expand Up @@ -180,6 +184,8 @@ class AWSAppSyncClient<TCacheShape extends NormalizedCacheObject> extends Apollo
storage = undefined,
callback = () => { },
storeCacheRootMutation = false,
detectNetwork = undefined,
getDelay=undefined
} = {},
}: AWSAppSyncClientOptions, options?: Partial<ApolloClientOptions<TCacheShape>>) {
const { cache: customCache = undefined, link: customLink = undefined } = options || {};
Expand All @@ -197,7 +203,9 @@ class AWSAppSyncClient<TCacheShape extends NormalizedCacheObject> extends Apollo
() => this, () => { resolveClient(this); },
dataIdFromObject,
storage,
callback
callback,
detectNetwork,
getDelay
);
const cache: ApolloCache<any> = disableOffline
? (customCache || new InMemoryCache(cacheOptions))
Expand All @@ -218,7 +226,7 @@ class AWSAppSyncClient<TCacheShape extends NormalizedCacheObject> extends Apollo
};
});
});
const link = waitForRehydrationLink.concat(customLink || createAppSyncLink({ url, region, auth, complexObjectsCredentials, conflictResolver }));
const link = waitForRehydrationLink.concat(customLink || createAppSyncLink({ url, region, auth, complexObjectsCredentials, conflictResolver, getDelay }));

const newOptions = {
...options,
Expand Down
5 changes: 3 additions & 2 deletions packages/aws-appsync/src/deltaSync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ const effect = async <TCache extends NormalizedCacheObject>(
}
//#endregion

const { baseRefreshIntervalInSeconds } = baseQuery;
const { baseRefreshIntervalInSeconds } = baseQuery || { baseRefreshIntervalInSeconds: undefined };
upperBoundTimeMS = baseRefreshIntervalInSeconds ? baseRefreshIntervalInSeconds * 1000 : DEFAULT_UPPER_BOUND_TIME_MS;

const skipBaseQuery = !(baseQuery && baseQuery.query) || (baseLastSyncTimestamp
Expand All @@ -336,7 +336,7 @@ const effect = async <TCache extends NormalizedCacheObject>(
query,
variables,
});
cacheProxy.writeQuery({ query, data: result.data });
cacheProxy.writeQuery({ query, variables, data: result.data });

if (typeof update === 'function') {
tryFunctionOrLogError(() => {
Expand Down Expand Up @@ -417,6 +417,7 @@ const effect = async <TCache extends NormalizedCacheObject>(

boundSaveSnapshot(store, client.cache);

client.initQueryManager();
const dataStore = client.queryManager.dataStore;
const enqueuedActionsFilter = [mutationsConfig.enqueueAction];
enquededMutations
Expand Down
15 changes: 10 additions & 5 deletions packages/aws-appsync/src/helpers/offline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,9 @@ export type CacheUpdateQuery = QueryWithVariables | DocumentNode;

export type CacheUpdatesDefinitions = {
[key in CacheOperationTypes]?: CacheUpdateQuery | CacheUpdateQuery[]
};
} | CacheUpdateQuery | CacheUpdateQuery[];

export type CacheUpdatesOptions = (variables?: object) => CacheUpdatesDefinitions | CacheUpdatesDefinitions;
export type CacheUpdatesOptions = ((variables?: object) => CacheUpdatesDefinitions) | CacheUpdatesDefinitions;

/**
* Builds a SubscribeToMoreOptions object ready to be used by Apollo's subscribeToMore() to automatically update the query result in the
Expand Down Expand Up @@ -171,9 +171,14 @@ const getOpTypeQueriesMap = (cacheUpdateQuery: CacheUpdatesOptions, variables):
const cacheUpdateQueryVal = typeof cacheUpdateQuery === 'function' ?
cacheUpdateQuery(variables) :
cacheUpdateQuery || {};
const opTypeQueriesMap = isDocument(cacheUpdateQueryVal) ?
{ [CacheOperationTypes.AUTO]: [].concat(cacheUpdateQueryVal) } as CacheUpdatesDefinitions :
cacheUpdateQueryVal;

let opTypeQueriesMap = cacheUpdateQueryVal;

if (isDocument(cacheUpdateQueryVal) ||
isDocument((cacheUpdateQueryVal as QueryWithVariables).query) ||
Array.isArray(cacheUpdateQuery)) {
opTypeQueriesMap = { [CacheOperationTypes.AUTO]: [].concat(cacheUpdateQueryVal) } as CacheUpdatesDefinitions;
}

return opTypeQueriesMap;
};
Expand Down
16 changes: 14 additions & 2 deletions packages/aws-appsync/src/link/auth-link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class AuthLink extends ApolloLink {
private link: ApolloLink;

/**
*
*
* @param {*} options
*/
constructor(options) {
Expand Down Expand Up @@ -167,7 +167,7 @@ export const authLink = ({ url, region, auth: { type = AUTH_TYPE.NONE, credentia
const formatAsRequest = ({ operationName, variables, query }, options) => {
const body = {
operationName,
variables,
variables: removeTemporaryVariables(variables),
query: print(query)
};

Expand All @@ -182,3 +182,15 @@ const formatAsRequest = ({ operationName, variables, query }, options) => {
},
};
}

/**
* Removes all temporary variables (starting with '@@') so that the signature matches the final request.
*/
const removeTemporaryVariables = (variables: any) =>
Object.keys(variables)
.filter(key => !key.startsWith("@@"))
.reduce((acc, key) => {
acc[key] = variables[key];
return acc;
}, {});

12 changes: 9 additions & 3 deletions packages/aws-appsync/src/link/retry-link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,29 @@ const BASE_TIME_MS = 100;
const JITTER_FACTOR = 100;
const MAX_DELAY_MS = 5 * 60 * 1000;

const getDelay = count => ((2 ** count) * BASE_TIME_MS) + (JITTER_FACTOR * Math.random());
const getDefaultDelay = count => ((2 ** count) * BASE_TIME_MS) + (JITTER_FACTOR * Math.random());

export const SKIP_RETRY_KEY = '@@skipRetry';
export const PERMANENT_ERROR_KEY = typeof Symbol !== 'undefined' ? Symbol('permanentError') : '@@permanentError';

export const getEffectDelay = (_action: OfflineAction, retries: number) => {
export const getEffectDelay = (getDelay:(retries:number) => number=getDefaultDelay) => (_action: OfflineAction, retries: number) => {
const delay = getDelay(retries);

return delay <= MAX_DELAY_MS ? delay : null;
};

export const createRetryLink = (origLink: ApolloLink) => {
export const createRetryLink = (origLink: ApolloLink, getDelay:(retries:number) => number=getDefaultDelay) => {
let delay;

const retryLink = new RetryLink({
attempts: (count, operation, error) => {
const { [PERMANENT_ERROR_KEY]: permanent = false } = error;
const { [SKIP_RETRY_KEY]: skipRetry = false } = operation.variables;

if (permanent) {
return false;
}

if (error.statusCode >= 400 && error.statusCode < 500) {
return false;
}
Expand Down
15 changes: 8 additions & 7 deletions packages/aws-appsync/src/link/subscription-handshake-link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import * as Paho from '../vendor/paho-mqtt';
import { ApolloError } from "apollo-client";
import { FieldNode } from "graphql";
import { getMainDefinition } from "apollo-utilities";
import { PERMANENT_ERROR_KEY } from "./retry-link";

const logger = rootLogger.extend('subscriptions');
const mqttLogger = logger.extend('mqtt');
Expand Down Expand Up @@ -66,13 +67,13 @@ export class SubscriptionHandshakeLink extends ApolloLink {
} = { subscription: { newSubscriptions: {}, mqttConnections: [] } },
errors = [],
}: {
extensions?: {
subscription: SubscriptionExtension
},
errors: any[]
} = subsInfo;
extensions?: {
subscription: SubscriptionExtension
},
errors: any[]
} = subsInfo;

if (errors.length) {
if (errors && errors.length) {
return new Observable(observer => {
observer.error(new ApolloError({
errorMessage: 'Error during subscription handshake',
Expand Down Expand Up @@ -170,7 +171,7 @@ export class SubscriptionHandshakeLink extends ApolloLink {
if (errorCode !== 0) {
topics.forEach(t => {
if (this.topicObservers.has(t)) {
this.topicObservers.get(t).forEach(observer => observer.error(args));
this.topicObservers.get(t).forEach(observer => observer.error({ ...args, [PERMANENT_ERROR_KEY]: true }));
}
});
}
Expand Down
6 changes: 4 additions & 2 deletions packages/aws-appsync/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { OfflineAction, NetInfo, NetworkCallback } from '@redux-offline/redux-of
import { offlineEffectConfig as deltaSyncConfig } from "./deltaSync";
import { Observable } from 'apollo-link';

const { detectNetwork } = defaultOfflineConfig;
const { detectNetwork : defaultDetectNetwork } = defaultOfflineConfig;

const logger = rootLogger.extend('store');

Expand All @@ -32,6 +32,8 @@ const newStore = <TCacheShape extends NormalizedCacheObject>(
dataIdFromObject: (obj) => string | null,
storage: any,
callback: OfflineCallback = () => { },
detectNetwork: OfflineStatusChangeCallbackCreator = defaultDetectNetwork,
getDelay?: (retries:number) => number
): Store<any> => {
logger('Creating store');

Expand All @@ -53,7 +55,7 @@ const newStore = <TCacheShape extends NormalizedCacheObject>(
applyMiddleware(thunk),
offline({
...defaultOfflineConfig,
retry: getEffectDelay,
retry: getEffectDelay(getDelay),
persistCallback: () => {
logger('Storage ready');

Expand Down
Loading