Skip to content

Commit

Permalink
feat(Contexts): API to create a new Context
Browse files Browse the repository at this point in the history
Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
  • Loading branch information
blizzz committed Feb 28, 2024
1 parent 344260c commit 9c24a8e
Show file tree
Hide file tree
Showing 10 changed files with 287 additions and 9 deletions.
2 changes: 2 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,5 +126,7 @@

['name' => 'Context#index', 'url' => '/api/2/contexts', 'verb' => 'GET'],
['name' => 'Context#show', 'url' => '/api/2/contexts/{contextId}', 'verb' => 'GET'],
['name' => 'Context#create', 'url' => '/api/2/contexts', 'verb' => 'POST'],

]
];
2 changes: 2 additions & 0 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ class Application extends App implements IBootstrap {
public const NODE_TYPE_TABLE = 0;
public const NODE_TYPE_VIEW = 1;

public const OWNER_TYPE_USER = 0;

public function __construct() {
parent::__construct(self::APP_ID);
}
Expand Down
22 changes: 22 additions & 0 deletions lib/Controller/ContextController.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,28 @@ public function show(int $contextId): DataResponse {
}
}

/**
* [api v2] Create a new context and return it
*
* @NoAdminRequired
*
* @param string $name Name of the context
* @param string $iconName Material design icon name of the context
* @param string $description Descriptive text of the context
* @param array $nodes optional nodes to be connected to this context
*
* @return DataResponse<Http::STATUS_OK, TablesContext, array{}>|DataResponse<Http::STATUS_INTERNAL_SERVER_ERROR, array{message: string}, array{}>
*
* 200: Tables returned
*/
public function create(string $name, string $iconName, string $description = '', array $nodes = []): DataResponse {
try {
return new DataResponse($this->contextService->create($name, $iconName, $description, $nodes, $this->userId, 0)->jsonSerialize());
} catch (Exception $e) {
return $this->handleError($e);
}
}

/**
* @param Context[] $contexts
* @return array
Expand Down
39 changes: 39 additions & 0 deletions lib/Db/ContextNodeRelation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

namespace OCA\Tables\Db;

use OCP\AppFramework\Db\Entity;

/**
* @method getContextId(): int
* @method setContextId(int $value): void
* @method getNodeId(): int
* @method setNodeId(int $value): void
* @method getNodeType(): string
* @method setNodeType(string $value): void
* @method getPermissions(): int
* @method setPermissions(int $value): void
*/

class ContextNodeRelation extends Entity implements \JsonSerializable {
protected ?int $contextId = null;
protected ?int $nodeId = null;
protected ?string $nodeType = null;
protected ?int $permissions = null;

public function __construct() {
$this->addType('id', 'integer');
}

public function jsonSerialize(): array {
return [
'id' => $this->getId(),
'contextId' => $this->getContextId(),
'nodeId' => $this->getNodeId(),
'nodeType' => $this->getNodeType(),
'permissions' => $this->getPermissions()
];
}
}
18 changes: 18 additions & 0 deletions lib/Db/ContextNodeRelationMapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace OCA\Tables\Db;

use OCP\AppFramework\Db\QBMapper;
use OCP\IDBConnection;

/** @template-extends QBMapper<ContextNodeRelation> */
class ContextNodeRelationMapper extends QBMapper {
protected string $table = 'tables_contexts_rel_context_node';

public function __construct(IDBConnection $db) {
parent::__construct($db, $this->table, ContextNodeRelation::class);
}

}
32 changes: 32 additions & 0 deletions lib/Db/Page.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace OCA\Tables\Db;

use OCP\AppFramework\Db\Entity;

/**
* @method getContextId(): int
* @method setContextId(int $value): void
* @method getPageType(): string
* @method setPageType(string $value): void
*/
class Page extends Entity implements \JsonSerializable {
public const TYPE_STARTPAGE = 'startpage';

protected ?int $contextId = null;
protected ?string $pageType = null;

public function __construct() {
$this->addType('id', 'integer');
}

public function jsonSerialize(): array {
return [
'id' => $this->getId(),
'contextId' => $this->getContextId(),
'pageType' => $this->getPageType(),
];
}
}
34 changes: 34 additions & 0 deletions lib/Db/PageContent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace OCA\Tables\Db;

use OCP\AppFramework\Db\Entity;

/**
* @method getPageId(): int
* @method setPageId(int $value): void
* @method getNodeRelId(): int
* @method setNodeRelId(int $value): void
* @method getOrder(): int
* @method setOrder(int $value): void
*/
class PageContent extends Entity implements \JsonSerializable {
protected ?int $pageId = null;
protected ?int $nodeRelId = null;
protected ?int $order = null;

public function __construct() {
$this->addType('id', 'integer');
}

public function jsonSerialize(): array {
return [
'id' => $this->getId(),
'pageId' => $this->getPageId(),
'nodeRelId' => $this->getNodeRelId(),
'order' => $this->getOrder(),
];
}
}
15 changes: 15 additions & 0 deletions lib/Db/PageContentMapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace OCA\Tables\Db;

use OCP\AppFramework\Db\QBMapper;
use OCP\IDBConnection;

/** @template-extends QBMapper<PageContent> */
class PageContentMapper extends QBMapper {
protected string $table = 'tables_contexts_page_content';

public function __construct(IDBConnection $db) {
parent::__construct($db, $this->table, PageContent::class);
}
}
15 changes: 15 additions & 0 deletions lib/Db/PageMapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace OCA\Tables\Db;

use OCP\AppFramework\Db\QBMapper;
use OCP\IDBConnection;

/** @template-extends QBMapper<Page> */
class PageMapper extends QBMapper {
protected string $table = 'tables_contexts_page';

public function __construct(IDBConnection $db) {
parent::__construct($db, $this->table, Page::class);
}
}
117 changes: 108 additions & 9 deletions lib/Service/ContextService.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,52 @@

namespace OCA\Tables\Service;

use OCA\Tables\AppInfo\Application;
use OCA\Tables\Db\Context;
use OCA\Tables\Db\ContextMapper;
use OCA\Tables\Db\ContextNodeRelation;
use OCA\Tables\Db\ContextNodeRelationMapper;
use OCA\Tables\Db\Page;
use OCA\Tables\Db\PageContent;
use OCA\Tables\Db\PageContentMapper;
use OCA\Tables\Db\PageMapper;
use OCA\Tables\Errors\InternalError;
use OCA\Tables\Errors\PermissionError;
use OCP\DB\Exception;
use Psr\Log\LoggerInterface;

class ContextService {

private ContextMapper $mapper;
private ContextMapper $contextMapper;
private bool $isCLI;
private LoggerInterface $logger;
private ContextNodeRelationMapper $contextNodeRelMapper;
private PageMapper $pageMapper;
private PageContentMapper $pageContentMapper;
private PermissionsService $permissionsService;

public function __construct(
ContextMapper $mapper,
LoggerInterface $logger,
bool $isCLI,
ContextMapper $contextMapper,
ContextNodeRelationMapper $contextNodeRelationMapper,
PageMapper $pageMapper,
PageContentMapper $pageContentMapper,
LoggerInterface $logger,
PermissionsService $permissionsService,
bool $isCLI,
) {
$this->mapper = $mapper;
$this->contextMapper = $contextMapper;
$this->isCLI = $isCLI;
$this->logger = $logger;
$this->contextNodeRelMapper = $contextNodeRelationMapper;
$this->pageMapper = $pageMapper;
$this->pageContentMapper = $pageContentMapper;
$this->permissionsService = $permissionsService;
}

/**
* @throws InternalError
* @throws Exception
* @return Context[]
* @throws Exception
* @throws InternalError
*/
public function findAll(?string $userId): array {
if ($userId !== null && trim($userId) === '') {
Expand All @@ -40,7 +60,7 @@ public function findAll(?string $userId): array {
$this->logger->warning($error);
throw new InternalError($error);
}
return $this->mapper->findAll($userId);
return $this->contextMapper->findAll($userId);
}

/**
Expand All @@ -57,6 +77,85 @@ public function findById(int $id, ?string $userId): Context {
throw new InternalError($error);
}

return $this->mapper->findById($id, $userId);
return $this->contextMapper->findById($id, $userId);
}

/**
* @throws Exception
*/
public function create(string $name, string $iconName, string $description, array $nodes, string $ownerId, int $ownerType): Context {
$context = new Context();
$context->setName($name);
$context->setIcon($iconName);
$context->setDescription($description);
$context->setOwnerId($ownerId);
$context->setOwnerType($ownerType);

$this->contextMapper->insert($context);

if (!empty($nodes)) {
$context->resetUpdatedFields();
$this->insertNodesFromArray($context, $nodes);
$this->insertPage($context);
}

return $context;
}


protected function insertPage(Context $context): void {
$page = new Page();
$page->setContextId($context->getId());
$page->setPageType(Page::TYPE_STARTPAGE);
$this->pageMapper->insert($page);

$addedPage = $page->jsonSerialize();

$i = 1;
foreach ($context->getNodes() as $node) {
$pageContent = new PageContent();
$pageContent->setPageId($page->getId());
$pageContent->setNodeRelId($node['id']);
$pageContent->setOrder(10 * $i++);

$this->pageContentMapper->insert($pageContent);

$addedPage['content'][$pageContent->getId()] = $pageContent->jsonSerialize();
// the content is already embedded in the page
unset($addedPage['content'][$pageContent->getId()]['pageId']);
}

$context->setPages($addedPage);
}

protected function insertNodesFromArray(Context $context, array $nodes): void {
$addedNodes = [];

$userId = $context->getOwnerType() === Application::OWNER_TYPE_USER ? $context->getOwnerId() : null;
foreach ($nodes as $node) {
$contextNodeRel = new ContextNodeRelation();
$contextNodeRel->setContextId($context->getId());
$contextNodeRel->setNodeId($node['id']);
$contextNodeRel->setNodeType($node['type']);
$contextNodeRel->setPermissions($node['permissions'] ?? 660);

try {
if (!$this->permissionsService->canManageNodeById($node['type'], $node['id'], $userId)) {
throw new PermissionError(sprintf('Owner cannot manage node %d (type %d)', $node['id'], $node['type']));
}

$this->contextNodeRelMapper->insert($contextNodeRel);
$addedNodes[] = $contextNodeRel->jsonSerialize();
} catch (Exception $e) {
$this->logger->warning('Could not add node {ntype}/{nid} to context {cid}, skipping.', [
'app' => Application::APP_ID,
'ntype' => $node['type'],
'nid' => $node['id'],
'cid' => $context['id'],
'exception' => $e,
]);
}
}
$context->setNodes($addedNodes);
}
}

0 comments on commit 9c24a8e

Please sign in to comment.