src/Lms/Modules/Materials/Infrastructure/SurveyMonkeyProxy/SurveyMonkeyProxyClient.php line 43

Open in your IDE?
  1. <?php
  2. namespace CodersLab\Lms\Modules\Materials\Infrastructure\SurveyMonkeyProxy;
  3. use CodersLab\Lms\Modules\Materials\Application\Service\Quiz\QuizException;
  4. use CodersLab\Lms\Modules\Materials\Application\Service\Quiz\QuizResponse;
  5. use CodersLab\Lms\Modules\Materials\Application\Service\SurveyMonkeyProxy\IProxyErrorsQuery;
  6. use CodersLab\Lms\Modules\Materials\Application\Service\SurveyMonkeyProxy\ProxyClient;
  7. use CodersLab\Lms\SharedKernel\Application\CredentialSettings;
  8. use CodersLab\Lms\SharedKernel\Application\DateTime\DateTime;
  9. use CodersLab\Lms\SharedKernel\Application\Enum\CredentialSettingsType;
  10. use CodersLab\Lms\SharedKernel\Application\Query\ICredentialSettingsQuery;
  11. use GuzzleHttp\Client;
  12. use GuzzleHttp\Exception\ClientException;
  13. use GuzzleHttp\Exception\RequestException;
  14. use Symfony\Component\HttpFoundation\Response;
  15. final class SurveyMonkeyProxyClient implements ProxyClient
  16. {
  17.     private const PROXY_ERROR_CODES = [
  18.         Response::HTTP_TOO_MANY_REQUESTS,
  19.         Response::HTTP_UNAUTHORIZED,
  20.         Response::HTTP_SERVICE_UNAVAILABLE,
  21.         Response::HTTP_GATEWAY_TIMEOUT,
  22.     ];
  23.     private static int $retries 0;
  24.     private Client $client;
  25.     private ?CredentialSettings $settings;
  26.     private int $retriesLimit;
  27.     public function __construct(
  28.         private ICredentialSettingsQuery $credentialQuery,
  29.         private IProxyErrorsQuery $proxyErrorsQuery,
  30.     ) {
  31.         $this->initClient();
  32.         $this->retriesLimit $this->credentialQuery->countWithNotExceededLimit(
  33.             CredentialSettingsType::SURVEYMONKEY_PROXY()
  34.         ) * 2;
  35.     }
  36.     public function getResponse(string $quizIdstring $responseId): QuizResponse
  37.     {
  38.         $url sprintf('/response?quiz_id=%s&response_id=%s'$quizId$responseId);
  39.         try {
  40.             $response $this->client->get($url);
  41.         } catch (ClientException $e) {
  42.             if (in_array($e->getResponse()->getStatusCode(), self::PROXY_ERROR_CODEStrue)) {
  43.                 return $this->repeatRequest($quizId$responseId$e);
  44.             }
  45.             $this->proxyErrorsQuery->storeError($quizId$responseId);
  46.             throw new QuizException($e->getMessage(), $e->getCode());
  47.         } catch (RequestException $e) {
  48.             return $this->repeatRequest($quizId$responseId$e);
  49.         }
  50.         $body json_decode($response->getBody(), true);
  51.         if (empty($body['data']['custom_variables'])) {
  52.             $this->throwMissingPropertyException('custom_variables'$quizId$responseId);
  53.         }
  54.         if (empty($body['data']['custom_variables']['student_id'])) {
  55.             $this->throwMissingPropertyException('custom_variables.student_id'$quizId$responseId);
  56.         }
  57.         if (empty($body['data']['custom_variables']['course_id'])) {
  58.             $this->throwMissingPropertyException('custom_variables.course_id'$quizId$responseId);
  59.         }
  60.         if (empty($body['data']['custom_variables']['course_instance_id'])) {
  61.             $this->throwMissingPropertyException('custom_variables.course_instance_id'$quizId$responseId);
  62.         }
  63.         return new QuizResponse(
  64.             $responseId,
  65.             $quizId,
  66.             (float)$body['data']['quiz_results']['score'],
  67.             $body['data']['custom_variables']['course_id'],
  68.             (int)$body['data']['custom_variables']['course_instance_id'],
  69.             (int)$body['data']['custom_variables']['student_id']
  70.         );
  71.     }
  72.     public function repeatRequest(string $quizIdstring $responseIdRequestException $e): QuizResponse
  73.     {
  74.         if (self::$retries $this->retriesLimit) {
  75.             $this->proxyErrorsQuery->storeError($quizId$responseId);
  76.             throw new QuizException(
  77.                 sprintf(
  78.                     'Maximum retries reached! Origin exception: code: %s, msg: %s',
  79.                     $e->getCode(),
  80.                     $e->getMessage()
  81.                 )
  82.             );
  83.         }
  84.         $limitReached $e->getResponse()->getStatusCode() === Response::HTTP_TOO_MANY_REQUESTS;
  85.         if ($limitReached) {
  86.             $limitRefreshInterval = new \DateInterval('P1D');
  87.             $limitRefreshDate = (new \DateTimeImmutable())->add($limitRefreshInterval);
  88.             $limitRefreshDate DateTime::createFromPHPDateTime(
  89.                 \DateTime::createFromImmutable($limitRefreshDate)
  90.             );
  91.             $this->credentialQuery->setLimitRenewalDate($this->settings->getId(), $limitRefreshDate);
  92.         }
  93.         $this->initClient();
  94.         self::$retries++;
  95.         return $this->getResponse($quizId$responseId);
  96.     }
  97.     private function initClient(): void
  98.     {
  99.         $this->settings $this->credentialQuery->getOneWithNotExceededLimit(
  100.             CredentialSettingsType::SURVEYMONKEY_PROXY()
  101.         );
  102.         if (!$this->settings) {
  103.             throw new QuizException('No credentials with free quota found');
  104.         }
  105.         $this->client = new Client([
  106.             'base_uri' => $this->settings->getUrl(),
  107.             'headers' => ['auth-token' => $this->settings->getAuthToken()],
  108.         ]);
  109.     }
  110.     private function throwMissingPropertyException(string $namestring $quizIdstring $responseId): void
  111.     {
  112.         $this->proxyErrorsQuery->storeError($quizId$responseId);
  113.         throw QuizException::missingProperty($name);
  114.     }
  115. }