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(api): Subscription Reconnection #2074

Merged
Merged
Show file tree
Hide file tree
Changes from 49 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
110672a
chore!(api): migrate API category type definitions (#1640)
Jun 15, 2022
66d99bf
chore(api): API Native Bridge for .addPlugin() (#1756)
Equartey Jun 23, 2022
2ff8e51
chore(api): API Pigeon update (#1813)
Equartey Jun 27, 2022
f4211af
feat(api): REST methods in dart with auth mode none (#1783)
Jun 27, 2022
5608b59
feat!(api): GraphQL API key auth mode (#1858)
Equartey Jul 13, 2022
985b045
feat!(core,auth): auth providers definition and CognitoIamAuthProvide…
Jul 19, 2022
7b6ce8e
feat(core,api): IAM auth mode for HTTP requests (REST and GQL) (#1893)
Jul 21, 2022
cb6fbdb
feat(api): GraphQL Custom Request Headers (#1938)
Equartey Jul 29, 2022
a1e8d9d
feat(auth,api): cognito user pools auth provider & auth mode for API …
Aug 8, 2022
3ac59f4
fix(auth): correct auth providers imports from rebase (#2042)
Aug 22, 2022
4009458
feat(api): .subscribe() for GraphQL (#1915)
Aug 26, 2022
48c9582
chore(api): Api Example - generate files for multi-plaform (#2080)
Equartey Sep 1, 2022
bef7f19
feat(api): Subscription Reconnection
Equartey Aug 30, 2022
fcc53a0
fix: unexposed retryTimeout
Equartey Aug 31, 2022
32178ca
fix: more robust uri replacement
Equartey Aug 31, 2022
8bc47dd
fix: syntax errors
Equartey Aug 31, 2022
826d470
fix: code review feedback 1
Equartey Sep 1, 2022
b786902
fix: export retry package
Equartey Sep 1, 2022
2abff51
fix: Hub Event errors
Equartey Sep 8, 2022
80aa0d5
fix: typos & PR comments
Equartey Sep 8, 2022
7fc1cea
fix: review comments
Equartey Sep 8, 2022
ff85ce2
fix: hub events types
Equartey Sep 9, 2022
9ddea6b
chore!(api): migrate API category type definitions (#1640)
Jun 15, 2022
8e52487
chore(api): API Native Bridge for .addPlugin() (#1756)
Equartey Jun 23, 2022
4941901
chore(api): API Pigeon update (#1813)
Equartey Jun 27, 2022
e780b22
feat(api): REST methods in dart with auth mode none (#1783)
Jun 27, 2022
daaff32
feat!(api): GraphQL API key auth mode (#1858)
Equartey Jul 13, 2022
d2bafc9
feat!(core,auth): auth providers definition and CognitoIamAuthProvide…
Jul 19, 2022
d6f6405
feat(core,api): IAM auth mode for HTTP requests (REST and GQL) (#1893)
Jul 21, 2022
d6d6019
feat(api): GraphQL Custom Request Headers (#1938)
Equartey Jul 29, 2022
5829de8
feat(auth,api): cognito user pools auth provider & auth mode for API …
Aug 8, 2022
f6c1c1b
fix(auth): correct auth providers imports from rebase (#2042)
Aug 22, 2022
2e05c6f
feat(api): .subscribe() for GraphQL (#1915)
Aug 26, 2022
3cae1e9
fix: unit & integ tests passing
Equartey Sep 9, 2022
e411b69
Merge branch 'feat/api-next' into feat/graphql-sub-reconnect
Equartey Sep 9, 2022
ea0950e
fix: removed dup var
Equartey Sep 9, 2022
76d9907
chore(api): Api Example - generate files for multi-plaform (#2080)
Equartey Sep 1, 2022
50f878e
Merge branch 'feat/api-next' into feat/graphql-sub-reconnect
Equartey Sep 9, 2022
e721775
Merge branch 'next' into feat/api-next
Equartey Sep 13, 2022
85c4227
Merge branch 'feat/api-next' into feat/graphql-sub-reconnect
Equartey Sep 13, 2022
e6230c5
chore(api): Refactor http client & cancelable operation (#2119)
Equartey Sep 14, 2022
12de4f0
Merge branch 'feat/api-next' into feat/graphql-sub-reconnect
Equartey Sep 14, 2022
5431552
fix: unit tests sans-reconnect & hub events
Equartey Sep 14, 2022
c926e66
Merge branch 'feat/api-next' into feat/graphql-sub-reconnect
Equartey Sep 27, 2022
426e0fa
Nothing gets past Travis 💪
Equartey Sep 27, 2022
51e8917
Integration file clean up
Equartey Sep 27, 2022
4aab1e1
missed two files
Equartey Sep 27, 2022
b8677e9
Removed http dep
Equartey Sep 27, 2022
3264fc7
Types, Lints, overrides, and Passing Tests.
Equartey Sep 28, 2022
d3896e7
Fixed pingClient init value
Equartey Sep 28, 2022
6b5bf50
fix: some nits
Equartey Sep 29, 2022
dc00c44
fix: some nits
Equartey Sep 30, 2022
b1f162b
fix: ios network none issue on second sub
Equartey Sep 30, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import FlutterMacOS
import Foundation

import amplify_secure_storage
import connectivity_plus_macos
Copy link
Contributor

Choose a reason for hiding this comment

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

when I run aft bs I get similar file modifications in packages/api/amplify_api/example/macos/Flutter/GeneratedPluginRegistrant.swift. Should that be added as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I noticed some extra generated files didn't committed. Will include them next push 👍

import path_provider_macos

func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AmplifySecureStoragePlugin.register(with: registry.registrar(forPlugin: "AmplifySecureStoragePlugin"))
ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

#include "generated_plugin_registrant.h"

#include <connectivity_plus_windows/connectivity_plus_windows_plugin.h>

void RegisterPlugins(flutter::PluginRegistry* registry) {
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#

list(APPEND FLUTTER_PLUGIN_LIST
connectivity_plus_windows
)

list(APPEND FLUTTER_FFI_PLUGIN_LIST
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-18/"/>
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
<classpathentry kind="output" path="bin/default"/>
</classpath>
13 changes: 12 additions & 1 deletion packages/amplify/amplify_flutter_android/android/.project
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>amplify_core</name>
<name>amplify_flutter_android</name>
<comment>Project amplify_core created by Buildship.</comment>
<projects>
</projects>
Expand All @@ -20,4 +20,15 @@
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
</natures>
<filteredResources>
<filter>
<id>0</id>
<name></name>
<type>30</type>
<matcher>
<id>org.eclipse.core.resources.regexFilterMatcher</id>
<arguments>node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
</matcher>
</filter>
</filteredResources>
</projectDescription>
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import FlutterMacOS
import Foundation

import amplify_secure_storage
import connectivity_plus_macos
import path_provider_macos

func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AmplifySecureStoragePlugin.register(with: registry.registrar(forPlugin: "AmplifySecureStoragePlugin"))
ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

#include "generated_plugin_registrant.h"

#include <connectivity_plus_windows/connectivity_plus_windows_plugin.h>

void RegisterPlugins(flutter::PluginRegistry* registry) {
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#

list(APPEND FLUTTER_PLUGIN_LIST
connectivity_plus_windows
)

list(APPEND FLUTTER_FFI_PLUGIN_LIST
Expand Down
4 changes: 4 additions & 0 deletions packages/amplify_core/lib/src/hub/amplify_hub_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ class AmplifyHubImpl extends AmplifyHub {
Future.wait<void>([
for (final stream in _availableStreams.values) stream.close(),
]).ignore();
Future.wait<void>([
Copy link
Contributor

Choose a reason for hiding this comment

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

Missed this from before, but what does this block do?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No worries, this was just added. @dnys1 helped debug this one. It cleans up hub event subscriptions on cancel, an issue I was seeing in testing.

Copy link
Contributor

Choose a reason for hiding this comment

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

cool, thanks for info. Might be worth a small comment here to explain.

for (final subs in _subscriptions.values)
for (final sub in subs) sub.cancel()
]).ignore();
_availableStreams.clear();
_subscriptions.clear();
}
Expand Down
5 changes: 4 additions & 1 deletion packages/amplify_core/lib/src/hub/hub_channel.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
Expand Down Expand Up @@ -28,4 +28,7 @@ enum HubChannel<HubEventPayload, E extends HubEvent<HubEventPayload>> {

/// Events of the DataStore category.
DataStore<DataStoreHubEventPayload, DataStoreHubEvent>(),

/// Events of the API category
Api<ApiHubEventPayload, ApiHubEvent>();
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
*/

import 'package:amplify_core/amplify_core.dart';
import 'package:async/async.dart';
Equartey marked this conversation as resolved.
Show resolved Hide resolved
Equartey marked this conversation as resolved.
Show resolved Hide resolved
import 'package:meta/meta.dart';

abstract class APIPluginInterface extends AmplifyPluginInterface {
Expand Down
7 changes: 7 additions & 0 deletions packages/amplify_core/lib/src/types/api/api_types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
* permissions and limitations under the License.
*/

// Packages
export 'package:retry/retry.dart' show RetryOptions;

// API Authorization
export 'auth/api_auth_provider.dart';
export 'auth/api_authorization_type.dart';
Expand All @@ -26,6 +29,10 @@ export 'graphql/graphql_request_type.dart';
export 'graphql/graphql_response.dart';
export 'graphql/graphql_response_error.dart';
export 'graphql/graphql_subscription_operation.dart';
export 'graphql/graphql_subscription_options.dart';

export 'hub/api_hub_event.dart';
export 'hub/api_subscription_hub_event.dart';

export 'rest/rest_exception.dart';
export 'rest/rest_operation.dart';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ class GraphQLRequest<T> {
/// Only required if your backend has multiple GraphQL endpoints in the amplifyconfiguration.dart file. This parameter is then needed to specify which one to use for this request.
final String? apiName;

/// A map of Strings to dynamically use for custom headers in the http request.
final Map<String, String>? headers;

/// Authorization type to use for this request.
///
/// If not supplied, the request will use the default endpoint mode.
final APIAuthorizationType? authorizationMode;

/// A map of Strings to dynamically use for custom headers in the http request.
final Map<String, String>? headers;

/// A map of values to dynamically use for variable names in the `document`.
///
/// See https://graphql.org/learn/queries/#variables for more information.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

import 'package:retry/retry.dart';

/// Configuration options for GraphQL Subscriptions and their WebSockets.
class GraphQLSubscriptionOptions {
/// Configure the ping interval for AppSync polling for subscription connections.
Copy link
Contributor

Choose a reason for hiding this comment

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

question: Would it make sense to state the default here? I'm guessing customers will see these comments when seeing what options they can provide and when deciding to provide a value or not it helps to know what the default is.

final Duration? pingInterval;

/// Configure the exponential retry strategy options
Equartey marked this conversation as resolved.
Show resolved Hide resolved
/// see: https://pub.dev/documentation/retry/latest/retry/RetryOptions-class.html
final RetryOptions? retryOptions;

const GraphQLSubscriptionOptions({this.pingInterval, this.retryOptions});
}
27 changes: 27 additions & 0 deletions packages/amplify_core/lib/src/types/api/hub/api_hub_event.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

import 'package:amplify_core/amplify_core.dart';

class ApiHubEvent extends HubEvent<ApiHubEventPayload> {
ApiHubEvent(
super.eventName, {
super.payload,
});
}

abstract class ApiHubEventPayload {
const ApiHubEventPayload();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
/*
* Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

import 'package:amplify_core/amplify_core.dart';

/// {@template amplify_common.hub.api_network_state}
/// Network states for graphql subscriptions.
/// {@endtemplate}
enum NetworkState {
/// {@macro amplify_common.hub.api_network_state_connected}
connected,

/// {@macro amplify_common.hub.api_network_state_disconnected}
disconnected,

/// {@macro amplify_common.hub.api_network_state_failed}
failed;
}

/// {@template amplify_common.hub.api_intended_state}
/// The intended network states for graphql subscriptions.
/// {@endtemplate}
enum IntendedState {
/// {@macro amplify_common.hub.api_intended_state_connected}
connected,

/// {@macro amplify_common.hub.api_intended_state_disconnected}
disconnected;
}

/// {@template amplify_common.hub.api_subscription_status}
/// A consolidated subscription connection status determined by the network and
/// intended states.
/// {@endtemplate}
enum SubscriptionStatus {
/// {@macro amplify_common.hub.api_subscription_status_connected}
connected,

/// {@macro amplify_common.hub.api_subscription_status_disconnected}
disconnected,

/// {@macro amplify_common.hub.api_subscription_status_connected}
connecting,

/// {@macro amplify_common.hub.api_subscription_status_disconnected}
pendingDisconnected,

/// {@macro amplify_common.hub.api_subscription_status_failed}
failed;
}

class SubscriptionDetails extends ApiHubEventPayload
with
AWSEquatable<SubscriptionDetails>,
AWSSerializable<Map<String, Object?>>,
AWSDebuggable {
/// {@macro amplify_common.hub.api_network_state}
final NetworkState networkState;

/// {@macro amplify_common.hub.api_intended_state}
final IntendedState intendedState;

SubscriptionDetails(this.networkState, this.intendedState);

@override
List<Object?> get props => [intendedState, networkState];

@override
Map<String, String> toJson() => {
'networkState': networkState.name,
'intendedState': intendedState.name,
};

@override
String get runtimeTypeName => 'SubscriptionDetails';
}

class SubscriptionHubEvent extends ApiHubEvent
Equartey marked this conversation as resolved.
Show resolved Hide resolved
with
AWSEquatable<SubscriptionHubEvent>,
AWSSerializable<Map<String, Object?>>,
AWSDebuggable {
final SubscriptionDetails details;

static const String _name = 'SubscriptionHubEvent';

SubscriptionHubEvent._(this.details) : super(_name, payload: details);

/// {@template amplify_common.hub.api_subscription_status}
/// An overall status for GraphQL subscription connection, determined by the
/// underlying details [networkState] & [intendedState]
/// {@endtemplate}
SubscriptionStatus get status {
// Connection failed
if (details.networkState == NetworkState.failed) {
return SubscriptionStatus.failed;
}
// Connected with active subscriptions
if (details.networkState == NetworkState.connected &&
details.intendedState == IntendedState.connected) {
return SubscriptionStatus.connected;
}
// Disconnected with active subscriptions
if (details.networkState == NetworkState.disconnected &&
details.intendedState == IntendedState.connected) {
return SubscriptionStatus.connecting;
}
// Connected without active subscriptions
if (details.networkState == NetworkState.connected &&
details.intendedState == IntendedState.disconnected) {
return SubscriptionStatus.pendingDisconnected;
}

// disconnected without active subscriptions
return SubscriptionStatus.disconnected;
}

/// {@template amplify_common.hub.api_subscription_connected}
/// Emitted when a GraphQL subscription is connected.
/// {@endtemplate}
SubscriptionHubEvent.connected()
: this._(SubscriptionDetails(
NetworkState.connected, IntendedState.connected));

/// {@template amplify_common.hub.api_subscription_connected}
/// Emitted when a GraphQL subscription is connecting/reconnecting.
/// {@endtemplate}
SubscriptionHubEvent.connecting()
: this._(SubscriptionDetails(
NetworkState.disconnected, IntendedState.connected));

/// {@template amplify_common.hub.api_subscription_disconnected}
/// Emitted when a GraphQL subscription connection has disconnected.
/// {@endtemplate}
SubscriptionHubEvent.disconnected()
: this._(SubscriptionDetails(
NetworkState.disconnected, IntendedState.disconnected));

/// {@template amplify_common.hub.api_subscription_pending_disconnect}
/// Emitted when a GraphQL subscription connection is pending disconnect,
/// but should exist.
/// {@endtemplate}
SubscriptionHubEvent.pendingDisconnect()
: this._(SubscriptionDetails(
NetworkState.connected, IntendedState.disconnected));

/// {@template amplify_common.hub.api_subscription_failed}
/// Emitted when a GraphQL subscription connection has failed.
/// {@endtemplate}
SubscriptionHubEvent.failed()
: this._(SubscriptionDetails(
NetworkState.failed, IntendedState.disconnected));

@override
List<Object?> get props => [details, status];

@override
String get runtimeTypeName => 'SubscriptionHubEvent';

@override
Map<String, Object?> toJson() => <String, dynamic>{
'status': status.name,
'details': details.toJson(),
};
}
Loading