Skip to content

Commit

Permalink
release(required): Amplify JS release (#13971)
Browse files Browse the repository at this point in the history
  • Loading branch information
joon-won authored Oct 29, 2024
2 parents f2248ad + e0fdeb7 commit b02577d
Show file tree
Hide file tree
Showing 28 changed files with 2,728 additions and 1,228 deletions.
42 changes: 21 additions & 21 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,24 @@

# The following paths involve server-side use cases, token/user session management.
# Changes made to these paths requires additional reviews and approvals.
/packages/auth @haverchuck @cshfang @jimblanc @HuiSF
/packages/aws-amplify/src/adapter-core @haverchuck @cshfang @jimblanc @HuiSF
/packages/core/src/adapterCore @haverchuck @cshfang @jimblanc @HuiSF
/packages/core/src/singleton @haverchuck @cshfang @jimblanc @HuiSF
/packages/core/src/utils/convert @haverchuck @cshfang @jimblanc @HuiSF
/packages/core/src/utils/WordArray.ts @haverchuck @cshfang @jimblanc @HuiSF
/packages/core/src/storage @haverchuck @cshfang @jimblanc @HuiSF
/packages/core/src/utils/generateRandomString.ts @haverchuck @cshfang @jimblanc @HuiSF
/packages/core/src/utils/globalHelpers @haverchuck @cshfang @jimblanc @HuiSF
/packages/core/src/utils/urlSafeDecode.ts @haverchuck @cshfang @jimblanc @HuiSF
/packages/core/src/awsClients/cognitoIdentity @haverchuck @cshfang @jimblanc @HuiSF
/packages/core/src/clients/internal @haverchuck @cshfang @jimblanc @HuiSF
/packages/core/src/Hub @haverchuck @cshfang @jimblanc @HuiSF
/packages/adapter-nextjs @haverchuck @cshfang @jimblanc @HuiSF
/packages/rtn-web-browser @haverchuck @cshfang @jimblanc @HuiSF
/packages/storage/src/providers/s3/apis/internal @haverchuck @cshfang @jimblanc @HuiSF
/packages/storage/src/providers/s3/apis/server @haverchuck @cshfang @jimblanc @HuiSF
/packages/api-rest/src/apis/server.ts @haverchuck @cshfang @jimblanc @HuiSF
/packages/api-rest/src/apis/common/internalPost.ts @haverchuck @cshfang @jimblanc @HuiSF
/packages/api-graphql/src/server @haverchuck @cshfang @jimblanc @HuiSF
/packages/api-graphql/src/internals/server @haverchuck @cshfang @jimblanc @HuiSF
/packages/auth @haverchuck @cshfang @HuiSF @pranavosu
/packages/aws-amplify/src/adapter-core @haverchuck @cshfang @HuiSF @pranavosu
/packages/core/src/adapterCore @haverchuck @cshfang @HuiSF @pranavosu
/packages/core/src/singleton @haverchuck @cshfang @HuiSF @pranavosu
/packages/core/src/utils/convert @haverchuck @cshfang @HuiSF @pranavosu
/packages/core/src/utils/WordArray.ts @haverchuck @cshfang @HuiSF @pranavosu
/packages/core/src/storage @haverchuck @cshfang @HuiSF @pranavosu
/packages/core/src/utils/generateRandomString.ts @haverchuck @cshfang @HuiSF @pranavosu
/packages/core/src/utils/globalHelpers @haverchuck @cshfang @HuiSF @pranavosu
/packages/core/src/utils/urlSafeDecode.ts @haverchuck @cshfang @HuiSF @pranavosu
/packages/core/src/awsClients/cognitoIdentity @haverchuck @cshfang @HuiSF @pranavosu
/packages/core/src/clients/internal @haverchuck @cshfang @HuiSF @pranavosu
/packages/core/src/Hub @haverchuck @cshfang @HuiSF @pranavosu
/packages/adapter-nextjs @haverchuck @cshfang @HuiSF @pranavosu
/packages/rtn-web-browser @haverchuck @cshfang @HuiSF @pranavosu
/packages/storage/src/providers/s3/apis/internal @haverchuck @cshfang @HuiSF @pranavosu
/packages/storage/src/providers/s3/apis/server @haverchuck @cshfang @HuiSF @pranavosu
/packages/api-rest/src/apis/server.ts @haverchuck @cshfang @HuiSF @pranavosu
/packages/api-rest/src/apis/common/internalPost.ts @haverchuck @cshfang @HuiSF @pranavosu
/packages/api-graphql/src/server @haverchuck @cshfang @HuiSF @pranavosu
/packages/api-graphql/src/internals/server @haverchuck @cshfang @HuiSF @pranavosu
177 changes: 177 additions & 0 deletions packages/api-graphql/__tests__/AWSAppSyncEventProvider.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import { Observable, Observer } from 'rxjs';
import { Reachability } from '@aws-amplify/core/internals/utils';
import { ConsoleLogger } from '@aws-amplify/core';
import { MESSAGE_TYPES } from '../src/Providers/constants';
import * as constants from '../src/Providers/constants';

import { delay, FakeWebSocketInterface } from './helpers';
import { ConnectionState as CS } from '../src/types/PubSub';

import { AWSAppSyncEventProvider } from '../src/Providers/AWSAppSyncEventsProvider';

describe('AppSyncEventProvider', () => {
describe('subscribe()', () => {
describe('returned observer', () => {
describe('connection logic with mocked websocket', () => {
let fakeWebSocketInterface: FakeWebSocketInterface;
const loggerSpy: jest.SpyInstance = jest.spyOn(
ConsoleLogger.prototype,
'_log',
);

let provider: AWSAppSyncEventProvider;
let reachabilityObserver: Observer<{ online: boolean }>;

beforeEach(async () => {
// Set the network to "online" for these tests
jest
.spyOn(Reachability.prototype, 'networkMonitor')
.mockImplementationOnce(() => {
return new Observable(observer => {
reachabilityObserver = observer;
});
})
// Twice because we subscribe to get the initial state then again to monitor reachability
.mockImplementationOnce(() => {
return new Observable(observer => {
reachabilityObserver = observer;
});
});

fakeWebSocketInterface = new FakeWebSocketInterface();
provider = new AWSAppSyncEventProvider();

// Saving this spy and resetting it by hand causes badness
// Saving it causes new websockets to be reachable across past tests that have not fully closed
// Resetting it proactively causes those same past tests to be dealing with null while they reach a settled state
jest
.spyOn(provider as any, '_getNewWebSocket')
.mockImplementation(() => {
fakeWebSocketInterface.newWebSocket();
return fakeWebSocketInterface.webSocket as WebSocket;
});

// Reduce retry delay for tests to 100ms
Object.defineProperty(constants, 'MAX_DELAY_MS', {
value: 100,
});
// Reduce retry delay for tests to 100ms
Object.defineProperty(constants, 'RECONNECT_DELAY', {
value: 100,
});
});

afterEach(async () => {
provider?.close();
await fakeWebSocketInterface?.closeInterface();
fakeWebSocketInterface?.teardown();
loggerSpy.mockClear();
});

test('subscription observer error is triggered when a connection is formed and a non-retriable connection_error data message is received', async () => {
expect.assertions(3);

const socketCloseSpy = jest.spyOn(
fakeWebSocketInterface.webSocket,
'close',
);
fakeWebSocketInterface.webSocket.readyState = WebSocket.OPEN;

const observer = provider.subscribe({
appSyncGraphqlEndpoint: 'ws://localhost:8080',
});

observer.subscribe({
error: e => {
expect(e.errors[0].message).toEqual(
'Connection failed: UnauthorizedException',
);
},
});

await fakeWebSocketInterface?.readyForUse;
await fakeWebSocketInterface?.triggerOpen();

// Resolve the message delivery actions
await Promise.resolve(
fakeWebSocketInterface?.sendDataMessage({
type: MESSAGE_TYPES.GQL_CONNECTION_ERROR,
errors: [
{
errorType: 'UnauthorizedException', // - non-retriable
errorCode: 401,
},
],
}),
);

// Watching for raised exception to be caught and logged
expect(loggerSpy).toHaveBeenCalledWith(
'DEBUG',
expect.stringContaining('error on bound '),
expect.objectContaining({
message: expect.stringMatching('UnauthorizedException'),
}),
);

await delay(1);

expect(socketCloseSpy).toHaveBeenCalledWith(3001);
});

test('subscription observer error is not triggered when a connection is formed and a retriable connection_error data message is received', async () => {
expect.assertions(2);

const observer = provider.subscribe({
appSyncGraphqlEndpoint: 'ws://localhost:8080',
});

observer.subscribe({
error: x => {},
});

const openSocketAttempt = async () => {
await fakeWebSocketInterface?.readyForUse;
await fakeWebSocketInterface?.triggerOpen();

// Resolve the message delivery actions
await Promise.resolve(
fakeWebSocketInterface?.sendDataMessage({
type: MESSAGE_TYPES.GQL_CONNECTION_ERROR,
errors: [
{
errorType: 'Retriable Test',
errorCode: 408, // Request timed out - retriable
},
],
}),
);
await fakeWebSocketInterface?.resetWebsocket();
};

// Go through two connection attempts to excercise backoff and retriable raise
await openSocketAttempt();
await openSocketAttempt();

// Watching for raised exception to be caught and logged
expect(loggerSpy).toHaveBeenCalledWith(
'DEBUG',
expect.stringContaining('error on bound '),
expect.objectContaining({
message: expect.stringMatching('Retriable Test'),
}),
);

await fakeWebSocketInterface?.waitUntilConnectionStateIn([
CS.ConnectionDisrupted,
]);

expect(loggerSpy).toHaveBeenCalledWith(
'DEBUG',
'Connection failed: Retriable Test',
);
});
});
});
});
});
50 changes: 25 additions & 25 deletions packages/api-graphql/__tests__/AWSAppSyncRealTimeProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import { ConnectionState as CS } from '../src/types/PubSub';

import { AWSAppSyncRealTimeProvider } from '../src/Providers/AWSAppSyncRealTimeProvider';
import { isCustomDomain } from '../src/Providers/AWSWebSocketProvider/appsyncUrl';

// Mock all calls to signRequest
jest.mock('@aws-amplify/core/internals/aws-client-utils', () => {
Expand All @@ -20,7 +21,7 @@ jest.mock('@aws-amplify/core/internals/aws-client-utils', () => {
);
return {
...original,
signRequest: (_request, _options) => {
signRequest: (_request: any, _options: any) => {
return {
method: 'test',
headers: { test: 'test' },
Expand All @@ -46,7 +47,7 @@ jest.mock('@aws-amplify/core', () => {
};
return {
...original,
fetchAuthSession: (_request, _options) => {
fetchAuthSession: (_request: any, _options: any) => {
return Promise.resolve(session);
},
Amplify: {
Expand All @@ -66,24 +67,19 @@ jest.mock('@aws-amplify/core', () => {
describe('AWSAppSyncRealTimeProvider', () => {
describe('isCustomDomain()', () => {
test('Custom domain returns `true`', () => {
const provider = new AWSAppSyncRealTimeProvider();
const result = (provider as any).isCustomDomain(
'https://unit-test.testurl.com/graphql',
);
const result = isCustomDomain('https://unit-test.testurl.com/graphql');
expect(result).toBe(true);
});

test('Non-custom domain returns `false`', () => {
const provider = new AWSAppSyncRealTimeProvider();
const result = (provider as any).isCustomDomain(
const result = isCustomDomain(
'https://12345678901234567890123456.appsync-api.us-west-2.amazonaws.com/graphql',
);
expect(result).toBe(false);
});

test('Non-custom domain in the amazonaws.com.cn subdomain space returns `false`', () => {
const provider = new AWSAppSyncRealTimeProvider();
const result = (provider as any).isCustomDomain(
const result = isCustomDomain(
'https://12345678901234567890123456.appsync-api.cn-north-1.amazonaws.com.cn/graphql',
);
expect(result).toBe(false);
Expand Down Expand Up @@ -136,10 +132,12 @@ describe('AWSAppSyncRealTimeProvider', () => {
// Saving this spy and resetting it by hand causes badness
// Saving it causes new websockets to be reachable across past tests that have not fully closed
// Resetting it proactively causes those same past tests to be dealing with null while they reach a settled state
jest.spyOn(provider, 'getNewWebSocket').mockImplementation(() => {
fakeWebSocketInterface.newWebSocket();
return fakeWebSocketInterface.webSocket as WebSocket;
});
jest
.spyOn(provider as any, '_getNewWebSocket')
.mockImplementation(() => {
fakeWebSocketInterface.newWebSocket();
return fakeWebSocketInterface.webSocket as WebSocket;
});

// Reduce retry delay for tests to 100ms
Object.defineProperty(constants, 'MAX_DELAY_MS', {
Expand Down Expand Up @@ -228,7 +226,7 @@ describe('AWSAppSyncRealTimeProvider', () => {
expect.assertions(1);

const newSocketSpy = jest
.spyOn(provider, 'getNewWebSocket')
.spyOn(provider as any, '_getNewWebSocket')
.mockImplementation(() => {
fakeWebSocketInterface.newWebSocket();
return fakeWebSocketInterface.webSocket as WebSocket;
Expand All @@ -254,7 +252,7 @@ describe('AWSAppSyncRealTimeProvider', () => {
expect.assertions(1);

const newSocketSpy = jest
.spyOn(provider, 'getNewWebSocket')
.spyOn(provider as any, '_getNewWebSocket')
.mockImplementation(() => {
fakeWebSocketInterface.newWebSocket();
return fakeWebSocketInterface.webSocket as WebSocket;
Expand All @@ -280,7 +278,7 @@ describe('AWSAppSyncRealTimeProvider', () => {
expect.assertions(1);

const newSocketSpy = jest
.spyOn(provider, 'getNewWebSocket')
.spyOn(provider as any, '_getNewWebSocket')
.mockImplementation(() => {
fakeWebSocketInterface.newWebSocket();
return fakeWebSocketInterface.webSocket as WebSocket;
Expand All @@ -307,7 +305,7 @@ describe('AWSAppSyncRealTimeProvider', () => {
expect.assertions(1);

const newSocketSpy = jest
.spyOn(provider, 'getNewWebSocket')
.spyOn(provider as any, '_getNewWebSocket')
.mockImplementation(() => {
fakeWebSocketInterface.newWebSocket();
return fakeWebSocketInterface.webSocket;
Expand Down Expand Up @@ -349,7 +347,7 @@ describe('AWSAppSyncRealTimeProvider', () => {
expect.assertions(1);

const newSocketSpy = jest
.spyOn(provider, 'getNewWebSocket')
.spyOn(provider as any, '_getNewWebSocket')
.mockImplementation(() => {
fakeWebSocketInterface.newWebSocket();
return fakeWebSocketInterface.webSocket;
Expand Down Expand Up @@ -545,7 +543,7 @@ describe('AWSAppSyncRealTimeProvider', () => {
await fakeWebSocketInterface?.standardConnectionHandshake();

await fakeWebSocketInterface?.sendDataMessage({
type: MESSAGE_TYPES.GQL_DATA,
type: MESSAGE_TYPES.DATA,
payload: { data: {} },
});

Expand All @@ -571,7 +569,7 @@ describe('AWSAppSyncRealTimeProvider', () => {
connectionTimeoutMs: 100,
});
await fakeWebSocketInterface?.sendDataMessage({
type: MESSAGE_TYPES.GQL_DATA,
type: MESSAGE_TYPES.DATA,
payload: { data: {} },
});

Expand All @@ -597,7 +595,7 @@ describe('AWSAppSyncRealTimeProvider', () => {
connectionTimeoutMs: 100,
});
await fakeWebSocketInterface?.sendDataMessage({
type: MESSAGE_TYPES.GQL_DATA,
type: MESSAGE_TYPES.DATA,
payload: { data: {} },
});
expect(mockNext).toHaveBeenCalled();
Expand Down Expand Up @@ -677,7 +675,9 @@ describe('AWSAppSyncRealTimeProvider', () => {
}),
);

expect(socketCloseSpy).toHaveBeenNthCalledWith(1, 3001);
await delay(1);

expect(socketCloseSpy).toHaveBeenCalledWith(3001);
});

test('subscription observer error is triggered when a connection is formed', async () => {
Expand Down Expand Up @@ -931,7 +931,7 @@ describe('AWSAppSyncRealTimeProvider', () => {

await fakeWebSocketInterface?.standardConnectionHandshake();
await fakeWebSocketInterface?.sendDataMessage({
type: MESSAGE_TYPES.GQL_DATA,
type: MESSAGE_TYPES.DATA,
payload: { data: {} },
});
await subscription.unsubscribe();
Expand Down Expand Up @@ -1181,7 +1181,7 @@ describe('AWSAppSyncRealTimeProvider', () => {
});

test('authenticating with AWS_LAMBDA/custom w/ custom header function that accepts request options', async () => {
expect.assertions(2);
expect.assertions(3);

provider
.subscribe({
Expand Down
Loading

0 comments on commit b02577d

Please sign in to comment.