Init
This commit is contained in:
93
resources/js/Components/ExternalApiSync.vue
Normal file
93
resources/js/Components/ExternalApiSync.vue
Normal file
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<p class="text-[11px] text-muted leading-relaxed">
|
||||
cursor-based pagination · rate limiting · token refresh · pause/resume
|
||||
</p>
|
||||
<div class="flex items-center gap-3 text-[10px] text-muted/60">
|
||||
<span class="font-mono">app/Temporal/ExternalApiSync/</span>
|
||||
<a href="https://docs.temporal.io/develop/php/failure-detection#activity-next-retry-delay" target="_blank" class="text-accent/50 hover:text-accent transition-colors">nextRetryDelay</a>
|
||||
<a href="https://docs.temporal.io/develop/php/failure-detection#activity-heartbeats" target="_blank" class="text-accent/50 hover:text-accent transition-colors">heartbeats</a>
|
||||
<a href="https://docs.temporal.io/develop/php/message-passing#signals" target="_blank" class="text-accent/50 hover:text-accent transition-colors">signals</a>
|
||||
</div>
|
||||
|
||||
<SimulationControls v-model="simulation" />
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<button @click="startSync"
|
||||
class="px-3 py-1 text-[11px] font-medium uppercase tracking-wider bg-accent/15 text-accent border border-accent/30 hover:bg-accent/25 hover:border-accent/50 transition-all rounded-sm cursor-pointer">
|
||||
Start Sync
|
||||
</button>
|
||||
<template v-if="workflowId">
|
||||
<button v-if="!status.isPaused" @click="pauseSync"
|
||||
class="px-2 py-0.5 text-[10px] uppercase tracking-wider text-warn border border-warn/30 hover:bg-warn/10 transition-all rounded-sm cursor-pointer">
|
||||
Pause
|
||||
</button>
|
||||
<button v-else @click="resumeSync"
|
||||
class="px-2 py-0.5 text-[10px] uppercase tracking-wider text-accent border border-accent/30 hover:bg-accent/10 transition-all rounded-sm cursor-pointer">
|
||||
Resume
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div v-if="workflowId" class="bg-deep/50 border border-border rounded-sm p-3 space-y-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="w-1.5 h-1.5 rounded-full"
|
||||
:class="isTerminal ? 'bg-accent' : 'bg-accent animate-pulse-dot'"></span>
|
||||
<span class="text-[11px] text-value">{{ status.status || 'starting' }}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4 text-[10px] text-muted">
|
||||
<span>pages <span class="text-value">{{ status.pagesFetched || 0 }}</span></span>
|
||||
<span>records <span class="text-value">{{ status.recordsSynced || 0 }}</span></span>
|
||||
<span>cursor <span class="text-value">{{ status.currentCursor || '0' }}</span></span>
|
||||
<span v-if="status.retryCount">retries <span class="text-warn">{{ status.retryCount }}</span></span>
|
||||
<span v-if="status.rateLimitHits">429s <span class="text-warn">{{ status.rateLimitHits }}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import axios from 'axios';
|
||||
import { usePolling } from '../composables/usePolling.js';
|
||||
import SimulationControls from './SimulationControls.vue';
|
||||
|
||||
const simulation = ref({
|
||||
failureRate: 0,
|
||||
latencyMs: 0,
|
||||
rateLimiting: { enabled: false, hitChance: 25, retryAfterMs: 2000 },
|
||||
});
|
||||
|
||||
const workflowId = ref(null);
|
||||
const status = ref({});
|
||||
|
||||
const isTerminal = computed(() =>
|
||||
['completed', 'failed'].includes(status.value.status) || !!status.value.error
|
||||
);
|
||||
|
||||
const { start: startPolling } = usePolling(async () => {
|
||||
const { data } = await axios.get(`/temporal/api-sync/${workflowId.value}/status`);
|
||||
if (data.error && !data.status) {
|
||||
status.value = { ...status.value, status: 'completed' };
|
||||
return true;
|
||||
}
|
||||
status.value = data;
|
||||
return isTerminal.value;
|
||||
});
|
||||
|
||||
async function startSync() {
|
||||
const { data } = await axios.post('/temporal/api-sync/start', { simulation: simulation.value });
|
||||
workflowId.value = data.workflow_id;
|
||||
status.value = { status: 'starting' };
|
||||
startPolling();
|
||||
}
|
||||
|
||||
async function pauseSync() {
|
||||
await axios.post(`/temporal/api-sync/${workflowId.value}/pause`);
|
||||
}
|
||||
|
||||
async function resumeSync() {
|
||||
await axios.post(`/temporal/api-sync/${workflowId.value}/resume`);
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user