diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..584e8be
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,25 @@
+APP_NAME=ApiGateway
+APP_ENV=local
+APP_KEY=
+APP_DEBUG=true
+APP_URL=http://localhost
+APP_TIMEZONE=UTC
+
+LOG_CHANNEL=stack
+LOG_SLACK_WEBHOOK_URL=
+
+DB_CONNECTION=mysql
+DB_HOST=127.0.0.1
+DB_PORT=3306
+DB_DATABASE=lu-ms1-api_gateway
+DB_USERNAME=
+DB_PASSWORD=
+
+CACHE_DRIVER=file
+QUEUE_CONNECTION=sync
+
+
+PRODUCTS_SERVICE_BASE_URI=http://localhost:8100/api
+PRODUCTS_SERVICE_SECRET=
+ORDERS_SERVICE_BASE_URI=http://localhost:8200/api
+ORDERS_SERVICE_SECRET=
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a98c1b9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+/vendor
+/.idea
+Homestead.json
+Homestead.yaml
+.env
+
+storage/*.key
+composer.lock
diff --git a/.gitignore.example b/.gitignore.example
new file mode 100644
index 0000000..a98c1b9
--- /dev/null
+++ b/.gitignore.example
@@ -0,0 +1,8 @@
+/vendor
+/.idea
+Homestead.json
+Homestead.yaml
+.env
+
+storage/*.key
+composer.lock
diff --git a/app/Console/Commands/.gitkeep b/app/Console/Commands/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php
new file mode 100644
index 0000000..0d8a594
--- /dev/null
+++ b/app/Console/Kernel.php
@@ -0,0 +1,29 @@
+getStatusCode();
+ $errorMessage = Response::$statusTexts[$errorCode];
+ return $this->errorResponse($errorMessage, $errorCode);
+ }
+
+ if ($exception instanceof ModelNotFoundException) {
+ $model = strtolower(class_basename($exception->getModel()));
+ return $this->errorResponse(
+ "Does not exist any instance of {$model} with a given id",
+ Response::HTTP_NOT_FOUND);
+ }
+
+ if ($exception instanceof AuthorizationException) {
+ return $this->errorResponse(
+ $exception->getMessage(),
+ Response::HTTP_FORBIDDEN);
+ }
+
+ if ($exception instanceof AuthenticationException) {
+ return $this->errorResponse(
+ $exception->getMessage(),
+ Response::HTTP_UNAUTHORIZED);
+ }
+
+ if ($exception instanceof ValidationException) {
+ $errors = $exception->validator->errors()->getMessages();
+ return $this->errorResponse(
+ $errors,
+ Response::HTTP_UNPROCESSABLE_ENTITY);
+ }
+
+ if ($exception instanceof ClientException) {
+ $errorMessage = $exception->getResponse()->getBody();
+ $errorCode = $exception->getCode();
+
+ return $this->errorMessage($errorMessage, $errorCode);
+ }
+
+
+ if (env('APP_DEBUG', false)) {
+ return parent::render($request, $exception);
+ }
+
+ return $this->errorResponse(
+ "Unexpected error. Try later!",
+ Response::HTTP_INTERNAL_SERVER_ERROR);
+ }
+}
diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php
new file mode 100644
index 0000000..1838462
--- /dev/null
+++ b/app/Http/Controllers/Controller.php
@@ -0,0 +1,13 @@
+orderService = $orderService;
+ $this->productService = $productService;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function index()
+ {
+ return $this->successResponse($this->orderService->fetchOrders());
+ }
+
+ /**
+ * @param $order
+ *
+ * @return mixed
+ */
+ public function show($order)
+ {
+ return $this->successResponse($this->orderService->fetchOrder($order));
+ }
+
+ /**
+ * @param \Illuminate\Http\Request $request
+ *
+ * @return mixed
+ */
+ public function store(Request $request)
+ {
+ return $this->successResponse($this->orderService->createOrder($request->all()));
+ }
+
+ /**
+ * @param \Illuminate\Http\Request $request
+ * @param $order
+ *
+ * @return mixed
+ */
+ public function update(Request $request, $order)
+ {
+ return $this->successResponse($this->orderService->updateOrder($order, $request->all()));
+ }
+
+ /**
+ * @param $order
+ *
+ * @return mixed
+ */
+ public function destroy($order)
+ {
+ return $this->successResponse($this->orderService->deleteOrder($order));
+ }
+}
diff --git a/app/Http/Controllers/ProductController.php b/app/Http/Controllers/ProductController.php
new file mode 100644
index 0000000..36381fe
--- /dev/null
+++ b/app/Http/Controllers/ProductController.php
@@ -0,0 +1,73 @@
+productService = $productService;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function index()
+ {
+ return $this->successResponse($this->productService->fetchProducts());
+ }
+
+ /**
+ * @param $product
+ *
+ * @return mixed
+ */
+ public function show($product)
+ {
+ return $this->successResponse($this->productService->fetchProduct($product));
+ }
+
+ /**
+ * @param \Illuminate\Http\Request $request
+ *
+ * @return mixed
+ */
+ public function store(Request $request)
+ {
+ return $this->successResponse($this->productService->createProduct($request->all()));
+ }
+
+ /**
+ * @param \Illuminate\Http\Request $request
+ * @param $product
+ *
+ * @return mixed
+ */
+ public function update(Request $request, $product)
+ {
+ return $this->successResponse($this->productService->updateProduct($product, $request->all()));
+ }
+
+ /**
+ * @param $product
+ *
+ * @return mixed
+ */
+ public function destroy($product)
+ {
+ return $this->successResponse($this->productService->deleteProduct($product));
+ }
+}
diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php
new file mode 100644
index 0000000..1fea457
--- /dev/null
+++ b/app/Http/Middleware/Authenticate.php
@@ -0,0 +1,48 @@
+auth = $auth;
+ }
+
+ /**
+ * Handle an incoming request.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @param \Closure $next
+ * @param string|null $guard
+ * @return mixed
+ */
+ public function handle($request, Closure $next, $guard = null)
+ {
+ if ($this->auth->guard($guard)->guest()) {
+ return response('Unauthorized.', 401);
+ }
+
+ return $next($request);
+ }
+}
diff --git a/app/Http/Middleware/ExampleMiddleware.php b/app/Http/Middleware/ExampleMiddleware.php
new file mode 100644
index 0000000..49c589c
--- /dev/null
+++ b/app/Http/Middleware/ExampleMiddleware.php
@@ -0,0 +1,22 @@
+app['auth']->viaRequest('api', function ($request) {
+ * if ($request->input('api_token')) {
+ * return User::where('api_token', $request->input('api_token'))->first();
+ * }
+ * });
+ *
+ * @return void
+ */
+ public function boot()
+ {
+ // Here you may define how you wish users to be authenticated for your Lumen
+ // application. The callback which receives the incoming request instance
+ // should return either a User instance or null. You're free to obtain
+ // the User instance via an API token or any other method necessary.
+
+ LumenPassport::routes($this->app->router);
+ }
+}
diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php
new file mode 100644
index 0000000..0175c14
--- /dev/null
+++ b/app/Providers/EventServiceProvider.php
@@ -0,0 +1,21 @@
+ [
+ 'App\Listeners\ExampleListener',
+ ],
+ ];
+}
diff --git a/app/Services/OrderService.php b/app/Services/OrderService.php
new file mode 100644
index 0000000..2bdd92f
--- /dev/null
+++ b/app/Services/OrderService.php
@@ -0,0 +1,79 @@
+baseUri = config('services.orders.base_uri');
+ $this->secret = config('services.orders.secret');
+ }
+
+ /**
+ * @return string
+ */
+ public function fetchOrders() : string
+ {
+ return $this->request('GET', '/api/order');
+ }
+
+ /**
+ * @param $order
+ *
+ * @return string
+ */
+ public function fetchOrder($order) : string
+ {
+ return $this->request('GET', "/api/order/{$order}");
+ }
+
+ /**
+ * @param $data
+ *
+ * @return string
+ */
+ public function createOrder($data) : string
+ {
+ return $this->request('POST', '/api/order', $data);
+ }
+
+ /**
+ * @param $order
+ * @param $data
+ *
+ * @return string
+ */
+ public function updateOrder($order, $data) : string
+ {
+ return $this->request('PATCH', "/api/order/{$order}", $data);
+ }
+
+ /**
+ * @param $order
+ *
+ * @return string
+ */
+ public function deleteOrder($order) : string
+ {
+ return $this->request('DELETE', "/api/order/{$order}");
+ }
+}
diff --git a/app/Services/ProductService.php b/app/Services/ProductService.php
new file mode 100644
index 0000000..9e4e414
--- /dev/null
+++ b/app/Services/ProductService.php
@@ -0,0 +1,82 @@
+baseUri = config('services.products.base_uri');
+ $this->secret = config('services.products.secret');
+ }
+
+ /**
+ * @return string
+ */
+ public function fetchProducts() : string
+ {
+ return $this->request('GET', '/api/product');
+ }
+
+ /**
+ * @param $product
+ *
+ * @return string
+ */
+ public function fetchProduct($product) : string
+ {
+ return $this->request('GET', "/api/product/{$product}");
+ }
+
+ /**
+ * @param $data
+ *
+ * @return string
+ */
+ public function createProduct($data) : string
+ {
+ return $this->request('POST', '/api/product', $data);
+ }
+
+ /**
+ * @param $product
+ * @param $data
+ *
+ * @return string
+ */
+ public function updateProduct($product, $data) : string
+ {
+ return $this->request('PATCH', "/api/product/{$product}", $data);
+ }
+
+ /**
+ * @param $product
+ *
+ * @return string
+ */
+ public function deleteProduct($product) : string
+ {
+ return $this->request('DELETE', "/api/product/{$product}");
+ }
+}
diff --git a/app/Traits/ApiResponse.php b/app/Traits/ApiResponse.php
new file mode 100644
index 0000000..6a6028f
--- /dev/null
+++ b/app/Traits/ApiResponse.php
@@ -0,0 +1,45 @@
+header('Content-Type', 'application/json');
+ }
+
+ /**
+ * @param $errorMessage
+ * @param $statusCode
+ *
+ * @return mixed
+ */
+ public function errorResponse($errorMessage, $statusCode)
+ {
+ return response()->json(['error' => $errorMessage, 'error_code' => $statusCode], $statusCode);
+ }
+
+ /**
+ * @param $errorMessage
+ * @param $statusCode
+ *
+ * @return mixed
+ */
+ public function errorMessage($errorMessage, $statusCode)
+ {
+ return response($errorMessage, $statusCode)->header('Content-Type', 'application/json');
+ }
+}
diff --git a/app/Traits/RequestService.php b/app/Traits/RequestService.php
new file mode 100644
index 0000000..feaa03d
--- /dev/null
+++ b/app/Traits/RequestService.php
@@ -0,0 +1,39 @@
+ $this->baseUri
+ ]);
+
+ if (isset($this->secret)) {
+ $headers['Authorization'] = $this->secret;
+ }
+
+ $response = $client->request($method, $requestUrl,
+ [
+ 'form_params' => $formParams,
+ 'headers' => $headers
+ ]
+ );
+
+ return $response->getBody()->getContents();
+ }
+}
diff --git a/app/User.php b/app/User.php
new file mode 100644
index 0000000..0ce760e
--- /dev/null
+++ b/app/User.php
@@ -0,0 +1,34 @@
+make(
+ 'Illuminate\Contracts\Console\Kernel'
+);
+
+exit($kernel->handle(new ArgvInput, new ConsoleOutput));
diff --git a/bootstrap/app.php b/bootstrap/app.php
new file mode 100644
index 0000000..bee8cab
--- /dev/null
+++ b/bootstrap/app.php
@@ -0,0 +1,118 @@
+bootstrap();
+} catch (Dotenv\Exception\InvalidPathException $e) {
+ //
+}
+
+/*
+|--------------------------------------------------------------------------
+| Create The Application
+|--------------------------------------------------------------------------
+|
+| Here we will load the environment and create the application instance
+| that serves as the central piece of this framework. We'll use this
+| application as an "IoC" container and router for this framework.
+|
+*/
+
+$app = new Laravel\Lumen\Application(
+ dirname(__DIR__)
+);
+
+ $app->withFacades();
+
+ $app->withEloquent();
+
+
+ $app->configure('services');
+ $app->configure('auth');
+
+/*
+|--------------------------------------------------------------------------
+| Register Container Bindings
+|--------------------------------------------------------------------------
+|
+| Now we will register a few bindings in the service container. We will
+| register the exception handler and the console kernel. You may add
+| your own bindings here if you like or you can make another file.
+|
+*/
+
+$app->singleton(
+ Illuminate\Contracts\Debug\ExceptionHandler::class,
+ App\Exceptions\Handler::class
+);
+
+$app->singleton(
+ Illuminate\Contracts\Console\Kernel::class,
+ App\Console\Kernel::class
+);
+
+/*
+|--------------------------------------------------------------------------
+| Register Middleware
+|--------------------------------------------------------------------------
+|
+| Next, we will register the middleware with the application. These can
+| be global middleware that run before and after each request into a
+| route or middleware that'll be assigned to some specific routes.
+|
+*/
+
+// $app->middleware([
+// App\Http\Middleware\ExampleMiddleware::class
+// ]);
+
+ $app->routeMiddleware([
+ 'auth' => App\Http\Middleware\Authenticate::class,
+ 'client.credentials' => \Laravel\Passport\Http\Middleware\CheckClientCredentials::class,
+ ]);
+
+
+
+/*
+|--------------------------------------------------------------------------
+| Register Service Providers
+|--------------------------------------------------------------------------
+|
+| Here we will register all of the application's service providers which
+| are used to bind services into the container. Service providers are
+| totally optional, so you are not required to uncomment this line.
+|
+*/
+
+// $app->register(App\Providers\AppServiceProvider::class);
+ $app->register(App\Providers\AuthServiceProvider::class);
+// $app->register(App\Providers\EventServiceProvider::class);
+
+
+$app->register(Laravel\Passport\PassportServiceProvider::class);
+$app->register(Dusterio\LumenPassport\PassportServiceProvider::class);
+
+
+/*
+|--------------------------------------------------------------------------
+| Load The Application Routes
+|--------------------------------------------------------------------------
+|
+| Next we will include the routes file so that they can all be added to
+| the application. This will provide all of the URLs the application
+| can respond to, as well as the controllers that may handle them.
+|
+*/
+
+$app->router->group([
+ 'namespace' => 'App\Http\Controllers',
+], function ($router) {
+ require __DIR__.'/../routes/web.php';
+});
+
+return $app;
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..72f7028
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,46 @@
+{
+ "name": "laravel/lumen",
+ "description": "The Laravel Lumen Framework.",
+ "keywords": ["framework", "laravel", "lumen"],
+ "license": "MIT",
+ "type": "project",
+ "require": {
+ "php": ">=7.1.3",
+ "dusterio/lumen-passport": "^0.2.15",
+ "guzzlehttp/guzzle": "^6.5",
+ "laravel/lumen-framework": "5.8.*",
+ "lcobucci/jwt": "3.3.3",
+ "vlucas/phpdotenv": "^3.3"
+ },
+ "require-dev": {
+ "fzaninotto/faker": "~1.4",
+ "mockery/mockery": "~1.0",
+ "phpunit/phpunit": "~7.0"
+ },
+ "autoload": {
+ "classmap": [
+ "database/seeds",
+ "database/factories"
+ ],
+ "psr-4": {
+ "App\\": "app/"
+ }
+ },
+ "autoload-dev": {
+ "classmap": [
+ "tests/"
+ ]
+ },
+ "scripts": {
+ "post-root-package-install": [
+ "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
+ ]
+ },
+ "config": {
+ "preferred-install": "dist",
+ "sort-packages": true,
+ "optimize-autoloader": true
+ },
+ "minimum-stability": "dev",
+ "prefer-stable": true
+}
diff --git a/config/auth.php b/config/auth.php
new file mode 100644
index 0000000..65cd3c2
--- /dev/null
+++ b/config/auth.php
@@ -0,0 +1,24 @@
+ [
+ 'guard' => 'api',
+ 'passwords' => 'users',
+ ],
+
+ 'guards' => [
+ 'api' => [
+ 'driver' => 'passport',
+ 'provider' => 'users',
+ ],
+ ],
+
+ 'providers' => [
+ 'users' => [
+ 'driver' => 'eloquent',
+ 'model' => \App\User::class
+ ]
+ ]
+];
diff --git a/config/services.php b/config/services.php
new file mode 100644
index 0000000..6ed3ef3
--- /dev/null
+++ b/config/services.php
@@ -0,0 +1,14 @@
+ [
+ 'base_uri' => env('PRODUCTS_SERVICE_BASE_URI'),
+ 'secret' => env('PRODUCTS_SERVICE_SECRET')
+ ],
+ 'orders' => [
+ 'base_uri' => env('ORDERS_SERVICE_BASE_URI'),
+ 'secret' => env('ORDERS_SERVICE_SECRET'),
+ ]
+];
diff --git a/database/factories/ModelFactory.php b/database/factories/ModelFactory.php
new file mode 100644
index 0000000..7594576
--- /dev/null
+++ b/database/factories/ModelFactory.php
@@ -0,0 +1,14 @@
+bigIncrements('id');
+ $table->string('name', 256);
+ $table->string('email', 256);
+ $table->string('password', 512);
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down() : void
+ {
+ Schema::dropIfExists('users');
+ }
+}
diff --git a/database/seeds/DatabaseSeeder.php b/database/seeds/DatabaseSeeder.php
new file mode 100644
index 0000000..532d795
--- /dev/null
+++ b/database/seeds/DatabaseSeeder.php
@@ -0,0 +1,19 @@
+call(UserTableSeeder::class);
+ }
+}
diff --git a/database/seeds/UserTableSeeder.php b/database/seeds/UserTableSeeder.php
new file mode 100644
index 0000000..9d9fe72
--- /dev/null
+++ b/database/seeds/UserTableSeeder.php
@@ -0,0 +1,23 @@
+ 'user1',
+ 'email' => 'user1@gmail.com',
+ 'password' => Hash::make('123456')
+ ]);
+ }
+}
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100644
index 0000000..b8c2751
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,26 @@
+
+
+
+
+ ./tests
+
+
+
+
+ ./app
+
+
+
+
+
+
+
+
diff --git a/public/.htaccess b/public/.htaccess
new file mode 100644
index 0000000..b75525b
--- /dev/null
+++ b/public/.htaccess
@@ -0,0 +1,21 @@
+
+
+ Options -MultiViews -Indexes
+
+
+ RewriteEngine On
+
+ # Handle Authorization Header
+ RewriteCond %{HTTP:Authorization} .
+ RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
+
+ # Redirect Trailing Slashes If Not A Folder...
+ RewriteCond %{REQUEST_FILENAME} !-d
+ RewriteCond %{REQUEST_URI} (.+)/$
+ RewriteRule ^ %1 [L,R=301]
+
+ # Handle Front Controller...
+ RewriteCond %{REQUEST_FILENAME} !-d
+ RewriteCond %{REQUEST_FILENAME} !-f
+ RewriteRule ^ index.php [L]
+
diff --git a/public/index.php b/public/index.php
new file mode 100644
index 0000000..adecaf5
--- /dev/null
+++ b/public/index.php
@@ -0,0 +1,30 @@
+run();
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..3584cb7
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,21 @@
+# Lumen PHP Framework
+
+[](https://travis-ci.org/laravel/lumen-framework)
+[](https://packagist.org/packages/laravel/lumen-framework)
+[](https://packagist.org/packages/laravel/lumen-framework)
+[](https://packagist.org/packages/laravel/lumen-framework)
+[](https://packagist.org/packages/laravel/lumen-framework)
+
+Laravel Lumen is a stunningly fast PHP micro-framework for building web applications with expressive, elegant syntax. We believe development must be an enjoyable, creative experience to be truly fulfilling. Lumen attempts to take the pain out of development by easing common tasks used in the majority of web projects, such as routing, database abstraction, queueing, and caching.
+
+## Official Documentation
+
+Documentation for the framework can be found on the [Lumen website](https://lumen.laravel.com/docs).
+
+## Security Vulnerabilities
+
+If you discover a security vulnerability within Lumen, please send an e-mail to Taylor Otwell at taylor@laravel.com. All security vulnerabilities will be promptly addressed.
+
+## License
+
+The Lumen framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
diff --git a/resources/views/.gitkeep b/resources/views/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/routes/web.php b/routes/web.php
new file mode 100644
index 0000000..3fa8199
--- /dev/null
+++ b/routes/web.php
@@ -0,0 +1,35 @@
+group(['prefix' => 'api', 'middleware' => ['client.credentials']], function () use ($router) {
+$router->group(['prefix' => 'api'], function () use ($router) {
+
+ $router->group(['prefix' => 'product'], function () use ($router) {
+ $router->get('/', ['uses' => 'ProductController@index']);
+ $router->post('/', ['uses' => 'ProductController@store']);
+ $router->get('/{product}', ['uses' => 'ProductController@show']);
+ $router->patch('/{product}', ['uses' => 'ProductController@update']);
+ $router->delete('/{product}', ['uses' => 'ProductController@destroy']);
+ });
+
+ $router->group(['prefix' => 'order'], function () use ($router) {
+ $router->get('/', ['uses' => 'OrderController@index']);
+ $router->post('/', ['uses' => 'OrderController@store']);
+ $router->get('/{order}', ['uses' => 'OrderController@show']);
+ $router->patch('/{order}', ['uses' => 'OrderController@update']);
+ $router->delete('/{order}', ['uses' => 'OrderController@destroy']);
+ });
+
+});
diff --git a/storage/app/.gitignore b/storage/app/.gitignore
new file mode 100644
index 0000000..d6b7ef3
--- /dev/null
+++ b/storage/app/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/storage/framework/cache/.gitignore b/storage/framework/cache/.gitignore
new file mode 100644
index 0000000..d6b7ef3
--- /dev/null
+++ b/storage/framework/cache/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/storage/framework/views/.gitignore b/storage/framework/views/.gitignore
new file mode 100644
index 0000000..d6b7ef3
--- /dev/null
+++ b/storage/framework/views/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/storage/logs/.gitignore b/storage/logs/.gitignore
new file mode 100644
index 0000000..d6b7ef3
--- /dev/null
+++ b/storage/logs/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/tests/ExampleTest.php b/tests/ExampleTest.php
new file mode 100644
index 0000000..1bad6ef
--- /dev/null
+++ b/tests/ExampleTest.php
@@ -0,0 +1,21 @@
+get('/');
+
+ $this->assertEquals(
+ $this->app->version(), $this->response->getContent()
+ );
+ }
+}
diff --git a/tests/TestCase.php b/tests/TestCase.php
new file mode 100644
index 0000000..89a058d
--- /dev/null
+++ b/tests/TestCase.php
@@ -0,0 +1,14 @@
+