Add support for etag and not modified resources
This commit is contained in:
parent
14183e390c
commit
235a70160b
|
@ -16,10 +16,20 @@ class AlertController extends Controller
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
return response()->json(
|
return response()->json(
|
||||||
Alert::with('crew.user')
|
request()->query('full', false) ?
|
||||||
->where('closed', false)
|
Alert::with(['crew.user' => [
|
||||||
->orderBy('created_at', 'desc')
|
"name",
|
||||||
->get()
|
"username",
|
||||||
|
"chief",
|
||||||
|
"driver"
|
||||||
|
]])
|
||||||
|
->where('closed', false)
|
||||||
|
->orderBy('created_at', 'desc')
|
||||||
|
->get()
|
||||||
|
:
|
||||||
|
Alert::where('closed', false)
|
||||||
|
->orderBy('created_at', 'desc')
|
||||||
|
->get()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,10 +123,15 @@ class AlertController extends Controller
|
||||||
*/
|
*/
|
||||||
public function show(Request $request, $id)
|
public function show(Request $request, $id)
|
||||||
{
|
{
|
||||||
User::where('id', $request->user()->id)->update(['last_access' => now()]);
|
|
||||||
|
|
||||||
return response()->json(
|
return response()->json(
|
||||||
Alert::with('crew.user')->find($id)
|
Alert::with(['crew.user' => [
|
||||||
|
"name",
|
||||||
|
"username",
|
||||||
|
"chief",
|
||||||
|
"driver"
|
||||||
|
]])
|
||||||
|
->where('id', $id)
|
||||||
|
->first()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
"laravel/framework": "^10.0",
|
"laravel/framework": "^10.0",
|
||||||
"laravel/sanctum": "^3.2",
|
"laravel/sanctum": "^3.2",
|
||||||
"laravel/tinker": "^2.8",
|
"laravel/tinker": "^2.8",
|
||||||
|
"matthewbdaly/laravel-etag-middleware": "^1.3",
|
||||||
"santigarcor/laratrust": "^7.2",
|
"santigarcor/laratrust": "^7.2",
|
||||||
"sentry/sentry-laravel": "^3.7"
|
"sentry/sentry-laravel": "^3.7"
|
||||||
},
|
},
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "f099d4ac47fd2d3bbc07fe77412bb9bd",
|
"content-hash": "f8b683b55dd63e861103c15d13fed172",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "brick/math",
|
"name": "brick/math",
|
||||||
|
@ -2202,6 +2202,61 @@
|
||||||
],
|
],
|
||||||
"time": "2023-08-05T12:09:49+00:00"
|
"time": "2023-08-05T12:09:49+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "matthewbdaly/laravel-etag-middleware",
|
||||||
|
"version": "1.3.6",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/matthewbdaly/laravel-etag-middleware.git",
|
||||||
|
"reference": "048cb22f5849f695f134e7e13cb52b4c21ef6fd4"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/matthewbdaly/laravel-etag-middleware/zipball/048cb22f5849f695f134e7e13cb52b4c21ef6fd4",
|
||||||
|
"reference": "048cb22f5849f695f134e7e13cb52b4c21ef6fd4",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"illuminate/http": "^5.5|^6.0|^7.0|^8.0|^9.0|^10.0",
|
||||||
|
"illuminate/support": "^5.5|^6.0|^7.0|^8.0|^9.0|^10.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"mockery/mockery": "^1.0",
|
||||||
|
"php-coveralls/php-coveralls": "^2.1",
|
||||||
|
"phpunit/phpunit": "^4.8|^5.2|^9.0",
|
||||||
|
"squizlabs/php_codesniffer": "^3.7",
|
||||||
|
"vimeo/psalm": "^5.8"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Matthewbdaly\\ETagMiddleware\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Matthew Daly",
|
||||||
|
"email": "450801+matthewbdaly@users.noreply.github.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A Laravel middleware for adding ETags to HTTP requests to improve response times",
|
||||||
|
"keywords": [
|
||||||
|
"ETags",
|
||||||
|
"Etag",
|
||||||
|
"http",
|
||||||
|
"laravel",
|
||||||
|
"middleware"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/matthewbdaly/laravel-etag-middleware/issues",
|
||||||
|
"source": "https://github.com/matthewbdaly/laravel-etag-middleware/tree/1.3.6"
|
||||||
|
},
|
||||||
|
"time": "2023-03-20T16:12:09+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "monolog/monolog",
|
"name": "monolog/monolog",
|
||||||
"version": "3.4.0",
|
"version": "3.4.0",
|
||||||
|
|
|
@ -14,6 +14,7 @@ use App\Http\Controllers\ServiceTypeController;
|
||||||
use App\Http\Controllers\TrainingController;
|
use App\Http\Controllers\TrainingController;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Artisan;
|
use Illuminate\Support\Facades\Artisan;
|
||||||
|
use \Matthewbdaly\ETagMiddleware\ETag;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
@ -44,16 +45,16 @@ Route::middleware('auth:sanctum')->group( function () {
|
||||||
Route::get('/schedules', [ScheduleSlotsController::class, 'index']);
|
Route::get('/schedules', [ScheduleSlotsController::class, 'index']);
|
||||||
Route::post('/schedules', [ScheduleSlotsController::class, 'store']);
|
Route::post('/schedules', [ScheduleSlotsController::class, 'store']);
|
||||||
|
|
||||||
Route::get('/availability', [AvailabilityController::class, 'get']);
|
Route::get('/availability', [AvailabilityController::class, 'get'])->middleware(ETag::class);
|
||||||
Route::post('/availability', [AvailabilityController::class, 'updateAvailability']);
|
Route::post('/availability', [AvailabilityController::class, 'updateAvailability']);
|
||||||
Route::post('/manual_mode', [AvailabilityController::class, 'updateAvailabilityManualMode']);
|
Route::post('/manual_mode', [AvailabilityController::class, 'updateAvailabilityManualMode']);
|
||||||
|
|
||||||
Route::get('/alerts', [AlertController::class, 'index']);
|
Route::get('/alerts', [AlertController::class, 'index'])->middleware(ETag::class);
|
||||||
Route::post('/alerts', [AlertController::class, 'store']);
|
Route::post('/alerts', [AlertController::class, 'store']);
|
||||||
Route::get('/alerts/{id}', [AlertController::class, 'show']);
|
Route::get('/alerts/{id}', [AlertController::class, 'show'])->middleware(ETag::class);
|
||||||
Route::patch('/alerts/{id}', [AlertController::class, 'update']);
|
Route::patch('/alerts/{id}', [AlertController::class, 'update']);
|
||||||
|
|
||||||
Route::get('/services', [ServiceController::class, 'index']);
|
Route::get('/services', [ServiceController::class, 'index'])->middleware(ETag::class);
|
||||||
Route::post('/services', [ServiceController::class, 'createOrUpdate']);
|
Route::post('/services', [ServiceController::class, 'createOrUpdate']);
|
||||||
Route::get('/services/{id}', [ServiceController::class, 'show']);
|
Route::get('/services/{id}', [ServiceController::class, 'show']);
|
||||||
Route::delete('/services/{id}', [ServiceController::class, 'destroy']);
|
Route::delete('/services/{id}', [ServiceController::class, 'destroy']);
|
||||||
|
@ -63,12 +64,12 @@ Route::middleware('auth:sanctum')->group( function () {
|
||||||
Route::get('/places/search', [PlacesController::class, 'search']);
|
Route::get('/places/search', [PlacesController::class, 'search']);
|
||||||
Route::get('/places/{id}', [PlacesController::class, 'show']);
|
Route::get('/places/{id}', [PlacesController::class, 'show']);
|
||||||
|
|
||||||
Route::get('/trainings', [TrainingController::class, 'index']);
|
Route::get('/trainings', [TrainingController::class, 'index'])->middleware(ETag::class);
|
||||||
Route::post('/trainings', [TrainingController::class, 'createOrUpdate']);
|
Route::post('/trainings', [TrainingController::class, 'createOrUpdate']);
|
||||||
Route::get('/trainings/{id}', [TrainingController::class, 'show']);
|
Route::get('/trainings/{id}', [TrainingController::class, 'show']);
|
||||||
Route::delete('/trainings/{id}', [TrainingController::class, 'destroy']);
|
Route::delete('/trainings/{id}', [TrainingController::class, 'destroy']);
|
||||||
|
|
||||||
Route::get('/logs', [LogsController::class, 'index']);
|
Route::get('/logs', [LogsController::class, 'index'])->middleware(ETag::class);
|
||||||
|
|
||||||
Route::post('/telegram_login_token', [TelegramController::class, 'loginToken']);
|
Route::post('/telegram_login_token', [TelegramController::class, 'loginToken']);
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,8 @@
|
||||||
"scripts": [],
|
"scripts": [],
|
||||||
"serviceWorker": true,
|
"serviceWorker": true,
|
||||||
"ngswConfigPath": "ngsw-config.json",
|
"ngswConfigPath": "ngsw-config.json",
|
||||||
"sourceMap": true
|
"sourceMap": true,
|
||||||
|
"allowedCommonJsDependencies": ["crypto-js"]
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"production": {
|
"production": {
|
||||||
|
|
|
@ -26,6 +26,8 @@ export class ModalAlertComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
alertClosed = 0;
|
alertClosed = 0;
|
||||||
|
|
||||||
|
private etag = "";
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public bsModalRef: BsModalRef,
|
public bsModalRef: BsModalRef,
|
||||||
private api: ApiClientService,
|
private api: ApiClientService,
|
||||||
|
@ -36,7 +38,9 @@ export class ModalAlertComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
loadResponsesData() {
|
loadResponsesData() {
|
||||||
//TODO: do not update data if not changed. Support for content hash in response header?
|
//TODO: do not update data if not changed. Support for content hash in response header?
|
||||||
this.api.get(`alerts/${this.id}`).then((response) => {
|
this.api.get(`alerts/${this.id}`, {}, this.etag).then((response) => {
|
||||||
|
if(this.api.isLastSame) return;
|
||||||
|
this.etag = this.api.lastEtag;
|
||||||
console.log(response, this.alertClosed, response.closed);
|
console.log(response, this.alertClosed, response.closed);
|
||||||
if(this.alertClosed !== response.closed) this.alertClosed = response.closed;
|
if(this.alertClosed !== response.closed) this.alertClosed = response.closed;
|
||||||
if(!isEqual(this.crewUsers, response.crew)) this.crewUsers = response.crew;
|
if(!isEqual(this.crewUsers, response.crew)) this.crewUsers = response.crew;
|
||||||
|
|
|
@ -60,6 +60,7 @@ export class TableComponent implements OnInit, OnDestroy {
|
||||||
public data: any = [];
|
public data: any = [];
|
||||||
public displayedData: any = [];
|
public displayedData: any = [];
|
||||||
public originalData: any = [];
|
public originalData: any = [];
|
||||||
|
private etag: string = "";
|
||||||
|
|
||||||
public loadDataInterval: NodeJS.Timer | undefined = undefined;
|
public loadDataInterval: NodeJS.Timer | undefined = undefined;
|
||||||
|
|
||||||
|
@ -87,7 +88,9 @@ export class TableComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
loadTableData() {
|
loadTableData() {
|
||||||
if(!this.sourceType) this.sourceType = "list";
|
if(!this.sourceType) this.sourceType = "list";
|
||||||
this.api.get(this.sourceType).then((data: any) => {
|
this.api.get(this.sourceType, {}, this.etag).then((data: any) => {
|
||||||
|
if(this.api.isLastSame) return;
|
||||||
|
this.etag = this.api.lastEtag;
|
||||||
this.data = data.filter((row: any) => typeof row.hidden !== 'undefined' ? !row.hidden : true);
|
this.data = data.filter((row: any) => typeof row.hidden !== 'undefined' ? !row.hidden : true);
|
||||||
this.originalData = this.data;
|
this.originalData = this.data;
|
||||||
this.totalElements = this.data.length;
|
this.totalElements = this.data.length;
|
||||||
|
|
|
@ -25,6 +25,8 @@ export class ListComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
public alertLoading = false;
|
public alertLoading = false;
|
||||||
|
|
||||||
|
private etag = "";
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public api: ApiClientService,
|
public api: ApiClientService,
|
||||||
public auth: AuthService,
|
public auth: AuthService,
|
||||||
|
@ -35,7 +37,9 @@ export class ListComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
loadAvailability() {
|
loadAvailability() {
|
||||||
this.api.get("availability").then((response) => {
|
this.api.get("availability", {}, this.etag).then((response) => {
|
||||||
|
if(this.api.isLastSame) return;
|
||||||
|
this.etag = this.api.lastEtag;
|
||||||
this.available = response.available;
|
this.available = response.available;
|
||||||
this.manual_mode = response.manual_mode;
|
this.manual_mode = response.manual_mode;
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
|
|
|
@ -8,6 +8,9 @@ import { Subject } from "rxjs";
|
||||||
export class ApiClientService {
|
export class ApiClientService {
|
||||||
private apiRoot = 'api/';
|
private apiRoot = 'api/';
|
||||||
|
|
||||||
|
public lastEtag = "";
|
||||||
|
public isLastSame = false;
|
||||||
|
|
||||||
public alertsChanged = new Subject<void>();
|
public alertsChanged = new Subject<void>();
|
||||||
public availableUsers: undefined | number = undefined;
|
public availableUsers: undefined | number = undefined;
|
||||||
|
|
||||||
|
@ -20,12 +23,20 @@ export class ApiClientService {
|
||||||
return this.apiRoot + endpoint;
|
return this.apiRoot + endpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get(endpoint: string, data: any = {}) {
|
public get(endpoint: string, data: any = {}, etag: string = "") {
|
||||||
return new Promise<any>((resolve, reject) => {
|
return new Promise<any>((resolve, reject) => {
|
||||||
this.http.get(this.apiEndpoint(endpoint), {
|
this.http.get(this.apiEndpoint(endpoint), {
|
||||||
params: new HttpParams({ fromObject: data })
|
params: new HttpParams({ fromObject: data }),
|
||||||
|
observe: 'response',
|
||||||
|
headers: (etag !== "" && etag !== null) ? {
|
||||||
|
'If-None-Match': etag
|
||||||
|
} : {}
|
||||||
}).subscribe({
|
}).subscribe({
|
||||||
next: (v) => resolve(v),
|
next: (v: any) => {
|
||||||
|
this.lastEtag = v.headers.get("etag");
|
||||||
|
this.isLastSame = etag === this.lastEtag;
|
||||||
|
resolve(v.body);
|
||||||
|
},
|
||||||
error: (e) => reject(e)
|
error: (e) => reject(e)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -21,6 +21,7 @@ export class AppComponent {
|
||||||
public loadingRoute = false;
|
public loadingRoute = false;
|
||||||
private loadAlertsInterval: NodeJS.Timer | undefined = undefined;
|
private loadAlertsInterval: NodeJS.Timer | undefined = undefined;
|
||||||
public alerts = [];
|
public alerts = [];
|
||||||
|
private alertsEtag = "";
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public auth: AuthService,
|
public auth: AuthService,
|
||||||
|
@ -37,8 +38,10 @@ export class AppComponent {
|
||||||
|
|
||||||
loadAlerts() {
|
loadAlerts() {
|
||||||
if(this.auth.profile) {
|
if(this.auth.profile) {
|
||||||
this.api.get("alerts").then((response) => {
|
this.api.get("alerts", {}, this.alertsEtag).then((response) => {
|
||||||
|
if(this.api.isLastSame) return;
|
||||||
this.alerts = response;
|
this.alerts = response;
|
||||||
|
this.alertsEtag = this.api.lastEtag;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue