Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(clients): use custom cognito-identity and pinpoint clients #11427

Merged
merged 48 commits into from
Jun 2, 2023

Conversation

AllanZhengYP
Copy link
Member

@AllanZhengYP AllanZhengYP commented May 29, 2023

Description of changes

Custom clients for Cognito Identity and Pinpoint to reduce the library bundle size. No interface change or compatibility change.

Bundle size reduction from custom clients:

Category Current size new size
analytics 56.5 kB 29.7 kB
api-graphql 104.5 kB 86.1 kB
api-rest 47.5 kB 28.7 kB
api 106 kB 87.4 kB
auth 73 kB 53.3 kB
datastore 155 kB 135.5 kB
geo 65 kB 50.2 kB
interactions 87 kB 74 kB
notifications 71 kB 29 kB
pubsub(IoT) 97.5 kB 78.5 kB
pubsub(Mqtt) 97.5 kB 78.4 kB

Description of how you validated changes

Unit test; Integrations test; Amplify UI canary; Amplify Studio build

Checklist

  • PR description included
  • yarn test passes
  • Tests are changed or added
  • Relevant documentation is changed or added (and PR referenced)

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

AllanZhengYP and others added 30 commits March 22, 2023 14:01
…eware (#11188)

* feat(clients): middleware interface and retry middleware

* test(clients): retry middleare unit test

* fix(clients): middleware type to include options

* feat(clients): add retry middleware unit test and update interface

* chore(clients): update bundle size limit for fetch and retry

* chore(clients): add retry docs; fix format

* fix(clients): address feedbacks
* fix(retry): add metadata to returns from retry middldeware; fix minor bugs

* feat(clients): address retry middleware feedbacks

Co-authored-by: Chris F <5827964+cshfang@users.noreply.github.com>
* feat(clients): implement service API composer

* chore(clients): rename middleware interface

* feat(clients): middleware handler interface can be non request/response interface

* chore(clients): move fetch handler to handlers folder

* feat(clients): implement user agent middleware

* feat(clients): implement unauth aws transfer handler

* feat(clients): implement api handler composer

* feat(clients): implement cognito-identity client

* chore(clients): update bundle size limit

* feat(clients): add serde utils; remove retry in api handler composer

* feat(clients): integrate cognito-identity client to Credentials class

* fix(clients): make retryDecider interface async

* chore: move cognito client to dev dep

* chore(clients): use Pascale path name

* fix(clients): handle fetch response.body undefined in RN

* chore: publish under v5-custom-clients dist-tag

* fix(clients): read body once for errors
* test(clients): add cognito-identity functional test

* chore(clients): prefer destructuring parameters at top
* feat(clients): Add custom signature v4 signer

* Updated docstrings

* Extracted common code

* Use == null instead of isNil

* Add unit tests

* Add missing licensing headers

* Use test case options in presign tests

* Fixed comment

* Add test for data hashing with SourceData keys

* Remove buffer dependency

* Remove internal sdk dependency
* feat(clients): Add signing middleware

* Use closure for clock offset

* Add bundle size test entry

* Address comments
…1311)

* feat(clients): support CN partition by adding DNS suffix resolver

* chore(clients): update bundle size test limit

* fix(clients): address feedbacks

* chore: update bundle size limit
* feat(clients) Add updateEndpoint API

* Rename handler
* feat(clients) Add putEvents API

* Add additional verification for expected date format

* Mark API as internal
* feat(clients) Add getInAppMessages API

* Update unit test
Co-authored-by: Allan Zheng <zheallan@amazon.com>
@AllanZhengYP AllanZhengYP requested review from a team as code owners May 31, 2023 18:20
.circleci/config.yml Outdated Show resolved Hide resolved
Copy link
Member

@stocaaro stocaaro left a comment

Choose a reason for hiding this comment

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

I'm only half way through, submitting comments now and will return to this in a couple hours.

package.json Outdated Show resolved Hide resolved
Jshhhh
Jshhhh previously approved these changes Jun 1, 2023
stocaaro
stocaaro previously approved these changes Jun 1, 2023
Copy link
Member

