diff --git a/application/controllers/api/v1/API_V1_Controller.php b/application/controllers/api/v1/API_V1_Controller.php deleted file mode 100644 index c5503d97..00000000 --- a/application/controllers/api/v1/API_V1_Controller.php +++ /dev/null @@ -1,174 +0,0 @@ - - * @copyright Copyright (c) 2013 - 2020, Alex Tselegidis - * @license https://opensource.org/licenses/GPL-3.0 - GPLv3 - * @link https://easyappointments.org - * @since v1.2.0 - * ---------------------------------------------------------------------------- */ - -use EA\Engine\Api\V1\Authorization; -use EA\Engine\Types\NonEmptyText; - -/** - * API V1 Controller - * - * Parent controller class for the API v1 resources. Extend this class instead of the CI_Controller - * and call the parent constructor. - * - * @package Controllers - */ -class API_V1_Controller extends EA_Controller { - /** - * Class Constructor - * - * This constructor will handle the common operations of each API call. - * - * Important: Do not forget to call the this constructor from the child classes. - * - * Notice: At the time being only the basic authentication is supported. Make sure - * that you use the API through SSL/TLS for security. - */ - public function __construct() - { - try - { - parent::__construct(); - - $this->load->model('settings_model'); - - $api_token = setting('api_token'); - - $authorization = new Authorization($this); - - if ( ! empty($api_token) && $api_token === $this->get_bearer_token()) - { - return; - } - - if ( ! isset($_SERVER['PHP_AUTH_USER'])) - { - $this->request_authentication(); - return; - } - - $username = new NonEmptyText($_SERVER['PHP_AUTH_USER']); - $password = new NonEmptyText($_SERVER['PHP_AUTH_PW']); - $authorization->basic($username, $password); - } - catch (Throwable $e) - { - $this->handle_exception($e); - exit; - } - } - - /** - * Returns the bearer token value. - * - * @return string - */ - protected function get_bearer_token() - { - $headers = $this->get_authorization_header(); - - // HEADER: Get the access token from the header - - if ( ! empty($headers)) - { - if (preg_match('/Bearer\s(\S+)/', $headers, $matches)) - { - return $matches[1]; - } - } - return NULL; - } - - /** - * Returns the authorization header. - * - * @return string - */ - protected function get_authorization_header() - { - $headers = NULL; - - if (isset($_SERVER['Authorization'])) - { - $headers = trim($_SERVER['Authorization']); - } - else - { - if (isset($_SERVER['HTTP_AUTHORIZATION'])) - { - //Nginx or fast CGI - $headers = trim($_SERVER['HTTP_AUTHORIZATION']); - } - elseif (function_exists('apache_request_headers')) - { - $requestHeaders = apache_request_headers(); - - // Server-side fix for bug in old Android versions (a nice side-effect of this fix means we don't care - // about capitalization for Authorization). - $requestHeaders = array_combine(array_map('ucwords', array_keys($requestHeaders)), array_values($requestHeaders)); - - if (isset($requestHeaders['Authorization'])) - { - $headers = trim($requestHeaders['Authorization']); - } - } - } - - return $headers; - } - - /** - * Sets request authentication headers. - */ - protected function request_authentication() - { - header('WWW-Authenticate: Basic realm="Easy!Appointments"'); - header('HTTP/1.0 401 Unauthorized'); - exit('You are not authorized to use the API.'); - } - - /** - * Outputs the required headers and messages for exception handling. - * - * Call this method from catch blocks of child controller callbacks. - * - * @param Exception $exception Thrown exception to be outputted. - */ - protected function handle_exception(Exception $exception) - { - $error = [ - 'code' => $exception->getCode() ?: 500, - 'message' => $e->getMessage(), - ]; - - $header = $exception instanceof \EA\Engine\Api\V1\Exception - ? $exception->getCode() . ' ' . $exception->get_header() - : '500 Internal Server Error'; - - header('HTTP/1.0 ' . $header); - header('Content-Type: application/json'); - - $this->output - ->set_content_type('application/json') - ->set_output(json_encode($error, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)); - } - - /** - * Throw an API exception stating that the requested record was not found. - * - * @throws \EA\Engine\Api\V1\Exception - */ - protected function throw_record_not_found() - { - throw new \EA\Engine\Api\V1\Exception('The requested record was not found!', 404, 'Not Found'); - } -} diff --git a/application/core/EA_Controller.php b/application/core/EA_Controller.php index a88b542c..57b811bd 100644 --- a/application/core/EA_Controller.php +++ b/application/core/EA_Controller.php @@ -52,6 +52,7 @@ * @property Users_model $users_model * * @property Accounts $accounts + * @property Api $api * @property Availability $availability * @property Google_Sync $google_sync * @property Ics_file $ics_file diff --git a/application/core/EA_Model.php b/application/core/EA_Model.php index 5138a18d..6ecb6dd6 100644 --- a/application/core/EA_Model.php +++ b/application/core/EA_Model.php @@ -38,11 +38,6 @@ * @property EA_Session $session * @property EA_Upload $upload * @property EA_URI $uri - * - * @method int save(array $record) - * @method array find(int $id) - * @method array get($where, int $limit, int $offset, string $order_by) - * @method mixed value(int $id, string $field) */ class EA_Model extends CI_Model { /** @@ -72,7 +67,12 @@ class EA_Model extends CI_Model { */ public function get_value(string $field, int $record_id): string { - return $this->value($field, $record_id); + if (method_exists($this, 'value')) + { + return $this->value($field, $record_id); + } + + throw new RuntimeException('The "get_value" is not defined in model: ', __CLASS__); } /** @@ -88,7 +88,12 @@ class EA_Model extends CI_Model { */ public function get_row(int $record_id): array { - return $this->find($record_id); + if (method_exists($this, 'find')) + { + return $this->find($record_id); + } + + throw new RuntimeException('The "get_row" is not defined in model: ', __CLASS__); } /** @@ -160,4 +165,17 @@ class EA_Model extends CI_Model { } } } + + /** + * Only keep the requested fields of the provided record. + * + * @param array $record Record data. + * @param array $fields Requested field names. + */ + public function only(array &$record, array $fields) + { + $record = array_filter($record, function ($field) use ($fields) { + return in_array($field, $fields); + }, ARRAY_FILTER_USE_KEY); + } } diff --git a/application/models/Admins_model.php b/application/models/Admins_model.php index 51ea8064..a27af2dd 100644 --- a/application/models/Admins_model.php +++ b/application/models/Admins_model.php @@ -523,4 +523,136 @@ class Admins_model extends EA_Model { { // Admins do not currently have any related resources (settings are already part of the admins). } + + /** + * Convert the database admin record to the equivalent API resource. + * + * @param array $admin Admin data. + */ + public function api_encode(array &$admin) + { + $encoded_response = [ + 'id' => array_key_exists('id', $admin) ? (int)$admin['id'] : NULL, + 'firstName' => $admin['first_name'], + 'lastName' => $admin['last_name'], + 'email' => $admin['email'], + 'mobile' => $admin['mobile_number'], + 'phone' => $admin['phone_number'], + 'address' => $admin['address'], + 'city' => $admin['city'], + 'state' => $admin['state'], + 'zip' => $admin['zip_code'], + 'notes' => $admin['notes'], + 'timezone' => $admin['timezone'], + 'settings' => [ + 'username' => $admin['settings']['username'], + 'notifications' => filter_var($admin['settings']['notifications'], FILTER_VALIDATE_BOOLEAN), + 'calendarView' => $admin['settings']['calendar_view'] + ] + ]; + + $admin = $encoded_response; + } + + /** + * Convert the API resource to the equivalent database admin record. + * + * @param array &$admin API resource. + * @param array|null $base Base admin data to be overwritten with the provided values (useful for updates). + */ + public function decode(array &$admin, array $base = NULL) + { + $decoded_response = $base ?? []; + + if (array_key_exists('id', $admin)) + { + $decoded_response['id'] = $admin['id']; + } + + if (array_key_exists('firstName', $admin)) + { + $decoded_response['first_name'] = $admin['firstName']; + } + + if (array_key_exists('lastName', $admin)) + { + $decoded_response['last_name'] = $admin['lastName']; + } + + if (array_key_exists('email', $admin)) + { + $decoded_response['email'] = $admin['email']; + } + + if (array_key_exists('mobile', $admin)) + { + $decoded_response['mobile_number'] = $admin['mobile']; + } + + if (array_key_exists('phone', $admin)) + { + $decoded_response['phone_number'] = $admin['phone']; + } + + if (array_key_exists('address', $admin)) + { + $decoded_response['address'] = $admin['address']; + } + + if (array_key_exists('city', $admin)) + { + $decoded_response['city'] = $admin['city']; + } + + if (array_key_exists('state', $admin)) + { + $decoded_response['state'] = $admin['state']; + } + + if (array_key_exists('zip', $admin)) + { + $decoded_response['zip_code'] = $admin['zip']; + } + + if (array_key_exists('notes', $admin)) + { + $decoded_response['notes'] = $admin['notes']; + } + + if (array_key_exists('timezone', $admin)) + { + $decoded_response['timezone'] = $admin['timezone']; + } + + if (array_key_exists('settings', $admin)) + { + if (empty($decoded_response['settings'])) + { + $decoded_response['settings'] = []; + } + + if (array_key_exists('username', $admin['settings'])) + { + $decoded_response['settings']['username'] = $admin['settings']['username']; + } + + if (array_key_exists('password', $admin['settings'])) + { + $decoded_response['settings']['password'] = $admin['settings']['password']; + } + + if (array_key_exists('notifications', $admin['settings'])) + { + $decoded_response['settings']['notifications'] = filter_var($admin['settings']['notifications'], + FILTER_VALIDATE_BOOLEAN); + } + + if (array_key_exists('calendarView', $admin['settings'])) + { + $decoded_response['settings']['calendar_view'] = $admin['settings']['calendarView']; + } + } + + $admin = $decoded_response; + } } diff --git a/swagger.yml b/swagger.yml index 59dd66fe..bd652cfb 100644 --- a/swagger.yml +++ b/swagger.yml @@ -2,8 +2,8 @@ swagger: '2.0' info: version: 1.0.0 title: 'easyappointments-api' -host: demo.easyappointments.org -basePath: /index.php/api/v1/ +host: localhost +basePath: /dev/easyappointments/index.php/api/v1/ tags: - name: admins - name: providers @@ -16,7 +16,7 @@ tags: - name: categories - name: settings schemes: - - https + #- https - http securityDefinitions: ApiKeyAuth: