Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Listing.organisation join #142

Open
wants to merge 4 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/Db/Listing.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class Listing extends Entity implements JsonSerializable
protected ?DateTime $lastSync = null;
protected ?bool $default = false;
protected ?bool $available = false;
protected ?string $organisation = null;

public function __construct() {
$this->addType(fieldName: 'title', type: 'string');
Expand All @@ -34,6 +35,7 @@ public function __construct() {
$this->addType(fieldName: 'lastSync', type: 'datetime');
$this->addType(fieldName: 'default', type: 'boolean');
$this->addType(fieldName: 'available', type: 'boolean');
$this->addType(fieldName: 'organisation', type: 'string');
}

public function getJsonFields(): array
Expand Down Expand Up @@ -81,6 +83,7 @@ public function jsonSerialize(): array
'lastSync' => $this->lastSync->format('c'),
'default' => $this->default,
'available' => $this->available,
'organisation'=> $this->organisation,
];

$jsonFields = $this->getJsonFields();
Expand Down
152 changes: 127 additions & 25 deletions lib/Db/ListingMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace OCA\OpenCatalogi\Db;

use OCA\OpenCatalogi\Db\Listing;
use OCA\OpenCatalogi\Db\Organisation;
use OCP\AppFramework\Db\Entity;
use OCP\AppFramework\Db\QBMapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
Expand All @@ -19,43 +20,144 @@ public function find(int $id): Listing
{
$qb = $this->db->getQueryBuilder();

$qb->select('*')
->from('listings')
->where(
$qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT))
);
$qb->select(
'l.*',
'o.id AS organisation_id',
'o.title AS organisation_title',
'o.summary AS organisation_summary',
'o.description AS organisation_description',
'o.image AS organisation_image',
'o.oin AS organisation_oin',
'o.tooi AS organisation_tooi',
'o.rsin AS organisation_rsin',
'o.pki AS organisation_pki'
)
->from('listings', 'l')
->leftJoin('l', 'organizations', 'o', 'l.organisation = o.id')
->where(
$qb->expr()->eq('l.id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT))
);

return $this->findEntityCustom(query: $qb);
}

return $this->findEntity(query: $qb);
/**
* Returns an db result and throws exceptions when there are more or less
* results CUSTOM FOR JOINS
*
* @param IQueryBuilder $query
* @return Entity the entity
* @psalm-return T the entity
* @throws Exception
* @throws MultipleObjectsReturnedException if more than one item exist
* @throws DoesNotExistException if the item does not exist
* @since 14.0.0
*/
protected function findEntityCustom(IQueryBuilder $query): Entity {
return $this->mapRowToEntityCustom($this->findOneQuery($query));
}

public function findAll(?int $limit = null, ?int $offset = null, ?array $filters = [], ?array $searchConditions = [], ?array $searchParams = []): array
{
$qb = $this->db->getQueryBuilder();
/**
* CUSTOM FOR JOINS
*/
protected function mapRowToEntityCustom(array $row): Entity {
unset($row['DOCTRINE_ROWNUM']); // remove doctrine/dbal helper column

// Map the Organisation fields to a sub-array
$organisationData = [
'id' => $row['organisation_id'] ?? null,
'title' => $row['organisation_title'] ?? null,
'summary' => $row['organisation_summary'] ?? null,
'description' => $row['organisation_description'] ?? null,
'image' => $row['organisation_image'] ?? null,
'oin' => $row['organisation_oin'] ?? null,
'tooi' => $row['organisation_tooi'] ?? null,
'rsin' => $row['organisation_rsin'] ?? null,
'pki' => $row['organisation_pki'] ?? null,
];

$organisationIsEmpty = true;
foreach ($organisationData as $key => $value) {
if ($value !== null) {
$organisationIsEmpty = false;
}

$qb->select('*')
->from('listings')
->setMaxResults($limit)
->setFirstResult($offset);

foreach($filters as $filter => $value) {
if ($value === 'IS NOT NULL') {
$qb->andWhere($qb->expr()->isNotNull($filter));
} elseif ($value === 'IS NULL') {
$qb->andWhere($qb->expr()->isNull($filter));
} else {
$qb->andWhere($qb->expr()->eq($filter, $qb->createNamedParameter($value)));
}
if (array_key_exists("organisation_$key", $row) === true) {
unset($row["organisation_$key"]);
}
}

$row['organisation'] = $organisationIsEmpty === true ? null : json_encode(Organisation::fromRow($organisationData)->jsonSerialize());

return \call_user_func($this->entityClass .'::fromRow', $row);
}

/**
* Runs a sql query and returns an array of entities CUSTOM FOR JOINS
*
* @param IQueryBuilder $query
* @return Entity[] all fetched entities
* @psalm-return T[] all fetched entities
* @throws Exception
* @since 14.0.0
*/
protected function findEntitiesCustom(IQueryBuilder $query): array {
$result = $query->executeQuery();
try {
$entities = [];
while ($row = $result->fetch()) {
$entities[] = $this->mapRowToEntityCustom($row);
}
return $entities;
} finally {
$result->closeCursor();
}
}

public function findAll(?int $limit = null, ?int $offset = null, ?array $filters = [], ?array $searchConditions = [], ?array $searchParams = []): array
{
$qb = $this->db->getQueryBuilder();

$qb->select(
'l.*',
'o.id AS organisation_id',
'o.title AS organisation_title',
'o.summary AS organisation_summary',
'o.description AS organisation_description',
'o.image AS organisation_image',
'o.oin AS organisation_oin',
'o.tooi AS organisation_tooi',
'o.rsin AS organisation_rsin',
'o.pki AS organisation_pki'
)
->from('listings', 'l')
->leftJoin('l', 'organizations', 'o', 'l.organisation = o.id')
->setMaxResults($limit)
->setFirstResult($offset);


// Apply filters
foreach ($filters as $filter => $value) {
if ($value === 'IS NOT NULL') {
$qb->andWhere($qb->expr()->isNotNull($filter));
} elseif ($value === 'IS NULL') {
$qb->andWhere($qb->expr()->isNull($filter));
} else {
$qb->andWhere($qb->expr()->eq($filter, $qb->createNamedParameter($value)));
}
}

// Apply search conditions
if (!empty($searchConditions)) {
$qb->andWhere('(' . implode(' OR ', $searchConditions) . ')');
foreach ($searchParams as $param => $value) {
$qb->setParameter($param, $value);
}
}

return $this->findEntities(query: $qb);
}

// Use the existing findEntities method to fetch and map the results
return $this->findEntitiesCustom($qb);
}

public function createFromArray(array $object): Listing
{
Expand Down
72 changes: 72 additions & 0 deletions lib/Migration/Version6Date20240809120147.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\OpenCatalogi\Migration;

use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\DB\Types;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;

/**
* FIXME Auto-generated migration step: Please modify to your needs!
*/
class Version6Date20240809120147 extends SimpleMigrationStep {

/**
* @param IOutput $output
* @param Closure(): ISchemaWrapper $schemaClosure
* @param array $options
*/
public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
}

/**
* @param IOutput $output
* @param Closure(): ISchemaWrapper $schemaClosure
* @param array $options
* @return null|ISchemaWrapper
*/
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
/**
* @var ISchemaWrapper $schema
*/
$schema = $schemaClosure();

if($schema->hasTable(tableName: 'listings') === true) {
$table = $schema->getTable(tableName: 'listings');

if($table->hasColumn(name: 'organization') === true) {
$column = $table->dropColumn('organization');
}
if($table->hasColumn(name: 'organisation') === false) {
$table->addColumn(
name: 'organisation',
typeName: Types::STRING,
options: [
'notNull' => false,
'default' => null
]);
$output->info('organisation should be added to listing');
}

}

return $schema;
}

/**
* @param IOutput $output
* @param Closure(): ISchemaWrapper $schemaClosure
* @param array $options
*/
public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
}
}
4 changes: 3 additions & 1 deletion lib/Service/DirectoryService.php
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,9 @@ public function listCatalog (array $catalog): array

$listing = $this->getDirectoryEntry(catalogId: $catalogId);

$listing['title'] = $catalog['title'];
$listing['title'] = $catalog['title'];
$listing['organisation'] = $catalog['organisation'];
$listing['metaData'] = $catalog['metaData'];

if($this->config->hasKey($this->appName, 'mongoStorage') === false
|| $this->config->getValueString($this->appName, 'mongoStorage') !== '1'
Expand Down
20 changes: 20 additions & 0 deletions src/entities/listing/listing.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@ export const mockListingsData = (): TListing[] => [
lastSync: '2024-07-25T00:00:00Z',
default: true,
available: false,
organisation: { // full data
id: '1',
title: 'Decat',
summary: 'a short form summary',
description: 'a really really long description about this organisation',
oin: 'string',
tooi: 'string',
rsin: 'string',
pki: 'string',
},
},
{
id: '2',
Expand All @@ -31,6 +41,16 @@ export const mockListingsData = (): TListing[] => [
lastSync: '',
default: true,
available: false,
organisation: { // full data
id: '1',
title: 'Decat',
summary: 'a short form summary',
description: 'a really really long description about this organisation',
oin: 'string',
tooi: 'string',
rsin: 'string',
pki: 'string',
},
},
{
id: '1',
Expand Down
3 changes: 3 additions & 0 deletions src/entities/listing/listing.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Organisation, TOrganisation } from '../organisation'
import { TListing } from './listing.types'
import { SafeParseReturnType, z } from 'zod'

Expand All @@ -16,6 +17,7 @@ export class Listing implements TListing {
public lastSync: string | Date
public available: boolean
public default: boolean
public organisation: string|TOrganisation

constructor(data: TListing) {
this.hydrate(data)
Expand All @@ -36,6 +38,7 @@ export class Listing implements TListing {
this.lastSync = data.lastSync || ''
this.available = data.available || true
this.default = data.default || false
this.organisation = data.organisation || ''

}

Expand Down
3 changes: 3 additions & 0 deletions src/entities/listing/listing.types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { TOrganisation } from "../organisation"

export type TListing = {
id: string
catalogusId: string
Expand All @@ -12,4 +14,5 @@ export type TListing = {
lastSync: string | Date
available: boolean
default: boolean
organisation: string|TOrganisation
}
5 changes: 4 additions & 1 deletion src/store/modules/directory.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ export const useDirectoryStore = defineStore(
response.json().then(
(data) => {
this.listingList = data.results.map(
(listingItem) => new Listing(listingItem),
(listingItem) => {
listingItem.organisation = listingItem?.organisation ? JSON.parse(listingItem.organisation): null;
return new Listing(listingItem)
},
)
},
)
Expand Down
1 change: 1 addition & 0 deletions src/views/directory/DirectoryList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import { navigationStore, directoryStore } from '../../store/store.js'
:key="`${listing}${i}`"
:name="listing.name ?? listing.title"
:active="directoryStore.listingItem?.id === listing?.id"
:details="listing?.organisation?.title || 'Geen organisatie'"
@click="directoryStore.setListingItem(listing)">
<template #icon>
<LayersOutline :class="directoryStore.listingItem?.id === listing?.id && 'selectedIcon'"
Expand Down
Loading