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

feat(api-graphql): alias authMode identityPool -> iam #13299

Merged
merged 3 commits into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
1 change: 1 addition & 0 deletions packages/core/src/singleton/API/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export type GraphQLAuthMode =
| 'oidc'
| 'userPool'
| 'iam'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we start marking iam as @deprecated so we can remove it from the next major version?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried that, but it doesn't actually surface over union types AFAICT

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe still worth it as a reminder to ourselves. I'll update

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot 2024-04-26 at 2 14 30 PM

| 'identityPool'
| 'lambda'
| 'none';

Expand Down