Merge branch 'master' into 'main'

Master

See merge request izyim/prototypes/lumen-microservices/microservice-products-api!1
This commit is contained in:
EDMOND MING
2022-09-30 11:34:33 +00:00
39 changed files with 946 additions and 0 deletions
+22
View File
@@ -0,0 +1,22 @@
APP_NAME=ProductsApi
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-products_api
DB_USERNAME=
DB_PASSWORD=
CACHE_DRIVER=file
QUEUE_CONNECTION=sync
ALLOWED_SECRETS=
+6
View File
@@ -0,0 +1,6 @@
/vendor
/.idea
Homestead.json
Homestead.yaml
.env
composer.lock
View File
+29
View File
@@ -0,0 +1,29 @@
<?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Laravel\Lumen\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands = [
//
];
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
//
}
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Events;
use Illuminate\Queue\SerializesModels;
abstract class Event
{
use SerializesModels;
}
+16
View File
@@ -0,0 +1,16 @@
<?php
namespace App\Events;
class ExampleEvent extends Event
{
/**
* Create a new event instance.
*
* @return void
*/
public function __construct()
{
//
}
}
+95
View File
@@ -0,0 +1,95 @@
<?php
namespace App\Exceptions;
use App\Traits\ApiResponse;
use Exception;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Http\Response;
use Illuminate\Validation\ValidationException;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Laravel\Lumen\Exceptions\Handler as ExceptionHandler;
use Symfony\Component\HttpKernel\Exception\HttpException;
class Handler extends ExceptionHandler
{
use ApiResponse;
/**
* A list of the exception types that should not be reported.
*
* @var array
*/
protected $dontReport = [
AuthorizationException::class,
HttpException::class,
ModelNotFoundException::class,
ValidationException::class,
];
/**
* Report or log an exception.
*
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
*
* @param \Exception $exception
* @return void
*/
public function report(Exception $exception)
{
parent::report($exception);
}
/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $exception
* @return \Illuminate\Http\Response
*/
public function render($request, Exception $exception)
{
if ($exception instanceof HttpException) {
$errorCode = $exception->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(env('APP_DEBUG',false)){
return parent::render($request, $exception);
}
return $this->errorResponse(
"Unexpected error. Try later!",
Response::HTTP_INTERNAL_SERVER_ERROR);
}
}
+11
View File
@@ -0,0 +1,11 @@
<?php
namespace App\Http\Controllers;
use App\Traits\ApiResponse;
use Laravel\Lumen\Routing\Controller as BaseController;
class Controller extends BaseController
{
use ApiResponse;
}
@@ -0,0 +1,67 @@
<?php
namespace App\Http\Controllers;
use App\Product;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
class ProductController extends Controller
{
public function index()
{
$products = Product::all();
return $this->successResponse($products);
}
public function show($product)
{
$product = Product::findOrFail($product);
return $this->successResponse($product);
}
public function store(Request $request)
{
$rules = [
'name' => 'required|max:255',
'price' => 'required|numeric|min:1|max:200'
];
$this->validate($request, $rules);
$product = Product::create($request->all());
return $this->successResponse($product);
}
public function update(Request $request, $product)
{
$rules = [
'name' => 'max:255',
'price' => 'numeric|min:1|max:200'
];
$this->validate($request, $rules);
$product = Product::findOrFail($product);
$product = $product->fill($request->all());
if ($product->isClean()) {
return $this->errorResponse('at least one value must be change',
Response::HTTP_UNPROCESSABLE_ENTITY);
}
$product->save();
return $this->successResponse($product);
}
public function destroy($product)
{
$product = Product::findOrFail($product);
$product->delete();
return $this->successResponse($product);
}
}
+44
View File
@@ -0,0 +1,44 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\Factory as Auth;
class Authenticate
{
/**
* The authentication guard factory instance.
*
* @var \Illuminate\Contracts\Auth\Factory
*/
protected $auth;
/**
* Create a new middleware instance.
*
* @param \Illuminate\Contracts\Auth\Factory $auth
* @return void
*/
public function __construct(Auth $auth)
{
$this->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);
}
}
@@ -0,0 +1,27 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Response;
class AuthenticateAccess
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
$allowedSecrets = explode(',', env('ALLOWED_SECRETS'));
if (in_array($request->header('Authorization'), $allowedSecrets)) {
return $next($request);
}
abort(Response::HTTP_UNAUTHORIZED);
}
}
+20
View File
@@ -0,0 +1,20 @@
<?php
namespace App\Http\Middleware;
use Closure;
class ExampleMiddleware
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
return $next($request);
}
}
+26
View File
@@ -0,0 +1,26 @@
<?php
namespace App\Jobs;
class ExampleJob extends Job
{
/**
* Create a new job instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
//
}
}
+24
View File
@@ -0,0 +1,24 @@
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
abstract class Job implements ShouldQueue
{
/*
|--------------------------------------------------------------------------
| Queueable Jobs
|--------------------------------------------------------------------------
|
| This job base class provides a central location to place any logic that
| is shared across all of your jobs. The trait included with the class
| provides access to the "queueOn" and "delay" queue helper methods.
|
*/
use InteractsWithQueue, Queueable, SerializesModels;
}
+31
View File
@@ -0,0 +1,31 @@
<?php
namespace App\Listeners;
use App\Events\ExampleEvent;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class ExampleListener
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param ExampleEvent $event
* @return void
*/
public function handle(ExampleEvent $event)
{
//
}
}
+19
View File
@@ -0,0 +1,19 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name', 'price'
];
}
+18
View File
@@ -0,0 +1,18 @@
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
}
+39
View File
@@ -0,0 +1,39 @@
<?php
namespace App\Providers;
use App\User;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
/**
* Boot the authentication services for the application.
*
* @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.
$this->app['auth']->viaRequest('api', function ($request) {
if ($request->input('api_token')) {
return User::where('api_token', $request->input('api_token'))->first();
}
});
}
}
+19
View File
@@ -0,0 +1,19 @@
<?php
namespace App\Providers;
use Laravel\Lumen\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
'App\Events\ExampleEvent' => [
'App\Listeners\ExampleListener',
],
];
}
+19
View File
@@ -0,0 +1,19 @@
<?php
namespace App\Traits;
use Illuminate\Http\Response;
trait ApiResponse
{
public function successResponse($data, $statusCode = Response::HTTP_OK)
{
return response()->json(['data' => $data], $statusCode);
}
public function errorResponse($errorMessage, $statusCode)
{
return response()->json(['error' => $errorMessage, 'error_code' => $statusCode], $statusCode);
}
}
+35
View File
@@ -0,0 +1,35 @@
#!/usr/bin/env php
<?php
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Output\ConsoleOutput;
/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| First we need to get an application instance. This creates an instance
| of the application / container and bootstraps the application so it
| is ready to receive HTTP / Console requests from the environment.
|
*/
$app = require __DIR__.'/bootstrap/app.php';
/*
|--------------------------------------------------------------------------
| Run The Artisan Application
|--------------------------------------------------------------------------
|
| When we run the console application, the current CLI command will be
| executed in this console and the response sent back to a terminal
| or another output device for the developers. Here goes nothing!
|
*/
$kernel = $app->make(
'Illuminate\Contracts\Console\Kernel'
);
exit($kernel->handle(new ArgvInput, new ConsoleOutput));
+103
View File
@@ -0,0 +1,103 @@
<?php
require_once __DIR__ . '/../vendor/autoload.php';
try {
$dotenv = Dotenv\Dotenv::create(__DIR__ . '/..');
$dotenv->load();
} 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();
/*
|--------------------------------------------------------------------------
| 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\AuthenticateAccess::class
]);
// $app->routeMiddleware([
// 'auth' => App\Http\Middleware\Authenticate::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);
/*
|--------------------------------------------------------------------------
| 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;
+49
View File
@@ -0,0 +1,49 @@
{
"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,
"allow-plugins": {
"kylekatarnls/update-helper": true
}
},
"minimum-stability": "dev",
"prefer-stable": true
}
+19
View File
@@ -0,0 +1,19 @@
<?php
/*
|--------------------------------------------------------------------------
| Model Factories
|--------------------------------------------------------------------------
|
| Here you may define all of your model factories. Model factories give
| you a convenient way to create models for testing and seeding your
| database. Just tell the factory how a default model should look.
|
*/
$factory->define(App\Product::class, function (Faker\Generator $faker) {
return [
'name' => $faker->userName,
'price' => $faker->numberBetween(10,200),
];
});
View File
@@ -0,0 +1,33 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateProductsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('products', function (Blueprint $table) {
$table->increments('id');
$table->string('name')->nullable();
$table->decimal('price')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('products');
}
}
+18
View File
@@ -0,0 +1,18 @@
<?php
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
// $this->call('UsersTableSeeder');
factory(\App\Product::class, 50)->create();
}
}
+26
View File
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="bootstrap/app.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false">
<testsuites>
<testsuite name="Application Test Suite">
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">./app</directory>
</whitelist>
</filter>
<php>
<env name="APP_ENV" value="testing"/>
<env name="CACHE_DRIVER" value="array"/>
<env name="QUEUE_CONNECTION" value="sync"/>
</php>
</phpunit>
+21
View File
@@ -0,0 +1,21 @@
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews -Indexes
</IfModule>
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]
</IfModule>
+28
View File
@@ -0,0 +1,28 @@
<?php
/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| First we need to get an application instance. This creates an instance
| of the application / container and bootstraps the application so it
| is ready to receive HTTP / Console requests from the environment.
|
*/
$app = require __DIR__.'/../bootstrap/app.php';
/*
|--------------------------------------------------------------------------
| Run The Application
|--------------------------------------------------------------------------
|
| Once we have the application, we can handle the incoming request
| through the kernel, and send the associated response back to
| the client's browser allowing them to enjoy the creative
| and wonderful application we have prepared for them.
|
*/
$app->run();
+6
View File
@@ -0,0 +1,6 @@
# ProductApi
for running a project please run following command on your terminal:
php -S 127.0.0.1:8100 -t public
database creation:
create schema `lu-ms1-products_api` character set utf8 collate utf8_general_ci;
View File
+23
View File
@@ -0,0 +1,23 @@
<?php
/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It is a breeze. Simply tell Lumen the URIs it should respond to
| and give it the Closure to call when that URI is requested.
|
*/
$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']);
});
});
+2
View File
@@ -0,0 +1,2 @@
*
!.gitignore
+2
View File
@@ -0,0 +1,2 @@
*
!.gitignore
+2
View File
@@ -0,0 +1,2 @@
*
!.gitignore
+2
View File
@@ -0,0 +1,2 @@
*
!.gitignore
+21
View File
@@ -0,0 +1,21 @@
<?php
use Laravel\Lumen\Testing\DatabaseMigrations;
use Laravel\Lumen\Testing\DatabaseTransactions;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*
* @return void
*/
public function testExample()
{
$this->get('/');
$this->assertEquals(
$this->app->version(), $this->response->getContent()
);
}
}
+14
View File
@@ -0,0 +1,14 @@
<?php
abstract class TestCase extends Laravel\Lumen\Testing\TestCase
{
/**
* Creates the application.
*
* @return \Laravel\Lumen\Application
*/
public function createApplication()
{
return require __DIR__.'/../bootstrap/app.php';
}
}