source->api_key && str_contains($this->source->api_key, ':')) { [$this->appId, $this->appKey] = explode(':', $this->source->api_key, 2); } else { throw new ApiException( 'Adzuna API key must be in format "app_id:app_key"', $this->source->name ); } // Extract country from configuration if set if (!empty($this->source->configuration['country'])) { $this->country = $this->source->configuration['country']; } return $client; } /** * Fetch jobs from Adzuna API * * @param array $params [ * 'what' => string, // Job title or keywords * 'where' => string, // Location * 'results_per_page' => int, * 'page' => int, * 'category' => string, * 'full_time' => int, // 1 for full-time only * 'part_time' => int, // 1 for part-time only * 'contract' => int, // 1 for contract only * 'max_days_old' => int, // Jobs posted within last N days * ] * @return array */ public function fetchJobs(array $params = []): array { try { $queryParams = $this->buildQueryParams($params); // Adzuna API endpoint structure: /v1/api/jobs/{country}/search/{page} $page = $queryParams['page'] ?? 1; unset($queryParams['page']); $endpoint = "search/{$page}"; $response = $this->makeRequest('GET', $endpoint, $queryParams); $data = $response->json(); if (!isset($data['results']) || !is_array($data['results'])) { throw new ApiException( 'Invalid response structure from Adzuna API', $this->source->name, ['response' => $data] ); } $this->updateLastFetched(); return $this->transformer->transformMany($data['results']); } catch (ApiException $e) { throw $e; } catch (\Throwable $e) { throw new ApiException( 'Failed to fetch jobs from Adzuna', $this->source->name, ['error' => $e->getMessage()], 0, $e ); } } /** * Build query parameters for the API request */ private function buildQueryParams(array $params): array { $queryParams = [ 'app_id' => $this->appId, 'app_key' => $this->appKey, 'results_per_page' => min($params['results_per_page'] ?? 20, 50), 'page' => $params['page'] ?? 1, ]; if (!empty($params['what'])) { $queryParams['what'] = $params['what']; } if (!empty($params['where'])) { $queryParams['where'] = $params['where']; } if (isset($params['category'])) { $queryParams['category'] = $params['category']; } if (isset($params['full_time'])) { $queryParams['full_time'] = (int) $params['full_time']; } if (isset($params['part_time'])) { $queryParams['part_time'] = (int) $params['part_time']; } if (isset($params['contract'])) { $queryParams['contract'] = (int) $params['contract']; } if (isset($params['max_days_old'])) { $queryParams['max_days_old'] = (int) $params['max_days_old']; } return $queryParams; } /** * Override buildUrl to include country in path */ protected function buildUrl(string $endpoint): string { $baseUrl = rtrim($this->source->api_url, '/'); $endpoint = ltrim($endpoint, '/'); return "{$baseUrl}/{$this->country}/{$endpoint}"; } /** * Get supported countries */ public function getSupportedCountries(): array { return [ 'au' => 'Australia', 'at' => 'Austria', 'br' => 'Brazil', 'ca' => 'Canada', 'fr' => 'France', 'de' => 'Germany', 'in' => 'India', 'it' => 'Italy', 'nl' => 'Netherlands', 'nz' => 'New Zealand', 'pl' => 'Poland', 'ru' => 'Russia', 'sg' => 'Singapore', 'za' => 'South Africa', 'ch' => 'Switzerland', 'gb' => 'United Kingdom', 'us' => 'United States', ]; } }