Skip to content

Commit

Permalink
[API] Adding /projects/$projectname (#4216)
Browse files Browse the repository at this point in the history
Convert /projects/$projectname endpoint from ad-hoc script to API.
  • Loading branch information
xlecours authored and driusan committed Jan 2, 2019
1 parent 84217df commit 6faf348
Show file tree
Hide file tree
Showing 10 changed files with 237 additions and 40 deletions.
11 changes: 6 additions & 5 deletions htdocs/api/v0.0.1/.htaccess
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,19 @@ Options -Indexes
RewriteCond %{ENV:REDIRECT_STATUS} 200
RewriteRule ^ - [L]

RewriteRule ^login Login.php?PrintLogin=true [L]
# Login
RewriteRule ^login[/]?$ /index.php?lorispath=api/v0.0.1/login/ [QSA,END]

# Projects API rewrite rules
RewriteRule ^projects/$ /index.php?lorispath=api/v0.0.1/projects/ [QSA,END]
RewriteRule ^projects/([a-zA-Z0-9_\w\s.]+)(/*)$ /index.php?lorispath=api/v0.0.1/projects/$1 [QSA,END]

RewriteRule ^projects/([a-zA-Z0-9_\w\s.]+)/instruments/([a-zA-Z0-9_.]+)$ projects/InstrumentForm.php?Instrument=$2&PrintInstrumentForm=true [L]
RewriteRule ^projects/([a-zA-Z0-9_\w\s.]+)/visits(/*)$ projects/Project.php?Project=$1&Visits=true&PrintProjectJSON=true [L]
RewriteRule ^projects/([a-zA-Z0-9_\w\s.]+)/candidates(/*)$ projects/Project.php?Project=$1&Candidates=true&PrintProjectJSON=true [L]
RewriteRule ^projects/([a-zA-Z0-9_\w\s.]+)/instruments(/*)$ projects/Project.php?Project=$1&Instruments=true&PrintProjectJSON=true&InstrumentDetails=true [L]
RewriteRule ^projects/([a-zA-Z0-9_\w\s.]+)(/*)$ projects/Project.php?Project=$1&Instruments=true&Visits=true&Candidates=true&PrintProjectJSON=true [L]

RewriteRule ^projects(/*)$ Projects.php?PrintProjects=true [L]

# Candidates API rewrite rules

RewriteRule ^candidates(/*)$ Candidates.php?PrintCandidates=true [L]
RewriteRule ^candidates/([0-9]+)(/*)$ candidates/Candidate.php?CandID=$1&PrintCandidate=true [L]
RewriteRule ^candidates/([0-9]+)/([a-zA-Z0-9_.]+)(/*)$ candidates/Visit.php?CandID=$1&VisitLabel=$2&PrintVisit=true [L]
Expand Down
4 changes: 3 additions & 1 deletion htdocs/api/v0.0.2/.htaccess
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ RewriteCond %{ENV:REDIRECT_STATUS} 200
RewriteRule ^ - [L]

# Projects API rewrite rules
RewriteRule ^projects/$ /index.php?lorispath=api/v0.0.2/projects/ [QSA,END]
RewriteRule ^projects/([a-zA-Z0-9_\w\s.]+)(/*)$ /index.php?lorispath=api/v0.0.2/projects/$1 [QSA,END]

RewriteRule ^projects/([a-zA-Z0-9_\w\s.]+)/instruments/([a-zA-Z0-9_.]+)$ projects/InstrumentForm.php?Instrument=$2&PrintInstrumentForm=true [L]
RewriteRule ^projects/([a-zA-Z0-9_\w\s.]+)/visits(/*)$ projects/Project.php?Project=$1&Visits=true&PrintProjectJSON=true [L]
RewriteRule ^projects/([a-zA-Z0-9_\w\s.]+)/candidates(/*)$ projects/Project.php?Project=$1&Candidates=true&PrintProjectJSON=true [L]
RewriteRule ^projects/([a-zA-Z0-9_\w\s.]+)/instruments(/*)$ projects/Project.php?Project=$1&Instruments=true&PrintProjectJSON=true&InstrumentDetails=true [L]
RewriteRule ^projects/([a-zA-Z0-9_\w\s.]+)(/*)$ projects/Project.php?Project=$1&Instruments=true&Visits=true&Candidates=true&PrintProjectJSON=true [L]

RewriteRule ^projects/$ /index.php?lorispath=api/v0.0.2/projects/ [QSA,END]

# Candidates API rewrite rules

Expand Down
11 changes: 6 additions & 5 deletions htdocs/api/v0.0.3-dev/.htaccess
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@ Options -Indexes
RewriteCond %{ENV:REDIRECT_STATUS} 200
RewriteRule ^ - [L]

# Login
RewriteRule ^login[/]?$ /index.php?lorispath=api/v0.0.3-dev/login/ [QSA,END]

# Projects API rewrite rules
RewriteRule ^projects/$ /index.php?lorispath=api/v0.0.3-dev/projects/ [QSA,END]
RewriteRule ^projects/([a-zA-Z0-9_\w\s.]+)(/*)$ /index.php?lorispath=api/v0.0.2/projects/$1 [QSA,END]

RewriteRule ^projects/([a-zA-Z0-9_\w\s.]+)/instruments/([a-zA-Z0-9_.]+)$ projects/InstrumentForm.php?Instrument=$2&PrintInstrumentForm=true [L]
RewriteRule ^projects/([a-zA-Z0-9_\w\s.]+)/visits(/*)$ projects/Project.php?Project=$1&Visits=true&PrintProjectJSON=true [L]
RewriteRule ^projects/([a-zA-Z0-9_\w\s.]+)/candidates(/*)$ projects/Project.php?Project=$1&Candidates=true&PrintProjectJSON=true [L]
RewriteRule ^projects/([a-zA-Z0-9_\w\s.]+)/instruments(/*)$ projects/Project.php?Project=$1&Instruments=true&PrintProjectJSON=true&InstrumentDetails=true [L]
RewriteRule ^projects/([a-zA-Z0-9_\w\s.]+)/images(/*)$ projects/Images.php?project_name=$1 [QSA,L]
RewriteRule ^projects/([a-zA-Z0-9_\w\s.]+)(/*)$ projects/Project.php?Project=$1&Instruments=true&Visits=true&Candidates=true&PrintProjectJSON=true [L]

RewriteRule ^projects/$ /index.php?lorispath=api/v0.0.3-dev/projects/ [QSA,END]

# Candidates API rewrite rules

Expand Down Expand Up @@ -44,5 +47,3 @@ RewriteRule ^candidates/([0-9]+)/([a-zA-Z0-9_.]+)/images/([a-zA-Z0-9_.-]+)$ cand
RewriteRule ^candidates/([0-9]+)/([a-zA-Z0-9_.]+)/dicoms$ candidates/visits/Dicoms.php?CandID=$1&VisitLabel=$2&PrintDicoms=true [L]
RewriteRule ^candidates/([0-9]+)/([a-zA-Z0-9_.]+)/dicoms/([a-zA-Z0-9_.-]+)$ candidates/visits/dicoms/Dicom.php?CandID=$1&VisitLabel=$2&Tarname=$3&PrintDicomData=true [L]

# New module enpoints
RewriteRule ^login[/]?$ /index.php?lorispath=api/v0.0.3-dev/login/ [QSA,END]
4 changes: 2 additions & 2 deletions modules/api/php/login.class.inc
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace LORIS\api;

use \Psr\Http\Message\ServerRequestInterface;
use \Psr\Http\Message\ResponseInterface;

use \LORIS\Api\Endpoint;

/**
* A class for handling the api/v????/login endpoint.
Expand All @@ -25,7 +25,7 @@ use \Psr\Http\Message\ResponseInterface;
* @license Loris license
* @link https://github.com/aces/Loris
*/
class Login extends APIEndpoint
class Login extends Endpoint
{
/**
* All users have access to the login endpoint to try and login.
Expand Down
8 changes: 6 additions & 2 deletions modules/api/php/module.class.inc
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,14 @@ class Module extends \Module

// Strip the version and add it to a request attribute, then let the default
// module handler kick in to delegate to the appropriate page.
$newurl = $request->getURI()->withPath($endpoint);
$newurl = $request->getURI()->withPath($endpoint);
// Split the url into parts to form a queue for the endpoints to delegate
// the request to subendpoints
$pathparts = explode('/', $newurl->getPath());
$newrequest = $request
->withURI($newurl)
->withAttribute("LORIS-API-Version", $version);
->withAttribute("LORIS-API-Version", $version)
->withAttribute('pathparts', $pathparts);

$parentresp = parent::handle($newrequest);
switch ($parentresp->getStatusCode()) {
Expand Down
35 changes: 23 additions & 12 deletions modules/api/php/projects.class.inc
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ namespace LORIS\api;

use \Psr\Http\Message\ServerRequestInterface;
use \Psr\Http\Message\ResponseInterface;


use \LORIS\Api\Endpoint;
/**
* A class for handling the api/v????/projects endpoint.
*
Expand All @@ -25,7 +24,7 @@ use \Psr\Http\Message\ResponseInterface;
* @license Loris license
* @link https://github.com/aces/Loris
*/
class Projects extends APIEndpoint implements \LORIS\Middleware\ETagCalculator
class Projects extends Endpoint implements \LORIS\Middleware\ETagCalculator
{
public $skipTemplate = true;

Expand Down Expand Up @@ -85,22 +84,34 @@ class Projects extends APIEndpoint implements \LORIS\Middleware\ETagCalculator
public function handle(ServerRequestInterface $request) : ResponseInterface
{
// FIXME: Validate permissions.
switch ($request->getURI()->getPath()) {
case "projects":
case "projects/":
$projects = $this->_getProjectList();
return (new \LORIS\Http\Response())
->withHeader("Content-Type", "application/json")
->withBody(new \LORIS\Http\StringStream(json_encode($projects)));
// FIXME: Delegate to other endpoints under /projects/ for other paths.
default:
$pathparts = $request->getAttribute('pathparts');

if ($pathparts[0] != 'projects') {
return (new \LORIS\Http\Response())
->withBody(
new \LORIS\Http\StringStream(
'{ "error" : "Invalid API endpoint" }'
)
)->withStatus(404);
}

if (count($pathparts) === 1) {
$projects = $this->_getProjectList();
return (new \LORIS\Http\Response())
->withHeader("Content-Type", "application/json")
->withBody(
new \LORIS\Http\StringStream(
json_encode($projects)
)
);
}

// Delegate to project specific endpoint.
$endpoint = new \LORIS\Api\Endpoints\Projects\Project();
array_shift($pathparts);
$request = $request->withAttribute('pathparts', $pathparts);

return $endpoint->process($request, $endpoint);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion php/libraries/Project.class.inc
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class Project
);

if (empty($projectData)) {
throw new \LorisException("No project named: $projectName");
throw new \NotFound("No project named: $projectName");
}

$project = new Project();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* @license Loris license
* @link https://github.com/aces/Loris
*/
namespace LORIS\api;
namespace LORIS\Api;

use \Psr\Http\Message\ServerRequestInterface;
use \Psr\Http\Server\RequestHandlerInterface;
Expand All @@ -27,7 +27,7 @@
* @license Loris license
* @link https://github.com/aces/Loris
*/
abstract class APIEndpoint extends \NDB_Page
abstract class Endpoint implements RequestHandlerInterface
{
public $skipTemplate = true;

Expand Down
171 changes: 171 additions & 0 deletions src/Api/Endpoints/Projects/Project.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
<?php
/**
* This implements the Project page class under Projects
*
* PHP Version 7
*
* @category API
* @package Loris
* @author Xavier Lecours Boucher <xavier.lecours@mcin.ca>
* @license Loris license
* @link https://github.com/aces/Loris
*/
namespace LORIS\Api\Endpoints\Projects;

use \Psr\Http\Message\ServerRequestInterface;
use \Psr\Http\Message\ResponseInterface;
use \LORIS\Api\Endpoint;

/**
* A class for handling the /projects/$projectname endpoint.
*
* @category API
* @package Loris
* @author Xavier Lecours Boucher <xavier.lecours@mcin.ca>
* @license Loris license
* @link https://github.com/aces/Loris
*/
class Project extends Endpoint implements \LORIS\Middleware\ETagCalculator
{
public $skipTemplate = true;

/**
* A cache of the results of the projects/$projectname endpoint, so that
* it doesn't need to be recalculated for the ETag and handler
*/
protected $responseCache = array();

/**
* All users have access to the login endpoint to try and login.
*
* @return boolean true if access is permitted
*/
function _hasAccess()
{
$user = \User::singleton();
return !($user instanceof \LORIS\AnonymousUser);
}

/**
* Return which methods are supported by this endpoint.
*
* Projects can only be retrieved, not created.
*
* @return array supported HTTP methods
*/
protected function allowedMethods() : array
{
return ['GET'];
}

/**
* Versions of the LORIS API which are supported by this
* endpoint.
*
* Projects has existed since v0.0.1 of the API and has not
* changed since.
*
* @return array a list of supported API versions.
*/
protected function supportedVersions() : array
{
return [
"v0.0.1",
"v0.0.2",
"v0.0.3-dev",
];
}

/**
* Handles a request that starts with /projects/$projectname
*
* @param ServerRequestInterface $request The incoming PSR7 request
*
* @return ResponseInterface The outgoing PSR7 response
*/
public function handle(ServerRequestInterface $request) : ResponseInterface
{
// FIXME: Validate project based permissions.

$pathparts = $request->getAttribute('pathparts');
$projectname = $pathparts[0];

$this->project = \NDB_Factory::singleton()
->project($projectname);

if (count($pathparts) > 1) {
switch($pathparts[1]) {
// FIXME: delegate to other handlers
case 'candidates':
case 'images':
case 'instruments':
case 'visits':
break;
default:
return (new \LORIS\Http\Response())
->withStatus(404);
}
}

return (new \LORIS\Http\Response())
->withHeader("Content-Type", "application/json")
->withBody(
new \LORIS\Http\StringStream(
json_encode($this->_getProject($projectname))
)
);
}

/**
* Returns an array of projects for this LORIS instance
* a format that can be JSON encoded to confirm to the
* API.
*
* @param string $name The project name
*
* @return array That endpoint representation of a project
*/
private function _getProject(string $name) : array
{
if (!isset($this->responseCache[$name])) {

$project = \NDB_Factory::singleton()->project($name);

$meta = array('Project' => $name);

$visits = array_keys(
\Utility::getExistingVisitLabels(
$project->getId()
)
);

$instruments = array_keys(
\Utility::getAllInstruments()
);

$candids = $project->getCandidateIds();

$responsebody['Meta'] = $meta;
$responsebody['Visits'] = $visits;
$responsebody['Instruments'] = $instruments;
$responsebody['Candidates'] = $candids;

$this->responseCache[$name] = $responsebody;
}

return $this->responseCache[$name];
}

/**
* Implements the ETagCalculator interface
*
* @param ServerRequestInterface $request The PSR7 incoming request.
*
* @return string etag summarizing value of this request.
*/
public function ETag(ServerRequestInterface $request) : string
{
$projectname = $request->getAttribute('pathparts')[0];
return md5(json_encode($this->_getProject($projectname), true));
}
}
Loading

0 comments on commit 6faf348

Please sign in to comment.