1: | <?php declare(strict_types=1); |
2: | |
3: | namespace Salient\Sync\Command; |
4: | |
5: | use Salient\Cli\Exception\CliInvalidArgumentsException; |
6: | use Salient\Cli\CliOption; |
7: | use Salient\Contract\Cli\CliOptionType; |
8: | use Salient\Contract\Cli\CliOptionValueType; |
9: | use Salient\Contract\Sync\SyncProviderInterface; |
10: | use Salient\Core\Facade\Console; |
11: | use Salient\Utility\Inflect; |
12: | |
13: | |
14: | |
15: | |
16: | |
17: | |
18: | final class CheckSyncProviderHeartbeat extends AbstractSyncCommand |
19: | { |
20: | |
21: | private array $ProviderBasename = []; |
22: | |
23: | private array $Provider = []; |
24: | private int $Ttl = 0; |
25: | private bool $FailEarly = false; |
26: | |
27: | public function getDescription(): string |
28: | { |
29: | return 'Send a heartbeat request to ' . ( |
30: | $this->Providers |
31: | ? 'registered providers' |
32: | : 'one or more providers' |
33: | ); |
34: | } |
35: | |
36: | protected function getOptionList(): iterable |
37: | { |
38: | $builder = CliOption::build() |
39: | ->name('provider') |
40: | ->multipleAllowed(); |
41: | |
42: | if ($this->Providers) { |
43: | yield $builder |
44: | ->optionType(CliOptionType::ONE_OF_POSITIONAL) |
45: | ->allowedValues(array_keys($this->Providers)) |
46: | ->addAll() |
47: | ->defaultValue('ALL') |
48: | ->bindTo($this->ProviderBasename); |
49: | } else { |
50: | yield $builder |
51: | ->description('The fully-qualified name of the provider to check') |
52: | ->optionType(CliOptionType::VALUE_POSITIONAL) |
53: | ->required() |
54: | ->bindTo($this->Provider); |
55: | } |
56: | |
57: | yield from [ |
58: | CliOption::build() |
59: | ->long('ttl') |
60: | ->short('t') |
61: | ->valueName('seconds') |
62: | ->description('The lifetime of a positive result, in seconds') |
63: | ->optionType(CliOptionType::VALUE) |
64: | ->valueType(CliOptionValueType::INTEGER) |
65: | ->defaultValue(300) |
66: | ->bindTo($this->Ttl), |
67: | CliOption::build() |
68: | ->long('fail-early') |
69: | ->short('f') |
70: | ->description('If a check fails, exit without checking other providers') |
71: | ->bindTo($this->FailEarly), |
72: | ]; |
73: | |
74: | yield from $this->getGlobalOptionList(); |
75: | } |
76: | |
77: | |
78: | protected function getLongDescription(): ?string |
79: | { |
80: | if ($this->Providers) { |
81: | $description[] = <<<EOF |
82: | If no providers are given, all providers are checked. |
83: | EOF; |
84: | } |
85: | |
86: | $description[] = <<<EOF |
87: | If a heartbeat request fails, __{{subcommand}}__ continues to the next provider |
88: | unless `-f/--fail-early` is given, in which case it exits immediately. |
89: | |
90: | The command exits with a non-zero status if a provider backend is unreachable. |
91: | EOF; |
92: | |
93: | return implode(\PHP_EOL . \PHP_EOL, $description); |
94: | } |
95: | |
96: | protected function run(string ...$args) |
97: | { |
98: | $this->startRun(); |
99: | |
100: | if ($this->Providers) { |
101: | $providers = array_values(array_map( |
102: | fn(string $providerClass) => |
103: | $this->App->get($providerClass), |
104: | array_intersect_key( |
105: | $this->Providers, |
106: | array_flip($this->ProviderBasename), |
107: | ), |
108: | )); |
109: | } else { |
110: | $providers = array_values(array_map( |
111: | function (string $providerClass) { |
112: | if (is_a( |
113: | $this->App->getName($providerClass), |
114: | SyncProviderInterface::class, |
115: | true |
116: | )) { |
117: | if (!$this->App->has($providerClass)) { |
118: | $this->App->singleton($providerClass); |
119: | } |
120: | return $this->App->get($providerClass); |
121: | } |
122: | |
123: | throw new CliInvalidArgumentsException(sprintf( |
124: | '%s does not implement %s', |
125: | $providerClass, |
126: | SyncProviderInterface::class, |
127: | )); |
128: | }, |
129: | $this->Provider, |
130: | )); |
131: | } |
132: | |
133: | $count = count($providers); |
134: | |
135: | Console::info(Inflect::format( |
136: | $count, |
137: | 'Sending heartbeat request to {{#}} {{#:provider}}', |
138: | )); |
139: | |
140: | $this->Store->checkProviderHeartbeats( |
141: | max(1, $this->Ttl), |
142: | $this->FailEarly, |
143: | ...$providers, |
144: | ); |
145: | |
146: | Console::summary(Inflect::format( |
147: | $count, |
148: | '{{#}} {{#:provider}} checked', |
149: | )); |
150: | } |
151: | } |
152: | |