From 146a0fb8b0e90f9196e569152f649baf60d6cc8f Mon Sep 17 00:00:00 2001
From: Joaquín Reñé <jrene@curisit.net>
Date: Tue, 07 Oct 2025 14:52:57 +0000
Subject: [PATCH] #4410 - Comments on classes

---
 securis/src/main/java/net/curisit/securis/services/PackResource.java |  407 +++++++++++++++++++++++++++++++++++++---------------------
 1 files changed, 260 insertions(+), 147 deletions(-)

diff --git a/securis/src/main/java/net/curisit/securis/services/PackResource.java b/securis/src/main/java/net/curisit/securis/services/PackResource.java
index 4623314..85b784f 100644
--- a/securis/src/main/java/net/curisit/securis/services/PackResource.java
+++ b/securis/src/main/java/net/curisit/securis/services/PackResource.java
@@ -1,3 +1,6 @@
+/*
+ * Copyright @ 2013 CurisTEC, S.A.S. All Rights Reserved.
+ */
 package net.curisit.securis.services;
 
 import java.security.Principal;
@@ -53,31 +56,35 @@
 import net.curisit.securis.utils.TokenHelper;
 
 /**
- * Pack resource, this service will provide methods to create, modify and delete
- * packs
- * 
- * @author roberto <roberto.sanchez@curisit.net>
+ * PackResource
+ * <p>
+ * Manages Packs (group of licenses bound to an organization, application/type,
+ * and configuration/metadata). Provides list/filter, get, create, modify,
+ * state transitions (activate/hold/cancel) and deletion.
+ *
+ * @author JRA
+ * Last reviewed by JRA on Oct 5, 2025.
  */
 @Path("/pack")
 public class PackResource {
 
 	private static final Logger LOG = LogManager.getLogger(PackResource.class);
 
-	@Inject
-	TokenHelper tokenHelper;
+	@Inject TokenHelper tokenHelper;
+	@Inject MetadataHelper metadataHelper;
+	@Inject private LicenseHelper licenseHelper;
 
-	@Inject
-	MetadataHelper metadataHelper;
-
-	@Context
-	EntityManager em;
-
-	@Inject
-	private LicenseHelper licenseHelper;
+	@Context EntityManager em;
 
 	/**
-	 * 
-	 * @return the server version in format majorVersion.minorVersion
+	 * index
+	 * <p>
+	 * List packs with optional filters (organizationId, applicationId, licenseTypeId).
+	 * For non-admins, results are scoped by both apps and orgs from {@link BasicSecurityContext}.
+	 *
+	 * @param uriInfo supplies query parameters.
+	 * @param bsc     security scope/roles.
+	 * @return 200 OK with the list (possibly empty).
 	 */
 	@GET
 	@Path("/")
@@ -86,70 +93,25 @@
 	public Response index(@Context UriInfo uriInfo, @Context BasicSecurityContext bsc) {
 		LOG.info("Getting packs list ");
 		MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters();
-
-		// EntityManager em = emProvider.get();
 		em.clear();
 
 		TypedQuery<Pack> q = createQuery(queryParams, bsc);
 		if (q == null) {
 			return Response.ok().build();
 		}
-
 		List<Pack> list = q.getResultList();
-
 		return Response.ok(list).build();
 	}
 
-	private String generateWhereFromParams(boolean addWhere, MultivaluedMap<String, String> queryParams) {
-		List<String> conditions = new ArrayList<>();
-		if (queryParams.containsKey("organizationId")) {
-			conditions.add(String.format("pa.organization.id = %s", queryParams.getFirst("organizationId")));
-		}
-		if (queryParams.containsKey("applicationId")) {
-			conditions.add(String.format("pa.licenseType.application.id = %s", queryParams.getFirst("applicationId")));
-		}
-		if (queryParams.containsKey("licenseTypeId")) {
-			conditions.add(String.format("pa.licenseType.id = %s", queryParams.getFirst("licenseTypeId")));
-		}
-		String connector = addWhere ? " where " : " and ";
-		return (conditions.isEmpty() ? "" : connector) + String.join(" and ", conditions);
-	}
-
-	private TypedQuery<Pack> createQuery(MultivaluedMap<String, String> queryParams, BasicSecurityContext bsc) {
-		TypedQuery<Pack> q;
-		String hql = "SELECT pa FROM Pack pa";
-		if (bsc.isUserInRole(BasicSecurityContext.ROL_ADMIN)) {
-			hql += generateWhereFromParams(true, queryParams);
-			q = em.createQuery(hql, Pack.class);
-		} else {
-			if (bsc.getApplicationsIds() == null || bsc.getApplicationsIds().isEmpty()) {
-				return null;
-			}
-			if (bsc.getOrganizationsIds() == null || bsc.getOrganizationsIds().isEmpty()) {
-				hql += " where pa.licenseType.application.id in :list_ids_app ";
-			} else {
-				hql += " where pa.organization.id in :list_ids_org and pa.licenseType.application.id in :list_ids_app ";
-			}
-			hql += generateWhereFromParams(false, queryParams);
-			q = em.createQuery(hql, Pack.class);
-			if (hql.contains("list_ids_org")) {
-				q.setParameter("list_ids_org", bsc.getOrganizationsIds());
-			}
-			q.setParameter("list_ids_app", bsc.getApplicationsIds());
-			LOG.info("Getting packs from orgs: {} and apps: {}", bsc.getOrganizationsIds(), bsc.getApplicationsIds());
-		}
-
-		return q;
-	}
-
-	private Response generateErrorUnathorizedAccess(Pack pack, Principal user) {
-		LOG.error("Pack with id {} not accesible by user {}", pack, user);
-		return Response.status(Status.UNAUTHORIZED).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "Unathorized access to pack").build();
-	}
-
 	/**
-	 * 
-	 * @return the server version in format majorVersion.minorVersion
+	 * get
+	 * <p>
+	 * Fetch a pack by id. If the caller is an ADVANCE user, validates
+	 * the organization scope.
+	 *
+	 * @param packId pack id.
+	 * @param bsc    security context.
+	 * @return 200 OK with entity, or 404/401 accordingly.
 	 */
 	@GET
 	@Path("/{packId}")
@@ -162,7 +124,6 @@
 			return Response.status(Status.NOT_FOUND).build();
 		}
 
-		// EntityManager em = emProvider.get();
 		em.clear();
 		Pack pack = em.find(Pack.class, packId);
 		if (pack == null) {
@@ -175,6 +136,18 @@
 		return Response.ok(pack).build();
 	}
 
+	/**
+	 * create
+	 * <p>
+	 * Create a new pack. Validates code uniqueness, sets organization and
+	 * license type references, stamps creator and timestamps, and persists
+	 * metadata entries.
+	 *
+	 * @param pack payload.
+	 * @param bsc  security context (for createdBy).
+	 * @return 200 OK with created pack or 404 when references not found.
+	 * @throws SeCurisServiceException on duplicated code.
+	 */
 	@POST
 	@Path("/")
 	@Securable(roles = Rol.ADMIN | Rol.ADVANCE)
@@ -184,7 +157,6 @@
 	@EnsureTransaction
 	public Response create(Pack pack, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
 		LOG.info("Creating new pack");
-		// EntityManager em = emProvider.get();
 
 		if (checkIfCodeExists(pack.getCode(), em)) {
 			throw new SeCurisServiceException(ErrorCodes.INVALID_DATA, "The pack code is already used in an existing pack");
@@ -221,67 +193,16 @@
 	}
 
 	/**
-	 * Check if there is some pack with the same code
-	 * 
-	 * @param code
-	 *            Pack code
-	 * @param em
-	 *            DB session object
-	 * @return <code>true</code> if code is already used, <code>false</code>
-	 *         otherwise
+	 * modify
+	 * <p>
+	 * Update a pack basic fields and reconcile metadata (remove/merge/persist).
+	 * If metadata keys changed, marks dependent licenses metadata as obsolete via
+	 * {@link MetadataHelper#markObsoleteMetadata}.
+	 *
+	 * @param pack   payload values.
+	 * @param packId target id.
+	 * @return 200 OK with updated pack or 404 on ref errors.
 	 */
-	private boolean checkIfCodeExists(String code, EntityManager em) {
-		TypedQuery<Pack> query = em.createNamedQuery("pack-by-code", Pack.class);
-		query.setParameter("code", code);
-		int packs = query.getResultList().size();
-		return packs > 0;
-	}
-
-	/**
-	 * 
-	 * @return The next available code suffix in pack for license code
-	 * @throws SeCurisServiceException
-	 */
-	@GET
-	@Path("/{packId}/next_license_code")
-	@Securable(roles = Rol.ADMIN | Rol.ADVANCE)
-	@Produces({ MediaType.TEXT_PLAIN })
-	public Response getCodeSuffix(@PathParam("packId") Integer packId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
-		// EntityManager em = emProvider.get();
-
-		if (packId == null) {
-			throw new SeCurisServiceException(ErrorCodes.INVALID_DATA, "The pack code is mandatory");
-		}
-		Integer codeSuffix = licenseHelper.getNextCodeSuffix(packId, em);
-		Pack pack = em.find(Pack.class, packId);
-		;
-
-		String licCode = LicUtils.getLicenseCode(pack.getCode(), codeSuffix);
-		return Response.ok(licCode).build();
-	}
-
-	private void setPackLicenseType(Pack pack, Integer licTypeId, EntityManager em) throws SeCurisException {
-		LicenseType lt = null;
-		if (licTypeId != null) {
-			lt = em.find(LicenseType.class, pack.getLicTypeId());
-			if (lt == null) {
-				LOG.error("Pack license type with id {} not found in DB", licTypeId);
-				throw new SeCurisException("Pack license type not found with ID: " + licTypeId);
-			}
-		}
-		pack.setLicenseType(lt);
-	}
-
-	private Set<String> getMdKeys(Set<PackMetadata> mds) {
-		Set<String> ids = new HashSet<String>();
-		if (mds != null) {
-			for (PackMetadata md : mds) {
-				ids.add(md.getKey());
-			}
-		}
-		return ids;
-	}
-
 	@PUT
 	@POST
 	@Path("/{packId}")
@@ -292,7 +213,6 @@
 	@Produces({ MediaType.APPLICATION_JSON })
 	public Response modify(Pack pack, @PathParam("packId") Integer packId) {
 		LOG.info("Modifying pack with id: {}", packId);
-		// EntityManager em = emProvider.get();
 		Pack currentPack = em.find(Pack.class, packId);
 
 		try {
@@ -348,6 +268,15 @@
 		return Response.ok(currentPack).build();
 	}
 
+	/**
+	 * activate
+	 * <p>
+	 * Move a pack to ACTIVE (only from allowed states).
+	 *
+	 * @param packId target pack id.
+	 * @return 200 OK with updated pack or error when invalid transition.
+	 * @throws SeCurisServiceException when invalid state transition.
+	 */
 	@POST
 	@Path("/{packId}/activate")
 	@EnsureTransaction
@@ -357,7 +286,6 @@
 	@Produces({ MediaType.APPLICATION_JSON })
 	public Response activate(@PathParam("packId") Integer packId) throws SeCurisServiceException {
 		LOG.info("Activating pack with id: {}", packId);
-		// EntityManager em = emProvider.get();
 
 		Pack currentPack = em.find(Pack.class, packId);
 
@@ -372,8 +300,17 @@
 		return Response.ok(currentPack).build();
 	}
 
+	/**
+	 * onhold
+	 * <p>
+	 * Put a pack ON_HOLD from allowed states.
+	 *
+	 * @param packId id.
+	 * @return 200 OK with updated pack or error on invalid state.
+	 * @throws SeCurisServiceException on invalid state.
+	 */
 	@POST
-	@Path("/{packId}/putonhold")
+	@Path("/{packId}/putonhold}")
 	@EnsureTransaction
 	@Securable(roles = Rol.ADMIN | Rol.ADVANCE)
 	@RolesAllowed(BasicSecurityContext.ROL_ADMIN)
@@ -381,7 +318,6 @@
 	@Produces({ MediaType.APPLICATION_JSON })
 	public Response onhold(@PathParam("packId") Integer packId) throws SeCurisServiceException {
 		LOG.info("Putting On hold pack with id: {}", packId);
-		// EntityManager em = emProvider.get();
 
 		Pack currentPack = em.find(Pack.class, packId);
 
@@ -396,6 +332,18 @@
 		return Response.ok(currentPack).build();
 	}
 
+	/**
+	 * cancel
+	 * <p>
+	 * Cancel a pack. Cascades cancel to ACTIVE/PRE_ACTIVE licenses in the pack
+	 * via {@link LicenseHelper#cancelLicense}.
+	 *
+	 * @param packId id.
+	 * @param reason cancellation reason.
+	 * @param bsc    actor for history entries.
+	 * @return 200 OK with updated pack.
+	 * @throws SeCurisServiceException on invalid state.
+	 */
 	@POST
 	@Path("/{packId}/cancel")
 	@EnsureTransaction
@@ -405,7 +353,6 @@
 	@Produces({ MediaType.APPLICATION_JSON })
 	public Response cancel(@PathParam("packId") Integer packId, @FormParam("reason") String reason, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
 		LOG.info("Cancelling pack with id: {}", packId);
-		// EntityManager em = emProvider.get();
 
 		Pack currentPack = em.find(Pack.class, packId);
 
@@ -426,18 +373,41 @@
 		return Response.ok(currentPack).build();
 	}
 
-	private void setPackOrganization(Pack currentPack, Integer orgId, EntityManager em) throws SeCurisException {
-		Organization org = null;
-		if (orgId != null) {
-			org = em.find(Organization.class, orgId);
-			if (org == null) {
-				LOG.error("Organization pack with id {} not found in DB", orgId);
-				throw new SeCurisException("Pack organization not found with ID: " + orgId);
-			}
+	/**
+	 * getCodeSuffix
+	 * <p>
+	 * Compute the next available license code for a pack, by asking the helper
+	 * for the next numeric suffix and composing with {@link LicUtils}.
+	 *
+	 * @param packId id.
+	 * @param bsc    (unused) security context.
+	 * @return 200 OK with the full code text.
+	 * @throws SeCurisServiceException if packId missing.
+	 */
+	@GET
+	@Path("/{packId}/next_license_code")
+	@Securable(roles = Rol.ADMIN | Rol.ADVANCE)
+	@Produces({ MediaType.TEXT_PLAIN })
+	public Response getCodeSuffix(@PathParam("packId") Integer packId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
+		if (packId == null) {
+			throw new SeCurisServiceException(ErrorCodes.INVALID_DATA, "The pack code is mandatory");
 		}
-		currentPack.setOrganization(org);
+		Integer codeSuffix = licenseHelper.getNextCodeSuffix(packId, em);
+		Pack pack = em.find(Pack.class, packId);
+		String licCode = LicUtils.getLicenseCode(pack.getCode(), codeSuffix);
+		return Response.ok(licCode).build();
 	}
 
+	/**
+	 * delete
+	 * <p>
+	 * Delete a pack after ensuring there are no ACTIVE/PRE_ACTIVE licenses.
+	 * Removes remaining licenses then the pack itself.
+	 *
+	 * @param packId String id.
+	 * @return 200 OK with success map, 404 if missing, or 409 if active license exists.
+	 * @throws SeCurisServiceException on constraint errors.
+	 */
 	@DELETE
 	@Path("/{packId}")
 	@Securable(roles = Rol.ADMIN | Rol.ADVANCE)
@@ -446,13 +416,11 @@
 	@Produces({ MediaType.APPLICATION_JSON })
 	public Response delete(@PathParam("packId") String packId) throws SeCurisServiceException {
 		LOG.info("Deleting pack with id: {}", packId);
-		// EntityManager em = emProvider.get();
 		Pack pack = em.find(Pack.class, Integer.parseInt(packId));
 		if (pack == null) {
 			LOG.error("Pack with id {} can not be deleted, It was not found in DB", packId);
 			return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "Pack was not found, ID: " + packId).build();
 		}
-		// Pack metadata is removed in cascade automatically.
 
 		Set<License> licenses = pack.getLicenses();
 		for (License license : licenses) {
@@ -466,4 +434,149 @@
 		return Response.ok(Utils.createMap("success", true, "id", packId)).build();
 	}
 
+	// ---------------------------------------------------------------------
+	// Helpers
+	// ---------------------------------------------------------------------
+
+	/**
+	 * generateWhereFromParams<p>
+	 * Generate where clause to include to a query
+	 * 
+	 * @param addWhere
+	 * @param queryParams
+	 * @return whereClause
+	 */
+	private String generateWhereFromParams(boolean addWhere, MultivaluedMap<String, String> queryParams) {
+		List<String> conditions = new ArrayList<>();
+		if (queryParams.containsKey("organizationId")) {
+			conditions.add(String.format("pa.organization.id = %s", queryParams.getFirst("organizationId")));
+		}
+		if (queryParams.containsKey("applicationId")) {
+			conditions.add(String.format("pa.licenseType.application.id = %s", queryParams.getFirst("applicationId")));
+		}
+		if (queryParams.containsKey("licenseTypeId")) {
+			conditions.add(String.format("pa.licenseType.id = %s", queryParams.getFirst("licenseTypeId")));
+		}
+		String connector = addWhere ? " where " : " and ";
+		return (conditions.isEmpty() ? "" : connector) + String.join(" and ", conditions);
+	}
+
+	/** 
+	 * createQuery<p>
+	 * Build a typed query considering role-based scopes and filters. 
+	 * 
+	 * @param queryParams
+	 * @param basicSecurityContext
+	 */
+	private TypedQuery<Pack> createQuery(MultivaluedMap<String, String> queryParams, BasicSecurityContext bsc) {
+		TypedQuery<Pack> q;
+		String hql = "SELECT pa FROM Pack pa";
+		if (bsc.isUserInRole(BasicSecurityContext.ROL_ADMIN)) {
+			hql += generateWhereFromParams(true, queryParams);
+			q = em.createQuery(hql, Pack.class);
+		} else {
+			if (bsc.getApplicationsIds() == null || bsc.getApplicationsIds().isEmpty()) {
+				return null;
+			}
+			if (bsc.getOrganizationsIds() == null || bsc.getOrganizationsIds().isEmpty()) {
+				hql += " where pa.licenseType.application.id in :list_ids_app ";
+			} else {
+				hql += " where pa.organization.id in :list_ids_org and pa.licenseType.application.id in :list_ids_app ";
+			}
+			hql += generateWhereFromParams(false, queryParams);
+			q = em.createQuery(hql, Pack.class);
+			if (hql.contains("list_ids_org")) {
+				q.setParameter("list_ids_org", bsc.getOrganizationsIds());
+			}
+			q.setParameter("list_ids_app", bsc.getApplicationsIds());
+			LOG.info("Getting packs from orgs: {} and apps: {}", bsc.getOrganizationsIds(), bsc.getApplicationsIds());
+		}
+		return q;
+	}
+
+	/** 
+	 * generateErrorUnathorizedAccess<p>
+	 * Convenience 401 generator with log.
+	 * 
+	 *  @param pack
+	 *  @param user
+	 */
+	private Response generateErrorUnathorizedAccess(Pack pack, Principal user) {
+		LOG.error("Pack with id {} not accesible by user {}", pack, user);
+		return Response.status(Status.UNAUTHORIZED).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "Unathorized access to pack").build();
+	}
+
+	/**
+	 * setPackLicenseType<p>
+	 * Set the pack type
+	 * 
+	 * @param pack
+	 * @param licTypeId
+	 * @param em
+	 * @throws SeCurisException
+	 */
+	private void setPackLicenseType(Pack pack, Integer licTypeId, EntityManager em) throws SeCurisException {
+		LicenseType lt = null;
+		if (licTypeId != null) {
+			lt = em.find(LicenseType.class, pack.getLicTypeId());
+			if (lt == null) {
+				LOG.error("Pack license type with id {} not found in DB", licTypeId);
+				throw new SeCurisException("Pack license type not found with ID: " + licTypeId);
+			}
+		}
+		pack.setLicenseType(lt);
+	}
+
+	/**
+	 * getMdKeys<p>
+	 * Get the MD keys
+	 * 
+	 * @param mds
+	 * @return mdKeys
+	 */
+	private Set<String> getMdKeys(Set<PackMetadata> mds) {
+		Set<String> ids = new HashSet<String>();
+		if (mds != null) {
+			for (PackMetadata md : mds) {
+				ids.add(md.getKey());
+			}
+		}
+		return ids;
+	}
+
+	/**
+	 * checkIfCodeExists<p>
+	 * Check if the code already exist
+	 * 
+	 * @param code
+	 * @param em
+	 * @return codeExist
+	 */
+	private boolean checkIfCodeExists(String code, EntityManager em) {
+		TypedQuery<Pack> query = em.createNamedQuery("pack-by-code", Pack.class);
+		query.setParameter("code", code);
+		int packs = query.getResultList().size();
+		return packs > 0;
+	}
+
+	/**
+	 * setPackOrganization<p>
+	 * Set the organization of the pack
+	 * 
+	 * @param currentPack
+	 * @param orgId
+	 * @param em
+	 * @throws SeCurisException
+	 */
+	private void setPackOrganization(Pack currentPack, Integer orgId, EntityManager em) throws SeCurisException {
+		Organization org = null;
+		if (orgId != null) {
+			org = em.find(Organization.class, orgId);
+			if (org == null) {
+				LOG.error("Organization pack with id {} not found in DB", orgId);
+				throw new SeCurisException("Pack organization not found with ID: " + orgId);
+			}
+		}
+		currentPack.setOrganization(org);
+	}
 }

--
Gitblit v1.3.2