Merge pull request #4011 from turbo124/v2

System Log API end points
This commit is contained in:
David Bomba 2020-08-24 21:17:07 +10:00 committed by GitHub
commit 7cf2d10f29
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 591 additions and 42 deletions

View File

@ -0,0 +1,110 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com)
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Filters;
use App\Models\Vendor;
use App\Models\User;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
/**
* SystemLogFilters
*/
class SystemLogFilters extends QueryFilters
{
public function type_id(int $type_id) :Builder
{
return $this->builder->where('type_id', $type_id);
}
public function category_id(int $category_id) :Builder
{
return $this->builder->where('category_id', $category_id);
}
public function event_id(int $event_id) :Builder
{
return $this->builder->where('event_id', $event_id);
}
public function client_id(int $client_id) :Builder
{
return $this->builder->where('client_id', $client_id);
}
/**
* Filter based on search text
*
* @param string query filter
* @return Illuminate\Database\Query\Builder
* @deprecated
*
*/
public function filter(string $filter = '') : Builder
{
if (strlen($filter) == 0) {
return $this->builder;
}
return $this->builder;
// return $this->builder->where(function ($query) use ($filter) {
// $query->where('vendors.name', 'like', '%'.$filter.'%')
// ->orWhere('vendors.id_number', 'like', '%'.$filter.'%')
// ->orWhere('vendor_contacts.first_name', 'like', '%'.$filter.'%')
// ->orWhere('vendor_contacts.last_name', 'like', '%'.$filter.'%')
// ->orWhere('vendor_contacts.email', 'like', '%'.$filter.'%')
// ->orWhere('vendors.custom_value1', 'like', '%'.$filter.'%')
// ->orWhere('vendors.custom_value2', 'like', '%'.$filter.'%')
// ->orWhere('vendors.custom_value3', 'like', '%'.$filter.'%')
// ->orWhere('vendors.custom_value4', 'like', '%'.$filter.'%');
// });
}
/**
* Sorts the list based on $sort
*
* @param string sort formatted as column|asc
* @return Illuminate\Database\Query\Builder
*/
public function sort(string $sort) : Builder
{
$sort_col = explode("|", $sort);
return $this->builder->orderBy($sort_col[0], $sort_col[1]);
}
/**
* Returns the base query
*
* @param int company_id
* @return Illuminate\Database\Query\Builder
* @deprecated
*/
public function baseQuery(int $company_id, User $user) : Builder
{
}
/**
* Filters the query by the users company ID
*
* @param $company_id The company Id
* @return Illuminate\Database\Query\Builder
*/
public function entityFilter()
{
//return $this->builder->whereCompanyId(auth()->user()->company()->id);
return $this->builder->company();
}
}

View File

@ -23,32 +23,6 @@ use Illuminate\Support\Facades\Gate;
class VendorFilters extends QueryFilters
{
/**
* Filter by balance
*
* @param string $balance
* @return Illuminate\Database\Query\Builder
*/
public function balance(string $balance): Builder
{
$parts = $this->split($balance);
return $this->builder->where('balance', $parts->operator, $parts->value);
}
/**
* Filter between balances
*
* @param string balance
* @return Illuminate\Database\Query\Builder
*/
public function between_balance(string $balance): Builder
{
$parts = explode(":", $balance);
return $this->builder->whereBetween('balance', [$parts[0], $parts[1]]);
}
/**
* Filter based on search text
*

View File

@ -146,8 +146,8 @@ class MigrationController extends BaseController
public function purgeCompanySaveSettings(Request $request, Company $company)
{
$company->clients()->delete();
$company->products()->delete();
$company->clients()->forceDelete();
$company->products()->forceDelete();
$company->save();

View File

@ -0,0 +1,17 @@
<?php
/**
* @OA\Schema(
* schema="SystemLog",
* type="object",
* @OA\Property(property="id", type="string", example="AS3df3A", description="The account hashed id"),
* @OA\Property(property="company_id", type="string", example="AS3df3A", description="The company hashed id"),
* @OA\Property(property="user_id", type="string", example="AS3df3A", description="The user_id hashed id"),
* @OA\Property(property="client_id", type="string", example="AS3df3A", description="The client_id hashed id"),
* @OA\Property(property="event_id", type="int", example="1", description="The Log Type ID"),
* @OA\Property(property="category_id", type="int", example="1", description="The Category Type ID"),
* @OA\Property(property="type_id", type="int", example="1", description="The Type Type ID"),
* @OA\Property(property="log", type="object", example="{'key':'value'}", description="The json object of the error"),
* @OA\Property(property="updated_at", type="string", example="2", description="______"),
* @OA\Property(property="created_at", type="string", example="2", description="______"),
* )
*/

View File

@ -0,0 +1,211 @@
<?php
namespace App\Http\Controllers;
use App\Filters\SystemLogFilters;
use App\Models\SystemLog;
use App\Transformers\SystemLogTransformer;
use App\Utils\Traits\MakesHash;
use Illuminate\Http\Request;
class SystemLogController extends BaseController
{
use MakesHash;
protected $entity_type = SystemLog::class;
protected $entity_transformer = SystemLogTransformer::class;
/**
* Show the list of Invoices
*
* @param \App\Filters\InvoiceFilters $filters The filters
*
* @return \Illuminate\Http\Response
*
* @OA\Get(
* path="/api/v1/system_logs",
* operationId="getSystemLogs",
* tags={"system_logs"},
* summary="Gets a list of system logs",
* description="Lists system logs, search and filters allow fine grained lists to be generated.
*
* Query parameters can be added to performed more fine grained filtering of the system logs, these are handled by the SystemLogFilters class which defines the methods available",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Response(
* response=200,
* description="A list of system logs",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/SystemLog"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*
*/
public function index(SystemLogFilters $filters)
{
$system_logs = SystemLog::filter($filters);
return $this->listResponse($system_logs);
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
$error = [
'message' => 'Cannot create system log',
'errors' => new \stdClass
];
return response()->json($error, 400);
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$error = [
'message' => 'Cannot store system log',
'errors' => new \stdClass
];
return response()->json($error, 400);
}
/**
* Display the specified resource.
*
* @param \App\Http\Requests\Invoice\ShowInvoiceRequest $request The request
* @param \App\Models\SystemLog $system_logs The system logs
*
* @return \Illuminate\Http\Response
*
*
* @OA\Get(
* path="/api/v1/system_logs/{id}",
* operationId="showSystemLogs",
* tags={"system_logs"},
* summary="Shows a system_logs",
* description="Displays a system_logs by id",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(
* name="id",
* in="path",
* description="The system_logs Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the system_logs object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/SystemLog"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*
*/
public function show(Request $request, SystemLog $system_log)
{
return $this->itemResponse($system_log);
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
$error = [
'message' => 'Cannot edit system log',
'errors' => new \stdClass
];
return response()->json($error, 400);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$error = [
'message' => 'Cannot update system log',
'errors' => new \stdClass
];
return response()->json($error, 400);
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
$error = [
'message' => 'Cannot destroy system log',
'errors' => new \stdClass
];
return response()->json($error, 400);
}
}

View File

@ -64,7 +64,6 @@ class WebhookHandler implements ShouldQueue
// generate JSON data
$manager = new Manager();
$manager->setSerializer(new ArraySerializer());
// $manager->parseIncludes($include);
$transformer = new $this->getTransformerClassName();
@ -101,7 +100,8 @@ class WebhookHandler implements ShouldQueue
public function failed($exception)
{
$exception->getMessage();
// etc...
info(print_r($exception->getMessage(),1));
}
}

View File

@ -202,6 +202,11 @@ class Client extends BaseModel implements HasLocalePreference
return $this->belongsTo(Country::class, 'shipping_country_id', 'id');
}
public function system_logs()
{
return $this->hasMany(SystemLog::class);
}
public function timezone()
{
return Timezone::find($this->getSetting('timezone_id'));

View File

@ -391,6 +391,11 @@ class Company extends BaseModel
return $this->hasMany(CompanyToken::class);
}
public function system_logs()
{
return $this->hasMany(SystemLog::class);
}
public function tokens_hashed()
{
return $this->hasMany(CompanyToken::class);

View File

@ -11,10 +11,26 @@
namespace App\Models;
use App\Models\Filterable;
use App\Utils\Traits\MakesHash;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class SystemLog extends Model
{
use Filterable;
use SoftDeletes;
use MakesHash;
protected $casts = [
'updated_at' => 'timestamp',
'created_at' => 'timestamp',
'deleted_at' => 'timestamp',
'log' => 'array'
];
protected $dateFormat = 'Y-m-d H:i:s.u';
/* Category IDs */
const CATEGORY_GATEWAY_RESPONSE = 1;
const CATEGORY_MAIL = 2;
@ -51,7 +67,23 @@ class SystemLog extends Model
'type_id',
];
protected $casts = [
'log' => 'array'
];
public function resolveRouteBinding($value)
{
if (is_numeric($value)) {
throw new ModelNotFoundException("Record with value {$value} not found");
}
return $this
->where('id', $this->decodePrimaryKey($value))->firstOrFail();
}
/*
V2 type of scope
*/
public function scopeCompany($query)
{
$query->where('company_id', auth()->user()->companyId());
return $query;
}
}

View File

@ -17,10 +17,12 @@ use App\Models\ClientContact;
use App\Models\ClientGatewayToken;
use App\Models\CompanyLedger;
use App\Models\Document;
use App\Models\SystemLog;
use App\Transformers\ActivityTransformer;
use App\Transformers\ClientGatewayTokenTransformer;
use App\Transformers\CompanyLedgerTransformer;
use App\Transformers\DocumentTransformer;
use App\Transformers\SystemLogTransformer;
use App\Utils\Traits\MakesHash;
/**
@ -44,6 +46,7 @@ class ClientTransformer extends EntityTransformer
'gateway_tokens',
'activities',
'ledger',
'system_logs',
];
@ -91,6 +94,14 @@ class ClientTransformer extends EntityTransformer
return $this->includeCollection($client->ledger, $transformer, CompanyLedger::class);
}
public function includeSystemLogs(Client $client)
{
$transformer = new SystemLogTransformer($this->serializer);
return $this->includeCollection($client->system_logs, $transformer, SystemLog::class);
}
/**
* @param Client $client
*

View File

@ -27,7 +27,6 @@ class CompanyGatewayTransformer extends EntityTransformer
* @var array
*/
protected $defaultIncludes = [
'gateway'
];
/**
@ -63,6 +62,8 @@ class CompanyGatewayTransformer extends EntityTransformer
'custom_value2' => $company_gateway->custom_value2 ?: '',
'custom_value3' => $company_gateway->custom_value3 ?: '',
'custom_value4' => $company_gateway->custom_value4 ?: '',
'label' => (string)$company_gateway->label ?: '',
'token_billing' => (string)$company_gateway->token_billing,
];
}

View File

@ -29,8 +29,9 @@ use App\Models\PaymentTerm;
use App\Models\Product;
use App\Models\Project;
use App\Models\Quote;
use App\Models\SystemLog;
use App\Models\Task;
use App\Models\TaxRate;
use App\Models\TaxRate;
use App\Models\User;
use App\Models\Webhook;
use App\Transformers\CompanyLedgerTransformer;
@ -39,6 +40,7 @@ use App\Transformers\CompanyTokenTransformer;
use App\Transformers\CreditTransformer;
use App\Transformers\DocumentTransformer;
use App\Transformers\PaymentTermTransformer;
use App\Transformers\SystemLogTransformer;
use App\Transformers\TaskTransformer;
use App\Transformers\WebhookTransformer;
use App\Utils\Traits\MakesHash;
@ -88,7 +90,8 @@ class CompanyTransformer extends EntityTransformer
'ledger',
'webhooks',
'tokens',
'tokens_hashed'
'tokens_hashed',
'system_logs',
];
@ -307,4 +310,11 @@ class CompanyTransformer extends EntityTransformer
return $this->includeCollection($company->payment_terms()->get(), $transformer, PaymentTerm::class);
}
public function includeSystemLogs(Company $company)
{
$transformer = new SystemLogTransformer($this->serializer);
return $this->includeCollection($company->system_logs, $transformer, SystemLog::class);
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace App\Transformers;
use App\Models\SystemLog;
use App\Utils\Traits\MakesHash;
class SystemLogTransformer extends EntityTransformer
{
use MakesHash;
protected $defaultIncludes = [];
/**
* @var array
*/
protected $availableIncludes = [];
/**
* @param Activity $system_log
*
* @return array
*/
public function transform(SystemLog $system_log)
{
return [
'id' => (string) $this->encodePrimaryKey($system_log->id),
'company_id' => (string) $this->encodePrimaryKey($system_log->company_id),
'user_id' => (string) $this->encodePrimaryKey($system_log->user_id),
'client_id' => (string) $this->encodePrimaryKey($system_log->client_id),
'event_id' => (int) $system_log->event_id,
'category_id' => (int) $system_log->category_id,
'type_id' => (int) $system_log->type_id,
'log' => (string) $system_log->log,
'updated_at' => (int)$system_log->updated_at,
'created_at' => (int)$system_log->created_at,
];
}
}

View File

