Skip to content
Merged
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
109 changes: 101 additions & 8 deletions lib/Service/SoftwareCatalogueService.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
use OCP\IUserSession;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
use RuntimeException;

/**
* Service for handling software catalog operations.
Expand Down Expand Up @@ -140,6 +141,31 @@ private function getOrganisationService(): ?\OCA\OpenRegister\Service\Organisati
}
}//end getOrganisationService()

/**
* Gets the OrganisationMapper instance
*
* OpenRegister is an optional capability for this service (ADR-083 rule 1),
* so the mapper is reached the same way the two services above are: the app
* is asked whether OpenRegister is available, and a failed resolution
* degrades to null with a logged error rather than escaping as a raw
* container exception. Callers must treat null as "OpenRegister is not
* available" and take their own not-available branch.
*
* @return \OCA\OpenRegister\Db\OrganisationMapper|null
*/
private function getOrganisationMapper(): ?\OCA\OpenRegister\Db\OrganisationMapper {
if ($this->_appManager->isEnabledForUser(appId: 'openregister') === false) {
return null;
}

try {
return $this->_container->get('OCA\\OpenRegister\\Db\\OrganisationMapper');
} catch (\Exception $e) {
$this->_logger->error('Failed to get OrganisationMapper: ' . $e->getMessage());
return null;
}
}//end getOrganisationMapper()

