1
0
Fork 0
mirror of synced 2024-09-30 01:08:13 +13:00

Merge remote-tracking branch 'origin/master' into feat-graphql-support

# Conflicts:
#	app/controllers/api/databases.php
#	composer.lock
This commit is contained in:
Jake Barnby 2022-07-08 17:50:16 +12:00
commit da71aec774
17 changed files with 210 additions and 47 deletions

View file

@ -1,3 +1,11 @@
# Version 0.15.2
## Bugs
- Fixed Realtime Authentication for the Console by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/3506
- Fixed Collection Usage by @stnguyen90 in https://github.com/appwrite/appwrite/pull/3505
- Fixed `$createdAt` after updating document by @Meldiron in https://github.com/appwrite/appwrite/pull/3498
- Fixed Redirect after deleting Collection in Console @TorstenDittmann in https://github.com/appwrite/appwrite/pull/3476
- Fixed broken Link for Documents under Collections by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/3469
# Version 0.15.1 # Version 0.15.1
## Bugs ## Bugs
- Fixed SMS for `createVerification` by @christyjacob4 in https://github.com/appwrite/appwrite/pull/3454 - Fixed SMS for `createVerification` by @christyjacob4 in https://github.com/appwrite/appwrite/pull/3454
@ -26,8 +34,8 @@
- `collections.[COLLECTION_ID]` is now `databases.[DATABASE_ID].collections.[COLLECTION_ID]` - `collections.[COLLECTION_ID]` is now `databases.[DATABASE_ID].collections.[COLLECTION_ID]`
- `collections.[COLLECTION_ID].documents.[DOCUMENT_ID]` is now `databases.[DATABASE_ID].collections.[COLLECTION_ID].documents.[DOCUMENT_ID]` - `collections.[COLLECTION_ID].documents.[DOCUMENT_ID]` is now `databases.[DATABASE_ID].collections.[COLLECTION_ID].documents.[DOCUMENT_ID]`
- Following Realtime Channels are changed: - Following Realtime Channels are changed:
- `collections.[COLLECTION_ID]` is now `databases.[DATABASE_ID].ollections.[COLLECTION_ID]` - `collections.[COLLECTION_ID]` is now `databases.[DATABASE_ID].collections.[COLLECTION_ID]`
- `collections.[COLLECTION_ID].documents` is now `databases.[DATABASE_ID].ollections.[COLLECTION_ID].documents` - `collections.[COLLECTION_ID].documents` is now `databases.[DATABASE_ID].collections.[COLLECTION_ID].documents`
- After Migration a Database called `default` is created for all your existing Database Collections - After Migration a Database called `default` is created for all your existing Database Collections
## Features ## Features

View file

@ -59,7 +59,7 @@ docker run -it --rm \
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
--entrypoint="install" \ --entrypoint="install" \
appwrite/appwrite:0.15.1 appwrite/appwrite:0.15.2
``` ```
### Windows ### Windows
@ -71,7 +71,7 @@ docker run -it --rm ^
--volume //var/run/docker.sock:/var/run/docker.sock ^ --volume //var/run/docker.sock:/var/run/docker.sock ^
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^ --volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
--entrypoint="install" ^ --entrypoint="install" ^
appwrite/appwrite:0.15.1 appwrite/appwrite:0.15.2
``` ```
#### PowerShell #### PowerShell
@ -81,7 +81,7 @@ docker run -it --rm ,
--volume /var/run/docker.sock:/var/run/docker.sock , --volume /var/run/docker.sock:/var/run/docker.sock ,
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw , --volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ,
--entrypoint="install" , --entrypoint="install" ,
appwrite/appwrite:0.15.1 appwrite/appwrite:0.15.2
``` ```
运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。 运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。

View file

@ -65,7 +65,7 @@ docker run -it --rm \
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
--entrypoint="install" \ --entrypoint="install" \
appwrite/appwrite:0.15.1 appwrite/appwrite:0.15.2
``` ```
### Windows ### Windows
@ -77,7 +77,7 @@ docker run -it --rm ^
--volume //var/run/docker.sock:/var/run/docker.sock ^ --volume //var/run/docker.sock:/var/run/docker.sock ^
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^ --volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
--entrypoint="install" ^ --entrypoint="install" ^
appwrite/appwrite:0.15.1 appwrite/appwrite:0.15.2
``` ```
#### PowerShell #### PowerShell
@ -87,7 +87,7 @@ docker run -it --rm ,
--volume /var/run/docker.sock:/var/run/docker.sock , --volume /var/run/docker.sock:/var/run/docker.sock ,
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw , --volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ,
--entrypoint="install" , --entrypoint="install" ,
appwrite/appwrite:0.15.1 appwrite/appwrite:0.15.2
``` ```
Once the Docker installation completes, go to http://localhost to access the Appwrite console from your browser. Please note that on non-Linux native hosts, the server might take a few minutes to start after installation completes. Once the Docker installation completes, go to http://localhost to access the Appwrite console from your browser. Please note that on non-Linux native hosts, the server might take a few minutes to start after installation completes.

View file

@ -180,7 +180,7 @@ return [
[ [
'key' => 'cli', 'key' => 'cli',
'name' => 'Command Line', 'name' => 'Command Line',
'version' => '0.18.0', 'version' => '0.18.1',
'url' => 'https://github.com/appwrite/sdk-for-cli', 'url' => 'https://github.com/appwrite/sdk-for-cli',
'package' => 'https://www.npmjs.com/package/appwrite-cli', 'package' => 'https://www.npmjs.com/package/appwrite-cli',
'enabled' => true, 'enabled' => true,

View file

@ -2277,7 +2277,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
$data = \array_merge($document->getArrayCopy(), $data); $data = \array_merge($document->getArrayCopy(), $data);
$data['$collection'] = $collection->getId(); // Make sure user don't switch collectionID $data['$collection'] = $collection->getId(); // Make sure user don't switch collectionID
$data['$createdAt'] = $collection->getCreatedAt(); // Make sure user don't switch createdAt $data['$createdAt'] = $document->getCreatedAt(); // Make sure user don't switch createdAt
$data['$id'] = $document->getId(); // Make sure user don't switch document unique ID $data['$id'] = $document->getId(); // Make sure user don't switch document unique ID
$data['$read'] = (is_null($read)) ? ($document->getRead() ?? []) : $read; // By default inherit read permissions $data['$read'] = (is_null($read)) ? ($document->getRead() ?? []) : $read; // By default inherit read permissions
$data['$write'] = (is_null($write)) ? ($document->getWrite() ?? []) : $write; // By default inherit write permissions $data['$write'] = (is_null($write)) ? ($document->getWrite() ?? []) : $write; // By default inherit write permissions
@ -2668,7 +2668,7 @@ App::get('/v1/databases/:databaseId/usage')
}); });
App::get('/v1/databases/:databaseId/collections/:collectionId/usage') App::get('/v1/databases/:databaseId/collections/:collectionId/usage')
->alias('/v1/database/collections/:collectionId/usage', ['databaseId' => 'default']) ->alias('/v1/database/:collectionId/usage', ['databaseId' => 'default'])
->desc('Get usage stats for a collection') ->desc('Get usage stats for a collection')
->groups(['api', 'database']) ->groups(['api', 'database'])
->label('scope', 'collections.read') ->label('scope', 'collections.read')

View file

@ -89,8 +89,8 @@ const APP_LIMIT_COMPRESSION = 20000000; //20MB
const APP_LIMIT_ARRAY_PARAMS_SIZE = 100; // Default maximum of how many elements can there be in API parameter that expects array value const APP_LIMIT_ARRAY_PARAMS_SIZE = 100; // Default maximum of how many elements can there be in API parameter that expects array value
const APP_LIMIT_ARRAY_ELEMENT_SIZE = 4096; // Default maximum length of element in array parameter represented by maximum URL length. const APP_LIMIT_ARRAY_ELEMENT_SIZE = 4096; // Default maximum length of element in array parameter represented by maximum URL length.
const APP_LIMIT_SUBQUERY = 1000; const APP_LIMIT_SUBQUERY = 1000;
const APP_CACHE_BUSTER = 401; const APP_CACHE_BUSTER = 402;
const APP_VERSION_STABLE = '0.15.1'; const APP_VERSION_STABLE = '0.15.2';
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum'; const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
const APP_DATABASE_ATTRIBUTE_IP = 'ip'; const APP_DATABASE_ATTRIBUTE_IP = 'ip';

View file

@ -491,8 +491,12 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
$database = new Database(new MariaDB($db), $cache); $database = new Database(new MariaDB($db), $cache);
$database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite')); $database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
$database->setNamespace("_console"); $database->setNamespace("_console");
$project = Authorization::skip(fn() => $database->getDocument('projects', $realtime->connections[$connection]['projectId'])); $projectId = $realtime->connections[$connection]['projectId'];
if ($projectId !== 'console') {
$project = Authorization::skip(fn() => $database->getDocument('projects', $projectId));
$database->setNamespace("_{$project->getInternalId()}"); $database->setNamespace("_{$project->getInternalId()}");
}
/* /*
* Abuse Check * Abuse Check

View file

@ -83,7 +83,7 @@ $logError = function (Throwable $error, string $action = 'syncUsageStats') use (
$version = App::getEnv('_APP_VERSION', 'UNKNOWN'); $version = App::getEnv('_APP_VERSION', 'UNKNOWN');
$log = new Log(); $log = new Log();
$log->setNamespace("realtime"); $log->setNamespace("usage");
$log->setServer(\gethostname()); $log->setServer(\gethostname());
$log->setVersion($version); $log->setVersion($version);
$log->setType(Log::TYPE_ERROR); $log->setType(Log::TYPE_ERROR);

View file

@ -72,7 +72,7 @@
} }
], ],
"require-dev": { "require-dev": {
"appwrite/sdk-generator": "0.19.4", "appwrite/sdk-generator": "0.19.5",
"phpunit/phpunit": "9.5.20", "phpunit/phpunit": "9.5.20",
"squizlabs/php_codesniffer": "^3.6", "squizlabs/php_codesniffer": "^3.6",
"swoole/ide-helper": "4.8.9", "swoole/ide-helper": "4.8.9",

22
composer.lock generated
View file

@ -236,16 +236,16 @@
}, },
{ {
"name": "chillerlan/php-settings-container", "name": "chillerlan/php-settings-container",
"version": "2.1.3", "version": "2.1.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/chillerlan/php-settings-container.git", "url": "https://github.com/chillerlan/php-settings-container.git",
"reference": "125dd573b45ffc7cabecf385986a356ba2c6f602" "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/125dd573b45ffc7cabecf385986a356ba2c6f602", "url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/1beb7df3c14346d4344b0b2e12f6f9a74feabd4a",
"reference": "125dd573b45ffc7cabecf385986a356ba2c6f602", "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -296,7 +296,7 @@
"type": "ko_fi" "type": "ko_fi"
} }
], ],
"time": "2022-03-09T13:18:58+00:00" "time": "2022-07-05T22:32:14+00:00"
}, },
{ {
"name": "colinmollenhour/credis", "name": "colinmollenhour/credis",
@ -2894,16 +2894,16 @@
"packages-dev": [ "packages-dev": [
{ {
"name": "appwrite/sdk-generator", "name": "appwrite/sdk-generator",
"version": "0.19.4", "version": "0.19.5",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/appwrite/sdk-generator.git", "url": "https://github.com/appwrite/sdk-generator.git",
"reference": "51a8e4205cd4809deeff8121a24e717642d68564" "reference": "04de540cf683e2b08b3192c137dde7f2c37003d9"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/51a8e4205cd4809deeff8121a24e717642d68564", "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/04de540cf683e2b08b3192c137dde7f2c37003d9",
"reference": "51a8e4205cd4809deeff8121a24e717642d68564", "reference": "04de540cf683e2b08b3192c137dde7f2c37003d9",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2938,9 +2938,9 @@
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
"support": { "support": {
"issues": "https://github.com/appwrite/sdk-generator/issues", "issues": "https://github.com/appwrite/sdk-generator/issues",
"source": "https://github.com/appwrite/sdk-generator/tree/0.19.4" "source": "https://github.com/appwrite/sdk-generator/tree/0.19.5"
}, },
"time": "2022-06-29T15:19:58+00:00" "time": "2022-07-06T11:05:57+00:00"
}, },
{ {
"name": "doctrine/instantiator", "name": "doctrine/instantiator",

View file

@ -1 +1 @@
Sends the user an SMS with a secret key for creating a session. Use the returned user ID and secret and submit a request to the [PUT /account/sessions/phone](/docs/client/account#accountUpdatePhoneSession) endpoint to complete the login process. The secret sent to the user's phone is valid for 15 minutes. Sends the user an SMS with a secret key for creating a session. If the provided user ID has not be registered, a new user will be created. Use the returned user ID and secret and submit a request to the [PUT /account/sessions/phone](/docs/client/account#accountUpdatePhoneSession) endpoint to complete the login process. The secret sent to the user's phone is valid for 15 minutes.

View file

@ -31,12 +31,6 @@ You can also fetch all the collections in your current project using
appwrite init collection appwrite init collection
``` ```
The CLI also comes with a convenient `--all` flag to perform both these steps at once.
```sh
appwrite init --all
```
* ### Creating and deploying cloud functions * ### Creating and deploying cloud functions
The CLI makes it extremely easy to create and deploy Appwrite's cloud functions. Initialise your new function using The CLI makes it extremely easy to create and deploy Appwrite's cloud functions. Initialise your new function using
@ -82,12 +76,6 @@ Similarly, you can deploy all your collections to your Appwrite server using
appwrite deploy collections appwrite deploy collections
``` ```
The `deploy` command also comes with a convenient `--all` flag to deploy all your functions and collections at once.
```sh
appwrite deploy --all
```
> ### Note > ### Note
> By default, requests to domains with self signed SSL certificates (or no certificates) are disabled. If you trust the domain, you can bypass the certificate validation using > By default, requests to domains with self signed SSL certificates (or no certificates) are disabled. If you trust the domain, you can bypass the certificate validation using
```sh ```sh

View file

@ -5,8 +5,8 @@
* **BREAKING** `dateCreated` attribute removed from `Team`, `Execution`, `File` models * **BREAKING** `dateCreated` attribute removed from `Team`, `Execution`, `File` models
* **BREAKING** `dateCreated` and `dateUpdated` attribute removed from `Func`, `Deployment`, `Bucket` models * **BREAKING** `dateCreated` and `dateUpdated` attribute removed from `Func`, `Deployment`, `Bucket` models
* **BREAKING** Realtime channels * **BREAKING** Realtime channels
* collections.[COLLECTION_ID] is now databases.[DATABASE_ID].ollections.[COLLECTION_ID] * collections.[COLLECTION_ID] is now databases.[DATABASE_ID].collections.[COLLECTION_ID]
* collections.[COLLECTION_ID].documents is now databases.[DATABASE_ID].ollections.[COLLECTION_ID].documents * collections.[COLLECTION_ID].documents is now databases.[DATABASE_ID].collections.[COLLECTION_ID].documents
**Full Changelog for Appwrite 0.15 can be found here**: https://github.com/appwrite/appwrite/blob/master/CHANGES.md#version-0150 **Full Changelog for Appwrite 0.15 can be found here**: https://github.com/appwrite/appwrite/blob/master/CHANGES.md#version-0150

View file

@ -6,8 +6,8 @@
* **BREAKING** `dateCreated` attribute removed from `Team`, `Execution`, `File` models * **BREAKING** `dateCreated` attribute removed from `Team`, `Execution`, `File` models
* **BREAKING** `dateCreated` and `dateUpdated` attribute removed from `Func`, `Deployment`, `Bucket` models * **BREAKING** `dateCreated` and `dateUpdated` attribute removed from `Func`, `Deployment`, `Bucket` models
* **BREAKING** Realtime channels * **BREAKING** Realtime channels
* collections.[COLLECTION_ID] is now databases.[DATABASE_ID].ollections.[COLLECTION_ID] * collections.[COLLECTION_ID] is now databases.[DATABASE_ID].collections.[COLLECTION_ID]
* collections.[COLLECTION_ID].documents is now databases.[DATABASE_ID].ollections.[COLLECTION_ID].documents * collections.[COLLECTION_ID].documents is now databases.[DATABASE_ID].collections.[COLLECTION_ID].documents
**Full Changelog for Appwrite 0.15 can be found here**: https://github.com/appwrite/appwrite/blob/master/CHANGES.md#version-0150 **Full Changelog for Appwrite 0.15 can be found here**: https://github.com/appwrite/appwrite/blob/master/CHANGES.md#version-0150

View file

@ -46,7 +46,8 @@ abstract class Migration
'0.14.1' => 'V13', '0.14.1' => 'V13',
'0.14.2' => 'V13', '0.14.2' => 'V13',
'0.15.0' => 'V14', '0.15.0' => 'V14',
'0.15.1' => 'V14' '0.15.1' => 'V14',
'0.15.2' => 'V14'
]; ];
/** /**

View file

@ -922,6 +922,63 @@ trait DatabasesBase
return ['documents' => $documents['body']['documents'], 'databaseId' => $databaseId]; return ['documents' => $documents['body']['documents'], 'databaseId' => $databaseId];
} }
public function testCreateCollectionAlias(): array
{
// Create default database
$database = $this->client->call(Client::METHOD_POST, '/databases', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => 'default',
'name' => 'Default'
]);
$this->assertNotEmpty($database['body']['$id']);
$this->assertEquals(201, $database['headers']['status-code']);
/**
* Test for SUCCESS
*/
$movies = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'collectionId' => 'unique()',
'name' => 'Movies',
'read' => [],
'write' => [],
'permission' => 'document',
]);
$this->assertEquals($movies['headers']['status-code'], 201);
$this->assertEquals($movies['body']['name'], 'Movies');
return ['moviesId' => $movies['body']['$id']];
}
/**
* @depends testCreateCollectionAlias
*/
public function testListDocumentsAlias(array $data): array
{
/**
* Test for SUCCESS
*/
$documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals($documents['headers']['status-code'], 200);
$this->assertEquals($documents['body']['total'], 0);
return [];
}
/** /**
* @depends testListDocuments * @depends testListDocuments
*/ */

