Skip to content

Commit

Permalink
restructure endpoints + required activity msg
Browse files Browse the repository at this point in the history
  • Loading branch information
wellingguzman committed Feb 23, 2018
1 parent 280ee2d commit 79460c8
Show file tree
Hide file tree
Showing 56 changed files with 2,316 additions and 1,944 deletions.
5 changes: 2 additions & 3 deletions src/bootstrap/application.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@

// Get Environment name
$env = get_api_env();
if ($env !== '_') {
$reservedNames = ['server'];
if ($env !== '_' && !in_array($env, $reservedNames)) {
$configFilePath = sprintf('%s/api.%s.php', $configPath, $env);
if (!file_exists($configFilePath)) {
http_response_code(404);
Expand All @@ -28,6 +29,4 @@

$app = new \Directus\Application\Application(realpath(__DIR__ . '/../../'), require $configFilePath);

create_ping_route($app);

return $app;
4 changes: 2 additions & 2 deletions src/core/Directus/Application/CoreServicesProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ protected function getEmitter()
}
return $payload;
});
$emitter->addAction('table.delete', function ($tableName) use ($container) {
$emitter->addAction('table.delete', function ($tableName, $id) use ($container) {
/** @var Acl $acl */
$acl = $container->get('acl');
$db = $container->get('database');
Expand All @@ -254,7 +254,7 @@ protected function getEmitter()
'ip' => get_request_ip(),
'user_agent' => isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '',
'collection' => $tableName,
'item' => null, // TODO: set the deleted record ID
'item' => $id,
'message' => null
// TODO: Move to revisions
// 'parent_id' => null,
Expand Down
227 changes: 0 additions & 227 deletions src/core/Directus/Application/Route.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,9 @@

use Directus\Application\Http\Request;
use Directus\Application\Http\Response;
use Directus\Database\Schema\SchemaManager;
use Directus\Database\TableGateway\RelationalTableGateway;
use Directus\Exception\BadRequestException;
use Directus\Hook\Emitter;
use Directus\Hook\Payload;
use Directus\Util\ArrayUtils;
use Directus\Validator\Exception\InvalidRequestException;
use Directus\Validator\Validator;
use Symfony\Component\Validator\ConstraintViolationList;

abstract class Route
{
Expand All @@ -29,167 +23,6 @@ abstract class Route
public function __construct(Container $container)
{
$this->container = $container;
$this->validator = new Validator();
}

/**
* @param Request $request
* @param string $tableName
*
* @throws BadRequestException
*/
public function validateRequestWithTable(Request $request, $tableName)
{
$payload = $request->getParsedBody();

$this->validateDataWithTable($request, $payload, $tableName);
}

public function validateDataWithTable(Request $request, array $payload, $tableName)
{
// Only validate create/update
if (in_array($request->getMethod(), ['PATCH', 'PUT', 'POST'])) {
$columnsToValidate = [];

// TODO: Validate empty request
// If the user PATCH, POST or PUT with empty body, must throw an exception to avoid continue the execution
// with the exception of POST, that can use the default value instead
// TODO: Crate a email interface for the sake of validation
if ($request->isPatch()) {
$columnsToValidate = array_keys($payload);

if (empty($columnsToValidate)) {
throw new InvalidRequestException('empty_request');
// return;
}
}

$this->validate($payload, $this->createConstraintFor($tableName, $columnsToValidate));
}
}

/**
* @param Request $request
* @param array $constraints
*
* @return array
*
* @throws BadRequestException
*/
public function validateRequest(Request $request, array $constraints)
{
$this->validate($request->getParsedBody(), $constraints);
}

/**
* @param array $data
* @param array $constraints
*
* @throws BadRequestException
*/
public function validate(array $data, array $constraints)
{
$constraintViolations = $this->getViolations($data, $constraints);

$this->throwErrorIfAny($constraintViolations);
}

/**
* @param array $data
* @param array $constraints
*
* @return array
*/
protected function getViolations(array $data, array $constraints)
{
$violations = [];

foreach ($constraints as $field => $constraint) {
if (is_string($constraint)) {
$constraint = explode('|', $constraint);
}

$violations[$field] = $this->validator->validate(ArrayUtils::get($data, $field), $constraint);
}

return $violations;
}

/**
* Throws an exception if any violations was made
*
* @param ConstraintViolationList[] $violations
*
* @throws BadRequestException
*/
protected function throwErrorIfAny(array $violations)
{
$results = [];

/** @var ConstraintViolationList $violation */
foreach ($violations as $field => $violation) {
$iterator = $violation->getIterator();

$errors = [];
while ($iterator->valid()) {
$constraintViolation = $iterator->current();
$errors[] = $constraintViolation->getMessage();
$iterator->next();
}

if ($errors) {
$results[] = sprintf('%s: %s', $field, implode(', ', $errors));
}
}

if (count($results) > 0) {
throw new InvalidRequestException(implode(' ', $results));
}
}

/**
* Creates the constraint for a an specific table columns
*
* @param string $collectionName
* @param array $fields List of columns name
*
* @return array
*/
protected function createConstraintFor($collectionName, array $fields = [])
{
/** @var SchemaManager $schemaManager */
$schemaManager = $this->container->get('schema_manager');
$collectionObject = $schemaManager->getTableSchema($collectionName);

$constraints = [];

if ($fields === null) {
return $constraints;
}

foreach ($collectionObject->getFields($fields) as $field) {
$columnConstraints = [];

if ($field->hasAutoIncrement()) {
continue;
}

if ($field->isRequired() || (!$field->isNullable() && $field->getDefaultValue() == null)) {
$columnConstraints[] = 'required';
}

if ($field->isArray()) {
$columnConstraints[] = 'array';
} else if ($field->isJson()) {
$columnConstraints[] = 'json';
}

if (!empty($columnConstraints)) {
$constraints[$field->getName()] = $columnConstraints;
}
}

return $constraints;
}

/**
Expand Down Expand Up @@ -272,64 +105,4 @@ public function withData(Response $response, array $data, array $options = [])

return $response->withJson($data);
}

protected function tagResponseCache($tags)
{
$this->container->get('response_cache')->tag($tags);
}

protected function invalidateCacheTags($tags)
{
$this->container->get('cache')->getPool()->invalidateTags($tags);
}

/**
* @param RelationalTableGateway $gateway
* @param array $params
* @param \Closure|null $queryCallback
* @return array|mixed
*/
protected function getEntriesAndSetResponseCacheTags(RelationalTableGateway $gateway, array $params, \Closure $queryCallback = null)
{
return $this->getDataAndSetResponseCacheTags([$gateway, 'getEntries'], [$params, $queryCallback]);
}

/**
* @param callable $callable
* @param array $callableParams
* @param null $pkName
* @return array|mixed
*/
protected function getDataAndSetResponseCacheTags(Callable $callable, array $callableParams = [], $pkName = null)
{
$container = $this->container;

if(is_array($callable) && $callable[0] instanceof RelationalTableGateway) {
/** @var $callable[0] RelationalTableGateway */
$pkName = $callable[0]->primaryKeyFieldName;
}

$setIdTags = function(Payload $payload) use($pkName, $container) {
$tableName = $payload->attribute('tableName');

$this->tagResponseCache('table_'.$tableName);
$this->tagResponseCache('privilege_table_'.$tableName.'_group_'.$container->get('acl')->getGroupId());

foreach($payload->getData() as $item) {
$this->tagResponseCache('entity_'.$tableName.'_'.$item[$pkName]);
}

return $payload;
};

/** @var Emitter $hookEmitter */
$hookEmitter = $container->get('hook_emitter');

$listenerId = $hookEmitter->addFilter('table.select', $setIdTags, Emitter::P_LOW);
$result = call_user_func_array($callable, $callableParams);
$hookEmitter->removeListenerWithIndex($listenerId);

return $result;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

class ItemNotFoundException extends NotFoundException
{
const ERROR_CODE = 1;
const ERROR_CODE = 203;

public function __construct($message = '')
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,13 @@
namespace Directus\Database\Exception;

use Directus\Exception\NotFoundException;
use Exception;

class TableNotFoundException extends NotFoundException
{
const ERROR_CODE = 200;

public function __construct($table)
public function __construct($name)
{
$message = __t('unable_to_find_table_x', ['table_name' => $table]);

parent::__construct($message, static::ERROR_CODE);
parent::__construct(sprintf('Unable to find collection: %s', $name), static::ERROR_CODE);
}
}
5 changes: 5 additions & 0 deletions src/core/Directus/Database/RowGateway/BaseRowGateway.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ public function __construct($primaryKeyColumn, $table, $adapterOrSql, Acl $acl =
parent::__construct($primaryKeyColumn, $table, $adapterOrSql);
}

public function getId()
{
return $this->data[$this->primaryKeyColumn[0]];
}

/**
* Override this function to do table-specific record data filtration, pre-insert and update.
* This method is called during #populate and #populateSkipAcl.
Expand Down
10 changes: 9 additions & 1 deletion src/core/Directus/Database/Schema/Object/Field.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,15 @@ public function getName()
*/
public function getType()
{
return $this->attributes->get('type');
$type = $this->attributes->get('type');

// if the type if empty in Directus Fields table
// fallback to the actual data type
if (!$type) {
$type = $this->getOriginalType();
}

return $type;
}

/**
Expand Down
15 changes: 9 additions & 6 deletions src/core/Directus/Database/Schema/SchemaManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@
class SchemaManager
{
// Tables
const TABLE_PERMISSIONS = 'directus_permissions';
const TABLE_COLLECTIONS = 'directus_collections';
const TABLE_FIELDS = 'directus_fields';
const TABLE_FILES = 'directus_files';
const TABLE_COLLECTION_PRESETS = 'directus_collection_presets';
const TABLE_USERS = 'directus_users';
const TABLE_COLLECTIONS = 'directus_collections';
const TABLE_COLLECTION_PRESETS = 'directus_collection_presets';
const TABLE_FIELDS = 'directus_fields';
const TABLE_FILES = 'directus_files';
const TABLE_GROUPS = 'directus_groups';
const TABLE_PERMISSIONS = 'directus_permissions';
const TABLE_REVISIONS = 'directus_revisions';
const TABLE_SETTINGS = 'directus_settings';
const TABLE_USERS = 'directus_users';

/**
* Schema source instance
Expand Down
16 changes: 10 additions & 6 deletions src/core/Directus/Database/Schema/Sources/AbstractSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ abstract class AbstractSchema implements SchemaInterface
* Cast records values by its column data type
*
* @param array $records
* @param Field[] $columns
* @param Field[] $fields
*
* @return array
*/
public function castRecordValues(array $records, $columns)
public function castRecordValues(array $records, $fields)
{
// hotfix: records sometimes are no set as an array of rows.
$singleRecord = false;
Expand All @@ -24,11 +24,15 @@ public function castRecordValues(array $records, $columns)
$singleRecord = true;
}

foreach ($columns as $column) {
foreach ($fields as $field) {
foreach ($records as $index => $record) {
$columnName = $column->getName();
if (ArrayUtils::has($record, $columnName)) {
$records[$index][$columnName] = $this->castValue($record[$columnName], $column->getType());
$fieldName = $field->getName();
if (ArrayUtils::has($record, $fieldName)) {
if ($fieldName === 'category_id') {
// var_dump($record, $field->getType());
// var_dump($this->castValue($record[$fieldName], $field->getType()));
}
$records[$index][$fieldName] = $this->castValue($record[$fieldName], $field->getType());
}
}
}
Expand Down
Loading

0 comments on commit 79460c8

Please sign in to comment.