WIP: Transition to SLIM app

parent 3cbd0b9a
# Web Service Client σε PHP
# Προγράμματα κατανάλωσης academic id API
Υλοποιήθηκε ένας απλός web service client και σε PHP για να πραγματοποιεί τις κλήσεις στο επίσημο web service μέσω php curl.
Οι λειτουργίες του είναι:
Τα παρακάτω προγράμματα - βιβλιοθήκες καταναλώνουν το academic id API.
* queryID, που επιστρέφει την απάντηση του επίσημου web service, με παραμέτρους:
* username προεραιτικό, το username για το web service
* password προεραιτικό, το password για το web service
* identity υποχρεωτικό, το academic id για έλεγχο
* queryIDis που επιστρέφει κείμενο isStudent:[true,false] όπως προκύπτει από το επίσημο web service, με παραμέτρους:
* username προεραιτικό, το username για το web service
* password προεραιτικό, το password για το web service
* identity υποχρεωτικό, το academic id για έλεγχο
* echo, που επιστρέφει το query_string, με οποιαδήποτε παράμετρο
Περιέχονται δείγματα κώδικα και βιβλιοθήκες κατανάλωσης στις εξής παραλλαγές:
Οι λειτουργίες δίνονται ως παράμετρος κατά την κλήση. Για παράδειγμα:
* http://local.dev/academic-id/wrapper.php?identity=123456789012 (default is queryID)
* http://local.dev/academic-id/wrapper.php?username=spapad&identity=123456789012&operation=queryIDis
* http://local.dev/academic-id/wrapper.php?operation=echo&identity=123456789012
Έγιναν αλλαγές στον κώδικα PHP για να είναι 1-1 αντίστοιχες οι λειτουργίες με την υλοποίηση σε java (http://ostmgmt.minedu.gov.gr/projects/wos2_esb/wiki/Pilot_service_Academic_ID)
Οι νέες λειτουργίες είναι:
* testServiceStatus
* queryIDnoCD
* queryID
Επίσης παράχθηκε [htaccess](.htaccess) αρχείο για να δίνονται ακριβώς ίδια endpoints.
* [Εφαρμογή διαδικτύου αναπτυγμένη σε SLIM framework](./slim-app/)
* [Εφαρμογή διαδικτύου αναπτυγμένη σε PHP](./barebone/)
# Web Service Client σε PHP
Υλοποιήθηκε ένας απλός web service client και σε PHP για να πραγματοποιεί τις κλήσεις στο επίσημο web service μέσω php curl.
Οι λειτουργίες του είναι:
* queryID, που επιστρέφει την απάντηση του επίσημου web service, με παραμέτρους:
* username προεραιτικό, το username για το web service
* password προεραιτικό, το password για το web service
* identity υποχρεωτικό, το academic id για έλεγχο
* queryIDis που επιστρέφει κείμενο isStudent:[true,false] όπως προκύπτει από το επίσημο web service, με παραμέτρους:
* username προεραιτικό, το username για το web service
* password προεραιτικό, το password για το web service
* identity υποχρεωτικό, το academic id για έλεγχο
* echo, που επιστρέφει το query_string, με οποιαδήποτε παράμετρο
Οι λειτουργίες δίνονται ως παράμετρος κατά την κλήση. Για παράδειγμα:
* http://local.dev/academic-id/wrapper.php?identity=123456789012 (default is queryID)
* http://local.dev/academic-id/wrapper.php?username=spapad&identity=123456789012&operation=queryIDis
* http://local.dev/academic-id/wrapper.php?operation=echo&identity=123456789012
Έγιναν αλλαγές στον κώδικα PHP για να είναι 1-1 αντίστοιχες οι λειτουργίες με την υλοποίηση σε java (http://ostmgmt.minedu.gov.gr/projects/wos2_esb/wiki/Pilot_service_Academic_ID)
Οι νέες λειτουργίες είναι:
* testServiceStatus
* queryIDnoCD
* queryID
Επίσης παράχθηκε [htaccess](.htaccess) αρχείο για να δίνονται ακριβώς ίδια endpoints.
......@@ -59,6 +59,7 @@ function wscall($params)
$payload = json_encode(array("SubmissionCode" => $params['identity']));
curl_setopt($ch, CURLOPT_URL, "https://academicidapp.grnet.gr/admin/web/ws/users/inspectAcademicID");
// curl_setopt($ch, CURLOPT_URL, "https://academicidapp.grnet.gr/admin/web/ws/users/inspectAMKA");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
......
/vendor/
/logs/*
!/logs/README.md
{
"name": "minedu-osteam/amka-api-slim-app",
"description": "A Slim Framework application for consuming the GUNET amka api",
"keywords": ["rest", "minedu"],
"homepage": "https://git.minedu.gov.gr/spapad/samples",
"license": "EUPL",
"authors": [
{
"name": "MINEDU OPEN SOURCE TEAM",
"email": "osteam@minedu.gov.gr",
"homepage": "http://ostmgmt.minedu.gov.gr/"
},
{
"name": "Stavros Papadakis",
"email": "spapad@gmail.com"
}
],
"require": {
"php": ">=5.5.0",
"slim/slim": "^3.1",
"slim/php-view": "^2.0",
"monolog/monolog": "^1.17"
},
"require-dev": {
"phpunit/phpunit": ">=4.8 < 6.0"
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"scripts": {
"start": "php -S 0.0.0.0:8080 -t public public/index.php",
"test": "phpunit"
}
}
This diff is collapsed.
Your Slim Framework application's log files will be written to this directory.
<phpunit bootstrap="vendor/autoload.php">
<testsuites>
<testsuite name="SampleApp">
<directory>tests</directory>
</testsuite>
</testsuites>
</phpunit>
\ No newline at end of file
RewriteEngine On
# Some hosts may require you to use the `RewriteBase` directive.
# If you need to use the `RewriteBase` directive, it should be the
# absolute physical path to the directory that contains this htaccess file.
#
# RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]
<?php
if (PHP_SAPI == 'cli-server') {
// To help the built-in PHP dev server, check if the request was actually for
// something which should probably be served as a static file
$url = parse_url($_SERVER['REQUEST_URI']);
$file = __DIR__ . $url['path'];
if (is_file($file)) {
return false;
}
}
$autoloader = require __DIR__ . '/../vendor/autoload.php';
session_name('MineduOsteamApp');
session_start();
date_default_timezone_set('Europe/Athens');
// Instantiate the app
$settings_file = __DIR__ . '/../src/settings.php';
if (is_readable($settings_file)) {
$settings = require($settings_file);
} else {
$settings = [];
}
$app = new \Slim\App($settings);
$container = $app->getContainer();
$container['autoloader'] = $autoloader;
$autoloader->addPsr4('Gr\Gov\Minedu\Osteam\Slim\\', __DIR__ . '/../src/osteam');
// Set up dependencies
require __DIR__ . '/../src/dependencies.php';
// Register routes
require __DIR__ . '/../src/routes.php';
// Register middleware
require __DIR__ . '/../src/middleware.php';
// Run app
$app->run();
<?php
// DIC configuration
$container = $app->getContainer();
// view renderer
// $container['renderer'] = function ($c) {
// $settings = $c->get('settings')['renderer'];
// return new Slim\Views\PhpRenderer($settings['template_path']);
// };
// monolog
$container['logger'] = function ($c) {
$settings = $c->get('settings')['logger'];
$logger = new Monolog\Logger($settings['name']);
$logger->pushProcessor(new Monolog\Processor\UidProcessor());
$logger->pushHandler(new Monolog\Handler\StreamHandler($settings['path'], $settings['level']));
return $logger;
};
$container['notFoundHandler'] = function ($c) {
return new Gr\Gov\Minedu\Osteam\Slim\Handlers\NotFound;
};
$container['errorHandler'] = function ($c) {
return new Gr\Gov\Minedu\Osteam\Slim\Handlers\Error;
};
<?php
$settings = $app->getContainer()->get('settings');
$username = isset($settings['app']['secure_endpoint_username']) ? $settings['app']['secure_endpoint_username'] : '';
$password = isset($settings['app']['secure_endpoint_password']) ? $settings['app']['secure_endpoint_password'] : '';
// $app->getContainer()->get('router')
// ->getNamedRoute('queryID')
// ->add(new Gr\Gov\Minedu\Osteam\Slim\AuthorizationGuard($username, $password));
<?php
namespace Gr\Gov\Minedu\Osteam\Slim;
use Interop\Container\ContainerInterface;
use Slim\Http\Body;
use Gr\Gov\Minedu\Osteam\Slim\BaseApp;
use Gr\Gov\Minedu\Osteam\Slim\Client;
/**
* Description of app
*
* @author spapad
*/
class App extends BaseApp
{
private $urlAcademicId = '';
private $urlAmka = '';
protected $username = '';
protected $password = '';
protected $secureEndpointUsername = '';
protected $secureEndpointPassword = '';
public function __construct(ContainerInterface $ci)
{
parent::__construct($ci);
$settings = $this->ci->get('settings');
if (isset($settings['app'])) {
$this->username = (isset($settings['app']['username']) ? $settings['app']['username'] : '');
$this->password = (isset($settings['app']['password']) ? $settings['app']['password'] : 0);
$this->secureEndpointUsername = (isset($settings['app']['secure_endpoint_username']) ? $settings['app']['secure_endpoint_username'] : '');
$this->secureEndpointPassword = (isset($settings['app']['secure_endpoint_password']) ? $settings['app']['secure_endpoint_password'] : '');
}
$this->urlAcademicId = 'https://academicidapp.grnet.gr/admin/web/ws/users/inspectAcademicID';
$this->urlAmka = 'https://academicidapp.grnet.gr/admin/web/ws/users/inspectAMKA';
}
protected function generateAuth()
{
$pass_md5 = md5($this->password);
return "Basic " . base64_encode("{$this->username}:{$pass_md5}");
}
/**
*
* @param Psr\Http\Message\ServerRequestInterface $req
* @param Psr\Http\Message\ResponseInterface $res
* @param $args
* @throws \Exception
* @return Response
*/
public function queryID($req, $res, $args)
{
$identity = $args['identity'];
if (preg_match('/^[0-9]{12}$/', $identity) !== 1) {
return $res->withJson(array_merge(BaseApp::coreResponseData(false), [
'message' => 'Error: Service call parameters error, academic id must be 12 digit number'
]), 400);
}
$data = json_encode([
"SubmissionCode" => $identity
]);
$this->logger->info("queryID::{$identity}");
$auth = $this->generateAuth();
$headers = [
"Authorization: {$auth}",
'Content-Type: application/json',
'Accept: */*',
'User-Agent: Academic ID SLIM Client/v1.0 osteam'
];
$results = $this->client->post($this->urlAcademicId, $data, $headers);
if ($results['success'] === false) {
return $this->withJsonReady($res, $results['response'], $results['http_status']);
}
$result = $results['response'];
return $this->withJsonReady($res, $result);
}
/**
*
* @param Psr\Http\Message\ServerRequestInterface $req
* @param Psr\Http\Message\ResponseInterface $res
* @param $args
* @throws \Exception
* @return Response
*/
public function student($req, $res, $args)
{
$identity = $args['identity'];
if (preg_match('/^[0-9]{11}$/', $identity) !== 1) {
return $res->withJson(array_merge(BaseApp::coreResponseData(false), [
'message' => 'Error: Service call parameters error, AMKA must be 11 digit number'
]), 400);
}
$data = json_encode([
"AMKA" => $identity
]);
$this->logger->info("queryID::{$identity}");
$auth = $this->generateAuth();
$headers = [
"Authorization: {$auth}",
'Content-Type: application/json',
'Accept: */*',
'User-Agent: Academic ID SLIM Client/v1.0 osteam'
];
$results = $this->client->post($this->urlAmka, $data, $headers);
if ($results['success'] === false) {
return $this->withJsonReady($res, $results['response'], $results['http_status']);
}
$result = $results['response'];
return $this->withJsonReady($res, $result);
}
// queryIDnoCD
// testServiceStatus
}
<?php
namespace Gr\Gov\Minedu\Osteam\Slim;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
if (!function_exists('getallheaders'))
{
function getallheaders()
{
$headers = '';
foreach ($_SERVER as $name => $value)
{
if (substr($name, 0, 5) == 'HTTP_')
{
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
}
}
return $headers;
}
}
class AuthorizationGuard
{
private $_username;
private $_password;
public function __construct($username, $password)
{
$this->_username = $username;
$this->_password = $password;
}
/**
* Check for authorization basic token
*
* @param \Psr\Http\Message\ServerRequestInterface $request PSR7 request
* @param \Psr\Http\Message\ResponseInterface $response PSR7 response
* @param callable $next Next middleware
*
* @return \Psr\Http\Message\ResponseInterface
*/
public function __invoke(RequestInterface $request, ResponseInterface $response, callable $next)
{
$auth = true;
$headers = getallheaders();
if (array_key_exists('Authorization', $headers)) {
$header = $headers['Authorization'];
$auth_parts = [];
if (preg_match('/^Basic (.+)$/', $header, $auth_parts) === 1) {
if ($auth_parts[1] !== base64_encode("{$this->_username}:{$this->_password}")) {
$auth = [
"message" => "Error: Invalid Credentials"
];
}
} else {
$auth = [
"message" => "Error: Mallformed Authorization Header"
];
}
} else {
$auth = [
"message" => "Error: Missing Authorization Header"
];
}
if ($auth !== true) {
return $response->withJson(array_merge(BaseApp::coreResponseData(false), $auth), 401);
} else {
return $next($request, $response);
}
}
}
<?php
namespace Gr\Gov\Minedu\Osteam\Slim;
use Interop\Container\ContainerInterface;
use Slim\Http\Body;
use Gr\Gov\Minedu\Osteam\Slim\Client;
/**
* Παρέχει μεθόδους ευκολίας για τις εφαρμογές.
* Πιθανές εφαρμογές μπορούν να κάνουν etxend αυτή την κλάση.
*
* @author spapad
*/
class BaseApp
{
protected $ci = null;
protected $logger = null;
protected $debug = false;
protected $client = null;
/**
* Κατασκευή της κλάσης και φύλαξη του container και του logger.
*/
public function __construct(ContainerInterface $ci)
{
$this->ci = $ci;
if (($logger = $this->ci->get('logger')) != null) {
$this->logger = $logger;
}
$this->client = new Client([
'NO_SAFE_CURL' => (isset($settings['verify_ssl']) ? $settings['verify_ssl'] === false : false),
]);
}
/**
* Παρέχει τις ελάχιστες ιδιότητες για το απαντητικό μήνυμα.
* success: true ή false
* timestamp: date σε μορφή ISO8601
*
* @param boolean $success Επιτυχής ή όχι κλήση
* @return array
*/
public static function coreResponseData($success)
{
return [
'success' => $success === true,
'timestamp' => date('c')
];
}
/**
* Αποστολή έτοιμου διαμορφωμένου json response.
*
* @param Response $res
* @param mixed $data Προδιαμορφωμένο json κείμενο
* @param int $status HTTP status code για αποστολή
* @return response
*/
public function withJsonReady($res, $data, $status = null)
{
$response = $res->withBody(new Body(fopen('php://temp', 'r+')));
$response->getBody()->write($data);
$jsonResponse = $response->withHeader('Content-Type', 'application/json;charset=utf-8');
if (isset($status)) {
return $jsonResponse->withStatus($status);
}
return $jsonResponse;
}
/**
* Αποστολή response απλού κειμένου.
*
* @param Response $res
* @param mixed $data Το κείμενο προς αποστολή
* @param int $status HTTP status code για αποστολή
* @return response
*/
public function withTextReady($res, $data, $status = null)
{
$response = $res->withBody(new Body(fopen('php://temp', 'r+')));
$response->getBody()->write($data);
$textResponse = $response->withHeader('Content-Type', 'text/plain');
if (isset($status)) {
return $textResponse->withStatus($status);
}
return $textResponse;
}
protected function log($msg)
{
if ($this->logger) {
$this->logger->info($msg);
}
}
/**
*
*/
public function setDebug($debug = true)
{
$this->debug = ($debug === true);
}
/**
* Αλλαγή χαρακτήρων ώστε το όνομα που παρέχεται να είναι ασφαλές για
* χρήση ως όνομα αρχείου.
*
* @param string $filename
* @return string
*/
protected function sanitizeFilename($filename)
{
return mb_ereg_replace("([^\w\s\d\-_,.])", '_', $filename);
}
}
<?php
/*
*
*/
namespace Gr\Gov\Minedu\Osteam\Slim;
use Exception;
/**
* Description of Client
*
* @author spapad
*/
class Client
{
private $_debug = false;
private $_settings = [
'base_uri' => '' // must set this
];
public function __construct($settings = [])
{
$this->_settings = array_merge($this->_settings, $settings);
}
protected function setCommonCurlOptions($ch, $uri, $headers)
{
curl_setopt($ch, CURLOPT_URL, $uri);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_USERAGENT, "OSTEAM SLIM client");
if (isset($this->_settings['NO_SAFE_CURL']) && $this->_settings['NO_SAFE_CURL'] === true) {
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
}
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_MAXREDIRS, 3);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
if ($this->_debug === true) {
curl_setopt($ch, CURLOPT_VERBOSE, true);
}
}
public function put($uri, $payload, $headers = [])
{
$ch = curl_init();
$this->setCommonCurlOptions($ch, $uri, $headers);
// curl_setopt($ch, CURLOPT_PUT, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
if (curl_errno($ch)) {
throw new Exception("Λάθος κατά την κλήση του {$uri}. Curl error: " . curl_error($ch) . " Curl info: " . var_export(curl_getinfo($ch), true));
}
if (intval(($http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE)) / 100) != 2) {
// πραγματοποιήθηκε κλήση αλλά δεν ήταν "επιτυχής"
throw new Exception("Αποτυχημένη κλήση. HTTP STATUS {$http_code}. Η απάντηση ήταν: {$result}", $http_code);
}
curl_close($ch);
return $result;
}
public function post($uri, $payload, $headers = [])
{
$ch = curl_init();
$this->setCommonCurlOptions($ch, $uri, $headers);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
if (curl_errno($ch)) {
throw new Exception("Λάθος κατά την κλήση του {$uri}. Curl error: " . curl_error($ch) . " Curl info: " . var_export(curl_getinfo($ch), true));
}
if (intval(($http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE)) / 100) != 2) {
// πραγματοποιήθηκε κλήση αλλά δεν ήταν "επιτυχής"
return [
'success' => false,
'http_status' => $http_code,
'response' => $result
];
// throw new Exception("Αποτυχημένη κλήση. HTTP STATUS {$http_code}. Η απάντηση ήταν: {$result}", $http_code);
}
curl_close($ch);
return [
'success' => true,
'http_status' => $http_code,
'response' => $result
];
}
public function get($uri, $params = [], $headers = [])
{
$ch = curl_init();
if (is_array($params) && count($params) > 0) {
$qs = '?' . http_build_query($params);
} else {
$qs = '';
}
$this->setCommonCurlOptions($ch, "{$uri}{$qs}", $headers);
// curl_setopt($ch, CURLOPT_HTTPGET, true); // default
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
if (curl_errno($ch)) {
throw new Exception("Λάθος κατά την κλήση του {$uri}. Curl error: " . curl_error($ch) . " Curl info: " . var_export(curl_getinfo($ch), true));
}
if (intval(($http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE)) / 100) != 2) {
// πραγματοποιήθηκε κλήση αλλά δεν ήταν "επιτυχής"
return [
'success' => false,
'http_status' => $http_code,
'response' => $result
];
// throw new Exception("Αποτυχημένη κλήση. HTTP STATUS {$http_code}. Η απάντηση ήταν: {$result}", $http_code);
}
curl_close($ch);
return [
'success' => true,
'http_status' => $http_code,
'response' => $result
];
}
public function setDebug($debug = true)
{
$this->_debug = ($debug === true);
return;
}
}
<?php
namespace Gr\Gov\Minedu\Osteam\Slim\Handlers;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Slim\Handlers\AbstractError;
use Gr\Gov\Minedu\Osteam\Slim\BaseApp;
/**
* Error handler.