/**
* Processes a contactpersoon object to create an inactive user
*
Expand Down Expand Up @@ -253,7 +279,21 @@ public function processContactpersoon(object $contactPersonObject, bool $isUpdat
);

try {
$organisationMapper = $this->_container->get('OCA\\OpenRegister\\Db\\OrganisationMapper');
$organisationMapper = $this->getOrganisationMapper();
if ($organisationMapper === null) {
$this->_logger->warning(
'SoftwareCatalogueService: OpenRegister OrganisationMapper not available, skipping organization membership',
[
'objectId' => $objectId,
'username' => $username,
'organization' => $organization,
]
);
// Nothing follows this block but `return $result;`, so this is the
// same exit the method would take after skipping the membership work.
return $result;
}

$organisation = $organisationMapper->findByUuid($organization);

if (empty($organisation) === false) {
Expand Down Expand Up @@ -534,7 +574,14 @@ public function handleNewOrganization(object $organizationObject): void {
return;
}

$organisationMapper = $this->_container->get('OCA\\OpenRegister\\Db\\OrganisationMapper');
$organisationMapper = $this->getOrganisationMapper();
if ($organisationMapper === null) {
$this->_logger->warning(
'SoftwareCatalogueService: OpenRegister OrganisationMapper not available, skipping contact person membership'
);
return;
}

$organisation = $organisationMapper->findByUuid($organizationUuid);

if (empty($organisation) === false) {
Expand Down Expand Up @@ -1286,7 +1333,14 @@ public function syncOrganizationWithOpenRegister(object $organizationObject): bo
$this->_logger->info('SoftwareCatalogueService: SYNC_STEP_5 - Checking if organization exists in OpenRegister');
try {
$this->_logger->info('SoftwareCatalogueService: SYNC_STEP_5A - Getting OrganisationMapper for lookup');
$organisationMapper = $this->_container->get('OCA\\OpenRegister\\Db\\OrganisationMapper');
$organisationMapper = $this->getOrganisationMapper();
if ($organisationMapper === null) {
$this->_logger->error(
'SoftwareCatalogueService: OpenRegister OrganisationMapper not available, cannot sync organization'
);
return false;
}

$this->_logger->info(
'SoftwareCatalogueService: SYNC_STEP_5B - Calling findByUuid',
[
Expand Down Expand Up @@ -1496,7 +1550,14 @@ private function createOrganisationInOpenRegisterInternal(

// Create organization directly via mapper to avoid user context requirements.
$this->_logger->info('SoftwareCatalogueService: STEP 3C - Getting OrganisationMapper from container');
$organisationMapper = $this->_container->get('OCA\\OpenRegister\\Db\\OrganisationMapper');
$organisationMapper = $this->getOrganisationMapper();
if ($organisationMapper === null) {
// This method's return type is non-nullable and its caller already
// holds an OpenRegister OrganisationService, so "unavailable" here
// escapes exactly as the raw container exception used to.
throw new RuntimeException('OpenRegister OrganisationMapper is not available');
}

$this->_logger->info(
'SoftwareCatalogueService: STEP 3D - OrganisationMapper retrieved',
[
Expand Down Expand Up @@ -1615,7 +1676,12 @@ private function createOrganisationInOpenRegisterInternal(

// Create organization directly via mapper to avoid service issues.
$this->_logger->info('SoftwareCatalogueService: STEP 4C - Getting OrganisationMapper from container');
$organisationMapper = $this->_container->get('OCA\\OpenRegister\\Db\\OrganisationMapper');
$organisationMapper = $this->getOrganisationMapper();
if ($organisationMapper === null) {
// Non-nullable return type, same reasoning as the anonymous branch above.
throw new RuntimeException('OpenRegister OrganisationMapper is not available');
}

$this->_logger->info(
'SoftwareCatalogueService: STEP 4D - OrganisationMapper retrieved',
[
Expand Down Expand Up @@ -1742,7 +1808,13 @@ private function updateOrganisationInOpenRegister(
// Note: OpenRegister Organisation entity doesn't have status or type fields.
// These are managed in the SoftwareCatalog object, not in the OpenRegister organisation.
// Save the updated organization.
$organisationMapper = $this->_container->get('OCA\\OpenRegister\\Db\\OrganisationMapper');
$organisationMapper = $this->getOrganisationMapper();
if ($organisationMapper === null) {
// Non-nullable return type; the caller already holds an OpenRegister
// OrganisationService, so this escapes as the container lookup used to.
throw new RuntimeException('OpenRegister OrganisationMapper is not available');
}

$updatedOrganisation = $organisationMapper->save($existingOrganisation);

$this->_logger->info(
Expand Down Expand Up @@ -3177,7 +3249,17 @@ public function syncContactPersonUsernamesWithOrganization(string $organizationU
}

// Get the organization entity.
$organisationMapper = $this->_container->get('OCA\\OpenRegister\\Db\\OrganisationMapper');
$organisationMapper = $this->getOrganisationMapper();
if ($organisationMapper === null) {
$this->_logger->error(
'SoftwareCatalogueService: OpenRegister OrganisationMapper not available for synchronization',
[
'organizationUuid' => $organizationUuid,
]
);
return;
}

$organisation = $organisationMapper->findByUuid($organizationUuid);

if ($organisation === null) {
Expand Down Expand Up @@ -3290,7 +3372,18 @@ private function ensureContactPersonInOrganization(object $contactPersonObject):

try {
// Get the organization entity.
$organisationMapper = $this->_container->get('OCA\\OpenRegister\\Db\\OrganisationMapper');
$organisationMapper = $this->getOrganisationMapper();
if ($organisationMapper === null) {
$this->_logger->error(
'SoftwareCatalogueService: OpenRegister OrganisationMapper not available for contact person',
[
'contactPersonId' => $contactPersonObject->getId(),
'organization' => $organization,
]
);
return;
}

$organisation = $organisationMapper->findByUuid($organization);

if ($organisation === null) {
Expand Down
18 changes: 9 additions & 9 deletions lib/Settings/softwarecatalogus_register.json
Original file line number Diff line number Diff line change
Expand Up @@ -2656,7 +2656,7 @@
"slug": "usage",
"title": "Usage",
"description": "Het gebruik van applicaties, diensten en koppelingen door afnemers",
"version": "1.4.0",
"version": "1.4.1",
"omschrijving": "",
"icon": "Gauge",
"x-openregister-notifications": {
Expand Down Expand Up @@ -2863,7 +2863,7 @@
"status": {
"description": "Selecteer de status van de versie in uw landschap (default status \"in productie\")",
"type": "string",
"default": "In productie",
"default": "In production",
"required": true,
"visible": true,
"order": 17,
Expand Down Expand Up @@ -3550,7 +3550,7 @@
"slug": "connection",
"title": "Connection",
"description": "Schema voor koppelingen tussen applicaties en systemen. ApplicatieB is voor koppelingen met andere applicaties. BuitengemeentelijkVoorziening is voor koppelingen met externe voorzieningen.",
"version": "0.3.0",
"version": "0.3.1",
"omschrijving": "",
"icon": "Link",
"required": [
Expand Down Expand Up @@ -3614,7 +3614,7 @@
"order": 3,
"facetable": false,
"title": "Status",
"default": "in gebruik",
"default": "in use",
"table": {
"default": true
},
Expand Down Expand Up @@ -3801,7 +3801,7 @@
"title": "Connection type",
"visible": false,
"hideOnForm": true,
"default": "{{ buitengemeentelijkVoorziening | ifFilled: extern, intern }}",
"default": "{{ buitengemeentelijkVoorziening | ifFilled: external, internal }}",
"defaultBehavior": "always",
"enum": [
"external",
Expand Down Expand Up @@ -6756,7 +6756,7 @@
},
"title": "Application",
"description": "Een applicatie is een softwarecomponent (applicatie of systeemsoftware)",
"version": "0.3.2",
"version": "0.3.3",
"omschrijving": "",
"icon": "Package",
"required": [
Expand Down Expand Up @@ -6975,7 +6975,7 @@
"order": 11,
"facetable": false,
"title": "Type",
"default": "Applicatie",
"default": "Application",
"enum": [
"Application",
"System software"
Expand Down Expand Up @@ -7641,7 +7641,7 @@
},
"title": "Application version",
"description": "Schema voor applicatieversies",
"version": "0.1.3",
"version": "0.1.4",
"omschrijving": "",
"icon": "ViewModule",
"required": [],
Expand Down Expand Up @@ -7696,7 +7696,7 @@
"end of support",
"withdrawn"
],
"default": "in gebruik"
"default": "in use"
},
"dateInDevelopment": {
"description": "Startdatum van de ontwikkelingsfase",
Expand Down
5 changes: 5 additions & 0 deletions src/components/sbom/SbomComponentsPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,11 @@ export default {
* producer/consumer pair is a silent break; `tests/vitest/
* sbomProvenanceLabel.spec.js` fails when the pair drifts again.
*
* The provenance line was the VISIBLE half. `parentModuleId` reads the
* same empty bag, so the module-scoped vulnerability heuristic was
* scoped to '' and matched nothing — silent, untested, and rendered as
* a legitimate "no matches" rather than as a fault.
*
* @return {object|null} The module version record.
* @spec openspec/specs/sbom-import/spec.md#requirement-moduleversie-records-sbom-import-provenance
*/
Expand Down
Loading
Loading