Skip to content

Commit

Permalink
fix: cleaned up endpoints and websockets
Browse files Browse the repository at this point in the history
  • Loading branch information
SlayerOrnstein committed Nov 26, 2023
1 parent 57ef468 commit ac10cdf
Show file tree
Hide file tree
Showing 11 changed files with 197 additions and 32 deletions.
18 changes: 17 additions & 1 deletion example/main.dart
Original file line number Diff line number Diff line change
@@ -1 +1,17 @@
Future<void> main() async {}
import 'package:market_client/market_client.dart';

Future<void> main() async {
final httpClient = MarketHttpClient();
final marketWebsocket = MarketWebsocket.openWebsocket(httpClient.platform);

final client = MarketClient(httpClient, marketWebsocket);
final recentOrders = await client.orders.getMostRecentOrders();

for (final order in recentOrders.sellOrders) {
// ignore: avoid_print
print(order.id);
}

// ignore: avoid_print
client.orders.newOrders().listen((event) => print(event.id));
}
1 change: 1 addition & 0 deletions lib/market_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ library market_client;

export 'src/endpoints/endpoints.dart';
export 'src/http_client.dart';
export 'src/marekt_client.dart';
export 'src/models/models.dart';
export 'src/utils/utils.dart';
4 changes: 2 additions & 2 deletions lib/src/endpoints/auth.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class AuthEndpoint {
/// Signs in a user and returns an instance of [CurrentUser] and the token.
///
/// Token should be saved by the client securely.
Future<(String, CurrentUser)> signIn(SignInRequest request) async {
Future<(MarketHttpClient, CurrentUser)> signIn(SignInRequest request) async {
final response = await _client.post(
'/auth/signin',
headers: <String, String>{'authorization': 'JWT'},
Expand All @@ -27,7 +27,7 @@ class AuthEndpoint {
}

return (
auth,
_client.copyWith(token: auth),
CurrentUser.fromJson(payload['user'] as Map<String, dynamic>)
);
}
Expand Down
1 change: 1 addition & 0 deletions lib/src/endpoints/endpoints.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export 'auth.dart';
export 'items.dart';
export 'orders.dart';
export 'websockets.dart';
41 changes: 41 additions & 0 deletions lib/src/endpoints/orders.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import 'package:market_client/market_client.dart';
import 'package:market_client/src/models/recent_orders.dart';

/// {@template orders_endpoint}
/// Contains functions to retrive new and recent orders.
/// {@endtemplate}
class OrdersEndpoint {
/// {@macro orders_endpoint}
const OrdersEndpoint(MarketHttpClient client, MarketWebsocket marketWebsocket)
: _client = client,
_marketWebsocket = marketWebsocket;

final MarketHttpClient _client;
final MarketWebsocket _marketWebsocket;

Map<String, dynamic> _parsePayload(Map<String, dynamic> event) {
final payload = event['payload'] as Map<String, dynamic>;

return payload['order'] as Map<String, dynamic>;
}

/// Initializes and listens for new orders.
Stream<OrderFull> newOrders() {
_marketWebsocket.send(MarketWebsocketTypes.subscribeToNewOrders);

return _marketWebsocket.messages
.where((e) => e['type'] == MarketWebsocketTypes.newOrderEvent)
.map(_parsePayload)
.map(OrderFull.fromJson);
}

/// Retrives a list of the most recent orders.
///
/// Not sure up till when so expect a long list.
Future<MostRecentOrders> getMostRecentOrders() async {
final response = await _client.get('/most_recent');
final payload = HttpHelpers.parseResponse(response.body);

return MostRecentOrders.fromJson(payload);
}
}
63 changes: 35 additions & 28 deletions lib/src/endpoints/websockets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,57 +2,64 @@ import 'dart:async';
import 'dart:convert';

import 'package:market_client/market_client.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:web_socket_client/web_socket_client.dart';

abstract class MarketWebsocketTypes {
static const subscribeToNewOrders = '@WS/SUBSCRIBE/MOST_RECENT';
static const newOrderEvent = '@WS/SUBSCRIPTIONS/MOST_RECENT/NEW_ORDER';
static const onlineCountEvent = '@WS/MESSAGE/ONLINE_COUNT';
}

/// {@template websocket_endpoint}
/// Basic functions to listen to the websocket provided by Warframe.market.
/// {@endtemplate}
class WebsocketEndpoints {
class MarketWebsocket {
/// {@macro websocket_endpoint}
WebsocketEndpoints(WebSocketChannel websocket)
: _sink = websocket.sink,
_stream = websocket.stream.map<Map<String, dynamic>>(
(event) => json.decode(event as String) as Map<String, dynamic>,
);

final WebSocketSink _sink;
final Stream<Map<String, dynamic>> _stream;

static WebsocketEndpoints? _socket;
MarketWebsocket(WebSocket websocket) : _websocket = websocket;

/// Creates a websocket connection or returns the currently active one.
static WebsocketEndpoints openWebsocket(MarketPlatform platform) {
factory MarketWebsocket.openWebsocket(MarketPlatform platform) {
final wsUri = Uri(
scheme: 'wss',
host: 'warframe.market',
path: 'socket',
queryParameters: {'platform': platform.name},
);

final channel = WebSocketChannel.connect(wsUri);
final channel = WebSocket(wsUri);

return _socket ??= WebsocketEndpoints(channel);
return _marketWebsocket ??= MarketWebsocket(channel);
}

/// Initializes and listens for new orders.
Stream<OrderFull> newOrders() {
const type = '@WS/SUBSCRIPTIONS/MOST_RECENT/NEW_ORDER';
final WebSocket _websocket;

_sink.add(json.encode({'type': '@WS/SUBSCRIBE/MOST_RECENT'}));
static MarketWebsocket? _marketWebsocket;

return _stream.where((e) => e['type'] == type).map((event) {
final payload = event['payload'] as Map<String, dynamic>;
/// Warframe market websocket events.
///
/// All events are decoded into a json object.
Stream<Map<String, dynamic>> get messages {
return _websocket.messages.map<Map<String, dynamic>>(
(event) => json.decode(event as String) as Map<String, dynamic>,
);
}

return payload['order'] as Map<String, dynamic>;
}).map(OrderFull.fromJson);
/// Sends a json object to the market websocket.
///
/// Must use the types in [MarketWebsocketTypes].
void send(String type) => _websocket.send(json.encode({'type': type}));

/// Closes and discards the websocket singleton.
void close() {
_websocket.close();
_marketWebsocket = null;
}

/// By default the websocket transmits an online count of users.
Stream<OnlineCount> onlineCount() {
const type = '@WS/MESSAGE/ONLINE_COUNT';

return _stream.where((e) => e['type'] == type).map((event) {
return event['payload'] as Map<String, dynamic>;
}).map(OnlineCount.fromJson);
return messages
.where((e) => e['type'] == MarketWebsocketTypes.onlineCountEvent)
.map((event) => event['payload'] as Map<String, dynamic>)
.map(OnlineCount.fromJson);
}
}
15 changes: 15 additions & 0 deletions lib/src/http_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,19 @@ class MarketHttpClient {

return HttpHelpers.parseResponse(res.body);
}

/// Creates a new [MarketHttpClient] with the new given parameters.
MarketHttpClient copyWith({
String? token,
String? language,
MarketPlatform? platform,
http.Client? client,
}) {
return MarketHttpClient(
token: token ?? this.token,
language: language ?? this.language,
platform: platform ?? this.platform,
client: client ?? this.client,
);
}
}
28 changes: 28 additions & 0 deletions lib/src/marekt_client.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import 'package:market_client/market_client.dart';

/// {@template market_client}
/// Base Market client that exposes other endpoints.
/// {@endtemplate}
class MarketClient {
/// {@macro market_client}
const MarketClient(MarketHttpClient client, MarketWebsocket marketWebsocket)
: _client = client,
_marketWebsocket = marketWebsocket;

final MarketHttpClient _client;
final MarketWebsocket _marketWebsocket;

/// endpoints to retrieve publicly available orders and new orders.
OrdersEndpoint get orders => OrdersEndpoint(_client, _marketWebsocket);

/// Get orders on market items.
///
/// Can also retrieve a list of items that warframe market currently allows.
ItemsEndpoint get items => ItemsEndpoint(_client);

/// User specfic endpoints.
AuthEndpoint get auth => AuthEndpoint(_client);

/// Get and updated count of online and registered users.
Stream<OnlineCount> get onlineCount => _marketWebsocket.onlineCount();
}
33 changes: 33 additions & 0 deletions lib/src/models/recent_orders.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import 'package:equatable/equatable.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:market_client/market_client.dart';

part 'recent_orders.g.dart';

/// {@template most_recent_orders}
/// Instance containing both sell and buy orders.
/// {@endtemplate}
@JsonSerializable()
class MostRecentOrders extends Equatable {
/// {@macro most_recent_orders}
const MostRecentOrders({required this.sellOrders, required this.buyOrders});

/// Creates an instance of [MostRecentOrders] from a json object.
factory MostRecentOrders.fromJson(Map<String, dynamic> json) {
return _$MostRecentOrdersFromJson(json);
}

/// A list of most recent sell orders.
@JsonKey(name: 'sell_orders')
final List<OrderFull> sellOrders;

/// a list of most recent buy orders.
@JsonKey(name: 'buy_orders')
final List<OrderFull> buyOrders;

/// Creates a json object from the current [MostRecentOrders].
Map<String, dynamic> toJson() => _$MostRecentOrdersToJson(this);

@override
List<Object?> get props => [sellOrders, buyOrders];
}
23 changes: 23 additions & 0 deletions lib/src/models/recent_orders.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ dependencies:
json_annotation: ">=4.8.1 <5.0.0"
meta: ">=1.9.1 <2.0.0"
uuid: ">=4.1.0 <5.0.0"
web_socket_channel: ^2.4.0
web_socket_client: ^0.1.0

dev_dependencies:
build_runner: ^2.4.6
Expand Down

0 comments on commit ac10cdf

Please sign in to comment.