Skip to content

Commit

Permalink
Merge pull request #452 from jjrom/develop
Browse files Browse the repository at this point in the history
[Catalog] Creation simplification
  • Loading branch information
jjrom authored Oct 16, 2024
2 parents b6335f8 + 89cef93 commit fca911f
Show file tree
Hide file tree
Showing 11 changed files with 125 additions and 92 deletions.
2 changes: 1 addition & 1 deletion app/resto/core/RestoConstants.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class RestoConstants
// [IMPORTANT] Starting resto 7.x, default routes are defined in RestoRouter class

// resto version
const VERSION = '9.0.0-RC16';
const VERSION = '9.0.0-RC17';

/* ============================================================
* NEVER EVER TOUCH THESE VALUES
Expand Down
20 changes: 14 additions & 6 deletions app/resto/core/api/STACAPI.php
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ public function getCatalogs($params)
}

// This is /catalogs/*
return $this->processPath($params['segments'], $params);
return $this->processPath($params['segments'], $params);
}

/**
Expand Down Expand Up @@ -392,13 +392,12 @@ public function addCatalog($params, $body)
*/
$body['rtype'] = 'catalog';
$body['id'] = $this->getIdPath($body, $parentId);
$baseUrl = $this->context->core['baseUrl'];

if ($this->catalogsFunctions->getCatalog($body['id'], $baseUrl) !== null) {

if ($this->catalogsFunctions->getCatalog($body['id'], $this->context->core['baseUrl']) !== null) {
RestoLogUtil::httpError(409, 'Catalog ' . $body['id'] . ' already exists');
}

return RestoLogUtil::success('Catalog added', $this->catalogsFunctions->storeCatalogs(array($body), $baseUrl, $this->user->profile['id'], null, null, true));
return RestoLogUtil::success('Catalog added', $this->catalogsFunctions->storeCatalogs(array($body), $this->context, $this->user->profile['id'], null, null, true));

}

Expand Down Expand Up @@ -492,7 +491,7 @@ public function updateCatalog($params, $body)
}
}

return $this->catalogsFunctions->updateCatalog($catalogs[0], $this->user->profile['id'], $this->context->core['baseUrl']) ? RestoLogUtil::success('Catalog updated') : RestoLogUtil::error('Cannot update catalog');
return $this->catalogsFunctions->updateCatalog($catalogs[0], $this->user->profile['id'], $this->context) ? RestoLogUtil::success('Catalog updated') : RestoLogUtil::error('Cannot update catalog');
}