View file

@ -13,6 +13,111 @@ class RealtimeConsoleClientTest extends Scope
use ProjectCustom; use ProjectCustom;
use SideConsole; use SideConsole;
public function testManualAuthentication()
{
$user = $this->getUser();
$userId = $user['$id'] ?? '';
$session = $user['session'] ?? '';
/**
* Test for SUCCESS
*/
$client = $this->getWebsocket(['account'], [
'origin' => 'http://localhost'
]);
$response = json_decode($client->receive(), true);
$this->assertArrayHasKey('type', $response);
$this->assertArrayHasKey('data', $response);
$this->assertEquals('connected', $response['type']);
$this->assertNotEmpty($response['data']);
$this->assertCount(1, $response['data']['channels']);
$this->assertContains('account', $response['data']['channels']);
$client->send(\json_encode([
'type' => 'authentication',
'data' => [
'session' => $session
]
]));
$response = json_decode($client->receive(), true);
$this->assertArrayHasKey('type', $response);
$this->assertArrayHasKey('data', $response);
$this->assertEquals('response', $response['type']);
$this->assertNotEmpty($response['data']);
$this->assertEquals('authentication', $response['data']['to']);
$this->assertTrue($response['data']['success']);
$this->assertNotEmpty($response['data']['user']);
$this->assertEquals($userId, $response['data']['user']['$id']);
/**
* Test for FAILURE
*/
$client->send(\json_encode([
'type' => 'authentication',
'data' => [
'session' => 'invalid_session'
]
]));
$response = json_decode($client->receive(), true);
$this->assertArrayHasKey('type', $response);
$this->assertArrayHasKey('data', $response);
$this->assertEquals('error', $response['type']);
$this->assertNotEmpty($response['data']);
$this->assertEquals(1003, $response['data']['code']);
$this->assertEquals('Session is not valid.', $response['data']['message']);
$client->send(\json_encode([
'type' => 'authentication',
'data' => []
]));
$response = json_decode($client->receive(), true);
$this->assertArrayHasKey('type', $response);
$this->assertArrayHasKey('data', $response);
$this->assertEquals('error', $response['type']);
$this->assertNotEmpty($response['data']);
$this->assertEquals(1003, $response['data']['code']);
$this->assertEquals('Payload is not valid.', $response['data']['message']);
$client->send(\json_encode([
'type' => 'unknown',
'data' => [
'session' => 'invalid_session'
]
]));
$response = json_decode($client->receive(), true);
$this->assertArrayHasKey('type', $response);
$this->assertArrayHasKey('data', $response);
$this->assertEquals('error', $response['type']);
$this->assertNotEmpty($response['data']);
$this->assertEquals(1003, $response['data']['code']);
$this->assertEquals('Message type is not valid.', $response['data']['message']);
$client->send(\json_encode([
'test' => '123',
]));
$response = json_decode($client->receive(), true);
$this->assertArrayHasKey('type', $response);
$this->assertArrayHasKey('data', $response);
$this->assertEquals('error', $response['type']);
$this->assertNotEmpty($response['data']);
$this->assertEquals(1003, $response['data']['code']);
$this->assertEquals('Message format is not valid.', $response['data']['message']);
$client->close();
}
public function testAttributes() public function testAttributes()
{ {
$user = $this->getUser(); $user = $this->getUser();