Skip to content

Commit

Permalink
[API] Visit patch request to start next stage
Browse files Browse the repository at this point in the history
  • Loading branch information
laemtl committed Jun 14, 2021
1 parent 6859c08 commit 339dc1a
Show file tree
Hide file tree
Showing 2 changed files with 160 additions and 31 deletions.
4 changes: 2 additions & 2 deletions modules/api/docs/LorisRESTAPI_v0.0.4-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -416,10 +416,10 @@ The JSON object is of the form:
}
```
A PUT of the same format but with only the Meta fields will create the VisitLabel
A PUT request of the same format but with only the Meta fields will create the VisitLabel
for this candidate, in an unstarted stage if the Visit label provided is valid.
PATCH is not supported for Visit Labels.
A PATCH request with the Meta fields and a date NextStageDate will start the next stage, if the stage is 'Not Started' and the CandID and Visit label provided are valid.
It will return a 404 Not Found if the visit label does not exist for this candidate
(as well as anything under the /candidates/$CandID/$VisitLabel hierarchy)
Expand Down
187 changes: 158 additions & 29 deletions modules/api/php/endpoints/candidate/visit/visit.class.inc
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,20 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator
*/
private $_visit;

/**
* The visit centerID
*
* @var ?\CenterID
*/
private $_centerID;

/**
* The visit Project
*
* @var ?\Project
*/
private $_project;

/**
* Contructor
*
Expand All @@ -63,6 +77,7 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator
{
return [
'GET',
'PATCH',
'PUT',
];
}
Expand Down Expand Up @@ -95,7 +110,6 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator
if (!$this->_visit->isAccessibleBy($user)) {
return new \LORIS\Http\Response\JSON\Forbidden('Permission denied');
}

}

$pathparts = $request->getAttribute('pathparts');
Expand All @@ -104,6 +118,9 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator
case 'GET':
return $this->_handleGET();

case 'PATCH':
return $this->_handlePATCH($request);

case 'PUT':
return $this->_handlePUT($request);

Expand Down Expand Up @@ -172,23 +189,22 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator
}

/**
* Handles a PUT request that creates or replace a candidate visit
*
* TODO: There is no way to validate the the visit_label in the url
* fits the json data of the request because it is removed from the
* pathparts in the calling class. The correct way to do it would be
* to pass a "light" timepoint class that contains the visit_label but
* no sessionid in the constructor.
* Validate a PUT request
*
* @param ServerRequestInterface $request The incoming PSR7 request
*
* @return ResponseInterface The outgoing PSR7 response
* @return ?ResponseInterface The outgoing PSR7 response
*/
private function _handlePUT(ServerRequestInterface $request): ResponseInterface
{
$user = $request->getAttribute('user');
$data = json_decode((string) $request->getBody(), true);
$visitinfo = $data ?? [];
private function _validatePUT(
ServerRequestInterface $request
): ?ResponseInterface {
$user = $request->getAttribute('user');
$data = json_decode((string) $request->getBody(), true);
$meta = $data ?? [];

if ($this->_visit === null) {
return new \LORIS\Http\Response\JSON\NotFound('Visit not found');
}

$requiredfields = [
'CandID',
Expand All @@ -197,7 +213,7 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator
'Battery',
'Project',
];
$diff = array_diff($requiredfields, array_keys($visitinfo));
$diff = array_diff($requiredfields, array_keys($meta));

if (!empty($diff)) {
return new \LORIS\Http\Response\JSON\BadRequest(
Expand All @@ -206,16 +222,16 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator
}

try {
$project = \NDB_Factory::singleton()->project($visitinfo['Project']);
$this->_project = \NDB_Factory::singleton()->project($meta['Project']);
} catch (\NotFound $e) {
return new \LORIS\Http\Response\JSON\BadRequest(
$e->getMessage()
);
}

if ($visitinfo['CandID'] !== $this->_candidate->getCandID()) {
if ($meta['CandID'] !== $this->_candidate->getCandID()) {
return new \LORIS\Http\Response\JSON\BadRequest(
'CandID do not match this candidate'
'The CandID meta field does not match this candidate'
);
}

Expand All @@ -225,20 +241,132 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator
);
}

$centerid = array_search($visitinfo['Site'], \Utility::getSiteList());
if ($meta['Visit'] !== $this->_visit->getVisitLabel()) {
return new \LORIS\Http\Response\JSON\BadRequest(
'The Visit meta field does not match this visit'
);
}

$centerID = array_search($meta['Site'], \Utility::getSiteList());

if ($centerid === false
|| !in_array(new \CenterID("$centerid"), $user->getCenterIDs())
if ($centerID === false || !in_array(
new \CenterID(strval($this->_centerID)),
$user->getCenterIDs()
)
) {
return new \LORIS\Http\Response\JSON\Forbidden(
"You can't create or modify candidates visit for the site " .
$centerid
$this->_centerID
);
}

// \Utility::getSiteList key was a string. Now that the
// validation is done, convert to an object.
$centerid = new \CenterID("$centerid");
// Now that the validation is done, convert to an object.
$this->_centerID = new \CenterID(strval($centerID));
}

/**
* Handles a PATCH request.
* For now, only support a NextStageDate argument to start next stage.
*
* @param ServerRequestInterface $request The incoming PSR7 request
*
* @return ResponseInterface The outgoing PSR7 response
*/
private function _handlePATCH(ServerRequestInterface $request): ResponseInterface
{
$data = json_decode((string) $request->getBody(), true);
$nextStageDate = $data['NextStageDate'] ?? null;

$response = $this->_validatePUT($request);
if ($response !== null) {
return $response;
}

if (!$nextStageDate) {
return new \LORIS\Http\Response\JSON\BadRequest(
'There is no stage date specified'
);
}

$date_format = 'YYYY-MM-DD';
$date = \DateTime::createFromFormat($date_format, $nextStageDate);
if (!($date && $date->format($date_format) === $nextStageDate)) {
return new \LORIS\Http\Response\JSON\BadRequest(
'The date specified is invalid. Date must be of the form YYYY-MM-DD'
);
}

if ($nextStageDate > date($date_format)) {
return new \LORIS\Http\Response\JSON\BadRequest(
'The date specified must be not later than today'
);
}

if ($this->_visit->getCurrentStage() !== 'Not Started') {
return new \LORIS\Http\Response\JSON\Conflict(
'This visit is already started'
);
}

$newStage = $this->_visit->getNextStage();
if (!$newStage) {
return new \LORIS\Http\Response\JSON\Conflict(
"Next stage is null"
);
}

// start that stage
$this->_visit->startStage($newStage);

// set the date for the new stage
$this->_visit->setData(["Date_".$newStage => $nextStageDate]);

// create a new battery object && new battery
$candidate = \Candidate::singleton($this->_visit->getCandID());

// First visit ?
$firstVisit = false;
try {
if ($candidate->getFirstVisit() == $this->_visit->getVisitLabel()) {
$firstVisit = true;
}
} catch (\LorisException $e) {
}

$battery = new \NDB_BVL_Battery;
// select a specific time point (sessionID) for the battery
$battery->selectBattery($this->_visit->getSessionID());

// add instruments to the time point
$battery->createBattery(
$request->getAttribute("loris"),
$this->_visit->getSubprojectID(),
$newStage,
$this->_visit->getVisitLabel(),
$this->_visit->getCenterID(),
$firstVisit
);

return new \LORIS\Http\Response\JSON\OK();
}

/**
* Handles a PUT request that creates or replace a candidate visit
*
* @param ServerRequestInterface $request The incoming PSR7 request
*
* @return ResponseInterface The outgoing PSR7 response
*/
private function _handlePUT(ServerRequestInterface $request): ResponseInterface
{
$user = $request->getAttribute('user');
$data = json_decode((string) $request->getBody(), true);
$visitinfo = $data ?? [];

$response = $this->_validatePUT($request);
if ($response !== null) {
return $response;
}

$subprojectid = array_search(
$visitinfo['Battery'],
Expand All @@ -255,6 +383,7 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator
$timepoint = \NDB_Factory::singleton()->timepoint(
new \SessionID(strval($sessionid))
);

if ($timepoint->getCurrentStage() !== 'Not Started') {
return new \LORIS\Http\Response\JSON\Conflict(
'This visit is already started'
Expand All @@ -266,7 +395,7 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator

$timepoint->setData(
[
'CenterID' => $centerid,
'CenterID' => $this->_centerID,
'Visit_label' => $visitinfo['Visit'],
'SubprojectID' => $subprojectid,
'Active' => 'Y',
Expand All @@ -275,7 +404,7 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator
'UserID' => $username,
'Date_registered' => $today,
'Testdate' => $today,
'ProjectID' => $project->getId(),
'ProjectID' => $this->_project->getId(),
]
);

Expand All @@ -301,8 +430,8 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator
$this->_candidate,
$subprojectid,
$visitinfo['Visit'],
\Site::singleton($centerid),
$project
\Site::singleton($this->_centerID),
$this->_project
);

$link = '/' . $request->getUri()->getPath();
Expand Down

0 comments on commit 339dc1a

Please sign in to comment.