Proof of concept - Vue Polling a workaround for AWS API Gateway limitation

This commit is contained in:
Dillon Ngo
2024-02-11 16:11:42 +08:00
parent bf5ea2b2f2
commit 7151caa9b8
12 changed files with 219 additions and 50 deletions
+2
View File
@@ -16,6 +16,8 @@ class ListBookingsJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $timeout = 900;
/** @var ListGenericJobObject */
private $listGenericJobObject;
+2
View File
@@ -16,6 +16,8 @@ class ListDocumentsJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $timeout = 900;
/** @var ListGenericJobObject */
private $listGenericJobObject;
+2
View File
@@ -16,6 +16,8 @@ class ListTransactionsJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $timeout = 900;
/** @var ListGenericJobObject */
private $listGenericJobObject;
@@ -2,6 +2,7 @@
namespace App\Classes\Modules\Jobs\Processors;
use App\Classes\Exceptions\JobResourceNotFoundException;
use App\Classes\Modules\Jobs\Services\FetchesJobResult;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
@@ -32,12 +33,13 @@ class FetchesJobResultProcessor
public function execute(Request $request){
$res1 = $this->fetchesJobResult->execute(['job_id' => $request->route('job_id')]);
if($request->route('is_last')){
$res2 = $this->fetchesJobResult->execute(['request_signature' => $res1->request_signature, 'result_not_null' => true, 'order_by_id_desc' => true]);
return $res2;
}
if(!$res1->result){
Log::info('Job id: '.$request->route('job_id'));
$res2 = $this->fetchesJobResult->execute(['request_signature' => $res1->request_signature, 'result_not_null' => true, 'order_by_id_desc' => true]);
Log::info('Job id: '.$res2->id." , request_signature: ".$res2->request_signature);
return $res2;
throw new JobResourceNotFoundException('Unable to find any job based on the criteria provided');
}
return $res1;
@@ -44,9 +44,9 @@ class UpdateJobResultProcessor
try{
$jobResultExisting = $this->fetchesJobResult->execute(['request_signature' => $jobResultCurrent->request_signature, 'result_not_null' => true, 'order_by_id_desc' => true]);
$resultSignatureExisting = $jobResultExisting->result_signature;
if($resultSignatureExisting != $resultSignatureCurrent){
//if($resultSignatureExisting != $resultSignatureCurrent){
$this->updateJobResult($jobResultCurrent, $resultCurrentJson, $resultSignatureCurrent, $listGenericJobObject->getJobCommandName(), $listGenericJobObject->getJobCommand());
}
//}
} catch (JobResourceNotFoundException $exception){
$this->updateJobResult($jobResultCurrent, $resultCurrentJson, $resultSignatureCurrent, $listGenericJobObject->getJobCommandName(), $listGenericJobObject->getJobCommand());
}
@@ -90,34 +90,34 @@
<div class="row no-margin">
<div class="col bg-white padding-25">
<div class="row tabsContainer tabContent m-l-0 m-r-0" tab-name="invoice" v-if="isActiveTab('invoice') || showTabContent('invoice')">
<list-polling-component key="2" section="invoiceSection" :endpoint="route('api.document.list.job')" :options="{'per_page': 30, 'with_owner': true, 'document_type_in': ['INVOICE']}">
<list-component key="2" section="invoiceSection" :endpoint="route('api.document.list')" :options="{'per_page': 30, 'with_owner': true, 'document_type_in': ['INVOICE']}">
<template slot="list" slot-scope="{data}">
<billing-document-component :data="data"></billing-document-component>
</template>
</list-polling-component>
</div>
<div class="row tabsContainer tabContent m-l-0 m-r-0 hide" tab-name="purchase-order" v-if="isActiveTab('purchase-order') || showTabContent('purchase-order')">
<list-polling-component key="2" section="purchaseOrderSection" :endpoint="route('api.document.list.job')" :options="{'per_page': 30, 'with_owner': true, 'document_type_in': ['PURCHASE_ORDER']}">
</list-component>
</div>
<div class="row tabsContainer tabContent m-l-0 m-r-0 hide" tab-name="purchase-order" v-if="isActiveTab('purchase-order') || showTabContent('purchase-order')">
<list-component key="2" section="purchaseOrderSection" :endpoint="route('api.document.list')" :options="{'per_page': 30, 'with_owner': true, 'document_type_in': ['PURCHASE_ORDER']}">
<template slot="list" slot-scope="{data}">
<billing-document-component :data="data"></billing-document-component>
</template>
</list-polling-component>
</div>
<div class="row tabsContainer tabContent m-l-0 m-r-0 hide" tab-name="delivery-order" v-if="isActiveTab('delivery-order') || showTabContent('delivery-order')">
<list-polling-component key="2" section="deliveryOrderSection" :endpoint="route('api.document.list.job')" :options="{'per_page': 30, 'with_owner': true, 'document_type_in': ['DELIVER_ORDER']}">
</list-component>
</div>
<div class="row tabsContainer tabContent m-l-0 m-r-0 hide" tab-name="delivery-order" v-if="isActiveTab('delivery-order') || showTabContent('delivery-order')">
<list-component key="2" section="deliveryOrderSection" :endpoint="route('api.document.list')" :options="{'per_page': 30, 'with_owner': true, 'document_type_in': ['DELIVER_ORDER']}">
<template slot="list" slot-scope="{data}">
<billing-document-component :data="data"></billing-document-component>
</template>
</list-polling-component>
</list-component>
</div>
<div class="row tabsContainer tabContent m-l-0 m-r-0 hide" tab-name="supplier-delivery-order" v-if="isActiveTab('supplier-delivery-order') || showTabContent('supplier-delivery-order')">
<list-polling-component key="2" section="SupplierDeliveryOrderSection" :endpoint="route('api.document.list.job')" :options="{'per_page': 30, 'with_owner': true, 'document_type_in': ['SUPPLIER_DELIVER_ORDER']}">
<list-component key="2" section="SupplierDeliveryOrderSection" :endpoint="route('api.document.list')" :options="{'per_page': 30, 'with_owner': true, 'document_type_in': ['SUPPLIER_DELIVER_ORDER']}">
<template slot="list" slot-scope="{data}">
<billing-document-component :data="data"></billing-document-component>
</template>
</list-polling-component>
</div>
</list-component>
</div>
</div>
</div>
</div>
</div>
File diff suppressed because one or more lines are too long
@@ -118,16 +118,16 @@
<div class="row">
<div class="col">
<!-- CIEF TODO: For easy revert to old code -->
<!-- <list-component ref="pendingOrdersList" section="pendingOrdersSection" :endpoint="route('api.transaction.list')" :options="{per_page: 5, status: 2, owner_type: 'App\\Models\\Booking', type: 1, original_currency_id_in: [selectedCurrency.id], transaction_service_id: selectedService.id}">
<list-component ref="pendingOrdersList" section="pendingOrdersSection" :endpoint="route('api.transaction.list')" :options="{per_page: 5, status: 2, owner_type: 'App\\Models\\Booking', type: 1, original_currency_id_in: [selectedCurrency.id], transaction_service_id: selectedService.id}">
<template slot="list" slot-scope="{data}">
<supplier-pending-order-component :data="data" v-on:input="updateOrder($event)"></supplier-pending-order-component>
</template>
</list-component> -->
<list-polling-component ref="pendingOrdersList" section="pendingOrdersSection" :endpoint="route('api.transaction.list.job')" :options="{per_page: 5, status: 2, owner_type: 'App\\Models\\Booking', type: 1, original_currency_id_in: [selectedCurrency.id], transaction_service_id: selectedService.id}">
</list-component>
<!-- <list-polling-component ref="pendingOrdersList" section="pendingOrdersSection" :endpoint="route('api.transaction.list.job')" :options="{per_page: 5, status: 2, owner_type: 'App\\Models\\Booking', type: 1, original_currency_id_in: [selectedCurrency.id], transaction_service_id: selectedService.id}">
<template slot="list" slot-scope="{data}">
<supplier-pending-order-component :data="data" v-on:input="updateOrder($event)"></supplier-pending-order-component>
</template>
</list-polling-component>
</list-polling-component> -->
</div>
</div>
</div>
@@ -50,6 +50,7 @@
</template>
<script>
import requestV2 from '../../../general/mixins/aws/requestV2'
export default {
props: {
section:{
@@ -75,6 +76,7 @@
filters: this.options,
pollingInterval: null,
isPolling: false,
isFetchingResult: false,
isLoading: false,
}
},
@@ -124,50 +126,58 @@
total: result.meta.total
};
console.log('ListPolling:', JSON.stringify(result.meta));
this.stopPolling();
this.$store.dispatch('completeList', {'name': this.section, 'data': result.data});
this.$refs.pagination.makePagination(result.meta, result.links);
this.isLoading = false;
},
errorHandler(error){
// console.log('ListPolling error:', JSON.stringify(error));
this.isFetchingResult = false;
this.isPolling = false;
// this.isLoading = false;
},
startPolling(jobId, maxAttempts = 3) {
startPolling(jobId, maxAttempts = 8) {
let attempts = 0;
let interval = 10000; // Initial interval
const resetPollingInterval = (customInterval) => {
this.pollingInterval = setInterval(pollJobResult, customInterval);
};
const pollJobResult = () => {
if (this.isPolling) {
if (this.isPolling || this.isFetchingResult) {
return;
}
this.isPolling = true;
attempts++;
if(attempts === 1){
this.stopPolling();
resetPollingInterval(5000);
}
if (attempts > maxAttempts) {
clearInterval(this.pollingInterval);
this.pollingInterval = null;
this.isPolling = false;
this.stopPolling();
this.isLoading = false;
console.log(`Reached maximum attempts (${maxAttempts}). Polling stopped.`);
return;
}
this.fetchJobResult(jobId);
if(attempts === maxAttempts){
this.fetchJobResult(jobId, true);
}
else{
this.fetchJobResult(jobId);
}
};
// Initial call
// pollJobResult();
// Set up polling interval
this.pollingInterval = setInterval(pollJobResult, 10000);
// pollJobResult(); // Initial call
this.pollingInterval = setInterval(pollJobResult, interval);
},
stopPolling() {
clearInterval(this.pollingInterval);
this.pollingInterval = null;
this.isPolling = false;
this.isFetchingResult = false;
},
submitJob(url){
try {
@@ -181,24 +191,23 @@
}
});
})
// this.job.progress = response.data.progress;
// this.job.completed = response.data.completed;
// if (this.job.completed) {
// this.stopPolling();
// }
} catch (error) {
console.error('Error submitJob', error);
}
},
fetchJobResult(jobId) {
fetchJobResult(jobId, isLastAttempt = false) {
this.isFetchingResult = true;
try {
let anotherEndpoint = route('api.job.fetch', jobId);
this.submit(anotherEndpoint, 'get', this.section, false, false); //cief todo: Uncaught (in promise) null
if(isLastAttempt){
anotherEndpoint = route('api.job.fetch.last.attempt', jobId, isLastAttempt);
}
this.poll(anotherEndpoint, 'get', this.section, false, false); //cief todo: Uncaught (in promise) null
} catch (error) {
console.error('Error fetchJobResult', error);
}
}
}
},
mixins: [requestV2]
}
</script>
@@ -0,0 +1,9 @@
@extends('layouts.base_portal')
@section('inner_content')
<div class="row" v-if="$store.getters.isAdmin">
<div class="col p-t-15 p-b-15">
<billing-component></billing-component>
<admin-payments-billing-section-polling-component></admin-payments-billing-section-polling-component>
</div>
</div>
@endsection
+1
View File
@@ -4,4 +4,5 @@ use Illuminate\Support\Facades\Route;
Route::group(['prefix' => 'job', 'as' => 'job.', 'namespace' => 'Jobs'], function () {
Route::get('/fetch/{job_id}', 'FetchJobResultController@fetch')->name('fetch');
Route::get('/fetch/{job_id}/{is_last}', 'FetchJobResultController@fetch')->name('fetch.last.attempt');
});
+9 -3
View File
@@ -92,6 +92,12 @@ Route::get('/billings', function () {
return view('pages.billings');
})->name('billings');
/* Vue Polling Experiment - Starts */
Route::get('/billings-experiment', function () {
return view('pages.billings_experiment');
})->name('billings.experiment');
/* Vue Polling Experiment - Ends */
Route::get('/currency_orders', function () {
return view('pages.currency_orders');
})->name('currency_orders');
@@ -813,13 +819,13 @@ Route::get('/invoice/{marking}/{started_at}/{ended_at}/fix', function($marking,
->withTrashed()
->orderBy('created_at', 'asc')
->first();
// get the first bill_no
$firstBillNo = $firstInvoice->bill_no;
if (strpos($firstBillNo, '-deleted') !== false) {
$firstBillNo = substr($firstBillNo, 0, strpos($firstBillNo, '-deleted'));
}
// update currentInvoice bill_no to '-deleted-'
$currentInvoice = $booking->transactions()->where('type', TransactionType::INVOICE)->first();
$currentInvoice->bill_no = $currentInvoice->bill_no ."-deleted-" . Str::random(10);
@@ -842,4 +848,4 @@ Route::get('/invoice/{marking}/{started_at}/{ended_at}/fix', function($marking,
}
}
);
})->name('invoice.fix.byCustomerMarking');
})->name('invoice.fix.byCustomerMarking');