1: <?php
2:
3: /*
4: * Yate core products API wrapper library
5: * (c) Alexey Pavlyuts <alexey@pavlyuts.ru>
6: */
7:
8: namespace Yate\Api;
9:
10: use Psr\Http\Message\RequestInterface;
11: use Psr\Http\Message\ResponseInterface;
12: use Psr\Http\Message\RequestFactoryInterface;
13: use Psr\Http\Message\StreamFactoryInterface;
14: use Psr\Http\Client\ClientInterface;
15: use Psr\Http\Client\ClientExceptionInterface;
16: use Yate\Api\Exception\YateConnectException;
17: use Yate\Api\Exception\YateApiException;
18:
19: /**
20: * Yate core API wrapper
21: *
22: */
23: class Api
24: {
25:
26: protected const NODE = 'node';
27: protected const REQUEST = 'request';
28: protected const PARAMS = 'params';
29: protected const CODE = 'code';
30: protected const MESSAGE = 'message';
31: protected const JSON_FLAGS = 0;
32:
33: protected ConfigInterface $config;
34: protected RequestFactoryInterface $requestFactory;
35: protected StreamFactoryInterface $streamFactory;
36: protected ClientInterface $client;
37:
38: /**
39: * Setups API with config and dependencies
40: *
41: * @param ConfigInterface $config
42: * @param RequestFactoryInterface $requestFactory
43: * @param StreamFactoryInterface $streamFactory
44: * @param ClientInterface $client
45: */
46: public function __construct(
47: ConfigInterface $config,
48: RequestFactoryInterface $requestFactory,
49: StreamFactoryInterface $streamFactory,
50: ClientInterface $client)
51: {
52: $this->config = $config;
53: $this->requestFactory = $requestFactory;
54: $this->streamFactory = $streamFactory;
55: $this->client = $client;
56: }
57:
58: /**
59: * Generates Request object for given node, API endpoint (request) and params
60: *
61: * @param string $node Yate node name like 'ucn', 'hss', 'smsc', 'dra', e.t.c.
62: * @param string $request Yate API 'request' string
63: * @param array $params Array of params to pass
64: * @return RequestInterface
65: */
66: public function prepareRequest(string $node, string $request, array $params = []): RequestInterface
67: {
68: return $this->requestFactory->createRequest('POST', $this->config->getNodeUri($node))
69: ->withHeader('Content-Type', 'application/json')
70: ->withHeader('X-Authentication', $this->config->getNodeSecret($node))
71: ->withBody(
72: $this->streamFactory->createStream(
73: json_encode(
74: [
75: self::NODE => $node,
76: self::REQUEST => $request,
77: self::PARAMS => $params,
78: ],
79: self::JSON_FLAGS
80: )
81: )
82: );
83: }
84:
85: /**
86: * Parse PSR-7 Response object from Yate API
87: *
88: * Chcks for any problems with answer and throws proper exceptions.
89: * If the answer is a good one, retun it as ApiResponse instance
90: *
91: * @param ResponseInterface $response Response from API as PSR-7 object
92: * @return ApiResponse Successfull response as ApiResponse object
93: * @throws YateConnectException In a case of HTTP errors
94: * @throws YateApiException In a case of API errors
95: */
96: public static function parseResult(ResponseInterface $response): ApiResponse
97: {
98: if ($response->getStatusCode() != 200) {
99: throw new YateConnectException($response->getReasonPhrase(), $response->getStatusCode());
100: }
101: try {
102: $result = json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR);
103: } catch (\JsonException $ex) {
104: throw new YateApiException("JSON decode error: " . $ex->getMessage());
105: }
106: if (!isset($result[self::CODE])) {
107: throw new YateApiException("Bad JSON content: 'code' not found");
108: }
109: if (0 != $result[self::CODE]) {
110: throw new YateApiException($result[self::MESSAGE] ?? '', $result[self::CODE]);
111: }
112: unset($result[self::CODE]);
113: return new ApiResponse($result);
114: }
115:
116: /**
117: * Performs API call
118: *
119: * @param string $node Yate node name like 'ucn', 'hss', 'smsc', 'dra', e.t.c.
120: * @param string $request Yate API 'request' string
121: * @param array $params Array of params to pass
122: * @return ApiResponse Successfull response as ApiResponse object
123: * @throws YateConnectException In a case of HTTP/connection errors
124: * @throws YateApiException In a case of API errors
125: */
126: public function call(string $node, string $request, array $params = []): ApiResponse
127: {
128: try {
129: return self::parseResult(
130: $this->client->sendRequest(
131: $this->prepareRequest($node, $request, $params)
132: )
133: );
134: } catch (ClientExceptionInterface $ex) {
135: throw new YateConnectException($ex->getMessage(), $ex->getCode());
136: }
137: }
138: }
139: