You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
290 lines
8.4 KiB
290 lines
8.4 KiB
<?php
|
|
/*
|
|
* Request.php
|
|
* Copyright (c) 2021 james@firefly-iii.org
|
|
*
|
|
* This file is part of the Firefly III Data Importer
|
|
* (https://github.com/firefly-iii/data-importer).
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Services\Nordigen\Request;
|
|
|
|
use App\Exceptions\AgreementExpiredException;
|
|
use App\Exceptions\ImporterErrorException;
|
|
use App\Exceptions\ImporterHttpException;
|
|
use App\Services\Shared\Response\Response;
|
|
use GuzzleHttp\Client;
|
|
use GuzzleHttp\Exception\ClientException;
|
|
use GuzzleHttp\Exception\GuzzleException;
|
|
use GuzzleHttp\Exception\TransferException;
|
|
use JsonException;
|
|
|
|
/**
|
|
* Class Request
|
|
*/
|
|
abstract class Request
|
|
{
|
|
private string $base;
|
|
private array $body;
|
|
private array $parameters;
|
|
private float $timeOut = 3.14;
|
|
/** @var string */
|
|
private string $token;
|
|
private string $url;
|
|
|
|
/**
|
|
* @return Response
|
|
* @throws ImporterHttpException
|
|
*/
|
|
abstract public function get(): Response;
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getBase(): string
|
|
{
|
|
return $this->base;
|
|
}
|
|
|
|
/**
|
|
* @param string $base
|
|
*/
|
|
public function setBase(string $base): void
|
|
{
|
|
$this->base = $base;
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getToken(): string
|
|
{
|
|
return $this->token;
|
|
}
|
|
|
|
/**
|
|
* @param string $token
|
|
*/
|
|
public function setToken(string $token): void
|
|
{
|
|
$this->token = $token;
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getUrl(): string
|
|
{
|
|
return $this->url;
|
|
}
|
|
|
|
/**
|
|
* @param string $url
|
|
*/
|
|
public function setUrl(string $url): void
|
|
{
|
|
$this->url = $url;
|
|
}
|
|
|
|
/**
|
|
* @return Response
|
|
* @throws ImporterHttpException
|
|
*/
|
|
abstract public function post(): Response;
|
|
|
|
/**
|
|
* @return Response
|
|
* @throws ImporterHttpException
|
|
*/
|
|
abstract public function put(): Response;
|
|
|
|
/**
|
|
* @param array $body
|
|
*/
|
|
public function setBody(array $body): void
|
|
{
|
|
$this->body = $body;
|
|
}
|
|
|
|
/**
|
|
* @param array $parameters
|
|
*/
|
|
public function setParameters(array $parameters): void
|
|
{
|
|
app('log')->debug('Request parameters will be set to: ', $parameters);
|
|
$this->parameters = $parameters;
|
|
}
|
|
|
|
/**
|
|
* @param float $timeOut
|
|
*/
|
|
public function setTimeOut(float $timeOut): void
|
|
{
|
|
$this->timeOut = $timeOut;
|
|
}
|
|
|
|
/**
|
|
* @return array
|
|
* @throws ImporterErrorException
|
|
* @throws ImporterHttpException
|
|
* @throws AgreementExpiredException
|
|
*/
|
|
protected function authenticatedGet(): array
|
|
{
|
|
$fullUrl = sprintf('%s/%s', $this->getBase(), $this->getUrl());
|
|
|
|
if (0 !== count($this->parameters)) {
|
|
$fullUrl = sprintf('%s?%s', $fullUrl, http_build_query($this->parameters));
|
|
}
|
|
app('log')->debug(sprintf('authenticatedGet(%s)', $fullUrl));
|
|
$client = $this->getClient();
|
|
$body = null;
|
|
try {
|
|
$res = $client->request(
|
|
'GET',
|
|
$fullUrl,
|
|
[
|
|
'headers' => [
|
|
'Accept' => 'application/json',
|
|
'Content-Type' => 'application/json',
|
|
'Authorization' => sprintf('Bearer %s', $this->getToken()),
|
|
'user-agent' => sprintf('Firefly III Nordigen importer / %s / %s', config('importer.version'), config('auth.line_b')),
|
|
],
|
|
]
|
|
);
|
|
} catch (TransferException|GuzzleException $e) {
|
|
app('log')->error(sprintf('%s: %s', get_class($e), $e->getMessage()));
|
|
|
|
// crash but there is a response, log it.
|
|
if (method_exists($e, 'getResponse') && method_exists($e, 'hasResponse') && $e->hasResponse()) {
|
|
$response = $e->getResponse();
|
|
app('log')->error(sprintf('%s', $response->getBody()->getContents()));
|
|
}
|
|
|
|
// if no response, parse as normal error response
|
|
if (method_exists($e, 'hasResponse') && !$e->hasResponse()) {
|
|
throw new ImporterHttpException(sprintf('Exception: %s', $e->getMessage()), 0, $e);
|
|
}
|
|
|
|
// if can get response, parse it.
|
|
$json = [];
|
|
if (method_exists($e, 'getResponse')) {
|
|
$body = (string)$e->getResponse()->getBody();
|
|
$json = json_decode($body, true) ?? [];
|
|
}
|
|
if (array_key_exists('summary', $json) and str_ends_with($json['summary'], 'has expired')) {
|
|
$exception = new AgreementExpiredException();
|
|
$exception->json = $json;
|
|
throw $exception;
|
|
}
|
|
|
|
// if status code is 503, the account does not exist.
|
|
$exception = new ImporterErrorException(sprintf('%s: %s', get_class($e), $e->getMessage()), 0, $e);
|
|
$exception->json = $json;
|
|
throw $exception;
|
|
}
|
|
if (200 !== $res->getStatusCode()) {
|
|
// return body, class must handle this
|
|
app('log')->error(sprintf('[1] Status code is %d', $res->getStatusCode()));
|
|
|
|
$body = (string)$res->getBody();
|
|
}
|
|
$body = $body ?? (string)$res->getBody();
|
|
|
|
try {
|
|
$json = json_decode($body, true, 512, JSON_THROW_ON_ERROR);
|
|
} catch (JsonException $e) {
|
|
throw new ImporterHttpException(
|
|
sprintf(
|
|
'Could not decode JSON (%s). Error[%d] is: %s. Response: %s',
|
|
$fullUrl,
|
|
$res->getStatusCode(),
|
|
$e->getMessage(),
|
|
$body
|
|
)
|
|
);
|
|
}
|
|
|
|
if (null === $json) {
|
|
throw new ImporterHttpException(sprintf('Body is empty. [2] Status code is %d.', $res->getStatusCode()));
|
|
}
|
|
app('log')->debug('Return JSON result of authenticatedGet');
|
|
|
|
return $json;
|
|
}
|
|
|
|
/**
|
|
* @param array $json
|
|
*
|
|
* @return array
|
|
* @throws GuzzleException
|
|
* @throws ImporterHttpException
|
|
*/
|
|
protected function authenticatedJsonPost(array $json): array
|
|
{
|
|
app('log')->debug(sprintf('Now at %s', __METHOD__));
|
|
$fullUrl = sprintf('%s/%s', $this->getBase(), $this->getUrl());
|
|
|
|
if (0 !== count($this->parameters)) {
|
|
$fullUrl = sprintf('%s?%s', $fullUrl, http_build_query($this->parameters));
|
|
}
|
|
|
|
$client = $this->getClient();
|
|
try {
|
|
$res = $client->request(
|
|
'POST',
|
|
$fullUrl,
|
|
[
|
|
'json' => $json,
|
|
'headers' => [
|
|
'Accept' => 'application/json',
|
|
'Content-Type' => 'application/json',
|
|
'Authorization' => sprintf('Bearer %s', $this->getToken()),
|
|
],
|
|
]
|
|
);
|
|
} catch (ClientException $e) {
|
|
// TODO error response, not an exception.
|
|
throw new ImporterHttpException(sprintf('AuthenticatedJsonPost: %s', $e->getMessage()), 0, $e);
|
|
}
|
|
$body = (string)$res->getBody();
|
|
|
|
try {
|
|
$json = json_decode($body, true, 512, JSON_THROW_ON_ERROR);
|
|
} catch (JsonException $e) {
|
|
// TODO error response, not an exception.
|
|
throw new ImporterHttpException(sprintf('AuthenticatedJsonPost JSON: %s', $e->getMessage()), 0, $e);
|
|
}
|
|
|
|
return $json;
|
|
}
|
|
|
|
/**
|
|
* @return Client
|
|
*/
|
|
private function getClient(): Client
|
|
{
|
|
// config here
|
|
|
|
return new Client(
|
|
[
|
|
'connect_timeout' => $this->timeOut,
|
|
]
|
|
);
|
|
}
|
|
}
|