Skip to content

Commit

Permalink
feat(api-graphql): alias authMode identityPool -> iam (aws-amplify#13299
Browse files Browse the repository at this point in the history
)
  • Loading branch information
iartemiev authored Apr 26, 2024
1 parent 588289c commit a5d176b
Show file tree
Hide file tree
Showing 4 changed files with 180 additions and 5 deletions.
162 changes: 162 additions & 0 deletions packages/api-graphql/__tests__/GraphQLAPI.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { graphql, cancel, isCancelError } from '../src/internals/v6';
import { Amplify } from 'aws-amplify';
import { Amplify as AmplifyCore } from '@aws-amplify/core';
import * as typedQueries from './fixtures/with-types/queries';
import * as typedSubscriptions from './fixtures/with-types/subscriptions';
import { expectGet } from './utils/expects';
import { InternalGraphQLAPIClass } from '../src/internals/InternalGraphQLAPI';

import {
__amplify,
Expand Down Expand Up @@ -668,6 +670,75 @@ describe('API test', () => {
);
});

test('multi-auth default case api-key, using identityPool as auth mode', async () => {
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 graphqlResponse = {
data: {
getThread: {
__typename: 'Thread',
...serverManagedFields,
...threadToGet,
},
},
};

const spy = jest
.spyOn((raw.GraphQLAPI as any)._api, 'post')
.mockReturnValue({
body: {
json: () => graphqlResponse,
},
});

const result: GraphQLResult<GetThreadQuery> = await client.graphql({
query: typedQueries.getThread,
variables: graphqlVariables,
authMode: 'identityPool',
});

const thread: GetThreadQuery['getThread'] = result.data?.getThread;
const errors = result.errors;

expect(errors).toBe(undefined);
expect(thread).toEqual(graphqlResponse.data.getThread);

expect(spy).toHaveBeenCalledWith(
expect.objectContaining({
Auth: expect.any(Object),
configure: expect.any(Function),
getConfig: expect.any(Function),
}),
{
abortController: expect.any(AbortController),
url: new URL('https://localhost/graphql'),
options: expect.objectContaining({
headers: expect.not.objectContaining({ 'X-Api-Key': 'FAKE-KEY' }),
signingServiceInfo: expect.objectContaining({
region: 'local-host-h4x',
service: 'appsync',
}),
}),
},
);
});

