-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
Conversation
@@ -440,7 +448,7 @@ describe('PubSub', () => { | |||
provider: 'MqttOverWSProvider', | |||
}); | |||
|
|||
expect(mqttOverWSProvider.isSSLEnabled).toBe(false); | |||
expect(mqttOverWSProvider['isSSLEnabled']).toBe(false); |
There was a problem hiding this comment.
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' |
There was a problem hiding this comment.
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.
This comment was marked as duplicate.
Sorry, something went wrong.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
logger.debug({ err }); | ||
const message = err['message'] ?? ''; | ||
const message = String(err.message ?? ''); |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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.
This comment was marked as resolved.
Sorry, something went wrong.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 Report
📣 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
📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more |
There was a problem hiding this 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.
_topics: string[] | string, | ||
_msg: object, | ||
_options?: AWSAppSyncRealTimeProviderOptions |
There was a problem hiding this comment.
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>
?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
packages/pubsub/src/Providers/AWSAppSyncRealTimeProvider/index.ts
Outdated
Show resolved
Hide resolved
packages/pubsub/src/Providers/AWSAppSyncRealTimeProvider/index.ts
Outdated
Show resolved
Hide resolved
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.
This comment was marked as duplicate.
Sorry, something went wrong.
packages/pubsub/src/Providers/AWSAppSyncRealTimeProvider/index.ts
Outdated
Show resolved
Hide resolved
packages/pubsub/src/Providers/AWSAppSyncRealTimeProvider/index.ts
Outdated
Show resolved
Hide resolved
@@ -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 { |
There was a problem hiding this comment.
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.
This comment was marked as resolved.
Sorry, something went wrong.
} | ||
|
||
protected get endpoint() { | ||
protected get endpoint(): string | undefined | Promise<string> { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
This comment was marked as outdated.
Sorry, something went wrong.
There was a problem hiding this 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 = { |
There was a problem hiding this comment.
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.
This comment was marked as outdated.
Sorry, something went wrong.
There was a problem hiding this 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'; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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>; |
There was a problem hiding this comment.
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>; |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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>>; |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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.
packages/pubsub/src/PubSub.ts
Outdated
error: error => observer.error({ provider, error }), | ||
next: (value: Record<string, unknown>) => | ||
observer.next({ provider, value }), | ||
error: (error: Record<string, unknown>) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure about this, maybe this to change as well
There was a problem hiding this comment.
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 bestring
s as well asRecord
s/object
s- The category subscription returns
{provider: x, value: y}
instead of thestring
/Record
itself, so that can be more clearly typed. - The
publish
and subscribevalue
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'; |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this 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' |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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>, |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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'; |
There was a problem hiding this comment.
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'; |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think Francisco is referring to: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export
There was a problem hiding this comment.
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.
On request, I have run the integ tests against this change, which prompted working out some Angular build issues. |
There was a problem hiding this 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.
This PR changed the PubSub message format in an unexpected way:
|
Description of changes
Changes made:
typescript-coverage-report
output to identify and fix ts issuespaho.js
AWSAppSyncProvider.ts
TS Coverage:
Description of how you validated changes
Checklist
yarn test
passesBy submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.