/** * Copyright (c) 2009 - 2012 Red Hat, Inc. * * This software is licensed to you under the GNU General Public License, * version 2 (GPLv2). There is NO WARRANTY for this software, express or * implied, including the implied warranties of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 * along with this software; if not, see * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * Red Hat trademarks are not licensed under GPLv2. No permission is * granted to use or replicate Red Hat trademarks that are incorporated * in this software or its documentation. */ package org.candlepin.resource; import org.candlepin.common.auth.SecurityHole; import org.candlepin.common.config.Configuration; import org.candlepin.common.exceptions.BadRequestException; import org.candlepin.common.exceptions.NotFoundException; import org.candlepin.config.ConfigProperties; import org.candlepin.model.CandlepinQuery; import org.candlepin.model.Owner; import org.candlepin.model.OwnerCurator; import org.candlepin.model.Product; import org.candlepin.model.ProductCertificate; import org.candlepin.model.ProductCertificateCurator; import org.candlepin.model.ProductCurator; import org.candlepin.model.ResultIterator; import org.candlepin.model.dto.ProductData; import org.candlepin.pinsetter.tasks.RefreshPoolsJob; import com.google.inject.Inject; import org.quartz.JobDetail; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xnap.commons.i18n.I18n; import java.util.LinkedList; import java.util.List; import java.util.Map; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; import io.swagger.annotations.Authorization; /** * API Gateway into /product */ @Path("/products") @Api(value = "products", authorizations = { @Authorization("basic") }) public class ProductResource { private static Logger log = LoggerFactory.getLogger(ProductResource.class); private ProductCurator productCurator; private OwnerCurator ownerCurator; private ProductCertificateCurator productCertCurator; private Configuration config; private I18n i18n; @Inject public ProductResource(ProductCurator productCurator, OwnerCurator ownerCurator, ProductCertificateCurator productCertCurator, Configuration config, I18n i18n) { this.productCurator = productCurator; this.productCertCurator = productCertCurator; this.ownerCurator = ownerCurator; this.config = config; this.i18n = i18n; } /** * Retrieves a Product instance for the product with the specified id. If no matching product * could be found, this method throws an exception. * * @param productUuid * The ID of the product to retrieve * * @throws NotFoundException * if no matching product could be found with the specified id * * @return * the Product instance for the product with the specified id */ protected Product fetchProduct(String productUuid) { Product product = this.productCurator.find(productUuid); if (product == null) { throw new NotFoundException( i18n.tr("Product with UUID ''{0}'' could not be found.", productUuid) ); } return product; } @ApiOperation(notes = "Retrieves a single Product", value = "getProduct") @ApiResponses({ @ApiResponse(code = 404, message = "") }) @GET @Path("/{product_uuid}") @Produces(MediaType.APPLICATION_JSON) @SecurityHole public ProductData getProduct(@PathParam("product_uuid") String productUuid) { Product product = this.fetchProduct(productUuid); return product.toDTO(); } @ApiOperation(notes = "Retreives a Certificate for a Product", value = "getProductCertificate") @ApiResponses({ @ApiResponse(code = 404, message = "") }) @GET @Path("/{product_uuid}/certificate") @Produces(MediaType.APPLICATION_JSON) @SecurityHole public ProductCertificate getProductCertificate( @PathParam("product_uuid") String productUuid) { // TODO: // Should this be enabled globally? This will create a cert if it hasn't yet been created. Product product = this.fetchProduct(productUuid); return this.productCertCurator.getCertForProduct(product); } /** * @deprecated Use per-org version * @return Product */ @ApiOperation(notes = "Creates a Product. Returns either the new created " + "Product or the Product that already existed. @deprecated Use per-org" + " version", value = "createProduct") @POST @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) @Deprecated public ProductData createProduct(ProductData product) { throw new BadRequestException(this.i18n.tr( "Organization-agnostic product write operations are no longer supported." )); } /** * @deprecated Use per-org version * @return Product */ @ApiOperation(notes = "Updates a Product @deprecated Use per-org version", value = "updateProduct") @ApiResponses({ @ApiResponse(code = 400, message = "") }) @PUT @Path("/{product_uuid}") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.WILDCARD) @Deprecated public ProductData updateProduct( @PathParam("product_uuid") String productUuid, @ApiParam(name = "product", required = true) ProductData product) { throw new BadRequestException(this.i18n.tr( "Organization-agnostic product write operations are no longer supported." )); } /** * @deprecated Use per-org version * @return Product */ @ApiOperation(notes = "Adds Content to a Product Batch mode @deprecated Use per-org version", value = "addBatchContent") @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @Path("/{product_uuid}/batch_content") @Deprecated public ProductData addBatchContent( @PathParam("product_uuid") String productUuid, Map<String, Boolean> contentMap) { throw new BadRequestException(this.i18n.tr( "Organization-agnostic product write operations are no longer supported." )); } /** * @deprecated Use per-org version * @return Product */ @ApiOperation(notes = "Adds Content to a Product. Single mode @deprecated Use " + "per-org version", value = "addContent") @ApiResponses({ }) @POST @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.WILDCARD) @Path("/{product_uuid}/content/{content_id}") @Deprecated public ProductData addContent( @PathParam("product_uuid") String productUuid, @PathParam("content_id") String contentId, @QueryParam("enabled") Boolean enabled) { throw new BadRequestException(this.i18n.tr( "Organization-agnostic product write operations are no longer supported." )); } /** * @deprecated Use per-org version */ @ApiOperation(notes = "Removes Content from a Product @deprecated Use per-org version", value = "removeContent") @DELETE @Produces(MediaType.APPLICATION_JSON) @Path("/{product_uuid}/content/{content_id}") @Deprecated public void removeContent( @PathParam("product_uuid") String productUuid, @PathParam("content_id") String contentId) { throw new BadRequestException(this.i18n.tr( "Organization-agnostic product write operations are no longer supported." )); } /** * @deprecated Use per-org version */ @ApiOperation(notes = "Removes a Product @deprecated Use per-org version", value = "deleteProduct") @ApiResponses({ @ApiResponse(code = 400, message = ""), @ApiResponse(code = 404, message = "") }) @DELETE @Produces(MediaType.APPLICATION_JSON) @Path("/{product_uuid}") @Deprecated public void deleteProduct( @PathParam("product_uuid") String productUuid) { throw new BadRequestException(this.i18n.tr( "Organization-agnostic product write operations are no longer supported." )); } @ApiOperation(notes = "Retrieves a list of Owners by Product", value = "getProductOwners", response = Owner.class, responseContainer = "list") @ApiResponses({ @ApiResponse(code = 400, message = "") }) @GET @Path("/owners") @Produces(MediaType.APPLICATION_JSON) public CandlepinQuery<Owner> getProductOwners( @ApiParam(value = "Multiple product UUIDs", required = true) @QueryParam("product") List<String> productUuids) { if (productUuids.isEmpty()) { throw new BadRequestException(i18n.tr("No product IDs specified")); } return this.ownerCurator.lookupOwnersWithProduct(productUuids); } @ApiOperation(notes = "Refreshes Pools by Product", value = "refreshPoolsForProduct") @PUT @Path("/subscriptions") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.WILDCARD) public JobDetail[] refreshPoolsForProduct( @ApiParam(value = "Multiple product UUIDs", required = true) @QueryParam("product") List<String> productUuids, @QueryParam("lazy_regen") @DefaultValue("true") Boolean lazyRegen) { if (productUuids.isEmpty()) { throw new BadRequestException(i18n.tr("No product IDs specified")); } if (config.getBoolean(ConfigProperties.STANDALONE)) { log.warn("Ignoring refresh pools request due to standalone config."); return null; } // TODO: // Replace this with the commented out block below once the job scheduling is no longer performed // via PinsetterAsyncFilter ResultIterator<Owner> iterator = this.ownerCurator.lookupOwnersWithProduct(productUuids).iterate(); List<JobDetail> details = new LinkedList<JobDetail>(); while (iterator.hasNext()) { details.add(RefreshPoolsJob.forOwner(iterator.next(), lazyRegen)); } iterator.close(); return details.toArray(new JobDetail[0]); // final Boolean lazy = lazyRegen; // Necessary to deal with Java's limitations with closures // return this.ownerCurator.lookupOwnersWithProduct(productUuids).transform( // new ElementTransform<Owner, JobDetail>() { // @Override // public JobDetail transform(Owner owner) { // return RefreshPoolsJob.forOwner(owner, lazy); // } // } // ); } }