Closes the last coverage gap for org (person remains intentionally unaddressed — no good free/non-scraping option exists). Unlike the other 8 sources, OpenCorporates now requires an API token even on its free tier (confirmed: keyless request gets 401). RunnerRegistry::has() checks services.opencorporates.token is configured before exposing the source as runnable, so it degrades to "not yet integrated" in the UI rather than a broken Run button, until OPENCORPORATES_API_TOKEN is set. Also fixes a real bug hit while building this: a RunnerRegistry::REQUIRES class constant mapping source key to a closure crashed on file load (PHP class constants can't hold closures) — inlined the one check instead of building an abstraction for a single case. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
57 lines
1.9 KiB
PHP
57 lines
1.9 KiB
PHP
<?php
|
|
|
|
namespace App\Osint\Runners;
|
|
|
|
use App\Models\Target;
|
|
use App\Osint\Runner;
|
|
use App\Osint\RunnerException;
|
|
use App\Osint\RunnerResult;
|
|
use Illuminate\Support\Facades\Http;
|
|
|
|
class OpenCorporatesRunner implements Runner
|
|
{
|
|
public function run(Target $target): RunnerResult
|
|
{
|
|
if ($target->type !== 'org') {
|
|
throw new RunnerException('OpenCorporates only supports "org" targets.');
|
|
}
|
|
|
|
$token = config('services.opencorporates.token');
|
|
if (blank($token)) {
|
|
throw new RunnerException('OpenCorporates API token not configured (OPENCORPORATES_API_TOKEN).');
|
|
}
|
|
|
|
$response = Http::timeout((int) config('osint.run_timeout'))
|
|
->get('https://api.opencorporates.com/v0.4/companies/search', [
|
|
'q' => $target->value,
|
|
'api_token' => $token,
|
|
'per_page' => 10,
|
|
]);
|
|
|
|
if (! $response->successful()) {
|
|
throw new RunnerException('OpenCorporates request failed with status '.$response->status().'.');
|
|
}
|
|
|
|
$companies = $response->json('results.companies') ?? [];
|
|
|
|
$matches = collect($companies)->map(function (array $entry) {
|
|
$company = $entry['company'];
|
|
|
|
$label = sprintf(
|
|
'%s (%s%s)',
|
|
$company['name'],
|
|
$company['jurisdiction_code'] ?? 'unknown jurisdiction',
|
|
$company['current_status'] ? ', '.$company['current_status'] : ''
|
|
);
|
|
|
|
return ['name' => $label, 'url' => $company['opencorporates_url']];
|
|
})->all();
|
|
|
|
$summary = count($matches) > 0
|
|
? sprintf('Found %d compan%s matching "%s".', count($matches), count($matches) === 1 ? 'y' : 'ies', $target->value)
|
|
: sprintf('No companies found matching "%s".', $target->value);
|
|
|
|
return new RunnerResult(raw: ['matches' => $matches], summary: $summary);
|
|
}
|
|
}
|