1
0
Fork 0
mirror of synced 2024-05-20 04:32:37 +12:00

Updated session structure

This commit is contained in:
Eldad Fux 2020-08-10 06:52:19 +03:00
parent 71ebad65b9
commit d6ad0779b3
7 changed files with 342 additions and 105 deletions

View file

@ -337,6 +337,123 @@ $collections = [
'required' => true,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'OS Code',
'key' => 'osCode',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'OS Name',
'key' => 'osName',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'OS Version',
'key' => 'osVersion',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Client Type',
'key' => 'clientType',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Client Code',
'key' => 'clientCode',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Client Name',
'key' => 'clientName',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Client Version',
'key' => 'clientVersion',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Client Engine',
'key' => 'clientEngine',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Client Engine Version',
'key' => 'clientEngineVersion',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Device Name',
'key' => 'deviceName',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Device Brand',
'key' => 'deviceBrand',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Device Model',
'key' => 'deviceModel',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Country Code',
'key' => 'countryCode',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
],
],
Database::SYSTEM_COLLECTION_MEMBERSHIPS => [

View file

@ -141,10 +141,12 @@ App::post('/v1/account/sessions')
->label('abuse-key', 'url:{url},email:{param-email}')
->param('email', '', function () { return new Email(); }, 'User email.')
->param('password', '', function () { return new Password(); }, 'User password. Must be between 6 to 32 chars.')
->action(function ($email, $password, $request, $response, $projectDB, $webhooks, $audits) {
->action(function ($email, $password, $request, $response, $projectDB, $locale, $geodb, $webhooks, $audits) {
/** @var Appwrite\Swoole\Request $request */
/** @var Appwrite\Swoole\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Utopia\Locale\Locale $locale */
/** @var GeoIp2\Database\Reader $geodb */
/** @var Appwrite\Event\Event $webhooks */
/** @var Appwrite\Event\Event $audits */
@ -167,6 +169,23 @@ App::post('/v1/account/sessions')
throw new Exception('Invalid credentials', 401); // Wrong password or username
}
$dd = new DeviceDetector($request->getUserAgent('UNKNOWN'));
$dd->parse();
$os = $dd->getOs();
$osCode = (isset($os['short_name'])) ? $os['short_name'] : '';
$osName = (isset($os['name'])) ? $os['name'] : '';
$osVersion = (isset($os['version'])) ? $os['version'] : '';
$client = $dd->getClient();
$clientType = (isset($client['type'])) ? $client['type'] : '';
$clientCode = (isset($client['short_name'])) ? $client['short_name'] : '';
$clientName = (isset($client['name'])) ? $client['name'] : '';
$clientVersion = (isset($client['version'])) ? $client['version'] : '';
$clientEngine = (isset($client['engine'])) ? $client['engine'] : '';
$clientEngineVersion = (isset($client['engine_version'])) ? $client['engine_version'] : '';
$expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG;
$secret = Auth::tokenGenerator();
$session = new Document([
@ -177,8 +196,32 @@ App::post('/v1/account/sessions')
'expire' => $expiry,
'userAgent' => $request->getUserAgent('UNKNOWN'),
'ip' => $request->getIP(),
'osCode' => $osCode,
'osName' => $osName,
'osVersion' => $osVersion,
'clientType' => $clientType,
'clientCode' => $clientCode,
'clientName' => $clientName,
'clientVersion' => $clientVersion,
'clientEngine' => $clientEngine,
'clientEngineVersion' => $clientEngineVersion,
'deviceName' => $dd->getDeviceName(),
'deviceBrand' => $dd->getBrandName(),
'deviceModel' => $dd->getModel(),
]);
try {
$record = $geodb->country($request->getIP());
$session
->setAttribute('countryCode', \strtolower($record->country->isoCode))
;
} catch (\Exception $e) {
$session
->setAttribute('countryCode', '--')
;
}
Authorization::setRole('user:'.$profile->getId());
$session = $projectDB->createDocument($session->getArrayCopy());
@ -219,9 +262,14 @@ App::post('/v1/account/sessions')
->addCookie(Auth::$cookieName, Auth::encodeSession($profile->getId(), $secret), $expiry, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'))
->setStatusCode(Response::STATUS_CODE_CREATED)
;
$session
->setAttribute('current', true)
->setAttribute('countryName', (isset($countries[$session->getAttribute('countryCode')])) ? $countries[$session->getAttribute('countryCode')] : $locale->getText('locale.country.unknown'))
;
$response->dynamic($session, Response::MODEL_SESSION);
}, ['request', 'response', 'projectDB', 'webhooks', 'audits']);
}, ['request', 'response', 'projectDB', 'locale', 'geodb', 'webhooks', 'audits']);
App::get('/v1/account/sessions/oauth2/:provider')
->desc('Create Account Session with OAuth2')
@ -569,58 +617,34 @@ App::get('/v1/account/sessions')
->label('sdk.namespace', 'account')
->label('sdk.method', 'getSessions')
->label('sdk.description', '/docs/references/account/get-sessions.md')
->action(function ($response, $user, $locale, $geodb) {
->action(function ($response, $user, $locale) {
/** @var Appwrite\Swoole\Response $response */
/** @var Appwrite\Database\Document $user */
/** @var Utopia\Locale\Locale $locale */
/** @var GeoIp2\Database\Reader $geodb */
$tokens = $user->getAttribute('tokens', []);
$sessions = [];
$current = Auth::tokenVerify($tokens, Auth::TOKEN_TYPE_LOGIN, Auth::$secret);
$index = 0;
$countries = $locale->getText('countries');
$current = Auth::tokenVerify($tokens, Auth::TOKEN_TYPE_LOGIN, Auth::$secret);
foreach ($tokens as $token) { /* @var $token Document */
if (Auth::TOKEN_TYPE_LOGIN != $token->getAttribute('type')) {
continue;
}
$userAgent = (!empty($token->getAttribute('userAgent'))) ? $token->getAttribute('userAgent') : 'UNKNOWN';
$token->setAttribute('countryName', (isset($countries[$token->getAttribute('contryCode')]))
? $countries[$token->getAttribute('contryCode')]
: $locale->getText('locale.country.unknown'));
$token->setAttribute('current', ($current == $token->getId()) ? true : false);
$dd = new DeviceDetector($userAgent);
// OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
// $dd->skipBotDetection();
$dd->parse();
$sessions[$index] = [
'$id' => $token->getId(),
'OS' => $dd->getOs(),
'client' => $dd->getClient(),
'device' => $dd->getDevice(),
'brand' => $dd->getBrand(),
'model' => $dd->getModel(),
'ip' => $token->getAttribute('ip', ''),
'geo' => [],
'current' => ($current == $token->getId()) ? true : false,
];
try {
$record = $geodb->country($token->getAttribute('ip', ''));
$sessions[$index]['geo']['isoCode'] = \strtolower($record->country->isoCode);
$sessions[$index]['geo']['country'] = (isset($countries[$record->country->isoCode])) ? $countries[$record->country->isoCode] : $locale->getText('locale.country.unknown');
} catch (\Exception $e) {
$sessions[$index]['geo']['isoCode'] = '--';
$sessions[$index]['geo']['country'] = $locale->getText('locale.country.unknown');
}
++$index;
$sessions[] = $token;
}
$response->json($sessions);
}, ['response', 'user', 'locale', 'geodb']);
$response->dynamic(new Document([
'sum' => count($sessions),
'sessions' => $sessions
]), Response::MODEL_SESSION_LIST);
}, ['response', 'user', 'locale']);
App::get('/v1/account/logs')
->desc('Get Account Logs')

View file

@ -158,11 +158,10 @@ App::get('/v1/users/:userId/sessions')
->label('sdk.method', 'getSessions')
->label('sdk.description', '/docs/references/users/get-user-sessions.md')
->param('userId', '', function () { return new UID(); }, 'User unique ID.')
->action(function ($userId, $response, $projectDB, $locale, $geodb) {
->action(function ($userId, $response, $projectDB, $locale) {
/** @var Appwrite\Swoole\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Utopia\Locale\Locale $locale */
/** @var GeoIp2\Database\Reader $geodb */
$user = $projectDB->getDocument($userId);
@ -172,7 +171,6 @@ App::get('/v1/users/:userId/sessions')
$tokens = $user->getAttribute('tokens', []);
$sessions = [];
$index = 0;
$countries = $locale->getText('countries');
foreach ($tokens as $token) { /* @var $token Document */
@ -180,40 +178,19 @@ App::get('/v1/users/:userId/sessions')
continue;
}
$userAgent = (!empty($token->getAttribute('userAgent'))) ? $token->getAttribute('userAgent') : 'UNKNOWN';
$token->setAttribute('countryName', (isset($countries[$token->getAttribute('contryCode')]))
? $countries[$token->getAttribute('contryCode')]
: $locale->getText('locale.country.unknown'));
$token->setAttribute('current', false);
$dd = new DeviceDetector($userAgent);
// OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
// $dd->skipBotDetection();
$dd->parse();
$sessions[$index] = [
'$id' => $token->getId(),
'OS' => $dd->getOs(),
'client' => $dd->getClient(),
'device' => $dd->getDevice(),
'brand' => $dd->getBrand(),
'model' => $dd->getModel(),
'ip' => $token->getAttribute('ip', ''),
'geo' => [],
];
try {
$record = $geodb->country($token->getAttribute('ip', ''));
$sessions[$index]['geo']['isoCode'] = \strtolower($record->country->isoCode);
$sessions[$index]['geo']['country'] = (isset($countries[$record->country->isoCode])) ? $countries[$record->country->isoCode] : $locale->getText('locale.country.unknown');
} catch (\Exception $e) {
$sessions[$index]['geo']['isoCode'] = '--';
$sessions[$index]['geo']['country'] = $locale->getText('locale.country.unknown');
}
++$index;
$sessions[] = $token;
}
$response->json($sessions);
}, ['response', 'projectDB', 'locale', 'geodb']);
$response->dynamic(new Document([
'sum' => count($sessions),
'sessions' => $sessions
]), Response::MODEL_SESSION_LIST);
}, ['response', 'projectDB', 'locale']);
App::get('/v1/users/:userId/logs')
->desc('Get User Logs')

View file

@ -192,7 +192,7 @@
data-name="sessions"
data-event="load,account.deleteRemoteSession">
<ul data-ls-loop="sessions" data-ls-as="session" class="list">
<ul data-ls-loop="sessions.sessions" data-ls-as="session" class="list">
<li class="clear">
<span data-ls-if="true != {{session.current}}">
<!-- From remote session (-logout event) -->
@ -236,17 +236,17 @@
</form>
</span>
<img data-ls-attrs="src={{env.API}}/avatars/browsers/{{session.client.short_name|lowercase}}?width=120&height=120&project={{env.PROJECT}},title={{session.client.name}},alt={{session.client.name}}" class="avatar trans pull-start margin-end" />
<img data-ls-attrs="src={{env.API}}/avatars/browsers/{{session.clientCode|lowercase}}?width=120&height=120&project={{env.PROJECT}},title={{session.clientName}},alt={{session.clientName}}" class="avatar trans pull-start margin-end" />
<span data-ls-bind="{{session.client.name}}"></span> <span data-ls-bind="{{session.client.version}}"></span> on <span data-ls-bind="{{session.model}}"></span> <span data-ls-bind="{{session.OS.name}}"></span> <span data-ls-bind="{{session.OS.version}}"></span>
<span data-ls-bind="{{session.clientName}}"></span> <span data-ls-bind="{{session.clientVersion}}"></span> on <span data-ls-bind="{{session.deviceModel}}"></span> <span data-ls-bind="{{session.osName}}"></span> <span data-ls-bind="{{session.osVersion}}"></span>
&nbsp;
<span data-ls-if="true == {{session.current}}">
<span class="tag green">Current Session</span>
</span>
<div class="margin-top-small">
<img data-ls-attrs="src={{env.API}}/avatars/flags/{{session.geo.isoCode}}?width=80&height=80&project={{env.PROJECT}}" class="avatar xxs margin-end-small inline" />
<small data-ls-bind="{{session.ip}}"></small> / <small data-ls-bind="{{session.geo.country}}"></small>
<img data-ls-attrs="src={{env.API}}/avatars/flags/{{session.countryCode}}?width=80&height=80&project={{env.PROJECT}}" class="avatar xxs margin-end-small inline" />
<small data-ls-bind="{{session.ip}}"></small> / <small data-ls-bind="{{session.countryName}}"></small>
</div>
</li>
</ul>

