Skip to content

Commit

Permalink
Merge pull request #117 from ConductionNL/master
Browse files Browse the repository at this point in the history
Merge master to dev
  • Loading branch information
rjzondervan authored Oct 21, 2024
2 parents a7445ac + 95a68df commit 58c9091
Show file tree
Hide file tree
Showing 8 changed files with 256 additions and 16 deletions.
2 changes: 1 addition & 1 deletion appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Create a [bug report](https://github.com/OpenCatalogi/.github/issues/new/choose)
Create a [feature request](https://github.com/OpenCatalogi/.github/issues/new/choose)
]]></description>
<version>0.6.18</version>
<version>0.6.27</version>
<licence>agpl</licence>
<author mail="[email protected]" homepage="https://www.conduction.nl/">Conduction</author>
<author mail="[email protected]" homepage="https://acato.nl/">Acato</author>
Expand Down
8 changes: 5 additions & 3 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@
'listings' => ['url' => '/api/listings'],
],
'routes' => [
// Custom
// Directory
['name' => 'listing#synchronise', 'url' => '/api/listings/synchronise/{id?}', 'verb' => 'POST'],
['name' => 'directory#index', 'url' => '/api/directory', 'verb' => 'GET'],
['name' => 'directory#show', 'url' => '/api/directory/{id}', 'verb' => 'GET'],
['name' => 'directory#show', 'url' => '/api/directory/{id}', 'verb' => 'GET', 'requirements' => ['path' => '.+']],
['name' => 'directory#update', 'url' => '/api/directory', 'verb' => 'POST'],
['name' => 'directory#publicationType', 'url' => '/api/directory/publication_type/{id}', 'verb' => 'GET'],
['name' => 'directory#publicationType', 'url' => '/api/directory/publication_types/{id}', 'verb' => 'GET'], // Should be in directory becouse its public
// Publication
['name' => 'publication_types#synchronise', 'url' => '/api/publication_types/synchronise', 'verb' => 'POST'],
// Dashboard
['name' => 'dashboard#index', 'url' => '/index', 'verb' => 'GET'],
['name' => 'dashboard#page', 'url' => '/', 'verb' => 'GET'],
Expand Down
3 changes: 3 additions & 0 deletions docs/installatie/instructies.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,6 @@ Let op! Je hebt hier een admin-account voor nodig. Dit werkt mogelijk niet met t
<figure><img src="../.gitbook/assets/image (4).png" alt=""><figcaption></figcaption></figure>

Met deze stappen ben je klaar om Nextcloud en OpenCatalogi te gebruiken zonder dat je een eigen server hoeft op te zetten. Als je nog vragen hebt of hulp nodig hebt, raadpleeg dan de [officiële Nextcloud documentatie](https://docs.nextcloud.com/) of stuur een mail naar [email protected].

** OpenCatalogi Bijwerken**
![alt text](upgrade.png)
Binary file added docs/installatie/upgrade.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 4 additions & 3 deletions lib/Controller/DirectoryController.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,22 +106,23 @@ public function show(string|int $id): JSONResponse
}

/**
* Get a specific publication type
* Get a specific publication type, used by external applications to synchronyse
*
* @NoAdminRequired
* @PublicPage
* @NoCSRFRequired
* @param string|int $id The ID of the publication type to retrieve
* @return JSONResponse The JSON response containing the publication type details
*/
public function publicationType(string|int $id): JSONResponse
{
try {
$publicationType = $this->objectService->get('publicationType', $id);
$publicationType = $this->objectService->getObject('publicationType', $id);
return new JSONResponse($publicationType);
} catch (DoesNotExistException $e) {
return new JSONResponse(['error' => 'Publication type not found'], 404);
} catch (\Exception $e) {
return new JSONResponse(['error' => 'An error occurred while retrieving the publication type'], 500);
}
}

}
60 changes: 59 additions & 1 deletion lib/Controller/PublicationTypesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use OCA\OpenCatalogi\Db\PublicationTypeMapper;
use OCA\OpenCatalogi\Service\ObjectService;
use OCA\OpenCatalogi\Service\DirectoryService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Http\JSONResponse;
Expand All @@ -25,13 +26,15 @@ class PublicationTypesController extends Controller
* @param IAppConfig $config The app configuration
* @param PublicationTypeMapper $publicationTypeMapper The publication type mapper
* @param ObjectService $objectService The object service
* @param DirectoryService $directoryService The directory service
*/
public function __construct(
$appName,
IRequest $request,
private readonly IAppConfig $config,
private readonly PublicationTypeMapper $publicationTypeMapper,
private readonly ObjectService $objectService
private readonly ObjectService $objectService,
private readonly DirectoryService $directoryService
)
{
parent::__construct($appName, $request);
Expand Down Expand Up @@ -157,4 +160,59 @@ public function destroy(string|int $id): JSONResponse
// Return the result as a JSON response
return new JSONResponse(['success' => $result], $result === true ? '200' : '404');
}

