Skip to content

Commit

Permalink
upgrade core and api (aws-amplify#4498)
Browse files Browse the repository at this point in the history
Co-authored-by: manueliglesias <[email protected]>
Co-authored-by: elorzafe <[email protected]>
  • Loading branch information
3 people committed Dec 3, 2019
1 parent b40a1c2 commit ec06c62
Show file tree
Hide file tree
Showing 7 changed files with 274 additions and 7 deletions.
3 changes: 2 additions & 1 deletion packages/api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*/

import APIClass, { graphqlOperation } from './API';
import { GraphQLResult } from './types';

import Amplify, { ConsoleLogger as Logger } from '@aws-amplify/core';

Expand All @@ -28,4 +29,4 @@ const API = _instance;
Amplify.register(API);

export default API;
export { APIClass, graphqlOperation };
export { APIClass, graphqlOperation, GraphQLResult };
8 changes: 5 additions & 3 deletions packages/api/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,10 @@ export enum GRAPHQL_AUTH_MODE {
AMAZON_COGNITO_USER_POOLS = 'AMAZON_COGNITO_USER_POOLS',
}

export interface GraphQLResult {
data?: object;
export interface GraphQLResult<T = object> {
data?: T;
errors?: [object];
extensions?: { [key: string]: any };
extensions?: {
[key: string]: any;
};
}
139 changes: 139 additions & 0 deletions packages/core/__tests__/Mutex-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*!
* The MIT License (MIT)
*
* Copyright (c) 2016 Christian Speckner <[email protected]>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

import { Mutex } from '../src/Util';

describe('Mutex', function() {
let mutex: Mutex;

beforeEach(() => (mutex = new Mutex()));

test('ownership is exclusive', function() {
let flag = false;

mutex.acquire().then(release =>
setTimeout(() => {
flag = true;
release();
}, 50)
);

return mutex.acquire().then(release => {
release();

expect(flag).toBe(true);
});
});

test('runExclusive passes result (immediate)', function() {
return mutex
.runExclusive<number>(() => 10)
.then(value => expect(value).toBe(10));
});

test('runExclusive passes result (promise)', function() {
return mutex
.runExclusive<number>(() => Promise.resolve(10))
.then(value => expect(value).toBe(10));
});

test('runExclusive passes rejection', function() {
return mutex
.runExclusive<number>(() => Promise.reject('foo'))
.then(
() => Promise.reject('should have been rejected'),
value => expect(value).toBe('foo')
);
});

test('runExclusive passes exception', function() {
return mutex
.runExclusive<number>(() => {
throw 'foo';
})
.then(
() => Promise.reject('should have been rejected'),
value => expect(value).toBe('foo')
);
});

test('runExclusive is exclusive', function() {
let flag = false;

mutex.runExclusive(
() =>
new Promise(resolve =>
setTimeout(() => {
flag = true;
resolve();
}, 50)
)
);

return mutex.runExclusive(() => expect(flag).toBe(true));
});

test('exceptions during runExclusive do not leave mutex locked', function() {
let flag = false;

mutex
.runExclusive<number>(() => {
flag = true;
throw new Error();
})
.then(
() => undefined,
() => undefined
);

return mutex.runExclusive(() => expect(flag).toBe(true));
});

test('new mutex is unlocked', function() {
expect(!mutex.isLocked()).toBe(true);
});

test('isLocked reflects the mutex state', async function() {
const lock1 = mutex.acquire(),
lock2 = mutex.acquire();

expect(mutex.isLocked()).toBe(true);

const releaser1 = await lock1;

expect(mutex.isLocked()).toBe(true);

releaser1();

expect(mutex.isLocked()).toBe(true);

const releaser2 = await lock2;

expect(mutex.isLocked()).toBe(true);

releaser2();

expect(!mutex.isLocked()).toBe(true);
});
});
94 changes: 94 additions & 0 deletions packages/core/src/Util/Mutex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*!
* The MIT License (MIT)
*
* Copyright (c) 2016 Christian Speckner <[email protected]>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

interface MutexInterface {
acquire(): Promise<MutexInterface.Releaser>;

runExclusive<T>(callback: MutexInterface.Worker<T>): Promise<T>;

isLocked(): boolean;
}

namespace MutexInterface {
export interface Releaser {
(): void;
}

export interface Worker<T> {
(): Promise<T> | T;
}
}

class Mutex implements MutexInterface {
isLocked(): boolean {
return this._pending;
}

acquire(): Promise<MutexInterface.Releaser> {
const ticket = new Promise<MutexInterface.Releaser>(resolve =>
this._queue.push(resolve)
);

if (!this._pending) {
this._dispatchNext();
}

return ticket;
}

runExclusive<T>(callback: MutexInterface.Worker<T>): Promise<T> {
return this.acquire().then(release => {
let result: T | Promise<T>;

try {
result = callback();
} catch (e) {
release();
throw e;
}

return Promise.resolve(result).then(
(x: T) => (release(), x),
e => {
release();
throw e;
}
);
});
}

private _dispatchNext(): void {
if (this._queue.length > 0) {
this._pending = true;
this._queue.shift()!(this._dispatchNext.bind(this));
} else {
this._pending = false;
}
}

private _queue: Array<(release: MutexInterface.Releaser) => void> = [];
private _pending = false;
}

export default Mutex;
28 changes: 28 additions & 0 deletions packages/core/src/Util/Reachability.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as Observable from 'zen-observable';

type NetworkStatus = {
online: boolean;
};

export default class ReachabilityNavigator implements Reachability {
networkMonitor(): Observable<NetworkStatus> {
return new Observable(observer => {
observer.next({ online: window.navigator.onLine });

const notifyOnline = () => observer.next({ online: true });
const notifyOffline = () => observer.next({ online: false });

window.addEventListener('online', notifyOnline);
window.addEventListener('offline', notifyOffline);

return () => {
window.removeEventListener('online', notifyOnline);
window.removeEventListener('offline', notifyOffline);
};
});
}
}

interface Reachability {
networkMonitor(): Observable<NetworkStatus>;
}
6 changes: 3 additions & 3 deletions packages/core/src/Util.ts → packages/core/src/Util/Retry.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { DelayFunction } from './types';
import { ConsoleLogger as Logger } from './Logger/ConsoleLogger';
import { DelayFunction } from '../types';
import { ConsoleLogger as Logger } from '../Logger/ConsoleLogger';
const logger = new Logger('Util');

export class NonRetryableError extends Error {
Expand Down Expand Up @@ -34,7 +34,7 @@ export async function retry(
);

try {
await functionToRetry.apply(undefined, args);
return await functionToRetry(...args);
} catch (err) {
logger.debug(`error on ${functionToRetry.name}: ${err} `);

Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/Util/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './Retry';
export { default as Mutex } from './Mutex';
export { default as Reachability } from './Reachability';

0 comments on commit ec06c62

Please sign in to comment.