diff --git a/.circleci/config.yml b/.circleci/config.yml index eac63f5ca6b..30574dee776 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1266,70 +1266,74 @@ jobs: spec: next-owner-based-default browser: << parameters.browser >> - integ_react_datastore_custom_pk_unconnected_models: - parameters: - browser: - type: string - executor: js-test-executor - <<: *test_env_vars - working_directory: ~/amplify-js-samples-staging/samples/react/datastore/v2/custom-pk-unconnected-models - steps: - - prepare_test_env - - integ_test_js: - test_name: 'DataStore - Custom Primary Key (Connected Models)' - framework: react - category: datastore - sample_name: v2/custom-pk-unconnected-models - spec: custom-pk-unconnected-models - browser: << parameters.browser >> - - integ_react_datastore_custom_pk_unconnected_models_webkit: - executor: webkit-test-executor - <<: *test_env_vars - working_directory: ~/amplify-js-samples-staging/samples/react/datastore/v2/custom-pk-unconnected-models - steps: - - prepare_test_env - - integ_test_js: - test_name: 'DataStore - Custom Primary Key (Connected Models)' - framework: react - category: datastore - sample_name: v2/custom-pk-unconnected-models - spec: custom-pk-unconnected-models - browser: webkit - - integ_react_datastore_custom_pk_has_one: - parameters: - browser: - type: string - executor: js-test-executor - <<: *test_env_vars - working_directory: ~/amplify-js-samples-staging/samples/react/datastore/v2/custom-pk-has-one - steps: - - prepare_test_env - - integ_test_js: - test_name: 'DataStore - Custom Primary Key (Has One)' - framework: react - category: datastore - sample_name: v2/custom-pk-has-one - spec: custom-pk-has-one - browser: << parameters.browser >> - - integ_react_datastore_custom_pk_has_many_many_to_many: - parameters: - browser: - type: string - executor: js-test-executor - <<: *test_env_vars - working_directory: ~/amplify-js-samples-staging/samples/react/datastore/v2/custom-pk-has-many-many-to-many - steps: - - prepare_test_env - - integ_test_js: - test_name: 'DataStore - Custom Primary Key (Has Many / Many to Many)' - framework: react - category: datastore - sample_name: v2/custom-pk-has-many-many-to-many - spec: custom-pk-has-many-many-to-many - browser: << parameters.browser >> + # TODO: re-enable or backfill test cases + # integ_react_datastore_custom_pk_unconnected_models: + # parameters: + # browser: + # type: string + # executor: js-test-executor + # <<: *test_env_vars + # working_directory: ~/amplify-js-samples-staging/samples/react/datastore/v2/custom-pk-unconnected-models + # steps: + # - prepare_test_env + # - integ_test_js: + # test_name: 'DataStore - Custom Primary Key (Connected Models)' + # framework: react + # category: datastore + # sample_name: v2/custom-pk-unconnected-models + # spec: custom-pk-unconnected-models + # browser: << parameters.browser >> + + # TODO: re-enable or backfill test cases + # integ_react_datastore_custom_pk_unconnected_models_webkit: + # executor: webkit-test-executor + # <<: *test_env_vars + # working_directory: ~/amplify-js-samples-staging/samples/react/datastore/v2/custom-pk-unconnected-models + # steps: + # - prepare_test_env + # - integ_test_js: + # test_name: 'DataStore - Custom Primary Key (Connected Models)' + # framework: react + # category: datastore + # sample_name: v2/custom-pk-unconnected-models + # spec: custom-pk-unconnected-models + # browser: webkit + + # TODO: re-enable or backfill test cases + # integ_react_datastore_custom_pk_has_one: + # parameters: + # browser: + # type: string + # executor: js-test-executor + # <<: *test_env_vars + # working_directory: ~/amplify-js-samples-staging/samples/react/datastore/v2/custom-pk-has-one + # steps: + # - prepare_test_env + # - integ_test_js: + # test_name: 'DataStore - Custom Primary Key (Has One)' + # framework: react + # category: datastore + # sample_name: v2/custom-pk-has-one + # spec: custom-pk-has-one + # browser: << parameters.browser >> + + # TODO: re-enable or backfill test cases + # integ_react_datastore_custom_pk_has_many_many_to_many: + # parameters: + # browser: + # type: string + # executor: js-test-executor + # <<: *test_env_vars + # working_directory: ~/amplify-js-samples-staging/samples/react/datastore/v2/custom-pk-has-many-many-to-many + # steps: + # - prepare_test_env + # - integ_test_js: + # test_name: 'DataStore - Custom Primary Key (Has Many / Many to Many)' + # framework: react + # category: datastore + # sample_name: v2/custom-pk-has-many-many-to-many + # spec: custom-pk-has-many-many-to-many + # browser: << parameters.browser >> integ_react_datastore_background_process_manager: parameters: @@ -2097,39 +2101,40 @@ workflows: matrix: parameters: <<: *minimal_browser_list - - integ_react_datastore_custom_pk_unconnected_models: - requires: - - integ_setup - - build - filters: - <<: *releasable_branches - matrix: - parameters: - <<: *extended_browser_list - - integ_react_datastore_custom_pk_unconnected_models_webkit: - requires: - - integ_setup - - build - filters: - <<: *releasable_branches - - integ_react_datastore_custom_pk_has_one: - requires: - - integ_setup - - build - filters: - <<: *releasable_branches - matrix: - parameters: - <<: *minimal_browser_list - - integ_react_datastore_custom_pk_has_many_many_to_many: - requires: - - integ_setup - - build - filters: - <<: *releasable_branches - matrix: - parameters: - <<: *minimal_browser_list + # TODO: re-enable or backfill test cases + # - integ_react_datastore_custom_pk_unconnected_models: + # requires: + # - integ_setup + # - build + # filters: + # <<: *releasable_branches + # matrix: + # parameters: + # <<: *extended_browser_list + # - integ_react_datastore_custom_pk_unconnected_models_webkit: + # requires: + # - integ_setup + # - build + # filters: + # <<: *releasable_branches + # - integ_react_datastore_custom_pk_has_one: + # requires: + # - integ_setup + # - build + # filters: + # <<: *releasable_branches + # matrix: + # parameters: + # <<: *minimal_browser_list + # - integ_react_datastore_custom_pk_has_many_many_to_many: + # requires: + # - integ_setup + # - build + # filters: + # <<: *releasable_branches + # matrix: + # parameters: + # <<: *minimal_browser_list - integ_react_datastore_background_process_manager: requires: - integ_setup diff --git a/.github/canary-config/canary-all.yml b/.github/canary-config/canary-all.yml index 1f27bab87ac..193265e719b 100644 --- a/.github/canary-config/canary-all.yml +++ b/.github/canary-config/canary-all.yml @@ -58,20 +58,22 @@ tests: sample_name: [schema-drift] spec: schema-drift browser: *minimal_browser_list - - test_name: integ_react_datastore_custom_pk_has_one - desc: 'DataStore - Custom Primary Key (Has One)' - framework: react - category: datastore - sample_name: [v2/custom-pk-has-one] - spec: custom-pk-has-one - browser: *minimal_browser_list - - test_name: integ_react_datastore_custom_pk_has_many_many_to_many - desc: 'DataStore - Custom Primary Key (Has Many / Many to Many)' - framework: react - category: datastore - sample_name: [v2/custom-pk-has-many-many-to-many] - spec: custom-pk-has-many-many-to-many - browser: *minimal_browser_list + # TODO: re-enable or backfill test cases + # - test_name: integ_react_datastore_custom_pk_has_one + # desc: 'DataStore - Custom Primary Key (Has One)' + # framework: react + # category: datastore + # sample_name: [v2/custom-pk-has-one] + # spec: custom-pk-has-one + # browser: *minimal_browser_list + # TODO: re-enable or backfill test cases + # - test_name: integ_react_datastore_custom_pk_has_many_many_to_many + # desc: 'DataStore - Custom Primary Key (Has Many / Many to Many)' + # framework: react + # category: datastore + # sample_name: [v2/custom-pk-has-many-many-to-many] + # spec: custom-pk-has-many-many-to-many + # browser: *minimal_browser_list - test_name: integ_react_datastore_background_process_manager desc: 'DataStore - Background Process Manager' framework: react diff --git a/.github/integ-config/integ-all.yml b/.github/integ-config/integ-all.yml index 2de58afb0fe..b364762784f 100644 --- a/.github/integ-config/integ-all.yml +++ b/.github/integ-config/integ-all.yml @@ -314,14 +314,16 @@ tests: sample_name: [schema-drift] spec: schema-drift browser: *minimal_browser_list - - test_name: integ_react_datastore_custom_pk_unconnected_models - desc: 'DataStore - Custom Primary Key (Connected Models)' - framework: react - category: datastore - sample_name: [v2/custom-pk-unconnected-models] - spec: custom-pk-unconnected-models - browser: *extended_browser_list - timeout_minutes: 60 + # TODO: re-enable or backfill test cases + # - test_name: integ_react_datastore_custom_pk_unconnected_models + # desc: 'DataStore - Custom Primary Key (Connected Models)' + # framework: react + # category: datastore + # sample_name: [v2/custom-pk-unconnected-models] + # spec: custom-pk-unconnected-models + # browser: *extended_browser_list + # timeout_minutes: 60 + # TODO: re-enable or backfill test cases # - test_name: integ_react_datastore_custom_pk_unconnected_models_webkit # desc: 'DataStore - Custom Primary Key (Connected Models)' # framework: react @@ -329,22 +331,24 @@ tests: # sample_name: [v2/custom-pk-unconnected-models] # spec: custom-pk-unconnected-models # browser: [webkit] - - test_name: integ_react_datastore_custom_pk_has_one - desc: 'DataStore - Custom Primary Key (Has One)' - framework: react - category: datastore - sample_name: [v2/custom-pk-has-one] - spec: custom-pk-has-one - browser: *minimal_browser_list - timeout_minutes: 60 - - test_name: integ_react_datastore_custom_pk_has_many_many_to_many - desc: 'DataStore - Custom Primary Key (Has Many / Many to Many)' - framework: react - category: datastore - sample_name: [v2/custom-pk-has-many-many-to-many] - spec: custom-pk-has-many-many-to-many - browser: *minimal_browser_list - timeout_minutes: 60 + # TODO: re-enable or backfill test cases + # - test_name: integ_react_datastore_custom_pk_has_one + # desc: 'DataStore - Custom Primary Key (Has One)' + # framework: react + # category: datastore + # sample_name: [v2/custom-pk-has-one] + # spec: custom-pk-has-one + # browser: *minimal_browser_list + # timeout_minutes: 60 + # TODO: re-enable or backfill test cases + # - test_name: integ_react_datastore_custom_pk_has_many_many_to_many + # desc: 'DataStore - Custom Primary Key (Has Many / Many to Many)' + # framework: react + # category: datastore + # sample_name: [v2/custom-pk-has-many-many-to-many] + # spec: custom-pk-has-many-many-to-many + # browser: *minimal_browser_list + # timeout_minutes: 60 - test_name: integ_react_datastore_background_process_manager desc: 'DataStore - Background Process Manager' framework: react diff --git a/packages/api-graphql/__tests__/GraphQLAPI-test.ts b/packages/api-graphql/__tests__/GraphQLAPI-test.ts index 1eb5a88f997..3824feed192 100644 --- a/packages/api-graphql/__tests__/GraphQLAPI-test.ts +++ b/packages/api-graphql/__tests__/GraphQLAPI-test.ts @@ -1,4 +1,4 @@ -import { Auth } from '@aws-amplify/auth'; +import { InternalAuth } from '@aws-amplify/auth/internals'; import { GraphQLAPIClass as API } from '../src'; import { InternalGraphQLAPIClass as InternalAPI } from '../src/internals'; import { graphqlOperation } from '../src/GraphQLAPI'; @@ -363,7 +363,7 @@ describe('API test', () => { }); const spyonAuth = jest - .spyOn(Auth, 'currentAuthenticatedUser') + .spyOn(InternalAuth, 'currentAuthenticatedUser') .mockImplementationOnce(() => { return new Promise((res, rej) => { res({ @@ -884,7 +884,7 @@ describe('API test', () => { .spyOn(RestClient.prototype, 'post') .mockReturnValue(Promise.resolve({})); - jest.spyOn(Auth, 'currentSession').mockReturnValue({ + jest.spyOn(InternalAuth, 'currentSession').mockReturnValue({ getAccessToken: () => ({ getJwtToken: () => 'Secret-Token', }), @@ -1394,7 +1394,7 @@ describe('Internal API customUserAgent test', () => { }); }); describe('graphql test', () => { - test('happy case mutation', async () => { + test('happy case mutation - API_KEY', async () => { const spyonAuth = jest .spyOn(Credentials, 'get') .mockImplementationOnce(() => { @@ -1410,6 +1410,7 @@ describe('Internal API customUserAgent test', () => { res({}); }); }); + const internalApi = new InternalAPI(config); const url = 'https://appsync.amazonaws.com', region = 'us-east-2', @@ -1464,6 +1465,152 @@ describe('Internal API customUserAgent test', () => { ); expect(spyon).toBeCalledWith(url, init); + + spyonAuth.mockClear(); + spyon.mockClear(); + }); + + test('happy case mutation - OPENID_CONNECT', async () => { + const cache_config = { + capacityInBytes: 3000, + itemMaxSize: 800, + defaultTTL: 3000000, + defaultPriority: 5, + warningThreshold: 0.8, + storage: window.localStorage, + }; + + Cache.configure(cache_config); + + const spyonCache = jest + .spyOn(Cache, 'getItem') + .mockImplementationOnce(() => { + return null; + }); + + const spyonAuth = jest + .spyOn(InternalAuth, 'currentAuthenticatedUser') + .mockImplementationOnce(() => { + return new Promise((res, rej) => { + res({ + name: 'federated user', + token: 'federated_token_from_storage', + }); + }); + }); + + const spyon = jest + .spyOn(RestClient.prototype, 'post') + .mockImplementationOnce((url, init) => { + return new Promise((res, rej) => { + res({}); + }); + }); + + const internalApi = new InternalAPI(config); + const url = 'https://appsync.amazonaws.com', + region = 'us-east-2', + variables = { id: '809392da-ec91-4ef0-b219-5238a8f942b2' }; + internalApi.configure({ + aws_appsync_graphqlEndpoint: url, + aws_appsync_region: region, + aws_appsync_authenticationType: 'OPENID_CONNECT', + }); + + const headers = { + Authorization: 'federated_token_from_storage', + 'x-amz-user-agent': expectedUserAgentAPI, + }; + + const body = { + query: getEventQuery, + variables, + }; + + const init = { + headers, + body, + signerServiceInfo: { + service: 'appsync', + region, + }, + cancellableToken: mockCancellableToken, + }; + + await internalApi.graphql( + graphqlOperation(GetEvent, variables), + undefined, + customUserAgentDetailsAPI + ); + + expect(spyon).toBeCalledWith(url, init); + expect(spyonAuth).toBeCalledWith(undefined, customUserAgentDetailsAPI); + + spyonCache.mockClear(); + spyonAuth.mockClear(); + spyon.mockClear(); + }); + + test('happy case mutation - AMAZON_COGNITO_USER_POOLS', async () => { + const spyon = jest + .spyOn(RestClient.prototype, 'post') + .mockReturnValue(Promise.resolve({})); + + const spyonAuth = jest + .spyOn(InternalAuth, 'currentSession') + .mockReturnValue({ + getAccessToken: () => ({ + getJwtToken: () => 'Secret-Token', + }), + } as any); + + const internalApi = new InternalAPI(config); + const url = 'https://appsync.amazonaws.com', + region = 'us-east-2', + variables = { id: '809392da-ec91-4ef0-b219-5238a8f942b2' }, + apiKey = 'secret-api-key'; + internalApi.configure({ + aws_appsync_graphqlEndpoint: url, + aws_appsync_region: region, + aws_appsync_authenticationType: 'API_KEY', + aws_appsync_apiKey: apiKey, + }); + + const headers = { + Authorization: 'Secret-Token', + 'x-amz-user-agent': expectedUserAgentAPI, + }; + + const body = { + query: getEventQuery, + variables, + }; + + const init = { + headers, + body, + signerServiceInfo: { + service: 'appsync', + region, + }, + cancellableToken: mockCancellableToken, + }; + + await internalApi.graphql( + { + query: GetEvent, + variables, + authMode: GRAPHQL_AUTH_MODE.AMAZON_COGNITO_USER_POOLS, + }, + undefined, + customUserAgentDetailsAPI + ); + + expect(spyon).toBeCalledWith(url, init); + expect(spyonAuth).toBeCalledWith(customUserAgentDetailsAPI); + + spyon.mockClear(); + spyonAuth.mockClear(); }); test('happy case subscription', async done => { diff --git a/packages/api-graphql/src/internals/InternalGraphQLAPI.ts b/packages/api-graphql/src/internals/InternalGraphQLAPI.ts index 40ecf3fb8af..e7c071d41e2 100644 --- a/packages/api-graphql/src/internals/InternalGraphQLAPI.ts +++ b/packages/api-graphql/src/internals/InternalGraphQLAPI.ts @@ -18,7 +18,7 @@ import { INTERNAL_AWS_APPSYNC_REALTIME_PUBSUB_PROVIDER, } from '@aws-amplify/core'; import { InternalPubSub } from '@aws-amplify/pubsub/internals'; -import { Auth } from '@aws-amplify/auth'; +import { InternalAuth } from '@aws-amplify/auth/internals'; import { Cache } from '@aws-amplify/cache'; import { GraphQLAuthError, @@ -51,7 +51,7 @@ export class InternalGraphQLAPIClass { private _options; private _api = null; - Auth = Auth; + InternalAuth = InternalAuth; Cache = Cache; Credentials = Credentials; @@ -119,7 +119,8 @@ export class InternalGraphQLAPIClass { private async _headerBasedAuth( defaultAuthenticationType?, - additionalHeaders: { [key: string]: string } = {} + additionalHeaders: { [key: string]: string } = {}, + customUserAgentDetails?: CustomUserAgentDetails ) { const { aws_appsync_authenticationType, aws_appsync_apiKey: apiKey } = this._options; @@ -151,7 +152,10 @@ export class InternalGraphQLAPIClass { if (federatedInfo) { token = federatedInfo.token; } else { - const currentUser = await Auth.currentAuthenticatedUser(); + const currentUser = await InternalAuth.currentAuthenticatedUser( + undefined, + customUserAgentDetails + ); if (currentUser) { token = currentUser.token; } @@ -168,7 +172,9 @@ export class InternalGraphQLAPIClass { break; case 'AMAZON_COGNITO_USER_POOLS': try { - const session = await this.Auth.currentSession(); + const session = await this.InternalAuth.currentSession( + customUserAgentDetails + ); headers = { Authorization: session.getAccessToken().getJwtToken(), }; @@ -285,10 +291,18 @@ export class InternalGraphQLAPIClass { const headers = { ...(!customGraphqlEndpoint && - (await this._headerBasedAuth(authMode, additionalHeaders))), + (await this._headerBasedAuth( + authMode, + additionalHeaders, + customUserAgentDetails + ))), ...(customGraphqlEndpoint && (customEndpointRegion - ? await this._headerBasedAuth(authMode, additionalHeaders) + ? await this._headerBasedAuth( + authMode, + additionalHeaders, + customUserAgentDetails + ) : { Authorization: null })), ...(await graphql_headers({ query, variables })), ...additionalHeaders, diff --git a/packages/api/src/internals/InternalAPI.ts b/packages/api/src/internals/InternalAPI.ts index 4b555f338b6..9c3231bc749 100644 --- a/packages/api/src/internals/InternalAPI.ts +++ b/packages/api/src/internals/InternalAPI.ts @@ -8,7 +8,7 @@ import { } from '@aws-amplify/api-graphql'; import { InternalGraphQLAPIClass } from '@aws-amplify/api-graphql/internals'; import { RestAPIClass } from '@aws-amplify/api-rest'; -import { Auth } from '@aws-amplify/auth'; +import { InternalAuth } from '@aws-amplify/auth/internals'; import { Cache } from '@aws-amplify/cache'; import { Amplify, @@ -37,7 +37,7 @@ export class InternalAPIClass { private _restApi: RestAPIClass; private _graphqlApi: InternalGraphQLAPIClass; - Auth = Auth; + InternalAuth = InternalAuth; Cache = Cache; Credentials = Credentials; @@ -67,7 +67,7 @@ export class InternalAPIClass { // Share Amplify instance with client for SSR this._restApi.Credentials = this.Credentials; - this._graphqlApi.Auth = this.Auth; + this._graphqlApi.InternalAuth = this.InternalAuth; this._graphqlApi.Cache = this.Cache; this._graphqlApi.Credentials = this.Credentials; diff --git a/packages/auth/src/internals/InternalAuth.ts b/packages/auth/src/internals/InternalAuth.ts index a54c3e1f13f..7cde958446e 100644 --- a/packages/auth/src/internals/InternalAuth.ts +++ b/packages/auth/src/internals/InternalAuth.ts @@ -261,7 +261,7 @@ export class InternalAuthClass { // See https://github.com/aws-amplify/amplify-js/issues/4388 const usedResponseUrls = {}; // Only register urlListener once - if (this.getModuleName() === 'InternalAuth') { + if (this.getModuleName() === 'Auth') { urlListener(({ url }) => { if (usedResponseUrls[url]) { return; diff --git a/packages/aws-amplify/__tests__/withSSRContext-test.ts b/packages/aws-amplify/__tests__/withSSRContext-test.ts index 0e109b5c396..1557d65b0c7 100644 --- a/packages/aws-amplify/__tests__/withSSRContext-test.ts +++ b/packages/aws-amplify/__tests__/withSSRContext-test.ts @@ -71,12 +71,12 @@ describe('withSSRContext', () => { }); it('should use Amplify components from the ssr context', () => { - const { Auth, DataStore, InternalAPI } = withSSRContext(); - - expect(DataStore.Auth).toBe(Auth); - expect(DataStore.Auth).not.toBe(Amplify.Auth); + const { Auth, DataStore, InternalAPI, InternalAuth } = withSSRContext(); expect(DataStore.InternalAPI).toBe(InternalAPI); + expect(DataStore.InternalAPI).not.toBe(Amplify.InternalAPI); + expect(DataStore.InternalAuth).toBe(InternalAuth); + expect(DataStore.InternalAuth).not.toBe(Amplify.InternalAuth); }); }); diff --git a/packages/aws-amplify/src/withSSRContext.ts b/packages/aws-amplify/src/withSSRContext.ts index 34cfe76e795..40ff4c8df47 100644 --- a/packages/aws-amplify/src/withSSRContext.ts +++ b/packages/aws-amplify/src/withSSRContext.ts @@ -3,6 +3,7 @@ import { API } from '@aws-amplify/api'; import { InternalAPI } from '@aws-amplify/api/internals'; import { Auth } from '@aws-amplify/auth'; +import { InternalAuth } from '@aws-amplify/auth/internals'; import { AmplifyClass, Credentials, UniversalStorage } from '@aws-amplify/core'; import { DataStore } from '@aws-amplify/datastore'; @@ -10,8 +11,10 @@ import { DataStore } from '@aws-amplify/datastore'; import { Amplify } from './index'; const requiredModules = [ - // API cannot function without Auth + // Credentials cannot function without Auth Auth, + // API cannot function without InternalAuth + InternalAuth, // Auth cannot function without Credentials Credentials, ]; @@ -29,6 +32,7 @@ export function withSSRContext(context: Context = {}) { if (modules.includes(DataStore)) { modules.push(InternalAPI); } + const previousConfig = Amplify.configure(); const amplify = new AmplifyClass(); const storage = new UniversalStorage({ req }); diff --git a/packages/datastore/__tests__/authStrategies.test.ts b/packages/datastore/__tests__/authStrategies.test.ts index d9b2411a3ce..b10a573d945 100644 --- a/packages/datastore/__tests__/authStrategies.test.ts +++ b/packages/datastore/__tests__/authStrategies.test.ts @@ -1,3 +1,8 @@ +import { + Category, + CustomUserAgentDetails, + DataStoreAction, +} from '@aws-amplify/core'; import { InternalSchema, ModelAttributeAuthAllow, @@ -447,7 +452,7 @@ async function testMultiAuthStrategy({ hasAuthenticatedUser: boolean; result: any; }) { - mockCurrentUser({ hasAuthenticatedUser }); + const currentUserSpy = mockCurrentUser({ hasAuthenticatedUser }); const multiAuthStrategyWrapper = require('../src/authModeStrategies/multiAuthStrategy').multiAuthStrategy; @@ -455,6 +460,10 @@ async function testMultiAuthStrategy({ const multiAuthStrategy = multiAuthStrategyWrapper({}); const schema = getAuthSchema(authRules); + const customUserAgentDetails: CustomUserAgentDetails = { + category: Category.DataStore, + action: DataStoreAction.GraphQl, + }; const authModes = await multiAuthStrategy({ schema, @@ -463,9 +472,11 @@ async function testMultiAuthStrategy({ // but it still technically a required attribute in TS, since customers // won't actually be calling the function directly in their app. operation: ModelOperation.READ, + customUserAgentDetails, }); expect(authModes).toEqual(result); + expect(currentUserSpy).toBeCalledWith(undefined, customUserAgentDetails); jest.resetModules(); jest.resetAllMocks(); } @@ -540,18 +551,22 @@ function mockCurrentUser({ }: { hasAuthenticatedUser: boolean; }) { - jest.mock('@aws-amplify/auth', () => ({ - Auth: { - currentAuthenticatedUser: () => { - return new Promise((res, rej) => { - if (hasAuthenticatedUser) { - res(hasAuthenticatedUser); - } else { - rej(hasAuthenticatedUser); - } - }); - }, + const currentAuthenticatedUserSpy = jest.fn().mockImplementation(() => { + return new Promise((res, rej) => { + if (hasAuthenticatedUser) { + res(hasAuthenticatedUser); + } else { + rej(hasAuthenticatedUser); + } + }); + }); + + jest.mock('@aws-amplify/auth/internals', () => ({ + InternalAuth: { + currentAuthenticatedUser: currentAuthenticatedUserSpy, }, GRAPHQL_AUTH_MODE, })); + + return currentAuthenticatedUserSpy; } diff --git a/packages/datastore/src/authModeStrategies/multiAuthStrategy.ts b/packages/datastore/src/authModeStrategies/multiAuthStrategy.ts index df1c84bfdff..515368f6ea4 100644 --- a/packages/datastore/src/authModeStrategies/multiAuthStrategy.ts +++ b/packages/datastore/src/authModeStrategies/multiAuthStrategy.ts @@ -1,6 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { Auth } from '@aws-amplify/auth'; +import { InternalAuth } from '@aws-amplify/auth/internals'; import { GRAPHQL_AUTH_MODE } from '@aws-amplify/api-graphql'; import { AuthModeStrategy, @@ -9,6 +9,7 @@ import { ModelAttributeAuthAllow, AmplifyContext, } from '../types'; +import { Category, DataStoreAction } from '@aws-amplify/core'; function getProviderFromRule( rule: ModelAttributeAuthProperty @@ -139,11 +140,14 @@ export const multiAuthStrategy: ( amplifyContext: AmplifyContext ) => AuthModeStrategy = (amplifyContext: AmplifyContext) => - async ({ schema, modelName }) => { - amplifyContext.Auth = amplifyContext.Auth || Auth; + async ({ schema, modelName, customUserAgentDetails }) => { + amplifyContext.InternalAuth = amplifyContext.InternalAuth || InternalAuth; let currentUser; try { - currentUser = await amplifyContext.Auth.currentAuthenticatedUser(); + currentUser = await amplifyContext.InternalAuth.currentAuthenticatedUser( + undefined, + customUserAgentDetails + ); } catch (e) { // No current user } diff --git a/packages/datastore/src/datastore/datastore.ts b/packages/datastore/src/datastore/datastore.ts index 98901289d6f..6df68ef12ed 100644 --- a/packages/datastore/src/datastore/datastore.ts +++ b/packages/datastore/src/datastore/datastore.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { InternalAPI } from '@aws-amplify/api/internals'; -import { Auth } from '@aws-amplify/auth'; +import { InternalAuth } from '@aws-amplify/auth/internals'; import { Cache } from '@aws-amplify/cache'; import { Amplify, @@ -1383,7 +1383,7 @@ enum DataStoreState { // https://github.com/aws-amplify/amplify-js/pull/10477/files#r1007363485 class DataStore { // reference to configured category instances. Used for preserving SSR context - private Auth = Auth; + private InternalAuth = InternalAuth; private InternalAPI = InternalAPI; private Cache = Cache; @@ -1414,7 +1414,7 @@ class DataStore { private storageAdapter!: Adapter; // object that gets passed to descendent classes. Allows us to pass these down by reference private amplifyContext: AmplifyContext = { - Auth: this.Auth, + InternalAuth: this.InternalAuth, InternalAPI: this.InternalAPI, Cache: this.Cache, }; @@ -2440,7 +2440,7 @@ class DataStore { }; configure = (config: DataStoreConfig = {}) => { - this.amplifyContext.Auth = this.Auth; + this.amplifyContext.InternalAuth = this.InternalAuth; this.amplifyContext.InternalAPI = this.InternalAPI; this.amplifyContext.Cache = this.Cache; diff --git a/packages/datastore/src/sync/constants.ts b/packages/datastore/src/sync/constants.ts new file mode 100644 index 00000000000..61a8e93eac7 --- /dev/null +++ b/packages/datastore/src/sync/constants.ts @@ -0,0 +1,17 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { + Category, + CustomUserAgentDetails, + DataStoreAction, +} from '@aws-amplify/core'; + +export const userAgentDetailsGraphQL: CustomUserAgentDetails = { + category: Category.DataStore, + action: DataStoreAction.GraphQl, +}; + +export const userAgentDetailsSubscribe: CustomUserAgentDetails = { + category: Category.DataStore, + action: DataStoreAction.Subscribe, +}; diff --git a/packages/datastore/src/sync/processors/mutation.ts b/packages/datastore/src/sync/processors/mutation.ts index cd073fc3c4d..17c34c2b888 100644 --- a/packages/datastore/src/sync/processors/mutation.ts +++ b/packages/datastore/src/sync/processors/mutation.ts @@ -3,10 +3,7 @@ import { GraphQLResult, GRAPHQL_AUTH_MODE } from '@aws-amplify/api'; import { InternalAPI } from '@aws-amplify/api/internals'; import { - Category, ConsoleLogger as Logger, - CustomUserAgentDetails, - DataStoreAction, jitteredBackoff, NonRetryableError, retry, @@ -14,6 +11,7 @@ import { } from '@aws-amplify/core'; import Observable, { ZenObservable } from 'zen-observable-ts'; import { MutationEvent } from '../'; +import { userAgentDetailsGraphQL as customUserAgentDetails } from '../constants'; import { ModelInstanceCreator } from '../../datastore/datastore'; import { ExclusiveStorage as Storage } from '../../storage/storage'; import { @@ -197,6 +195,7 @@ class MutationProcessor { this.amplifyConfig.aws_appsync_authenticationType, modelName: model, schema: this.schema, + customUserAgentDetails, }); const operationAuthModes = modelAuthModes[operation.toUpperCase()]; @@ -351,11 +350,6 @@ class MutationProcessor { const opType = this.opTypeFromTransformerOperation(operation); - const customUserAgentDetails: CustomUserAgentDetails = { - category: Category.DataStore, - action: DataStoreAction.GraphQl, - }; - do { try { const result = >>( diff --git a/packages/datastore/src/sync/processors/subscription.ts b/packages/datastore/src/sync/processors/subscription.ts index 7cc65e12848..421e8beaeac 100644 --- a/packages/datastore/src/sync/processors/subscription.ts +++ b/packages/datastore/src/sync/processors/subscription.ts @@ -2,19 +2,17 @@ // SPDX-License-Identifier: Apache-2.0 import { GraphQLResult, GRAPHQL_AUTH_MODE } from '@aws-amplify/api'; import { InternalAPI } from '@aws-amplify/api/internals'; -import { Auth } from '@aws-amplify/auth'; +import { InternalAuth } from '@aws-amplify/auth/internals'; import { Cache } from '@aws-amplify/cache'; import { - Category, ConsoleLogger as Logger, - CustomUserAgentDetails, - DataStoreAction, Hub, HubCapsule, BackgroundProcessManager, } from '@aws-amplify/core'; import { CONTROL_MSG as PUBSUB_CONTROL_MSG } from '@aws-amplify/pubsub'; import Observable, { ZenObservable } from 'zen-observable-ts'; +import { userAgentDetailsSubscribe as customUserAgentDetails } from '../constants'; import { InternalSchema, PersistentModel, @@ -86,7 +84,7 @@ class SubscriptionProcessor { private readonly authModeStrategy: AuthModeStrategy, private readonly errorHandler: ErrorHandler, private readonly amplifyContext: AmplifyContext = { - Auth, + InternalAuth, InternalAPI, Cache, } @@ -297,11 +295,14 @@ class SubscriptionProcessor { let cognitoTokenPayload: { [field: string]: any }, oidcTokenPayload: { [field: string]: any }; let userCredentials = USER_CREDENTIALS.none; + this.runningProcesses.add(async () => { try { // retrieving current AWS Credentials const credentials = - await this.amplifyContext.Auth.currentCredentials(); + await this.amplifyContext.InternalAuth.currentCredentials( + customUserAgentDetails + ); userCredentials = credentials.authenticated ? USER_CREDENTIALS.auth : USER_CREDENTIALS.unauth; @@ -311,7 +312,9 @@ class SubscriptionProcessor { try { // retrieving current token info from Cognito UserPools - const session = await this.amplifyContext.Auth.currentSession(); + const session = await this.amplifyContext.InternalAuth.currentSession( + customUserAgentDetails + ); cognitoTokenPayload = session.getIdToken().decodePayload(); } catch (err) { // best effort to get jwt from Cognito @@ -335,7 +338,10 @@ class SubscriptionProcessor { token = federatedInfo.token; } else { const currentUser = - await this.amplifyContext.Auth.currentAuthenticatedUser(); + await this.amplifyContext.InternalAuth.currentAuthenticatedUser( + undefined, + customUserAgentDetails + ); if (currentUser) { token = currentUser.token; } @@ -365,6 +371,7 @@ class SubscriptionProcessor { this.amplifyConfig.aws_appsync_authenticationType, modelName: modelDefinition.name, schema: this.schema, + customUserAgentDetails, }); // subscriptions are created only based on the READ auth mode(s) @@ -431,11 +438,6 @@ class SubscriptionProcessor { const variables = {}; - const customUserAgentDetails: CustomUserAgentDetails = { - category: Category.DataStore, - action: DataStoreAction.Subscribe, - }; - if (addFilter && predicatesGroup) { variables['filter'] = predicateToGraphQLFilter(predicatesGroup); diff --git a/packages/datastore/src/sync/processors/sync.ts b/packages/datastore/src/sync/processors/sync.ts index d561be7ffe9..24fddc0573f 100644 --- a/packages/datastore/src/sync/processors/sync.ts +++ b/packages/datastore/src/sync/processors/sync.ts @@ -3,6 +3,7 @@ import { GraphQLResult, GRAPHQL_AUTH_MODE } from '@aws-amplify/api'; import { InternalAPI } from '@aws-amplify/api/internals'; import Observable from 'zen-observable-ts'; +import { userAgentDetailsGraphQL as customUserAgentDetails } from '../constants'; import { InternalSchema, ModelInstanceMetadata, @@ -25,10 +26,7 @@ import { } from '../utils'; import { jitteredExponentialRetry, - Category, ConsoleLogger as Logger, - CustomUserAgentDetails, - DataStoreAction, Hub, NonRetryableError, BackgroundProcessManager, @@ -118,6 +116,7 @@ class SyncProcessor { defaultAuthMode: this.amplifyConfig.aws_appsync_authenticationType, modelName: modelDefinition.name, schema: this.schema, + customUserAgentDetails, }); // sync only needs the READ auth mode(s) @@ -219,11 +218,6 @@ class SyncProcessor { this.amplifyConfig ); - const customUserAgentDetails: CustomUserAgentDetails = { - category: Category.DataStore, - action: DataStoreAction.GraphQl, - }; - return await this.amplifyContext.InternalAPI.graphql( { query, diff --git a/packages/datastore/src/sync/utils.ts b/packages/datastore/src/sync/utils.ts index 0edce1a2b5d..9c14a93185a 100644 --- a/packages/datastore/src/sync/utils.ts +++ b/packages/datastore/src/sync/utils.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { GRAPHQL_AUTH_MODE } from '@aws-amplify/api-graphql'; import { GraphQLAuthError } from '@aws-amplify/api'; -import { Logger } from '@aws-amplify/core'; +import { CustomUserAgentDetails, Logger } from '@aws-amplify/core'; import { ModelInstanceCreator } from '../datastore/datastore'; import { AuthorizationRule, @@ -819,11 +819,13 @@ export async function getModelAuthModes({ defaultAuthMode, modelName, schema, + customUserAgentDetails, }: { authModeStrategy: AuthModeStrategy; defaultAuthMode: GRAPHQL_AUTH_MODE; modelName: string; schema: InternalSchema; + customUserAgentDetails?: CustomUserAgentDetails; }): Promise<{ [key in ModelOperation]: GRAPHQL_AUTH_MODE[]; }> { @@ -845,6 +847,7 @@ export async function getModelAuthModes({ schema, modelName, operation, + customUserAgentDetails, }); if (typeof authModes === 'string') { diff --git a/packages/datastore/src/types.ts b/packages/datastore/src/types.ts index e5468981a85..d09b1ec5fa6 100644 --- a/packages/datastore/src/types.ts +++ b/packages/datastore/src/types.ts @@ -16,10 +16,11 @@ import { } from './util'; import { PredicateAll } from './predicates'; import { GRAPHQL_AUTH_MODE } from '@aws-amplify/api-graphql'; -import { Auth } from '@aws-amplify/auth'; +import { InternalAuth } from '@aws-amplify/auth/internals'; import { InternalAPI } from '@aws-amplify/api/internals'; import { Cache } from '@aws-amplify/cache'; import { Adapter } from './storage/adapter'; +import { CustomUserAgentDetails } from '@aws-amplify/core'; export type Scalar = T extends Array ? InnerType : T; @@ -971,6 +972,7 @@ export type AuthModeStrategyParams = { schema: InternalSchema; modelName: string; operation: ModelOperation; + customUserAgentDetails?: CustomUserAgentDetails; }; export type AuthModeStrategy = ( @@ -1102,7 +1104,7 @@ export enum LimitTimerRaceResolvedValues { //#endregion export type AmplifyContext = { - Auth: typeof Auth; + InternalAuth: typeof InternalAuth; InternalAPI: typeof InternalAPI; Cache: typeof Cache; };