test('multi-auth default case api-key, using AWS_LAMBDA as auth mode', async () => {
Amplify.configure({
API: {
Expand Down Expand Up @@ -1395,5 +1466,96 @@ describe('API test', () => {
}),
);
});

test('identityPool alias with query', async () => {
Amplify.configure({
API: {
GraphQL: {
defaultAuthMode: 'apiKey',
apiKey: 'FAKE-KEY',
endpoint: 'https://localhost/graphql',
region: 'local-host-h4x',
},
},
});

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

const graphqlResponse = {
data: {
getThread: {},
},
};

const spy = jest.spyOn(
InternalGraphQLAPIClass.prototype as any,
'_headerBasedAuth',
);

const spy2 = jest
.spyOn((raw.GraphQLAPI as any)._api, 'post')
.mockReturnValue({
body: {
json: () => graphqlResponse,
},
});

await client.graphql({
query: typedQueries.getThread,
variables: graphqlVariables,
authMode: 'identityPool',
});

expect(spy).toHaveBeenCalledWith(
expect.objectContaining({
Auth: expect.any(Object),
configure: expect.any(Function),
getConfig: expect.any(Function),
}),
'iam',
{},
);
});

test('identityPool alias with subscription', async () => {
Amplify.configure({
API: {
GraphQL: {
defaultAuthMode: 'apiKey',
apiKey: 'FAKE-KEY',
endpoint: 'https://localhost/graphql',
region: 'local-host-h4x',
},
},
});

const graphqlResponse = {
data: {
getThread: {},
},
};

const spy = jest.spyOn(AWSAppSyncRealTimeProvider.prototype, 'subscribe');

const _spy2 = jest
.spyOn((raw.GraphQLAPI as any)._api, 'post')
.mockReturnValue({
body: {
json: () => graphqlResponse,
},
});

await client.graphql({
query: typedSubscriptions.onCreateThread,
authMode: 'identityPool',
});

expect(spy).toHaveBeenCalledWith(
expect.objectContaining({
authenticationType: 'iam',
}),
expect.objectContaining({}),
);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ const dispatchApiEvent = (payload: HubPayload) => {
Hub.dispatch('api', payload, 'PubSub', AMPLIFY_SYMBOL);
};

// resolved/actual AuthMode values. identityPool gets resolves to IAM upstream in InternalGraphQLAPI._graphqlSubscribe
type ResolvedGraphQLAuthModes = Exclude<GraphQLAuthMode, 'identityPool'>;
export interface ObserverQuery {
observer: PubSubContentObserver;
query: string;
Expand Down Expand Up @@ -96,7 +98,7 @@ interface ParsedMessagePayload {

export interface AWSAppSyncRealTimeProviderOptions {
appSyncGraphqlEndpoint?: string;
authenticationType?: GraphQLAuthMode;
authenticationType?: ResolvedGraphQLAuthModes;
query?: string;
variables?: Record<string, DocumentType>;
apiKey?: string;
Expand Down Expand Up @@ -935,7 +937,7 @@ export class AWSAppSyncRealTimeProvider {
Record<string, unknown> | undefined
> {
const headerHandler: {
[key in GraphQLAuthMode]: (
[key in ResolvedGraphQLAuthModes]: (
arg0: AWSAppSyncRealTimeAuthInput,
) => Promise<Record<string, unknown>> | Record<string, unknown>;
} = {
Expand Down
15 changes: 12 additions & 3 deletions packages/api-graphql/src/internals/InternalGraphQLAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,10 @@ export class InternalGraphQLAPIClass {
defaultAuthMode,
} = resolveConfig(amplify);

const authMode = explicitAuthMode || defaultAuthMode || 'iam';
const initialAuthMode = explicitAuthMode || defaultAuthMode || 'iam';
// identityPool is an alias for iam. TODO: remove 'iam' in v7
const authMode =
initialAuthMode === 'identityPool' ? 'iam' : initialAuthMode;

/**
* Retrieve library options from Amplify configuration.
Expand Down Expand Up @@ -425,13 +428,19 @@ export class InternalGraphQLAPIClass {

private _graphqlSubscribe(
amplify: AmplifyClassV6,
{ query, variables, authMode }: GraphQLOptions,
{ query, variables, authMode: explicitAuthMode }: GraphQLOptions,
additionalHeaders: CustomHeaders = {},
customUserAgentDetails?: CustomUserAgentDetails,
authToken?: string,
): Observable<any> {
const config = resolveConfig(amplify);

const initialAuthMode =
explicitAuthMode || config?.defaultAuthMode || 'iam';
// identityPool is an alias for iam. TODO: remove 'iam' in v7
const authMode =
initialAuthMode === 'identityPool' ? 'iam' : initialAuthMode;

/**
* Retrieve library options from Amplify configuration.
* `libraryConfigHeaders` are from the Amplify configuration options,
Expand All @@ -449,7 +458,7 @@ export class InternalGraphQLAPIClass {
variables,
appSyncGraphqlEndpoint: config?.endpoint,
region: config?.region,
authenticationType: authMode || config?.defaultAuthMode,
authenticationType: authMode,
apiKey: config?.apiKey,
additionalHeaders,
authToken,
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/singleton/API/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ export type GraphQLAuthMode =
| 'apiKey'
| 'oidc'
| 'userPool'
// @deprecated; use 'identityPool'
| 'iam'
| 'identityPool'
| 'lambda'
| 'none';

Expand Down

0 comments on commit a5d176b

Please sign in to comment.