mirror of
https://github.com/snipe/snipe-it.git
synced 2024-12-24 05:04:07 -08:00
* remove miselading comment line * added dedicated API endpoint for license seats * don't display a seat name via API it makes no sense and we don't have any particular sorting order so the numbering would be inconsistent anyway * reduce amount of IFs * add sanity checks to show() * fix goofed logging logic * add tests for action log entries
This commit is contained in:
parent
3e934a1b96
commit
90b7d34c69
138
app/Http/Controllers/Api/LicenseSeatsController.php
Normal file
138
app/Http/Controllers/Api/LicenseSeatsController.php
Normal file
|
@ -0,0 +1,138 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Transformers\LicenseSeatsTransformer;
|
||||
use App\Models\Asset;
|
||||
use App\Models\License;
|
||||
use App\Models\LicenseSeat;
|
||||
use App\Models\User;
|
||||
use Auth;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class LicenseSeatsController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(Request $request, $licenseId)
|
||||
{
|
||||
//
|
||||
if ($license = License::find($licenseId)) {
|
||||
$this->authorize('view', $license);
|
||||
|
||||
$seats = LicenseSeat::with('license', 'user', 'asset', 'user.department')
|
||||
->where('license_seats.license_id', $licenseId);
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
if ($request->input('sort')=='department') {
|
||||
$seats->OrderDepartments($order);
|
||||
} else {
|
||||
$seats->orderBy('id', $order);
|
||||
}
|
||||
|
||||
$total = $seats->count();
|
||||
$offset = (($seats) && (request('offset') > $total)) ? 0 : request('offset', 0);
|
||||
$limit = request('limit', 50);
|
||||
|
||||
$seats = $seats->skip($offset)->take($limit)->get();
|
||||
|
||||
if ($seats) {
|
||||
return (new LicenseSeatsTransformer)->transformLicenseSeats($seats, $total);
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/licenses/message.does_not_exist')), 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show($licenseId, $seatId)
|
||||
{
|
||||
//
|
||||
$this->authorize('view', License::class);
|
||||
// sanity checks:
|
||||
// 1. does the license seat exist?
|
||||
if (!$licenseSeat = LicenseSeat::find($seatId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Seat not found'));
|
||||
}
|
||||
// 2. does the seat belong to the specified license?
|
||||
if (!$license = $licenseSeat->license()->first() || $license->id != intval($licenseId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Seat does not belong to the specified license'));
|
||||
}
|
||||
return (new LicenseSeatsTransformer)->transformLicenseSeat($licenseSeat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param int $licenseId
|
||||
* @param int $seatId
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(Request $request, $licenseId, $seatId)
|
||||
{
|
||||
$this->authorize('checkout', License::class);
|
||||
|
||||
// sanity checks:
|
||||
// 1. does the license seat exist?
|
||||
if (!$licenseSeat = LicenseSeat::find($seatId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Seat not found'));
|
||||
}
|
||||
// 2. does the seat belong to the specified license?
|
||||
if (!$license = $licenseSeat->license()->first() || $license->id != intval($licenseId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Seat does not belong to the specified license'));
|
||||
}
|
||||
|
||||
$oldUser = $licenseSeat->user()->first();
|
||||
$oldAsset = $licenseSeat->asset()->first();
|
||||
|
||||
// attempt to update the license seat
|
||||
$licenseSeat->fill($request->all());
|
||||
$licenseSeat->user_id = Auth::user()->id;
|
||||
|
||||
// check if this update is a checkin operation
|
||||
// 1. are relevant fields touched at all?
|
||||
$touched = $licenseSeat->isDirty('assigned_to') || $licenseSeat->isDirty('asset_id');
|
||||
// 2. are they cleared? if yes then this is a checkin operation
|
||||
$is_checkin = ($touched && $licenseSeat->assigned_to === null && $licenseSeat->asset_id === null);
|
||||
|
||||
if (!$touched) {
|
||||
// nothing to update
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $licenseSeat, trans('admin/licenses/message.update.success')));
|
||||
}
|
||||
|
||||
if ($licenseSeat->save()) {
|
||||
// the logging functions expect only one "target". if both asset and user are present in the request,
|
||||
// we simply let assets take precedence over users...
|
||||
$changes = $licenseSeat->getChanges();
|
||||
if (array_key_exists('assigned_to', $changes)) {
|
||||
$target = $is_checkin ? $oldUser : User::find($changes['assigned_to']);
|
||||
}
|
||||
if (array_key_exists('asset_id', $changes)) {
|
||||
$target = $is_checkin ? $oldAsset : Asset::find($changes['asset_id']);
|
||||
}
|
||||
|
||||
if ($is_checkin) {
|
||||
$licenseSeat->logCheckin($target, $request->input('note'));
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $licenseSeat, trans('admin/licenses/message.update.success')));
|
||||
}
|
||||
|
||||
// in this case, relevant fields are touched but it's not a checkin operation. so it must be a checkout operation.
|
||||
$licenseSeat->logCheckout($request->input('note'), $target);
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $licenseSeat, trans('admin/licenses/message.update.success')));
|
||||
}
|
||||
|
||||
return Helper::formatStandardApiResponse('error', null, $licenseSeat->getErrors());
|
||||
}
|
||||
}
|
|
@ -237,50 +237,6 @@ class LicensesController extends Controller
|
|||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/licenses/message.assoc_users')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get license seat listing
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
* @param int $licenseId
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function seats(Request $request, $licenseId)
|
||||
{
|
||||
|
||||
if ($license = License::find($licenseId)) {
|
||||
|
||||
$this->authorize('view', $license);
|
||||
|
||||
$seats = LicenseSeat::with('license', 'user', 'asset', 'user.department')
|
||||
->where('license_seats.license_id', $licenseId);
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
if ($request->input('sort')=='department') {
|
||||
$seats->OrderDepartments($order);
|
||||
} else {
|
||||
$seats->orderBy('id', $order);
|
||||
}
|
||||
|
||||
$offset = (($seats) && (request('offset') > $seats->count())) ? 0 : request('offset', 0);
|
||||
$limit = request('limit', 50);
|
||||
|
||||
$total = $seats->count();
|
||||
|
||||
$seats = $seats->skip($offset)->take($limit)->get();
|
||||
|
||||
if ($seats) {
|
||||
return (new LicenseSeatsTransformer)->transformLicenseSeats($seats, $total);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/licenses/message.does_not_exist')), 200);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets a paginated collection for the select2 menus
|
||||
|
|
|
@ -20,12 +20,11 @@ class LicenseSeatsTransformer
|
|||
return (new DatatablesTransformer)->transformDatatables($array, $total);
|
||||
}
|
||||
|
||||
public function transformLicenseSeat (LicenseSeat $seat, $seat_count)
|
||||
public function transformLicenseSeat (LicenseSeat $seat, $seat_count=0)
|
||||
{
|
||||
$array = [
|
||||
'id' => (int) $seat->id,
|
||||
'license_id' => (int) $seat->license->id,
|
||||
'name' => 'Seat '.$seat_count,
|
||||
'assigned_user' => ($seat->user) ? [
|
||||
'id' => (int) $seat->user->id,
|
||||
'name'=> e($seat->user->present()->fullName),
|
||||
|
@ -49,6 +48,10 @@ class LicenseSeatsTransformer
|
|||
'user_can_checkout' => (($seat->assigned_to=='') && ($seat->asset_id=='')),
|
||||
];
|
||||
|
||||
if($seat_count != 0) {
|
||||
$array['name'] = 'Seat '.$seat_count;
|
||||
}
|
||||
|
||||
$permissions_array['available_actions'] = [
|
||||
'checkout' => Gate::allows('checkout', License::class),
|
||||
'checkin' => Gate::allows('checkin', License::class),
|
||||
|
|
|
@ -20,6 +20,16 @@ class LicenseSeat extends SnipeModel implements ICompanyableChild
|
|||
protected $guarded = 'id';
|
||||
protected $table = 'license_seats';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'assigned_to',
|
||||
'asset_id'
|
||||
];
|
||||
|
||||
use Acceptable;
|
||||
|
||||
public function getCompanyableParents()
|
||||
|
|
|
@ -350,7 +350,7 @@
|
|||
data-sort-order="asc"
|
||||
data-sort-name="name"
|
||||
class="table table-striped snipe-table"
|
||||
data-url="{{ route('api.license.seats', $license->id) }}"
|
||||
data-url="{{ route('api.licenses.seats.index', $license->id) }}"
|
||||
data-export-options='{
|
||||
"fileName": "export-seats-{{ str_slug($license->name) }}-{{ date('Y-m-d') }}",
|
||||
"ignoreColumn": ["actions","image","change","checkbox","checkincheckout","icon"]
|
||||
|
|
|
@ -166,7 +166,6 @@ Route::group(['prefix' => 'v1','namespace' => 'Api', 'middleware' => 'auth:api']
|
|||
|
||||
/*--- Departments API ---*/
|
||||
|
||||
/*--- Suppliers API ---*/
|
||||
Route::group(['prefix' => 'departments'], function () {
|
||||
|
||||
|
||||
|
@ -496,11 +495,6 @@ Route::group(['prefix' => 'v1','namespace' => 'Api', 'middleware' => 'auth:api']
|
|||
/*--- Licenses API ---*/
|
||||
|
||||
Route::group(['prefix' => 'licenses'], function () {
|
||||
Route::get('{licenseId}/seats', [
|
||||
'as' => 'api.license.seats',
|
||||
'uses' => 'LicensesController@seats'
|
||||
]);
|
||||
|
||||
Route::get('selectlist',
|
||||
[
|
||||
'as' => 'api.licenses.selectlist',
|
||||
|
@ -525,7 +519,18 @@ Route::group(['prefix' => 'v1','namespace' => 'Api', 'middleware' => 'auth:api']
|
|||
]
|
||||
); // Licenses resource
|
||||
|
||||
|
||||
Route::resource('licenses.seats', 'LicenseSeatsController',
|
||||
[
|
||||
'names' =>
|
||||
[
|
||||
'index' => 'api.licenses.seats.index',
|
||||
'show' => 'api.licenses.seats.show',
|
||||
'update' => 'api.licenses.seats.update'
|
||||
],
|
||||
'except' => ['create', 'edit', 'destroy', 'store'],
|
||||
'parameters' => ['licenseseat' => 'licenseseat_id']
|
||||
]
|
||||
); // Licenseseats resource
|
||||
|
||||
/*--- Locations API ---*/
|
||||
|
||||
|
|
185
tests/api/ApiLicenseSeatsCest.php
Normal file
185
tests/api/ApiLicenseSeatsCest.php
Normal file
|
@ -0,0 +1,185 @@
|
|||
<?php
|
||||
|
||||
use App\Http\Transformers\LicenseSeatsTransformer;
|
||||
use App\Models\Asset;
|
||||
use App\Models\License;
|
||||
use App\Models\LicenseSeat;
|
||||
use App\Models\User;
|
||||
|
||||
class ApiLicenseSeatsCest
|
||||
{
|
||||
protected $license;
|
||||
protected $timeFormat;
|
||||
|
||||
public function _before(ApiTester $I)
|
||||
{
|
||||
$this->user = \App\Models\User::find(1);
|
||||
$I->haveHttpHeader('Accept', 'application/json');
|
||||
$I->amBearerAuthenticated($I->getToken($this->user));
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function indexLicenseSeats(ApiTester $I)
|
||||
{
|
||||
$I->wantTo('Get a list of license seats for a specific license');
|
||||
|
||||
// call
|
||||
$I->sendGET('/licenses/1/seats?limit=10&order=desc');
|
||||
$I->seeResponseIsJson();
|
||||
$I->seeResponseCodeIs(200);
|
||||
|
||||
// sample verify
|
||||
$licenseSeats = App\Models\LicenseSeat::where('license_id', 1)
|
||||
->orderBy('id','desc')->take(10)->get();
|
||||
// pick a random seat
|
||||
$licenseSeat = $licenseSeats->random();
|
||||
// need the index in the original list so that the "name" field is determined correctly
|
||||
$licenseSeatNumber = 0;
|
||||
foreach($licenseSeats as $index=>$seat) {
|
||||
if ($licenseSeat === $seat) {
|
||||
$licenseSeatNumber = $index+1;
|
||||
}
|
||||
}
|
||||
$I->seeResponseContainsJson($I->removeTimestamps((new LicenseSeatsTransformer)->transformLicenseSeat($licenseSeat, $licenseSeatNumber)));
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function showLicenseSeat(ApiTester $I)
|
||||
{
|
||||
$I->wantTo('Get a license seat');
|
||||
|
||||
// call
|
||||
$I->sendGET('/licenses/1/seats/10');
|
||||
$I->seeResponseIsJson();
|
||||
$I->seeResponseCodeIs(200);
|
||||
|
||||
// sample verify
|
||||
$licenseSeat = App\Models\LicenseSeat::findOrFail(10);
|
||||
$I->seeResponseContainsJson($I->removeTimestamps((new LicenseSeatsTransformer)->transformLicenseSeat($licenseSeat)));
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function checkoutLicenseSeatToUser(ApiTester $I)
|
||||
{
|
||||
$I->wantTo('Checkout a license seat to a user');
|
||||
|
||||
$user = App\Models\User::all()->random();
|
||||
$licenseSeat = App\Models\LicenseSeat::all()->random();
|
||||
$endpoint = '/licenses/'.$licenseSeat->license_id.'/seats/'.$licenseSeat->id;
|
||||
|
||||
$data = [
|
||||
'assigned_to' => $user->id,
|
||||
'note' => 'Test Checkout to User via API'
|
||||
];
|
||||
|
||||
// update
|
||||
$I->sendPATCH($endpoint, $data);
|
||||
$I->seeResponseIsJson();
|
||||
$I->seeResponseCodeIs(200);
|
||||
|
||||
$response = json_decode($I->grabResponse());
|
||||
$I->assertEquals('success', $response->status);
|
||||
$I->assertEquals(trans('admin/licenses/message.update.success'), $response->messages);
|
||||
$I->assertEquals($licenseSeat->license_id, $response->payload->license_id); // license id does not change
|
||||
$I->assertEquals($licenseSeat->id, $response->payload->id); // license seat id does not change
|
||||
|
||||
// verify
|
||||
$licenseSeat = $licenseSeat->fresh();
|
||||
$I->sendGET($endpoint);
|
||||
$I->seeResponseIsJson();
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->seeResponseContainsJson($I->removeTimestamps((new LicenseSeatsTransformer)->transformLicenseSeat($licenseSeat)));
|
||||
|
||||
// verify that the last logged action is a checkout
|
||||
$I->sendGET('/reports/activity?item_type=license&limit=1&item_id='.$licenseSeat->license_id);
|
||||
$I->seeResponseIsJson();
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->seeResponseContainsJson([
|
||||
"action_type" => "checkout"
|
||||
]);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function checkoutLicenseSeatToAsset(ApiTester $I)
|
||||
{
|
||||
$I->wantTo('Checkout a license seat to an asset');
|
||||
|
||||
$asset = App\Models\Asset::all()->random();
|
||||
$licenseSeat = App\Models\LicenseSeat::all()->random();
|
||||
$endpoint = '/licenses/'.$licenseSeat->license_id.'/seats/'.$licenseSeat->id;
|
||||
|
||||
$data = [
|
||||
'asset_id' => $asset->id,
|
||||
'note' => 'Test Checkout to Asset via API'
|
||||
];
|
||||
|
||||
// update
|
||||
$I->sendPATCH($endpoint, $data);
|
||||
$I->seeResponseIsJson();
|
||||
$I->seeResponseCodeIs(200);
|
||||
|
||||
$response = json_decode($I->grabResponse());
|
||||
$I->assertEquals('success', $response->status);
|
||||
$I->assertEquals(trans('admin/licenses/message.update.success'), $response->messages);
|
||||
$I->assertEquals($licenseSeat->license_id, $response->payload->license_id); // license id does not change
|
||||
$I->assertEquals($licenseSeat->id, $response->payload->id); // license seat id does not change
|
||||
|
||||
// verify
|
||||
$licenseSeat = $licenseSeat->fresh();
|
||||
$I->sendGET($endpoint);
|
||||
$I->seeResponseIsJson();
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->seeResponseContainsJson($I->removeTimestamps((new LicenseSeatsTransformer)->transformLicenseSeat($licenseSeat)));
|
||||
|
||||
// verify that the last logged action is a checkout
|
||||
$I->sendGET('/reports/activity?item_type=license&limit=1&item_id='.$licenseSeat->license_id);
|
||||
$I->seeResponseIsJson();
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->seeResponseContainsJson([
|
||||
"action_type" => "checkout"
|
||||
]);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function checkoutLicenseSeatToUserAndAsset(ApiTester $I)
|
||||
{
|
||||
$I->wantTo('Checkout a license seat to a user AND an asset');
|
||||
|
||||
$asset = App\Models\Asset::all()->random();
|
||||
$user = App\Models\User::all()->random();
|
||||
$licenseSeat = App\Models\LicenseSeat::all()->random();
|
||||
$endpoint = '/licenses/'.$licenseSeat->license_id.'/seats/'.$licenseSeat->id;
|
||||
|
||||
$data = [
|
||||
'asset_id' => $asset->id,
|
||||
'assigned_to' => $user->id,
|
||||
'note' => 'Test Checkout to User and Asset via API'
|
||||
];
|
||||
|
||||
// update
|
||||
$I->sendPATCH($endpoint, $data);
|
||||
$I->seeResponseIsJson();
|
||||
$I->seeResponseCodeIs(200);
|
||||
|
||||
$response = json_decode($I->grabResponse());
|
||||
$I->assertEquals('success', $response->status);
|
||||
$I->assertEquals(trans('admin/licenses/message.update.success'), $response->messages);
|
||||
$I->assertEquals($licenseSeat->license_id, $response->payload->license_id); // license id does not change
|
||||
$I->assertEquals($licenseSeat->id, $response->payload->id); // license seat id does not change
|
||||
|
||||
// verify
|
||||
$licenseSeat = $licenseSeat->fresh();
|
||||
$I->sendGET($endpoint);
|
||||
$I->seeResponseIsJson();
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->seeResponseContainsJson($I->removeTimestamps((new LicenseSeatsTransformer)->transformLicenseSeat($licenseSeat)));
|
||||
|
||||
// verify that the last logged action is a checkout
|
||||
$I->sendGET('/reports/activity?item_type=license&limit=1&item_id='.$licenseSeat->license_id);
|
||||
$I->seeResponseIsJson();
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->seeResponseContainsJson([
|
||||
"action_type" => "checkout"
|
||||
]);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue