From 9291868b0ba46695903a9bf0352665f2e708b9af Mon Sep 17 00:00:00 2001 From: Laetitia Fesselier Date: Mon, 14 Jun 2021 14:47:33 -0400 Subject: [PATCH] [API] Visit patch request to start next stage --- modules/api/docs/LorisRESTAPI_v0.0.4-dev.md | 2 - .../endpoints/candidate/visit/visit.class.inc | 121 +++++++++++++++++- 2 files changed, 119 insertions(+), 4 deletions(-) diff --git a/modules/api/docs/LorisRESTAPI_v0.0.4-dev.md b/modules/api/docs/LorisRESTAPI_v0.0.4-dev.md index a03be523543..9ae6b3ffbb4 100644 --- a/modules/api/docs/LorisRESTAPI_v0.0.4-dev.md +++ b/modules/api/docs/LorisRESTAPI_v0.0.4-dev.md @@ -419,8 +419,6 @@ The JSON object is of the form: A PUT 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. - 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) diff --git a/modules/api/php/endpoints/candidate/visit/visit.class.inc b/modules/api/php/endpoints/candidate/visit/visit.class.inc index 526a18279e9..5e17a0189d6 100644 --- a/modules/api/php/endpoints/candidate/visit/visit.class.inc +++ b/modules/api/php/endpoints/candidate/visit/visit.class.inc @@ -63,6 +63,7 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator { return [ 'GET', + 'PATCH', 'PUT', ]; } @@ -95,7 +96,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'); @@ -104,6 +104,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); @@ -171,10 +174,123 @@ class Visit extends Endpoint implements \LORIS\Middleware\ETagCalculator ); } + /** + * 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); + $meta = $data['Meta'] ?? []; + $nextStageDate = $data['NextStageDate'] ?? null; + + $requiredfields = [ + 'CandID', + 'Visit', + 'Site', + 'Battery', + 'Project', + ]; + $diff = array_diff($requiredfields, array_keys($meta)); + + if (!empty($diff)) { + return new \LORIS\Http\Response\JSON\BadRequest( + 'Field(s) missing: ' . implode(', ', $diff) + ); + } + + 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' + ); + } + + $sessionID = \NDB_Factory::singleton()->database()->pselectOne( + "SELECT ID FROM session + WHERE CandID=:cid AND Visit_label=:vl", + [ + "cid" => $meta['CandID'], + "vl" => $meta['Visit'] + ] + ); + + if (empty($sessionID)) { + return new \LORIS\Http\Response\JSON\BadRequest( + "There is no Session with the provided criteria" + ); + } + + $session = \TimePoint::singleton(new \SessionID($sessionID)); + if ($session->getCurrentStage() !== 'Not Started') { + return new \LORIS\Http\Response\JSON\Conflict( + 'This visit is already started' + ); + } + + $newStage = $session->getNextStage(); + if (!$newStage) { + return new \LORIS\Http\Response\JSON\Conflict( + "Next stage is null" + ); + } + + // start that stage + $session->startStage($newStage); + + // set the date for the new stage + $session->setData(["Date_".$newStage => $nextStageDate]); + + // create a new battery object && new battery + $candidate = \Candidate::singleton($session->getCandID()); + + // First visit ? + $firstVisit = false; + try { + if ($candidate->getFirstVisit() == $session->getVisitLabel()) { + $firstVisit = true; + } + } catch (\LorisException $e) { + } + + $battery = new \NDB_BVL_Battery; + // select a specific time point (sessionID) for the battery + $battery->selectBattery(new \SessionID($sessionID)); + + // add instruments to the time point + $battery->createBattery( + $request->getAttribute("loris"), + $session->getSubprojectID(), + $newStage, + $session->getVisitLabel(), + $session->getCenterID(), + $firstVisit + ); + + return new \LORIS\Http\Response\JSON\OK(); + } + /** * 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 + * TODO: There is no way to validate 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 @@ -255,6 +371,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'