View file

@ -153,13 +153,13 @@
data-param-user-id="{{router.params.id}}"
data-event="load,users.update">
<div data-ls-if="{{sessions.length}} === 0" style="display: none" class="margin-top-xxl margin-bottom-xxl text-align-center">
<div data-ls-if="{{sessions.sessions.length}} === 0" style="display: none" class="margin-top-xxl margin-bottom-xxl text-align-center">
No sessions available.
</div>
<div data-ls-if="{{sessions.length}} !== 0" style="display: none">
<div data-ls-if="{{sessions.sessions.length}} !== 0" style="display: none">
<div class="box margin-bottom">
<ul data-ls-loop="sessions" data-ls-as="session" class="list">
<ul data-ls-loop="sessions.sessions" data-ls-as="session" class="list">
<li class="clear">
<form class="pull-end"
data-analytics-event="submit"
@ -179,13 +179,13 @@
<button class="danger">Logout</button>
</form>
<img data-ls-attrs="src={{env.API}}/avatars/browsers/{{session.client.short_name|lowercase}}?width=120&height=120&project={{env.PROJECT}},title={{session.client.name}},alt={{session.client.name}}" class="avatar trans pull-start margin-end" />
<img data-ls-attrs="src={{env.API}}/avatars/browsers/{{session.clientCode|lowercase}}?width=120&height=120&project={{env.PROJECT}},title={{session.clientName}},alt={{session.clientName}}" class="avatar trans pull-start margin-end" />
<span data-ls-bind="{{session.client.name}}"></span> <span data-ls-bind="{{session.client.version}}"></span> on <span data-ls-bind="{{session.model}}"></span> <span data-ls-bind="{{session.OS.name}}"></span> <span data-ls-bind="{{session.OS.version}}"></span>
<span data-ls-bind="{{session.clientName}}"></span> <span data-ls-bind="{{session.clientVersion}}"></span> on <span data-ls-bind="{{session.deviceModel}}"></span> <span data-ls-bind="{{session.osName}}"></span> <span data-ls-bind="{{session.osVersion}}"></span>
<div class="margin-top-small">
<img data-ls-attrs="src={{env.API}}/avatars/flags/{{session.geo.isoCode}}?width=80&height=80&project={{env.PROJECT}}" class="avatar xxs margin-end-small inline" />
<small data-ls-bind="{{session.ip}}"></small> / <small data-ls-bind="{{session.geo.country}}"></small>
<img data-ls-attrs="src={{env.API}}/avatars/flags/{{session.countryCode}}?width=80&height=80&project={{env.PROJECT}}" class="avatar xxs margin-end-small inline" />
<small data-ls-bind="{{session.ip}}"></small> / <small data-ls-bind="{{session.countryName}}"></small>
</div>
</li>
</ul>

122
docs/lists/clients.json Normal file
View file

@ -0,0 +1,122 @@
{
"36": "360 Phone Browser",
"3B": "360 Browser",
"AA": "Avant Browser",
"AB": "ABrowse",
"AG": "ANTGalio",
"AM": "Amaya",
"AO": "Amigo",
"AN": "Android Browser",
"AR": "Arora",
"AV": "Amiga Voyager",
"AW": "Amiga Aweb",
"BB": "BlackBerry Browser",
"BD": "Baidu Browser",
"BS": "Baidu Spark",
"BE": "Beonex",
"BJ": "Bunjalloo",
"BX": "BrowseX",
"CA": "Camino",
"CC": "Coc Coc",
"CD": "Comodo Dragon",
"CX": "Charon",
"CF": "Chrome Frame",
"CH": "Chrome",
"CI": "Chrome Mobile iOS",
"CK": "Conkeror",
"CM": "Chrome Mobile",
"CN": "CoolNovo",
"CO": "CometBird",
"CP": "ChromePlus",
"CR": "Chromium",
"CS": "Cheshire",
"DE": "Deepnet Explorer",
"DF": "Dolphin",
"DI": "Dillo",
"EL": "Elinks",
"EP": "Epiphany",
"ES": "Espial TV Browser",
"FB": "Firebird",
"FD": "Fluid",
"FE": "Fennec",
"FF": "Firefox",
"FL": "Flock",
"FN": "Fireweb Navigator",
"GA": "Galeon",
"GE": "Google Earth",
"HJ": "HotJava",
"IA": "Iceape",
"IB": "IBrowse",
"IC": "iCab",
"ID": "IceDragon",
"IW": "Iceweasel",
"IE": "Internet Explorer",
"IM": "IE Mobile",
"IR": "Iron",
"JS": "Jasmine",
"KI": "Kindle Browser",
"KM": "K-meleon",
"KO": "Konqueror",
"KP": "Kapiko",
"KY": "Kylo",
"KZ": "Kazehakase",
"LB": "Liebao",
"LI": "Links",
"LS": "Lunascape",
"LX": "Lynx",
"MB": "MicroB",
"MC": "NCSA Mosaic",
"ME": "Mercury",
"MF": "Mobile Safari",
"MI": "Midori",
"MU": "MIUI Browser",
"MS": "Mobile Silk",
"MX": "Maxthon",
"NB": "Nokia Browser",
"NO": "Nokia OSS Browser",
"NV": "Nokia Ovi Browser",
"NF": "NetFront",
"NL": "NetFront Life",
"NP": "NetPositive",
"NS": "Netscape",
"OB": "Obigo",
"OD": "Odyssey Web Browser",
"OF": "Off By One",
"OE": "ONE Browser",
"OI": "Opera Mini",
"OM": "Opera Mobile",
"OP": "Opera",
"ON": "Opera Next",
"OR": "Oregano",
"OV": "Openwave Mobile Browser",
"OW": "OmniWeb",
"PL": "Palm Blazer",
"PM": "Pale Moon",
"PR": "Palm Pre",
"PU": "Puffin",
"PW": "Palm WebPro",
"PX": "Phoenix",
"PO": "Polaris",
"PS": "Microsoft Edge",
"QQ": "QQ Browser",
"RK": "Rekonq",
"RM": "RockMelt",
"SA": "Sailfish Browser",
"SC": "SEMC-Browser",
"SE": "Sogou Explorer",
"SF": "Safari",
"SH": "Shiira",
"SL": "Sleipnir",
"SM": "SeaMonkey",
"SN": "Snowshoe",
"SR": "Sunrise",
"SX": "Swiftfox",
"TZ": "Tizen Browser",
"UC": "UC Browser",
"VI": "Vivaldi",
"WE": "WebPositive",
"WO": "wOSBrowser",
"WT": "WeTab Browser",
"YA": "Yandex Browser",
"XI": "Xiino"
}

View file

@ -235,31 +235,28 @@ trait AccountBase
$this->assertEquals($response['headers']['status-code'], 200);
$this->assertIsArray($response['body']);
$this->assertNotEmpty($response['body']);
$this->assertCount(1, $response['body']);
$this->assertEquals($sessionId, $response['body'][0]['$id']);
$this->assertCount(2, $response['body']);
$this->assertEquals(1, $response['body']['sum']);
$this->assertEquals($sessionId, $response['body']['sessions'][0]['$id']);
$this->assertIsArray($response['body'][0]['OS']);
$this->assertEquals('Windows', $response['body'][0]['OS']['name']);
$this->assertEquals('WIN', $response['body'][0]['OS']['short_name']);
$this->assertEquals('10', $response['body'][0]['OS']['version']);
$this->assertEquals('x64', $response['body'][0]['OS']['platform']);
$this->assertEquals('Windows', $response['body']['sessions'][0]['osName']);
$this->assertEquals('WIN', $response['body']['sessions'][0]['osCode']);
$this->assertEquals('10', $response['body']['sessions'][0]['osVersion']);
$this->assertIsArray($response['body'][0]['client']);
$this->assertEquals('browser', $response['body'][0]['client']['type']);
$this->assertEquals('Chrome', $response['body'][0]['client']['name']);
$this->assertEquals('CH', $response['body'][0]['client']['short_name']); // FIXME (v1) key name should be camelcase
$this->assertEquals('70.0', $response['body'][0]['client']['version']);
$this->assertEquals('Blink', $response['body'][0]['client']['engine']);
$this->assertEquals(0, $response['body'][0]['device']);
$this->assertEquals('', $response['body'][0]['brand']);
$this->assertEquals('', $response['body'][0]['model']);
$this->assertEquals($response['body'][0]['ip'], filter_var($response['body'][0]['ip'], FILTER_VALIDATE_IP));
$this->assertEquals('browser', $response['body']['sessions'][0]['clientType']);
$this->assertEquals('Chrome', $response['body']['sessions'][0]['clientName']);
$this->assertEquals('CH', $response['body']['sessions'][0]['clientCode']);
$this->assertEquals('70.0', $response['body']['sessions'][0]['clientVersion']);
$this->assertEquals('Blink', $response['body']['sessions'][0]['clientEngine']);
$this->assertEquals('desktop', $response['body']['sessions'][0]['deviceName']);
$this->assertEquals('', $response['body']['sessions'][0]['deviceBrand']);
$this->assertEquals('', $response['body']['sessions'][0]['deviceModel']);
$this->assertEquals($response['body']['sessions'][0]['ip'], filter_var($response['body']['sessions'][0]['ip'], FILTER_VALIDATE_IP));
$this->assertIsArray($response['body'][0]['geo']);
$this->assertEquals('--', $response['body'][0]['geo']['isoCode']);
$this->assertEquals('Unknown', $response['body'][0]['geo']['country']);
$this->assertEquals('--', $response['body']['sessions'][0]['countryCode']);
$this->assertEquals('Unknown', $response['body']['sessions'][0]['countryName']);
$this->assertEquals(true, $response['body'][0]['current']);
$this->assertEquals(true, $response['body']['sessions'][0]['current']);
/**
* Test for FAILURE