11package org .opendevstack .component_catalog .server .services ;
22
3+ import org .opendevstack .component_catalog .server .services .catalog .CatalogServiceAdapter ;
34import org .opendevstack .component_catalog .server .services .catalog .InvalidCatalogEntityException ;
45import org .opendevstack .component_catalog .server .services .catalog .entity .CatalogItemEntityContext ;
56import org .opendevstack .component_catalog .server .services .exceptions .InvalidIdException ;
@@ -30,13 +31,15 @@ public class CatalogItemBySlugService {
3031
3132 private final CatalogsCollectionService catalogsCollectionService ;
3233 private final CatalogEntitiesService catalogEntitiesService ;
34+ private final CatalogServiceAdapter catalogServiceAdapter ;
35+ private final BitbucketService bitbucketService ;
3336
3437 /**
3538 * Resolves a {@link CatalogItemSlug} to the matching {@link CatalogItemEntityContext}.
3639 *
3740 * @param slug the parsed composite slug
3841 * @return an {@link Optional} with the matching context, or empty if not found
39- * @throws InvalidIdException if any catalog id is structurally invalid
42+ * @throws InvalidIdException if any catalog or item id is structurally invalid
4043 * @throws InvalidCatalogEntityException if a catalog entity cannot be parsed
4144 */
4245 public Optional <CatalogItemEntityContext > findByCatalogItemSlug (CatalogItemSlug slug )
@@ -59,27 +62,40 @@ public Optional<CatalogItemEntityContext> findByCatalogItemSlug(CatalogItemSlug
5962
6063 for (var target : targets ) {
6164 var catalogId = idEncode (target .getUrl ());
62- var itemContexts = catalogEntitiesService .getCatalogItemsEntities (catalogId );
63- var match = itemContexts .stream ()
64- .filter (ctx -> itemMatchesSlug (ctx , slug ))
65- .findFirst ();
66- if (match .isPresent ()) {
67- log .debug ("Resolved slug '{}' to item in catalog target '{}'" , slug , target .getUrl ());
68- return match ;
65+
66+ var catalogIdPathAt = catalogServiceAdapter .bitbucketPathAtFromId (catalogId );
67+
68+ // Optimisation: skip catalogs whose Bitbucket project key does not match the slug's project key,
69+ // avoiding unnecessary item loading. This relies on the assumption that catalog items live in the
70+ // same Bitbucket project as their catalog definition.
71+ if (CatalogItemSlug .normalise (catalogIdPathAt .getProjectKey ()).equals (slug .getProjectKey ())) {
72+ log .debug ("Catalog target '{}' matches slug project key '{}', checking items..." ,
73+ target .getUrl (), slug .getProjectKey ());
74+
75+ var slugCatalogEntity = catalogEntitiesService .getCatalogEntity (catalogId );
76+
77+ if (slugCatalogEntity .isPresent ()) {
78+ // A slug uniquely identifies one item. Multiple matches (e.g. the same repo referenced
79+ // in more than one catalog entry) are not expected; we return the first one found.
80+ for (var itemTarget : slugCatalogEntity .get ().getMetadata ().getSpec ().getTargets ()) {
81+ var itemPathAt = bitbucketService .pathAtBuilder ()
82+ .rawUrl (itemTarget .getUrl ())
83+ .build ();
84+ if (CatalogItemSlug .normalise (itemPathAt .getProjectKey ()).equals (slug .getProjectKey ())
85+ && itemPathAt .getRepoSlug ().equalsIgnoreCase (slug .getRepoName ())) {
86+ var itemId = idEncode (itemPathAt .getPathAt ());
87+ log .debug ("Resolved slug '{}' to item id '{}' in catalog target '{}'" , slug , itemId , target .getUrl ());
88+ return catalogEntitiesService .getCatalogItemEntity (itemId );
89+ }
90+ }
91+ }
92+
6993 }
7094 }
7195
7296 log .debug ("No catalog item matched slug '{}'" , slug );
7397 return Optional .empty ();
7498 }
7599
76- // --- private helpers ---
77-
78- private boolean itemMatchesSlug (CatalogItemEntityContext ctx , CatalogItemSlug slug ) {
79- var normalisedProjectKey = CatalogItemSlug .normalise (ctx .getRepoCatalogItemPathAt ().getProjectKey ());
80- return normalisedProjectKey .equals (slug .getProjectKey ())
81- && slug .getRepoName ().equalsIgnoreCase (ctx .getRepoCatalogItemPathAt ().getRepoSlug ());
82- }
83-
84100}
85101
0 commit comments