Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
52.94% covered (warning)
52.94%
36 / 68
20.00% covered (danger)
20.00%
1 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
LibGuides
52.94% covered (warning)
52.94%
36 / 68
20.00% covered (danger)
20.00%
1 / 5
42.68
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 getAccounts
75.00% covered (warning)
75.00%
6 / 8
0.00% covered (danger)
0.00%
0 / 1
3.14
 getAZ
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 authenticateAndSetHeaders
93.33% covered (success)
93.33%
14 / 15
0.00% covered (danger)
0.00%
0 / 1
4.00
 doGet
36.36% covered (danger)
36.36%
12 / 33
0.00% covered (danger)
0.00%
0 / 1
11.44
1<?php
2
3/**
4 * LibGuides API connection class.
5 *
6 * PHP version 8
7 *
8 * Copyright (C) Villanova University 2023.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2,
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 *
23 * @category VuFind
24 * @package  Connection
25 * @author   Demian Katz <demian.katz@villanova.edu>
26 * @author   Brent Palmer <brent-palmer@icpl.org>
27 * @author   Maccabee Levine <msl321@lehigh.edu>
28 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
29 * @link     https://vufind.org
30 */
31
32namespace VuFind\Connection;
33
34use Exception;
35use Laminas\Log\LoggerAwareInterface;
36
37/**
38 * LibGuides API connection class.
39 *
40 * Note: This is for the LibGuides API used by the LibGuidesProfile recommendation service,
41 * this is *not* for the LibGuides search widget "API" used by the LibGuides and LibGuidesAZ
42 * data sources.
43 *
44 * Closely adapted from VuFind\DigitalContent\OverdriveConnector.
45 *
46 * @category VuFind
47 * @package  Connection
48 * @author   Demian Katz <demian.katz@villanova.edu>
49 * @author   Brent Palmer <brent-palmer@icpl.org>
50 * @author   Maccabee Levine <msl321@lehigh.edu>
51 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
52 * @link     https://vufind.org
53 */
54class LibGuides implements
55    OauthServiceInterface,
56    \VuFindHttp\HttpServiceAwareInterface,
57    LoggerAwareInterface
58{
59    use OauthServiceTrait;
60    use \VuFindHttp\HttpServiceAwareTrait;
61    use \VuFind\Log\LoggerAwareTrait {
62        logError as error;
63    }
64
65    /**
66     * HTTP Client
67     *
68     * @var \Laminas\Http\HttpClient
69     */
70    protected $client;
71
72    /**
73     * Base URL of the LibGuides API
74     *
75     * @var string
76     */
77    protected $baseUrl;
78
79    /**
80     * Client ID for a client_credentials grant
81     *
82     * @var string
83     */
84    protected $clientId;
85
86    /**
87     * Client Secret for a client_credentials grant
88     *
89     * @var string
90     */
91    protected $clientSecret;
92
93    /**
94     * User agent to send in header
95     *
96     * @var string
97     */
98    protected $userAgent = 'VuFind';
99
100    /**
101     * Constructor
102     *
103     * @param Config               $config LibGuides API configuration object
104     * @param \Laminas\Http\Client $client HTTP client
105     *
106     * @link https://ask.springshare.com/libguides/faq/873#api-auth
107     */
108    public function __construct(
109        $config,
110        $client
111    ) {
112        $this->client = $client;
113        $this->baseUrl = $config->General->api_base_url;
114        $this->clientId = $config->General->client_id;
115        $this->clientSecret = $config->General->client_secret;
116    }
117
118    /**
119     * Load all LibGuides accounts.
120     *
121     * @return object|null A JSON object of all LibGuides accounts, or null
122     * if an error occurs
123     */
124    public function getAccounts()
125    {
126        if (!$this->authenticateAndSetHeaders()) {
127            return null;
128        }
129
130        $result = $this->doGet(
131            $this->baseUrl . '/accounts?expand=profile,subjects'
132        );
133
134        if (isset($result->errorCode)) {
135            return null;
136        }
137        return $result;
138    }
139
140    /**
141     * Load all LibGuides AZ databases.
142     *
143     * @return object|null A JSON object of all LibGuides databases, or null
144     * if an error occurs
145     */
146    public function getAZ()
147    {
148        if (!$this->authenticateAndSetHeaders()) {
149            return null;
150        }
151
152        $result = $this->doGet(
153            $this->baseUrl . '/az?expand=az_props'
154        );
155
156        if (isset($result->errorCode)) {
157            return null;
158        }
159        return $result;
160    }
161
162    /**
163     * Authenticate to the LibGuides API and set authentication headers.
164     *
165     * @return bool Indicates if authentication succeeded.
166     */
167    protected function authenticateAndSetHeaders()
168    {
169        $tokenData = $this->authenticateWithClientCredentials(
170            $this->baseUrl . '/oauth/token',
171            $this->clientId,
172            $this->clientSecret
173        );
174        if (!$tokenData) {
175            return false;
176        }
177
178        $headers = [];
179        if (
180            isset($tokenData->token_type)
181            && isset($tokenData->access_token)
182        ) {
183            $headers[] = "Authorization: {$tokenData->token_type} "
184                . $tokenData->access_token;
185        }
186        $headers[] = 'User-Agent: ' . $this->userAgent;
187
188        $this->client->setHeaders($headers);
189
190        return true;
191    }
192
193    /**
194     * Perform a GET request to the LibGuides API.
195     *
196     * @param string $url Full request url
197     *
198     * @return object|null A JSON object of the response data, or null if an error occurs
199     */
200    protected function doGet($url)
201    {
202        $this->client->setMethod('GET');
203        $this->client->setUri($url);
204        try {
205            $response = $this->client->send();
206        } catch (Exception $ex) {
207            $this->error(
208                'Exception during request: ' .
209                $ex->getMessage()
210            );
211            return null;
212        }
213
214        if ($response->isServerError()) {
215            $this->error(
216                'LibGuides API HTTP Error: ' .
217                $response->getStatusCode()
218            );
219            $this->debug('Request: ' . $this->client->getRequest());
220            $this->debug('Response: ' . $this->client->getResponse());
221            return null;
222        }
223        $body = $response->getBody();
224        $returnVal = json_decode($body);
225        $this->debug(
226            'Return from LibGuides API Call: ' . $this->varDump($returnVal)
227        );
228        if ($returnVal != null) {
229            if (isset($returnVal->errorCode)) {
230                // In some cases, this should be returned perhaps...
231                $this->error('LibGuides Error: ' . $returnVal->errorCode);
232            }
233            return $returnVal;
234        } else {
235            $this->error(
236                'LibGuides API Error: Nothing returned from API call.'
237            );
238            $this->debug(
239                'Body return from LibGuides API Call: ' . $this->varDump($body)
240            );
241        }
242        return null;
243    }
244}