diff --git a/packages/adapter-nextjs/__tests__/runWithAmplifyServerContext.test.ts b/packages/adapter-nextjs/__tests__/runWithAmplifyServerContext.test.ts index f09284932b7..24daac1f39a 100644 --- a/packages/adapter-nextjs/__tests__/runWithAmplifyServerContext.test.ts +++ b/packages/adapter-nextjs/__tests__/runWithAmplifyServerContext.test.ts @@ -74,7 +74,7 @@ describe('runWithAmplifyServerContext', () => { describe('when amplifyConfig.Auth is defined', () => { describe('when nextServerContext is null (opt-in unauthenticated role)', () => { - it('should create auth providers with MemoryKeyValueStorage', () => { + it('should create auth providers with sharedInMemoryStorage', () => { const operation = jest.fn(); runWithAmplifyServerContext({ operation, nextServerContext: null }); expect( diff --git a/packages/adapter-nextjs/__tests__/utils/getAmplifyConfig.test.ts b/packages/adapter-nextjs/__tests__/utils/getAmplifyConfig.test.ts index 7d7f042155b..db38841e89c 100644 --- a/packages/adapter-nextjs/__tests__/utils/getAmplifyConfig.test.ts +++ b/packages/adapter-nextjs/__tests__/utils/getAmplifyConfig.test.ts @@ -1,32 +1,52 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import { NextConfig } from 'next'; +import getConfig from 'next/config'; import { getAmplifyConfig } from '../../src/utils/getAmplifyConfig'; +import { AmplifyError } from '@aws-amplify/core/internals/utils'; + +jest.mock('next/config'); + +const mockGetConfig = getConfig as jest.Mock; describe('getAmplifyConfig', () => { + const mockAmplifyConfig = { + Auth: { + identityPoolId: '123', + userPoolId: 'abc', + userPoolWebClientId: 'def', + }, + Storage: { + bucket: 'bucket', + region: 'us-east-1', + }, + }; + beforeEach(() => { + mockGetConfig.mockReturnValue({}); delete process.env.amplifyConfig; }); it('should return amplifyConfig from env vars', () => { - const mockAmplifyConfig = { - Auth: { - identityPoolId: '123', - userPoolId: 'abc', - userPoolWebClientId: 'def', - }, - Storage: { - bucket: 'bucket', - region: 'us-east-1', - }, - }; process.env.amplifyConfig = JSON.stringify(mockAmplifyConfig); const result = getAmplifyConfig(); expect(result).toEqual(mockAmplifyConfig); }); + it('should attempt to get amplifyConfig via getConfig provided by Next.js as a fallback', () => { + mockGetConfig.mockReturnValueOnce({ + serverRuntimeConfig: { + amplifyConfig: JSON.stringify(mockAmplifyConfig), + }, + }); + + const result = getAmplifyConfig(); + expect(result).toEqual(mockAmplifyConfig); + }); + it('should throw error when amplifyConfig is not found from env vars', () => { - expect(() => getAmplifyConfig()).toThrowError(); + expect(() => getAmplifyConfig()).toThrow(AmplifyError); }); }); diff --git a/packages/adapter-nextjs/__tests__/withAmplify.test.ts b/packages/adapter-nextjs/__tests__/withAmplify.test.ts index 4c050fb6d12..f943254b509 100644 --- a/packages/adapter-nextjs/__tests__/withAmplify.test.ts +++ b/packages/adapter-nextjs/__tests__/withAmplify.test.ts @@ -29,6 +29,9 @@ describe('withAmplify', () => { env: { amplifyConfig: JSON.stringify(mockAmplifyConfig), }, + serverRuntimeConfig: { + amplifyConfig: JSON.stringify(mockAmplifyConfig), + }, }); }); @@ -37,6 +40,9 @@ describe('withAmplify', () => { env: { existingKey: '123', }, + serverRuntimeConfig: { + myKey: 'myValue', + }, }; const result = withAmplify(nextConfig, mockAmplifyConfig); @@ -45,6 +51,10 @@ describe('withAmplify', () => { existingKey: '123', amplifyConfig: JSON.stringify(mockAmplifyConfig), }, + serverRuntimeConfig: { + myKey: 'myValue', + amplifyConfig: JSON.stringify(mockAmplifyConfig), + }, }); }); }); diff --git a/packages/adapter-nextjs/src/runWithAmplifyServerContext.ts b/packages/adapter-nextjs/src/runWithAmplifyServerContext.ts index d7122954418..27bbda0b0a3 100644 --- a/packages/adapter-nextjs/src/runWithAmplifyServerContext.ts +++ b/packages/adapter-nextjs/src/runWithAmplifyServerContext.ts @@ -14,6 +14,49 @@ import { runWithAmplifyServerContext as runWithAmplifyServerContextCore, } from 'aws-amplify/internals/adapter-core'; +/** + * Runs the {@link operation} with the the context created from the {@link nextServerContext}. + * + * @param input The input to call the {@link runWithAmplifyServerContext}. + * @param input.nextServerContext The Next.js server context. It varies depends + * where the {@link runWithAmplifyServerContext} is being called. + * - In the [middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware): + * the context consists of an instance of the `NextRequest` and an instance + * of the `NextResponse`. + * - In a [Page server component](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts#pages): + * the context is the [`cookies`](https://nextjs.org/docs/app/api-reference/functions/cookies) + * function provided by Next.js. + * - In a [Route Handler](https://nextjs.org/docs/app/building-your-application/routing/route-handlers): + * the context can be the [`cookies`](https://nextjs.org/docs/app/api-reference/functions/cookies) + * function or a combination of an instance of the `NextRequest` and an instance + * of the `NextResponse`. + * - In a [Server Action](https://nextjs.org/docs/app/building-your-application/data-fetching/forms-and-mutations#how-server-actions-work): + * the context is the [`cookies`](https://nextjs.org/docs/app/api-reference/functions/cookies) + * function provided by Next.js. + * @param input.operation The function that contains the business logic calling + * Amplify APIs. It expects a `contextSpec` parameter. + * @returns The result returned by the {@link operation}. + * @example + * // Use the `fetchAuthSession` API in the Next.js `middleware`. + * import { NextRequest, NextResponse } from "next/server"; + * import { fetchAuthSession } from "aws-amplify/auth/server"; + * import { runWithAmplifyServerContext } from "@aws-amplify/adapter-nextjs"; + + * export async function middleware(request: NextRequest) { + * const response = NextResponse.next(); + * const authenticated = await runWithAmplifyServerContext({ + * nextServerContext: { request, response }, + * operation: async (contextSpec) => { + * const session = await fetchAuthSession(contextSpec); + * return session.tokens !== undefined; + * } + * }); + * if (authenticated) { + * return response; + * } + * return NextResponse.redirect(new URL('/sign-in', request.url)); + * } + */ export const runWithAmplifyServerContext: NextServer.RunOperationWithContext = async ({ nextServerContext, operation }) => { // 1. get amplify config from env vars diff --git a/packages/adapter-nextjs/src/types/NextServer.ts b/packages/adapter-nextjs/src/types/NextServer.ts index ed7d8d79ae9..ed9f31ba606 100644 --- a/packages/adapter-nextjs/src/types/NextServer.ts +++ b/packages/adapter-nextjs/src/types/NextServer.ts @@ -51,12 +51,18 @@ export namespace NextServer { response: NextGetServerSidePropsContext['res']; }; + /** + * The union of possible Next.js app server context types. + */ export type Context = | NextRequestAndNextResponseContext | NextRequestAndResponseContext | ServerComponentContext | GetServerSidePropsContext; + /** + * The interface of the input of {@link RunOperationWithContext}. + */ export interface RunWithContextInput { nextServerContext: Context | null; operation: ( diff --git a/packages/adapter-nextjs/src/utils/getAmplifyConfig.ts b/packages/adapter-nextjs/src/utils/getAmplifyConfig.ts index b0697fdbfd0..8b00f207c0e 100644 --- a/packages/adapter-nextjs/src/utils/getAmplifyConfig.ts +++ b/packages/adapter-nextjs/src/utils/getAmplifyConfig.ts @@ -3,9 +3,19 @@ import { ResourcesConfig } from 'aws-amplify'; import { AmplifyServerContextError } from '@aws-amplify/core/internals/adapter-core'; +import getConfig from 'next/config'; export const getAmplifyConfig = (): ResourcesConfig => { - const configStr = process.env.amplifyConfig; + let configStr = process.env.amplifyConfig; + + // With a Next.js app that uses the Pages Router, the key-value pairs + // listed under the `env` field in the `next.config.js` is not accessible + // via process.env. at some occasion. Using the following as a fallback + // See: https://github.com/vercel/next.js/issues/39299 + if (!configStr) { + const { serverRuntimeConfig } = getConfig(); + configStr = serverRuntimeConfig?.amplifyConfig; + } if (!configStr) { throw new AmplifyServerContextError({ diff --git a/packages/adapter-nextjs/src/withAmplify.ts b/packages/adapter-nextjs/src/withAmplify.ts index 745b38f553e..1221196e80a 100644 --- a/packages/adapter-nextjs/src/withAmplify.ts +++ b/packages/adapter-nextjs/src/withAmplify.ts @@ -31,9 +31,15 @@ export const withAmplify = ( nextConfig: NextConfig, amplifyConfig: ResourcesConfig ) => { + const configStr = JSON.stringify(amplifyConfig); nextConfig.env = { ...nextConfig.env, - amplifyConfig: JSON.stringify(amplifyConfig), + amplifyConfig: configStr, + }; + + nextConfig.serverRuntimeConfig = { + ...nextConfig.serverRuntimeConfig, + amplifyConfig: configStr, }; return nextConfig; diff --git a/packages/adapter-nextjs/tslint.json b/packages/adapter-nextjs/tslint.json index 8eafab1d2b4..fcca611fccf 100644 --- a/packages/adapter-nextjs/tslint.json +++ b/packages/adapter-nextjs/tslint.json @@ -5,7 +5,13 @@ "jsRules": {}, "rules": { "prefer-const": true, - "max-line-length": [true, 120], + "max-line-length": [ + true, + { + "limit": 120, + "ignore-pattern": "^//|^ *" + } + ], "no-empty-interface": true, "no-var-keyword": true, "object-literal-shorthand": true, diff --git a/packages/analytics/src/index.ts b/packages/analytics/src/index.ts index 0a3ebe04134..215d6c33eab 100644 --- a/packages/analytics/src/index.ts +++ b/packages/analytics/src/index.ts @@ -1,5 +1,10 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -export * from './providers/pinpoint'; +export { + record, + identifyUser, + RecordInput, + IdentifyUserInput, +} from './providers/pinpoint'; export { AnalyticsError } from './errors'; diff --git a/packages/analytics/src/providers/pinpoint/apis/identifyUser.ts b/packages/analytics/src/providers/pinpoint/apis/identifyUser.ts index c8a303478e2..219f93adaca 100644 --- a/packages/analytics/src/providers/pinpoint/apis/identifyUser.ts +++ b/packages/analytics/src/providers/pinpoint/apis/identifyUser.ts @@ -5,21 +5,56 @@ import { AnalyticsAction } from '@aws-amplify/core/internals/utils'; import { updateEndpoint } from '@aws-amplify/core/internals/providers/pinpoint'; import { AnalyticsValidationErrorCode } from '../../../errors'; import { getAnalyticsUserAgentString } from '../../../utils/userAgent'; -import { IdentifyUserParameters, UpdateEndpointException } from '../types'; +import { IdentifyUserInput, UpdateEndpointException } from '../types'; import { resolveConfig, resolveCredentials } from '../utils'; /** - * Identifies the current user with Pinpoint. + * Sends information about a user to Pinpoint. Sending user information allows you to associate a user to their user + * profile and activities or actions in your application. Activity can be tracked across devices & platforms by using + * the same `userId`. * - * @param {IdentifyUserParameters} params parameters used to construct requests sent to Pinpoint's UpdateEndpoint API. + * @param {IdentifyUserParameters} params The input object used to construct requests sent to Pinpoint's UpdateEndpoint + * API. * - * @throws An {@link UpdateEndpointException} when the underlying Pinpoint service returns an error. - * @throws An {@link AnalyticsValidationErrorCode} when API call parameters are invalid. + * @throws service: {@link UpdateEndpointException} - Thrown when the underlying Pinpoint service returns an error. + * @throws validation: {@link AnalyticsValidationErrorCode} - Thrown when the provided parameters or library + * configuration is incorrect. + * + * @returns A promise that will resolve when the operation is complete. + * + * @example + * ```ts + * // Identify a user with Pinpoint + * await identifyUser({ + * userId, + * userProfile: { + * attributes: { + * email: [userEmail], + * }, + * } + * }); + * ``` + * + * @example + * ```ts + * // Identify a user with Pinpoint with some additional demographics + * await identifyUser({ + * userId, + * userProfile: { + * attributes: { + * email: [userEmail], + * }, + * demographic: { + * platform: 'ios', + * timezone: 'America/Los_Angeles' + * } + * } + * }); */ export const identifyUser = async ({ userId, userProfile, -}: IdentifyUserParameters): Promise => { +}: IdentifyUserInput): Promise => { const { credentials, identityId } = await resolveCredentials(); const { appId, region } = resolveConfig(); updateEndpoint({ diff --git a/packages/analytics/src/providers/pinpoint/apis/record.ts b/packages/analytics/src/providers/pinpoint/apis/record.ts index a7bd16d9696..5d33e5503a8 100644 --- a/packages/analytics/src/providers/pinpoint/apis/record.ts +++ b/packages/analytics/src/providers/pinpoint/apis/record.ts @@ -11,21 +11,46 @@ import { assertValidationError, } from '../../../errors'; import { getAnalyticsUserAgentString } from '../../../utils/userAgent'; -import { RecordParameters } from '../types/parameters'; +import { RecordInput } from '../types'; import { resolveConfig, resolveCredentials } from '../utils'; const logger = new Logger('Analytics'); /** - * Sends an event to Pinpoint. + * Records an Analytic event to Pinpoint. Events will be buffered and periodically sent to Pinpoint. * - * @param {RecordParameters} params Parameters used to construct the request. + * @param {RecordInput} params The input object used to construct the request. * - * @throws An {@link AnalyticsValidationErrorCode} when there is an error in the parameters or configuration. - * - * @returns A promise that will resolve when the request is complete. + * @throws validation: {@link AnalyticsValidationErrorCode} - Thrown when the provided parameters or library + * configuration is incorrect. + * + * @example + * ```ts + * // Send an event to Pinpoint + * record({ + * event: { + * name: eventName, + * } + * }) + * ``` + * + * @example + * ```ts + * // Send an event to Pinpoint with metrics & custom attributes + * record({ + * event: { + * name: eventName, + * attributes: { + * 'my-attribute': attributeValue + * }, + * metrics: { + * 'my-metric': metricValue + * } + * } + * }) + * ``` */ -export const record = ({ event }: RecordParameters): void => { +export const record = ({ event }: RecordInput): void => { const { appId, region } = resolveConfig(); assertValidationError(!!event, AnalyticsValidationErrorCode.NoEvent); diff --git a/packages/analytics/src/providers/pinpoint/index.ts b/packages/analytics/src/providers/pinpoint/index.ts index 6206929b659..7f06964f28f 100644 --- a/packages/analytics/src/providers/pinpoint/index.ts +++ b/packages/analytics/src/providers/pinpoint/index.ts @@ -1,4 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -export * from './apis'; +export { + record, + identifyUser +} from './apis'; +export { + RecordInput, + IdentifyUserInput +} from './types/inputs'; diff --git a/packages/analytics/src/providers/pinpoint/types/index.ts b/packages/analytics/src/providers/pinpoint/types/index.ts index d938f2d9631..060d9e35063 100644 --- a/packages/analytics/src/providers/pinpoint/types/index.ts +++ b/packages/analytics/src/providers/pinpoint/types/index.ts @@ -2,4 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 export { UpdateEndpointException } from './errors'; -export { IdentifyUserParameters } from './parameters'; +export { + RecordInput, + IdentifyUserInput +} from './inputs'; diff --git a/packages/analytics/src/providers/pinpoint/types/parameters.ts b/packages/analytics/src/providers/pinpoint/types/inputs.ts similarity index 87% rename from packages/analytics/src/providers/pinpoint/types/parameters.ts rename to packages/analytics/src/providers/pinpoint/types/inputs.ts index 35d46c34c29..feae0a39fbd 100644 --- a/packages/analytics/src/providers/pinpoint/types/parameters.ts +++ b/packages/analytics/src/providers/pinpoint/types/inputs.ts @@ -4,14 +4,14 @@ import { UserProfile } from '@aws-amplify/core'; import { PinpointAnalyticsEvent } from '@aws-amplify/core/internals/providers/pinpoint'; -export type RecordParameters = { +export type RecordInput = { /** * An event to send to the default Analytics provider. */ event: PinpointAnalyticsEvent; }; -export type IdentifyUserParameters = { +export type IdentifyUserInput = { /** * A User ID associated to the current device. */ diff --git a/packages/auth/__tests__/providers/cognito/confirmSignInErrorCases.test.ts b/packages/auth/__tests__/providers/cognito/confirmSignInErrorCases.test.ts index 46016896f90..94001b5f2d4 100644 --- a/packages/auth/__tests__/providers/cognito/confirmSignInErrorCases.test.ts +++ b/packages/auth/__tests__/providers/cognito/confirmSignInErrorCases.test.ts @@ -3,7 +3,6 @@ import { AuthValidationErrorCode } from '../../../src/errors/types/validation'; import { authAPITestParams } from './testUtils/authApiTestParams'; import { signIn } from '../../../src/providers/cognito/apis/signIn'; import * as signInHelpers from '../../../src/providers/cognito/utils/signInHelpers'; -import { AuthSignInStep } from '../../../src/types'; import { confirmSignIn } from '../../../src/providers/cognito/apis/confirmSignIn'; import { RespondToAuthChallengeException } from '../../../src/providers/cognito/types/errors'; import { RespondToAuthChallengeCommandOutput } from '../../../src/providers/cognito/utils/clients/CognitoIdentityProvider/types'; @@ -54,7 +53,7 @@ describe('confirmSignIn API error path cases:', () => { }); test(`confirmSignIn API should throw a validation AuthError when sign-in step is - ${AuthSignInStep.CONTINUE_SIGN_IN_WITH_MFA_SELECTION} and challengeResponse is not "SMS" or "TOTP" `, async () => { + ${'CONTINUE_SIGN_IN_WITH_MFA_SELECTION'} and challengeResponse is not "SMS" or "TOTP" `, async () => { expect.assertions(2); try { await signIn({ username, password }); diff --git a/packages/auth/__tests__/providers/cognito/confirmSignInHappyCases.test.ts b/packages/auth/__tests__/providers/cognito/confirmSignInHappyCases.test.ts index ffafa285422..6fdd4879e23 100644 --- a/packages/auth/__tests__/providers/cognito/confirmSignInHappyCases.test.ts +++ b/packages/auth/__tests__/providers/cognito/confirmSignInHappyCases.test.ts @@ -5,7 +5,6 @@ import { Amplify } from '@aws-amplify/core'; import { authAPITestParams } from './testUtils/authApiTestParams'; import { signIn } from '../../../src/providers/cognito/apis/signIn'; import * as signInHelpers from '../../../src/providers/cognito/utils/signInHelpers'; -import { AuthSignInStep } from '../../../src/types'; import { confirmSignIn } from '../../../src/providers/cognito/apis/confirmSignIn'; import { RespondToAuthChallengeCommandOutput } from '../../../src/providers/cognito/utils/clients/CognitoIdentityProvider/types'; import { cognitoCredentialsProvider } from '../../../src/providers/cognito/credentialsProvider'; @@ -84,7 +83,7 @@ describe('confirmSignIn API happy path cases', () => { expect(signInResult).toEqual({ isSignedIn: false, nextStep: { - signInStep: AuthSignInStep.CONFIRM_SIGN_IN_WITH_SMS_CODE, + signInStep: 'CONFIRM_SIGN_IN_WITH_SMS_CODE', codeDeliveryDetails: { deliveryMedium: 'SMS', destination: '*******9878', @@ -94,7 +93,7 @@ describe('confirmSignIn API happy path cases', () => { expect(confirmSignInResult).toEqual({ isSignedIn: true, nextStep: { - signInStep: AuthSignInStep.DONE, + signInStep: 'DONE', }, }); @@ -128,13 +127,13 @@ describe('confirmSignIn API happy path cases', () => { expect(signInResult).toEqual({ isSignedIn: false, nextStep: { - signInStep: AuthSignInStep.CONFIRM_SIGN_IN_WITH_TOTP_CODE, + signInStep: 'CONFIRM_SIGN_IN_WITH_TOTP_CODE', }, }); expect(confirmSignInResult).toEqual({ isSignedIn: true, nextStep: { - signInStep: AuthSignInStep.DONE, + signInStep: 'DONE', }, }); @@ -185,7 +184,7 @@ describe('confirmSignIn API happy path cases', () => { expect(signInResult).toEqual({ isSignedIn: false, nextStep: { - signInStep: AuthSignInStep.CONTINUE_SIGN_IN_WITH_MFA_SELECTION, + signInStep: 'CONTINUE_SIGN_IN_WITH_MFA_SELECTION', allowedMFATypes: ['SMS', 'TOTP'], }, }); @@ -193,7 +192,7 @@ describe('confirmSignIn API happy path cases', () => { expect(confirmSignInResult).toEqual({ isSignedIn: false, nextStep: { - signInStep: AuthSignInStep.CONFIRM_SIGN_IN_WITH_SMS_CODE, + signInStep: 'CONFIRM_SIGN_IN_WITH_SMS_CODE', codeDeliveryDetails: { deliveryMedium: 'SMS', destination: '*******9878', diff --git a/packages/auth/__tests__/providers/cognito/confirmSignUp.test.ts b/packages/auth/__tests__/providers/cognito/confirmSignUp.test.ts index 2336ea89f81..55657207274 100644 --- a/packages/auth/__tests__/providers/cognito/confirmSignUp.test.ts +++ b/packages/auth/__tests__/providers/cognito/confirmSignUp.test.ts @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 import { confirmSignUp } from '../../../src/providers/cognito'; -import { AuthSignUpStep } from '../../../src/types'; import * as confirmSignUpClient from '../../../src/providers/cognito/utils/clients/CognitoIdentityProvider'; import { authAPITestParams } from './testUtils/authApiTestParams'; import { AuthValidationErrorCode } from '../../../src/errors/types/validation'; @@ -46,7 +45,7 @@ describe('confirmSignUp API Happy Path Cases:', () => { expect(result).toEqual({ isSignUpComplete: true, nextStep: { - signUpStep: AuthSignUpStep.DONE, + signUpStep: 'DONE', }, }); expect(confirmSignUpClientSpy).toHaveBeenCalledWith( diff --git a/packages/auth/__tests__/providers/cognito/credentialsProvider.test.ts b/packages/auth/__tests__/providers/cognito/credentialsProvider.test.ts index e0e64522e17..916c0e0f194 100644 --- a/packages/auth/__tests__/providers/cognito/credentialsProvider.test.ts +++ b/packages/auth/__tests__/providers/cognito/credentialsProvider.test.ts @@ -10,9 +10,9 @@ import { AuthError } from '../../../src/errors/AuthError'; import { GetCredentialsForIdentityInput, GetCredentialsForIdentityOutput, - MemoryKeyValueStorage, ResourcesConfig, getCredentialsForIdentity, + sharedInMemoryStorage, } from '@aws-amplify/core'; jest.mock('@aws-amplify/core', () => ({ @@ -73,7 +73,7 @@ describe('Guest Credentials', () => { beforeEach(() => { cognitoCredentialsProvider = new CognitoAWSCredentialsAndIdentityIdProvider( - new DefaultIdentityIdStore(MemoryKeyValueStorage) + new DefaultIdentityIdStore(sharedInMemoryStorage) ); credentialsForIdentityIdSpy.mockImplementationOnce( async ({}, params: GetCredentialsForIdentityInput) => { @@ -125,7 +125,7 @@ describe('Guest Credentials', () => { beforeEach(() => { cognitoCredentialsProvider = new CognitoAWSCredentialsAndIdentityIdProvider( - new DefaultIdentityIdStore(MemoryKeyValueStorage) + new DefaultIdentityIdStore(sharedInMemoryStorage) ); credentialsForIdentityIdSpy.mockImplementationOnce( async ({}, params: GetCredentialsForIdentityInput) => { @@ -164,7 +164,7 @@ describe('Primary Credentials', () => { beforeEach(() => { cognitoCredentialsProvider = new CognitoAWSCredentialsAndIdentityIdProvider( - new DefaultIdentityIdStore(MemoryKeyValueStorage) + new DefaultIdentityIdStore(sharedInMemoryStorage) ); credentialsForIdentityIdSpy.mockImplementation( async ({}, params: GetCredentialsForIdentityInput) => { @@ -245,7 +245,7 @@ describe('Primary Credentials', () => { beforeEach(() => { cognitoCredentialsProvider = new CognitoAWSCredentialsAndIdentityIdProvider( - new DefaultIdentityIdStore(MemoryKeyValueStorage) + new DefaultIdentityIdStore(sharedInMemoryStorage) ); }); afterEach(() => { diff --git a/packages/auth/__tests__/providers/cognito/signInWithSRP.test.ts b/packages/auth/__tests__/providers/cognito/signInWithSRP.test.ts index 0b6dfaf6340..be8a84f3228 100644 --- a/packages/auth/__tests__/providers/cognito/signInWithSRP.test.ts +++ b/packages/auth/__tests__/providers/cognito/signInWithSRP.test.ts @@ -1,8 +1,5 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 - -import { AmplifyErrorString } from '@aws-amplify/core/internals/utils'; - import { AuthError } from '../../../src/errors/AuthError'; import { AuthValidationErrorCode } from '../../../src/errors/types/validation'; import { authAPITestParams } from './testUtils/authApiTestParams'; @@ -14,7 +11,6 @@ import { RespondToAuthChallengeCommandOutput } from '../../../src/providers/cogn import { Amplify } from 'aws-amplify'; import { fetchTransferHandler } from '@aws-amplify/core/internals/aws-client-utils'; import { buildMockErrorResponse, mockJsonResponse } from './testUtils/data'; -import { cognitoCredentialsProvider } from '../../../src/providers/cognito/credentialsProvider'; import { CognitoUserPoolsTokenProvider } from '../../../src/providers/cognito/tokenProvider'; jest.mock('@aws-amplify/core/lib/clients/handlers/fetch'); diff --git a/packages/auth/__tests__/providers/cognito/signUp.test.ts b/packages/auth/__tests__/providers/cognito/signUp.test.ts index f847961bc35..c9ceaca4541 100644 --- a/packages/auth/__tests__/providers/cognito/signUp.test.ts +++ b/packages/auth/__tests__/providers/cognito/signUp.test.ts @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 import { signUp } from '../../../src/providers/cognito'; -import { AuthSignUpStep } from '../../../src/types'; import * as signUpClient from '../../../src/providers/cognito/utils/clients/CognitoIdentityProvider'; import { authAPITestParams } from './testUtils/authApiTestParams'; import { AuthValidationErrorCode } from '../../../src/errors/types/validation'; @@ -46,7 +45,7 @@ describe('SignUp API Happy Path Cases:', () => { expect(result).toEqual({ isSignUpComplete: false, nextStep: { - signUpStep: AuthSignUpStep.CONFIRM_SIGN_UP, + signUpStep: 'CONFIRM_SIGN_UP', codeDeliveryDetails: { destination: user1.email, deliveryMedium: 'EMAIL', diff --git a/packages/auth/__tests__/providers/cognito/testUtils/authApiTestParams.ts b/packages/auth/__tests__/providers/cognito/testUtils/authApiTestParams.ts index 56d1eeadfbf..ecdf97d5120 100644 --- a/packages/auth/__tests__/providers/cognito/testUtils/authApiTestParams.ts +++ b/packages/auth/__tests__/providers/cognito/testUtils/authApiTestParams.ts @@ -2,11 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { decodeJWT } from '@aws-amplify/core/internals/utils'; -import { - AuthResetPasswordStep, - AuthSignInResult, - AuthSignInStep, -} from '../../../../src/types'; +import { AuthSignInResult } from '../../../../src/types'; export const authAPITestParams = { user1: { @@ -41,7 +37,7 @@ export const authAPITestParams = { resetPasswordResult: { isPasswordReset: false, nextStep: { - resetPasswordStep: AuthResetPasswordStep.CONFIRM_RESET_PASSWORD_WITH_CODE, + resetPasswordStep: 'CONFIRM_RESET_PASSWORD_WITH_CODE', codeDeliveryDetails: { destination: 'test@email.com', deliveryMedium: 'EMAIL', @@ -202,7 +198,7 @@ export const authAPITestParams = { return { isSignedIn: false, nextStep: { - signInStep: AuthSignInStep.CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE, + signInStep: 'CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE', }, }; }, @@ -210,7 +206,7 @@ export const authAPITestParams = { return { isSignedIn: true, nextStep: { - signInStep: AuthSignInStep.DONE, + signInStep: 'DONE', }, }; }, diff --git a/packages/auth/__tests__/providers/cognito/updateUserAttributes.test.ts b/packages/auth/__tests__/providers/cognito/updateUserAttributes.test.ts index 2a10e1d04fe..04434b173e3 100644 --- a/packages/auth/__tests__/providers/cognito/updateUserAttributes.test.ts +++ b/packages/auth/__tests__/providers/cognito/updateUserAttributes.test.ts @@ -12,7 +12,6 @@ import { fetchTransferHandler } from '@aws-amplify/core/internals/aws-client-uti import { buildMockErrorResponse, mockJsonResponse } from './testUtils/data'; import { UpdateUserAttributesCommandOutput } from '../../../src/providers/cognito/utils/clients/CognitoIdentityProvider/types'; import { toAttributeType } from '../../../src/providers/cognito/utils/apiHelpers'; -import { AuthUpdateAttributeStep } from '../../../src/types/enums'; jest.mock('@aws-amplify/core/lib/clients/handlers/fetch'); Amplify.configure({ @@ -86,22 +85,21 @@ describe('updateUserAttributes API happy path cases', () => { expect(result.address).toEqual({ isUpdated: true, nextStep: { - updateAttributeStep: AuthUpdateAttributeStep.DONE, + updateAttributeStep: 'DONE', }, }); expect(result.name).toEqual({ isUpdated: true, nextStep: { - updateAttributeStep: AuthUpdateAttributeStep.DONE, + updateAttributeStep: 'DONE', }, }); expect(result.email).toEqual({ isUpdated: false, nextStep: { - updateAttributeStep: - AuthUpdateAttributeStep.CONFIRM_ATTRIBUTE_WITH_CODE, + updateAttributeStep: 'CONFIRM_ATTRIBUTE_WITH_CODE', codeDeliveryDetails: { attributeName: 'email', deliveryMedium: 'EMAIL', @@ -113,8 +111,7 @@ describe('updateUserAttributes API happy path cases', () => { expect(result.phone_number).toEqual({ isUpdated: false, nextStep: { - updateAttributeStep: - AuthUpdateAttributeStep.CONFIRM_ATTRIBUTE_WITH_CODE, + updateAttributeStep: 'CONFIRM_ATTRIBUTE_WITH_CODE', codeDeliveryDetails: { attributeName: 'phone_number', deliveryMedium: 'SMS', @@ -157,14 +154,14 @@ describe('updateUserAttributes API happy path cases', () => { expect(result.address).toEqual({ isUpdated: true, nextStep: { - updateAttributeStep: AuthUpdateAttributeStep.DONE, + updateAttributeStep: 'DONE', }, }); expect(result.name).toEqual({ isUpdated: true, nextStep: { - updateAttributeStep: AuthUpdateAttributeStep.DONE, + updateAttributeStep: 'DONE', }, }); @@ -215,8 +212,7 @@ describe('updateUserAttributes API happy path cases', () => { expect(result.email).toEqual({ isUpdated: false, nextStep: { - updateAttributeStep: - AuthUpdateAttributeStep.CONFIRM_ATTRIBUTE_WITH_CODE, + updateAttributeStep: 'CONFIRM_ATTRIBUTE_WITH_CODE', codeDeliveryDetails: { attributeName: 'email', deliveryMedium: 'EMAIL', @@ -228,8 +224,7 @@ describe('updateUserAttributes API happy path cases', () => { expect(result.phone_number).toEqual({ isUpdated: false, nextStep: { - updateAttributeStep: - AuthUpdateAttributeStep.CONFIRM_ATTRIBUTE_WITH_CODE, + updateAttributeStep: 'CONFIRM_ATTRIBUTE_WITH_CODE', codeDeliveryDetails: { attributeName: 'phone_number', deliveryMedium: 'SMS', diff --git a/packages/auth/package.json b/packages/auth/package.json index 212ad073413..707e27a39c3 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -25,7 +25,7 @@ "clean:size": "rimraf dual-publish-tmp tmp*", "format": "echo \"Not implemented\"", "lint": "tslint '{src}/**/*.ts' && npm run ts-coverage", - "ts-coverage": "typescript-coverage-report -p ./tsconfig.json -t 91.23" + "ts-coverage": "typescript-coverage-report -p ./tsconfig.json -t 91.19" }, "typesVersions": { ">=3.8": { diff --git a/packages/auth/src/index.ts b/packages/auth/src/index.ts index 4772a57a421..96975b4ebee 100644 --- a/packages/auth/src/index.ts +++ b/packages/auth/src/index.ts @@ -22,12 +22,6 @@ export { fetchUserAttributes, signOut, } from './providers/cognito'; -export { - AuthResetPasswordStep, - AuthSignInStep, - AuthSignUpStep, - AuthUpdateAttributeStep -} from './types/enums'; export { AuthError } from './errors/AuthError'; diff --git a/packages/auth/src/providers/cognito/apis/confirmSignIn.ts b/packages/auth/src/providers/cognito/apis/confirmSignIn.ts index 30a3b44a211..7bed27595a0 100644 --- a/packages/auth/src/providers/cognito/apis/confirmSignIn.ts +++ b/packages/auth/src/providers/cognito/apis/confirmSignIn.ts @@ -8,7 +8,6 @@ import { } from '../types/errors'; import { AuthSignInResult, - AuthSignInStep, ConfirmSignInRequest, } from '../../../types'; import { CognitoConfirmSignInOptions } from '../types'; @@ -119,7 +118,7 @@ export async function confirmSignIn( await cacheCognitoTokens(AuthenticationResult); return { isSignedIn: true, - nextStep: { signInStep: AuthSignInStep.DONE }, + nextStep: { signInStep: 'DONE' }, }; } diff --git a/packages/auth/src/providers/cognito/apis/confirmSignUp.ts b/packages/auth/src/providers/cognito/apis/confirmSignUp.ts index 8298c0d1485..8ba0ab36f6b 100644 --- a/packages/auth/src/providers/cognito/apis/confirmSignUp.ts +++ b/packages/auth/src/providers/cognito/apis/confirmSignUp.ts @@ -5,7 +5,6 @@ import { Amplify } from '@aws-amplify/core'; import { assertTokenProviderConfig } from '@aws-amplify/core/internals/utils'; import { AuthSignUpResult, - AuthSignUpStep, AuthStandardAttributeKey, ConfirmSignUpRequest, } from '../../../types'; @@ -61,7 +60,7 @@ export async function confirmSignUp( return { isSignUpComplete: true, nextStep: { - signUpStep: AuthSignUpStep.DONE, + signUpStep: 'DONE', }, }; } diff --git a/packages/auth/src/providers/cognito/apis/resetPassword.ts b/packages/auth/src/providers/cognito/apis/resetPassword.ts index d2aff689bd6..6d23aaecce6 100644 --- a/packages/auth/src/providers/cognito/apis/resetPassword.ts +++ b/packages/auth/src/providers/cognito/apis/resetPassword.ts @@ -6,7 +6,6 @@ import { assertTokenProviderConfig } from '@aws-amplify/core/internals/utils'; import { AuthValidationErrorCode } from '../../../errors/types/validation'; import { assertValidationError } from '../../../errors/utils/assertValidationError'; import { - AuthResetPasswordStep, AuthStandardAttributeKey, DeliveryMedium, ResetPasswordRequest, @@ -54,7 +53,7 @@ export async function resetPassword( return { isPasswordReset: false, nextStep: { - resetPasswordStep: AuthResetPasswordStep.CONFIRM_RESET_PASSWORD_WITH_CODE, + resetPasswordStep: 'CONFIRM_RESET_PASSWORD_WITH_CODE', codeDeliveryDetails: { deliveryMedium: codeDeliveryDetails?.DeliveryMedium as DeliveryMedium, destination: codeDeliveryDetails?.Destination as string, diff --git a/packages/auth/src/providers/cognito/apis/signInWithCustomAuth.ts b/packages/auth/src/providers/cognito/apis/signInWithCustomAuth.ts index 49b0d067e68..9870c14adad 100644 --- a/packages/auth/src/providers/cognito/apis/signInWithCustomAuth.ts +++ b/packages/auth/src/providers/cognito/apis/signInWithCustomAuth.ts @@ -6,7 +6,6 @@ import { assertValidationError } from '../../../errors/utils/assertValidationErr import { SignInRequest, AuthSignInResult, - AuthSignInStep, } from '../../../types'; import { assertServiceError } from '../../../errors/utils/assertServiceError'; import { @@ -74,7 +73,7 @@ export async function signInWithCustomAuth( await cacheCognitoTokens(AuthenticationResult); return { isSignedIn: true, - nextStep: { signInStep: AuthSignInStep.DONE }, + nextStep: { signInStep: 'DONE' }, }; } diff --git a/packages/auth/src/providers/cognito/apis/signInWithCustomSRPAuth.ts b/packages/auth/src/providers/cognito/apis/signInWithCustomSRPAuth.ts index b822b89737d..2eaefdb552a 100644 --- a/packages/auth/src/providers/cognito/apis/signInWithCustomSRPAuth.ts +++ b/packages/auth/src/providers/cognito/apis/signInWithCustomSRPAuth.ts @@ -18,7 +18,6 @@ import { import { SignInRequest, AuthSignInResult, - AuthSignInStep, } from '../../../types'; import { CognitoSignInOptions } from '../types'; import { @@ -78,7 +77,7 @@ export async function signInWithCustomSRPAuth( cleanActiveSignInState(); return { isSignedIn: true, - nextStep: { signInStep: AuthSignInStep.DONE }, + nextStep: { signInStep: 'DONE' }, }; } diff --git a/packages/auth/src/providers/cognito/apis/signInWithSRP.ts b/packages/auth/src/providers/cognito/apis/signInWithSRP.ts index f0f806cbc40..802fba6076b 100644 --- a/packages/auth/src/providers/cognito/apis/signInWithSRP.ts +++ b/packages/auth/src/providers/cognito/apis/signInWithSRP.ts @@ -23,7 +23,6 @@ import { CognitoSignInOptions } from '../types'; import { SignInRequest, AuthSignInResult, - AuthSignInStep, } from '../../../types'; import { setActiveSignInState, @@ -83,7 +82,7 @@ export async function signInWithSRP( await cacheCognitoTokens(AuthenticationResult); return { isSignedIn: true, - nextStep: { signInStep: AuthSignInStep.DONE }, + nextStep: { signInStep: 'DONE' }, }; } diff --git a/packages/auth/src/providers/cognito/apis/signInWithUserPassword.ts b/packages/auth/src/providers/cognito/apis/signInWithUserPassword.ts index bd06a91fed1..c07f18f4096 100644 --- a/packages/auth/src/providers/cognito/apis/signInWithUserPassword.ts +++ b/packages/auth/src/providers/cognito/apis/signInWithUserPassword.ts @@ -4,11 +4,7 @@ import { AuthValidationErrorCode } from '../../../errors/types/validation'; import { assertServiceError } from '../../../errors/utils/assertServiceError'; import { assertValidationError } from '../../../errors/utils/assertValidationError'; -import { - AuthSignInResult, - AuthSignInStep, - SignInRequest, -} from '../../../types'; +import { AuthSignInResult, SignInRequest } from '../../../types'; import { ChallengeName, ChallengeParameters, @@ -79,7 +75,7 @@ export async function signInWithUserPassword( cleanActiveSignInState(); return { isSignedIn: true, - nextStep: { signInStep: AuthSignInStep.DONE }, + nextStep: { signInStep: 'DONE' }, }; } diff --git a/packages/auth/src/providers/cognito/apis/signUp.ts b/packages/auth/src/providers/cognito/apis/signUp.ts index 500447c3022..79708b6cfce 100644 --- a/packages/auth/src/providers/cognito/apis/signUp.ts +++ b/packages/auth/src/providers/cognito/apis/signUp.ts @@ -5,7 +5,6 @@ import { Amplify } from '@aws-amplify/core'; import { assertTokenProviderConfig } from '@aws-amplify/core/internals/utils'; import { AuthSignUpResult, - AuthSignUpStep, AuthStandardAttributeKey, DeliveryMedium, SignUpRequest, @@ -79,14 +78,14 @@ export async function signUp( return { isSignUpComplete: true, nextStep: { - signUpStep: AuthSignUpStep.DONE, + signUpStep: 'DONE', }, }; } else { return { isSignUpComplete: false, nextStep: { - signUpStep: AuthSignUpStep.CONFIRM_SIGN_UP, + signUpStep: 'CONFIRM_SIGN_UP', codeDeliveryDetails: { deliveryMedium: CodeDeliveryDetails?.DeliveryMedium as DeliveryMedium, destination: CodeDeliveryDetails?.Destination as string, diff --git a/packages/auth/src/providers/cognito/apis/updateUserAttributes.ts b/packages/auth/src/providers/cognito/apis/updateUserAttributes.ts index 3aafdf04beb..d052024da4b 100644 --- a/packages/auth/src/providers/cognito/apis/updateUserAttributes.ts +++ b/packages/auth/src/providers/cognito/apis/updateUserAttributes.ts @@ -19,7 +19,6 @@ import { assertAuthTokens } from '../utils/types'; import { getRegion } from '../utils/clients/CognitoIdentityProvider/utils'; import { toAttributeType } from '../utils/apiHelpers'; import { CodeDeliveryDetailsType } from '../utils/clients/CognitoIdentityProvider/types'; -import { AuthUpdateAttributeStep } from '../../../types/enums'; import { UpdateUserAttributesException } from '../types/errors'; /** @@ -68,7 +67,7 @@ function getConfirmedAttributes( confirmedAttributes[key] = { isUpdated: true, nextStep: { - updateAttributeStep: AuthUpdateAttributeStep.DONE, + updateAttributeStep: 'DONE', }, }; }); @@ -86,8 +85,7 @@ function getUnConfirmedAttributes( unConfirmedAttributes[AttributeName] = { isUpdated: false, nextStep: { - updateAttributeStep: - AuthUpdateAttributeStep.CONFIRM_ATTRIBUTE_WITH_CODE, + updateAttributeStep: 'CONFIRM_ATTRIBUTE_WITH_CODE', codeDeliveryDetails: { attributeName: AttributeName, deliveryMedium: DeliveryMedium as DeliveryMedium, diff --git a/packages/auth/src/providers/cognito/utils/signInHelpers.ts b/packages/auth/src/providers/cognito/utils/signInHelpers.ts index f55a200011e..dedc84075e7 100644 --- a/packages/auth/src/providers/cognito/utils/signInHelpers.ts +++ b/packages/auth/src/providers/cognito/utils/signInHelpers.ts @@ -16,7 +16,6 @@ import { ClientMetadata, CognitoConfirmSignInOptions } from '../types'; import { AdditionalInfo, AuthSignInResult, - AuthSignInStep, DeliveryMedium, } from '../../../types'; import { AuthError } from '../../../errors/AuthError'; @@ -385,7 +384,7 @@ export async function getSignInResult(params: { return { isSignedIn: false, nextStep: { - signInStep: AuthSignInStep.CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE, + signInStep: 'CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE', additionalInfo: challengeParameters as AdditionalInfo, }, }; @@ -413,7 +412,7 @@ export async function getSignInResult(params: { return { isSignedIn: false, nextStep: { - signInStep: AuthSignInStep.CONTINUE_SIGN_IN_WITH_TOTP_SETUP, + signInStep: 'CONTINUE_SIGN_IN_WITH_TOTP_SETUP', totpSetupDetails: getTOTPSetupDetails(secretCode!, username), }, }; @@ -421,7 +420,7 @@ export async function getSignInResult(params: { return { isSignedIn: false, nextStep: { - signInStep: AuthSignInStep.CONFIRM_SIGN_IN_WITH_NEW_PASSWORD_REQUIRED, + signInStep: 'CONFIRM_SIGN_IN_WITH_NEW_PASSWORD_REQUIRED', missingAttributes: parseAttributes( challengeParameters.requiredAttributes ), @@ -431,7 +430,7 @@ export async function getSignInResult(params: { return { isSignedIn: false, nextStep: { - signInStep: AuthSignInStep.CONTINUE_SIGN_IN_WITH_MFA_SELECTION, + signInStep: 'CONTINUE_SIGN_IN_WITH_MFA_SELECTION', allowedMFATypes: getMFATypes( parseMFATypes(challengeParameters.MFAS_CAN_CHOOSE) ), @@ -441,7 +440,7 @@ export async function getSignInResult(params: { return { isSignedIn: false, nextStep: { - signInStep: AuthSignInStep.CONFIRM_SIGN_IN_WITH_SMS_CODE, + signInStep: 'CONFIRM_SIGN_IN_WITH_SMS_CODE', codeDeliveryDetails: { deliveryMedium: challengeParameters.CODE_DELIVERY_DELIVERY_MEDIUM as DeliveryMedium, @@ -453,7 +452,7 @@ export async function getSignInResult(params: { return { isSignedIn: false, nextStep: { - signInStep: AuthSignInStep.CONFIRM_SIGN_IN_WITH_TOTP_CODE, + signInStep: 'CONFIRM_SIGN_IN_WITH_TOTP_CODE', }, }; case 'ADMIN_NO_SRP_AUTH': @@ -496,12 +495,12 @@ export function getSignInResultFromError( if (errorName === InitiateAuthException.PasswordResetRequiredException) { return { isSignedIn: false, - nextStep: { signInStep: AuthSignInStep.RESET_PASSWORD }, + nextStep: { signInStep: 'RESET_PASSWORD' }, }; } else if (errorName === InitiateAuthException.UserNotConfirmedException) { return { isSignedIn: false, - nextStep: { signInStep: AuthSignInStep.CONFIRM_SIGN_UP }, + nextStep: { signInStep: 'CONFIRM_SIGN_UP' }, }; } } diff --git a/packages/auth/src/types/enums.ts b/packages/auth/src/types/enums.ts deleted file mode 100644 index d8dc19d6d24..00000000000 --- a/packages/auth/src/types/enums.ts +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -/** - * Denotes the next step in the Reset Password process. - */ -export enum AuthResetPasswordStep { - CONFIRM_RESET_PASSWORD_WITH_CODE = 'CONFIRM_RESET_PASSWORD_WITH_CODE', - DONE = 'DONE', -} - -/** - * Denotes the next step in the Sign In process. - */ -export enum AuthSignInStep { - /** - * Auth step requires user to use SMS as multifactor authentication by retriving a code sent to cellphone. - * - * ```typescript - * // Example - * - * // Code retrieved from cellphone - * const smsCode = '112233' - * await confirmSignIn({challengeResponse: smsCode}) - * ``` - */ - CONFIRM_SIGN_IN_WITH_SMS_CODE = 'CONFIRM_SIGN_IN_WITH_SMS_CODE', - - /** - * Auth step requires user to respond to a custom challenge. - * - * ```typescript - * // Example - * - * const challengeAnswer = 'my-custom-response' - * await confirmSignIn({challengeResponse: challengeAnswer}) - * ``` - */ - CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE = 'CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE', - - /** - * Auth step requires user to change their password with any requierd attributes. - * - * ```typescript - * // Example - * - * const attributes = { - * email: 'email@email' - * phone_number: '+11111111111' - * } - * const newPassword = 'my-new-password' - * await confirmSignIn({ - * challengeResponse: newPassword, - * options: { - * serviceOptions: { - * userAttributes: attributes - * } - * } - * }) - * ``` - */ - CONFIRM_SIGN_IN_WITH_NEW_PASSWORD_REQUIRED = 'CONFIRM_SIGN_IN_WITH_NEW_PASSWORD_REQUIRED', - - /** - * Auth step requires user to use TOTP as multifactor authentication by retriving an OTP code from authenticator app. - * - * ```typescript - * // Example - * - * // Code retrieved from authenticator app - * const otpCode = '112233' - * await confirmSignIn({challengeResponse: otpCode}) - - * ``` - */ - CONFIRM_SIGN_IN_WITH_TOTP_CODE = 'CONFIRM_SIGN_IN_WITH_TOTP_CODE', - - /** - * Auth step requires user to set up TOTP as multifactor authentication by associating an authenticator app - * and retriving an OTP code. - * - * ```typescript - * // Example - * - * // Code retrieved from authenticator app - * const otpCode = '112233' - * await confirmSignIn({challengeResponse: otpCode}) - - * ``` - */ - CONTINUE_SIGN_IN_WITH_TOTP_SETUP = 'CONTINUE_SIGN_IN_WITH_TOTP_SETUP', - - /** - * Auth step requires user to select an mfa option(SMS | TOTP) to continue with the sign-in flow. - * - * ```typescript - * // Example - * - * await confirmSignIn({challengeResponse:'TOTP'}) - * // OR - * await confirmSignIn({challengeResponse:'SMS'}) - * ``` - */ - CONTINUE_SIGN_IN_WITH_MFA_SELECTION = 'CONTINUE_SIGN_IN_WITH_MFA_SELECTION', - - /** - * Auth step requires to confirm user's sign-up. - * - * Try calling confirmSignUp. - */ - CONFIRM_SIGN_UP = 'CONFIRM_SIGN_UP', - - /** - * Auth step requires user to chage their password. - * - * Try calling resetPassword. - */ - RESET_PASSWORD = 'RESET_PASSWORD', - - /** - * The sign-in process is complete. - * - * No further action is needed. - */ - DONE = 'DONE', -} - -/** - * Denotes the next step in the Sign Up process. - */ -export enum AuthSignUpStep { - CONFIRM_SIGN_UP = 'CONFIRM_SIGN_UP', - DONE = 'DONE', -} - -/** - * Denotes the next step in the Update User Attribute process. - */ -export enum AuthUpdateAttributeStep { - /** - * Auth update attribute step requires user to confirm an attribute with a code sent to cellphone or email. - */ - CONFIRM_ATTRIBUTE_WITH_CODE = 'CONFIRM_ATTRIBUTE_WITH_CODE', - - /** - * Auth update attribute step indicates that the attribute is updated. - */ - DONE = 'DONE', -} diff --git a/packages/auth/src/types/index.ts b/packages/auth/src/types/index.ts index 421e292fb95..78732345098 100644 --- a/packages/auth/src/types/index.ts +++ b/packages/auth/src/types/index.ts @@ -4,13 +4,6 @@ // TODO: Remove "./Auth" export export * from './Auth'; -export { - AuthSignUpStep, - AuthResetPasswordStep, - AuthSignInStep, - AuthUpdateAttributeStep, -} from './enums'; - export { AdditionalInfo, DeliveryMedium, @@ -27,6 +20,9 @@ export { AllowedMFATypes, AuthUser, TOTPSetupDetails, + AuthResetPasswordStep, + AuthSignUpStep, + AuthUpdateAttributeStep, } from './models'; export { AuthServiceOptions, AuthSignUpOptions } from './options'; diff --git a/packages/auth/src/types/models.ts b/packages/auth/src/types/models.ts index 5a7d2bc30a3..a538fa0d41d 100644 --- a/packages/auth/src/types/models.ts +++ b/packages/auth/src/types/models.ts @@ -1,13 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { - AuthResetPasswordStep, - AuthSignInStep, - AuthSignUpStep, - AuthUpdateAttributeStep, -} from './enums'; - /** * Additional data that may be returned from Auth APIs. */ @@ -30,7 +23,10 @@ export type AuthCodeDeliveryDetails< deliveryMedium?: DeliveryMedium; attributeName?: UserAttributeKey; }; - +/** + * Denotes the next step in the Reset Password process. + */ +export type AuthResetPasswordStep = 'CONFIRM_RESET_PASSWORD_WITH_CODE' | 'DONE'; export type AuthNextResetPasswordStep< UserAttributeKey extends AuthUserAttributeKey = AuthUserAttributeKey > = { @@ -49,45 +45,130 @@ export type MFAType = 'SMS' | 'TOTP'; export type AllowedMFATypes = MFAType[]; export type ContinueSignInWithTOTPSetup = { - signInStep: AuthSignInStep.CONTINUE_SIGN_IN_WITH_TOTP_SETUP; + /** + * Auth step requires user to set up TOTP as multifactor authentication by associating an authenticator app + * and retrieving an OTP code. + * + * @example + * ```typescript + * // Code retrieved from authenticator app + * const otpCode = '112233'; + * await confirmSignIn({challengeResponse: otpCode}); + * ``` + */ + signInStep: 'CONTINUE_SIGN_IN_WITH_TOTP_SETUP'; totpSetupDetails: TOTPSetupDetails; }; export type ConfirmSignInWithTOTPCode = { - signInStep: AuthSignInStep.CONFIRM_SIGN_IN_WITH_TOTP_CODE; + /** + * Auth step requires user to use TOTP as multifactor authentication by retriving an OTP code from authenticator app. + * + * @example + * ```typescript + * // Code retrieved from authenticator app + * const otpCode = '112233'; + * await confirmSignIn({challengeResponse: otpCode}); + * ``` + */ + signInStep: 'CONFIRM_SIGN_IN_WITH_TOTP_CODE'; }; export type ContinueSignInWithMFASelection = { - signInStep: AuthSignInStep.CONTINUE_SIGN_IN_WITH_MFA_SELECTION; + /** + * Auth step requires user to select an mfa option (SMS | TOTP) to continue with the sign-in flow. + * + * @example + * ```typescript + * await confirmSignIn({challengeResponse:'TOTP'}); + * // OR + * await confirmSignIn({challengeResponse:'SMS'}); + * ``` + */ + signInStep: 'CONTINUE_SIGN_IN_WITH_MFA_SELECTION'; allowedMFATypes?: AllowedMFATypes; }; export type ConfirmSignInWithCustomChallenge = { - signInStep: AuthSignInStep.CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE; + /** + * Auth step requires user to respond to a custom challenge. + * + * @example + * ```typescript + * const challengeAnswer = 'my-custom-response'; + * await confirmSignIn({challengeResponse: challengeAnswer}); + * ``` + */ + signInStep: 'CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE'; additionalInfo?: AdditionalInfo; }; export type ConfirmSignInWithNewPasswordRequired< UserAttributeKey extends AuthUserAttributeKey = AuthUserAttributeKey > = { - signInStep: AuthSignInStep.CONFIRM_SIGN_IN_WITH_NEW_PASSWORD_REQUIRED; + /** + * Auth step requires user to change their password with any required attributes. + * + * @example + * ```typescript + * const attributes = { + * email: 'email@email' + * phone_number: '+11111111111' + * }; + * const newPassword = 'my-new-password'; + * await confirmSignIn({ + * challengeResponse: newPassword, + * options: { + * serviceOptions: { + * userAttributes: attributes + * } + * } + * }); + * ``` + */ + signInStep: 'CONFIRM_SIGN_IN_WITH_NEW_PASSWORD_REQUIRED'; missingAttributes?: UserAttributeKey[]; }; export type ConfirmSignInWithSMSCode = { - signInStep: AuthSignInStep.CONFIRM_SIGN_IN_WITH_SMS_CODE; + /** + * Auth step requires user to use SMS as multifactor authentication by retrieving a code sent to cellphone. + * + * @example + * ```typescript + * // Code retrieved from cellphone + * const smsCode = '112233' + * await confirmSignIn({challengeResponse: smsCode}) + * ``` + */ + signInStep: 'CONFIRM_SIGN_IN_WITH_SMS_CODE'; codeDeliveryDetails?: AuthCodeDeliveryDetails; }; export type ConfirmSignUpStep = { - signInStep: AuthSignInStep.CONFIRM_SIGN_UP; + /** + * Auth step requires to confirm user's sign-up. + * + * Try calling confirmSignUp. + */ + signInStep: 'CONFIRM_SIGN_UP'; }; export type ResetPasswordStep = { - signInStep: AuthSignInStep.RESET_PASSWORD; + /** + * Auth step requires user to change their password. + * + * Try calling resetPassword. + */ + signInStep: 'RESET_PASSWORD'; }; export type DoneSignInStep = { - signInStep: AuthSignInStep.DONE; + /** + * The sign-in process is complete. + * + * No further action is needed. + */ + signInStep: 'DONE'; }; export type AuthNextSignInStep< @@ -139,6 +220,11 @@ export type AuthUserAttribute< */ export type AuthUserAttributeKey = AuthStandardAttributeKey | AnyAttribute; +/** + * Denotes the next step in the Sign Up process. + */ +export type AuthSignUpStep = 'CONFIRM_SIGN_UP' | 'DONE'; + /** * Data encapsulating the next step in the Sign Up process */ @@ -150,20 +236,26 @@ export type AuthNextSignUpStep< codeDeliveryDetails?: AuthCodeDeliveryDetails; }; -export type ConfirmAttributeWithCodeAttributeStep< - UserAttributeKey extends AuthUserAttributeKey = AuthUserAttributeKey -> = { - updateAttributeStep: AuthUpdateAttributeStep.CONFIRM_ATTRIBUTE_WITH_CODE; - codeDeliveryDetails: AuthCodeDeliveryDetails; -}; +/** + * Denotes the next step in the Update User Attribute process. + */ +export type AuthUpdateAttributeStep = + /** + * Auth update attribute step requires user to confirm an attribute with a code sent to cellphone or email. + */ + | 'CONFIRM_ATTRIBUTE_WITH_CODE' -export type DoneAttributeStep = { - updateAttributeStep: AuthUpdateAttributeStep.DONE; -}; + /** + * Auth update attribute step indicates that the attribute is updated. + */ + | 'DONE'; export type AuthNextUpdateAttributeStep< UserAttributeKey extends AuthUserAttributeKey = AuthUserAttributeKey -> = ConfirmAttributeWithCodeAttributeStep | DoneAttributeStep; +> = { + updateAttributeStep: AuthUpdateAttributeStep; + codeDeliveryDetails?: AuthCodeDeliveryDetails; +}; /** * The AuthUser object contains username and userId from the idToken. diff --git a/packages/auth/src/urlListener.native.ts b/packages/auth/src/urlListener.native.ts deleted file mode 100644 index 0835a7d6642..00000000000 --- a/packages/auth/src/urlListener.native.ts +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { ConsoleLogger as Logger } from '@aws-amplify/core/internals/utils'; -const logger = new Logger('urlListener'); - -let handler: Function | undefined; - -export default async (callback: Function) => { - if (handler) { - return; - } - - let Linking: any; - let AppState: any; - let subscription: any; - try { - ({ Linking, AppState } = require('react-native')); - } catch (error) { - /* Keep webpack happy */ - } - - handler = - handler || - (({ url, ...rest }: { url: string }) => { - logger.debug('urlListener', { url, ...rest }); - callback({ url }); - }); - - // Handles backward compatibility. removeEventListener is only available on RN versions before 0.65. - if (Linking.removeEventListener === typeof 'function') { - Linking.removeEventListener('url', handler); - Linking.addEventListener('url', handler); - } else { - // remove() method is only available on RN v0.65+. - subscription?.remove?.(); - subscription = Linking.addEventListener('url', handler); - } - AppState.addEventListener('change', async (newAppState: string) => { - if (newAppState === 'active') { - const initialUrl = await Linking.getInitialURL(); - if (handler) handler({ url: initialUrl }); - } - }); -}; diff --git a/packages/auth/src/urlListener.ts b/packages/auth/src/urlListener.ts deleted file mode 100644 index 20e9d9bc02a..00000000000 --- a/packages/auth/src/urlListener.ts +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { browserOrNode } from '@aws-amplify/core/internals/utils'; - -export default (callback: Function) => { - if (browserOrNode().isBrowser && window.location) { - const url = window.location.href; - - callback({ url }); - } else if (browserOrNode().isNode) { - // continue building on ssr - () => {}; // noop - } else { - throw new Error('Not supported'); - } -}; diff --git a/packages/core/__tests__/JS-browser-runtime.test.ts b/packages/core/__tests__/JS-browser-runtime.test.ts index 235fd42fe89..864ae1535f3 100644 --- a/packages/core/__tests__/JS-browser-runtime.test.ts +++ b/packages/core/__tests__/JS-browser-runtime.test.ts @@ -6,7 +6,7 @@ * jsdom (which is also the default) Since this is allowed per test file * and not per test or describe, we have two tests, one for node and other for browser */ -import { browserOrNode } from '../src/Util/JS'; +import { isBrowser } from '../src/Util/JS'; describe('JS browserOrNode build test', () => { // Prevent Jest test resolves Node.js version from the global `process` of the @@ -23,9 +23,6 @@ describe('JS browserOrNode build test', () => { }); test('when its browser ', () => { - expect(browserOrNode()).toStrictEqual({ - isBrowser: true, - isNode: false, - }); + expect(isBrowser()).toBe(true); }); }); diff --git a/packages/core/__tests__/JS-node-runtime.test.ts b/packages/core/__tests__/JS-node-runtime.test.ts deleted file mode 100644 index 5ba87a4e5aa..00000000000 --- a/packages/core/__tests__/JS-node-runtime.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * @jest-environment node - */ - -/** The doc block above is to change the running environment of Jest to node. - * Since this is allowed per test file and not per test or describe, we have - * two tests, one for node and other for browser - */ -import { browserOrNode } from '../src/Util/JS'; - -describe('JS build test', () => { - test('when its node ', () => { - expect(browserOrNode()).toStrictEqual({ - isBrowser: false, - isNode: true, - }); - }); -}); diff --git a/packages/core/__tests__/clients/fetch.test.ts b/packages/core/__tests__/clients/fetch.test.ts index ad8cd6fde52..c3aaef011d0 100644 --- a/packages/core/__tests__/clients/fetch.test.ts +++ b/packages/core/__tests__/clients/fetch.test.ts @@ -1,8 +1,3 @@ -const mockUnfetch = jest.fn(); -jest.mock('isomorphic-unfetch', () => { - global['fetch'] = mockUnfetch; -}); - import { fetchTransferHandler } from '../../src/clients/handlers/fetch'; describe(fetchTransferHandler.name, () => { @@ -25,17 +20,22 @@ describe(fetchTransferHandler.name, () => { url: new URL('https://foo.bar'), }; const mockPayloadValue = 'payload value'; + const mockFetch = jest.fn(); + + beforeAll(() => { + global['fetch'] = mockFetch; + }); beforeEach(() => { jest.clearAllMocks(); - mockUnfetch.mockResolvedValue(mockFetchResponse); + mockFetch.mockResolvedValue(mockFetchResponse); }); test('should support abort signal', async () => { const signal = new AbortController().signal; await fetchTransferHandler(mockRequest, { abortSignal: signal }); - expect(mockUnfetch).toBeCalledTimes(1); - expect(mockUnfetch.mock.calls[0][1]).toEqual( + expect(mockFetch).toBeCalledTimes(1); + expect(mockFetch.mock.calls[0][1]).toEqual( expect.objectContaining({ signal }) ); }); @@ -88,8 +88,8 @@ describe(fetchTransferHandler.name, () => { { ...mockRequest, method, body: 'Mock Body' }, {} ); - expect(mockUnfetch).toBeCalledTimes(1); - expect(mockUnfetch.mock.calls[0][0].body).toBeUndefined(); + expect(mockFetch).toBeCalledTimes(1); + expect(mockFetch.mock.calls[0][0].body).toBeUndefined(); } ); }); diff --git a/packages/core/__tests__/singleton/Singleton.test.ts b/packages/core/__tests__/singleton/Singleton.test.ts index 75056a582da..e4eeb991aa2 100644 --- a/packages/core/__tests__/singleton/Singleton.test.ts +++ b/packages/core/__tests__/singleton/Singleton.test.ts @@ -3,6 +3,7 @@ import { AuthClass as Auth } from '../../src/singleton/Auth'; import { decodeJWT } from '../../src/singleton/Auth/utils'; import { AWSCredentialsAndIdentityId } from '../../src/singleton/Auth/types'; import { TextEncoder, TextDecoder } from 'util'; +import { fetchAuthSession } from '../../src'; Object.assign(global, { TextDecoder, TextEncoder }); type ArgumentTypes = F extends (...args: infer A) => any ? A @@ -86,6 +87,32 @@ describe('Session tests', () => { expect(session.credentials).toBe(undefined); }); + test('fetchAuthSession with credentials provider only', async () => { + const mockCredentials = { + accessKeyId: 'accessKeyValue', + secretAccessKey: 'secreatAccessKeyValue', + }; + Amplify.configure( + {}, + { + Auth: { + credentialsProvider: { + getCredentialsAndIdentityId: async () => { + return { + credentials: mockCredentials, + }; + }, + clearCredentialsAndIdentityId: () => {}, + }, + }, + } + ); + + const session = await fetchAuthSession(); + + expect(session.credentials).toBe(mockCredentials); + }); + test('fetch user after no credentials', async () => { expect.assertions(3); const config: ArgumentTypes[0] = { diff --git a/packages/core/package.json b/packages/core/package.json index 2ce5dfc07c8..eef13e47a7e 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -64,8 +64,6 @@ "@aws-crypto/sha256-js": "5.0.0", "@aws-sdk/types": "3.398.0", "@smithy/util-hex-encoding": "2.0.0", - "@types/node-fetch": "2.6.4", - "isomorphic-unfetch": "^3.0.0", "js-cookie": "^2.2.1", "tslib": "^2.5.0", "uuid": "^9.0.0", diff --git a/packages/core/src/OAuthHelper/FacebookOAuth.ts b/packages/core/src/OAuthHelper/FacebookOAuth.ts deleted file mode 100644 index 74c49274267..00000000000 --- a/packages/core/src/OAuthHelper/FacebookOAuth.ts +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { ConsoleLogger as Logger } from '../Logger'; -import { browserOrNode } from '../Util/JS'; -import { NonRetryableError } from '../Util'; - -const logger = new Logger('CognitoCredentials'); - -const waitForInit = new Promise((res, rej) => { - if (!browserOrNode().isBrowser) { - logger.debug('not in the browser, directly resolved'); - return res(); - } - const fb = window['FB']; - if (fb) { - logger.debug('FB SDK already loaded'); - return res(); - } else { - setTimeout(() => { - return res(); - }, 2000); - } -}); - -export class FacebookOAuth { - public initialized = false; - - constructor() { - this.refreshFacebookToken = this.refreshFacebookToken.bind(this); - this._refreshFacebookTokenImpl = this._refreshFacebookTokenImpl.bind(this); - } - - public async refreshFacebookToken() { - if (!this.initialized) { - logger.debug('need to wait for the Facebook SDK loaded'); - await waitForInit; - this.initialized = true; - logger.debug('finish waiting'); - } - - return this._refreshFacebookTokenImpl(); - } - - private _refreshFacebookTokenImpl() { - let fb: any = null; - if (browserOrNode().isBrowser) fb = window['FB']; - if (!fb) { - const errorMessage = 'no fb sdk available'; - logger.debug(errorMessage); - return Promise.reject(new NonRetryableError(errorMessage)); - } - - return new Promise((res, rej) => { - fb.getLoginStatus( - (fbResponse: any) => { - if (!fbResponse || !fbResponse.authResponse) { - const errorMessage = - 'no response from facebook when refreshing the jwt token'; - logger.debug(errorMessage); - // There is no definitive indication for a network error in - // fbResponse, so we are treating it as an invalid token. - rej(new NonRetryableError(errorMessage)); - } else { - const response = fbResponse.authResponse; - const { accessToken, expiresIn } = response; - const date = new Date(); - const expires_at = expiresIn * 1000 + date.getTime(); - if (!accessToken) { - const errorMessage = 'the jwtToken is undefined'; - logger.debug(errorMessage); - rej(new NonRetryableError(errorMessage)); - } - res({ - token: accessToken, - expires_at, - }); - } - }, - { scope: 'public_profile,email' } - ); - }); - } -} diff --git a/packages/core/src/OAuthHelper/GoogleOAuth.ts b/packages/core/src/OAuthHelper/GoogleOAuth.ts deleted file mode 100644 index a4a5d653022..00000000000 --- a/packages/core/src/OAuthHelper/GoogleOAuth.ts +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { ConsoleLogger as Logger } from '../Logger'; -import { browserOrNode } from '../Util/JS'; -import { NonRetryableError } from '../Util'; - -const logger = new Logger('CognitoCredentials'); - -const waitForInit = new Promise((res, rej) => { - if (!browserOrNode().isBrowser) { - logger.debug('not in the browser, directly resolved'); - return res(); - } - const ga = - window['gapi'] && window['gapi'].auth2 ? window['gapi'].auth2 : null; - if (ga) { - logger.debug('google api already loaded'); - return res(); - } else { - setTimeout(() => { - return res(); - }, 2000); - } -}); - -export class GoogleOAuth { - public initialized = false; - - constructor() { - this.refreshGoogleToken = this.refreshGoogleToken.bind(this); - this._refreshGoogleTokenImpl = this._refreshGoogleTokenImpl.bind(this); - } - - public async refreshGoogleToken() { - if (!this.initialized) { - logger.debug('need to wait for the Google SDK loaded'); - await waitForInit; - this.initialized = true; - logger.debug('finish waiting'); - } - - return this._refreshGoogleTokenImpl(); - } - - private _refreshGoogleTokenImpl() { - let ga: any = null; - if (browserOrNode().isBrowser) - ga = window['gapi'] && window['gapi'].auth2 ? window['gapi'].auth2 : null; - if (!ga) { - logger.debug('no gapi auth2 available'); - return Promise.reject('no gapi auth2 available'); - } - - return new Promise((res, rej) => { - ga.getAuthInstance() - .then((googleAuth: any) => { - if (!googleAuth) { - logger.debug('google Auth undefined'); - rej(new NonRetryableError('google Auth undefined')); - } - - const googleUser = googleAuth.currentUser.get(); - // refresh the token - if (googleUser.isSignedIn()) { - logger.debug('refreshing the google access token'); - googleUser - .reloadAuthResponse() - .then((authResponse: any) => { - const { id_token, expires_at } = authResponse; - res({ token: id_token, expires_at }); - }) - .catch((err: unknown) => { - if ( - err && - (err as { error: string }).error === 'network_error' - ) { - // Not using NonRetryableError so handler will be retried - rej('Network error reloading google auth response'); - } else { - rej( - new NonRetryableError( - 'Failed to reload google auth response' - ) - ); - } - }); - } else { - rej(new NonRetryableError('User is not signed in with Google')); - } - }) - .catch((err: unknown) => { - logger.debug('Failed to refresh google token', err); - rej(new NonRetryableError('Failed to refresh google token')); - }); - }); - } -} diff --git a/packages/core/src/OAuthHelper/index.ts b/packages/core/src/OAuthHelper/index.ts deleted file mode 100644 index db81e659126..00000000000 --- a/packages/core/src/OAuthHelper/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { GoogleOAuth as GoogleOAuthClass } from './GoogleOAuth'; -import { FacebookOAuth as FacebookOAuthClass } from './FacebookOAuth'; - -export const GoogleOAuth = new GoogleOAuthClass(); -export const FacebookOAuth = new FacebookOAuthClass(); diff --git a/packages/core/src/RNComponents/index.ts b/packages/core/src/RNComponents/index.ts index 146f848233e..776569e837b 100644 --- a/packages/core/src/RNComponents/index.ts +++ b/packages/core/src/RNComponents/index.ts @@ -1,8 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { browserOrNode } from '../Util/JS'; import { getDefaultStorageWithFallback } from '../storage/utils'; +import { isBrowser } from '../Util/JS'; export const Linking = {}; export const AppState = { @@ -11,6 +11,6 @@ export const AppState = { }; // if not in react native, just use local storage -export const AsyncStorage = browserOrNode().isBrowser +export const AsyncStorage = isBrowser() ? getDefaultStorageWithFallback() : undefined; diff --git a/packages/core/src/ServiceWorker/ServiceWorker.ts b/packages/core/src/ServiceWorker/ServiceWorker.ts index 43f2f3f3aa6..8397bea30fe 100644 --- a/packages/core/src/ServiceWorker/ServiceWorker.ts +++ b/packages/core/src/ServiceWorker/ServiceWorker.ts @@ -13,7 +13,7 @@ * and limitations under the License. */ import { ConsoleLogger as Logger } from '../Logger'; -import { browserOrNode } from '../Util/JS'; +import { isBrowser } from '../Util/JS'; import { Amplify } from '../Amplify'; import { asserts } from '../Util/errors/AssertError'; import { AmplifyError } from '../Util/Errors'; @@ -136,7 +136,7 @@ export class ServiceWorkerClass { }); this._publicKey = publicKey; return new Promise((resolve, reject) => { - if (browserOrNode().isBrowser) { + if (isBrowser()) { asserts(this._registration !== undefined, { name: SERVICE_WORKER_EXCEPTION, message: 'Service Worker registration is undefined', diff --git a/packages/core/src/Util/JS.ts b/packages/core/src/Util/JS.ts index 5d523d1b3dd..0ac4513ad46 100644 --- a/packages/core/src/Util/JS.ts +++ b/packages/core/src/Util/JS.ts @@ -170,19 +170,8 @@ export const isWebWorker = () => { ); }; -export const browserOrNode = () => { - const isBrowser = - typeof window !== 'undefined' && typeof window.document !== 'undefined'; - const isNode = - typeof process !== 'undefined' && - process.versions != null && - process.versions.node != null; - - return { - isBrowser, - isNode, - }; -}; +export const isBrowser = () => + typeof window !== 'undefined' && typeof window.document !== 'undefined'; /** * transfer the first letter of the keys to lowercase diff --git a/packages/core/src/Util/Reachability.ts b/packages/core/src/Util/Reachability.ts index c49759fdac5..edd03f4a63d 100644 --- a/packages/core/src/Util/Reachability.ts +++ b/packages/core/src/Util/Reachability.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import Observable, { ZenObservable } from 'zen-observable-ts'; -import { browserOrNode, isWebWorker } from './JS'; +import { isWebWorker } from './JS'; type NetworkStatus = { online: boolean; @@ -13,10 +13,6 @@ export default class ReachabilityNavigator implements Reachability { > = []; networkMonitor(netInfo?: any): Observable { - if (browserOrNode().isNode) { - return Observable.from([{ online: true }]); - } - const globalObj = isWebWorker() ? self : window; return new Observable(observer => { diff --git a/packages/core/src/clients/handlers/fetch.ts b/packages/core/src/clients/handlers/fetch.ts index d26deb20c3c..245d7e2bebf 100644 --- a/packages/core/src/clients/handlers/fetch.ts +++ b/packages/core/src/clients/handlers/fetch.ts @@ -1,7 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import 'isomorphic-unfetch'; // TODO: remove this dependency in v6 import { HttpRequest, HttpResponse, HttpTransferOptions } from '../types/http'; import { TransferHandler } from '../types/core'; import { withMemoization } from '../utils/memoization'; diff --git a/packages/core/src/libraryUtils.ts b/packages/core/src/libraryUtils.ts index f66a8c2d039..26b55d3616b 100644 --- a/packages/core/src/libraryUtils.ts +++ b/packages/core/src/libraryUtils.ts @@ -7,7 +7,7 @@ utils for use throughout the library. */ // JS utilities export { - browserOrNode, + isBrowser, filenameToContentType, generateRandomString, isEmpty, @@ -96,7 +96,6 @@ export { AmplifyError, AmplifyErrorString, } from './Util/Errors'; -export { FacebookOAuth, GoogleOAuth } from './OAuthHelper'; export { AppState, AsyncStorage, Linking } from './RNComponents'; export { ErrorParams, AmplifyErrorMap, ServiceError } from './types'; export { diff --git a/packages/core/src/providers/pinpoint/apis/record.ts b/packages/core/src/providers/pinpoint/apis/record.ts index 6b92b75d4de..3453bb022c1 100644 --- a/packages/core/src/providers/pinpoint/apis/record.ts +++ b/packages/core/src/providers/pinpoint/apis/record.ts @@ -2,9 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 import { v4 as uuid } from 'uuid'; -import { PinpointRecordParameters, PinpointSession } from '../types'; +import { PinpointRecordInput, PinpointSession } from '../types'; import { getEndpointId } from '../utils'; -import { +import { BUFFER_SIZE, FLUSH_INTERVAL, FLUSH_SIZE, @@ -28,11 +28,11 @@ export const record = async ({ identityId, region, userAgentValue, -}: PinpointRecordParameters): Promise => { +}: PinpointRecordInput): Promise => { const timestampISOString = new Date().toISOString(); const eventId = uuid(); let endpointId = await getEndpointId(appId, category); - + // Prepare event buffer if required const buffer = getEventBuffer({ appId, @@ -43,7 +43,7 @@ export const record = async ({ identityId, region, resendLimit: RESEND_LIMIT, - userAgentValue + userAgentValue, }); // Prepare a Pinpoint endpoint via updateEndpoint if one does not already exist, which will generate and cache an @@ -64,7 +64,7 @@ export const record = async ({ if (!endpointId) { throw new AmplifyError({ name: 'ENDPOINT_NOT_CREATED', - message: 'Endpoint was not created.' + message: 'Endpoint was not created.', }); } @@ -77,7 +77,7 @@ export const record = async ({ StartTimestamp: timestampISOString, }; } - + // Push event to buffer buffer.push({ eventId, @@ -85,6 +85,6 @@ export const record = async ({ event, session, timestamp: timestampISOString, - resendLimit: RESEND_LIMIT + resendLimit: RESEND_LIMIT, }); }; diff --git a/packages/core/src/providers/pinpoint/apis/updateEndpoint.ts b/packages/core/src/providers/pinpoint/apis/updateEndpoint.ts index 849c29aa627..fec31197293 100644 --- a/packages/core/src/providers/pinpoint/apis/updateEndpoint.ts +++ b/packages/core/src/providers/pinpoint/apis/updateEndpoint.ts @@ -7,7 +7,7 @@ import { updateEndpoint as clientUpdateEndpoint, UpdateEndpointInput, } from '../../../AwsClients/Pinpoint'; -import { PinpointUpdateEndpointParameters } from '../types'; +import { PinpointUpdateEndpointInput } from '../types'; import { cacheEndpointId, getEndpointId } from '../utils'; /** @@ -25,7 +25,7 @@ export const updateEndpoint = async ({ userId, userProfile, userAgentValue, -}: PinpointUpdateEndpointParameters): Promise => { +}: PinpointUpdateEndpointInput): Promise => { const endpointId = await getEndpointId(appId, category); // only generate a new endpoint id if one was not found in cache const createdEndpointId = !endpointId ? uuidv4() : undefined; diff --git a/packages/core/src/providers/pinpoint/types/pinpoint.ts b/packages/core/src/providers/pinpoint/types/pinpoint.ts index 297cdc5b11b..8e920fe6b41 100644 --- a/packages/core/src/providers/pinpoint/types/pinpoint.ts +++ b/packages/core/src/providers/pinpoint/types/pinpoint.ts @@ -44,12 +44,13 @@ type PinpointCommonParameters = { userAgentValue?: string; }; -export type PinpointUpdateEndpointParameters = PinpointCommonParameters & PinpointServiceOptions & { - channelType?: SupportedChannelType; - userId?: string; - userProfile?: UserProfile; -}; +export type PinpointUpdateEndpointInput = PinpointCommonParameters & + PinpointServiceOptions & { + channelType?: SupportedChannelType; + userId?: string; + userProfile?: UserProfile; + }; -export type PinpointRecordParameters = PinpointCommonParameters & { +export type PinpointRecordInput = PinpointCommonParameters & { event: PinpointAnalyticsEvent; }; diff --git a/packages/core/src/singleton/Storage/types.ts b/packages/core/src/singleton/Storage/types.ts index b7ee97955b7..d53dcce74f1 100644 --- a/packages/core/src/singleton/Storage/types.ts +++ b/packages/core/src/singleton/Storage/types.ts @@ -7,8 +7,13 @@ export interface StorageConfig { S3: { bucket?: string; region?: string; + /** + * Internal-only configuration for testing purpose. You should not use this. + * + * @internal + */ dangerouslyConnectToHttpEndpointForTesting?: string; - } + }; } type StoragePrefixResolver = (params: { @@ -16,7 +21,6 @@ type StoragePrefixResolver = (params: { targetIdentityId?: string; }) => Promise; -// TODO[AllanZhengYP]: need to finalize the decision whether to move defaultAccessLevel to StorageConfig export interface LibraryStorageOptions { S3: { prefixResolver?: StoragePrefixResolver; diff --git a/packages/datastore/src/datastore/datastore.ts b/packages/datastore/src/datastore/datastore.ts index dcbb0dbde22..8539fba3774 100644 --- a/packages/datastore/src/datastore/datastore.ts +++ b/packages/datastore/src/datastore/datastore.ts @@ -6,7 +6,6 @@ import { Amplify, ConsoleLogger as Logger, Hub, - browserOrNode, BackgroundProcessManager, Cache, } from '@aws-amplify/core'; @@ -117,7 +116,6 @@ enablePatches(); const logger = new Logger('DataStore'); const ulid = monotonicUlidFactory(Date.now()); -const { isNode } = browserOrNode(); type SettingMetaData = { identifier: ManagedIdentifier; @@ -1563,11 +1561,8 @@ class DataStore { .start({ fullSyncInterval: fullSyncIntervalInMilliseconds }) .subscribe({ next: ({ type, data }) => { - // In Node, we need to wait for queries to be synced to prevent returning empty arrays. // In the Browser, we can begin returning data once subscriptions are in place. - const readyType = isNode - ? ControlMessage.SYNC_ENGINE_SYNC_QUERIES_READY - : ControlMessage.SYNC_ENGINE_STORAGE_SUBSCRIBED; + const readyType = ControlMessage.SYNC_ENGINE_STORAGE_SUBSCRIBED; if (type === readyType) { this.initResolve(); diff --git a/packages/datastore/src/sync/index.ts b/packages/datastore/src/sync/index.ts index 65a94f566cd..55dea86e112 100644 --- a/packages/datastore/src/sync/index.ts +++ b/packages/datastore/src/sync/index.ts @@ -1,7 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { - browserOrNode, ConsoleLogger as Logger, BackgroundProcessManager, Hub, @@ -53,7 +52,6 @@ import { TransformerMutationType, } from './utils'; -const { isNode } = browserOrNode(); const logger = new Logger('DataStore'); const ownSymbol = Symbol('sync'); @@ -249,53 +247,47 @@ export class SyncEngine { [TransformerMutationType, SchemaModel, PersistentModel] >; - // NOTE: need a way to override this conditional for testing. - if (isNode) { - logger.warn( - 'Realtime disabled when in a server-side environment' - ); - } else { - this.stopDisruptionListener = - this.startDisruptionListener(); - //#region GraphQL Subscriptions - [ctlSubsObservable, dataSubsObservable] = - this.subscriptionsProcessor.start(); - - try { - await new Promise((resolve, reject) => { - onTerminate.then(reject); - const ctlSubsSubscription = - ctlSubsObservable.subscribe({ - next: msg => { - if (msg === CONTROL_MSG.CONNECTED) { - resolve(); - } - }, - error: err => { - reject(err); - const handleDisconnect = - this.disconnectionHandler(); - handleDisconnect(err); - }, - }); - - subscriptions.push(ctlSubsSubscription); - }); - } catch (err) { - observer.error(err); - failedStarting(); - return; - } + this.stopDisruptionListener = + this.startDisruptionListener(); + //#region GraphQL Subscriptions + [ctlSubsObservable, dataSubsObservable] = + this.subscriptionsProcessor.start(); - logger.log('Realtime ready'); + try { + await new Promise((resolve, reject) => { + onTerminate.then(reject); + const ctlSubsSubscription = ctlSubsObservable.subscribe( + { + next: msg => { + if (msg === CONTROL_MSG.CONNECTED) { + resolve(); + } + }, + error: err => { + reject(err); + const handleDisconnect = + this.disconnectionHandler(); + handleDisconnect(err); + }, + } + ); - observer.next({ - type: ControlMessage.SYNC_ENGINE_SUBSCRIPTIONS_ESTABLISHED, + subscriptions.push(ctlSubsSubscription); }); - - //#endregion + } catch (err) { + observer.error(err); + failedStarting(); + return; } + logger.log('Realtime ready'); + + observer.next({ + type: ControlMessage.SYNC_ENGINE_SUBSCRIPTIONS_ESTABLISHED, + }); + + //#endregion + //#region Base & Sync queries try { await new Promise((resolve, reject) => { @@ -376,32 +368,29 @@ export class SyncEngine { //#endregion //#region Merge subscriptions buffer - // TODO: extract to function - if (!isNode) { - subscriptions.push( - dataSubsObservable!.subscribe( - ([_transformerMutationType, modelDefinition, item]) => - this.runningProcesses.add(async () => { - const modelConstructor = this.userModelClasses[ - modelDefinition.name - ] as PersistentModelConstructor; - - const model = this.modelInstanceCreator( - modelConstructor, - item - ); - - await this.storage.runExclusive(storage => - this.modelMerger.merge( - storage, - model, - modelDefinition - ) - ); - }, 'subscription dataSubsObservable event') - ) - ); - } + subscriptions.push( + dataSubsObservable!.subscribe( + ([_transformerMutationType, modelDefinition, item]) => + this.runningProcesses.add(async () => { + const modelConstructor = this.userModelClasses[ + modelDefinition.name + ] as PersistentModelConstructor; + + const model = this.modelInstanceCreator( + modelConstructor, + item + ); + + await this.storage.runExclusive(storage => + this.modelMerger.merge( + storage, + model, + modelDefinition + ) + ); + }, 'subscription dataSubsObservable event') + ) + ); //#endregion } else if (!online) { this.online = online; diff --git a/packages/pubsub/src/PubSub.ts b/packages/pubsub/src/PubSub.ts index 23bc5038293..3fb9146b4c5 100644 --- a/packages/pubsub/src/PubSub.ts +++ b/packages/pubsub/src/PubSub.ts @@ -3,11 +3,7 @@ // import '../Common/Polyfills'; import Observable from 'zen-observable-ts'; -import { - Amplify, - browserOrNode, - ConsoleLogger as Logger, -} from '@aws-amplify/core'; +import { Amplify, ConsoleLogger as Logger } from '@aws-amplify/core'; import { PubSubProvider, ProviderOptions } from './types'; import { InternalPubSubClass } from './internals'; diff --git a/packages/pubsub/src/internals/InternalPubSub.ts b/packages/pubsub/src/internals/InternalPubSub.ts index c71a0608d41..ea1ec679a7c 100644 --- a/packages/pubsub/src/internals/InternalPubSub.ts +++ b/packages/pubsub/src/internals/InternalPubSub.ts @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 import { Amplify, - browserOrNode, Category, ConsoleLogger as Logger, CustomUserAgentDetails, @@ -14,7 +13,6 @@ import { AWSAppSyncRealTimeProvider } from '../Providers'; import { PubSubContent } from '../types/PubSub'; import Observable from 'zen-observable-ts'; -const { isNode } = browserOrNode(); const logger = new Logger('PubSub'); type PubSubObservable = { @@ -144,12 +142,6 @@ export class InternalPubSubClass { options?: ProviderOptions, customUserAgentDetails?: CustomUserAgentDetails ): Observable { - if (isNode && this._options && this._options.ssr) { - throw new Error( - 'Subscriptions are not supported for Server-Side Rendering (SSR)' - ); - } - logger.debug('subscribe options', options); const providers = this.getProviders(options); diff --git a/packages/storage/__tests__/providers/s3/apis/copy.test.ts b/packages/storage/__tests__/providers/s3/apis/copy.test.ts index 83d8dbd2176..41f40ac660a 100644 --- a/packages/storage/__tests__/providers/s3/apis/copy.test.ts +++ b/packages/storage/__tests__/providers/s3/apis/copy.test.ts @@ -5,6 +5,10 @@ import { Credentials } from '@aws-sdk/types'; import { Amplify, StorageAccessLevel } from '@aws-amplify/core'; import { copyObject } from '../../../../src/providers/s3/utils/client'; import { copy } from '../../../../src/providers/s3/apis'; +import { + StorageCopySourceOptions, + StorageCopyDestinationOptions, +} from '../../../../src/types'; jest.mock('../../../../src/providers/s3/utils/client'); jest.mock('@aws-amplify/core', () => ({ @@ -24,6 +28,7 @@ const destinationKey = 'destinationKey'; const bucket = 'bucket'; const region = 'region'; const targetIdentityId = 'targetIdentityId'; +const defaultIdentityId = 'defaultIdentityId'; const copyResult = { key: destinationKey }; const credentials: Credentials = { accessKeyId: 'accessKeyId', @@ -39,72 +44,17 @@ const copyObjectClientBaseParams = { MetadataDirective: 'COPY', }; -/** - * bucket is appended at start if it's a sourceKey - * guest: public/${key}` - * private: private/${targetIdentityId}/${key}` - * protected: protected/${targetIdentityId}/${key}` - */ -const buildClientRequestKey = ( - key: string, - KeyType: 'source' | 'destination', - accessLevel: StorageAccessLevel -) => { - const targetIdentityId = 'targetIdentityId'; - const bucket = 'bucket'; - const finalAccessLevel = accessLevel == 'guest' ? 'public' : accessLevel; - let finalKey = KeyType == 'source' ? `${bucket}/` : ''; - finalKey += `${finalAccessLevel}/`; - finalKey += finalAccessLevel != 'public' ? `${targetIdentityId}/` : ''; - finalKey += `${key}`; - return finalKey; -}; - -const interAccessLevelTest = async ( - sourceAccessLevel, - destinationAccessLevel -) => { - expect.assertions(3); - const source = { - key: sourceKey, - accessLevel: sourceAccessLevel, - }; - sourceAccessLevel == 'protected' - ? (source['targetIdentityId'] = targetIdentityId) - : null; - - expect( - await copy({ - source, - destination: { - key: destinationKey, - accessLevel: destinationAccessLevel, - }, - }) - ).toEqual(copyResult); - expect(copyObject).toBeCalledTimes(1); - expect(copyObject).toHaveBeenCalledWith(copyObjectClientConfig, { - ...copyObjectClientBaseParams, - CopySource: buildClientRequestKey(sourceKey, 'source', sourceAccessLevel), - Key: buildClientRequestKey( - destinationKey, - 'destination', - destinationAccessLevel - ), - }); -}; - describe('copy API', () => { beforeAll(() => { mockFetchAuthSession.mockResolvedValue({ credentials, - identityId: targetIdentityId, + identityId: defaultIdentityId, }); mockGetConfig.mockReturnValue({ Storage: { S3: { - bucket: 'bucket', - region: 'region', + bucket, + region, }, }, }); @@ -120,40 +70,114 @@ describe('copy API', () => { afterEach(() => { jest.clearAllMocks(); }); - - describe('Copy from guest to all access levels', () => { - it('Should copy guest -> guest', async () => - await interAccessLevelTest('guest', 'guest')); - it('Should copy guest -> private', async () => - await interAccessLevelTest('guest', 'private')); - it('Should copy guest -> protected', async () => - await interAccessLevelTest('guest', 'protected')); - }); - - describe('Copy from private to all access levels', () => { - it('Should copy private -> guest', async () => - await interAccessLevelTest('private', 'guest')); - it('Should copy private -> private', async () => - await interAccessLevelTest('private', 'private')); - it('Should copy private -> protected', async () => - await interAccessLevelTest('private', 'protected')); - }); - - describe('Copy from protected to all access levels', () => { - it('Should copy protected -> guest', async () => - await interAccessLevelTest('protected', 'guest')); - it('Should copy protected -> private', async () => - await interAccessLevelTest('protected', 'private')); - it('Should copy protected -> protected', async () => - await interAccessLevelTest('protected', 'protected')); - }); + [ + { + source: { accessLevel: 'guest' }, + destination: { accessLevel: 'guest' }, + expectedSourceKey: `${bucket}/public/${sourceKey}`, + expectedDestinationKey: `public/${destinationKey}`, + }, + { + source: { accessLevel: 'guest' }, + destination: { accessLevel: 'private' }, + expectedSourceKey: `${bucket}/public/${sourceKey}`, + expectedDestinationKey: `private/${defaultIdentityId}/${destinationKey}`, + }, + { + source: { accessLevel: 'guest' }, + destination: { accessLevel: 'protected' }, + expectedSourceKey: `${bucket}/public/${sourceKey}`, + expectedDestinationKey: `protected/${defaultIdentityId}/${destinationKey}`, + }, + { + source: { accessLevel: 'private' }, + destination: { accessLevel: 'guest' }, + expectedSourceKey: `${bucket}/private/${defaultIdentityId}/${sourceKey}`, + expectedDestinationKey: `public/${destinationKey}`, + }, + { + source: { accessLevel: 'private' }, + destination: { accessLevel: 'private' }, + expectedSourceKey: `${bucket}/private/${defaultIdentityId}/${sourceKey}`, + expectedDestinationKey: `private/${defaultIdentityId}/${destinationKey}`, + }, + { + source: { accessLevel: 'private' }, + destination: { accessLevel: 'protected' }, + expectedSourceKey: `${bucket}/private/${defaultIdentityId}/${sourceKey}`, + expectedDestinationKey: `protected/${defaultIdentityId}/${destinationKey}`, + }, + { + source: { accessLevel: 'protected' }, + destination: { accessLevel: 'guest' }, + expectedSourceKey: `${bucket}/protected/${defaultIdentityId}/${sourceKey}`, + expectedDestinationKey: `public/${destinationKey}`, + }, + { + source: { accessLevel: 'protected' }, + destination: { accessLevel: 'private' }, + expectedSourceKey: `${bucket}/protected/${defaultIdentityId}/${sourceKey}`, + expectedDestinationKey: `private/${defaultIdentityId}/${destinationKey}`, + }, + { + source: { accessLevel: 'protected' }, + destination: { accessLevel: 'protected' }, + expectedSourceKey: `${bucket}/protected/${defaultIdentityId}/${sourceKey}`, + expectedDestinationKey: `protected/${defaultIdentityId}/${destinationKey}`, + }, + { + source: { accessLevel: 'protected', targetIdentityId }, + destination: { accessLevel: 'guest' }, + expectedSourceKey: `${bucket}/protected/${targetIdentityId}/${sourceKey}`, + expectedDestinationKey: `public/${destinationKey}`, + }, + { + source: { accessLevel: 'protected', targetIdentityId }, + destination: { accessLevel: 'private' }, + expectedSourceKey: `${bucket}/protected/${targetIdentityId}/${sourceKey}`, + expectedDestinationKey: `private/${defaultIdentityId}/${destinationKey}`, + }, + { + source: { accessLevel: 'protected', targetIdentityId }, + destination: { accessLevel: 'protected' }, + expectedSourceKey: `${bucket}/protected/${targetIdentityId}/${sourceKey}`, + expectedDestinationKey: `protected/${defaultIdentityId}/${destinationKey}`, + }, + ].forEach( + ({ source, destination, expectedSourceKey, expectedDestinationKey }) => { + const targetIdentityIdMsg = source?.targetIdentityId + ? `with targetIdentityId` + : ''; + it(`should copy ${source.accessLevel} ${targetIdentityIdMsg} -> ${destination.accessLevel}`, async () => { + expect.assertions(3); + expect( + await copy({ + source: { + ...(source as StorageCopySourceOptions), + key: sourceKey, + }, + destination: { + ...(destination as StorageCopyDestinationOptions), + key: destinationKey, + }, + }) + ).toEqual(copyResult); + expect(copyObject).toBeCalledTimes(1); + expect(copyObject).toHaveBeenCalledWith(copyObjectClientConfig, { + ...copyObjectClientBaseParams, + CopySource: expectedSourceKey, + Key: expectedDestinationKey, + }); + }); + } + ); }); describe('Error Path Cases:', () => { afterEach(() => { jest.clearAllMocks(); }); - it('Should return a not found error', async () => { + it('should return a not found error', async () => { mockCopyObject.mockRejectedValueOnce( Object.assign(new Error(), { $metadata: { httpStatusCode: 404 }, diff --git a/packages/storage/__tests__/providers/s3/apis/downloadData.test.ts b/packages/storage/__tests__/providers/s3/apis/downloadData.test.ts index 7dec964df35..a4b095a6d90 100644 --- a/packages/storage/__tests__/providers/s3/apis/downloadData.test.ts +++ b/packages/storage/__tests__/providers/s3/apis/downloadData.test.ts @@ -6,6 +6,7 @@ import { Amplify } from '@aws-amplify/core'; import { getObject } from '../../../../src/providers/s3/utils/client'; import { downloadData } from '../../../../src/providers/s3'; import { createDownloadTask } from '../../../../src/providers/s3/utils'; +import { StorageOptions } from '../../../../src/types'; jest.mock('../../../../src/providers/s3/utils/client'); jest.mock('../../../../src/providers/s3/utils'); @@ -16,14 +17,17 @@ jest.mock('@aws-amplify/core', () => ({ fetchAuthSession: jest.fn(), }, }, - fetchAuthSession: jest.fn(), })); const credentials: Credentials = { accessKeyId: 'accessKeyId', sessionToken: 'sessionToken', secretAccessKey: 'secretAccessKey', }; -const identityId = 'identityId'; +const key = 'key'; +const bucket = 'bucket'; +const region = 'region'; +const targetIdentityId = 'targetIdentityId'; +const defaultIdentityId = 'defaultIdentityId'; const mockFetchAuthSession = Amplify.Auth.fetchAuthSession as jest.Mock; const mockCreateDownloadTask = createDownloadTask as jest.Mock; @@ -35,13 +39,13 @@ describe('downloadData', () => { beforeAll(() => { mockFetchAuthSession.mockResolvedValue({ credentials, - identityId: identityId, + identityId: defaultIdentityId, }); mockGetConfig.mockReturnValue({ Storage: { S3: { - bucket: 'bucket', - region: 'region', + bucket, + region, }, }, }); @@ -56,38 +60,61 @@ describe('downloadData', () => { expect(downloadData({ key: 'key' })).toBe('downloadTask'); }); - it('should supply the correct parameters to getObject API handler', async () => { - expect.assertions(2); - (getObject as jest.Mock).mockResolvedValueOnce({ Body: 'body' }); - const onProgress = jest.fn(); - const targetIdentityId = 'targetIdentityId'; - const accessLevel = 'protected'; - const key = 'key'; - downloadData({ - key, - options: { - targetIdentityId, - accessLevel, - useAccelerateEndpoint: true, - onProgress, - }, + [ + { + expectedKey: `public/${key}`, + }, + { + options: { accessLevel: 'guest' }, + expectedKey: `public/${key}`, + }, + { + options: { accessLevel: 'private' }, + expectedKey: `private/${defaultIdentityId}/${key}`, + }, + { + options: { accessLevel: 'protected' }, + expectedKey: `protected/${defaultIdentityId}/${key}`, + }, + { + options: { accessLevel: 'protected', targetIdentityId }, + expectedKey: `protected/${targetIdentityId}/${key}`, + }, + ].forEach(({ options, expectedKey }) => { + const accessLevelMsg = options?.accessLevel ?? 'default'; + const targetIdentityIdMsg = options?.targetIdentityId + ? `and targetIdentityId` + : ''; + + it(`should supply the correct parameters to getObject API handler with ${accessLevelMsg} accessLevel ${targetIdentityIdMsg}`, async () => { + expect.assertions(2); + (getObject as jest.Mock).mockResolvedValueOnce({ Body: 'body' }); + const onProgress = jest.fn(); + downloadData({ + key, + options: { + ...(options as StorageOptions), + useAccelerateEndpoint: true, + onProgress, + }, + }); + const job = mockCreateDownloadTask.mock.calls[0][0].job; + await job(); + expect(getObject).toBeCalledTimes(1); + expect(getObject).toHaveBeenCalledWith( + { + credentials, + region, + useAccelerateEndpoint: true, + onDownloadProgress: onProgress, + abortSignal: expect.any(AbortSignal), + }, + { + Bucket: bucket, + Key: expectedKey, + } + ); }); - const job = mockCreateDownloadTask.mock.calls[0][0].job; - await job(); - expect(getObject).toBeCalledTimes(1); - expect(getObject).toHaveBeenCalledWith( - { - credentials, - region: 'region', - useAccelerateEndpoint: true, - onDownloadProgress: onProgress, - abortSignal: expect.any(AbortSignal), - }, - { - Bucket: 'bucket', - Key: `${accessLevel}/${targetIdentityId}/${key}`, - } - ); }); it('should assign the getObject API handler response to the result', async () => { diff --git a/packages/storage/__tests__/providers/s3/apis/getProperties.test.ts b/packages/storage/__tests__/providers/s3/apis/getProperties.test.ts index 0485b8c5869..60765c2a95f 100644 --- a/packages/storage/__tests__/providers/s3/apis/getProperties.test.ts +++ b/packages/storage/__tests__/providers/s3/apis/getProperties.test.ts @@ -28,13 +28,13 @@ const credentials: Credentials = { secretAccessKey: 'secretAccessKey', }; const targetIdentityId = 'targetIdentityId'; -const identityId = 'identityId'; +const defaultIdentityId = 'defaultIdentityId'; describe('getProperties api', () => { beforeAll(() => { mockFetchAuthSession.mockResolvedValue({ credentials, - identityId, + identityId: defaultIdentityId, }); mockGetConfig.mockReturnValue({ Storage: { @@ -73,26 +73,32 @@ describe('getProperties api', () => { afterEach(() => { jest.clearAllMocks(); }); - it.each([ + [ + { + expectedKey: `public/${key}`, + }, { options: { accessLevel: 'guest' }, - expectedKey: 'public/key', + expectedKey: `public/${key}`, }, { - options: { accessLevel: 'protected', targetIdentityId }, - expectedKey: 'protected/targetIdentityId/key', + options: { accessLevel: 'private' }, + expectedKey: `private/${defaultIdentityId}/${key}`, }, { options: { accessLevel: 'protected' }, - expectedKey: 'protected/identityId/key', + expectedKey: `protected/${defaultIdentityId}/${key}`, }, { - options: { accessLevel: 'private' }, - expectedKey: 'private/identityId/key', + options: { accessLevel: 'protected', targetIdentityId }, + expectedKey: `protected/${targetIdentityId}/${key}`, }, - ])( - 'getProperties api with $options.accessLevel', - async ({ options, expectedKey }) => { + ].forEach(({ options, expectedKey }) => { + const accessLevelMsg = options?.accessLevel ?? 'default'; + const targetIdentityIdMsg = options?.targetIdentityId + ? `and targetIdentityId` + : ''; + it(`should getProperties with ${accessLevelMsg} accessLevel ${targetIdentityIdMsg}`, async () => { const headObjectOptions = { Bucket: 'bucket', Key: expectedKey, @@ -106,8 +112,8 @@ describe('getProperties api', () => { ).toEqual(expected); expect(headObject).toBeCalledTimes(1); expect(headObject).toHaveBeenCalledWith(config, headObjectOptions); - } - ); + }); + }); }); describe('getProperties error path', () => { diff --git a/packages/storage/__tests__/providers/s3/apis/getUrl.test.ts b/packages/storage/__tests__/providers/s3/apis/getUrl.test.ts index 4cd7d7586bf..74b992f18c2 100644 --- a/packages/storage/__tests__/providers/s3/apis/getUrl.test.ts +++ b/packages/storage/__tests__/providers/s3/apis/getUrl.test.ts @@ -30,13 +30,13 @@ const credentials: Credentials = { secretAccessKey: 'secretAccessKey', }; const targetIdentityId = 'targetIdentityId'; -const identityId = 'identityId'; +const defaultIdentityId = 'defaultIdentityId'; describe('getUrl test', () => { beforeAll(() => { mockFetchAuthSession.mockResolvedValue({ credentials, - identityId, + identityId: defaultIdentityId, }); mockGetConfig.mockReturnValue({ Storage: { @@ -47,10 +47,11 @@ describe('getUrl test', () => { }, }); }); + describe('getUrl happy path', () => { const config = { credentials, - region: 'region', + region, }; const key = 'key'; beforeEach(() => { @@ -71,40 +72,50 @@ describe('getUrl test', () => { afterEach(() => { jest.clearAllMocks(); }); - it.each([ + [ + { + expectedKey: `public/${key}`, + }, { options: { accessLevel: 'guest' }, - expectedKey: 'public/key', + expectedKey: `public/${key}`, }, { - options: { accessLevel: 'protected', targetIdentityId }, - expectedKey: 'protected/targetIdentityId/key', + options: { accessLevel: 'private' }, + expectedKey: `private/${defaultIdentityId}/${key}`, }, { options: { accessLevel: 'protected' }, - expectedKey: 'protected/identityId/key', + expectedKey: `protected/${defaultIdentityId}/${key}`, }, { - options: { accessLevel: 'private' }, - expectedKey: 'private/identityId/key', + options: { accessLevel: 'protected', targetIdentityId }, + expectedKey: `protected/${targetIdentityId}/${key}`, }, - ])('getUrl with $options.accessLevel', async ({ options, expectedKey }) => { - const headObjectOptions = { - Bucket: 'bucket', - Key: expectedKey, - }; - const optionsVal = { ...options, validateObjectExistence: true }; - - expect.assertions(4); - const result = await getUrl({ - key, - options: optionsVal as StorageOptions, - }); - expect(getPresignedGetObjectUrl).toBeCalledTimes(1); - expect(headObject).toBeCalledTimes(1); - expect(headObject).toHaveBeenCalledWith(config, headObjectOptions); - expect(result.url).toEqual({ - url: new URL('https://google.com'), + ].forEach(({ options, expectedKey }) => { + const accessLevelMsg = options?.accessLevel ?? 'default'; + const targetIdentityIdMsg = options?.targetIdentityId + ? `and targetIdentityId` + : ''; + it(`should getUrl with ${accessLevelMsg} accessLevel ${targetIdentityIdMsg}`, async () => { + const headObjectOptions = { + Bucket: bucket, + Key: expectedKey, + }; + expect.assertions(4); + const result = await getUrl({ + key, + options: { + ...(options as StorageOptions), + validateObjectExistence: true, + }, + }); + expect(getPresignedGetObjectUrl).toBeCalledTimes(1); + expect(headObject).toBeCalledTimes(1); + expect(headObject).toHaveBeenCalledWith(config, headObjectOptions); + expect(result.url).toEqual({ + url: new URL('https://google.com'), + }); }); }); }); @@ -112,7 +123,7 @@ describe('getUrl test', () => { afterAll(() => { jest.clearAllMocks(); }); - it('Should return not found error when the object is not found', async () => { + it('should return not found error when the object is not found', async () => { (headObject as jest.Mock).mockImplementation(() => Object.assign(new Error(), { $metadata: { httpStatusCode: 404 }, diff --git a/packages/storage/__tests__/providers/s3/apis/list.test.ts b/packages/storage/__tests__/providers/s3/apis/list.test.ts index b57d282f096..fecad15e696 100644 --- a/packages/storage/__tests__/providers/s3/apis/list.test.ts +++ b/packages/storage/__tests__/providers/s3/apis/list.test.ts @@ -5,6 +5,7 @@ import { Credentials } from '@aws-sdk/types'; import { Amplify } from '@aws-amplify/core'; import { listObjectsV2 } from '../../../../src/providers/s3/utils/client'; import { list } from '../../../../src/providers/s3/apis'; +import { StorageOptions } from '../../../../src/types'; jest.mock('../../../../src/providers/s3/utils/client'); jest.mock('@aws-amplify/core', () => ({ @@ -19,10 +20,12 @@ const mockFetchAuthSession = Amplify.Auth.fetchAuthSession as jest.Mock; const mockGetConfig = Amplify.getConfig as jest.Mock; const mockListObject = listObjectsV2 as jest.Mock; const key = 'path/itemsKey'; +const path = key; const bucket = 'bucket'; const region = 'region'; const nextToken = 'nextToken'; const targetIdentityId = 'targetIdentityId'; +const defaultIdentityId = 'defaultIdentityId'; const eTag = 'eTag'; const lastModified = 'lastModified'; const size = 'size'; @@ -40,17 +43,11 @@ const listObjectClientBaseResultItem = { LastModified: lastModified, Size: size, }; -const copyResultItem = { - key, +const listResultItem = { eTag, lastModified, size, }; - -const listResultObj = { - ...listObjectClientBaseResultItem, - Key: `public/${key}`, -}; const mockListObjectsV2ApiWithPages = pages => { let methodCalls = 0; mockListObject.mockClear(); @@ -63,22 +60,18 @@ const mockListObjectsV2ApiWithPages = pages => { if (input.ContinuationToken === undefined || methodCalls < pages) { token = nextToken; } - if (input.Prefix === 'public/listALLResultsPath') { - return { - Contents: [listResultObj], - NextContinuationToken: token, - }; - } + return { + Contents: [{ ...listObjectClientBaseResultItem, Key: input.Prefix }], + NextContinuationToken: token, + }; }); }; -// TODO(ashwinkumar6) this currently only tests for guest -// Update to test across all accessLevels describe('list API', () => { beforeAll(() => { mockFetchAuthSession.mockResolvedValue({ credentials, - identityId: targetIdentityId, + identityId: defaultIdentityId, }); mockGetConfig.mockReturnValue({ Storage: { @@ -94,108 +87,176 @@ describe('list API', () => { jest.clearAllMocks(); }); - it('Should list objects with default params', async () => { - mockListObject.mockImplementationOnce(() => { - return { - Contents: [listResultObj], - NextContinuationToken: nextToken, - }; - }); + const accessLevelTests = [ + { + expectedPath: `public/`, + }, + { + path, + expectedPath: `public/${path}`, + }, + { + path, + options: { accessLevel: 'guest' }, + expectedPath: `public/${path}`, + }, + { + path, + options: { accessLevel: 'private' }, + expectedPath: `private/${defaultIdentityId}/${path}`, + }, + { + path, + options: { accessLevel: 'protected' }, + expectedPath: `protected/${defaultIdentityId}/${path}`, + }, + { + path, + options: { accessLevel: 'protected', targetIdentityId }, + expectedPath: `protected/${targetIdentityId}/${path}`, + }, + ]; - expect.assertions(4); - let response = await list(); - expect(response.items).toEqual([copyResultItem]); - expect(response.nextToken).toEqual(nextToken); - expect(listObjectsV2).toBeCalledTimes(1); - expect(listObjectsV2).toHaveBeenCalledWith(listObjectClientConfig, { - Bucket: bucket, - MaxKeys: 1000, - Prefix: 'public/', + accessLevelTests.forEach(({ path, options, expectedPath }) => { + const pathMsg = path ? 'custom' : 'default'; + const accessLevelMsg = options?.accessLevel ?? 'default'; + const targetIdentityIdMsg = options?.targetIdentityId + ? `with targetIdentityId` + : ''; + it(`should list objects with pagination, default pageSize, ${pathMsg} path, ${accessLevelMsg} accessLevel ${targetIdentityIdMsg}`, async () => { + mockListObject.mockImplementationOnce(() => { + return { + Contents: [ + { ...listObjectClientBaseResultItem, Key: expectedPath }, + ], + NextContinuationToken: nextToken, + }; + }); + expect.assertions(4); + let response = await list({ + prefix: path, + options: options as StorageOptions, + }); + expect(response.items).toEqual([ + { ...listResultItem, key: path ?? '' }, + ]); + expect(response.nextToken).toEqual(nextToken); + expect(listObjectsV2).toBeCalledTimes(1); + expect(listObjectsV2).toHaveBeenCalledWith(listObjectClientConfig, { + Bucket: bucket, + MaxKeys: 1000, + Prefix: expectedPath, + }); }); }); - it('Should list object with pagination using pageSize and nextToken', async () => { - mockListObject.mockImplementationOnce(() => { - return { - Contents: [listResultObj], - NextContinuationToken: nextToken, - }; - }); - - expect.assertions(4); - const customPageSize = 5; - const response = await list({ - prefix: 'listWithTokenResultsPath', - options: { - accessLevel: 'guest', - pageSize: customPageSize, - nextToken: nextToken, - }, - }); - expect(response.items).toEqual([copyResultItem]); - expect(response.nextToken).toEqual(nextToken); - expect(listObjectsV2).toBeCalledTimes(1); - expect(listObjectsV2).toHaveBeenCalledWith(listObjectClientConfig, { - Bucket: bucket, - Prefix: 'public/listWithTokenResultsPath', - ContinuationToken: nextToken, - MaxKeys: customPageSize, + accessLevelTests.forEach(({ path, options, expectedPath }) => { + const pathMsg = path ? 'custom' : 'default'; + const accessLevelMsg = options?.accessLevel ?? 'default'; + const targetIdentityIdMsg = options?.targetIdentityId + ? `with targetIdentityId` + : ''; + it(`should list objects with pagination using pageSize, nextToken, ${pathMsg} path, ${accessLevelMsg} accessLevel ${targetIdentityIdMsg}`, async () => { + mockListObject.mockImplementationOnce(() => { + return { + Contents: [ + { ...listObjectClientBaseResultItem, Key: expectedPath }, + ], + NextContinuationToken: nextToken, + }; + }); + expect.assertions(4); + const customPageSize = 5; + const response = await list({ + prefix: path, + options: { + ...(options as StorageOptions), + pageSize: customPageSize, + nextToken: nextToken, + }, + }); + expect(response.items).toEqual([ + { ...listResultItem, key: path ?? '' }, + ]); + expect(response.nextToken).toEqual(nextToken); + expect(listObjectsV2).toBeCalledTimes(1); + expect(listObjectsV2).toHaveBeenCalledWith(listObjectClientConfig, { + Bucket: bucket, + Prefix: expectedPath, + ContinuationToken: nextToken, + MaxKeys: customPageSize, + }); }); }); - it('Should list all objects successfully having three pages', async () => { - expect.assertions(5); - mockListObjectsV2ApiWithPages(3); - - const result = await list({ - prefix: 'listALLResultsPath', - options: { accessLevel: 'guest', listAll: true }, + accessLevelTests.forEach(({ path, options, expectedPath }) => { + const pathMsg = path ? 'custom' : 'default'; + const accessLevelMsg = options?.accessLevel ?? 'default'; + const targetIdentityIdMsg = options?.targetIdentityId + ? `with targetIdentityId` + : ''; + it(`should list objects with zero results with ${pathMsg} path, ${accessLevelMsg} accessLevel ${targetIdentityIdMsg}`, async () => { + mockListObject.mockImplementationOnce(() => { + return {}; + }); + expect.assertions(3); + let response = await list({ + prefix: path, + options: options as StorageOptions, + }); + expect(response.items).toEqual([]); + expect(response.nextToken).toEqual(undefined); + expect(listObjectsV2).toHaveBeenCalledWith(listObjectClientConfig, { + Bucket: bucket, + MaxKeys: 1000, + Prefix: expectedPath, + }); }); + }); - expect(result.items).toEqual([ - copyResultItem, - copyResultItem, - copyResultItem, - ]); - expect(result).not.toHaveProperty(nextToken); + accessLevelTests.forEach(({ path, options, expectedPath }) => { + const pathMsg = path ? 'custom' : 'default'; + const accessLevelMsg = options?.accessLevel ?? 'default'; + const targetIdentityIdMsg = options?.targetIdentityId + ? `with targetIdentityId` + : ''; + it(`should list all objects having three pages with ${pathMsg} path, ${accessLevelMsg} accessLevel ${targetIdentityIdMsg}`, async () => { + expect.assertions(5); + mockListObjectsV2ApiWithPages(3); + const result = await list({ + prefix: path, + options: { ...(options as StorageOptions), listAll: true }, + }); - // listing three times for three pages - expect(listObjectsV2).toHaveBeenCalledTimes(3); + const listResult = { ...listResultItem, key: path ?? '' }; + expect(result.items).toEqual([listResult, listResult, listResult]); + expect(result).not.toHaveProperty(nextToken); - // first input recieves undefined as the Continuation Token - expect(listObjectsV2).toHaveBeenNthCalledWith(1, listObjectClientConfig, { - Bucket: bucket, - Prefix: 'public/listALLResultsPath', - MaxKeys: 1000, - ContinuationToken: undefined, - }); - // last input recieves TEST_TOKEN as the Continuation Token - expect(listObjectsV2).toHaveBeenNthCalledWith(3, listObjectClientConfig, { - Bucket: bucket, - Prefix: 'public/listALLResultsPath', - MaxKeys: 1000, - ContinuationToken: nextToken, - }); - }); + // listing three times for three pages + expect(listObjectsV2).toHaveBeenCalledTimes(3); - it('Should list objects with zero results', async () => { - mockListObject.mockImplementationOnce(() => { - return {}; - }); - - expect.assertions(3); - let response = await list({ - prefix: 'emptyListResultsPath', - options: { - accessLevel: 'guest', - }, - }); - expect(response.items).toEqual([]); - expect(response.nextToken).toEqual(undefined); - expect(listObjectsV2).toHaveBeenCalledWith(listObjectClientConfig, { - Bucket: bucket, - MaxKeys: 1000, - Prefix: 'public/emptyListResultsPath', + // first input recieves undefined as the Continuation Token + expect(listObjectsV2).toHaveBeenNthCalledWith( + 1, + listObjectClientConfig, + { + Bucket: bucket, + Prefix: expectedPath, + MaxKeys: 1000, + ContinuationToken: undefined, + } + ); + // last input recieves TEST_TOKEN as the Continuation Token + expect(listObjectsV2).toHaveBeenNthCalledWith( + 3, + listObjectClientConfig, + { + Bucket: bucket, + Prefix: expectedPath, + MaxKeys: 1000, + ContinuationToken: nextToken, + } + ); }); }); }); @@ -204,7 +265,7 @@ describe('list API', () => { afterEach(() => { jest.clearAllMocks(); }); - it('Should return a not found error', async () => { + it('should return a not found error', async () => { mockListObject.mockRejectedValueOnce( Object.assign(new Error(), { $metadata: { httpStatusCode: 404 }, diff --git a/packages/storage/__tests__/providers/s3/apis/remove.test.ts b/packages/storage/__tests__/providers/s3/apis/remove.test.ts index b7a40c108c2..f5dcf3813d6 100644 --- a/packages/storage/__tests__/providers/s3/apis/remove.test.ts +++ b/packages/storage/__tests__/providers/s3/apis/remove.test.ts @@ -5,6 +5,7 @@ import { Credentials } from '@aws-sdk/types'; import { Amplify } from '@aws-amplify/core'; import { deleteObject } from '../../../../src/providers/s3/utils/client'; import { remove } from '../../../../src/providers/s3/apis'; +import { StorageOptions } from '../../../../src/types'; jest.mock('../../../../src/providers/s3/utils/client'); jest.mock('@aws-amplify/core', () => ({ @@ -21,7 +22,7 @@ const mockGetConfig = Amplify.getConfig as jest.Mock; const key = 'key'; const bucket = 'bucket'; const region = 'region'; -const targetIdentityId = 'targetIdentityId'; +const defaultIdentityId = 'defaultIdentityId'; const removeResult = { key }; const credentials: Credentials = { accessKeyId: 'accessKeyId', @@ -37,13 +38,13 @@ describe('remove API', () => { beforeAll(() => { mockFetchAuthSession.mockResolvedValue({ credentials, - identityId: targetIdentityId, + identityId: defaultIdentityId, }); mockGetConfig.mockReturnValue({ Storage: { S3: { - bucket: 'bucket', - region: 'region', + bucket, + region, }, }, }); @@ -59,52 +60,35 @@ describe('remove API', () => { afterEach(() => { jest.clearAllMocks(); }); + [ + { + expectedKey: `public/${key}`, + }, + { + options: { accessLevel: 'guest' }, + expectedKey: `public/${key}`, + }, + { + options: { accessLevel: 'private' }, + expectedKey: `private/${defaultIdentityId}/${key}`, + }, + { + options: { accessLevel: 'protected' }, + expectedKey: `protected/${defaultIdentityId}/${key}`, + }, + ].forEach(({ options, expectedKey }) => { + const accessLevel = options?.accessLevel ?? 'default'; - it('Should remove object with default accessLevel', async () => { - expect.assertions(3); - expect(await remove({ key })).toEqual(removeResult); - expect(deleteObject).toBeCalledTimes(1); - expect(deleteObject).toHaveBeenCalledWith(deleteObjectClientConfig, { - Bucket: bucket, - Key: `public/${key}`, - }); - }); - - it('Should remove object with guest accessLevel', async () => { - expect.assertions(3); - expect(await remove({ key, options: { accessLevel: 'guest' } })).toEqual( - removeResult - ); - expect(deleteObject).toBeCalledTimes(1); - expect(deleteObject).toHaveBeenCalledWith(deleteObjectClientConfig, { - Bucket: bucket, - Key: `public/${key}`, - }); - }); - - it('Should remove object with private accessLevel', async () => { - expect.assertions(3); - const accessLevel = 'private'; - expect(await remove({ key, options: { accessLevel } })).toEqual( - removeResult - ); - expect(deleteObject).toBeCalledTimes(1); - expect(deleteObject).toHaveBeenCalledWith(deleteObjectClientConfig, { - Bucket: bucket, - Key: `${accessLevel}/${targetIdentityId}/${key}`, - }); - }); - - it('Should remove object with protected accessLevel', async () => { - expect.assertions(3); - const accessLevel = 'protected'; - expect(await remove({ key, options: { accessLevel } })).toEqual( - removeResult - ); - expect(deleteObject).toBeCalledTimes(1); - expect(deleteObject).toHaveBeenCalledWith(deleteObjectClientConfig, { - Bucket: bucket, - Key: `${accessLevel}/${targetIdentityId}/${key}`, + it(`should remove object with ${accessLevel} accessLevel`, async () => { + expect.assertions(3); + expect( + await remove({ key, options: options as StorageOptions }) + ).toEqual(removeResult); + expect(deleteObject).toBeCalledTimes(1); + expect(deleteObject).toHaveBeenCalledWith(deleteObjectClientConfig, { + Bucket: bucket, + Key: expectedKey, + }); }); }); }); @@ -113,7 +97,7 @@ describe('remove API', () => { afterEach(() => { jest.clearAllMocks(); }); - it('Should return a not found error', async () => { + it('should return a not found error', async () => { mockDeleteObject.mockRejectedValueOnce( Object.assign(new Error(), { $metadata: { httpStatusCode: 404 }, diff --git a/packages/storage/__tests__/providers/s3/apis/uploadData/multipartHandlers.test.ts b/packages/storage/__tests__/providers/s3/apis/uploadData/multipartHandlers.test.ts index 69a6aafe8a9..ac0808f60ee 100644 --- a/packages/storage/__tests__/providers/s3/apis/uploadData/multipartHandlers.test.ts +++ b/packages/storage/__tests__/providers/s3/apis/uploadData/multipartHandlers.test.ts @@ -19,6 +19,7 @@ import { import { UPLOADS_STORAGE_KEY } from '../../../../../src/providers/s3/utils/constants'; import { byteLength } from '../../../../../src/providers/s3/apis/uploadData/byteLength'; import { CanceledError } from '../../../../../src/errors/CanceledError'; +import { StorageOptions } from '../../../../../src/types'; jest.mock('@aws-amplify/core'); jest.mock('../../../../../src/providers/s3/utils/client'); @@ -28,7 +29,7 @@ const credentials: Credentials = { sessionToken: 'sessionToken', secretAccessKey: 'secretAccessKey', }; -const identityId = 'identityId'; +const defaultIdentityId = 'defaultIdentityId'; const mockFetchAuthSession = Amplify.Auth.fetchAuthSession as jest.Mock; const bucket = 'bucket'; const region = 'region'; @@ -119,7 +120,7 @@ describe('getMultipartUploadHandlers', () => { beforeAll(() => { mockFetchAuthSession.mockResolvedValue({ credentials, - identityId, + identityId: defaultIdentityId, }); (Amplify.getConfig as jest.Mock).mockReturnValue({ Storage: { @@ -154,41 +155,61 @@ describe('getMultipartUploadHandlers', () => { describe('upload', () => { const getBlob = (size: number) => new Blob(['1'.repeat(size)]); - it.each([ - ['file', new File([getBlob(8 * MB)], 'someName')], - ['blob', getBlob(8 * MB)], - ['string', '1'.repeat(8 * MB)], - ['arrayBuffer', new ArrayBuffer(8 * MB)], - ['arrayBufferView', new Uint8Array(8 * MB)], - ])( - 'should upload a %s type body that splits in 2 parts', - async (_, twoPartsPayload) => { - mockMultipartUploadSuccess(); - const { multipartUploadJob } = getMultipartUploadHandlers({ - key: defaultKey, - data: twoPartsPayload, - }); - const result = await multipartUploadJob(); - expect(mockCreateMultipartUpload).toBeCalledWith( - expect.objectContaining({ - credentials, - region, - abortSignal: expect.any(AbortSignal), - }), - expect.objectContaining({ - Bucket: bucket, - Key: `public/${defaultKey}`, - ContentType: defaultContentType, - }) - ); - expect(result).toEqual( - expect.objectContaining({ key: defaultKey, eTag: 'etag' }) - ); - expect(mockCreateMultipartUpload).toBeCalledTimes(1); - expect(mockUploadPart).toBeCalledTimes(2); - expect(mockCompleteMultipartUpload).toBeCalledTimes(1); - } - ); + [ + { + expectedKey: `public/${defaultKey}`, + }, + { + options: { accessLevel: 'guest' }, + expectedKey: `public/${defaultKey}`, + }, + { + options: { accessLevel: 'private' }, + expectedKey: `private/${defaultIdentityId}/${defaultKey}`, + }, + { + options: { accessLevel: 'protected' }, + expectedKey: `protected/${defaultIdentityId}/${defaultKey}`, + }, + ].forEach(({ options, expectedKey }) => { + const accessLevelMsg = options?.accessLevel ?? 'default'; + it.each([ + ['file', new File([getBlob(8 * MB)], 'someName')], + ['blob', getBlob(8 * MB)], + ['string', '1'.repeat(8 * MB)], + ['arrayBuffer', new ArrayBuffer(8 * MB)], + ['arrayBufferView', new Uint8Array(8 * MB)], + ])( + `should upload a %s type body that splits in 2 parts using ${accessLevelMsg} accessLevel`, + async (_, twoPartsPayload) => { + mockMultipartUploadSuccess(); + const { multipartUploadJob } = getMultipartUploadHandlers({ + key: defaultKey, + data: twoPartsPayload, + options: options as StorageOptions, + }); + const result = await multipartUploadJob(); + expect(mockCreateMultipartUpload).toBeCalledWith( + expect.objectContaining({ + credentials, + region, + abortSignal: expect.any(AbortSignal), + }), + expect.objectContaining({ + Bucket: bucket, + Key: expectedKey, + ContentType: defaultContentType, + }) + ); + expect(result).toEqual( + expect.objectContaining({ key: defaultKey, eTag: 'etag' }) + ); + expect(mockCreateMultipartUpload).toBeCalledTimes(1); + expect(mockUploadPart).toBeCalledTimes(2); + expect(mockCompleteMultipartUpload).toBeCalledTimes(1); + } + ); + }); it('should throw if unsupported payload type is provided', async () => { mockMultipartUploadSuccess(); diff --git a/packages/storage/src/index.ts b/packages/storage/src/index.ts index 4a4c9bd45b7..06c52e118cc 100644 --- a/packages/storage/src/index.ts +++ b/packages/storage/src/index.ts @@ -10,6 +10,31 @@ export { copy, getUrl, } from './providers/s3'; + +export { + UploadDataInput, + DownloadDataInput, + RemoveInput, + ListAllInput, + ListPaginateInput, + GetPropertiesInput, + CopyInput, + GetUrlInput, +} from './providers/s3/types/inputs'; + +export { + UploadDataOutput, + DownloadDataOutput, + RemoveOutput, + ListAllOutput, + ListPaginateOutput, + GetPropertiesOutput, + CopyOutput, + GetUrlOutput, +} from './providers/s3/types/outputs'; + +export { TransferProgressEvent, TransferTaskState } from './types'; + // TODO[AllanZhengYP]: support isCancelError in Node.js with node-fetch export { isCancelError } from './errors/CanceledError'; export { StorageError } from './errors/StorageError'; diff --git a/packages/storage/src/providers/s3/apis/copy.ts b/packages/storage/src/providers/s3/apis/copy.ts index 9baee642de8..17c32e557e2 100644 --- a/packages/storage/src/providers/s3/apis/copy.ts +++ b/packages/storage/src/providers/s3/apis/copy.ts @@ -2,8 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { Amplify } from '@aws-amplify/core'; -import { CopyRequest } from '../../../types'; -import { S3CopyResult } from '../types'; +import { CopyInput, CopyOutput } from '../types'; import { copy as copyInternal } from './internal/copy'; /** @@ -11,12 +10,12 @@ import { copy as copyInternal } from './internal/copy'; * different level or identityId (if source object's level is 'protected'). * * @async - * @param {CopyRequest} copyRequest - The request object. - * @return {Promise} Promise resolves upon successful copy of the object. + * @param {CopyInput} input - The request object. + * @return {Promise} Promise resolves upon successful copy of the object. * @throws service: {@link S3Exception} - Thrown when checking for existence of the object * @throws validation: {@link StorageValidationErrorCode } - Thrown when * source or destination key are not defined. */ -export const copy = async (copyRequest: CopyRequest): Promise => { - return copyInternal(Amplify, copyRequest); +export const copy = async (input: CopyInput): Promise => { + return copyInternal(Amplify, input); }; diff --git a/packages/storage/src/providers/s3/apis/downloadData.ts b/packages/storage/src/providers/s3/apis/downloadData.ts index 2ee1ce265ec..de948b18536 100644 --- a/packages/storage/src/providers/s3/apis/downloadData.ts +++ b/packages/storage/src/providers/s3/apis/downloadData.ts @@ -3,19 +3,18 @@ import { Amplify } from '@aws-amplify/core'; -import { S3TransferOptions, S3DownloadDataResult } from '../types'; +import { DownloadDataInput, DownloadDataOutput } from '../types'; import { resolveS3ConfigAndInput } from '../utils/resolveS3ConfigAndInput'; import { StorageValidationErrorCode } from '../../../errors/types/validation'; -import { StorageDownloadDataRequest, DownloadTask } from '../../../types'; import { createDownloadTask } from '../utils'; import { getObject } from '../utils/client'; /** * Download S3 object data to memory * - * @param {StorageDownloadDataRequest} downloadDataRequest The parameters that are passed to the + * @param {DownloadDataRequest} input The parameters that are passed to the * downloadData operation. - * @returns {DownloadTask} Cancelable task exposing result promise from `result` property. + * @returns {DownloadDataOutput} Cancelable task exposing result promise from `result` property. * @throws service: {@link S3Exception} - thrown when checking for existence of the object * @throws validation: {@link StorageValidationErrorCode } - Validation errors * @@ -41,13 +40,11 @@ import { getObject } from '../utils/client'; * } *``` */ -export const downloadData = ( - downloadDataRequest: StorageDownloadDataRequest -): DownloadTask => { +export const downloadData = (input: DownloadDataInput): DownloadDataOutput => { const abortController = new AbortController(); const downloadTask = createDownloadTask({ - job: downloadDataJob(downloadDataRequest, abortController.signal), + job: downloadDataJob(input, abortController.signal), onCancel: (abortErrorOverwrite?: Error) => { abortController.abort(abortErrorOverwrite); }, @@ -57,10 +54,7 @@ export const downloadData = ( const downloadDataJob = ( - { - options: downloadDataOptions, - key, - }: StorageDownloadDataRequest, + { options: downloadDataOptions, key }: DownloadDataInput, abortSignal: AbortSignal ) => async () => { diff --git a/packages/storage/src/providers/s3/apis/getProperties.ts b/packages/storage/src/providers/s3/apis/getProperties.ts index 523e6e53364..33034f4132a 100644 --- a/packages/storage/src/providers/s3/apis/getProperties.ts +++ b/packages/storage/src/providers/s3/apis/getProperties.ts @@ -2,21 +2,20 @@ // SPDX-License-Identifier: Apache-2.0 import { Amplify } from '@aws-amplify/core'; -import { StorageOperationRequest, StorageOptions } from '../../../types'; -import { S3GetPropertiesResult } from '../types'; +import { GetPropertiesOutput, GetPropertiesInput } from '../types'; import { getProperties as getPropertiesInternal } from './internal/getProperties'; /** * Gets the properties of a file. The properties include S3 system metadata and * the user metadata that was provided when uploading the file. * - * @param {StorageOperationRequest} req The request to make an API call. - * @returns {Promise} A promise that resolves the properties. + * @param {GetPropertiesInput} The input to make an API call. + * @returns {Promise} A promise that resolves the properties. * @throws A {@link S3Exception} when the underlying S3 service returned error. * @throws A {@link StorageValidationErrorCode} when API call parameters are invalid. */ export const getProperties = ( - req: StorageOperationRequest -): Promise => { - return getPropertiesInternal(Amplify, req); + input: GetPropertiesInput +): Promise => { + return getPropertiesInternal(Amplify, input); }; diff --git a/packages/storage/src/providers/s3/apis/getUrl.ts b/packages/storage/src/providers/s3/apis/getUrl.ts index 9c87b12a467..4fdcafa2a4d 100644 --- a/packages/storage/src/providers/s3/apis/getUrl.ts +++ b/packages/storage/src/providers/s3/apis/getUrl.ts @@ -2,8 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 import { Amplify } from '@aws-amplify/core'; -import { StorageDownloadDataRequest } from '../../../types'; -import { S3GetUrlOptions, S3GetUrlResult } from '../types'; +import {} from '../../../types'; +import { GetUrlInput, GetUrlOutput } from '../types'; import { getUrl as getUrlInternal } from './internal/getUrl'; /** @@ -14,15 +14,13 @@ import { getUrl as getUrlInternal } from './internal/getUrl'; * to true, this method will verify the given object already exists in S3 before returning a presigned * URL, and will throw {@link StorageError} if the object does not exist. * - * @param {StorageDownloadDataRequest} The request object - * @return {Promise} url of the object + * @param {GetUrlInput} The input object + * @return {Promise} url of the object * @throws service: {@link S3Exception} - thrown when checking for existence of the object * @throws validation: {@link StorageValidationErrorCode } - Validation errors * thrown either username or key are not defined. * */ -export const getUrl = ( - req: StorageDownloadDataRequest -): Promise => { - return getUrlInternal(Amplify, req); +export const getUrl = (input: GetUrlInput): Promise => { + return getUrlInternal(Amplify, input); }; diff --git a/packages/storage/src/providers/s3/apis/internal/copy.ts b/packages/storage/src/providers/s3/apis/internal/copy.ts index 1fe6db384e9..0120a49e97b 100644 --- a/packages/storage/src/providers/s3/apis/internal/copy.ts +++ b/packages/storage/src/providers/s3/apis/internal/copy.ts @@ -2,8 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { AmplifyClassV6 } from '@aws-amplify/core'; -import { S3CopyResult } from '../../types'; -import { CopyRequest } from '../../../../types'; +import { CopyInput, CopyOutput } from '../../types'; import { resolveS3ConfigAndInput } from '../../utils'; import { StorageValidationErrorCode } from '../../../../errors/types/validation'; import { assertValidationError } from '../../../../errors/utils/assertValidationError'; @@ -11,12 +10,12 @@ import { copyObject } from '../../utils/client'; export const copy = async ( amplify: AmplifyClassV6, - copyRequest: CopyRequest -): Promise => { + input: CopyInput +): Promise => { const { source: { key: sourceKey }, destination: { key: destinationKey }, - } = copyRequest; + } = input; assertValidationError(!!sourceKey, StorageValidationErrorCode.NoSourceKey); assertValidationError( @@ -28,10 +27,10 @@ export const copy = async ( s3Config, bucket, keyPrefix: sourceKeyPrefix, - } = await resolveS3ConfigAndInput(amplify, copyRequest.source); + } = await resolveS3ConfigAndInput(amplify, input.source); const { keyPrefix: destinationKeyPrefix } = await resolveS3ConfigAndInput( amplify, - copyRequest.destination + input.destination ); // resolveS3ConfigAndInput does not make extra API calls or storage access if called repeatedly. // TODO(ashwinkumar6) V6-logger: warn `You may copy files from another user if the source level is "protected", currently it's ${srcLevel}` diff --git a/packages/storage/src/providers/s3/apis/internal/getProperties.ts b/packages/storage/src/providers/s3/apis/internal/getProperties.ts index 11448142fb4..ce8c4d3168e 100644 --- a/packages/storage/src/providers/s3/apis/internal/getProperties.ts +++ b/packages/storage/src/providers/s3/apis/internal/getProperties.ts @@ -2,16 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 import { AmplifyClassV6 } from '@aws-amplify/core'; -import { StorageOptions, StorageOperationRequest } from '../../../../types'; -import { S3GetPropertiesResult } from '../../types'; +import { GetPropertiesInput, GetPropertiesOutput } from '../../types'; import { resolveS3ConfigAndInput } from '../../utils'; import { headObject } from '../../utils/client'; export const getProperties = async function ( amplify: AmplifyClassV6, - getPropertiesRequest: StorageOperationRequest -): Promise { - const { key, options } = getPropertiesRequest; + input: GetPropertiesInput +): Promise { + const { key, options } = input; const { s3Config, bucket, keyPrefix } = await resolveS3ConfigAndInput( amplify, options diff --git a/packages/storage/src/providers/s3/apis/internal/getUrl.ts b/packages/storage/src/providers/s3/apis/internal/getUrl.ts index a99ebb26fb1..a0562c7c479 100644 --- a/packages/storage/src/providers/s3/apis/internal/getUrl.ts +++ b/packages/storage/src/providers/s3/apis/internal/getUrl.ts @@ -2,9 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { AmplifyClassV6 } from '@aws-amplify/core'; - -import { StorageDownloadDataRequest } from '../../../../types'; -import { S3GetUrlOptions, S3GetUrlResult } from '../../types'; +import { GetUrlInput, GetUrlOutput } from '../../types'; import { StorageValidationErrorCode } from '../../../../errors/types/validation'; import { getPresignedGetObjectUrl } from '../../utils/client'; import { getProperties } from './getProperties'; @@ -17,9 +15,9 @@ import { export const getUrl = async function ( amplify: AmplifyClassV6, - getUrlRequest: StorageDownloadDataRequest -): Promise { - const { key, options } = getUrlRequest; + input: GetUrlInput +): Promise { + const { key, options } = input; if (options?.validateObjectExistence) { await getProperties(amplify, { key, options }); diff --git a/packages/storage/src/providers/s3/apis/internal/list.ts b/packages/storage/src/providers/s3/apis/internal/list.ts index fff8647d7ad..f5321198262 100644 --- a/packages/storage/src/providers/s3/apis/internal/list.ts +++ b/packages/storage/src/providers/s3/apis/internal/list.ts @@ -3,14 +3,11 @@ import { AmplifyClassV6 } from '@aws-amplify/core'; import { - StorageListRequest, - StorageListAllOptions, - StorageListPaginateOptions, -} from '../../../../types'; -import { - S3ListOutputItem, - S3ListAllResult, - S3ListPaginateResult, + ListAllInput, + ListPaginateInput, + ListAllOutput, + ListPaginateOutput, + ListOutputItem, } from '../../types'; import { resolveS3ConfigAndInput } from '../../utils'; import { ResolvedS3Config } from '../../types/options'; @@ -22,7 +19,7 @@ import { const MAX_PAGE_SIZE = 1000; -type ListRequestArgs = { +type ListInputArgs = { s3Config: ResolvedS3Config; listParams: ListObjectsV2Input; prefix: string; @@ -30,11 +27,9 @@ type ListRequestArgs = { export const list = async ( amplify: AmplifyClassV6, - listRequest?: - | StorageListRequest - | StorageListRequest -): Promise => { - const { options = {}, prefix: path = '' } = listRequest ?? {}; + input?: ListAllInput | ListPaginateInput +): Promise => { + const { options = {}, prefix: path = '' } = input ?? {}; const { s3Config, bucket, @@ -55,9 +50,9 @@ const _listAll = async ({ s3Config, listParams, prefix, -}: ListRequestArgs): Promise => { +}: ListInputArgs): Promise => { // TODO(ashwinkumar6) V6-logger: pageSize and nextToken aren't required when listing all items - const listResult: S3ListOutputItem[] = []; + const listResult: ListOutputItem[] = []; let continuationToken = listParams.ContinuationToken; do { const { items: pageResults, nextToken: pageNextToken } = await _list({ @@ -82,7 +77,7 @@ const _list = async ({ s3Config, listParams, prefix, -}: ListRequestArgs): Promise => { +}: ListInputArgs): Promise => { const listParamsClone = { ...listParams }; if (!listParamsClone.MaxKeys || listParamsClone.MaxKeys > MAX_PAGE_SIZE) { listParamsClone.MaxKeys = MAX_PAGE_SIZE; diff --git a/packages/storage/src/providers/s3/apis/internal/remove.ts b/packages/storage/src/providers/s3/apis/internal/remove.ts index 15e8314c9a9..93fc9a6bead 100644 --- a/packages/storage/src/providers/s3/apis/internal/remove.ts +++ b/packages/storage/src/providers/s3/apis/internal/remove.ts @@ -2,20 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 import { AmplifyClassV6 } from '@aws-amplify/core'; - -import { - StorageOperationRequest, - StorageRemoveOptions, - StorageRemoveResult, -} from '../../../../types'; +import { RemoveInput, RemoveOutput } from '../../types'; import { resolveS3ConfigAndInput } from '../../utils'; import { deleteObject } from '../../utils/client'; export const remove = async ( amplify: AmplifyClassV6, - removeRequest: StorageOperationRequest -): Promise => { - const { key, options = {} } = removeRequest; + input: RemoveInput +): Promise => { + const { key, options = {} } = input; const { s3Config, keyPrefix, bucket } = await resolveS3ConfigAndInput( amplify, options diff --git a/packages/storage/src/providers/s3/apis/list.ts b/packages/storage/src/providers/s3/apis/list.ts index f54f78f0678..d7910c43ba3 100644 --- a/packages/storage/src/providers/s3/apis/list.ts +++ b/packages/storage/src/providers/s3/apis/list.ts @@ -3,37 +3,35 @@ import { Amplify } from '@aws-amplify/core'; import { - StorageListAllOptions, - StorageListPaginateOptions, - StorageListRequest, -} from '../../../types'; -import { S3ListAllResult, S3ListPaginateResult } from '../types'; + ListAllInput, + ListPaginateInput, + ListAllOutput, + ListPaginateOutput, +} from '../types'; import { list as listInternal } from './internal/list'; -type S3ListApi = { +type ListApi = { /** * List files with given prefix in pages * pageSize defaulted to 1000. Additionally, the result will include a nextToken if there are more items to retrieve. - * @param {StorageListRequest} req - The request object - * @return {Promise} - Promise resolves to list of keys and metadata with + * @param {ListPaginateInput} The input object + * @return {Promise} - Promise resolves to list of keys and metadata with * @throws service: {@link S3Exception} - S3 service errors thrown when checking for existence of bucket * @throws validation: {@link StorageValidationErrorCode } - thrown when there are issues with credentials */ - ( - req?: StorageListRequest - ): Promise; + (input?: ListPaginateInput): Promise; /** * List all files from S3. You can set `listAll` to true in `options` to get all the files from S3. - * @param {StorageListRequest} req - The request object - * @return {Promise} - Promise resolves to list of keys and metadata for all objects in path + * @param {ListAllInput} The input object + * @return {Promise} - Promise resolves to list of keys and metadata for all objects in path * @throws service: {@link S3Exception} - S3 service errors thrown when checking for existence of bucket * @throws validation: {@link StorageValidationErrorCode } - thrown when there are issues with credentials */ - (req?: StorageListRequest): Promise; + (input?: ListAllInput): Promise; }; -export const list: S3ListApi = ( - req -): Promise => { - return listInternal(Amplify, req ?? {}); +export const list: ListApi = ( + input?: ListAllInput | ListPaginateInput +): Promise => { + return listInternal(Amplify, input ?? {}); }; diff --git a/packages/storage/src/providers/s3/apis/remove.ts b/packages/storage/src/providers/s3/apis/remove.ts index 76d0a75b3a2..4a4b7428c42 100644 --- a/packages/storage/src/providers/s3/apis/remove.ts +++ b/packages/storage/src/providers/s3/apis/remove.ts @@ -2,22 +2,16 @@ // SPDX-License-Identifier: Apache-2.0 import { Amplify } from '@aws-amplify/core'; -import { - StorageOperationRequest, - StorageRemoveOptions, - StorageRemoveResult, -} from '../../../types'; +import { RemoveInput, RemoveOutput } from '../types'; import { remove as removeInternal } from './internal/remove'; /** * Remove a file from your S3 bucket. - * @param {StorageOperationRequest} req - The request object - * @return {Promise} - Promise resolves upon successful removal of the object + * @param {RemoveInput} The input object + * @return {Promise} - Promise resolves upon successful removal of the object * @throws service: {@link S3Exception} - S3 service errors thrown while getting properties * @throws validation: {@link StorageValidationErrorCode } - Validation errors thrown */ -export const remove = ( - req: StorageOperationRequest -): Promise => { - return removeInternal(Amplify, req); +export const remove = (input: RemoveInput): Promise => { + return removeInternal(Amplify, input); }; diff --git a/packages/storage/src/providers/s3/apis/server/copy.ts b/packages/storage/src/providers/s3/apis/server/copy.ts index 75b039f0aae..ea8e9de90f8 100644 --- a/packages/storage/src/providers/s3/apis/server/copy.ts +++ b/packages/storage/src/providers/s3/apis/server/copy.ts @@ -5,16 +5,12 @@ import { AmplifyServer, getAmplifyServerContext, } from '@aws-amplify/core/internals/adapter-core'; -import { CopyRequest } from '../../../../types'; -import { S3CopyResult } from '../../types'; +import { CopyInput, CopyOutput } from '../../types'; import { copy as copyInternal } from '../internal/copy'; export const copy = async ( contextSpec: AmplifyServer.ContextSpec, - copyRequest: CopyRequest -): Promise => { - return copyInternal( - getAmplifyServerContext(contextSpec).amplify, - copyRequest - ); + input: CopyInput +): Promise => { + return copyInternal(getAmplifyServerContext(contextSpec).amplify, input); }; diff --git a/packages/storage/src/providers/s3/apis/server/getProperties.ts b/packages/storage/src/providers/s3/apis/server/getProperties.ts index d9ec4f490f1..c26bf24f502 100644 --- a/packages/storage/src/providers/s3/apis/server/getProperties.ts +++ b/packages/storage/src/providers/s3/apis/server/getProperties.ts @@ -5,16 +5,15 @@ import { AmplifyServer, getAmplifyServerContext, } from '@aws-amplify/core/internals/adapter-core'; -import { StorageOperationRequest, StorageOptions } from '../../../../types'; -import { S3GetPropertiesResult } from '../../types'; +import { GetPropertiesInput, GetPropertiesOutput } from '../../types'; import { getProperties as getPropertiesInternal } from '../internal/getProperties'; export const getProperties = ( contextSpec: AmplifyServer.ContextSpec, - req: StorageOperationRequest -): Promise => { + input: GetPropertiesInput +): Promise => { return getPropertiesInternal( getAmplifyServerContext(contextSpec).amplify, - req + input ); }; diff --git a/packages/storage/src/providers/s3/apis/server/getUrl.ts b/packages/storage/src/providers/s3/apis/server/getUrl.ts index 44a1ad76740..7b6cc802cbe 100644 --- a/packages/storage/src/providers/s3/apis/server/getUrl.ts +++ b/packages/storage/src/providers/s3/apis/server/getUrl.ts @@ -5,13 +5,12 @@ import { AmplifyServer, getAmplifyServerContext, } from '@aws-amplify/core/internals/adapter-core'; -import { StorageDownloadDataRequest } from '../../../../types'; -import { S3GetUrlOptions, S3GetUrlResult } from '../../types'; +import { GetUrlInput, GetUrlOutput } from '../../types'; import { getUrl as getUrlInternal } from '../internal/getUrl'; export const getUrl = async ( contextSpec: AmplifyServer.ContextSpec, - req: StorageDownloadDataRequest -): Promise => { - return getUrlInternal(getAmplifyServerContext(contextSpec).amplify, req); + input: GetUrlInput +): Promise => { + return getUrlInternal(getAmplifyServerContext(contextSpec).amplify, input); }; diff --git a/packages/storage/src/providers/s3/apis/server/list.ts b/packages/storage/src/providers/s3/apis/server/list.ts index 68b81fdf165..970301c1622 100644 --- a/packages/storage/src/providers/s3/apis/server/list.ts +++ b/packages/storage/src/providers/s3/apis/server/list.ts @@ -6,39 +6,45 @@ import { getAmplifyServerContext, } from '@aws-amplify/core/internals/adapter-core'; import { - StorageListAllOptions, - StorageListPaginateOptions, - StorageListRequest, -} from '../../../../types'; -import { S3ListAllResult, S3ListPaginateResult } from '../../types'; + ListAllInput, + ListPaginateInput, + ListAllOutput, + ListPaginateOutput, +} from '../../types'; import { list as listInternal } from '../internal/list'; -type S3ListApi = { +type ListApi = { /** * Lists bucket objects with pagination. - * @param {StorageListRequest} req - The request object - * @return {Promise} - Promise resolves to list of keys and metadata with + * @param {ListPaginateInput} The input object + * @return {Promise} - Promise resolves to list of keys and metadata with * pageSize defaulting to 1000. Additionally the result will include a nextToken if there are more items to retrieve * @throws service: {@link S3Exception} - S3 service errors thrown when checking for existence of bucket * @throws validation: {@link StorageValidationErrorCode } - thrown when there are issues with credentials */ ( contextSpec: AmplifyServer.ContextSpec, - req?: StorageListRequest - ): Promise; + input?: ListPaginateInput + ): Promise; /** * Lists all bucket objects. - * @param {StorageListRequest} req - The request object - * @return {Promise} - Promise resolves to list of keys and metadata for all objects in path + * @param {ListAllInput} The input object + * @return {Promise} - Promise resolves to list of keys and metadata for all objects in path * @throws service: {@link S3Exception} - S3 service errors thrown when checking for existence of bucket * @throws validation: {@link StorageValidationErrorCode } - thrown when there are issues with credentials */ ( contextSpec: AmplifyServer.ContextSpec, - req?: StorageListRequest - ): Promise; + input?: ListAllInput + ): Promise; }; -export const list: S3ListApi = (contextSpec, req) => { - return listInternal(getAmplifyServerContext(contextSpec).amplify, req ?? {}); +export const list: ListApi = ( + contextSpec: AmplifyServer.ContextSpec, + input?: ListAllInput | ListPaginateInput +): Promise => { + return listInternal( + getAmplifyServerContext(contextSpec).amplify, + input ?? {} + ); }; diff --git a/packages/storage/src/providers/s3/apis/server/remove.ts b/packages/storage/src/providers/s3/apis/server/remove.ts index bfd1a97ef8d..d54d0687333 100644 --- a/packages/storage/src/providers/s3/apis/server/remove.ts +++ b/packages/storage/src/providers/s3/apis/server/remove.ts @@ -5,16 +5,12 @@ import { AmplifyServer, getAmplifyServerContext, } from '@aws-amplify/core/internals/adapter-core'; -import { - StorageOperationRequest, - StorageRemoveOptions, - StorageRemoveResult, -} from '../../../../types'; +import { RemoveInput, RemoveOutput } from '../../types'; import { remove as removeInternal } from '../internal/remove'; export const remove = ( contextSpec: AmplifyServer.ContextSpec, - req: StorageOperationRequest -): Promise => { - return removeInternal(getAmplifyServerContext(contextSpec).amplify, req); + input: RemoveInput +): Promise => { + return removeInternal(getAmplifyServerContext(contextSpec).amplify, input); }; diff --git a/packages/storage/src/providers/s3/apis/uploadData/index.ts b/packages/storage/src/providers/s3/apis/uploadData/index.ts index 3dcf3a783a7..03dbe76f19b 100644 --- a/packages/storage/src/providers/s3/apis/uploadData/index.ts +++ b/packages/storage/src/providers/s3/apis/uploadData/index.ts @@ -1,9 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { S3UploadDataResult, S3UploadOptions } from '../../types'; +import { UploadDataInput, UploadDataOutput } from '../../types'; import { createUploadTask } from '../../utils'; -import { StorageUploadDataRequest, UploadTask } from '../../../../types'; import { assertValidationError } from '../../../../errors/utils/assertValidationError'; import { StorageValidationErrorCode } from '../../../../errors/types/validation'; import { DEFAULT_PART_SIZE, MAX_OBJECT_SIZE } from '../../utils/constants'; @@ -19,9 +18,9 @@ import { getMultipartUploadHandlers } from './multipart'; * * Maximum object size is 5TB. * * Maximum object size if the size cannot be determined before upload is 50GB. * - * @param {StorageUploadDataRequest} uploadDataRequest The parameters that are passed to the + * @param {UploadDataInput} The input parameters that are passed to the * uploadData operation. - * @returns {UploadTask} Cancelable and Resumable task exposing result promise from `result` + * @returns {UploadDataOutput} Cancelable and Resumable task exposing result promise from `result` * property. * @throws service: {@link S3Exception} - thrown when checking for existence of the object * @throws validation: {@link StorageValidationErrorCode } - Validation errors. @@ -60,10 +59,8 @@ import { getMultipartUploadHandlers } from './multipart'; * await uploadTask.result; * ``` */ -export const uploadData = ( - uploadDataRequest: StorageUploadDataRequest -): UploadTask => { - const { data } = uploadDataRequest; +export const uploadData = (input: UploadDataInput): UploadDataOutput => { + const { data } = input; const dataByteLength = byteLength(data); assertValidationError( @@ -75,18 +72,14 @@ export const uploadData = ( const abortController = new AbortController(); return createUploadTask({ isMultipartUpload: false, - job: putObjectJob( - uploadDataRequest, - abortController.signal, - dataByteLength - ), + job: putObjectJob(input, abortController.signal, dataByteLength), onCancel: (abortErrorOverwrite?: Error) => { abortController.abort(abortErrorOverwrite); }, }); } else { const { multipartUploadJob, onPause, onResume, onCancel } = - getMultipartUploadHandlers(uploadDataRequest, dataByteLength); + getMultipartUploadHandlers(input, dataByteLength); return createUploadTask({ isMultipartUpload: true, job: multipartUploadJob, diff --git a/packages/storage/src/providers/s3/apis/uploadData/multipart/getDataChunker.ts b/packages/storage/src/providers/s3/apis/uploadData/multipart/getDataChunker.ts index bb91d363010..75e391cc4db 100644 --- a/packages/storage/src/providers/s3/apis/uploadData/multipart/getDataChunker.ts +++ b/packages/storage/src/providers/s3/apis/uploadData/multipart/getDataChunker.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { StorageUploadSourceOptions } from '../../../../../types'; +import { StorageUploadDataPayload } from '../../../../../types'; import { StorageValidationErrorCode, validationErrorMap, @@ -16,7 +16,7 @@ export type PartToUpload = { }; export const getDataChunker = ( - data: StorageUploadSourceOptions, + data: StorageUploadDataPayload, totalSize?: number ) => { const partSize = calculatePartSize(totalSize); diff --git a/packages/storage/src/providers/s3/apis/uploadData/multipart/initialUpload.ts b/packages/storage/src/providers/s3/apis/uploadData/multipart/initialUpload.ts index be62c10fcac..1c81e9efa1a 100644 --- a/packages/storage/src/providers/s3/apis/uploadData/multipart/initialUpload.ts +++ b/packages/storage/src/providers/s3/apis/uploadData/multipart/initialUpload.ts @@ -9,12 +9,12 @@ import { getUploadsCacheKey, } from './uploadCache'; import { ResolvedS3Config } from '../../../types/options'; -import { StorageUploadSourceOptions } from '../../../../../types'; +import { StorageUploadDataPayload } from '../../../../../types'; import { Part, createMultipartUpload } from '../../../utils/client'; type LoadOrCreateMultipartUploadOptions = { s3Config: ResolvedS3Config; - data: StorageUploadSourceOptions; + data: StorageUploadDataPayload; bucket: string; accessLevel: StorageAccessLevel; keyPrefix: string; diff --git a/packages/storage/src/providers/s3/apis/uploadData/multipart/uploadHandlers.ts b/packages/storage/src/providers/s3/apis/uploadData/multipart/uploadHandlers.ts index 708bc04b92e..62f01d1de1e 100644 --- a/packages/storage/src/providers/s3/apis/uploadData/multipart/uploadHandlers.ts +++ b/packages/storage/src/providers/s3/apis/uploadData/multipart/uploadHandlers.ts @@ -4,10 +4,9 @@ import { Amplify, StorageAccessLevel } from '@aws-amplify/core'; import { getDataChunker } from './getDataChunker'; -import { S3UploadOptions } from '../../../types'; +import { UploadDataInput } from '../../../types'; import { resolveS3ConfigAndInput } from '../../../utils'; -import { StorageUploadDataRequest } from '../../../../../types'; -import { S3Item } from '../../../types/results'; +import { Item as S3Item } from '../../../types/outputs'; import { DEFAULT_ACCESS_LEVEL, DEFAULT_QUEUE_SIZE, @@ -33,11 +32,7 @@ import { * @internal */ export const getMultipartUploadHandlers = ( - { - options: uploadDataOptions, - key, - data, - }: StorageUploadDataRequest, + { options: uploadDataOptions, key, data }: UploadDataInput, size?: number ) => { let resolveCallback: ((value: S3Item) => void) | undefined; diff --git a/packages/storage/src/providers/s3/apis/uploadData/putObjectJob.ts b/packages/storage/src/providers/s3/apis/uploadData/putObjectJob.ts index 081947e0daf..50922b1334b 100644 --- a/packages/storage/src/providers/s3/apis/uploadData/putObjectJob.ts +++ b/packages/storage/src/providers/s3/apis/uploadData/putObjectJob.ts @@ -3,10 +3,9 @@ import { Amplify } from '@aws-amplify/core'; -import { S3UploadOptions } from '../../types'; +import { UploadDataInput } from '../../types'; import { calculateContentMd5, resolveS3ConfigAndInput } from '../../utils'; -import { StorageUploadDataRequest } from '../../../../types'; -import { S3Item } from '../../types/results'; +import { Item as S3Item } from '../../types/outputs'; import { putObject } from '../../utils/client'; /** @@ -16,11 +15,7 @@ import { putObject } from '../../utils/client'; */ export const putObjectJob = ( - { - options: uploadDataOptions, - key, - data, - }: StorageUploadDataRequest, + { options: uploadDataOptions, key, data }: UploadDataInput, abortSignal: AbortSignal, totalLength?: number ) => diff --git a/packages/storage/src/providers/s3/index.ts b/packages/storage/src/providers/s3/index.ts index d4bf1fb7ae4..dd2f2eb015e 100644 --- a/packages/storage/src/providers/s3/index.ts +++ b/packages/storage/src/providers/s3/index.ts @@ -10,3 +10,25 @@ export { copy, getUrl, } from './apis'; + +export { + UploadDataInput, + DownloadDataInput, + RemoveInput, + ListAllInput, + ListPaginateInput, + GetPropertiesInput, + CopyInput, + GetUrlInput, +} from './types/inputs'; + +export { + UploadDataOutput, + DownloadDataOutput, + RemoveOutput, + ListAllOutput, + ListPaginateOutput, + GetPropertiesOutput, + CopyOutput, + GetUrlOutput, +} from './types/outputs'; diff --git a/packages/storage/src/providers/s3/types/index.ts b/packages/storage/src/providers/s3/types/index.ts index 9e2ecb86f1f..17c64167090 100644 --- a/packages/storage/src/providers/s3/types/index.ts +++ b/packages/storage/src/providers/s3/types/index.ts @@ -2,20 +2,33 @@ // SPDX-License-Identifier: Apache-2.0 export { - S3Options, - S3TransferOptions, - S3GetUrlOptions, - S3UploadOptions, + GetUrlOptions, + UploadDataOptions, + GetPropertiesOptions, + ListAllOptions, + ListPaginateOptions, + RemoveOptions, + DownloadDataOptions, } from './options'; export { - S3DownloadDataResult, - S3DownloadFileResult, - S3GetUrlResult, - S3UploadDataResult, - S3ListOutputItem, - S3ListAllResult, - S3ListPaginateResult, - S3GetPropertiesResult, - S3CopyResult, -} from './results'; + DownloadDataOutput, + GetUrlOutput, + UploadDataOutput, + ListOutputItem, + ListAllOutput, + ListPaginateOutput, + GetPropertiesOutput, + CopyOutput, + RemoveOutput, +} from './outputs'; +export { + CopyInput, + GetPropertiesInput, + GetUrlInput, + ListAllInput, + ListPaginateInput, + RemoveInput, + DownloadDataInput, + UploadDataInput, +} from './inputs'; export { S3Exception } from './errors'; diff --git a/packages/storage/src/providers/s3/types/inputs.ts b/packages/storage/src/providers/s3/types/inputs.ts new file mode 100644 index 00000000000..3640d6cd7ca --- /dev/null +++ b/packages/storage/src/providers/s3/types/inputs.ts @@ -0,0 +1,38 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { + StorageCopyInput, + StorageGetPropertiesInput, + StorageGetUrlInput, + StorageListInput, + StorageRemoveInput, + StorageDownloadDataInput, + StorageUploadDataInput, +} from '../../../types'; +import { + GetPropertiesOptions, + GetUrlOptions, + ListAllOptions, + ListPaginateOptions, + RemoveOptions, + DownloadDataOptions, + UploadDataOptions, +} from '../types'; + +export type CopyInput = StorageCopyInput; + +export type GetPropertiesInput = + StorageGetPropertiesInput; + +export type GetUrlInput = StorageGetUrlInput; + +export type ListAllInput = StorageListInput; + +export type ListPaginateInput = StorageListInput; + +export type RemoveInput = StorageRemoveInput; + +export type DownloadDataInput = StorageDownloadDataInput; + +export type UploadDataInput = StorageUploadDataInput; diff --git a/packages/storage/src/providers/s3/types/options.ts b/packages/storage/src/providers/s3/types/options.ts index 94bd3241fa2..b2bd747e1e9 100644 --- a/packages/storage/src/providers/s3/types/options.ts +++ b/packages/storage/src/providers/s3/types/options.ts @@ -5,12 +5,16 @@ import { Credentials } from '@aws-sdk/types'; import { TransferProgressEvent } from '../../../types'; -import { StorageOptions } from '../../../types/options'; +import { + StorageOptions, + StorageListAllOptions, + StorageListPaginateOptions, +} from '../../../types/options'; /** - * Request options type for S3 Storage operations. + * Input options type for S3 Storage operations. */ -export type S3Options = StorageOptions & { +export type Options = StorageOptions & { /** * Whether to use accelerate endpoint. * @default false @@ -19,16 +23,34 @@ export type S3Options = StorageOptions & { }; /** - * Request options type for S3 downloadData, uploadData APIs. + * Input options type for S3 getProperties API. */ -export type S3TransferOptions = S3Options & { - /** - * Callback function tracking the upload/download progress. - */ - onProgress?: (event: TransferProgressEvent) => void; -}; +export type GetPropertiesOptions = Options; + +/** + * Input options type for S3 getProperties API. + */ +export type RemoveOptions = Options; + +/** + * Input options type for S3 list API. + */ +export type ListAllOptions = StorageListAllOptions; + +/** + * Input options type for S3 list API. + */ +export type ListPaginateOptions = StorageListPaginateOptions; + +/** + * Input options type for S3 downloadData API. + */ +export type DownloadDataOptions = TransferOptions; -export type S3GetUrlOptions = S3Options & { +/** + * Input options type for S3 getUrl API. + */ +export type GetUrlOptions = Options & { /** * Whether to head object to make sure the object existence before downloading. * @default false @@ -41,7 +63,7 @@ export type S3GetUrlOptions = S3Options & { expiresIn?: number; }; -export type S3UploadOptions = Omit & { +export type UploadDataOptions = Omit & { /** * The default content-disposition header value of the file when downloading it. * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition @@ -76,3 +98,12 @@ export type ResolvedS3Config = { forcePathStyle?: boolean; useAccelerateEndpoint?: boolean; }; +/** + * Input options type for S3 downloadData, uploadData APIs. + */ +type TransferOptions = Options & { + /** + * Callback function tracking the upload/download progress. + */ + onProgress?: (event: TransferProgressEvent) => void; +}; diff --git a/packages/storage/src/providers/s3/types/outputs.ts b/packages/storage/src/providers/s3/types/outputs.ts new file mode 100644 index 00000000000..1c149a9213a --- /dev/null +++ b/packages/storage/src/providers/s3/types/outputs.ts @@ -0,0 +1,42 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { + StorageDownloadDataOutput, + StorageGetUrlOutput, + StorageItem, + StorageListOutput, + DownloadTask, + UploadTask, +} from '../../../types'; + +export interface Item extends StorageItem { + /** + * VersionId used to reference a specific version of the object. + */ + versionId?: string; + /** + * A standard MIME type describing the format of the object data. + */ + contentType?: string; +} + +export type DownloadDataOutput = DownloadTask>; + +export type GetUrlOutput = StorageGetUrlOutput; + +export type UploadDataOutput = UploadTask; + +export type GetPropertiesOutput = Item; + +export type ListOutputItem = Omit; + +export type ListAllOutput = StorageListOutput; + +export type ListPaginateOutput = StorageListOutput & { + nextToken?: string; +}; + +// TODO: expose more properties if required +export type CopyOutput = Pick; +export type RemoveOutput = Pick; diff --git a/packages/storage/src/providers/s3/types/results.ts b/packages/storage/src/providers/s3/types/results.ts deleted file mode 100644 index 0e187e733f0..00000000000 --- a/packages/storage/src/providers/s3/types/results.ts +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { - StorageDownloadDataResult, - StorageGetUrlResult, - StorageItem, - StorageUploadResult, - StorageListResult, -} from '../../../types'; - -export interface S3Item extends StorageItem { - /** - * VersionId used to reference a specific version of the object. - */ - versionId?: string; - /** - * A standard MIME type describing the format of the object data. - */ - contentType?: string; -} - -export type S3DownloadDataResult = StorageDownloadDataResult; - -export type S3DownloadFileResult = S3Item; - -export type S3GetUrlResult = StorageGetUrlResult; - -export type S3UploadDataResult = S3Item; - -export type S3GetPropertiesResult = S3Item; - -export type S3ListOutputItem = S3Item; - -export type S3ListAllResult = StorageListResult; - -export type S3ListPaginateResult = StorageListResult & { - nextToken?: string; -}; - -// TODO: expose more properties if required -export type S3CopyResult = Required>; diff --git a/packages/storage/src/types/index.ts b/packages/storage/src/types/index.ts index 31e0446d596..c85523a9c77 100644 --- a/packages/storage/src/types/index.ts +++ b/packages/storage/src/types/index.ts @@ -1,14 +1,23 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -export { DownloadTask, TransferProgressEvent, UploadTask } from './common'; export { - StorageListRequest, - StorageOperationRequest, - StorageDownloadDataRequest, - StorageUploadDataRequest, - CopyRequest, -} from './requests'; + DownloadTask, + TransferProgressEvent, + TransferTaskState, + UploadTask, +} from './common'; +export { + StorageOperationInput, + StorageListInput, + StorageGetPropertiesInput, + StorageRemoveInput, + StorageDownloadDataInput, + StorageUploadDataInput, + StorageCopyInput, + StorageGetUrlInput, + StorageUploadDataPayload, +} from './inputs'; export { StorageOptions, StorageRemoveOptions, @@ -16,13 +25,11 @@ export { StorageListPaginateOptions, StorageCopySourceOptions, StorageCopyDestinationOptions, - StorageUploadSourceOptions, } from './options'; export { StorageItem, - StorageListResult, - StorageDownloadDataResult, - StorageGetUrlResult, - StorageUploadResult, - StorageRemoveResult, -} from './results'; + StorageListOutput, + StorageDownloadDataOutput, + StorageGetUrlOutput, + StorageUploadOutput, +} from './outputs'; diff --git a/packages/storage/src/types/inputs.ts b/packages/storage/src/types/inputs.ts new file mode 100644 index 00000000000..61ed132335b --- /dev/null +++ b/packages/storage/src/types/inputs.ts @@ -0,0 +1,49 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { + StorageOptions, + StorageListAllOptions, + StorageListPaginateOptions, + StorageCopySourceOptions, + StorageCopyDestinationOptions, +} from './options'; + +export type StorageOperationInput = { + key: string; + options?: Options; +}; + +export type StorageGetPropertiesInput = + StorageOperationInput; + +export type StorageRemoveInput = + StorageOperationInput; + +export type StorageListInput< + Options extends StorageListAllOptions | StorageListPaginateOptions +> = { + prefix?: string; + options?: Options; +}; + +export type StorageGetUrlInput = + StorageOperationInput; + +export type StorageDownloadDataInput = + StorageOperationInput; + +export type StorageUploadDataInput = + StorageOperationInput & { + data: StorageUploadDataPayload; + }; + +export type StorageCopyInput = { + source: StorageCopySourceOptions; + destination: StorageCopyDestinationOptions; +}; + +/** + * The data payload type for upload operation. + */ +export type StorageUploadDataPayload = Blob | BufferSource | string | File; diff --git a/packages/storage/src/types/options.ts b/packages/storage/src/types/options.ts index e0d79933a62..3a95ffa764d 100644 --- a/packages/storage/src/types/options.ts +++ b/packages/storage/src/types/options.ts @@ -10,11 +10,6 @@ export type StorageOptions = targetIdentityId?: string; }; -/** - * The data payload type for upload operation. - */ -export type StorageUploadSourceOptions = Blob | BufferSource | string | File; - export type StorageListAllOptions = StorageOptions & { listAll: true; }; diff --git a/packages/storage/src/types/results.ts b/packages/storage/src/types/outputs.ts similarity index 77% rename from packages/storage/src/types/results.ts rename to packages/storage/src/types/outputs.ts index fb35ccde051..766acf95365 100644 --- a/packages/storage/src/types/results.ts +++ b/packages/storage/src/types/outputs.ts @@ -7,7 +7,7 @@ export type StorageItem = { /** * Key of the object */ - key?: string; + key: string; /** * Creation date of the object. */ @@ -28,11 +28,11 @@ export type StorageItem = { metadata?: Record; }; -export type StorageDownloadDataResult = T & { +export type StorageDownloadDataOutput = T & { body: ResponseBodyMixin; }; -export type StorageGetUrlResult = { +export type StorageGetUrlOutput = { /** * presigned URL of the given object. */ @@ -43,12 +43,8 @@ export type StorageGetUrlResult = { expiresAt: Date; }; -export type StorageUploadResult = Item; - -export type StorageRemoveResult = { - key: string; -}; +export type StorageUploadOutput = Item; -export type StorageListResult = { +export type StorageListOutput = { items: Item[]; }; diff --git a/packages/storage/src/types/requests.ts b/packages/storage/src/types/requests.ts deleted file mode 100644 index 2b8f59db3b1..00000000000 --- a/packages/storage/src/types/requests.ts +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { - StorageOptions, - StorageUploadSourceOptions, - StorageListAllOptions, - StorageListPaginateOptions, - StorageCopySourceOptions, - StorageCopyDestinationOptions, -} from './options'; - -export type StorageOperationRequest = { - key: string; - options?: Options; -}; - -export type StorageListRequest< - Options extends StorageListAllOptions | StorageListPaginateOptions -> = { - prefix?: string; - options?: Options; -}; - -export type StorageDownloadDataRequest = - StorageOperationRequest; - -export type StorageUploadDataRequest = - StorageOperationRequest & { - data: StorageUploadSourceOptions; - }; - -export type CopyRequest = { - source: StorageCopySourceOptions; - destination: StorageCopyDestinationOptions; -}; diff --git a/yarn.lock b/yarn.lock index e611acf906c..d49797e75e2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2782,14 +2782,6 @@ resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== -"@types/node-fetch@2.6.4": - version "2.6.4" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.4.tgz#1bc3a26de814f6bf466b25aeb1473fa1afe6a660" - integrity sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg== - dependencies: - "@types/node" "*" - form-data "^3.0.0" - "@types/node@*", "@types/node@^20.3.1": version "20.6.0" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.6.0.tgz#9d7daa855d33d4efec8aea88cd66db1c2f0ebe16" @@ -5726,15 +5718,6 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== -form-data@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" - integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - form-data@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" @@ -7111,14 +7094,6 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== -isomorphic-unfetch@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz#87341d5f4f7b63843d468438128cb087b7c3e98f" - integrity sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q== - dependencies: - node-fetch "^2.6.1" - unfetch "^4.2.0" - isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -12479,11 +12454,6 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" -unfetch@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be" - integrity sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA== - unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc"