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: | public function getLongDescription(): ?string |
78: | { |
79: | if ($this->Providers) { |
80: | $description[] = <<<EOF |
81: | If no providers are given, all providers are checked. |
82: | EOF; |
83: | } |
84: | |
85: | $description[] = <<<EOF |
86: | If a heartbeat request fails, __{{subcommand}}__ continues to the next provider |
87: | unless `-f/--fail-early` is given, in which case it exits immediately. |
88: | |
89: | The command exits with a non-zero status if a provider backend is unreachable. |
90: | EOF; |
91: | |
92: | return implode(\PHP_EOL . \PHP_EOL, $description); |
93: | } |
94: | |
95: | protected function run(string ...$args) |
96: | { |
97: | $this->startRun(); |
98: | |
99: | if ($this->Providers) { |
100: | $providers = array_values(array_map( |
101: | fn(string $providerClass) => |
102: | $this->App->get($providerClass), |
103: | array_intersect_key( |
104: | $this->Providers, |
105: | array_flip($this->ProviderBasename), |
106: | ), |
107: | )); |
108: | } else { |
109: | $providers = array_values(array_map( |
110: | function (string $providerClass) { |
111: | if (is_a( |
112: | $this->App->getName($providerClass), |
113: | SyncProviderInterface::class, |
114: | true |
115: | )) { |
116: | if (!$this->App->has($providerClass)) { |
117: | $this->App->singleton($providerClass); |
118: | } |
119: | return $this->App->get($providerClass); |
120: | } |
121: | |
122: | throw new CliInvalidArgumentsException(sprintf( |
123: | '%s does not implement %s', |
124: | $providerClass, |
125: | SyncProviderInterface::class, |
126: | )); |
127: | }, |
128: | $this->Provider, |
129: | )); |
130: | } |
131: | |
132: | $count = count($providers); |
133: | |
134: | Console::info(Inflect::format( |
135: | $count, |
136: | 'Sending heartbeat request to {{#}} {{#:provider}}', |
137: | )); |
138: | |
139: | $this->Store->checkProviderHeartbeats( |
140: | max(1, $this->Ttl), |
141: | $this->FailEarly, |
142: | ...$providers, |
143: | ); |
144: | |
145: | Console::summary(Inflect::format( |
146: | $count, |
147: | '{{#}} {{#:provider}} checked', |
148: | )); |
149: | } |
150: | } |
151: | |