Skip to content

Commit

Permalink
chore: Sync next/main with next/release (#12044)
Browse files Browse the repository at this point in the history
  • Loading branch information
jimblanc authored Sep 14, 2023
2 parents c39db56 + 6749da1 commit 448963f
Show file tree
Hide file tree
Showing 102 changed files with 1,400 additions and 1,423 deletions.
44 changes: 32 additions & 12 deletions packages/adapter-nextjs/__tests__/utils/getAmplifyConfig.test.ts
Original file line number Diff line number Diff line change
@@ -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);
});
});
10 changes: 10 additions & 0 deletions packages/adapter-nextjs/__tests__/withAmplify.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ describe('withAmplify', () => {
env: {
amplifyConfig: JSON.stringify(mockAmplifyConfig),
},
serverRuntimeConfig: {
amplifyConfig: JSON.stringify(mockAmplifyConfig),
},
});
});

Expand All @@ -37,6 +40,9 @@ describe('withAmplify', () => {
env: {
existingKey: '123',
},
serverRuntimeConfig: {
myKey: 'myValue',
},
};
const result = withAmplify(nextConfig, mockAmplifyConfig);

Expand All @@ -45,6 +51,10 @@ describe('withAmplify', () => {
existingKey: '123',
amplifyConfig: JSON.stringify(mockAmplifyConfig),
},
serverRuntimeConfig: {
myKey: 'myValue',
amplifyConfig: JSON.stringify(mockAmplifyConfig),
},
});
});
});
43 changes: 43 additions & 0 deletions packages/adapter-nextjs/src/runWithAmplifyServerContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions packages/adapter-nextjs/src/types/NextServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<OperationResult> {
nextServerContext: Context | null;
operation: (
Expand Down
12 changes: 11 additions & 1 deletion packages/adapter-nextjs/src/utils/getAmplifyConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.<key> 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({
Expand Down
8 changes: 7 additions & 1 deletion packages/adapter-nextjs/src/withAmplify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
8 changes: 7 additions & 1 deletion packages/adapter-nextjs/tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
7 changes: 6 additions & 1 deletion packages/analytics/src/index.ts
Original file line number Diff line number Diff line change
@@ -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';
47 changes: 41 additions & 6 deletions packages/analytics/src/providers/pinpoint/apis/identifyUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> => {
}: IdentifyUserInput): Promise<void> => {
const { credentials, identityId } = await resolveCredentials();
const { appId, region } = resolveConfig();
updateEndpoint({
Expand Down
39 changes: 32 additions & 7 deletions packages/analytics/src/providers/pinpoint/apis/record.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
9 changes: 8 additions & 1 deletion packages/analytics/src/providers/pinpoint/index.ts
Original file line number Diff line number Diff line change
@@ -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';
5 changes: 4 additions & 1 deletion packages/analytics/src/providers/pinpoint/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

export { UpdateEndpointException } from './errors';
export { IdentifyUserParameters } from './parameters';
export {
RecordInput,
IdentifyUserInput
} from './inputs';
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down
Loading

0 comments on commit 448963f

Please sign in to comment.