/**
Expand Down Expand Up @@ -1337,6 +1336,11 @@ private function processPath($segments, $params = array())

// The path is the catalog identifier
$parentAndChilds = $this->getParentAndChilds(join('/', $segments), $params);

if ( !isset($parentAndChilds) ) {
return RestoLogUtil::httpError(404);
}

$catalog = array(
'stac_version' => STACAPI::STAC_VERSION,
'id' => $segments[count($segments) -1 ],
Expand Down Expand Up @@ -1472,6 +1476,10 @@ private function getParentAndChilds($catalogId, $params)
'id' => $catalogId,
'q' => $params['q'] ?? null
), $this->context->core['baseUrl'], true);

if ( empty($catalogs) ) {
return null;
}

$parentAndChilds = array(
'parent' => $catalogs[0] ?? null,
Expand Down
70 changes: 26 additions & 44 deletions app/resto/core/dbfunctions/CatalogsFunctions.php
Original file line number Diff line number Diff line change
Expand Up @@ -200,13 +200,13 @@ public function getCatalogItems($catalogId, $baseUrl)
* !! THIS FUNCTION IS THREAD SAFE !!
*
* @param array $catalogs
* @param string $baseUrl
* @param RestoContext $context
* @param string $userid
* @param RestoCollection $collection
* @param string $featureId
* @param boolean addBeginCommit // True means that call is already within a BEGIN/COMMIT block
*/
public function storeCatalogs($catalogs, $baseUrl, $userid, $collection, $featureId, $addBeginCommit)
public function storeCatalogs($catalogs, $context, $userid, $collection, $featureId, $addBeginCommit)
{

// Empty catalogs - do nothing
Expand All @@ -223,7 +223,7 @@ public function storeCatalogs($catalogs, $baseUrl, $userid, $collection, $featur
}

for ($i = count($catalogs); $i--;) {
$this->storeCatalog($catalogs[$i], $userid, $baseUrl, $collectionId, $featureId);
$this->storeCatalog($catalogs[$i], $userid, $context, $collectionId, $featureId);
}

// Update all counters at the same time for a given featureId
Expand Down Expand Up @@ -251,10 +251,10 @@ public function storeCatalogs($catalogs, $baseUrl, $userid, $collection, $featur
*
* @param array $catalog
* @param string $userid
* @param string $baseUrl
* @param RestoContext $context
* @return boolean
*/
public function updateCatalog($catalog, $userid, $baseUrl)
public function updateCatalog($catalog, $userid, $context)
{

if ( !isset($catalog['id']) ) {
Expand All @@ -274,7 +274,7 @@ public function updateCatalog($catalog, $userid, $baseUrl)
);

$set = array();
$cleanLinks = $this->getCleanLinks($catalog, $userid, $baseUrl);
$cleanLinks = $this->getCleanLinks($catalog, $userid, $context);
$catalog['links'] = $cleanLinks['links'];

foreach (array_keys($catalog) as $key ) {
Expand Down Expand Up @@ -402,11 +402,11 @@ public function removeCatalog($catalogId)
*
* @param array $catalog
* @param string $userid
* @param string $baseUrl
* @param RestoContext $context
* @param string $collectionId
* @param string $featureId
*/
private function storeCatalog($catalog, $userid, $baseUrl, $collectionId, $featureId)
private function storeCatalog($catalog, $userid, $context, $collectionId, $featureId)
{
// Empty catalog - do nothing
if (!isset($catalog)) {
Expand All @@ -418,7 +418,7 @@ private function storeCatalog($catalog, $userid, $baseUrl, $collectionId, $featu
$catalog['id'] = rtrim($catalog['id'], '/');
}

$cleanLinks = $this->getCleanLinks($catalog, $userid, $baseUrl);
$cleanLinks = $this->getCleanLinks($catalog, $userid, $context);

$insert = 'INSERT INTO ' . $this->dbDriver->targetSchema . '.catalog (id, title, description, level, counters, owner, links, visibility, rtype, created) SELECT $1,$2,$3,$4,$5,$6,$7,$8,$9,now() ON CONFLICT (id) DO NOTHING';
$this->dbDriver->pQuery($insert, array(
Expand Down Expand Up @@ -478,21 +478,6 @@ private function storeCatalog($catalog, $userid, $baseUrl, $collectionId, $featu
*/
$this->addInternalItems($cleanLinks['internalItems'], $catalog['id']);

/*
* Now the tricky part - change catalogs level
*/
for ($i = 0, $ii = count($cleanLinks['updateCatalogs']); $i < $ii; $i++) {
$updateCatalogs = $cleanLinks['updateCatalogs'][$i];
$this->dbDriver->pQuery('UPDATE ' . $this->dbDriver->targetSchema . '.catalog SET id=$2, level=level + 1 WHERE lower(id)=lower($1)', array(
$updateCatalogs['id'],
$catalog['id'] . '/' . $updateCatalogs['id']
), 500, 'Cannot update child link ' . $updateCatalogs['id']);
$this->dbDriver->pQuery('UPDATE ' . $this->dbDriver->targetSchema . '.catalog_feature SET path=$2 WHERE path=$1', array(
RestoUtil::path2ltree($updateCatalogs['id']),
RestoUtil::path2ltree($catalog['id'] . '/' . $updateCatalogs['id'])
), 500, 'Cannot update catalog feature association for child link ' . $updateCatalogs['id']);
}

return $catalog;

}
Expand Down Expand Up @@ -691,20 +676,19 @@ private function insertIntoCatalogFeature($featureId, $path, $catalogId, $collec
*
* @param array $catalog
* @param string userid
* @param string $baseUrl
* @param string $context
* @return array
*/
private function getCleanLinks($catalog, $userid, $baseUrl) {
private function getCleanLinks($catalog, $userid, $context) {

$output = array(
'links' => array(),
'updateCatalogs' => array(),
'internalItems' => array()
);

if ( !isset($catalog['links']) ) {
return $output;
}
};

for ($i = 0, $ii = count($catalog['links']); $i < $ii; $i++) {
$link = $catalog['links'][$i];
Expand All @@ -723,18 +707,21 @@ private function getCleanLinks($catalog, $userid, $baseUrl) {
*/
if ( in_array($link['rel'], array('item', 'items')) ) {

if ( !str_starts_with($link['href'], $baseUrl . RestoRouter::ROUTE_TO_COLLECTIONS ) ) {
if ( !str_starts_with($link['href'], $context->core['baseUrl'] . RestoRouter::ROUTE_TO_COLLECTIONS ) ) {
$output['links'][] = $link;
continue;
}

$exploded = explode('/', substr($link['href'], strlen($baseUrl . RestoRouter::ROUTE_TO_COLLECTIONS) + 1));
$exploded = explode('/', substr($link['href'], strlen($context->core['baseUrl'] . RestoRouter::ROUTE_TO_COLLECTIONS) + 1));
// A item endpoint is /collections/{collectionId}/items/{featureId}
if (count($exploded) === 3) {

// Eventually convert collection alias to real collection id
$collectionId = (new CollectionsFunctions($this->dbDriver))->aliasToCollectionId($exploded[0]) ?? $exploded[0];
$internalItem = array(
'id' => RestoUtil::isValidUUID($exploded[2]) ? $exploded[2] : RestoUtil::toUUID($exploded[2]),
'href' => $link['href'],
'collection' => $exploded[0]
'collection' => $collectionId
);
$output['internalItems'][] = $internalItem;

Expand All @@ -754,8 +741,8 @@ private function getCleanLinks($catalog, $userid, $baseUrl) {
/*
* Avoid cycling (i.e. catalog self referencing one of its parent)
*/
if (str_starts_with($link['href'], $baseUrl . RestoRouter::ROUTE_TO_CATALOGS )) {
$exploded = explode('/', substr($link['href'], strlen($baseUrl . RestoRouter::ROUTE_TO_CATALOGS) + 1));
if (str_starts_with($link['href'], $context->core['baseUrl'] . RestoRouter::ROUTE_TO_CATALOGS )) {
$exploded = explode('/', substr($link['href'], strlen($context->core['baseUrl'] . RestoRouter::ROUTE_TO_CATALOGS) + 1));
if ( count($exploded) <= count(explode('/', $catalog['id'])) ) {
return RestoLogUtil::httpError(400, 'Child ' . $link['href'] . ' is invalid because it references a parent resource');
}
Expand All @@ -764,29 +751,24 @@ private function getCleanLinks($catalog, $userid, $baseUrl) {
/*
* Store local collection within links
*/
if (str_starts_with($link['href'], $baseUrl . RestoRouter::ROUTE_TO_COLLECTIONS )) {
if (str_starts_with($link['href'], $context->core['baseUrl'] . RestoRouter::ROUTE_TO_COLLECTIONS )) {
$output['links'][] = $link;
continue;
}

}

$exploded = explode($baseUrl . RestoRouter::ROUTE_TO_CATALOGS . '/', $link['href']);
$exploded = explode($context->core['baseUrl'] . RestoRouter::ROUTE_TO_CATALOGS . '/', $link['href']);
if ( count($exploded) !== 2) {
return RestoLogUtil::httpError(400, 'One link child has an external href i.e. not starting with ' . $baseUrl . RestoRouter::ROUTE_TO_CATALOGS);
return RestoLogUtil::httpError(400, 'One link child has an external href i.e. not starting with ' . $context->core['baseUrl'] . RestoRouter::ROUTE_TO_CATALOGS);
}

$childCatalog = $this->getCatalog($exploded[1], $baseUrl);
$childCatalog = $this->getCatalog($exploded[1], $context->core['baseUrl']);
if ( $childCatalog === null ) {
return RestoLogUtil::httpError(400, 'Catalog child ' . $link['href'] . ' does not exist in database');
return RestoLogUtil::httpError(400, 'Catalog child ' . $link['href'] . ' does not exist');
}

if ($childCatalog['level'] === 1 && $childCatalog['owner'] === $userid) {
array_push($output['updateCatalogs'], ...$this->getCatalogs(array('id' => $childCatalog['id']), $baseUrl, true));
}
else {
$output['links'][] = $link;
}
$output['links'][] = $link;

}

Expand Down
4 changes: 2 additions & 2 deletions app/resto/core/dbfunctions/FeaturesFunctions.php
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ public function storeFeature($id, $collection, $featureArray)
/*
* Store catalogs
*/
(new CatalogsFunctions($this->dbDriver))->storeCatalogs($keysAndValues['catalogs'], $collection->context->core['baseUrl'], $collection->user->profile['id'], $collection, $result['id'], false);
(new CatalogsFunctions($this->dbDriver))->storeCatalogs($keysAndValues['catalogs'], $collection->context, $collection->user->profile['id'], $collection, $result['id'], false);

/*
* Commit everything - rollback if one of the inserts failed
Expand Down Expand Up @@ -520,7 +520,7 @@ public function updateFeature($feature, $collection, $newFeatureArray)
$feature->id
)
);
(new CatalogsFunctions($this->dbDriver))->storeCatalogs($keysAndValues['catalogs'], $collection->context->core['baseUrl'], $collection->user->profile['id'], $collection, $feature->id, false);
(new CatalogsFunctions($this->dbDriver))->storeCatalogs($keysAndValues['catalogs'], $collection->context, $collection->user->profile['id'], $collection, $feature->id, false);

/*
* Commit
Expand Down
33 changes: 19 additions & 14 deletions docs/COLLECTIONS_AND_CATALOGS.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,34 +40,35 @@ Then get the feature :

*Note: Any user with the "createFeature" right can insert a feature to a collection he owns ([see rights](./USERS.md))*

curl -X POST -d@examples/features/dummySargasse.json "http://admin:admin@localhost:5252/collections/S2/items"

## Catalogs

### Create a dummy collection

# Create a dummy collection
curl -X POST -d@examples/collections/DummyCollection.json "http://admin:admin@localhost:5252/collections"

### Create a catalog

# Create a catalog - user with the "createCatalog" right can create a catalog
curl -X POST -d@examples/catalogs/dummyCatalog.json "http://admin:admin@localhost:5252/catalogs"

### Create a catalog with existing childs

# This will raise an error because catalog' childs does not exist. They must be created first
curl -X POST -d@examples/catalogs/dummyCatalogWithChilds.json "http://admin:admin@localhost:5252/catalogs"

# Good way: ingest childs then parent
curl -X POST -d@examples/catalogs/dummyCatalogChild1.json "http://admin:admin@localhost:5252/catalogs"
curl -X POST -d@examples/catalogs/dummyCatalogChild2.json "http://admin:admin@localhost:5252/catalogs"
curl -X POST -d@examples/catalogs/dummyCatalogWithChilds.json "http://admin:admin@localhost:5252/catalogs"

### Create a catalog under an existing catalog

# The catalog dummyCatalogChild1 is posted under /catalogs/dummyCatalog
curl -X POST -d@examples/catalogs/dummyCatalogChild1.json "http://admin:admin@localhost:5252/catalogs/dummyCatalog"
**[IMPORTANT]** You cannot create a catalog with childs in links because a child cannot exist before its parent. The good way
is to create an empty catalog then add its childs through the POST API
# This will raise an error because the catalog references childs that do not exist.
curl -X POST -d@examples/catalogs/dummyCatalogWithChilds_invalid.json "http://admin:admin@localhost:5252/catalogs"

# Good way: ingest parent without childs then add childs under parent
curl -X POST -d@examples/catalogs/dummyCatalogWithChilds_valid.json "http://admin:admin@localhost:5252/catalogs"
curl -X POST -d@examples/catalogs/dummyCatalogChild1.json "http://admin:admin@localhost:5252/catalogs/dummyCatalogWithChilds"
curl -X POST -d@examples/catalogs/dummyCatalogChild2.json "http://admin:admin@localhost:5252/catalogs/dummyCatalogWithChilds"
## Create a catalog under an existing catalog that cycle on itself

# The catalog dummyCatalogCycling is posted under /catalogs/dummyCatalogChild1 but reference one of this
# parent as a child which is forbiden
curl -X POST -d@examples/catalogs/dummyCatalogChild1.json "http://admin:admin@localhost:5252/catalogs/dummyCatalog"
curl -X POST -d@examples/catalogs/dummyCatalogCycling.json "http://admin:admin@localhost:5252/catalogs/dummyCatalog/dummyCatalogChild1"

### Create a catalog with item
Expand All @@ -76,6 +77,10 @@ Then get the feature :
# It references :
# * a local item that is added to catalog_feature table
# * an external item that is kept as referenced within the links column in catalog table

# First POST the item because it should exist before referencing it within the catalog
curl -X POST -d@examples/features/dummySargasse.json "http://admin:admin@localhost:5252/collections/DummyCollection/items"

curl -X POST -d@examples/catalogs/dummyCatalogWithItem.json "http://admin:admin@localhost:5252/catalogs/dummyCatalog/dummyCatalogChild1"

### Update a catalog
Expand Down
21 changes: 0 additions & 21 deletions examples/catalogs/dummyCatalogWithChilds.json

This file was deleted.

21 changes: 21 additions & 0 deletions examples/catalogs/dummyCatalogWithChilds_invalid.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"id": "dummyCatalogWithChilds",
"title": "Dummy Catalog with childs",
"type": "Catalog",
"description":"This catalog has childs - this is invalid because they must exist before and a child cannot exist before its parent",
"stac_version":"1.0.0",
"links":[
{
"rel":"child",
"href":"http://127.0.0.1:5252/catalogs/dummyCatalogWithChilds/dummyCatalogChild1"
},
{
"rel":"child",
"href":"http://127.0.0.1:5252/catalogs/dummyCatalogWithChilds/dummyCatalogChild2"
},
{
"rel":"child",
"href":"http://127.0.0.1:5252/collections/DummyCollection"
}
]
}
8 changes: 8 additions & 0 deletions examples/catalogs/dummyCatalogWithChilds_valid.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"id": "dummyCatalogWithChilds",
"title": "Dummy Catalog with childs",
"type": "Catalog",
"description":"This catalog has no childs - they will be added later on",
"stac_version":"1.0.0",
"links":[]
}
Loading

0 comments on commit fca911f

Please sign in to comment.