/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.ambari.server.api.services;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriInfo;

import org.apache.ambari.server.api.resources.ResourceInstance;
import org.apache.ambari.server.controller.ComponentDependencyResponse;
import org.apache.ambari.server.controller.ExtensionLinkResponse;
import org.apache.ambari.server.controller.QuickLinksResponse;
import org.apache.ambari.server.controller.StackArtifactResponse;
import org.apache.ambari.server.controller.StackConfigurationDependencyResponse;
import org.apache.ambari.server.controller.StackConfigurationResponse;
import org.apache.ambari.server.controller.StackResponse;
import org.apache.ambari.server.controller.StackServiceArtifactResponse;
import org.apache.ambari.server.controller.StackServiceComponentResponse;
import org.apache.ambari.server.controller.StackServiceResponse;
import org.apache.ambari.server.controller.StackVersionResponse;
import org.apache.ambari.server.controller.ThemeResponse;
import org.apache.ambari.server.controller.spi.Resource;
import org.apache.http.HttpStatus;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;


@Path("/stacks/")
@Api(value = "Stacks", description = "Endpoint for stack specific operations")
public class StacksService extends BaseService {

  @GET
  @Produces(MediaType.TEXT_PLAIN)
  @ApiOperation(value = "Get all stacks",
      nickname = "StacksService#getStacks",
      notes = "Returns all stacks.",
      response = StackResponse.StackResponseSwagger.class,
      responseContainer = RESPONSE_CONTAINER_LIST)
  @ApiImplicitParams({
      @ApiImplicitParam(name = QUERY_FIELDS, value = "Filter stack details", defaultValue = "Stacks/stack_name", dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_SORT, value = "Sort stack privileges (asc | desc)", defaultValue = "Stacks/stack_name.asc", dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_PAGE_SIZE, value = QUERY_PAGE_SIZE_DESCRIPTION, defaultValue = DEFAULT_PAGE_SIZE, dataType = DATA_TYPE_INT, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_FROM, value = QUERY_FROM_DESCRIPTION, defaultValue = DEFAULT_FROM, dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_TO, value = QUERY_TO_DESCRIPTION, dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY)
  })
  @ApiResponses(value = {
      @ApiResponse(code = HttpStatus.SC_OK, message = MSG_SUCCESSFUL_OPERATION),
      @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = MSG_SERVER_ERROR)
  })
  public Response getStacks(@Context HttpHeaders headers, @Context UriInfo ui) {
    return handleRequest(headers, null, ui, Request.Type.GET,
        createStackResource(null));
  }

  @GET
  @Path("{stackName}")
  @Produces(MediaType.TEXT_PLAIN)
  @ApiOperation(value = "Get a stack", nickname = "StacksService#getStack", notes = "Returns stack details.",
      response = StackResponse.StackResponseSwagger.class, responseContainer = RESPONSE_CONTAINER_LIST)
  @ApiImplicitParams({
      @ApiImplicitParam(name = QUERY_FIELDS, value = "Filter stack details",
          defaultValue = "Stacks/*", dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY),
  })
  @ApiResponses(value = {
      @ApiResponse(code = HttpStatus.SC_OK, message = MSG_SUCCESSFUL_OPERATION),
      @ApiResponse(code = HttpStatus.SC_NOT_FOUND, message = MSG_RESOURCE_NOT_FOUND),
      @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = MSG_SERVER_ERROR)
  })
  public Response getStack(@Context HttpHeaders headers,
                           @Context UriInfo ui,
                           @ApiParam @PathParam("stackName") String stackName) {

    return handleRequest(headers, null, ui, Request.Type.GET,
        createStackResource(stackName));
  }

  @GET
  @Path("{stackName}/versions")
  @Produces(MediaType.TEXT_PLAIN)
  @ApiOperation(value = "Get all versions for a stacks",
      nickname = "StacksService#getStackVersions",
      notes = "Returns all versions for a stack.",
      response = StackVersionResponse.StackVersionResponseSwagger.class,
      responseContainer = RESPONSE_CONTAINER_LIST)
  @ApiImplicitParams({
      @ApiImplicitParam(name = QUERY_FIELDS, value = "Filter stack version details",
          defaultValue = "Versions/stack_name,Versions/stack_version",
          dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_SORT, value = "Sort stack privileges (asc | desc)",
          defaultValue = "Versions/stack_name.asc,Versions/stack_version.asc",
          dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_PAGE_SIZE, value = QUERY_PAGE_SIZE_DESCRIPTION, defaultValue = DEFAULT_PAGE_SIZE, dataType = DATA_TYPE_INT, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_FROM, value = QUERY_FROM_DESCRIPTION, defaultValue = DEFAULT_FROM, dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_TO, value = QUERY_TO_DESCRIPTION, dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY)
  })
  @ApiResponses(value = {
      @ApiResponse(code = HttpStatus.SC_OK, message = MSG_SUCCESSFUL_OPERATION),
      @ApiResponse(code = HttpStatus.SC_NOT_FOUND, message = MSG_RESOURCE_NOT_FOUND),
      @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = MSG_SERVER_ERROR)
  })
  public Response getStackVersions(@Context HttpHeaders headers, @Context UriInfo ui,
                                   @ApiParam @PathParam("stackName") String stackName) {

    return handleRequest(headers, null, ui, Request.Type.GET,
        createStackVersionResource(stackName, null));
  }

  @GET
  @Path("{stackName}/versions/{stackVersion}")
  @Produces(MediaType.TEXT_PLAIN)
  @ApiOperation(value = "Get details for a stack version",
      nickname = "StacksService#getStackVersion",
      notes = "Returns the details for a stack version.",
      response = StackVersionResponse.StackVersionResponseSwagger.class)
  @ApiImplicitParams({
      @ApiImplicitParam(name = QUERY_FIELDS, value = "Filter stack version details",
          defaultValue = "Versions/*", dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY)
  })
  @ApiResponses(value = {
      @ApiResponse(code = HttpStatus.SC_OK, message = MSG_SUCCESSFUL_OPERATION),
      @ApiResponse(code = HttpStatus.SC_NOT_FOUND, message = MSG_RESOURCE_NOT_FOUND),
      @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = MSG_SERVER_ERROR)
  })
  public Response getStackVersion(@Context HttpHeaders headers, @Context UriInfo ui,
                                  @ApiParam @PathParam("stackName") String stackName,
                                  @ApiParam @PathParam("stackVersion") String stackVersion) {

    return handleRequest(headers, null, ui, Request.Type.GET,
        createStackVersionResource(stackName, stackVersion));
  }

  @GET
  @Path("{stackName}/versions/{stackVersion}/links")
  @Produces(MediaType.TEXT_PLAIN)
  @ApiOperation(value = "Get extension links for a stack version",
      nickname = "StacksService#getStackVersionLinks",
      notes = "Returns the extension links for a stack version.",
      response = ExtensionLinkResponse.class,
      responseContainer = RESPONSE_CONTAINER_LIST)
  @ApiImplicitParams({
      @ApiImplicitParam(name = QUERY_FIELDS, value = "Filter extension link attributes",
          defaultValue = "ExtensionLink/link_id," +
              "ExtensionLink/stack_name," +
              "ExtensionLink/stack_version," +
              "ExtensionLink/extension_name," +
              "ExtensionLink/extension_version", dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_SORT, value = "Sort extension links (asc | desc)",
          defaultValue = "ExtensionLink/link_id.asc," +
              "ExtensionLink/stack_name.asc," +
              "ExtensionLink/stack_version.asc," +
              "ExtensionLink/extension_name.asc," +
              "ExtensionLink/extension_version.asc",
          dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_PAGE_SIZE, value = QUERY_PAGE_SIZE_DESCRIPTION, defaultValue = DEFAULT_PAGE_SIZE, dataType = DATA_TYPE_INT, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_FROM, value = QUERY_FROM_DESCRIPTION, defaultValue = DEFAULT_FROM, dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_TO, value = QUERY_TO_DESCRIPTION, dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY)

  })
  @ApiResponses(value = {
      @ApiResponse(code = HttpStatus.SC_OK, message = MSG_SUCCESSFUL_OPERATION),
      @ApiResponse(code = HttpStatus.SC_NOT_FOUND, message = MSG_RESOURCE_NOT_FOUND),
      @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = MSG_SERVER_ERROR)
  })
  public Response getStackVersionLinks(@Context HttpHeaders headers, @Context UriInfo ui,
                                       @ApiParam @PathParam("stackName") String stackName,
                                       @ApiParam @PathParam("stackVersion") String stackVersion) {
    return handleRequest(headers, null, ui, Request.Type.GET,
        createExtensionLinkResource(stackName, stackVersion, null, null));
  }

  @GET
  @Path("{stackName}/versions/{stackVersion}/configurations")
  @Produces(MediaType.TEXT_PLAIN)
  @ApiOperation(value = "Get all configurations for a stack version",
      nickname = "StacksService#getStackLevelConfigurations",
      notes = "Returns all configurations for a stack version.",
      response = StackConfigurationResponse.StackConfigurationResponseSwagger.class,
      responseContainer = RESPONSE_CONTAINER_LIST)
  @ApiImplicitParams({
      @ApiImplicitParam(name = QUERY_FIELDS, value = "Filter returned attributes",
          defaultValue = "StackLevelConfigurations/stack_name," +
            "StackLevelConfigurations/stack_version," +
            "StackLevelConfigurations/property_name",
          dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_SORT, value = "Sort configuration (asc | desc)",
          defaultValue = "StackLevelConfigurations/stack_name.asc," +
              "StackLevelConfigurations/stack_version.asc," +
              "StackLevelConfigurations/property_name.asc ",
          dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_PAGE_SIZE, value = QUERY_PAGE_SIZE_DESCRIPTION, defaultValue = DEFAULT_PAGE_SIZE, dataType = DATA_TYPE_INT, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_FROM, value = QUERY_FROM_DESCRIPTION, defaultValue = DEFAULT_FROM, dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_TO, value = QUERY_TO_DESCRIPTION, dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY)

  })
  @ApiResponses(value = {
      @ApiResponse(code = HttpStatus.SC_OK, message = MSG_SUCCESSFUL_OPERATION),
      @ApiResponse(code = HttpStatus.SC_NOT_FOUND, message = MSG_RESOURCE_NOT_FOUND),
      @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = MSG_SERVER_ERROR)
  })
  public Response getStackLevelConfigurations(@Context HttpHeaders headers, @Context UriInfo ui,
                                              @ApiParam @PathParam("stackName") String stackName,
                                              @ApiParam @PathParam("stackVersion") String stackVersion) {

    return handleRequest(headers, null, ui, Request.Type.GET,
        createStackLevelConfigurationsResource(stackName, stackVersion, null));
  }

  @GET
  @Path("{stackName}/versions/{stackVersion}/configurations/{propertyName}")
  @Produces(MediaType.TEXT_PLAIN)
  @ApiOperation(value = "Get configuration details for a given property",
      nickname = "StacksService#getStackLevelConfiguration",
      notes = "Returns the configuration details for a given property.",
      response = StackConfigurationResponse.StackConfigurationResponseSwagger.class)
  @ApiImplicitParams({
      @ApiImplicitParam(name = QUERY_FIELDS, value = "Filter returned attributes",
          defaultValue = "StackLevelConfigurations/*",
          dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY)
  })
  @ApiResponses(value = {
      @ApiResponse(code = HttpStatus.SC_OK, message = MSG_SUCCESSFUL_OPERATION),
      @ApiResponse(code = HttpStatus.SC_NOT_FOUND, message = MSG_RESOURCE_NOT_FOUND),
      @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = MSG_SERVER_ERROR)
  })
  public Response getStackLevelConfiguration(@Context HttpHeaders headers, @Context UriInfo ui,
                                             @ApiParam @PathParam("stackName") String stackName,
                                             @ApiParam @PathParam("stackVersion") String stackVersion,
                                             @ApiParam @PathParam("propertyName") String propertyName) {

    return handleRequest(headers, null, ui, Request.Type.GET,
        createStackLevelConfigurationsResource(stackName, stackVersion, propertyName));
  }

  @GET
  @Path("{stackName}/versions/{stackVersion}/services")
  @Produces(MediaType.TEXT_PLAIN)
  @ApiOperation(value = "Get all services for a stack version",
      nickname = "StacksService#getStackServices",
      notes = "Returns all services for a stack version.",
      response = StackServiceResponse.StackServiceResponseSwagger.class,
      responseContainer = RESPONSE_CONTAINER_LIST)
  @ApiImplicitParams({
      @ApiImplicitParam(name = QUERY_FIELDS, value = "Filter returned attributes",
          defaultValue = "StackServices/stack_name," +
              "StackServices/stack_version," +
              "StackServices/service_name",
          dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_SORT, value = "Sort stack services (asc | desc)",
          defaultValue = "StackServices/stack_name.asc," +
              "StackServices/stack_version.asc," +
              "StackServices/service_name.asc",
          dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_PAGE_SIZE, value = QUERY_PAGE_SIZE_DESCRIPTION, defaultValue = DEFAULT_PAGE_SIZE, dataType = DATA_TYPE_INT, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_FROM, value = QUERY_FROM_DESCRIPTION, defaultValue = DEFAULT_FROM, dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_TO, value = QUERY_TO_DESCRIPTION, dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY)
  })
  @ApiResponses(value = {
      @ApiResponse(code = HttpStatus.SC_OK, message = MSG_SUCCESSFUL_OPERATION),
      @ApiResponse(code = HttpStatus.SC_NOT_FOUND, message = MSG_RESOURCE_NOT_FOUND),
      @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = MSG_SERVER_ERROR)
  })
  public Response getStackServices(@Context HttpHeaders headers, @Context UriInfo ui,
                                   @ApiParam @PathParam("stackName") String stackName,
                                   @ApiParam @PathParam("stackVersion") String stackVersion) {

    return handleRequest(headers, null, ui, Request.Type.GET,
        createStackServiceResource(stackName, stackVersion, null));
  }

  @GET
  @Path("{stackName}/versions/{stackVersion}/services/{serviceName}")
  @Produces(MediaType.TEXT_PLAIN)
  @ApiOperation(value = "Get stack service details",
      nickname = "StacksService#getStackService",
      notes = "Returns the details of a stack service.",
      response = StackServiceResponse.StackServiceResponseSwagger.class)
  @ApiImplicitParams({
      @ApiImplicitParam(name = QUERY_FIELDS, value = "Filter returned attributes",
          defaultValue = "StackServices/*",
          dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY)
  })
  @ApiResponses(value = {
      @ApiResponse(code = HttpStatus.SC_OK, message = MSG_SUCCESSFUL_OPERATION),
      @ApiResponse(code = HttpStatus.SC_NOT_FOUND, message = MSG_RESOURCE_NOT_FOUND),
      @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = MSG_SERVER_ERROR)
  })
  public Response getStackService(@Context HttpHeaders headers, @Context UriInfo ui,
                                  @ApiParam @PathParam("stackName") String stackName,
                                  @ApiParam @PathParam("stackVersion") String stackVersion,
                                  @ApiParam @PathParam("serviceName") String serviceName) {

    return handleRequest(headers, null, ui, Request.Type.GET,
        createStackServiceResource(stackName, stackVersion, serviceName));
  }

  @GET
  @Path("{stackName}/versions/{stackVersion}/artifacts")
  @Produces(MediaType.TEXT_PLAIN)
  @ApiOperation(value = "Get all stack artifacts",
      nickname = "StacksService#getStackArtifacts",
      notes = "Returns all stack artifacts (e.g: kerberos descriptor, metrics descriptor)",
      response = StackArtifactResponse.class,
      responseContainer = RESPONSE_CONTAINER_LIST)
  @ApiImplicitParams({
      @ApiImplicitParam(name = QUERY_FIELDS, value = "Filter returned attributes",
          defaultValue = "Artifacts/artifact_name," +
              "Artifacts/stack_name," +
              "Artifacts/stack_version",
          dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY)
  })
  @ApiResponses(value = {
      @ApiResponse(code = HttpStatus.SC_OK, message = MSG_SUCCESSFUL_OPERATION),
      @ApiResponse(code = HttpStatus.SC_NOT_FOUND, message = MSG_RESOURCE_NOT_FOUND),
      @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = MSG_SERVER_ERROR)
  })
  public Response getStackArtifacts(@Context HttpHeaders headers, @Context UriInfo ui,
                                    @ApiParam @PathParam("stackName") String stackName,
                                    @ApiParam @PathParam("stackVersion") String stackVersion) {

    return handleRequest(headers, null, ui, Request.Type.GET,
        createStackArtifactsResource(stackName, stackVersion, null));
  }

  @GET
  @Path("{stackName}/versions/{stackVersion}/artifacts/{artifactName}")
  @Produces(MediaType.TEXT_PLAIN)
  @ApiOperation(value = "Get stack artifact details",
      nickname = "StacksService#getStackArtifact",
      notes = "Returns the details of a stack artifact",
      response = StackArtifactResponse.class)
  @ApiImplicitParams({
      @ApiImplicitParam(name = QUERY_FIELDS, value = "Filter returned attributes",
          defaultValue = "Artifacts/*",
          dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY)
  })
  @ApiResponses(value = {
      @ApiResponse(code = HttpStatus.SC_OK, message = MSG_SUCCESSFUL_OPERATION),
      @ApiResponse(code = HttpStatus.SC_NOT_FOUND, message = MSG_RESOURCE_NOT_FOUND),
      @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = MSG_SERVER_ERROR)
  })
  public Response getStackArtifact(@Context HttpHeaders headers, @Context UriInfo ui,
                                   @PathParam("stackName") String stackName,
                                   @ApiParam @PathParam("stackVersion") String stackVersion,
                                   @ApiParam @PathParam("artifactName") String artifactName) {
    return handleRequest(headers, null, ui, Request.Type.GET,
        createStackArtifactsResource(stackName, stackVersion, artifactName));
  }

  @GET
  @Path("{stackName}/versions/{stackVersion}/services/{serviceName}/artifacts")
  @Produces(MediaType.TEXT_PLAIN)
  @ApiOperation(value = "Get all artifacts for a stack service",
      nickname = "StacksService#getStackServiceArtifacts",
      notes = "Returns all stack service artifacts",
      response = StackServiceArtifactResponse.class,
      responseContainer = RESPONSE_CONTAINER_LIST)
  @ApiImplicitParams({
      @ApiImplicitParam(name = QUERY_FIELDS, value = "Filter returned attributes",
          defaultValue = "Artifacts/artifact_name," +
              "Artifacts/stack_name," +
              "Artifacts/stack_version",
          dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_SORT, value = "Sort service artifacts (asc | desc)",
          defaultValue = "Artifacts/artifact_name.asc," +
              "Artifacts/stack_name.asc," +
              "Artifacts/stack_version.asc",
          dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_PAGE_SIZE, value = QUERY_PAGE_SIZE_DESCRIPTION, defaultValue = DEFAULT_PAGE_SIZE, dataType = DATA_TYPE_INT, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_FROM, value = QUERY_FROM_DESCRIPTION, defaultValue = DEFAULT_FROM, dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_TO, value = QUERY_TO_DESCRIPTION, dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY)
  })
  @ApiResponses(value = {
      @ApiResponse(code = HttpStatus.SC_OK, message = MSG_SUCCESSFUL_OPERATION),
      @ApiResponse(code = HttpStatus.SC_NOT_FOUND, message = MSG_RESOURCE_NOT_FOUND),
      @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = MSG_SERVER_ERROR)
  })
  public Response getStackServiceArtifacts(@Context HttpHeaders headers, @Context UriInfo ui,
                                           @ApiParam @PathParam("stackName") String stackName,
                                           @ApiParam @PathParam("stackVersion") String stackVersion,
                                           @ApiParam @PathParam("serviceName") String serviceName) {

    return handleRequest(headers, null, ui, Request.Type.GET,
        createStackServiceArtifactsResource(stackName, stackVersion, serviceName, null));
  }

  @GET
  @Path("{stackName}/versions/{stackVersion}/services/{serviceName}/themes")
  @Produces(MediaType.TEXT_PLAIN)
  @ApiOperation(value = "Get all themes for a stack service",
      nickname = "StacksService#getStackServiceThemes",
      notes = "Returns all stack themes",
      response = ThemeResponse.class,
      responseContainer = RESPONSE_CONTAINER_LIST)
  @ApiImplicitParams({
      @ApiImplicitParam(name = QUERY_FIELDS, value = "Filter returned attributes",
          defaultValue = "ThemeInfo/file_name," +
              "ThemeInfo/service_name," +
              "ThemeInfo/stack_name," +
              "ThemeInfo/stack_version",
          dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_SORT, value = "Sort service artifacts (asc | desc)",
          defaultValue = "ThemeInfo/file_name.asc," +
              "ThemeInfo/service_name.asc," +
              "ThemeInfo/stack_name.asc," +
              "ThemeInfo/stack_version.asc",
          dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_PAGE_SIZE, value = QUERY_PAGE_SIZE_DESCRIPTION, defaultValue = DEFAULT_PAGE_SIZE, dataType = DATA_TYPE_INT, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_FROM, value = QUERY_FROM_DESCRIPTION, defaultValue = DEFAULT_FROM, dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_TO, value = QUERY_TO_DESCRIPTION, dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY)
  })
  @ApiResponses(value = {
      @ApiResponse(code = HttpStatus.SC_OK, message = MSG_SUCCESSFUL_OPERATION),
      @ApiResponse(code = HttpStatus.SC_NOT_FOUND, message = MSG_RESOURCE_NOT_FOUND),
      @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = MSG_SERVER_ERROR)
  })
  public Response getStackServiceThemes(@Context HttpHeaders headers, @Context UriInfo ui,
                                        @ApiParam @PathParam("stackName") String stackName,
                                        @ApiParam @PathParam("stackVersion") String stackVersion,
                                        @ApiParam @PathParam("serviceName") String serviceName) {

    return handleRequest(headers, null, ui, Request.Type.GET,
      createStackServiceThemesResource(stackName, stackVersion, serviceName, null));
  }

  @GET
  @Path("{stackName}/versions/{stackVersion}/services/{serviceName}/themes/{themeName}")
  @Produces(MediaType.TEXT_PLAIN)
  @ApiOperation(value = "Get theme details for a stack service",
      nickname = "StacksService#getStackServiceTheme",
      notes = "Returns stack service theme details.",
      response = ThemeResponse.class)
  @ApiImplicitParams({
      @ApiImplicitParam(name = QUERY_FIELDS, value = "Filter returned attributes",
          defaultValue = "ThemeInfo/*",
          dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY)
  })
  @ApiResponses(value = {
      @ApiResponse(code = HttpStatus.SC_OK, message = MSG_SUCCESSFUL_OPERATION),
      @ApiResponse(code = HttpStatus.SC_NOT_FOUND, message = MSG_RESOURCE_NOT_FOUND),
      @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = MSG_SERVER_ERROR)
  })
  public Response getStackServiceTheme(@Context HttpHeaders headers, @Context UriInfo ui,
                                       @ApiParam @PathParam("stackName") String stackName,
                                       @ApiParam @PathParam("stackVersion") String stackVersion,
                                       @ApiParam @PathParam("serviceName") String serviceName,
                                       @ApiParam @PathParam("themeName") String themeName) {

    return handleRequest(headers, null, ui, Request.Type.GET,
      createStackServiceThemesResource(stackName, stackVersion, serviceName, themeName));
  }

  @GET
  @Path("{stackName}/versions/{stackVersion}/services/{serviceName}/quicklinks")
  @Produces(MediaType.TEXT_PLAIN)
  @ApiOperation(value = "Get all quicklinks configurations for a stack service",
      nickname = "StacksService#getStackServiceQuickLinksConfigurations",
      notes = "Returns all quicklinks configurations for a stack service.",
      response = QuickLinksResponse.class,
      responseContainer = RESPONSE_CONTAINER_LIST)
  @ApiImplicitParams({
      @ApiImplicitParam(name = QUERY_FIELDS, value = "Filter returned attributes",
          defaultValue = "QuickLinkInfo/file_name," +
              "QuickLinkInfo/service_name," +
              "QuickLinkInfo/stack_name," +
              "QuickLinkInfo/stack_version",
          dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_SORT, value = "Sort quick links (asc | desc)",
          defaultValue = "QuickLinkInfo/file_name.asc," +
              "QuickLinkInfo/service_name.asc," +
              "QuickLinkInfo/stack_name.asc," +
              "QuickLinkInfo/stack_version.asc",
          dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_PAGE_SIZE, value = QUERY_PAGE_SIZE_DESCRIPTION, defaultValue = DEFAULT_PAGE_SIZE, dataType = DATA_TYPE_INT, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_FROM, value = QUERY_FROM_DESCRIPTION, defaultValue = DEFAULT_FROM, dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY),
      @ApiImplicitParam(name = QUERY_TO, value = QUERY_TO_DESCRIPTION, dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY)
  })
  @ApiResponses(value = {
      @ApiResponse(code = HttpStatus.SC_OK, message = MSG_SUCCESSFUL_OPERATION),
      @ApiResponse(code = HttpStatus.SC_NOT_FOUND, message = MSG_RESOURCE_NOT_FOUND),
      @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = MSG_SERVER_ERROR)
  })
  public Response getStackServiceQuickLinksConfigurations(@Context HttpHeaders headers, @Context UriInfo ui,
                                                          @ApiParam @PathParam("stackName") String stackName,
                                                          @ApiParam @PathParam("stackVersion") String stackVersion,
                                                          @ApiParam @PathParam("serviceName") String serviceName) {

    return handleRequest(headers, null, ui, Request.Type.GET,
      createStackServiceQuickLinksResource(stackName, stackVersion, serviceName, null));
  }

  @GET
  @Path("{stackName}/versions/{stackVersion}/services/{serviceName}/quicklinks/{quickLinksConfigurationName}")
  @Produces(MediaType.TEXT_PLAIN)
  @ApiOperation(value = "Get quicklinks configuration details",
      nickname = "StacksService#getStackServiceQuickLinksConfiguration",
      notes = "Returns the details of a quicklinks configuration.",
      response = QuickLinksResponse.class,
      responseContainer = RESPONSE_CONTAINER_LIST)
  @ApiImplicitParams({
      @ApiImplicitParam(name = QUERY_FIELDS, value = "Filter returned attributes",
          defaultValue = "QuickLinkInfo/*",
          dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY)
  })
  @ApiResponses(value = {
      @ApiResponse(code = HttpStatus.SC_OK, message = MSG_SUCCESSFUL_OPERATION),
      @ApiResponse(code = HttpStatus.SC_NOT_FOUND, message = MSG_RESOURCE_NOT_FOUND),
      @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = MSG_SERVER_ERROR)
  })
  public Response getStackServiceQuickLinksConfiguration(@Context HttpHeaders headers, @Context UriInfo ui,
                                                         @ApiParam @PathParam("stackName") String stackName,
                                                         @ApiParam @PathParam("stackVersion") String stackVersion,
                                                         @ApiParam @PathParam("serviceName") String serviceName,
                                                         @ApiParam @PathParam("quickLinksConfigurationName") String quickLinksConfigurationName) {

    return handleRequest(headers, null, ui, Request.Type.GET,
      createStackServiceQuickLinksResource(stackName, stackVersion, serviceName, quickLinksConfigurationName));
  }

  @GET
  @Path("{stackName}/versions/{stackVersion}/services/{serviceName}/artifacts/{artifactName}")
  @Produces(MediaType.TEXT_PLAIN)
  @ApiOperation(value = "Get stack service artifact details",
      nickname = "StacksService#getStackServiceArtifact",
      notes = "Returns the details of a stack service artifact.",
      response = StackArtifactResponse.class
  )
  @ApiImplicitParams({
      @ApiImplicitParam(name = QUERY_FIELDS, value = "Filter returned attributes",
          defaultValue = "Artifacts/*",
          dataType = DATA_TYPE_STRING, paramType = PARAM_TYPE_QUERY)
  })
  @ApiResponses(value = {
      @ApiResponse(code = HttpStatus.SC_OK, message = MSG_SUCCESSFUL_OPERATION),
      @ApiResponse(code = HttpStatus.SC_NOT_FOUND, message = MSG_RESOURCE_NOT_FOUND),
      @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = MSG_SERVER_ERROR)
  })
  public Response getStackServiceArtifact(@Context HttpHeaders headers, @Context UriInfo ui,
                                          @ApiParam @PathParam("stackName") String stackName,
                                          @ApiParam @PathParam("stackVersion") String stackVersion,
                                          @ApiParam @PathParam("serviceName") String serviceName,
                                          @ApiParam @PathParam("artifactName") String artifactName) {

    return handleRequest(headers, null, ui, Request.Type.GET,
