From ae178852ed9b1e0c3bda8b64792a5d3aff83068b Mon Sep 17 00:00:00 2001 From: necessarylion Date: Mon, 6 Nov 2023 09:19:46 +0700 Subject: [PATCH] extract websocket and fix bug on isolate --- example/cache/redis_driver.dart | 67 ------------------- example/config/database.dart | 52 --------------- example/config/redis.dart | 34 ---------- example/example.dart | 26 -------- lib/app.dart | 50 ++++++++------ lib/dox_core.dart | 3 - lib/env/env.dart | 1 + lib/http/request/dox_request.dart | 4 +- lib/isolate/dox_isolate.dart | 22 +------ lib/isolate/isolate_handler.dart | 34 +++++----- lib/isolate/isolate_interfaces.dart | 8 +-- lib/router/route.dart | 27 ++++++-- lib/websocket/dox_websocket.dart | 99 ---------------------------- lib/websocket/event.dart | 10 --- lib/websocket/socket_emitter.dart | 93 -------------------------- lib/websocket/socket_storage.dart | 80 ---------------------- lib/websocket/web_socket_info.dart | 15 ----- pubspec.yaml | 2 +- test/integration/isolate_test.dart | 4 +- test/integration/websocket_test.dart | 84 ----------------------- test/unit/route_test.dart | 2 +- 21 files changed, 79 insertions(+), 638 deletions(-) delete mode 100644 example/cache/redis_driver.dart delete mode 100644 example/config/database.dart delete mode 100644 example/config/redis.dart delete mode 100644 example/example.dart delete mode 100644 lib/websocket/dox_websocket.dart delete mode 100644 lib/websocket/event.dart delete mode 100644 lib/websocket/socket_emitter.dart delete mode 100644 lib/websocket/socket_storage.dart delete mode 100644 lib/websocket/web_socket_info.dart delete mode 100644 test/integration/websocket_test.dart diff --git a/example/cache/redis_driver.dart b/example/cache/redis_driver.dart deleted file mode 100644 index 0bbe4ea..0000000 --- a/example/cache/redis_driver.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'package:dox_core/cache/cache_driver_interface.dart'; -import 'package:redis/redis.dart'; - -import '../config/redis.dart'; - -class RedisCacheDriver implements CacheDriverInterface { - @override - String tag = ''; - - @override - String get prefix => 'dox-framework-cache-$tag:'; - - RedisCacheDriver(); - - @override - void setTag(String tagName) { - tag = tagName; - } - - @override - Future flush() async { - Command cmd = Redis().command; - List res = await cmd.send_object(['KEYS', '$prefix*']); - if (res.isNotEmpty) { - await cmd.send_object(['DEL', ...res]); - } - } - - @override - Future forever(String key, String value) async { - await put(key, value, duration: Duration(days: 365 * 1000)); - } - - @override - Future forget(String key) async { - Command cmd = Redis().command; - return await cmd.send_object(['DEL', prefix + key]); - } - - @override - Future get(String key) async { - Command cmd = Redis().command; - return await cmd.send_object(['GET', prefix + key]); - } - - @override - Future has(String key) async { - dynamic val = await get(key); - return val != null ? true : false; - } - - @override - Future put(String key, String value, {Duration? duration}) async { - duration = duration ?? Duration(hours: 1); - DateTime time = DateTime.now().add(duration); - - Command cmd = Redis().command; - - await cmd.send_object([ - 'SET', - prefix + key, - value, - 'PXAT', - time.millisecondsSinceEpoch, - ]); - } -} diff --git a/example/config/database.dart b/example/config/database.dart deleted file mode 100644 index 0563ee4..0000000 --- a/example/config/database.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'package:dox_core/dox_core.dart'; -import 'package:dox_migration/dox_migration.dart'; -import 'package:dox_query_builder/dox_query_builder.dart'; -import 'package:postgres_pool/postgres_pool.dart'; - -class Database implements DoxService { - /// Declare as Singleton to reuse connection pool - static final Database _i = Database._internal(); - factory Database() => _i; - Database._internal(); - - late PgPool pool; - - late PgEndpoint endPoint; - - bool debug = false; - - int concurrency = 10; - - @override - Future setup() async { - Database().endPoint = PgEndpoint( - host: Env.get('DB_HOST', 'localhost'), - port: int.parse(Env.get('DB_PORT', '5432')), - database: Env.get('DB_NAME', 'dox'), - username: Env.get('DB_USERNAME', 'postgres'), - password: Env.get('DB_PASSWORD', 'postgres'), - ); - - Database().pool = PgPool( - Database().endPoint, - settings: PgPoolSettings() - ..maxConnectionAge = Duration(hours: 1) - ..concurrency = concurrency, - ); - - /// this will create connection pool initially when server started - /// so prevent connecting to connection pool when calling API for first time - await Database().pool.query('SELECT 1'); - - /// Initialize Sql QueryBuilder - SqlQueryBuilder.initialize( - database: Database().pool, - debug: debug, - printer: ConsoleQueryPrinter(), - ); - } - - Future migrate() async { - await Migration().migrate(); - } -} diff --git a/example/config/redis.dart b/example/config/redis.dart deleted file mode 100644 index 44ceeae..0000000 --- a/example/config/redis.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:dox_core/dox_core.dart'; -import 'package:redis/redis.dart'; - -class Redis implements DoxService { - /// Declare as Singleton to reuse connection - static final Redis _i = Redis._internal(); - factory Redis() => _i; - Redis._internal(); - - late Command command; - - @override - Future setup() async { - RedisConnection conn = RedisConnection(); - String tls = Env.get('REDIS_TLS', 'false'); - String host = Env.get('REDIS_HOST', 'localhost'); - int port = int.parse(Env.get('REDIS_PORT', '6379')); - - if (tls == 'true') { - Redis().command = await conn.connectSecure(host, port); - } else { - Redis().command = await conn.connect(host, port); - } - - String username = Env.get('REDIS_USERNAME', ''); - String password = Env.get('REDIS_PASSWORD', ''); - - if (username.isNotEmpty && password.isNotEmpty) { - await Redis().command.send_object( - ['AUTH', username, password], - ); - } - } -} diff --git a/example/example.dart b/example/example.dart deleted file mode 100644 index a6ef77e..0000000 --- a/example/example.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:dox_core/dox_core.dart'; - -import '../test/integration/requirements/config/app.dart'; -import 'config/database.dart'; -import 'config/redis.dart'; - -void main() async { - /// Initialize Dox - Dox().initialize(Config()); - - /// add database connection to isolate if required - Dox().addService(Database()); - - /// add database connection to isolate if required - Dox().addService(Redis()); - - /// set total thread - Dox().totalIsolate(3); - - /// run database migration before starting server - /// running migration should not be on part of Dox services - await Database().migrate(); - - /// start dox http server - await Dox().startServer(); -} diff --git a/lib/app.dart b/lib/app.dart index a9e4aa8..489cbdb 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -1,11 +1,12 @@ -import 'dart:isolate'; - +import 'package:dox_annotation/dox_annotation.dart'; import 'package:dox_core/dox_core.dart'; import 'package:dox_core/isolate/dox_isolate.dart'; import 'package:dox_core/server/dox_server.dart'; import 'package:dox_core/utils/logger.dart'; import 'package:sprintf/sprintf.dart'; +IocContainer _ioc = IocContainer(); + class Dox { /// setup singleton static Dox? _singleton; @@ -27,17 +28,17 @@ class Dox { late AppConfig config; /// global dox ioc containers - late IocContainer ioc; - - /// isolate send port - SendPort? sendPort; + IocContainer ioc = _ioc; /// isolate Id - int? isolateId; + int isolateId = 1; /// auth guard Guard? authGuard; + /// websocket + IDoxWebsocket? websocket; + /// total isolate to spawn int? _totalIsolate; @@ -51,11 +52,10 @@ class Dox { /// Dox().initialize(config); /// ``` void initialize(AppConfig appConfig) async { - ioc = IocContainer(); config = appConfig; } - /// set total thread + /// override total isolate from config /// default is 3 void totalIsolate(int value) { _totalIsolate = value; @@ -64,28 +64,39 @@ class Dox { /// set authorization config /// and this function can only call after initialize() /// ``` - /// await Dox().initialize(config) + /// Dox().initialize(config) /// Dox().setAuthConfig(AuthConfig()) /// ``` void setAuthConfig(AuthConfigInterface authConfig) { authGuard = authConfig.guards[authConfig.defaultGuard]; } + /// set websocket + /// ``` + /// Dox().initialize(config) + /// Dox().setWebsocket(DoxWebsocket()) + /// ``` + void setWebsocket(IDoxWebsocket ws) { + websocket = ws; + } + /// start dox server /// ``` /// await Dox().startServer(); /// ``` Future startServer() async { _totalIsolate ??= Dox().config.totalIsolate; - if (_totalIsolate == 1) { - await startServices(); - DoxServer().setResponseHandler(config.responseHandler); - await DoxServer().listen(config.serverPort, isolateId: 1); - } else { - await DoxIsolate().spawn(_totalIsolate!); + int isolatesToSpawn = _totalIsolate ?? 1; + + await startServices(); + DoxServer().setResponseHandler(config.responseHandler); + await DoxServer().listen(config.serverPort, isolateId: 1); + + if (isolatesToSpawn > 1) { + await DoxIsolate().spawn(isolatesToSpawn); } DoxLogger.info(sprintf( - 'Server started at http://127.0.0.1:%s with $_totalIsolate isolate', + 'Server started at http://127.0.0.1:%s with $isolatesToSpawn isolate', [Dox().config.serverPort], )); } @@ -106,12 +117,11 @@ class Dox { /// this is internal core use only /// your app do not need to call this function Future startServices() async { - _registerFormRequests(); - _registerRoute(); - for (DoxService service in doxServices) { await service.setup(); } + _registerFormRequests(); + _registerRoute(); } /// ################ end ########### diff --git a/lib/dox_core.dart b/lib/dox_core.dart index 19d7025..2a0e244 100644 --- a/lib/dox_core.dart +++ b/lib/dox_core.dart @@ -49,6 +49,3 @@ export 'storage/storage_driver_interface.dart'; /// Utils export 'utils/extensions.dart'; export 'utils/hash.dart'; - -/// Websocket -export 'websocket/socket_emitter.dart'; diff --git a/lib/env/env.dart b/lib/env/env.dart index 4598ab9..95a5cc8 100644 --- a/lib/env/env.dart +++ b/lib/env/env.dart @@ -34,6 +34,7 @@ class Env { Map data = {}; File envFile = file ?? File('${Directory.current.path}/.env'); + if (!envFile.existsSync()) return data; String contents = envFile.readAsStringSync(); // splitting with new line for each variables diff --git a/lib/http/request/dox_request.dart b/lib/http/request/dox_request.dart index 4f9dccf..7ec309f 100644 --- a/lib/http/request/dox_request.dart +++ b/lib/http/request/dox_request.dart @@ -1,16 +1,18 @@ import 'dart:io'; +import 'package:dox_annotation/dox_annotation.dart'; import 'package:dox_core/dox_core.dart'; import 'package:dox_core/http/request/http_request_body.dart'; import 'package:dox_core/router/route_data.dart'; import 'package:dox_core/utils/aes_encryptor.dart'; import 'package:dox_core/validation/dox_validator.dart'; -class DoxRequest { +class DoxRequest implements IDoxRequest { final RouteData route; final Uri uri; final ContentType? contentType; final HttpHeaders httpHeaders; + @override final HttpRequest httpRequest; final String? clientIp; diff --git a/lib/isolate/dox_isolate.dart b/lib/isolate/dox_isolate.dart index 34553ab..29daa22 100644 --- a/lib/isolate/dox_isolate.dart +++ b/lib/isolate/dox_isolate.dart @@ -3,7 +3,6 @@ import 'dart:isolate'; import 'package:dox_core/dox_core.dart'; import 'package:dox_core/isolate/isolate_handler.dart'; import 'package:dox_core/isolate/isolate_interfaces.dart'; -import 'package:dox_core/websocket/event.dart'; class DoxIsolate { /// singleton @@ -24,7 +23,7 @@ class DoxIsolate { /// await DoxIsolate().spawn(3) /// ``` Future spawn(int count) async { - for (int i = 0; i < count; i++) { + for (int i = 1; i < count; i++) { await _spawn(i + 1); } } @@ -42,34 +41,17 @@ class DoxIsolate { /// create a thread Future _spawn(int isolateId) async { - ReceivePort receivePort = ReceivePort(); - Isolate isolate = await Isolate.spawn( isolateHandler, IsolateSpawnParameter( isolateId, - receivePort.sendPort, Dox().config, Dox().doxServices, authGuard: Dox().authGuard, + routes: Route().routes, ), ); - receivePort.listen((dynamic message) { - if (message is SendPort) { - _sendPorts[isolateId] = message; - } - if (message is WebSocketEmitEvent) { - DoxIsolate().isolates.forEach((int isolateId, _) { - SendPort? sendPort = DoxIsolate().sendPorts[isolateId]; - if (sendPort != null) { - sendPort.send(message); - } - }); - } - }); - _isolates[isolateId] = isolate; - _receivePorts[isolateId] = receivePort; } } diff --git a/lib/isolate/isolate_handler.dart b/lib/isolate/isolate_handler.dart index 18fdfe1..57680c5 100644 --- a/lib/isolate/isolate_handler.dart +++ b/lib/isolate/isolate_handler.dart @@ -1,33 +1,29 @@ -import 'dart:isolate'; - import 'package:dox_core/dox_core.dart'; import 'package:dox_core/isolate/isolate_interfaces.dart'; import 'package:dox_core/server/dox_server.dart'; -import 'package:dox_core/websocket/event.dart'; /// process middleware and controller and sent data via sentPort void isolateHandler(IsolateSpawnParameter param) async { + /// send port of main isolate + AppConfig appConfig = param.config; + List services = param.services; + Guard? authGuard = param.authGuard; + + /// creating dox in new isolate; Dox().isolateId = param.isolateId; - Dox().sendPort = param.sendPort; - Dox().initialize(param.config); - Dox().addServices(param.services); - Dox().authGuard = param.authGuard; - await Dox().startServices(); + Dox().initialize(appConfig); + Dox().addServices(services); + Dox().authGuard = authGuard; - /// sending sendPort to main isolate - ReceivePort receivePort = ReceivePort(); - param.sendPort.send(receivePort.sendPort); + /// register routes + Route().setRoutes(param.routes); - receivePort.listen((dynamic message) { - /// listen for to emit message and - if (message is WebSocketEmitEvent) { - SocketEmitter emitter = - SocketEmitter(message.senderId, message.roomId, via: 'isolate'); - emitter.emit(message.event, message.message, exclude: message.exclude); - } - }); + /// starting registered services in new isolate; + await Dox().startServices(); + /// starting server in new isolate DoxServer().setResponseHandler(param.config.responseHandler); + await DoxServer().listen( param.config.serverPort, isolateId: param.isolateId, diff --git a/lib/isolate/isolate_interfaces.dart b/lib/isolate/isolate_interfaces.dart index fab66e0..28c1d86 100644 --- a/lib/isolate/isolate_interfaces.dart +++ b/lib/isolate/isolate_interfaces.dart @@ -1,18 +1,18 @@ -import 'dart:isolate'; - import 'package:dox_core/dox_core.dart'; +import 'package:dox_core/router/route_data.dart'; class IsolateSpawnParameter { - final SendPort sendPort; final int isolateId; final AppConfig config; final List services; final Guard? authGuard; + final List routes; + const IsolateSpawnParameter( this.isolateId, - this.sendPort, this.config, this.services, { this.authGuard, + this.routes = const [], }); } diff --git a/lib/router/route.dart b/lib/router/route.dart index 0f15bb1..0b0d9c3 100644 --- a/lib/router/route.dart +++ b/lib/router/route.dart @@ -1,9 +1,13 @@ // ignore_for_file: empty_catches +import 'package:dox_annotation/dox_annotation.dart'; +import 'package:dox_core/app.dart'; import 'package:dox_core/constants/http_request_method.dart'; import 'package:dox_core/router/route_data.dart'; import 'package:dox_core/utils/utils.dart'; -import 'package:dox_core/websocket/dox_websocket.dart'; + +/// get list of routes registered +List _routes = []; class Route { /// register as singleton @@ -16,7 +20,12 @@ class Route { List _preMiddleware = []; /// get list of routes registered - List routes = []; + List get routes => _routes; + + // set route list + void setRoutes(List routeList) { + _routes = routeList; + } /// group route /// ``` @@ -110,12 +119,16 @@ class Route { /// socket.on('intro', controller); /// }); /// ``` - static void websocket(String route, Function(DoxWebsocket) callback, + static void websocket(String route, Function(WebsocketEvent) callback, {List middleware = const []}) { - DoxWebsocket ws = DoxWebsocket(); + IDoxWebsocket? ws = Dox().websocket; + if (ws == null) { + throw Exception('websocket is not registered'); + } + WebsocketEvent event = ws.create(); Route()._addRoute(HttpRequestMethod.GET, Route()._prefix + route, - [...middleware, ws.handle]); - callback(ws); + [...middleware, event.handle]); + callback(event); } /// get route @@ -309,7 +322,7 @@ class Route { if (controller is List) { controllers.addAll(controller); } - routes.add(RouteData( + _routes.add(RouteData( method: method.name, path: path, controllers: controllers, diff --git a/lib/websocket/dox_websocket.dart b/lib/websocket/dox_websocket.dart deleted file mode 100644 index 24f8378..0000000 --- a/lib/websocket/dox_websocket.dart +++ /dev/null @@ -1,99 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:dox_core/dox_core.dart'; -import 'package:dox_core/websocket/socket_storage.dart'; -import 'package:uuid/uuid.dart'; - -Uuid uuid = Uuid(); - -class DoxWebsocket { - /// storage to store active socket connection - final SocketStorage _storage = SocketStorage(); - - DoxWebsocket(); - - /// registered websocket events listener - final Map _events = {}; - - /// register websocket event - /// ``` - /// DoxWebsocket.on('info', (SocketEmitter emitter, message) { - /// /// your logic here - /// }); - /// ``` - void on(String event, Function(SocketEmitter, dynamic) controller) { - _events[event] = controller; - } - - /// handle http request and convert into websocket - Future handle(DoxRequest req) async { - WebSocket ws = await WebSocketTransformer.upgrade(req.httpRequest); - - /// create socket id for each client connection - /// socketId is just `uuid` generated by us - String socketId = _createSocketId(); - - /// prepare emitter to pass to controller - SocketEmitter emitter = - SocketEmitter(socketId, WEB_SOCKET_DEFAULT_ROOM_NAME); - - /// add to active ws connection storage to reuse later - _storage.addWebSocketInfo(socketId, ws); - - /// add socket id to the room default room is the route eg. /ws - _storage.addWebSocketIdToRoom(socketId, WEB_SOCKET_DEFAULT_ROOM_NAME); - - /// send connected event to client - emitter.emitToSender('connected', {'id': socketId}); - - /// listen for new message sent from client - ws.listen( - (dynamic data) async { - /// decode message to map - Map payload = jsonDecode(data); - - String eventName = payload[WEB_SOCKET_EVENT_KEY]; - dynamic message = payload[WEB_SOCKET_MESSAGE_KEY]; - - /// get controller fom event controller - /// eg. socket.on(eventName, controller); - Function? controller = _events[eventName]; - - if (controller != null) { - Function.apply(controller, [emitter, message]); - } - - /// if event name is WEB_SOCKET_JOIN_ROOM_EVENT_NAME, - /// join to the room and message is room name - if (eventName == WEB_SOCKET_JOIN_ROOM_EVENT_NAME) { - String room = message; - _storage.addWebSocketIdToRoom(socketId, room); - } - }, - onDone: () { - /// once websocket is disconnected, remove socket from - /// active connection storage - _storage.removeWebSocketInfo(socketId); - }, - - /// coverage:ignore-start - onError: (dynamic error) { - /// if websocket has error, remove socket from - /// active connection storage - _storage.removeWebSocketInfo(socketId); - }, - - /// coverage:ignore-end - ); - - /// return websocket, so that response controller - /// do not close the connection - return ws; - } - - /// creating socket id with unique timestamp - String _createSocketId() { - return 'ws:${uuid.v4()}'; - } -} diff --git a/lib/websocket/event.dart b/lib/websocket/event.dart deleted file mode 100644 index df38162..0000000 --- a/lib/websocket/event.dart +++ /dev/null @@ -1,10 +0,0 @@ -class WebSocketEmitEvent { - final String senderId; - final String roomId; - final dynamic message; - final String event; - final List exclude; - - const WebSocketEmitEvent( - this.senderId, this.roomId, this.message, this.event, this.exclude); -} diff --git a/lib/websocket/socket_emitter.dart b/lib/websocket/socket_emitter.dart deleted file mode 100644 index 9be66eb..0000000 --- a/lib/websocket/socket_emitter.dart +++ /dev/null @@ -1,93 +0,0 @@ -import 'dart:io'; - -import 'package:dox_core/dox_core.dart'; -import 'package:dox_core/utils/json.dart'; -import 'package:dox_core/websocket/event.dart'; -import 'package:dox_core/websocket/socket_storage.dart'; -import 'package:dox_core/websocket/web_socket_info.dart'; - -class SocketEmitter { - /// room id to sent message - String _roomId; - - /// sender socket id - final String sender; - - /// where it is sending from - /// via callback or via isolate - final String via; - - /// storage to get active connection - final SocketStorage _storage = SocketStorage(); - - SocketEmitter(this.sender, this._roomId, {this.via = 'callback'}); - - /// set room to sent message - /// ``` - /// emitter.room('ABC'); - /// ``` - SocketEmitter room(dynamic id) { - _roomId = id; - return this; - } - - /// emit message only to sender - /// ``` - /// emitter.emitToSender('event', message); - /// ``` - void emitToSender(String event, dynamic message) { - WebSocketInfo? info = _storage.getWebSocketInfo(sender); - if (info != null) { - WebSocket websocket = info.websocket; - websocket.add(_createPayload(event, message)); - } - } - - /// set message except the sender - /// ``` - /// emitter.emitExceptSender('event', message); - /// ``` - void emitExceptSender(String event, dynamic message) { - emit(event, message, exclude: [sender]); - } - - /// set message to everyone in the room - /// ``` - /// emitter.emit('event', message); - /// ``` - void emit(String event, dynamic message, - {List exclude = const []}) { - List members = _storage.getWebSocketIdsOfTheRoom(_roomId); - - /// If sending from callback, it mean we need to notify other isolates. - /// So, sending to other isolates to get active connections of the rooms and - /// sent message to correct clients - if (via == 'callback' && Dox().sendPort != null) { - WebSocketEmitEvent emitEvent = - WebSocketEmitEvent(sender, _roomId, message, event, exclude); - Dox().sendPort?.send(emitEvent); - return; - } - - for (String socketId in members) { - if (!exclude.contains(socketId)) { - WebSocketInfo? info = _storage.getWebSocketInfo(socketId); - if (info != null) { - WebSocket websocket = info.websocket; - websocket.add(_createPayload(event, message)); - } - } - } - } - - /// create payload to sent message - String _createPayload(String event, dynamic message) { - return JSON.stringify({ - WEB_SOCKET_EVENT_KEY: event, - WEB_SOCKET_MESSAGE_KEY: message, - WEB_SOCKET_SENDER_KEY: sender, - WEB_SOCKET_ROOM_KEY: - _roomId == WEB_SOCKET_DEFAULT_ROOM_NAME ? null : _roomId, - }); - } -} diff --git a/lib/websocket/socket_storage.dart b/lib/websocket/socket_storage.dart deleted file mode 100644 index 8415485..0000000 --- a/lib/websocket/socket_storage.dart +++ /dev/null @@ -1,80 +0,0 @@ -import 'dart:io'; - -import 'package:dox_core/websocket/web_socket_info.dart'; - -class SocketStorage { - static final SocketStorage _singleton = SocketStorage._internal(); - factory SocketStorage() => _singleton; - SocketStorage._internal(); - - /// active websocket connections - final Map _socketConnections = - {}; - - /// active rooms with active users in the room - final Map> _rooms = >{}; - - /// add new websocket to active connections - /// this is used when new ws connection is connected - void addWebSocketInfo(String socketId, WebSocket ws) { - _socketConnections[socketId] = WebSocketInfo( - socketId: socketId, - websocket: ws, - ); - } - - /// get web socket info of socket Id - /// this is used to get websocket info - WebSocketInfo? getWebSocketInfo(String socketId) { - return _socketConnections[socketId]; - } - - /// remove connection by socketId - /// this is used when user leave or disconnected or on error - /// first remove from the room and then remove from connection - void removeWebSocketInfo(String socketId) { - WebSocketInfo? info = getWebSocketInfo(socketId); - if (info != null && info.activeRoom != null) { - removeWebSocketIdFromRoom(socketId, info.activeRoom); - } - _socketConnections.remove(socketId); - } - - /// add socket Id to the room - /// this is use to join the room - void addWebSocketIdToRoom(String socketId, String roomId) { - if (_rooms[roomId] == null) { - _rooms[roomId] = []; - } - _rooms[roomId]?.add(socketId); - updateActiveRoomId(socketId, roomId); - } - - /// update websocket active room id - /// this is used when user join new room - /// this remove previous room and set new room - void updateActiveRoomId(String socketId, String roomId) { - WebSocketInfo? info = getWebSocketInfo(socketId); - if (info != null) { - if (info.previousRoom != null) { - removeWebSocketIdFromRoom(socketId, info.previousRoom); - } - info.activeRoom = roomId; - info.previousRoom = roomId; - } - } - - /// get list of socket id from the room - /// this is used when we want to emit message to specific room - List getWebSocketIdsOfTheRoom(String roomId) { - return _rooms[roomId] ?? []; - } - - /// remove websocketId from the room - /// this is used when user leave the room - void removeWebSocketIdFromRoom(String socketId, String? roomId) { - if (_rooms[roomId] != null && roomId != null) { - _rooms[roomId]?.remove(socketId); - } - } -} diff --git a/lib/websocket/web_socket_info.dart b/lib/websocket/web_socket_info.dart deleted file mode 100644 index 361a9f3..0000000 --- a/lib/websocket/web_socket_info.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'dart:io'; - -class WebSocketInfo { - final String socketId; - final WebSocket websocket; - String? activeRoom; - String? previousRoom; - - WebSocketInfo({ - required this.socketId, - required this.websocket, - this.activeRoom, - this.previousRoom, - }); -} diff --git a/pubspec.yaml b/pubspec.yaml index d22cf1f..cee2012 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,6 +14,7 @@ dependencies: mime: ^1.0.4 string_scanner: ^1.1.0 uuid: ^3.0.6 + dox_annotation: 1.0.3 dev_dependencies: lints: ^2.0.0 @@ -21,6 +22,5 @@ dev_dependencies: http: ^0.13.6 redis: ^3.1.0 postgres_pool: ^2.1.6 - dox_query_builder: ^1.1.13 dox_migration: ^1.0.4 http_parser: ^4.0.2 diff --git a/test/integration/isolate_test.dart b/test/integration/isolate_test.dart index 011573a..f08c48b 100644 --- a/test/integration/isolate_test.dart +++ b/test/integration/isolate_test.dart @@ -45,7 +45,7 @@ void main() { setUpAll(() async { Dox().initialize(config); Dox().addService(ExampleService()); - Dox().totalIsolate(2); + Dox().totalIsolate(5); await Dox().startServer(); }); @@ -59,7 +59,7 @@ void main() { expect(res.statusCode, 200); expect(res.body, 'pong'); - expect(DoxIsolate().isolates.length, 2); + expect(DoxIsolate().isolates.length, 5 - 1); }); }); } diff --git a/test/integration/websocket_test.dart b/test/integration/websocket_test.dart deleted file mode 100644 index 86f2f98..0000000 --- a/test/integration/websocket_test.dart +++ /dev/null @@ -1,84 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:dox_core/dox_core.dart'; -import 'package:dox_core/utils/json.dart'; -import 'package:dox_core/websocket/dox_websocket.dart'; -import 'package:test/test.dart'; - -import '../utils/start_http_server.dart'; -import 'requirements/config/app.dart'; - -Config config = Config(); -String baseUrl = 'http://localhost:${config.serverPort}'; - -void main() { - group('Websocket |', () { - setUpAll(() async { - await startHttpServer(config); - }); - - tearDownAll(() async { - await Dox().server.close(); - }); - - test('websocket', () async { - Route.websocket('ws', (DoxWebsocket socket) { - socket.on('intro', (SocketEmitter emitter, dynamic message) { - emitter.room('ws').emit('intro', message); - }); - - socket.on('json', (SocketEmitter emitter, dynamic message) { - emitter.emitExceptSender('json', message); - }); - }); - - WebSocket socket = - await WebSocket.connect('ws://localhost:${config.serverPort}/ws'); - - String data = jsonEncode({ - 'event': 'intro', - 'message': 'hello', - }); - - String jsonData = jsonEncode({ - 'event': 'json', - 'message': {'title': 'hello'} - }); - - socket.listen((dynamic message) { - Map data = JSON.parse(message); - if (data['event'] == 'intro') { - expect(data['message'], 'hello'); - } - }); - - socket.add(data); - socket.add(jsonData); - - WebSocket socket2 = - await WebSocket.connect('ws://localhost:${config.serverPort}/ws'); - - String joinRoomData = jsonEncode({ - 'event': 'joinRoom', - 'message': 'ws', - }); - - socket2.add(joinRoomData); - - socket2.listen((dynamic message) { - Map data = JSON.parse(message); - if (data['event'] == 'intro') { - expect(data['message'], 'hello'); - } - - if (data['event'] == 'json') { - expect(data['message']['title'], 'hello'); - } - }); - - await socket.close(); - await socket2.close(); - }); - }); -} diff --git a/test/unit/route_test.dart b/test/unit/route_test.dart index 1625492..2db7ab8 100644 --- a/test/unit/route_test.dart +++ b/test/unit/route_test.dart @@ -8,7 +8,7 @@ import '../integration/requirements/controllers/blog.controller.dart'; void main() { group('Route |', () { setUp(() { - Route().routes = []; + Route().setRoutes([]); }); test('get', () {