/**
* Synchronize or delete a publication type based on listing status
*
* @PublicPage
* @NoCSRFRequired
* @return JSONResponse The JSON response containing the result of the operation
*/
public function synchronise(): JSONResponse
{
// Get the source and listed parameters from the request
$source = $this->request->getParam('source');
$listed = $this->request->getParam('listed', false);

// Check if the source parameter is provided
if (empty($source)) {
return new JSONResponse(['error' => 'source parameter is required'], 400);
}

try {
if ($listed) {
// If listed is true, synchronize the publication type
$syncedPublicationType = $this->directoryService->syncPublicationType($source);
return new JSONResponse($syncedPublicationType);
} else {
// If listed is false, attempt to delete the publication type
// Get all publication types
$allPublicationTypes = $this->objectService->getObjects(
objectType: 'publicationType',
);

// Filter publication types to only include those with a matching source
$publicationTypes = array_filter($allPublicationTypes, function($publicationType) use ($source) {
// Check if the publication type has a 'source' property and if it matches the given source
return isset($publicationType['source']) && $publicationType['source'] === $source;
});

// Check the number of publication types found
if (!empty($publicationTypes)) {
$result = true;
foreach ($publicationTypes as $publicationType) {
$deleteResult = $this->objectService->deleteObject('publicationType', $publicationType['id']);
$result = $result && $deleteResult;
}
return new JSONResponse(['success' => $result], $result ? 200 : 500);
} else {
// If no publication types are found, return an error
return new JSONResponse(['error' => 'Publication type not found'], 404);
}
}
} catch (\Exception $e) {
// If an exception occurs, return an error response
return new JSONResponse(['error' => 'An error occurred: ' . $e->getMessage()], 500);
}
}
}
143 changes: 141 additions & 2 deletions lib/Service/DirectoryService.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ class DirectoryService
/** @var Client The HTTP client for making requests */
private Client $client;

/** @var array The list of external publication types that are used by this instance */
private array $externalPublicationTypes = [];

/**
* Constructor for DirectoryService
*
Expand Down Expand Up @@ -103,6 +106,22 @@ public function updateExternalDirectory(string $directoryUrl): int
}
}

/**
* Get the list of external publication types that are used by this instance
*
* @return array The list of external publication types
*/
private function getExternalPublicationTypes(): array
{
if (empty($this->externalPublicationTypes)) {
$result = $this->objectService->getObjects('publicationType');
$this->externalPublicationTypes = array_filter($result, function($pt) {
return !empty($pt['source']);
});
}
return $this->externalPublicationTypes;
}

/**
* Convert a listing object or array to a directory array
*
Expand All @@ -124,6 +143,35 @@ private function getDirectoryFromListing(Listing|array $listing): array
// $listing['uuid'], //@todo this breaks stuff when trying to find and update a listing
$listing['hash']);

// Process publication types
if (isset($listing['publicationTypes']) && is_array($listing['publicationTypes'])) {
foreach ($listing['publicationTypes'] as &$publicationType) {
// Convert publicationType to array if it's an object
if ($publicationType instanceof \JsonSerializable) {
$publicationType = $publicationType->jsonSerialize();
}

// set listed and owner to false by default
$publicationType['listed'] = false;
$publicationType['owner'] = false;

// check if this publication type is used by this instance
if (isset($publicationType['source'])) {
// Get all external publication types used by this instance
$externalPublicationTypes = $this->getExternalPublicationTypes();

// Filter external types to find matches with the current publication type
$matchingTypes = array_filter($externalPublicationTypes, function($externalType) use ($publicationType) {
// Check if the external type has a source and if it matches the current publication type's source
return isset($externalType['source']) && $externalType['source'] === $publicationType['source'];
});

// Set 'listed' to true if there are any matching types, false otherwise
$publicationType['listed'] = !empty($matchingTypes);
}
}
}

// TODO: This should be mapped to the stoplight documentation
return $listing;
}
Expand Down Expand Up @@ -158,6 +206,21 @@ private function getDirectoryFromCatalog(Catalog|array $catalog): array
$catalog['search'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute("opencatalogi.search.index"));
$catalog['directory'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute("opencatalogi.directory.index"));

// Process publication types
if (isset($catalog['publicationTypes']) && is_array($catalog['publicationTypes'])) {
foreach ($catalog['publicationTypes'] as &$publicationType) {
// Convert publicationType to array if it's an object
if ($publicationType instanceof \JsonSerializable) {
$publicationType = $publicationType->jsonSerialize();
}
$publicationType['listed'] = true;
$publicationType['owner'] = true;
if (!isset($publicationType['source']) || empty($publicationType['source'])) {
$publicationType['source'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute("opencatalogi.directory.publicationType", ['id' => $publicationType['id']]));
}
}
}

// TODO: This should be mapped to the stoplight documentation
return $catalog;
}
Expand All @@ -171,7 +234,7 @@ private function getDirectoryFromCatalog(Catalog|array $catalog): array
private function getListings(): array
{
// Get all the listings
$listings = $this->objectService->getObjects(objectType: 'listing', extend: ['publicationTypes','organization']);
$listings = $this->objectService->getObjects(objectType: 'listing');
$listings = array_map([$this, 'getDirectoryFromListing'], $listings);

// TODO: Define when a listed item should not be shown (e.g. when secret or trusted is true), this is a product decision
Expand Down Expand Up @@ -200,7 +263,7 @@ public function getDirectories(): array
// TODO: Define when a listed item should not be shown (e.g. when secret or trusted is true), this is a product decision

// Get all the catalogi
$catalogi = $this->objectService->getObjects(objectType: 'catalog', extend: ['publicationTypes','organization']);
$catalogi = $this->objectService->getObjects(objectType: 'catalog', extend: ['publicationTypes', 'organization']);
$catalogi = array_map([$this, 'getDirectoryFromCatalog'], $catalogi);

// Filter out the catalogi that are not listed
Expand Down Expand Up @@ -374,4 +437,80 @@ public function synchronise(?string $id = null): array

return $object;
}

/**
* Copy or update a publication type from an external URL
*
* @param string $url The URL of the publication type to copy or update
* @return array The copied or updated publication type
* @throws GuzzleException
* @throws DoesNotExistException
* @throws MultipleObjectsReturnedException
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws \InvalidArgumentException If the URL is invalid
*/
public function syncPublicationType(string $url): array
{
// Validate the URL
if (!filter_var($url, FILTER_VALIDATE_URL)) {
throw new \InvalidArgumentException('Invalid URL provided');
}

// Fetch the publication type data from the external URL
try {
$response = $this->client->get($url);
} catch (GuzzleException $e) {
throw new \InvalidArgumentException('Unable to fetch data from the provided URL: ' . $e->getMessage());
}

$publicationType = json_decode($response->getBody()->getContents(), true);

if (json_last_error() !== JSON_ERROR_NONE) {
throw new \InvalidArgumentException('Invalid JSON data received from the URL');
}

// Set the source to the URL
$publicationType['source'] = $url;

// Prevent against malicious input
unset($publicationType['id']);
unset($publicationType['uuid']);

// Check if a publication type with the same name already exists
/*
$existingPublicationType = $this->objectService->getObjects(
objectType: 'publicationType',
limit: 1,
filters: [
['source' => $url]
]
);
*/

// TODO: THis is a hacky workaround for failing filters: PRIORITY: High
$existingPublicationTypes = $this->objectService->getObjects(
objectType: 'publicationType',
);
// Filter publication types to only include those with a matching source
$existingPublicationTypes = array_filter($existingPublicationTypes, function($publicationType) use ($source) {
// Check if the publication type has a 'source' property and if it matches the given source
return isset($publicationType['source']) && $publicationType['source'] === $source;
});


if (!empty($existingPublicationTypes)) {
// Update existing publication types
$updatedPublicationTypes = [];
foreach ($existingPublicationTypes as $existingType) {
$updatedType = $this->objectService->updateObject('publicationType', $existingType['id'], $publicationType);
$updatedPublicationTypes[] = $updatedType->jsonSerialize();
}
return $updatedPublicationTypes;
} else {
// Save the new publication type
$newPublicationType = $this->objectService->saveObject('publicationType', $publicationType);
return [$newPublicationType->jsonSerialize()];
}
}
}
Loading

0 comments on commit 58c9091

Please sign in to comment.