@ -481,7 +481,7 @@ class CreateUsersTable extends Migration
$t->boolean('custom_surcharge_tax3')->default(false);
$t->boolean('custom_surcharge_tax4')->default(false);
$t->decimal('exchange_rate', 16, 4);
$t->decimal('exchange_rate', 13, 6)->default(1);
$t->decimal('amount', 16, 4);
$t->decimal('balance', 16, 4);
$t->decimal('partial', 16, 4)->nullable();
@ -559,7 +559,7 @@ class CreateUsersTable extends Migration
$t->boolean('custom_surcharge_tax3')->default(false);
$t->boolean('custom_surcharge_tax4')->default(false);
$t->decimal('exchange_rate', 16, 4);
$t->decimal('exchange_rate', 13, 6)->default(1);
$t->decimal('amount', 16, 4);
$t->decimal('balance', 16, 4);
$t->decimal('partial', 16, 4)->nullable();
@ -803,7 +803,7 @@ class CreateUsersTable extends Migration
$t->boolean('custom_surcharge_tax3')->default(false);
$t->boolean('custom_surcharge_tax4')->default(false);
$t->decimal('exchange_rate', 16, 4);
$t->decimal('exchange_rate', 13, 6)->default(1);
$t->decimal('amount', 16, 4);
$t->decimal('balance', 16, 4);
$t->decimal('partial', 16, 4)->nullable();
@ -964,7 +964,7 @@ class CreateUsersTable extends Migration
$t->softDeletes('deleted_at', 6);
$t->boolean('is_deleted')->default(false);
$t->boolean('is_manual')->default(false);
$t->decimal('exchange_rate', 16, 6)->default(1);
$t->decimal('exchange_rate', 13, 6)->default(1);
$t->unsignedInteger('currency_id');
$t->unsignedInteger('exchange_currency_id');
@ -1297,7 +1297,7 @@ class CreateUsersTable extends Migration
$table->boolean('is_deleted')->default(false);
$table->decimal('amount', 13, 2);
$table->decimal('foreign_amount', 13, 2);
$table->decimal('exchange_rate', 13, 4);
$table->decimal('exchange_rate', 13, 6)->default(1);
$table->string('tax_name1')->nullable();
$table->decimal('tax_rate1', 13, 3)->default(0);
$table->string('tax_name2')->nullable();

View File

@ -31,6 +31,11 @@ class AddIsPublicToDocumentsTable extends Migration
$table->text('meta')->nullable();
});
Schema::table('system_logs', function (Blueprint $table) {
$table->softDeletes('deleted_at', 6);
});
}
/**

View File

@ -146,6 +146,8 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a
// Route::delete('hooks/{subscription_id}', 'SubscriptionController@unsubscribe')->name('hooks.unsubscribe');
Route::resource('webhooks', 'WebhookController');
Route::resource('system_logs', 'SystemLogController');
Route::post('webhooks/bulk', 'WebhookController@bulk')->name('webhooks.bulk');
/*Company Ledger */

View File

@ -0,0 +1,127 @@
<?php
namespace Tests\Feature;
use App\DataMapper\DefaultSettings;
use App\Models\Account;
use App\Models\Client;
use App\Models\ClientContact;
use App\Models\Company;
use App\Models\SystemLog;
use App\Models\User;
use App\Utils\Traits\MakesHash;
use Faker\Factory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Session;
use Tests\MockAccountData;
use Tests\TestCase;
/**
* @test
* @covers App\Http\Controllers\SystemLogController
*/
class SystemLogApiTest extends TestCase
{
use MakesHash;
use DatabaseTransactions;
use MockAccountData;
public function setUp() :void
{
parent::setUp();
$this->makeTestData();
}
public function testSystemLogRoutes()
{
$sl = [
'client_id' => $this->client->id,
'company_id' => $this->company->id,
'user_id' => $this->client->user_id,
'log' => 'thelog',
'category_id' => 1,
'event_id' => 1,
'type_id' => 1,
];
SystemLog::create($sl);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token
])->get('/api/v1/system_logs');
$response->assertStatus(200);
$arr = $response->json();
$this->assertTrue(count($arr['data']) >=1);
$hashed_id = $arr['data'][0]['id'];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token
])->get('/api/v1/system_logs/' . $hashed_id);
$response->assertStatus(200);
$arr = $response->json();
$this->assertEquals($hashed_id, $arr['data']['id']);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token
])->put('/api/v1/system_logs/' . $hashed_id, $sl)->assertStatus(400);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token
])->delete('/api/v1/system_logs/' . $hashed_id)->assertStatus(400);
}
public function testStoreRouteFails()
{
$sl = [
'client_id' => $this->client->id,
'company_id' => $this->company->id,
'user_id' => $this->client->user_id,
'log' => 'thelog',
'category_id' => 1,
'event_id' => 1,
'type_id' => 1,
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token
])->post('/api/v1/system_logs', $sl)->assertStatus(400);
}
public function testCreateRouteFails()
{
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token
])->get('/api/v1/system_logs/create')->assertStatus(400);
}
}