@stocaaro stocaaro left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Member

@svidgen svidgen left a comment

Choose a reason for hiding this comment

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

Lots of comments for you! Feel free to take most as signaling my preferences for future PR's. And on that note, let's try strategize future PR's to be more focused, please.

Four things I'd want touched on in this PR before approving:

  1. The potential race in the response stream/body reader/cache promise.
  2. Confirmation that error cases are tested to the extent they need to be.
  3. A link to the encodeURIComponent RFC gapfill.
  4. I had a comment somewhere in here about something that sounds like a latent bomb. Can't remember where. But, something there to settle my nerves, please!

In general, a few things were initially hard to follow. But, nothing that should block the PR. And, it seems like some of the harder-to-follow bits are in support of some pretty easy-to-read client/"service api" generation. So, net good, IMO. Good work!

I think most of my comments are about half-baked docstrings. If you have time, I'd love to see followup PR (at least eventually) with meaningful docstrings. It looks like you started in some areas and produced some really helpful docs and maybe just lost steam.

And my disclosure: I'm not an expert on the auth exchanges, and I didn't look to closely at the pinpoint stuff. I assume others are reviewing these in depth.

packages/core/src/clients/handlers/fetch.ts Outdated Show resolved Hide resolved
Comment on lines 63 to 69
let cached: T;
return async () => {
if (!cached) {
cached = await payloadAccessor();
}
return cached;
};
Copy link
Member

Choose a reason for hiding this comment

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

This formulation contains an inherent race. If this is invoked repeatedly in rapid succession, multiple calls to payloadAccessor() can be spawned concurrently. The race can be eliminated by storing and branching on the source promise directly instead of branching on the awaited result.

Suggested change
let cached: T;
return async () => {
if (!cached) {
cached = await payloadAccessor();
}
return cached;
};
let cached: Promise<T>;
return async () => {
if (!cached) {
// Explicitly not awaiting. Intermediate await would add overhead and
// introduce a possible race in the event that this wrapper is called
// again before the first `payloadAccessor` call resolves.
cached = payloadAccessor();
}
return cached;
};

I've tried to demo what I mean in this playground.

The inherent race might be mitigated due to calls being internal and fairly well-controlled. But, I don't have my head around this all yet to be confident. So, at the moment, I'd be more comfortable if we could eliminate the possibility.

Copy link
Member Author

Choose a reason for hiding this comment

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

This race will only happen if the withPayloadCaching is called concurrently. However payload accessor is only called once per http response

Comment on lines +32 to +35
const responseHeaders = {};
resp.headers?.forEach((value, key) => {
responseHeaders[key.toLowerCase()] = value;
});
Copy link
Member

Choose a reason for hiding this comment

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

I didn't expect to see this here. That doesn't mean it's wrong, but perhaps it warrants a comment. Why is lower-casing header keys a responsibility of this handler?

