Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 5 additions & 1 deletion analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ analyzer:
todo: ignore
exclude:
# exclude all the generated files
# - packages/*/lib/**/*.*.dart
- packages/*/lib/**/*.g.dart

linter:
rules:
Expand Down Expand Up @@ -89,3 +89,7 @@ linter:

# There are situations where we use default in enums on purpose
no_default_cases: false

# Temporarily disabled to find more important issues
public_member_api_docs: false
avoid_print: false
10 changes: 10 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
coverage:
status:
project:
default: # default is the status check's name, not default settings
target: auto
threshold: 5
base: auto
patch:
default:
target: 80%
2 changes: 2 additions & 0 deletions melos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ command:
environment:
sdk: ^3.6.2
flutter: ">=3.27.4"
dev_dependencies:
build_runner: ^2.4.15

scripts:
postclean:
Expand Down
7 changes: 7 additions & 0 deletions packages/stream_feeds/build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
targets:
$default:
builders:
json_serializable:
options:
explicit_to_json: true
field_rename: snake
127 changes: 62 additions & 65 deletions packages/stream_feeds/lib/src/feeds_client.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import 'dart:async';

import 'package:meta/meta.dart';
import 'package:rxdart/rxdart.dart';
import 'package:stream_core/stream_core.dart';
import 'package:uuid/uuid.dart';

import '../stream_feeds.dart';
import 'generated/api/api.g.dart' as api;
import 'generated/api/api.dart' as api;
import 'repositories.dart';
import 'utils/endpoint_config.dart';
import 'ws/feeds_ws_event.dart';
Expand All @@ -14,20 +14,38 @@ class FeedsClient {
FeedsClient({
required this.apiKey,
required this.user,
required this.userToken,
String? userToken,
TokenProvider? userTokenProvider,
this.config = const FeedsConfig(),
this.userTokenProvider,
this.networkMonitor,
}) {
FeedsClientEnvironment environment = const FeedsClientEnvironment(),
}) : assert(
userToken != null || userTokenProvider != null,
'Provide either a user token or a user token provider, or both',
) {
tokenManager = userTokenProvider != null
? TokenManager.provider(
user: user,
provider: userTokenProvider,
token: userToken,
)
: TokenManager.static(user: user, token: userToken ?? '');

// TODO: fill with correct values
final systemEnvironmentManager = SystemEnvironmentManager(
environment: const SystemEnvironment(
sdkName: 'stream-feeds-dart',
sdkIdentifier: 'dart',
sdkVersion: '0.1.0',
),
);

apiClient = api.DefaultApi(
api.ApiClient(
basePath: endpointConfig.baseFeedsUrl,
authentication: _Authentication(
apiKey: apiKey,
user: user,
getToken: () async => userToken,
getConnectionId: () => webSocketClient.connectionId,
),
CoreHttpClient(
apiKey,
systemEnvironmentManager: systemEnvironmentManager,
options: HttpClientOptions(baseUrl: endpointConfig.baseFeedsUrl),
connectionIdProvider: () => webSocketClient.connectionId,
tokenManager: tokenManager,
),
);
final websocketUri = Uri.parse(endpointConfig.wsEndpoint).replace(
Expand All @@ -38,7 +56,7 @@ class FeedsClient {
},
);

webSocketClient = WebSocketClient(
webSocketClient = environment.createWebSocketClient(
url: websocketUri.toString(),
eventDecoder: FeedsWsEvent.fromEventObject,
onConnectionEstablished: _authenticate,
Expand All @@ -49,15 +67,15 @@ class FeedsClient {

final String apiKey;
final User user;
final String userToken;
late final TokenManager tokenManager;
final FeedsConfig config;
final UserTokenProvider? userTokenProvider;
final NetworkMonitor? networkMonitor;

late final api.DefaultApi apiClient;

@internal
late final FeedsRepository feedsRepository;

static final endpointConfig = EndpointConfig.production;
static const endpointConfig = EndpointConfig.production;
late final WebSocketClient webSocketClient;
ConnectionRecoveryHandler? connectionRecoveryHandler;
Stream<FeedsWsEvent> get feedsEvents =>
Expand All @@ -77,7 +95,7 @@ class FeedsClient {

connectionRecoveryHandler = DefaultConnectionRecoveryHandler(
client: webSocketClient,
networkMonitor: networkMonitor,
networkMonitor: config.networkMonitor,
);

_connectionCompleter = Completer<void>();
Expand Down Expand Up @@ -114,10 +132,10 @@ class FeedsClient {
}
}

void _authenticate() {
Future<void> _authenticate() async {
final connectUserRequest = WsAuthMessageRequest(
products: ['feeds'],
token: userToken,
token: await tokenManager.loadToken(),
userDetails: ConnectUserDetailsRequest(
id: user.id,
name: user.originalName,
Expand All @@ -142,49 +160,28 @@ class FeedsClient {
}

class FeedsConfig {
const FeedsConfig();
// TODO: Add config for feeds
}

typedef ConnectionIdProvider = String? Function();
typedef UserTokenProvider = Future<String> Function();

// TODO: Migrate the API to dio for authentication and refresh of user tokens
class _Authentication extends api.Authentication {
_Authentication({
required this.apiKey,
required this.user,
required this.getToken,
required this.getConnectionId,
const FeedsConfig({
this.networkMonitor,
});

final String apiKey;
final User user;
final UserTokenProvider getToken;
final ConnectionIdProvider getConnectionId;

@override
Future<void> applyToParams(
List<api.QueryParam> queryParams,
Map<String, String> headerParams,
) async {
queryParams.add(api.QueryParam('api_key', apiKey));
final connectionId = getConnectionId();
final userToken = await getToken();
switch (user.type) {
case UserAuthType.regular || UserAuthType.guest:
if (connectionId != null) {
queryParams.add(api.QueryParam('connection_id', connectionId));
}
headerParams['stream-auth-type'] = 'jwt';
headerParams['Authorization'] = userToken;
case UserAuthType.anonymous:
headerParams['stream-auth-type'] = 'anonymous';
if (userToken.isNotEmpty) {
headerParams['Authorization'] = userToken;
}
}
headerParams['X-Stream-Client'] = 'stream-feeds-dart';
headerParams['x-client-request-id'] = const Uuid().v4();
}
final NetworkMonitor? networkMonitor;
}

class FeedsClientEnvironment {
const FeedsClientEnvironment();

WebSocketClient createWebSocketClient({
required String url,
required EventDecoder eventDecoder,
PingReguestBuilder? pingReguestBuilder,
VoidCallback? onConnectionEstablished,
VoidCallback? onConnected,
}) =>
WebSocketClient(
url: url,
eventDecoder: FeedsWsEvent.fromEventObject,
pingReguestBuilder: pingReguestBuilder,
onConnectionEstablished: onConnectionEstablished,
onConnected: onConnected,
);
}
4 changes: 4 additions & 0 deletions packages/stream_feeds/lib/src/generated/api/api.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Code generated by GetStream internal OpenAPI code generator. DO NOT EDIT.

export 'api/default_api.dart';
export 'models.dart';
Loading