diff --git a/README.md b/README.md index ef5a975232..ac70516282 100644 --- a/README.md +++ b/README.md @@ -77,21 +77,28 @@ $client = new Client(); $client->setAdapter($adapter); ``` -### Using HTTP Adapter +### Using HTTP Adapters Actually Guzzle is used as HTTP client library ```php setAdapter($adapter); ``` +#### Supported types of exceptions + +* InfluxGeneralException +* InfluxAuthorizationException (extends InfluxGeneralException) +* InfluxBadResponseException (extends InfluxGeneralException) +* InfluxNoSeriesException (extends InfluxGeneralException) +* InfluxUnexpectedResponseException (extends InfluxGeneralException) + ### Create your client with the factory method Effectively the client creation is not so simple, for that @@ -100,7 +107,7 @@ reason you can you the factory method provided with the library. ```php $options = [ "adapter" => [ - "name" => "InfluxDB\\Adapter\\GuzzleAdapter", + "name" => "InfluxDB\\Adapter\\HttpAdapter", "options" => [ // guzzle options ], @@ -144,7 +151,7 @@ $influx->query("select * from mine", "s"); // with time_precision ``` You can query the database only if the adapter is queryable (implements -`QueryableInterface`), actually `GuzzleAdapter`. +`QueryableInterface`), actually `HttpAdapter`. The adapter returns the json decoded body of the InfluxDB response, something like: diff --git a/VERSION b/VERSION index abd410582d..0d91a54c7d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.2.4 +0.3.0 diff --git a/spec/InfluxDB/Adapter/HttpAdapterSpec.php b/spec/InfluxDB/Adapter/HttpAdapterSpec.php new file mode 100755 index 0000000000..fac255668b --- /dev/null +++ b/spec/InfluxDB/Adapter/HttpAdapterSpec.php @@ -0,0 +1,194 @@ +shouldHaveType('InfluxDB\Adapter\HttpAdapter'); + } + + function let(Options $options, Client $client) + { + $options->getHttpSeriesEndpoint()->willReturn("localhost"); + $options->getHttpDatabaseEndpoint()->willReturn("localhost"); + $options->getUsername()->willReturn("one"); + $options->getPassword()->willReturn("two"); + $this->beConstructedWith($options, $client); + } + + function it_should_send_data_via_post(Client $client) + { + $responseBody = ['key'=>'value']; + $response = new Response(200,[], Stream::factory(json_encode($responseBody))); + $client->post("localhost", [ + 'auth' => ["one", "two"], + 'exceptions' => false, + 'body' => json_encode(['pippo']) + ])->willReturn($response) + ->shouldBeCalledTimes(1); + $this->send(["pippo"])->shouldReturn($responseBody); + } + + function it_should_query_data(Client $client, Options $options) + { + $client->get( + "localhost", + [ + "auth" => ["one", "two"], + "exceptions" => false, + "query" => [ + "q" => "select * from tcp.test", + ] + ] + )->willReturn(new Response(200,[],null)); + $this->query("select * from tcp.test")->shouldReturn(null); + } + + function it_should_query_data_with_time_precision(Client $client, Options $options) + { + $client->get( + "localhost", + [ + "auth" => ["one", "two"], + "exceptions" => false, + "query" => [ + "time_precision" => "s", + "q" => "select * from tcp.test", + ] + ] + )->willReturn(new Response(200, [], null)); + $this->query("select * from tcp.test", "s")->shouldReturn(null); + } + + function it_should_list_all_databases(Client $client, Options $options) + { + $client->get( + "localhost", + [ + "auth" => ["one", "two"], + "exceptions" => false, + ] + )->shouldBeCalledTimes(1)->willReturn(new Response(200, [], null)); + + $this->getDatabases()->shouldReturn(null); + } + + function it_should_create_a_new_database(Client $client, Options $options) + { + $client->post( + "localhost", + [ + "auth" => ["one", "two"], + "exceptions" => false, + "body" => json_encode(["name" => "db_name"]) + ] + )->shouldBeCalledTimes(1)->willReturn(new Response(201, [], null)); + + $this->createDatabase("db_name")->shouldReturn(true); + } + + function it_should_return_true_with_success(Client $client) { + foreach ([201,204,299] as $code) { + $client->post(Argument::any(), Argument::any(), Argument::any())->willReturn(new Response($code, [], null)); + + $this->createDatabase("db_name")->shouldReturn(true); + } + } + + + + function it_should_throw_no_series_exception (Client $client) + { + $client->get( + "localhost", + [ + "auth" => ["one", "two"], + "exceptions" => false, + "query" => [ + "q" => "select * from tcp.test", + ] + ] + )->willReturn(new Response(HttpAdapter::STATUS_CODE_BAD_REQUEST,[], Stream::factory("Couldn't find series: tcp.test"))); + $this->shouldThrow(new InfluxNoSeriesException("Couldn't find series: tcp.test", HttpAdapter::STATUS_CODE_BAD_REQUEST)) + ->during("query", ["select * from tcp.test"]); + } + + function it_should_throw_authorization_exception (Client $client) + { + $codes = [HttpAdapter::STATUS_CODE_UNAUTHORIZED, HttpAdapter::STATUS_CODE_FORBIDDEN]; + foreach ($codes as $code) { + $client->get( + "localhost", + [ + "auth" => ["one", "two"], + "exceptions" => false, + "query" => [ + "q" => "select * from tcp.test", + ] + ] + )->willReturn(new Response($code,[], Stream::factory("Message"))); + $this->shouldThrow(new InfluxAuthorizationException("Message", $code)) + ->during("query", ["select * from tcp.test"]); + } + } + + function it_should_throw_general_exception (Client $client) + { + $client->get( + "localhost", + [ + "auth" => ["one", "two"], + "exceptions" => false, + "query" => [ + "q" => "select * from tcp.test", + ] + ] + )->willReturn(new Response(409,[], Stream::factory("Message"))); + $this->shouldThrow(new InfluxGeneralException("Message", 409)) + ->during("query", ["select * from tcp.test"]); + } + + function it_should_throw_general_exception_with_default_message (Client $client) + { + $client->get(Argument::any(), Argument::any())->willReturn(new Response(409)); + $this->shouldThrow(new InfluxGeneralException("Conflict", 409)) + ->during("query", ["select * from tcp.test"]); + } + + function it_should_throw_bad_response_exception(Client $client) + { + $response = new Response(200,[], Stream::factory('bad response')); + $client->post("localhost", [ + 'auth' => ["one", "two"], + 'exceptions' => false, + 'body' => json_encode(['pippo']) + ])->willReturn($response) + ->shouldBeCalledTimes(1); + $this->shouldThrow(new InfluxBadResponseException("Unable to parse JSON data: JSON_ERROR_SYNTAX - Syntax error, malformed JSON; Response is 'bad response'", 0)) + ->during("send", [["pippo"]]); + } + + function it_should_throw_unexpected_response_exception (Client $client) + { + foreach ([0, 300, 500] as $code) { + $client->get(Argument::any(), Argument::any())->willReturn(new Response($code, [], Stream::factory("Message"))); + $this->shouldThrow(new InfluxUnexpectedResponseException("Message", $code)) + ->during("query", ["select * from tcp.test"]); + } + } +} diff --git a/src/InfluxDB/Adapter/AdapterInterface.php b/src/InfluxDB/Adapter/AdapterInterface.php index 79ed186706..73d1b2fe58 100644 --- a/src/InfluxDB/Adapter/AdapterInterface.php +++ b/src/InfluxDB/Adapter/AdapterInterface.php @@ -1,7 +1,15 @@ httpClient = $httpClient; $this->options = $options; } + /** + * @return Options + */ public function getOptions() { return $this->options; } + /** + * {@inheritDoc} + */ public function send($message, $timePrecision = false) { $httpMessage = [ @@ -35,6 +58,9 @@ class GuzzleAdapter implements AdapterInterface, QueryableInterface return $this->httpClient->post($endpoint, $httpMessage); } + /** + * {@inheritDoc} + */ public function query($query, $timePrecision = false) { $options = [ @@ -53,6 +79,9 @@ class GuzzleAdapter implements AdapterInterface, QueryableInterface return $this->httpClient->get($endpoint, $options)->json(); } + /** + * {@inheritDoc} + */ public function getDatabases() { $options = [ @@ -64,6 +93,9 @@ class GuzzleAdapter implements AdapterInterface, QueryableInterface return $this->httpClient->get($endpoint, $options)->json(); } + /** + * {@inheritDoc} + */ public function createDatabase($name) { $httpMessage = [ @@ -75,6 +107,9 @@ class GuzzleAdapter implements AdapterInterface, QueryableInterface return $this->httpClient->post($endpoint, $httpMessage)->json(); } + /** + * {@inheritDoc} + */ public function deleteDatabase($name) { $httpMessage = [ diff --git a/src/InfluxDB/Adapter/HttpAdapter.php b/src/InfluxDB/Adapter/HttpAdapter.php new file mode 100644 index 0000000000..738f43c834 --- /dev/null +++ b/src/InfluxDB/Adapter/HttpAdapter.php @@ -0,0 +1,198 @@ +options = $options; + $this->client = $client ?: new Client(); + } + + /** + * @return Options + */ + public function getOptions() + { + return $this->options; + } + + /** + * @param array $body + * @param array $query + * @param bool $timePrecision + * @return array + */ + protected function getRequest(array $body = [], array $query = [], $timePrecision = false) + { + $request = [ + "auth" => [$this->options->getUsername(), $this->options->getPassword()], + "exceptions" => false + ]; + if (count($body)) { + $request['body'] = json_encode($body); + } + if (count($query)) { + $request['query'] = $query; + } + if ($timePrecision) { + $request["query"]["time_precision"] = $timePrecision; + } + return $request; + } + + /** + * @param ResponseInterface $response + * @return mixed + * @throws \InfluxDB\Exception\InfluxGeneralException + * @throws \InfluxDB\Exception\InfluxAuthorizationException + * @throws \InfluxDB\Exception\InfluxNoSeriesException + */ + protected function parseResponse(ResponseInterface $response) + { + $statusCode = $response->getStatusCode(); + if ($statusCode >= 400 && $statusCode < 500) { + $message = (string)$response->getBody(); + if (!$message) { + $message = $response->getReasonPhrase(); + } + switch ($statusCode) { + case self::STATUS_CODE_UNAUTHORIZED: + case self::STATUS_CODE_FORBIDDEN: + throw new InfluxAuthorizationException($message, $statusCode); + case self::STATUS_CODE_BAD_REQUEST: + if (strpos($message, "Couldn't find series:") !== false) { + throw new InfluxNoSeriesException($message, $statusCode); + } + } + throw new InfluxGeneralException($message, $statusCode); + } else if ($statusCode == self::STATUS_CODE_OK) { + try { + return $response->json(); + } catch (ParseException $ex) { + throw new InfluxBadResponseException( + sprintf("%s; Response is '%s'", $ex->getMessage(), (string)$response->getBody()), + $ex->getCode(), $ex + ); + } + } else if ($statusCode > 200 && $statusCode < 300) { + return true; + } + throw new InfluxUnexpectedResponseException((string)$response->getBody(), $statusCode); + } + + /** + * @param $message + * @param bool $timePrecision + * @return \GuzzleHttp\Message\ResponseInterface + */ + public function send($message, $timePrecision = false) + { + try { + $response = $this->client->post( + $this->options->getHttpSeriesEndpoint(), + $this->getRequest($message, [], $timePrecision) + ); + } catch (\Exception $ex) { + throw new InfluxGeneralException($ex->getMessage(), $ex->getCode(), $ex); + } + return $this->parseResponse($response); + } + + /** + * @param $query + * @param bool $timePrecision + * @return mixed + */ + public function query($query, $timePrecision = false) + { + try { + $response = $this->client->get( + $this->options->getHttpSeriesEndpoint(), + $this->getRequest([], ["q" => $query], $timePrecision) + ); + } catch (\Exception $ex) { + throw new InfluxGeneralException($ex->getMessage(), $ex->getCode(), $ex); + } + return $this->parseResponse($response); + } + + /** + * @return mixed + */ + public function getDatabases() + { + try { + $response = $this->client->get( + $this->options->getHttpDatabaseEndpoint(), + $this->getRequest() + ); + } catch (\Exception $ex) { + throw new InfluxGeneralException($ex->getMessage(), $ex->getCode(), $ex); + } + return $this->parseResponse($response); + } + + /** + * @param $name + * @return mixed + */ + public function createDatabase($name) + { + try { + $response = $this->client->post( + $this->options->getHttpDatabaseEndpoint(), + $this->getRequest(["name" => $name]) + ); + } catch (\Exception $ex) { + throw new InfluxGeneralException($ex->getMessage(), $ex->getCode(), $ex); + } + return $this->parseResponse($response); + } + + /** + * @param $name + * @return mixed + */ + public function deleteDatabase($name) + { + try { + $response = $this->client->delete( + $this->options->getHttpDatabaseEndpoint($name), + $this->getRequest() + ); + } catch (\Exception $ex) { + throw new InfluxGeneralException($ex->getMessage(), $ex->getCode(), $ex); + } + return $this->parseResponse($response); + } +} \ No newline at end of file diff --git a/src/InfluxDB/Adapter/QueryableInterface.php b/src/InfluxDB/Adapter/QueryableInterface.php index 2e47540112..3bed923849 100644 --- a/src/InfluxDB/Adapter/QueryableInterface.php +++ b/src/InfluxDB/Adapter/QueryableInterface.php @@ -1,10 +1,34 @@ options = $options; } + /** + * @return Options + */ public function getOptions() { return $this->options; } + /** + * {@inheritDoc} + */ public function send($message, $timePrecision = false) { $message = json_encode($message); diff --git a/src/InfluxDB/Client.php b/src/InfluxDB/Client.php index 94e879f8f8..b7215244f3 100644 --- a/src/InfluxDB/Client.php +++ b/src/InfluxDB/Client.php @@ -5,33 +5,68 @@ namespace InfluxDB; use InfluxDb\Adapter\QueryableInterface; use InfluxDB\Filter\FilterInterface; +/** + * Client to manage request at InfluxDB + */ class Client { + /** + * @var \InfluxDB\Adapter\AdapterInterface + */ private $adapter; + + /** + * @var \InfluxDB\Filter\FilterInterface + */ private $filter; + /** + * Set filter + * @param Filter\FilterInterface $filter + * @return Client + */ public function setFilter(Filter\FilterInterface $filter) { $this->filter = $filter; return $this; } + /** + * Get filter + * @return Filter\FilterInterface + */ public function getFilter() { return $this->filter; } + /** + * Set InfluxDB adapter + * @param Adapter\AdapterInterface + * @return Client + */ public function setAdapter(Adapter\AdapterInterface $adapter) { $this->adapter = $adapter; return $this; } + /** + * Get adapter + * @return Adapter\AdapterInterface + */ public function getAdapter() { return $this->adapter; } + /** + * Insert point into series + * @param string $name + * @param array $value + * @param bool|string $timePrecision + * @return mixed + */ public function mark($name, array $values, $timePrecision = false) { $data =[]; @@ -45,6 +80,12 @@ class Client return $this->getAdapter()->send([$data], $timePrecision); } + /** + * Make a query into database + * @param string $query + * @param bool|string $timePrecision + * @return array + */ public function query($query, $timePrecision = false) { if (!($this->getAdapter() instanceOf QueryableInterface)) { @@ -62,6 +103,10 @@ class Client return $return; } + /** + * List of databases + * @return array + */ public function getDatabases() { if (!($this->getAdapter() instanceOf QueryableInterface)) { @@ -70,6 +115,10 @@ class Client return $this->getAdapter()->getDatabases(); } + /** + * Create database by name + * @param string $name + */ public function createDatabase($name) { if (!($this->getAdapter() instanceOf QueryableInterface)) { @@ -78,6 +127,10 @@ class Client return $this->getAdapter()->createDatabase($name); } + /** + * Delete database by name + * @param string $name + */ public function deleteDatabase($name) { if (!($this->getAdapter() instanceOf QueryableInterface)) { @@ -86,6 +139,11 @@ class Client return $this->getAdapter()->deleteDatabase($name); } + /** + * List of time precision choose + * @param string $timePrecision + * @return bool|string + */ private function clearTimePrecision($timePrecision) { switch ($timePrecision) { diff --git a/src/InfluxDB/ClientFactory.php b/src/InfluxDB/ClientFactory.php index cd94929f68..a466cfc54a 100644 --- a/src/InfluxDB/ClientFactory.php +++ b/src/InfluxDB/ClientFactory.php @@ -4,8 +4,18 @@ namespace InfluxDB; use Zend\Stdlib\Hydrator\ClassMethods; use GuzzleHttp\Client as GuzzleClient; +/** + * Create your static client + */ abstract class ClientFactory { + /** + * Create new client + * @param array $options + * @return Client + * @throws InvalidArgumentException If not exist adapter name + * or not find adapter + */ public static function create(array $options) { $defaultOptions = [ @@ -38,6 +48,9 @@ abstract class ClientFactory case 'InfluxDB\\Adapter\\GuzzleAdapter': $adapter = new $adapterName(new GuzzleClient($options["adapter"]["options"]), $adapterOptions); break; + case 'InfluxDB\\Adapter\\HttpAdapter': + $adapter = new $adapterName($adapterOptions, new GuzzleClient($options["adapter"]["options"])); + break; default: throw new \InvalidArgumentException("Missing adapter {$adapter}"); } diff --git a/src/InfluxDB/Exception/InfluxAuthorizationException.php b/src/InfluxDB/Exception/InfluxAuthorizationException.php new file mode 100644 index 0000000000..f4feb823f8 --- /dev/null +++ b/src/InfluxDB/Exception/InfluxAuthorizationException.php @@ -0,0 +1,8 @@ +host = "localhost"; @@ -21,17 +49,27 @@ class Options $this->setProtocol("http"); } + /** + * @return string + */ public function getProtocol() { return $this->protocol; } + /** + * @param string $protocol + * @return Options + */ public function setProtocol($protocol) { $this->protocol = $protocol; return $this; } + /** + * @return string + */ public function getHost() { return $this->host; @@ -43,50 +81,82 @@ class Options return $this; } + /** + * @return string|int + */ public function getPort() { return $this->port; } + /** + * @param string|int $port + * @return Options + */ public function setPort($port) { $this->port = $port; return $this; } + /** + * @return string + */ public function getUsername() { return $this->username; } + /** + * @param string $usarname + * @return Options + */ public function setUsername($username) { $this->username = $username; return $this; } + /** + * @return string + */ public function getPassword() { return $this->password; } + /** + * @param string $password + * @return Options + */ public function setPassword($password) { $this->password = $password; return $this; } + /** + * @return string + */ public function getDatabase() { return $this->database; } + /** + * @param string $database + * @return Options + */ public function setDatabase($database) { $this->database = $database; return $this; } + /** + * Build http series edpoint + * @return string + */ public function getHttpSeriesEndpoint() { return sprintf( @@ -98,6 +168,11 @@ class Options ); } + /** + * Build http database endpoint by name + * @param string $name + * @return string + */ public function getHttpDatabaseEndpoint($name = false) { $url = sprintf( diff --git a/tests/InfluxDB/ClientFactoryTest.php b/tests/InfluxDB/ClientFactoryTest.php index ddaf378983..7cc8cbffcb 100644 --- a/tests/InfluxDB/ClientFactoryTest.php +++ b/tests/InfluxDB/ClientFactoryTest.php @@ -61,12 +61,13 @@ class ClientFactoryTest extends \PHPUnit_Framework_TestCase /** * @group factory * @group tcp + * @dataProvider getTcpAdapters */ - public function testCreateTcpClient() + public function testCreateTcpClient($adapter) { $options = [ "adapter" => [ - "name" => "InfluxDB\\Adapter\\GuzzleAdapter", + "name" => $adapter, ], "options" => [ "host" => "127.0.0.1", @@ -78,21 +79,30 @@ class ClientFactoryTest extends \PHPUnit_Framework_TestCase $client = ClientFactory::create($options); $this->assertInstanceOf("InfluxDB\\Client", $client); - $this->assertInstanceOf("InfluxDB\\Adapter\\GuzzleAdapter", $client->getAdapter()); + $this->assertInstanceOf($adapter, $client->getAdapter()); $this->assertEquals("127.0.0.1", $client->getAdapter()->getOptions()->getHost()); $this->assertEquals("user", $client->getAdapter()->getOptions()->getUsername()); $this->assertEquals("pass", $client->getAdapter()->getOptions()->getPassword()); } + public function getTcpAdapters() + { + return [ + ["InfluxDB\\Adapter\\GuzzleAdapter"], + ["InfluxDB\\Adapter\\HttpAdapter"], + ]; + } + /** * @group factory * @group filters + * @dataProvider getTcpAdapters */ - public function testCreateTcpClientWithFilter() + public function testCreateTcpClientWithFilter($adapter) { $options = [ "adapter" => [ - "name" => "InfluxDB\\Adapter\\GuzzleAdapter", + "name" => $adapter, ], "options" => [ "host" => "127.0.0.1", @@ -109,7 +119,7 @@ class ClientFactoryTest extends \PHPUnit_Framework_TestCase $client = ClientFactory::create($options); $this->assertInstanceOf("InfluxDB\\Client", $client); - $this->assertInstanceOf("InfluxDB\\Adapter\\GuzzleAdapter", $client->getAdapter()); + $this->assertInstanceOf($adapter, $client->getAdapter()); $this->assertEquals("127.0.0.1", $client->getAdapter()->getOptions()->getHost()); $this->assertEquals("user", $client->getAdapter()->getOptions()->getUsername()); $this->assertEquals("pass", $client->getAdapter()->getOptions()->getPassword()); diff --git a/tests/InfluxDB/HttpAdapterTest.php b/tests/InfluxDB/HttpAdapterTest.php new file mode 100644 index 0000000000..9d6871c83c --- /dev/null +++ b/tests/InfluxDB/HttpAdapterTest.php @@ -0,0 +1,128 @@ +rawOptions = $options; + + $tcpOptions = $options["tcp"]; + + $options = new Options(); + $options->setHost($tcpOptions["host"]); + $options->setPort($tcpOptions["port"]); + $options->setUsername($tcpOptions["username"]); + $options->setPassword($tcpOptions["password"]); + $options->setDatabase($tcpOptions["database"]); + + $this->options = $options; + + $adapter = new HttpAdapter($options); + + $influx = new Client(); + $influx->setAdapter($adapter); + $this->object = $influx; + + $databases = $this->object->getDatabases(); + foreach ($databases as $database) { + $this->object->deleteDatabase($database["name"]); + } + + $this->object->createDatabase($this->rawOptions["tcp"]["database"]); + } + + /** + * @group tcp + */ + public function testApiWorksCorrectly() + { + $this->object->mark("tcp.test", ["mark" => "element"]); + + $body = $this->object->query("select * from tcp.test"); + $this->assertCount(1, $body[0]["points"]); + $this->assertEquals("element", $body[0]["points"][0][2]); + } + + /** + * @group tcp + */ + public function testQueryApiWorksCorrectly() + { + $this->object->mark("tcp.test", ["mark" => "element"]); + + $body = $this->object->query("select * from tcp.test"); + + $this->assertCount(1, $body); + $this->assertEquals("tcp.test", $body[0]["name"]); + $this->assertEquals("element", $body[0]["points"][0][2]); + } + + /** + * @group tcp + */ + public function testQueryApiWithMultipleData() + { + $this->object->mark("tcp.test", ["mark" => "element"]); + $this->object->mark("tcp.test", ["mark" => "element2"]); + $this->object->mark("tcp.test", ["mark" => "element3"]); + + $body = $this->object->query("select mark from tcp.test", "s"); + + $this->assertCount(3, $body[0]["points"]); + $this->assertEquals("tcp.test", $body[0]["name"]); + } + + /** + * @group tcp + */ + public function testQueryApiWithTimePrecision() + { + $this->object->mark("tcp.test", ["mark" => "element"]); + + $body = $this->object->query("select mark from tcp.test", "s"); + + $this->assertCount(1, $body[0]["points"]); + $this->assertEquals("tcp.test", $body[0]["name"]); + } + + /** + * @group tcp + */ + public function testWriteApiWithTimePrecision() + { + $this->object->mark("tcp.test", ["time" => 1410591552, "mark" => "element"], "s"); + + $body = $this->object->query("select mark from tcp.test", "ms"); + + $this->assertCount(1, $body[0]["points"]); + $this->assertEquals("tcp.test", $body[0]["name"]); + + $this->assertEquals("1410591552000", $body[0]["points"][0][0]); + } + + public function testListActiveDatabses() + { + $databases = $this->object->getDatabases(); + + $this->assertCount(1, $databases); + } + + public function testCreateANewDatabase() + { + $this->object->createDatabase("walter"); + $databases = $this->object->getDatabases(); + + $this->assertCount(2, $databases); + + $this->object->deleteDatabase("walter"); + } +}