export const jitteredBackoff: RetryOptions['computeDelay'] = attempt => {
const delayFunction = jitteredBackoffUtil();
const delay = delayFunction(attempt);
return delay === false ? MAX_DELAY_MS : delay;
Copy link
Member

Choose a reason for hiding this comment

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

Is delay ever > MAX_DELAY_MS?

maxAttempts = DEFAULT_RETRY_ATTEMPTS,
retryDecider,
computeDelay,
abortSignal,
Copy link
Member

Choose a reason for hiding this comment

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

Interesting ... I must have read right past this before or it didn't "click" for me. But, having this here means that we build the middleware for each request, correct?

Not a blocker. But, that seems odd to me.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, the composing of pure functional middleware happens for each request. It's the same for AWS SDK too.

Comment on lines +19 to +20
serializer: (input: Input, endpoint: Endpoint) => HttpRequest,
deserializer: (output: HttpResponse) => Promise<Output>,
Copy link
Member

Choose a reason for hiding this comment

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

FWIW: Here and throughout, I find it a little odd that these functions are nouns instead of verbs.

elorzafe
elorzafe previously approved these changes Jun 1, 2023
Copy link
Contributor

@elorzafe elorzafe left a comment

Choose a reason for hiding this comment

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

This is awesome!!

Thanks for the hard work and putting this out.
I have some nit comments, but feel free to update later.

packages/amazon-cognito-identity-js/CHANGELOG.md Outdated Show resolved Hide resolved
.github/workflows/issue-pending-response.yml Outdated Show resolved Hide resolved
.github/workflows/snyk-security.yml Outdated Show resolved Hide resolved
packages/amazon-cognito-identity-js/README.md Outdated Show resolved Hide resolved
packages/amazon-cognito-identity-js/index.d.ts Outdated Show resolved Hide resolved
packages/core/src/Credentials.ts Outdated Show resolved Hide resolved
let credentials = undefined;
if (identity_id) {
const cognitoIdentityParams: FromCognitoIdentityParameters = {
const credentialsProvider = async () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: the name could be authenticatedCredentialsProvider

IdentityPoolId: identityPoolId,
})
);
const credentialsProvider = async () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: I would name it guestCredentialsProvider

semi-nit: This code is very similar to a code below

* The endpoint resolver function that returns the endpoint URL for a given region.
*/
const endpointResolver = ({ region }: EndpointResolverOptions) => ({
url: new URL(`https://cognito-identity.${region}.${getDnsSuffix(region)}`),
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: reuse SERVICE_NAME to save a couple of bytes

Copy link
Contributor

@elorzafe elorzafe left a comment

Choose a reason for hiding this comment

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

Great work

Copy link
Contributor

@wlee221 wlee221 left a comment

Choose a reason for hiding this comment

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

Approving for the devops portion (which got removed anyway 😛)

Copy link
Member

@svidgen svidgen left a comment

Choose a reason for hiding this comment

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

Thanks for the rev!

Comment on lines 8 to 16
const MAX_DELAY_MS = 5 * 60 * 1000;
const DEFAULT_MAX_DELAY_MS = 5 * 60 * 1000;

export const jitteredBackoff: RetryOptions['computeDelay'] = attempt => {
const delayFunction = jitteredBackoffUtil();
const delayFunction = jitteredBackoffUtil(DEFAULT_MAX_DELAY_MS);
const delay = delayFunction(attempt);
return delay === false ? MAX_DELAY_MS : delay;
// The delayFunction returns false when the delay is greater than the max delay(5 mins).
// In this case, the retry middleware will delay 5 mins instead, as a ceiling of the delay.
return delay === false ? DEFAULT_MAX_DELAY_MS : delay;
};
Copy link
Member

Choose a reason for hiding this comment

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

It's a bit odd having both the delay function and this function doing conditional things around the DEFAULT_MAX_DELAY_MS. But, I think I can see how it makes sense. And, the change addresses the larger concern I had, so I appreciate it. Thanks!

// SPDX-License-Identifier: Apache-2.0

/**
* Wraps encodeURIComponent to encode additional characters to fully adhere to RFC 3986.
Copy link
Member

Choose a reason for hiding this comment

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

Not the kind of reference I was hoping for. But, it does demonstrates that this copies another established library that does AWS stuff. So, I'll take it! Thanks!

@@ -3,16 +3,16 @@

import { getSkewCorrectedDate } from './getSkewCorrectedDate';

const SKEW_WINDOW = 5 * 60 * 1000; // 5 minutes
// 5 mins in milliseconds. Ref: https://github.com/aws/aws-sdk-js-v3/blob/6c0f44fab30a1bb2134af47362a31332abc3666b/packages/middleware-signing/src/utils/isClockSkewed.ts#L10
Copy link
Member

Choose a reason for hiding this comment

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

Not quite the explanation I thought we'd have, but it works for me!

Copy link
Member

@svidgen svidgen left a comment

Choose a reason for hiding this comment

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

Thanks for the rev!

@AllanZhengYP AllanZhengYP merged commit 2d934a3 into main Jun 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants