Skip to content

Commit

Permalink
Qe 795 rest api publication access start expire (#3776)
Browse files Browse the repository at this point in the history
* Dev: API uses JSON DateTime format

* Dev: Add REST API /site-settings endpoint
  • Loading branch information
kevin-foster-uk committed Mar 5, 2024
1 parent c93378f commit 1f7a2af
Show file tree
Hide file tree
Showing 15 changed files with 191 additions and 34 deletions.
9 changes: 5 additions & 4 deletions application/config/rest/v1.php
Expand Up @@ -17,9 +17,9 @@
'name' => 'Survey Group',
'description' => 'Survey Group',
],
'user' => [
'name' => 'User',
'description' => 'User',
'site-settings' => [
'name' => 'Site Settings',
'description' => 'Site Settings',
]
]
]
Expand All @@ -30,5 +30,6 @@
include_once __DIR__ . '/v1/survey.php',
include_once __DIR__ . '/v1/session.php',
include_once __DIR__ . '/v1/survey-group.php',
include_once __DIR__ . '/v1/user.php'
include_once __DIR__ . '/v1/user.php',
include_once __DIR__ . '/v1/site-settings.php',
);
27 changes: 27 additions & 0 deletions application/config/rest/v1/site-settings.php
@@ -0,0 +1,27 @@
<?php

use LimeSurvey\Api\Command\V1\{
SiteSettings
};

use LimeSurvey\Api\Rest\V1\SchemaFactory\SchemaFactorySiteSettings;

$rest = [];

$rest['v1/site-settings'] = [
'GET' => [
'description' => 'Site Settings',
'commandClass' => SiteSettings::class,
'params' => [],
'responses' => [
'success' => [
'code' => 200,
'description' => 'Success',
'content' => null,
'schema' => (new SchemaFactorySiteSettings)->make()
]
]
]
];

return $rest;
47 changes: 47 additions & 0 deletions application/libraries/Api/Command/V1/SiteSettings.php
@@ -0,0 +1,47 @@
<?php

namespace LimeSurvey\Api\Command\V1;

use Yii;
use LimeSurvey\Api\Command\{
CommandInterface,
Request\Request,
Response\Response,
Response\ResponseFactory
};

/**
* Site Settings
*
* Site settings are different from global settings
* because this endpoint is public.
*/
class SiteSettings implements CommandInterface
{
protected ResponseFactory $responseFactory;

/**
* Constructor
*
* @param ResponseFactory $responseFactory
*/
public function __construct(ResponseFactory $responseFactory)
{
$this->responseFactory = $responseFactory;
}

/**
* Run settings command
*
* @param Request $request
* @return Response
*/
public function run(Request $request)
{
return $this->responseFactory
->makeSuccess([
'siteName' => Yii::app()->getConfig('sitename'),
'timezone' => date_default_timezone_get()
]);
}
}
Expand Up @@ -89,6 +89,7 @@ public function handle(OpInterface $op): void
$surveyUpdater = $diContainer->get(
SurveyAggregateService::class
);
$surveyUpdater->setRestMode(true);

$props = $op->getProps();
$transformedProps = $this->transformer->transform($props);
Expand Down
Expand Up @@ -23,13 +23,12 @@ public function __construct()
'admin' => ['length' => ['min' => 1, 'max' => 50]],
'adminEmail' => ['key' => 'adminemail', 'filter' => 'trim'],
'expires' => [
'key' => 'expires',
'date',
'date' => true,
'formatter' => ['dateTimeToJson' => ['revert' => true]]
],
'startDate' => [
'key' => 'startdate',
'date',
'date' => true,
'formatter' => ['dateTimeToJson' => ['revert' => true]]
],
'anonymized' => ['formatter' => ['ynToBool' => ['revert' => true]]],
Expand Down
Expand Up @@ -27,7 +27,6 @@ public function __construct(
'adminemail' => 'adminEmail',
'language' => true,
'expires' => [
'key' => 'expires',
'formatter' => ['dateTimeToJson' => true]
],
'startdate' => [
Expand Down
Expand Up @@ -12,6 +12,7 @@
class TransformerOutputSurveyDetail extends TransformerOutputActiveRecord
{
private TransformerOutputSurvey $transformerSurvey;
private TransformerOutputSurveyGroup $transformerSurveyGroup;
private TransformerOutputQuestionGroup $transformerQuestionGroup;
private TransformerOutputQuestionGroupL10ns $transformerQuestionGroupL10ns;
private TransformerOutputQuestion $transformerQuestion;
Expand All @@ -27,6 +28,7 @@ class TransformerOutputSurveyDetail extends TransformerOutputActiveRecord
*/
public function __construct(
TransformerOutputSurvey $transformerOutputSurvey,
TransformerOutputSurveyGroup $transformerOutputSurveyGroup,
TransformerOutputQuestionGroup $transformerOutputQuestionGroup,
TransformerOutputQuestionGroupL10ns $transformerOutputQuestionGroupL10ns,
TransformerOutputQuestion $transformerOutputQuestion,
Expand All @@ -38,6 +40,7 @@ public function __construct(
QuestionService $questionService
) {
$this->transformerSurvey = $transformerOutputSurvey;
$this->transformerSurveyGroup = $transformerOutputSurveyGroup;
$this->transformerQuestionGroup = $transformerOutputQuestionGroup;
$this->transformerQuestionGroupL10ns = $transformerOutputQuestionGroupL10ns;
$this->transformerQuestion = $transformerOutputQuestion;
Expand Down Expand Up @@ -77,7 +80,7 @@ public function transform($data, $options = [])
"survey/index",
array('sid' => $data->sid, 'newtest' => "Y", 'lang' => $data->language)
);
$survey['surveyGroup'] = $data->surveygroup;
$survey['surveyGroup'] = $this->transformerSurveyGroup->transform($data->surveygroup);
$survey['owner'] = $this->transformerSurveyOwner->transform($data->owner);
$survey['ownerInherited'] = $this->transformerSurveyOwner->transform($data->oOptions->owner);

Expand Down
Expand Up @@ -19,9 +19,16 @@ public function __construct()
'templateeditormode' => 'templateEditorMode',
'questionselectormode' => 'questionSelectorMode',
'dateformat' => ['key' => 'dateFormat', 'type' => 'int'],
'last_login' => 'lastLogin',
'created' => true,
'modified' => true,
'last_login' => [
'key' => 'lastLogin',
'formatter' => ['dateTimeToJson' => ['revert' => true]]
],
'created' => [
'formatter' => ['dateTimeToJson' => ['revert' => true]]
],
'modified' => [
'formatter' => ['dateTimeToJson' => ['revert' => true]]
],
'user_status' => 'userStatus',
]);
}
Expand Down
@@ -0,0 +1,23 @@
<?php

namespace LimeSurvey\Api\Rest\V1\SchemaFactory;

use GoldSpecDigital\ObjectOrientedOAS\Objects\Schema;
use GoldSpecDigital\ObjectOrientedOAS\Contracts\SchemaContract;

class SchemaFactorySiteSettings
{
/**
* @param \GoldSpecDigital\ObjectOrientedOAS\Contracts\SchemaContract $properties
*/
public function make(SchemaContract ...$properties): Schema
{
return Schema::create()->title('Site Settings')
->description('Site Settings')
->type(Schema::TYPE_OBJECT)
->properties(
Schema::string('siteName')->default(null),
Schema::string('timezone')->default(null),
);
}
}
Expand Up @@ -2,13 +2,26 @@

namespace LimeSurvey\Api\Transformer\Formatter;

/**
* Formatter DateTime to Json
*
* This formatter converts date/time string values assumed to be in server timezone
* to UTC time and formats to the JSON standard 'Y-m-d\TH:i:s.000\Z'.
*
*
* For values that should always be displayed as is, we should not use this formatter
* but instead use only the 'date' valitator. For exmaple we use this formatter on
* 'survey.dateCreated' but not on 'survey.expires' or 'survey.startDate' because we
* want to display and edit the values of 'survey.expires' or 'survey.startDate' using
* the server timezone not the local timezone.
*/
class FormatterDateTimeToJson implements FormatterInterface
{
private string $name = 'dateTimeToJson';
/** @var bool */
private $revert = false;
/** @var string */
private $inputTimezone = 'UTC';
private $inputTimezone;

/**
* @param bool $revert If true performs reverse format conversion
Expand Down Expand Up @@ -67,7 +80,7 @@ protected function revert($value)
$value,
'UTC',
$this->inputTimezone,
'c'
'Y-m-d H:i:s'
);
}

Expand All @@ -87,7 +100,7 @@ protected function dateFormat(
$outputFormat
) {
$timezone = $inputTimeZone;
if ($value === null || $value === "") {
if ($value === null || $value === '') {
return null;
}
$dateTime = date_create(
Expand Down
21 changes: 12 additions & 9 deletions application/libraries/Api/Transformer/Validator/ValidatorDate.php
Expand Up @@ -25,18 +25,21 @@ public function validate($key, $value, $config, $data, $options = [])
$config[$this->name] = $this->normaliseConfigValue($config);
$messages = [];
if ($config[$this->name] !== false && !empty($value)) {
// we expect incoming dates to be in ISO 8601 format
// (Z at the end is optional)
//-- Complete precision (2024-12-24T18:00:01.1234):
$complete = '^\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.(\d+$|\d+Z$)';
//-- No milliseconds (2024-12-24T18:00:01):
$noMili = '^\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:([0-5]\d$|[0-5]\dZ$)';
//-- No Seconds (2024-12-24T18:00):
$noSec = '^\d{4}-[01]\d-[0-3]\dT[0-2]\d:([0-5]\d$|[0-5]\dZ$)';
// We expect incoming dates to be in ISO 8601 format
//-- Complete precision
//- 2024-12-24T18:00:01.1234Z
//- 2024-12-24T18:00:01.1234
//- 2024-12-24T18:00:01
//- 2024-12-24 18:00:01
$complete = '^\d{4}-\d{2}-\d{2}(T|\s)\d{2}:\d{2}:\d{2}(\.\d+)?Z?$';
//-- No Seconds
//- 2024-12-24T18:00
//- 2024-12-24 18:00
$noSec = '^\d{4}-\d{2}-\d{2}(T|\s)\d{2}:\d{2}$';
//-- No Time (2024-12-24):
$noTime = '^\d{4}-\d{2}-\d{2}$';

$regex = "/($complete)|($noMili)|($noSec)|($noTime)/";
$regex = "/($complete)|($noSec)|($noTime)/";
$regexValidator = new ValidatorRegex();
$result = $regexValidator->validateByPattern($regex, $value);

Expand Down
16 changes: 16 additions & 0 deletions application/models/services/SurveyAggregateService.php
Expand Up @@ -29,6 +29,7 @@ class SurveyAggregateService
private UrlParams $urlParams;
private ProxyExpressionManager $proxyExpressionManager;
private TemplateConfiguration $templateConfiguration;
private $restMode = false;

public function __construct(
LanguageSettings $languageSettings,
Expand All @@ -44,6 +45,21 @@ public function __construct(
$this->templateConfiguration = $templateConfiguration;
}

/**
* Set REST Mode
*
* In rest mode we have different expecations about data formats.
* For example datetime objects inputs/output
* as UTC JSON format Y-m-d\TH:i:s.000\Z.
*
* @param bool $restMode
*/
public function setRestMode($restMode)
{
$this->restMode = (bool) $restMode;
$this->generalSettings->setRestMode($this->restMode);
}

/**
* Update
*
Expand Down
Expand Up @@ -33,13 +33,14 @@ class GeneralSettings
private PluginManager $pluginManager;
private LanguageConsistency $languageConsistency;
private User $modelUser;
private $restMode = false;

const FIELD_TYPE_YN = 'yesorno';
const FIELD_TYPE_DATETIME = 'dateime';
const FIELD_TYPE_GAKEY = 'gakey';
const FIELD_TYPE_USE_CAPTCHA = 'use_captcha';
public const FIELD_TYPE_YN = 'yesorno';
public const FIELD_TYPE_DATETIME = 'datetime';
public const FIELD_TYPE_GAKEY = 'gakey';
public const FIELD_TYPE_USE_CAPTCHA = 'use_captcha';

const GA_GLOBAL_KEY = '9999useGlobal9999';
public const GA_GLOBAL_KEY = '9999useGlobal9999';

public function __construct(
Permission $modelPermission,
Expand All @@ -59,6 +60,20 @@ public function __construct(
$this->modelUser = $modelUser;
}

/**
* Set REST Mode
*
* In rest mode we have different expecations about data formats.
* For example datetime objects inputs/output
* as UTC JSON format Y-m-d\TH:i:s.000\Z.
*
* @param boolean $restMode
*/
public function setRestMode($restMode)
{
$this->restMode = (bool) $restMode;
}

/**
* Update
*
Expand Down Expand Up @@ -334,9 +349,12 @@ private function setField($field, &$input, Survey $survey, $meta, $fieldOpts = n
$value = $input[$field] ?? null;
switch ($type) {
case static::FIELD_TYPE_DATETIME:
$value = !empty($value)
? $this->formatDateTimeInput($value)
: $default;
// In rest mode API transformer handles date format conversion
if ($this->restMode === false) {
$value = !empty($value)
? $this->formatDateTimeInput($value)
: $default;
}
break;
case static::FIELD_TYPE_YN:
if (!in_array('' . $value, ['Y', 'N', 'I'])) {
Expand Down

0 comments on commit 1f7a2af

Please sign in to comment.