120 lines
5.7 KiB
Vue
120 lines
5.7 KiB
Vue
<template>
|
|
<div class="border border-border/50 rounded-sm overflow-hidden">
|
|
<button @click="open = !open"
|
|
class="w-full flex items-center gap-2 px-2 py-1.5 bg-deep/50 hover:bg-deep/80 transition-colors cursor-pointer select-none">
|
|
<span class="inline-block w-0 h-0 border-l-[3px] border-l-transparent border-r-[3px] border-r-transparent border-t-[4px] border-t-muted transition-transform duration-200"
|
|
:class="{ '-rotate-90': !open }"></span>
|
|
<span class="text-[10px] uppercase tracking-wider text-muted">Fault Injection</span>
|
|
<span v-if="isActive" class="ml-auto w-1.5 h-1.5 rounded-full bg-warn animate-pulse-dot"></span>
|
|
</button>
|
|
<div v-show="open" class="px-3 py-2 space-y-2 bg-deep/30">
|
|
<p class="text-[10px] text-muted/80 leading-relaxed pb-1 border-b border-border/30">
|
|
Inject faults into activities to test Temporal's retry and error handling.
|
|
Temporal automatically retries failed activities per the configured retry policy.
|
|
</p>
|
|
<!-- Failure Rate -->
|
|
<div class="space-y-0.5">
|
|
<div class="flex items-center gap-2">
|
|
<label class="text-[10px] text-muted w-20 shrink-0">Failure %</label>
|
|
<input type="range" min="0" max="100" step="5"
|
|
:value="modelValue.failureRate"
|
|
@input="update('failureRate', +$event.target.value)"
|
|
class="flex-1 h-1 accent-danger cursor-pointer">
|
|
<span class="text-[10px] text-value w-8 text-right">{{ modelValue.failureRate }}%</span>
|
|
</div>
|
|
<p class="text-[9px] text-muted/50 pl-[88px]">Chance each activity throws a RuntimeException (retried automatically)</p>
|
|
</div>
|
|
|
|
<!-- Latency -->
|
|
<div class="space-y-0.5">
|
|
<div class="flex items-center gap-2">
|
|
<label class="text-[10px] text-muted w-20 shrink-0">Latency ms</label>
|
|
<input type="range" min="0" max="2000" step="100"
|
|
:value="modelValue.latencyMs"
|
|
@input="update('latencyMs', +$event.target.value)"
|
|
class="flex-1 h-1 accent-warn cursor-pointer">
|
|
<span class="text-[10px] text-value w-8 text-right">{{ modelValue.latencyMs }}</span>
|
|
</div>
|
|
<p class="text-[9px] text-muted/50 pl-[88px]">Artificial delay added to every activity execution</p>
|
|
</div>
|
|
|
|
<!-- Rate Limiting -->
|
|
<div class="space-y-0.5">
|
|
<div class="flex items-center gap-2">
|
|
<label class="text-[10px] text-muted w-20 shrink-0">Rate limit</label>
|
|
<button @click="toggleRateLimit"
|
|
class="px-1.5 py-0.5 text-[9px] uppercase tracking-wider rounded-sm border transition-all cursor-pointer"
|
|
:class="modelValue.rateLimiting.enabled
|
|
? 'text-warn border-warn/40 bg-warn/10'
|
|
: 'text-muted border-border/50'">
|
|
{{ modelValue.rateLimiting.enabled ? 'ON' : 'OFF' }}
|
|
</button>
|
|
</div>
|
|
<p class="text-[9px] text-muted/50 pl-[88px]">Simulates HTTP 429 responses with nextRetryDelay backoff</p>
|
|
</div>
|
|
|
|
<template v-if="modelValue.rateLimiting.enabled">
|
|
<div class="flex items-center gap-2 pl-4">
|
|
<label class="text-[10px] text-muted w-16 shrink-0">Hit %</label>
|
|
<input type="range" min="0" max="100" step="5"
|
|
:value="modelValue.rateLimiting.hitChance"
|
|
@input="updateRL('hitChance', +$event.target.value)"
|
|
class="flex-1 h-1 accent-warn cursor-pointer">
|
|
<span class="text-[10px] text-value w-8 text-right">{{ modelValue.rateLimiting.hitChance }}%</span>
|
|
</div>
|
|
<div class="flex items-center gap-2 pl-4">
|
|
<label class="text-[10px] text-muted w-16 shrink-0">Retry ms</label>
|
|
<input type="range" min="500" max="5000" step="500"
|
|
:value="modelValue.rateLimiting.retryAfterMs"
|
|
@input="updateRL('retryAfterMs', +$event.target.value)"
|
|
class="flex-1 h-1 accent-warn cursor-pointer">
|
|
<span class="text-[10px] text-value w-8 text-right">{{ modelValue.rateLimiting.retryAfterMs }}</span>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed } from 'vue';
|
|
|
|
const props = defineProps({
|
|
modelValue: {
|
|
type: Object,
|
|
default: () => ({
|
|
failureRate: 0,
|
|
latencyMs: 0,
|
|
rateLimiting: { enabled: false, hitChance: 25, retryAfterMs: 2000 },
|
|
}),
|
|
},
|
|
});
|
|
|
|
const emit = defineEmits(['update:modelValue']);
|
|
|
|
const open = ref(false);
|
|
|
|
const isActive = computed(() =>
|
|
props.modelValue.failureRate > 0 ||
|
|
props.modelValue.latencyMs > 0 ||
|
|
props.modelValue.rateLimiting.enabled
|
|
);
|
|
|
|
function update(key, value) {
|
|
emit('update:modelValue', { ...props.modelValue, [key]: value });
|
|
}
|
|
|
|
function toggleRateLimit() {
|
|
emit('update:modelValue', {
|
|
...props.modelValue,
|
|
rateLimiting: { ...props.modelValue.rateLimiting, enabled: !props.modelValue.rateLimiting.enabled },
|
|
});
|
|
}
|
|
|
|
function updateRL(key, value) {
|
|
emit('update:modelValue', {
|
|
...props.modelValue,
|
|
rateLimiting: { ...props.modelValue.rateLimiting, [key]: value },
|
|
});
|
|
}
|
|
</script>
|