1
0
Fork 0
mirror of synced 2024-06-02 02:44:47 +12:00

add ratchet websocket client to e2e test

This commit is contained in:
Torsten Dittmann 2021-03-02 10:56:18 +01:00
parent a3fb2abf66
commit bbabeaf026
4 changed files with 695 additions and 24 deletions

View file

@ -61,6 +61,7 @@
"require-dev": {
"appwrite/sdk-generator": "0.5.5",
"phpunit/phpunit": "9.4.2",
"ratchet/pawl": "0.3.5",
"swoole/ide-helper": "4.5.5",
"vimeo/psalm": "4.1.1"
},

659
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "5893b378d1dcda91aedf77059f4b0efb",
"content-hash": "ee078cbbb5ae7897f97c13e82fec567f",
"packages": [
{
"name": "adhocore/jwt",
@ -349,7 +349,7 @@
"issues": "https://github.com/domnikl/statsd-php/issues",
"source": "https://github.com/domnikl/statsd-php/tree/master"
},
"abandoned": true,
"abandoned": "slickdeals/statsd",
"time": "2020-01-03T14:24:58+00:00"
},
{
@ -410,6 +410,53 @@
],
"time": "2020-08-21T02:30:13+00:00"
},
{
"name": "evenement/evenement",
"version": "v3.0.1",
"source": {
"type": "git",
"url": "https://github.com/igorw/evenement.git",
"reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/igorw/evenement/zipball/531bfb9d15f8aa57454f5f0285b18bec903b8fb7",
"reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7",
"shasum": ""
},
"require": {
"php": ">=7.0"
},
"require-dev": {
"phpunit/phpunit": "^6.0"
},
"type": "library",
"autoload": {
"psr-0": {
"Evenement": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Igor Wiedler",
"email": "igor@wiedler.ch"
}
],
"description": "Événement is a very simple event dispatching library for PHP",
"keywords": [
"event-dispatcher",
"event-emitter"
],
"support": {
"issues": "https://github.com/igorw/evenement/issues",
"source": "https://github.com/igorw/evenement/tree/master"
},
"time": "2017-07-23T21:35:13+00:00"
},
{
"name": "guzzlehttp/guzzle",
"version": "7.2.0",
@ -1093,6 +1140,570 @@
},
"time": "2019-03-08T08:55:37+00:00"
},
{
"name": "ratchet/pawl",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/ratchetphp/Pawl.git",
"reference": "89ec703c76dc893484a2a0ed44b48a37d445abd5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ratchetphp/Pawl/zipball/89ec703c76dc893484a2a0ed44b48a37d445abd5",
"reference": "89ec703c76dc893484a2a0ed44b48a37d445abd5",
"shasum": ""
},
"require": {
"evenement/evenement": "^3.0 || ^2.0",
"php": ">=5.4",
"ratchet/rfc6455": "^0.3",
"react/socket": "^1.0 || ^0.8 || ^0.7"
},
"require-dev": {
"phpunit/phpunit": "~4.8"
},
"suggest": {
"reactivex/rxphp": "~2.0"
},
"default-branch": true,
"type": "library",
"autoload": {
"psr-4": {
"Ratchet\\Client\\": "src"
},
"files": [
"src/functions_include.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Asynchronous WebSocket client",
"keywords": [
"Ratchet",
"async",
"client",
"websocket",
"websocket client"
],
"support": {
"issues": "https://github.com/ratchetphp/Pawl/issues",
"source": "https://github.com/ratchetphp/Pawl/tree/v0.3.5"
},
"time": "2020-07-17T15:32:47+00:00"
},
{
"name": "ratchet/rfc6455",
"version": "v0.3",
"source": {
"type": "git",
"url": "https://github.com/ratchetphp/RFC6455.git",
"reference": "c8651c7938651c2d55f5d8c2422ac5e57a183341"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ratchetphp/RFC6455/zipball/c8651c7938651c2d55f5d8c2422ac5e57a183341",
"reference": "c8651c7938651c2d55f5d8c2422ac5e57a183341",
"shasum": ""
},
"require": {
"guzzlehttp/psr7": "^1.0",
"php": ">=5.4.2"
},
"require-dev": {
"phpunit/phpunit": "5.7.*",
"react/socket": "^1.3"
},
"type": "library",
"autoload": {
"psr-4": {
"Ratchet\\RFC6455\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Chris Boden",
"email": "cboden@gmail.com",
"role": "Developer"
},
{
"name": "Matt Bonneau",
"role": "Developer"
}
],
"description": "RFC6455 WebSocket protocol handler",
"homepage": "http://socketo.me",
"keywords": [
"WebSockets",
"rfc6455",
"websocket"
],
"support": {
"chat": "https://gitter.im/reactphp/reactphp",
"issues": "https://github.com/ratchetphp/RFC6455/issues",
"source": "https://github.com/ratchetphp/RFC6455/tree/v0.3"
},
"time": "2020-05-15T18:31:24+00:00"
},
{
"name": "react/cache",
"version": "v1.1.1",
"source": {
"type": "git",
"url": "https://github.com/reactphp/cache.git",
"reference": "4bf736a2cccec7298bdf745db77585966fc2ca7e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/reactphp/cache/zipball/4bf736a2cccec7298bdf745db77585966fc2ca7e",
"reference": "4bf736a2cccec7298bdf745db77585966fc2ca7e",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
"react/promise": "^3.0 || ^2.0 || ^1.1"
},
"require-dev": {
"phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35"
},
"type": "library",
"autoload": {
"psr-4": {
"React\\Cache\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Christian Lück",
"email": "christian@clue.engineering",
"homepage": "https://clue.engineering/"
},
{
"name": "Cees-Jan Kiewiet",
"email": "reactphp@ceesjankiewiet.nl",
"homepage": "https://wyrihaximus.net/"
},
{
"name": "Jan Sorgalla",
"email": "jsorgalla@gmail.com",
"homepage": "https://sorgalla.com/"
},
{
"name": "Chris Boden",
"email": "cboden@gmail.com",
"homepage": "https://cboden.dev/"
}
],
"description": "Async, Promise-based cache interface for ReactPHP",
"keywords": [
"cache",
"caching",
"promise",
"reactphp"
],
"support": {
"issues": "https://github.com/reactphp/cache/issues",
"source": "https://github.com/reactphp/cache/tree/v1.1.1"
},
"funding": [
{
"url": "https://github.com/WyriHaximus",
"type": "github"
},
{
"url": "https://github.com/clue",
"type": "github"
}
],
"time": "2021-02-02T06:47:52+00:00"
},
{
"name": "react/dns",
"version": "v1.4.0",
"source": {
"type": "git",
"url": "https://github.com/reactphp/dns.git",
"reference": "665260757171e2ab17485b44e7ffffa7acb6ca1f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/reactphp/dns/zipball/665260757171e2ab17485b44e7ffffa7acb6ca1f",
"reference": "665260757171e2ab17485b44e7ffffa7acb6ca1f",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
"react/cache": "^1.0 || ^0.6 || ^0.5",
"react/event-loop": "^1.0 || ^0.5",
"react/promise": "^3.0 || ^2.7 || ^1.2.1",
"react/promise-timer": "^1.2"
},
"require-dev": {
"clue/block-react": "^1.2",
"phpunit/phpunit": "^9.3 || ^4.8.35"
},
"type": "library",
"autoload": {
"psr-4": {
"React\\Dns\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Christian Lück",
"email": "christian@clue.engineering",
"homepage": "https://clue.engineering/"
},
{
"name": "Cees-Jan Kiewiet",
"email": "reactphp@ceesjankiewiet.nl",
"homepage": "https://wyrihaximus.net/"
},
{
"name": "Jan Sorgalla",
"email": "jsorgalla@gmail.com",
"homepage": "https://sorgalla.com/"
},
{
"name": "Chris Boden",
"email": "cboden@gmail.com",
"homepage": "https://cboden.dev/"
}
],
"description": "Async DNS resolver for ReactPHP",
"keywords": [
"async",
"dns",
"dns-resolver",
"reactphp"
],
"support": {
"issues": "https://github.com/reactphp/dns/issues",
"source": "https://github.com/reactphp/dns/tree/v1.4.0"
},
"funding": [
{
"url": "https://github.com/WyriHaximus",
"type": "github"
},
{
"url": "https://github.com/clue",
"type": "github"
}
],
"time": "2020-09-18T12:12:55+00:00"
},
{
"name": "react/event-loop",
"version": "v1.1.1",
"source": {
"type": "git",
"url": "https://github.com/reactphp/event-loop.git",
"reference": "6d24de090cd59cfc830263cfba965be77b563c13"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/reactphp/event-loop/zipball/6d24de090cd59cfc830263cfba965be77b563c13",
"reference": "6d24de090cd59cfc830263cfba965be77b563c13",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35"
},
"suggest": {
"ext-event": "~1.0 for ExtEventLoop",
"ext-pcntl": "For signal handling support when using the StreamSelectLoop",
"ext-uv": "* for ExtUvLoop"
},
"type": "library",
"autoload": {
"psr-4": {
"React\\EventLoop\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.",
"keywords": [
"asynchronous",
"event-loop"
],
"support": {
"issues": "https://github.com/reactphp/event-loop/issues",
"source": "https://github.com/reactphp/event-loop/tree/v1.1.1"
},
"time": "2020-01-01T18:39:52+00:00"
},
{
"name": "react/promise",
"version": "2.x-dev",
"source": {
"type": "git",
"url": "https://github.com/reactphp/promise.git",
"reference": "a9752a861e21c0fe0b380c9f9e55beddc0ed7d31"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/reactphp/promise/zipball/a9752a861e21c0fe0b380c9f9e55beddc0ed7d31",
"reference": "a9752a861e21c0fe0b380c9f9e55beddc0ed7d31",
"shasum": ""
},
"require": {
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.36"
},
"type": "library",
"autoload": {
"psr-4": {
"React\\Promise\\": "src/"
},
"files": [
"src/functions_include.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jan Sorgalla",
"email": "jsorgalla@gmail.com"
}
],
"description": "A lightweight implementation of CommonJS Promises/A for PHP",
"keywords": [
"promise",
"promises"
],
"support": {
"issues": "https://github.com/reactphp/promise/issues",
"source": "https://github.com/reactphp/promise/tree/2.x"
},
"funding": [
{
"url": "https://github.com/WyriHaximus",
"type": "github"
},
{
"url": "https://github.com/clue",
"type": "github"
}
],
"time": "2021-02-09T15:06:50+00:00"
},
{
"name": "react/promise-timer",
"version": "v1.6.0",
"source": {
"type": "git",
"url": "https://github.com/reactphp/promise-timer.git",
"reference": "daee9baf6ef30c43ea4c86399f828bb5f558f6e6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/reactphp/promise-timer/zipball/daee9baf6ef30c43ea4c86399f828bb5f558f6e6",
"reference": "daee9baf6ef30c43ea4c86399f828bb5f558f6e6",
"shasum": ""
},
"require": {
"php": ">=5.3",
"react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5",
"react/promise": "^3.0 || ^2.7.0 || ^1.2.1"
},
"require-dev": {
"phpunit/phpunit": "^9.0 || ^5.7 || ^4.8.35"
},
"type": "library",
"autoload": {
"psr-4": {
"React\\Promise\\Timer\\": "src/"
},
"files": [
"src/functions_include.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Christian Lück",
"email": "christian@lueck.tv"
}
],
"description": "A trivial implementation of timeouts for Promises, built on top of ReactPHP.",
"homepage": "https://github.com/reactphp/promise-timer",
"keywords": [
"async",
"event-loop",
"promise",
"reactphp",
"timeout",
"timer"
],
"support": {
"issues": "https://github.com/reactphp/promise-timer/issues",
"source": "https://github.com/reactphp/promise-timer/tree/v1.6.0"
},
"time": "2020-07-10T12:18:06+00:00"
},
{
"name": "react/socket",
"version": "v1.6.0",
"source": {
"type": "git",
"url": "https://github.com/reactphp/socket.git",
"reference": "e2b96b23a13ca9b41ab343268dbce3f8ef4d524a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/reactphp/socket/zipball/e2b96b23a13ca9b41ab343268dbce3f8ef4d524a",
"reference": "e2b96b23a13ca9b41ab343268dbce3f8ef4d524a",
"shasum": ""
},
"require": {
"evenement/evenement": "^3.0 || ^2.0 || ^1.0",
"php": ">=5.3.0",
"react/dns": "^1.1",
"react/event-loop": "^1.0 || ^0.5",
"react/promise": "^2.6.0 || ^1.2.1",
"react/promise-timer": "^1.4.0",
"react/stream": "^1.1"
},
"require-dev": {
"clue/block-react": "^1.2",
"phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35",
"react/promise-stream": "^1.2"
},
"type": "library",
"autoload": {
"psr-4": {
"React\\Socket\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Christian Lück",
"email": "christian@clue.engineering",
"homepage": "https://clue.engineering/"
},
{
"name": "Cees-Jan Kiewiet",
"email": "reactphp@ceesjankiewiet.nl",
"homepage": "https://wyrihaximus.net/"
},
{
"name": "Jan Sorgalla",
"email": "jsorgalla@gmail.com",
"homepage": "https://sorgalla.com/"
},
{
"name": "Chris Boden",
"email": "cboden@gmail.com",
"homepage": "https://cboden.dev/"
}
],
"description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP",
"keywords": [
"Connection",
"Socket",
"async",
"reactphp",
"stream"
],
"support": {
"issues": "https://github.com/reactphp/socket/issues",
"source": "https://github.com/reactphp/socket/tree/v1.6.0"
},
"funding": [
{
"url": "https://github.com/WyriHaximus",
"type": "github"
},
{
"url": "https://github.com/clue",
"type": "github"
}
],
"time": "2020-08-28T12:49:05+00:00"
},
{
"name": "react/stream",
"version": "v1.1.1",
"source": {
"type": "git",
"url": "https://github.com/reactphp/stream.git",
"reference": "7c02b510ee3f582c810aeccd3a197b9c2f52ff1a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/reactphp/stream/zipball/7c02b510ee3f582c810aeccd3a197b9c2f52ff1a",
"reference": "7c02b510ee3f582c810aeccd3a197b9c2f52ff1a",
"shasum": ""
},
"require": {
"evenement/evenement": "^3.0 || ^2.0 || ^1.0",
"php": ">=5.3.8",
"react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5"
},
"require-dev": {
"clue/stream-filter": "~1.2",
"phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35"
},
"type": "library",
"autoload": {
"psr-4": {
"React\\Stream\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP",
"keywords": [
"event-driven",
"io",
"non-blocking",
"pipe",
"reactphp",
"readable",
"stream",
"writable"
],
"support": {
"issues": "https://github.com/reactphp/stream/issues",
"source": "https://github.com/reactphp/stream/tree/v1.1.1"
},
"time": "2020-05-04T10:17:57+00:00"
},
{
"name": "resque/php-resque",
"version": "v1.3.6",
@ -2545,9 +3156,9 @@
],
"support": {
"issues": "https://github.com/felixfbecker/php-language-server-protocol/issues",
"source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.0"
"source": "https://github.com/felixfbecker/php-language-server-protocol/tree/1.5.1"
},
"time": "2020-10-23T13:55:30+00:00"
"time": "2021-02-22T14:02:09+00:00"
},
{
"name": "matthiasmullie/minify",
@ -3375,7 +3986,7 @@
"type": "github"
}
],
"time": "2021-02-14T06:52:34+00:00"
"time": "2021-02-23T15:48:43+00:00"
},
{
"name": "phpunit/php-invoker",
@ -3439,7 +4050,7 @@
"type": "github"
}
],
"time": "2021-02-14T06:52:42+00:00"
"time": "2021-02-23T15:48:51+00:00"
},
{
"name": "phpunit/php-text-template",
@ -3499,7 +4110,7 @@
"type": "github"
}
],
"time": "2021-02-14T06:53:15+00:00"
"time": "2021-02-23T15:49:24+00:00"
},
{
"name": "phpunit/php-timer",
@ -3559,7 +4170,7 @@
"type": "github"
}
],
"time": "2021-02-14T06:52:50+00:00"
"time": "2021-02-23T15:48:59+00:00"
},
{
"name": "phpunit/phpunit",
@ -3773,7 +4384,7 @@
"type": "github"
}
],
"time": "2021-02-14T06:53:40+00:00"
"time": "2021-02-23T15:49:50+00:00"
},
{
"name": "sebastian/code-unit",
@ -3885,7 +4496,7 @@
"type": "github"
}
],
"time": "2021-02-14T06:51:27+00:00"
"time": "2021-02-23T15:47:39+00:00"
},
{
"name": "sebastian/comparator",
@ -3960,7 +4571,7 @@
"type": "github"
}
],
"time": "2021-02-14T06:51:35+00:00"
"time": "2021-02-23T15:47:47+00:00"
},
{
"name": "sebastian/complexity",
@ -4084,7 +4695,7 @@
"type": "github"
}
],
"time": "2021-02-14T06:51:43+00:00"
"time": "2021-02-23T15:47:55+00:00"
},
{
"name": "sebastian/environment",
@ -4148,7 +4759,7 @@
"type": "github"
}
],
"time": "2021-02-14T06:51:52+00:00"
"time": "2021-02-23T15:48:03+00:00"
},
{
"name": "sebastian/exporter",
@ -4226,7 +4837,7 @@
"type": "github"
}
],
"time": "2021-02-14T06:52:00+00:00"
"time": "2021-02-23T15:48:12+00:00"
},
{
"name": "sebastian/global-state",
@ -4291,7 +4902,7 @@
"type": "github"
}
],
"time": "2021-02-14T06:52:09+00:00"
"time": "2021-02-23T15:48:19+00:00"
},
{
"name": "sebastian/lines-of-code",
@ -4406,7 +5017,7 @@
"type": "github"
}
],
"time": "2021-02-14T06:52:17+00:00"
"time": "2021-02-23T15:48:28+00:00"
},
{
"name": "sebastian/object-reflector",
@ -4462,7 +5073,7 @@
"type": "github"
}
],
"time": "2021-02-14T06:52:26+00:00"
"time": "2021-02-23T15:48:35+00:00"
},
{
"name": "sebastian/recursion-context",
@ -4526,7 +5137,7 @@
"type": "github"
}
],
"time": "2021-02-14T06:52:58+00:00"
"time": "2021-02-23T15:49:08+00:00"
},
{
"name": "sebastian/resource-operations",
@ -4639,7 +5250,7 @@
"type": "github"
}
],
"time": "2021-02-14T06:53:07+00:00"
"time": "2021-02-23T15:49:16+00:00"
},
{
"name": "sebastian/version",
@ -4828,7 +5439,7 @@
"type": "tidelift"
}
],
"time": "2021-02-17T15:27:35+00:00"
"time": "2021-02-23T10:10:15+00:00"
},
{
"name": "symfony/polyfill-ctype",
@ -5347,7 +5958,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "2.3-dev"
"dev-main": "2.4-dev"
},
"thanks": {
"name": "symfony/contracts",
@ -5400,7 +6011,7 @@
"type": "tidelift"
}
],
"time": "2021-01-27T16:27:53+00:00"
"time": "2021-02-25T16:38:04+00:00"
},
{
"name": "symfony/string",
@ -5613,7 +6224,7 @@
"type": "tidelift"
}
],
"time": "2021-02-08T09:50:07+00:00"
"time": "2021-02-22T11:56:05+00:00"
},
{
"name": "vimeo/psalm",
@ -5777,7 +6388,7 @@
"issues": "https://github.com/webmozarts/assert/issues",
"source": "https://github.com/webmozarts/assert/tree/master"
},
"time": "2021-01-18T12:52:36+00:00"
"time": "2021-02-28T20:01:57+00:00"
},
{
"name": "webmozart/path-util",

View file

@ -0,0 +1,44 @@
<?php
namespace Tests\E2E\Services\Realtime;
use Ratchet;
use Ratchet\RFC6455\Messaging\MessageInterface;
trait RealtimeBase
{
private function getWebsocket($channels = []) {
$query = [
'project' => $this->getProject()['$id'],
'channels' => $channels
];
return 'ws://appwrite-traefik/v1/realtime?' . http_build_query($query);
}
public function testHandshake()
{
/**
* Test for SUCCESS
*/
Ratchet\Client\connect($this->getWebsocket(['documents']), [], ['origin' => 'appwrite.test'])->then(function($conn) {
$conn->on('message', function(MessageInterface $msg) use ($conn) {
$this->assertEquals('{"documents":0}', $msg->__toString());
$conn->close();
});
}, function ($e) {
echo "Could not connect: {$e->getMessage()}\n";
});
/**
* Test for FAILURE
*/
Ratchet\Client\connect($this->getWebsocket(['account']), [], ['origin' => 'appwrite.test'])->then(function($conn) {
$conn->on('message', function(Message $msg) use ($conn) {
$this->assertEquals('Missing channels', $msg->__toString());
$conn->close();
});
}, function ($e) {
echo "Could not connect: {$e->getMessage()}\n";
});
}
}

View file

@ -0,0 +1,15 @@
<?php
namespace Tests\E2E\Services\Realtime;
use Tests\E2E\Client;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\SideClient;
class RealtimeCustomClientTest extends Scope
{
use RealtimeBase;
use ProjectCustom;
use SideClient;
}