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(pubsub): Typescript improvements and cleanup #10954

Merged
merged 21 commits into from
Mar 1, 2023

Conversation

stocaaro
Copy link
Member

@stocaaro stocaaro commented Feb 9, 2023

Description of changes

Changes made:

  • Use typescript-coverage-report output to identify and fix ts issues
    • TS coverage is now 94.83% and gated at regression below 93%
  • Re-add coverage protection for the build and update to omit paho.js
  • Removed the unused code in AWSAppSyncProvider.ts
  • Removed paho as a dependency (we're using the vendor lib version right now).

TS Coverage:

  • Before ~88% (ignoring Paho), shows 79.9% with Paho counted
  • After 94.83%

Description of how you validated changes

  • Fixed unit testing issues
  • Manually tested against IoT/API/Datastore

Checklist

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

@stocaaro stocaaro requested review from a team as code owners February 9, 2023 01:23
@@ -440,7 +448,7 @@ describe('PubSub', () => {
provider: 'MqttOverWSProvider',
});

expect(mqttOverWSProvider.isSSLEnabled).toBe(false);
expect(mqttOverWSProvider['isSSLEnabled']).toBe(false);
Copy link
Member Author

Choose a reason for hiding this comment

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

Change because it is accessing a protected getter

return !this.options
.aws_appsync_dangerously_connect_to_http_endpoint_for_testing;
return !this.options[
'aws_appsync_dangerously_connect_to_http_endpoint_for_testing'
Copy link
Member Author

Choose a reason for hiding this comment

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

Changing access pattern since we opted not to have this member explicitly on the options type.

This comment was marked as duplicate.

Copy link
Member Author

Choose a reason for hiding this comment

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

I made a run at rewriting the ProviderOptions interface as a Record type. Turned into cascading problems that couldn't be teased out in the 20 minutes I gave it. I'm sure it can be done, but since this is a key customer facing interface, I would prefer to leave it or wrap it in more testing before retooling as a type.

In this case, we have added all other variables as fields on the interface. We opted not to add this one as the name isn't something we want folks autocompleting to / seeing in the public surface. Which is why TS prefers not to directly access it.

Copy link
Contributor

Choose a reason for hiding this comment

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

This is used also for mock use case IIRC. That is a CLI feature that allows you to test the API without doing amplify push

I think it makes sense to not expose that unless is generated automatically by CLI

Comment on lines 388 to +389
logger.debug({ err });
const message = err['message'] ?? '';
const message = String(err.message ?? '');
Copy link
Member Author

Choose a reason for hiding this comment

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

Errors are a pain in the current version of ts. Newer versions default to unknown and for narrowing. Currently catches will be any which makes coverage reporting unhappy. Will look at upgrading

aws_pubsub_endpoint?: string;
}

interface PahoClient {
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 is sort of like inlining a d.ts file for Paho.Client so that we aren't interacting with it as any.

This comment was marked as resolved.

Copy link
Member Author

Choose a reason for hiding this comment

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

Excellent call. Pulling it in.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes I would use that

@codecov-commenter
Copy link

codecov-commenter commented Feb 9, 2023

Codecov Report

Merging #10954 (035cdb0) into main (91e0c8f) will increase coverage by 0.31%.
The diff coverage is 90.00%.

📣 This organization is not using Codecov’s GitHub App Integration. We recommend you install it so Codecov can continue to function properly for your repositories. Learn more

@@            Coverage Diff             @@
##             main   #10954      +/-   ##
==========================================
+ Coverage   81.90%   82.21%   +0.31%     
==========================================
  Files         194      193       -1     
  Lines       19376    19295      -81     
  Branches     4179     4165      -14     
==========================================
- Hits        15870    15864       -6     
+ Misses       3217     3143      -74     
+ Partials      289      288       -1     
Impacted Files Coverage Δ
packages/core/src/constants.ts 100.00% <ø> (ø)
packages/core/src/index.ts 100.00% <ø> (ø)
packages/pubsub/src/Providers/index.ts 100.00% <ø> (ø)
packages/pubsub/src/index.ts 100.00% <ø> (ø)
packages/pubsub/src/types/PubSub.ts 100.00% <ø> (ø)
...ackages/pubsub/src/utils/ConnectionStateMonitor.ts 100.00% <ø> (ø)
packages/pubsub/src/PubSub.ts 91.17% <75.00%> (-1.25%) ⬇️
...ackages/pubsub/src/Providers/MqttOverWSProvider.ts 92.22% <90.32%> (+0.69%) ⬆️
.../src/Providers/AWSAppSyncRealTimeProvider/index.ts 95.68% <91.66%> (-0.39%) ⬇️
packages/pubsub/src/Providers/AWSIotProvider.ts 100.00% <100.00%> (ø)
... and 2 more

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

Copy link
Member

@cshfang cshfang left a comment

Choose a reason for hiding this comment

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

I left some comments. There's nothing blocking but this is a Typescript improvement PR after all so I was being somewhat nit-picky. Some of it probably just stems from my unfamiliarity with pubsub so thank you for bearing with me.

Comment on lines 174 to 176
_topics: string[] | string,
_msg: object,
_options?: AWSAppSyncRealTimeProviderOptions
Copy link
Member

Choose a reason for hiding this comment

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

This isn't from this PR but why the underscores? 🤔

Can _msg be typed as a Record<K, V>?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure if its idiomatic in JS/TS, but in other languages it is used to denote variables that are not used. In this case, I read it as, we take the standard inputs, but then no matter what raise, so these variables don't matter.

Copy link
Member

Choose a reason for hiding this comment

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

Ah. The pattern I see most commonly would be simply _, __ without naming the unused var. I find this _foo pattern confusing especially since we also use this convention to denote private vars/functions in classes.

Having said that, this might be another candidate for audit/align/cleanup for the team. We could consider turning on this lint rule: https://eslint.org/docs/latest/rules/no-unused-vars and defining a regex pattern for ignoring unused vars. Def not a blocker for this PR though

return !this.options
.aws_appsync_dangerously_connect_to_http_endpoint_for_testing;
return !this.options[
'aws_appsync_dangerously_connect_to_http_endpoint_for_testing'

This comment was marked as duplicate.

@@ -15,8 +15,8 @@ export class AWSIoTProvider extends MqttOverWSProvider {
super(options);
}

protected get region() {
return this.options.aws_pubsub_region;
protected get region(): string | undefined {
Copy link
Member

Choose a reason for hiding this comment

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

This is a good example of when we would want undefined for sure

aws_pubsub_endpoint?: string;
}

interface PahoClient {

This comment was marked as resolved.

}

protected get endpoint() {
protected get endpoint(): string | undefined | Promise<string> {
Copy link
Member

Choose a reason for hiding this comment

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

In what use case would this be a promise? This isn't a public API but it seems like if it's sometimes a Promise, it'd have to be awaited so why not just make it always a Promise?

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 function is overridden in the IoT Provider, which returns a Promise<string>

This comment was marked as outdated.

@stocaaro stocaaro requested a review from cshfang February 10, 2023 22:51
cshfang
cshfang previously approved these changes Feb 13, 2023
Copy link
Member

@cshfang cshfang left a comment

Choose a reason for hiding this comment

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

Have a couple lingering comments but I don't think they're blocking

@@ -69,11 +73,29 @@ const customDomainPath = '/realtime';

type GraphqlAuthModes = keyof typeof GRAPHQL_AUTH_MODE;

type DataObject = {
Copy link
Member

Choose a reason for hiding this comment

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

🤩

}

protected get endpoint() {
protected get endpoint(): string | undefined | Promise<string> {

This comment was marked as outdated.

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.

Thanks @stocaaro take a look on the comments we can chat more offline if you want

@@ -1,11 +1,12 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import * as Paho from '../vendor/paho-mqtt';
import { Client as PahoClient } from 'paho-mqtt';
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this is a type only thing, should use the import from the type package?

Copy link
Member Author

Choose a reason for hiding this comment

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

Correct. Types only. Dev dependency. I originally had inlined this interface and adopted this package approach for typing paho at Jim and Chris's recommendations

Copy link
Contributor

Choose a reason for hiding this comment

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

Cool can we do import type to make it explicit, I got a little scared about that import statement

@@ -5,7 +5,6 @@ export { PubSub } from './PubSub';
export { CONNECTION_STATE_CHANGE } from './Providers/constants';
export { ConnectionState, CONTROL_MSG } from './types';
export {
AWSAppSyncProvider,
Copy link
Contributor

Choose a reason for hiding this comment

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

This might be consider a breaking change :/

publish(topics: string[] | string, msg: any, options?: ProviderOptions): void;
publish(
topics: string[] | string,
msg: Record<string, unknown> | string,
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks good!

Hub.dispatch('api', { event, data, message }, 'PubSub', AMPLIFY_SYMBOL);
};

export type ObserverQuery = {
observer: ZenObservable.SubscriptionObserver<any>;
query: string;
variables: object;
variables: Record<string, unknown>;
Copy link
Contributor

Choose a reason for hiding this comment

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

I would double check this with Data team

export interface AWSAppSyncRealTimeProviderOptions extends ProviderOptions {
appSyncGraphqlEndpoint?: string;
authenticationType?: GraphqlAuthModes;
query?: string;
variables?: object;
variables?: Record<string, unknown>;
Copy link
Contributor

Choose a reason for hiding this comment

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

I would check this with Data team

Copy link
Member Author

Choose a reason for hiding this comment

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

Agreed that this needs review from the data team.

Copy link
Member Author

Choose a reason for hiding this comment

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

Regarding data team review, I've been watching the api-graphql category for issues. The subscription interface is translated to Observable on output with strongly typed inputs, so I don't expect this set of type changes to cause any impact to api/datastore users.


subscribe(
topics: string[] | string,
options?: ProviderOptions
): Observable<any>;
): Observable<Record<string, unknown>>;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this always the case, can be returned something different? like a string?

Copy link
Member Author

Choose a reason for hiding this comment

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

In all cases, the observable emits objects of one type or another. I had originally normalized these to object which Chris had me refactor as Record<string, unknown> as this is more idiomatic with TS since it will encourage the user to clarify the unknown values received back.

error: error => observer.error({ provider, error }),
next: (value: Record<string, unknown>) =>
observer.next({ provider, value }),
error: (error: Record<string, unknown>) =>
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

You're right. Also, several of these comments circle around the same topic. I find myself convinced that:

  • next values can be strings as well as Records/objects
  • The category subscription returns {provider: x, value: y} instead of the string/Record itself, so that can be more clearly typed.
  • The publish and subscribe value types should be represented by a shared type instead of being repeated everywhere.

@@ -1,11 +1,12 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import * as Paho from '../vendor/paho-mqtt';
import { Client as PahoClient } from 'paho-mqtt';
import { v4 as uuid } from 'uuid';
import Observable, { ZenObservable } from 'zen-observable-ts';
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we get rid of this zen-observable-ts dependency?

I remember having bundle size issues with this

Copy link
Member Author

Choose a reason for hiding this comment

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

Took a look at removing zen-observable-ts, which should be replaceable with @types/zen-observable. It didn't go well. VSCode is happy, but the build fails with errors like zen observable _1.default is not a constructor. There's got to be a work around, but I didn't find it in my 30 minute time box, so I'm not including any related changes in this update.

@stocaaro stocaaro requested review from cshfang and elorzafe February 14, 2023 21:09
elorzafe
elorzafe previously approved these changes Feb 15, 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.

I only have minor non blocking comments, I am good to merge this after checking usage numbers of the old AppSync provider

return !this.options
.aws_appsync_dangerously_connect_to_http_endpoint_for_testing;
return !this.options[
'aws_appsync_dangerously_connect_to_http_endpoint_for_testing'
Copy link
Contributor

Choose a reason for hiding this comment

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

This is used also for mock use case IIRC. That is a CLI feature that allows you to test the API without doing amplify push

I think it makes sense to not expose that unless is generated automatically by CLI

@@ -270,7 +303,7 @@ export class AWSAppSyncRealTimeProvider extends AbstractPubSubProvider {
subscriptionId,
}: {
options: AWSAppSyncRealTimeProviderOptions;
observer: ZenObservable.SubscriptionObserver<any>;
observer: PubSubContentObserver;
Copy link
Contributor

Choose a reason for hiding this comment

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

❤️

private _logStartSubscriptionError(subscriptionId, observer, err) {
private _logStartSubscriptionError(
subscriptionId: string,
observer: ZenObservable.SubscriptionObserver<any>,
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this also use an internal type?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep. Good call.

@@ -1,11 +1,12 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import * as Paho from '../vendor/paho-mqtt';
import { Client as PahoClient } from 'paho-mqtt';
Copy link
Contributor

Choose a reason for hiding this comment

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

Cool can we do import type to make it explicit, I got a little scared about that import statement

@@ -1,5 +1,6 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import { ZenObservable } from 'zen-observable-ts';
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this be imported as type, or import SubscriptionObserver?

Copy link
Member Author

Choose a reason for hiding this comment

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

SubscriptionObserver isn't exported directly.

The d.ts file looks like this:

export declare namespace ZenObservable {
    interface SubscriptionObserver<T> {
        closed: boolean;
        next(value: T): void;
        error(errorValue: any): void;
        complete(): void;
    }
...
}

Am I misunderstanding what you're asking for here? What would I import to do this?

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

Have discussed with Francisco. We'll be looking at retooling our zen-observable usage for v6. I did some exploration here to try to excise zen-observable-ts, however the @types/zen-observable package is missing some of the features zen-observable patches onto global. Would be nice to have a ts first observable lib that is bundle-size efficient.

@stocaaro stocaaro requested a review from elorzafe February 16, 2023 20:23
@stocaaro
Copy link
Member Author

stocaaro commented Mar 1, 2023

On request, I have run the integ tests against this change, which prompted working out some Angular build issues.

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.

It's hard to validate with my eyeballs, but from a DataStore/API perspective, it looks like most of the changes here should be non-impactful. If we've got passing integ tests, I think that's good for me.

@stocaaro stocaaro merged commit 5147c5c into aws-amplify:main Mar 1, 2023
@WolfWalter
Copy link

This PR changed the PubSub message format in an unexpected way:
5147c5c#diff-2a29f8a41f2b66733bb64a134e53acb5e67615e026667885a5686ce933d3869eL267-R300

topicSymbol is now an object key and the Symbol is not used anymore. This totally changed how to get the topic and the message object gets altered in an unexpected way.

Was that really intentional @stocaaro and @elorzafe?

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.

7 participants