Add OpenCorporates collector for org targets, gated on an API token
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>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
86f73c46b2
commit
dc4388eabf
@@ -0,0 +1,56 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user