WIP: skeleton app

parent 54736aa1
/vendor/
/logs/*
!/logs/README.md
# Δοκιμαστικές κλήσεις
http://generic.local.dev/public/ping
http://generic.local.dev/public/apikey
http://generic.local.dev/public/docdata/43AHBTWUtcX9a4KhU8eXBg%3D%3D
http://generic.local.dev/public/docdata/43AHBTWUtcX9a4KhU8eXBg%3D%3D/eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMDAwMDAwIn0.dW6qmMRJoGL8nA6j5U2zLYJUSc9mnoWioqXsC0D80OE
http://generic.local.dev/public/search
http://generic.local.dev/public/search/incoming
http://generic.local.dev/public/search?date_from=2016-12-01
http://generic.local.dev/public/search?sender_id=100000001&date_from=2016-12-01&date_to=2016-12-22
http://generic.local.dev/public/pdf/Q%2BnehDdzn91TSuVrWOWtoQ%3D%3D
http://generic.local.dev/public/pdf/Q%2BnehDdzn91TSuVrWOWtoQ%3D%3D/eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMDAwMDAwIn0.dW6qmMRJoGL8nA6j5U2zLYJUSc9mnoWioqXsC0D80OE
http://generic.local.dev/public/pdf/download/Q%2BnehDdzn91TSuVrWOWtoQ%3D%3D
# Slim Framework 3 Skeleton Application
Use this skeleton application to quickly setup and start working on a new Slim Framework 3 application. This application uses the latest Slim 3 with the PHP-View template renderer. It also uses the Monolog logger.
This skeleton application was built for Composer. This makes setting up a new Slim Framework application quick and easy.
## Install the Application
Run this command from the directory in which you want to install your new Slim Framework application.
php composer.phar create-project slim/slim-skeleton [my-app-name]
https://github.com/slimphp/Slim-Skeleton
Replace `[my-app-name]` with the desired directory name for your new application. You'll want to:
* Point your virtual host document root to your new application's `public/` directory.
* Ensure `logs/` is web writeable.
To run the application in development, you can also run this command.
php composer.phar start
Run this command to run the test suite
php composer.phar test
That's it! Now go build something cool.
{
"name": "minedu-osteam/papyros-open-api-slim-app",
"description": "A Slim Framework application for consuming the PAPYROS Open Api for MINEDU",
"keywords": ["rest", "minedu"],
"homepage": "https://git.minedu.gov.gr/spapad/OpenApi-Papyros-PHP-Clients",
"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="SlimSkeleton">
<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 = require __DIR__ . '/../src/settings.php';
$app = new \Slim\App($settings);
$container = $app->getContainer();
// Set up dependencies
require __DIR__ . '/../src/dependencies.php';
// Register middleware
require __DIR__ . '/../src/middleware.php';
//
// setup the app
//
$container['autoloader'] = $autoloader;
$autoloader->addPsr4('Gr\\Gov\\Minedu\\Osteam\\', __DIR__ . '/../src/osteam');
$container['errorHandler'] = function ($c) {
return function ($request, $response, $exception) use ($c) {
return $c['response']->withJson([
'message' => 'Προέκυψε λάθος',
'in' => $exception->getMessage()
], $exception->getCode()
);
};
};
//
// end setup the app
//
// Register routes
require __DIR__ . '/../src/routes.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;
};
<?php
// Application middleware
// e.g: $app->add(new \Slim\Csrf\Guard);
<?php
//
namespace Gr\Gov\Minedu\Osteam;
use Interop\Container\ContainerInterface;
use Slim\Http\Body;
use Gr\Gov\Minedu\Osteam\Client;
/**
* Description of app
*
* @author spapad
*/
class App
{
// Εισερχόμενο
const DOCUMENT_INCOMING = 1;
const DOCUMENT_INCOMING_STR = 'incoming';
// Εξερχόμενο
const DOCUMENT_OUTGOING = 2;
const DOCUMENT_OUTGOING_STR = 'outgoing';
protected $ci = null;
protected $client = null;
private $username = '';
private $password = '';
private $sender_id = -1;
private $_files = [];
public function __construct(ContainerInterface $ci)
{
$this->ci = $ci;
$settings = $this->ci->get('settings');
$this->username = (isset($settings['username']) ? $settings['username'] : '');
$this->password = (isset($settings['password']) ? $settings['password'] : '');
$this->sender_id = (isset($settings['sender_id']) ? $settings['sender_id'] : '');
$this->client = new Client();
}
/**
* Send a JSON formatted string as JSON response to the client.
*
* @param Response $res
* @param mixed $data The data
* @param int $status The 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;
}
protected function setting($name)
{
throw new \Exception("Not implemented");
}
public function ping($req, $res, $args)
{
return $res->withJson([
'username' => $this->username,
'sender_id' => $this->sender_id,
]);
}
/**
* Λήψη κλειδιού πιστοποίησης.
*
* @return string The api key
* @throws \Exception
*/
protected function getApiKey()
{
$payload = json_encode([
'username' => $this->username,
'password' => $this->password
]);
$response = json_decode($this->client->pauth($payload));
return $response->apiKey;
}
/**
* Λήψη κλειδιού πιστοποίησης.
*
* @throws \Exception
*/
public function apiKey($req, $res, $args)
{
$payload = json_encode([
'username' => $this->username,
'password' => $this->password
]);
return $this->withJsonReady($res, $this->client->pauth($payload));
}
/**
* Ανάκτηση πληροφοριών εγγράφου.
*
* @param string[] $args Πίνακας με παραμέτρους από το call:
* 'hashid' Το μοναδικό hashid του εγγράφου
* 'apikey' (προεραιτικό) Κλειδί αυθεντικοποίησης
* @throws \Exception
*/
public function docData($req, $res, $args)
{
$hashid = (isset($args['hashid']) ? $args['hashid'] : '');
$apikey = (isset($args['apikey']) ? $args['apikey'] : $this->getApiKey());
return $this->withJsonReady($res, $this->client->getDocData($hashid, $apikey));
}
/**
* Αναζήτηση εγγράφων.
* Εκτός από τις path parameters που περιγράφονται παρακάτω ($args)
* η μέθοδος αναζητά και τις παρακάτω (προεραιτικές) παραμέτρους στο
* query string:
* 'sender_id' Κωδικός αποστολέα, π.χ. 10000001
* Η προκαθορισμένη τιμή του senderId προέρχεται από τα settings
* 'date_from' (προεραιτικό) Ημερομηνία αναζήτησης - Από σε μορφή DATE_W3C
* κατά προτίμηση (μπορεί να είναι και YYYY-MM-DD ή άλλο)
* Η προκαθορισμένη τιμή είναι 5 μέρες πριν
* 'date_to' (προεραιτικό) Ημερομηνία αναζήτησης - Έως σε μορφή DATE_W3C
* κατά προτίμηση (μπορεί να είναι και YYYY-MM-DD ή άλλο)
* Η προκαθορισμένη τιμή είναι η χρονική στιγμή της κλήσης
* @param string[] $args Πίνακας με παραμέτρους από το call:
* 'doc_type' (προεραιτικό) Δείτε App::DOCUMENT_INCOMING, App::DOCUMENT_OUTGOING
* App::DOCUMENT_INCOMING_STR, App::DOCUMENT_OUTGOING_STR
* 'apikey' (προεραιτικό) Κλειδί αυθεντικοποίησης
* @throws \Exception
*/
public function searchDocuments($req, $res, $args)
{
$doc_type = null;
if (isset($args['doc_type'])) {
if ($args['doc_type'] == self::DOCUMENT_INCOMING || $args['doc_type'] == self::DOCUMENT_INCOMING_STR) {
$doc_type = self::DOCUMENT_INCOMING;
} elseif ($args['doc_type'] == self::DOCUMENT_OUTGOING || $args['doc_type'] == self::DOCUMENT_OUTGOING_STR) {
$doc_type = self::DOCUMENT_OUTGOING;
}
}
$apikey = (isset($args['apikey']) ? $args['apikey'] : $this->getApiKey());
$payload = json_encode([
'senderId' => $req->getQueryParam('sender_id', $this->sender_id),
'docType' => "$doc_type",
'startDate' => $req->getQueryParam('date_from', date(DATE_W3C, mktime(0, 0, 0, date("m"), date("d") - 5, date("Y")))),
'endDate' => $req->getQueryParam('date_to', date(DATE_W3C)),
]
);
return $res->withJson([
'hashIds' => json_decode($this->client->searchDocuments($payload, $apikey))
]);
}
/**
* Λήψη πληροφοριών αρχείου
*
* @param string[] $args Πίνακας με παραμέτρους από το call:
* 'hashid' Το μοναδικό hashid του εγγράφου
* 'apikey' (προεραιτικό) Κλειδί αυθεντικοποίησης
* @throws \Exception
*/
public function pdfData($req, $res, $args)
{
$hashid = (isset($args['hashid']) ? $args['hashid'] : '');
$apikey = (isset($args['apikey']) ? $args['apikey'] : $this->getApiKey());
return $this->withJsonReady($res, $this->client->getPdf($hashid, $apikey));
}
/**
* Λήψη - μεταφόρτωση αρχείου
*
* @param string[] $args Πίνακας με παραμέτρους από το call:
* 'hashid' Το μοναδικό hashid του εγγράφου
* 'apikey' (προεραιτικό) Κλειδί αυθεντικοποίησης
* @throws \Exception
*/
public function pdfDownload($req, $res, $args)
{
$hashid = (isset($args['hashid']) ? $args['hashid'] : '');
$apikey = (isset($args['apikey']) ? $args['apikey'] : $this->getApiKey());
$result = json_decode($this->client->getPdf($hashid, $apikey), true);
$res = $res->withBody(new Body(fopen('php://temp', 'r+')));
$res->getBody()->write(base64_decode($result["document"]["base64"]));
return $res
->withHeader('Content-Description', 'Get file ' . filter_var($result["description"], FILTER_SANITIZE_STRING))
->withHeader('Content-Type', 'application/pdf')
->withHeader('Content-Disposition', 'attachment;filename="' . basename($result["fileName"]) . '"')
->withHeader('Content-Transfer-Encoding', 'binary')
->withHeader('Expires', '0')
->withHeader('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
->withHeader('Pragma', 'public');
}
/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
public function postProtocol($submission_data, $apikey = null)
{
$postdata = array_merge([
'senderId' => $this->setting('sender_id'),
], $submission_data
);
// take care of files...
if (isset($postdata['mainDoc'])) {
if (is_string($postdata['mainDoc']['document'])) {
$postdata['mainDoc']['document'] = $this->_files[$postdata['mainDoc']['document']];
}
}
if (isset($postdata['attachedDoc'])) {
$postdata['attachedDoc'] = array_map(function ($doc) {
if (is_string($doc['document'])) {
$doc['document'] = $this->_files[$doc['document']];
}
return $doc;
}, $postdata['attachedDoc']);
}
$payload = json_encode($postdata);
$response = json_decode($this->client->postProtocol($payload, $apikey === null ? $this->getApiKey() : $apikey), true);
return $response;
}
/**
* Read a file to use later
*
* @param string $filename The file to read
* @return boolean
*/
public function loadFile($filename)
{
if (is_readable($filename)) {
$file = base64_encode(file_get_contents($filename));
if ($file !== false) {
$this->_files["$filename"] = $file;
return true;
}
}
return false;
}
public function setDebug($debug = true)
{
$this->client->setDebug($debug === true);
return;
}
}
<?php
/*
*
*/
namespace Gr\Gov\Minedu\Osteam;
use Exception;
/**
* Description of Client
*
* @author spapad
*/
class Client
{
private $_debug = false;
private $_settings = [
'base_uri' => 'https://protocoltest.minedu.gov.gr:443/openpapyros/api'
];
public function __construct($settings = [])
{
$this->_settings = array_merge($this->_settings, $settings);
}
public function pauth($payload)
{
$result = $this->put("{$this->_settings['base_uri']}/pauthenticate/pauth", $payload, [
"Content-Type: application/json",
"Accept: application/json",
]
);
return $result;
}
public function searchDocuments($payload, $apikey)
{
$result = $this->post("{$this->_settings['base_uri']}/search/documents", $payload, [
"api_key: {$apikey}",
"Content-Type: application/json",
"Accept: application/json",
]
);
return $result;
}
public function getDocData($hashid, $apikey)
{
$result = $this->get("{$this->_settings['base_uri']}/document/data/{$hashid}", [], [
"api_key: {$apikey}",
"Content-Type: text/plain",
"Accept: application/json",
]
);
return $result;
}
public function getPdf($hashid, $apikey)
{
$result = $this->get("{$this->_settings['base_uri']}/document/pdf/{$hashid}", [], [
"api_key: {$apikey}",
"Content-Type: text/plain",
"Accept: application/json",
]
);
return $result;
}
public function postProtocol($payload, $apikey)
{
$result = $this->post("{$this->_settings['base_uri']}/protocol/submit", $payload, [
"api_key: {$apikey}",
"Content-Type: application/json",
"Accept: application/json",
]
);
return $result;
}
protected function setCommonCurlOptions($ch, $uri, $headers)
{
curl_setopt($ch, CURLOPT_URL, $uri);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_USERAGENT, "OSTEAM barebone php client");
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) {
// πραγματοποιήθηκε κλήση αλλά δεν ήταν "επιτυχής"
// throw new Exception("Αποτυχημένη κλήση. HTTP STATUS {$http_code}. Η απάντηση ήταν: {$result}", $http_code);
// }
curl_close($ch);
return $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) {
// πραγματοποιήθηκε κλήση αλλά δεν ήταν "επιτυχής"
// throw new Exception("Αποτυχημένη κλήση. HTTP STATUS {$http_code}. Η απάντηση ήταν: {$result}", $http_code);
// }
curl_close($ch);
return $result;
}
public function setDebug($debug = true)
{
$this->_debug = ($debug === true);
return;
}
}
<?php
$app->get('/ping', '\Gr\Gov\Minedu\Osteam\App:ping');
$app->get('/apikey', '\Gr\Gov\Minedu\Osteam\App:apiKey');
$app->get('/docdata/{hashid}[/{apikey}]', '\Gr\Gov\Minedu\Osteam\App:docData');
$app->get('/search[/{doc_type}]', '\Gr\Gov\Minedu\Osteam\App:searchDocuments');
$app->get('/pdf/download/{hashid}[/{apikey}]', '\Gr\Gov\Minedu\Osteam\App:pdfDownload');
$app->get('/pdf/{hashid}[/{apikey}]', '\Gr\Gov\Minedu\Osteam\App:pdfData');
$app->any('/[{anythingelse}]', function ($request, $response, $args) {
$this->logger->info("Void response, no action route was enabled");
return $response->withJson([
'message' => 'Δεν αναγνωρίστηκε το αίτημα σας',
'in' => var_export($args, true)
], 404
);
});
<?php
return [
'settings' => [
'displayErrorDetails' => true, // set to false in production
'addContentLengthHeader' => false, // Allow the web server to send the content-length header
// Renderer settings
'renderer' => [
'template_path' => __DIR__ . '/../templates/',
],
// Monolog settings
'logger' => [
'name' => 'slim-app',
'path' => __DIR__ . '/../logs/app.log',
'level' => \Monolog\Logger::DEBUG,
],
//
// app custom settings
// username and password to authenticate
'username' => 'test',
'password' => '123456',
// sender id for test lookups
'sender_id' => 100000001
],
];
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Slim 3</title>
<link href='//fonts.googleapis.com/css?family=Lato:300' rel='stylesheet' type='text/css'>
<style>
body {
margin: 50px 0 0 0;
padding: 0;
width: 100%;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
text-align: center;
color: #aaa;
font-size: 18px;
}
h1 {
color: #719e40;
letter-spacing: -3px;
font-family: 'Lato', sans-serif;
font-size: 100px;
font-weight: 200;
margin-bottom: 0;
}
</style>
</head>
<body>
<h1>Slim</h1>
<div>a microframework for PHP</div>
<?php if (isset($name)) : ?>
<h2>Hello <?= htmlspecialchars($name); ?>!</h2>
<?php else: ?>
<p>Try <a href="http://www.slimframework.com">SlimFramework</a></p>
<?php endif; ?>
</body>
</html>
<?php
namespace Tests\Functional;
use Slim\App;
use Slim\Http\Request;
use Slim\Http\Response;
use Slim\Http\Environment;