Skip to content

Commit

Permalink
release: Amplify JS release (#12693)
Browse files Browse the repository at this point in the history
  • Loading branch information
AllanZhengYP authored Dec 12, 2023
2 parents babab19 + f4d3873 commit e9723dc
Show file tree
Hide file tree
Showing 34 changed files with 5,717 additions and 2,233 deletions.
38 changes: 6 additions & 32 deletions packages/adapter-nextjs/src/api/generateServerClient.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import { generateServerClient } from '@aws-amplify/api/internals';
import { generateClientWithAmplifyInstance } from '@aws-amplify/api/internals';
import { generateClient } from 'aws-amplify/api/server';
import {
getAmplifyServerContext,
AmplifyServerContextError,
AmplifyServer,
} from '@aws-amplify/core/internals/adapter-core';
import {
V6ClientSSRRequest,
V6ClientSSRCookies,
GraphQLMethod,
GraphQLMethodSSR,
__amplify,
GraphQLOptionsV6,
} from '@aws-amplify/api-graphql';
import { NextServer } from '../types';
import { createServerRunnerForAPI } from './createServerRunnerForAPI';
import { getAmplifyConfig } from '../utils';
import { GraphQLAuthMode } from '@aws-amplify/core/internals/utils';
import { CustomHeaders } from '@aws-amplify/data-schema-types';

type CookiesClientParams = {
cookies: NextServer.ServerComponentContext['cookies'];
Expand Down Expand Up @@ -74,7 +70,7 @@ export function generateServerClientUsingCookies<
fn(getAmplifyServerContext(contextSpec).amplify),
});

return generateServerClient<T, V6ClientSSRCookies<T>>({
return generateClientWithAmplifyInstance<T, V6ClientSSRCookies<T>>({
amplify: getAmplify,
config: resourcesConfig,
authMode,
Expand All @@ -89,9 +85,9 @@ export function generateServerClientUsingCookies<
* import config from './amplifyconfiguration.json';
* import { listPosts } from './graphql/queries';
*
* const client = generateServerClientUsingReqRes();
* const client = generateServerClientUsingReqRes({ config });
*
* result = await runWithAmplifyServerContext({
* const result = await runWithAmplifyServerContext({
* nextServerContext: { request, response },
* operation: (contextSpec) => client.graphql(contextSpec, {
* query: listPosts,
Expand All @@ -102,31 +98,9 @@ export function generateServerClientUsingReqRes<
T extends Record<any, any> = never,
>({ config, authMode, authToken }: ReqClientParams): V6ClientSSRRequest<T> {
const amplifyConfig = getAmplifyConfig(config);
// passing `null` instance because each (future model) method must retrieve a valid instance
// from server context
const client = generateServerClient<T, V6ClientSSRRequest<T>>({
amplify: null,
return generateClient<T>({
config: amplifyConfig,
authMode,
authToken,
});

// TODO: improve this and the next type
const prevGraphql = client.graphql as unknown as GraphQLMethod;

const wrappedGraphql = (
contextSpec: AmplifyServer.ContextSpec,
options: GraphQLOptionsV6,
additionalHeaders?: CustomHeaders
) => {
const amplifyInstance = getAmplifyServerContext(contextSpec).amplify;
return prevGraphql.call(
{ [__amplify]: amplifyInstance },
options,
additionalHeaders as any
);
};

client.graphql = wrappedGraphql as unknown as GraphQLMethodSSR;
return client;
}
2 changes: 1 addition & 1 deletion packages/api-graphql/__tests__/APIClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ describe('flattenItems', () => {
const selSet = generateSelectionSet(modelIntroSchema.models, 'Todo');

const expected =
'id name description createdAt updatedAt todoMetaId owner';
'id name description status tags createdAt updatedAt todoMetaId owner';

expect(selSet).toEqual(expected);
});
Expand Down
120 changes: 120 additions & 0 deletions packages/api-graphql/__tests__/GraphQLAPI.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,76 @@ describe('API test', () => {
expect(thread).toEqual(graphqlResponse.data.getThread);
});

test('auth-error-case', async () => {
expect.assertions(1);
Amplify.configure({
API: {
GraphQL: {
defaultAuthMode: 'apiKey',
apiKey: 'FAKE-KEY',
endpoint: 'https://localhost/graphql',
region: 'local-host-h4x',
},
},
});

const threadToGet = {
id: 'some-id',
topic: 'something reasonably interesting',
};

const graphqlVariables = { id: 'some-id' };

const err = {
name: 'GraphQLError',
message: 'Unknown error',
originalError: {
name: 'UnauthorizedException',
underlyingError: {
name: 'UnauthorizedException',
$metadata: {
httpStatusCode: 401,
requestId: '12345abcde-test-error-id',
},
},
$metadata: {
httpStatusCode: 401,
requestId: '12345abcde-test-error-id',
},
},
};

const spy = jest
.spyOn((raw.GraphQLAPI as any)._api, 'post')
.mockImplementation(() => {
return {
body: {
json: () => ({
errors: [err],
}),
},
};
});

try {
const result: GraphQLResult<GetThreadQuery> = await client.graphql({
query: typedQueries.getThread,
variables: graphqlVariables,
authMode: 'apiKey',
});
} catch (e: any) {
const errors = e.errors;
expect(errors).toEqual([
expect.objectContaining({
message: 'Unauthorized',
recoverySuggestion: expect.stringContaining(
`If you're calling an Amplify-generated API, make sure to set the "authMode" in generateClient`
),
}),
]);
}
});

test('cancel-graphql-query', async () => {
Amplify.configure({
API: {
Expand Down Expand Up @@ -942,6 +1012,56 @@ describe('API test', () => {
expect(observable).not.toBe(undefined);
});

test('subscription auth permissions error', done => {
expect.assertions(3);

const spyon_appsync_realtime = jest
.spyOn(AWSAppSyncRealTimeProvider.prototype, 'subscribe')
.mockImplementation(
jest.fn(() => {
return new Observable(observer => {
observer.error({
errors: [
{
message:
'Connection failed: {"errors":[{"errorType":"UnauthorizedException","message":"Permission denied"}]}',
},
],
});
});
})
);

const query = `subscription SubscribeToEventComments($eventId: String!) {
subscribeToEventComments(eventId: $eventId) {
eventId
commentId
content
}
}`;

const variables = { id: '809392da-ec91-4ef0-b219-5238a8f942b2' };

const observable = (
client.graphql({ query, variables }) as unknown as Observable<object>
).subscribe({
error: e => {
expect(spyon_appsync_realtime).toHaveBeenCalledTimes(1);
expect(e.errors).toEqual([
expect.objectContaining({
message: 'Unauthorized',
recoverySuggestion: expect.stringContaining(
`If you're calling an Amplify-generated API, make sure to set the "authMode" in generateClient`
),
}),
]);
done();
},
});

expect(observable).not.toBe(undefined);
});

test('happy case subscription with additionalHeaders', done => {
const spyon_appsync_realtime = jest
.spyOn(AWSAppSyncRealTimeProvider.prototype, 'subscribe')
Expand Down
Loading

0 comments on commit e9723dc

Please sign in to comment.