content: /**
   * 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
   * <p>
   * http://www.apache.org/licenses/LICENSE-2.0
   * <p>
   * 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.atlas.web.rest;
  
  import com.sun.jersey.core.header.FormDataContentDisposition;
  import com.sun.jersey.multipart.FormDataParam;
  import org.apache.atlas.AtlasErrorCode;
  import org.apache.atlas.SortOrder;
  import org.apache.atlas.annotation.Timed;
  import org.apache.atlas.bulkimport.BulkImportResponse;
  import org.apache.atlas.exception.AtlasBaseException;
  import org.apache.atlas.glossary.GlossaryService;
  import org.apache.atlas.glossary.GlossaryTermUtils;
  import org.apache.atlas.model.glossary.AtlasGlossary;
  import org.apache.atlas.model.glossary.AtlasGlossaryCategory;
  import org.apache.atlas.model.glossary.AtlasGlossaryTerm;
  import org.apache.atlas.model.glossary.relations.AtlasRelatedCategoryHeader;
  import org.apache.atlas.model.glossary.relations.AtlasRelatedTermHeader;
  import org.apache.atlas.model.instance.AtlasRelatedObjectId;
  import org.apache.atlas.utils.AtlasPerfTracer;
  import org.apache.atlas.web.util.Servlets;
  import org.apache.commons.collections.MapUtils;
  import org.slf4j.Logger;
  import org.springframework.stereotype.Service;
  
  import javax.inject.Inject;
  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 javax.ws.rs.core.StreamingOutput;
  
  import java.io.InputStream;
  import java.util.List;
  import java.util.Map;
  import java.util.Objects;
  import java.util.Set;
  
  @Path("v2/glossary")
  @Service
  @Consumes({Servlets.JSON_MEDIA_TYPE, MediaType.APPLICATION_JSON})
  @Produces({Servlets.JSON_MEDIA_TYPE, MediaType.APPLICATION_JSON})
  public class GlossaryREST {
      private static final Logger PERF_LOG = AtlasPerfTracer.getPerfLogger("rest.GlossaryREST");
  
      private final GlossaryService glossaryService;
  
      @Inject
      public GlossaryREST(final GlossaryService glossaryService) {
          this.glossaryService = glossaryService;
      }
  
      /**
       * Retrieve all glossaries registered with Atlas
       *
       * @param limit page size - by default there is no paging
       * @param offset offset for pagination purpose
       * @param sort Sort order, ASC (default) or DESC
       * @return List of glossary entities fitting the above criteria
       * @throws AtlasBaseException
       * @HTTP 200 List of existing glossaries fitting the search criteria or empty list if nothing matches
       */
      @GET
      @Timed
      public List<AtlasGlossary> getGlossaries(@DefaultValue("-1") @QueryParam("limit") final String limit, @DefaultValue("0") @QueryParam("offset") final String offset, @DefaultValue("ASC") @QueryParam("sort") final String sort) throws AtlasBaseException {
          AtlasPerfTracer perf = null;
  
          try {
              if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                  perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "GlossaryREST.getGlossaries()");
              }
  
              return glossaryService.getGlossaries(Integer.parseInt(limit), Integer.parseInt(offset), toSortOrder(sort));
          } finally {
              AtlasPerfTracer.log(perf);
          }
      }
  
      /**
       * Get a specific Glossary
       *
       * @param glossaryGuid unique glossary identifier
       * @return Glossary
       * @throws AtlasBaseException
       * @HTTP 200 If glossary with given guid exists
       * @HTTP 404 If glossary GUID is invalid
       */
      @GET
      @Path("/{glossaryGuid}")
      @Timed
      public AtlasGlossary getGlossary(@PathParam("glossaryGuid") String glossaryGuid) throws AtlasBaseException {
          Servlets.validateQueryParamLength("glossaryGuid", glossaryGuid);
  
          AtlasPerfTracer perf = null;
  
          try {
              if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                  perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "GlossaryREST.getGlossary(" + glossaryGuid + ")");
              }
  
              AtlasGlossary ret = glossaryService.getGlossary(glossaryGuid);
  
              if (ret == null) {
                  throw new AtlasBaseException(AtlasErrorCode.INSTANCE_GUID_NOT_FOUND);
              }
  
              return ret;
          } finally {
              AtlasPerfTracer.log(perf);
          }
      }
  
      /**
       * Get a specific Glossary
       *
       * @param glossaryGuid unique glossary identifier
       * @return Glossary
       * @throws AtlasBaseException
       * @HTTP 200 If glossary exists for given GUID
       * @HTTP 404 If glossary GUID is invalid
       */
      @GET
      @Path("/{glossaryGuid}/detailed")
      @Timed
      public AtlasGlossary.AtlasGlossaryExtInfo getDetailedGlossary(@PathParam("glossaryGuid") String glossaryGuid) throws AtlasBaseException {
          Servlets.validateQueryParamLength("glossaryGuid", glossaryGuid);
  
          AtlasPerfTracer perf = null;
  
          try {
              if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                  perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "GlossaryREST.getDetailedGlossary(" + glossaryGuid + ")");
              }
  
              AtlasGlossary.AtlasGlossaryExtInfo ret = glossaryService.getDetailedGlossary(glossaryGuid);
  
              if (ret == null) {
                  throw new AtlasBaseException(AtlasErrorCode.INSTANCE_GUID_NOT_FOUND);
              }
  
              return ret;
          } finally {
              AtlasPerfTracer.log(perf);
          }
      }
  
      /**
       * Get specific glossary term
       *
       * @param termGuid unique identifier for glossary term
       * @return Glossary term
       * @throws AtlasBaseException
       * @HTTP 200 If glossary term exists for given GUID
       * @HTTP 404 If glossary term GUID is invalid
       */
      @GET
      @Path("/term/{termGuid}")
      @Timed
      public AtlasGlossaryTerm getGlossaryTerm(@PathParam("termGuid") String termGuid) throws AtlasBaseException {
          Servlets.validateQueryParamLength("termGuid", termGuid);
  
          AtlasPerfTracer perf = null;
  
          try {
              if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                  perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "GlossaryREST.getGlossaryTerm(" + termGuid + ")");
              }
  
              AtlasGlossaryTerm ret = glossaryService.getTerm(termGuid);
  
              if (ret == null) {
                  throw new AtlasBaseException(AtlasErrorCode.INSTANCE_GUID_NOT_FOUND);
              }
  
              return ret;
          } finally {
              AtlasPerfTracer.log(perf);
          }
      }
  
      /**
       * Get specific glossary category
       *
       * @param categoryGuid unique identifier for glossary category
       * @return Glossary category
       * @throws AtlasBaseException
       * @HTTP 200 If glossary category exists for given GUID
       * @HTTP 404 If glossary category GUID is invalid
       */
      @GET
      @Path("/category/{categoryGuid}")
      @Timed
      public AtlasGlossaryCategory getGlossaryCategory(@PathParam("categoryGuid") String categoryGuid) throws AtlasBaseException {
          Servlets.validateQueryParamLength("categoryGuid", categoryGuid);
  
          AtlasPerfTracer perf = null;
  
          try {
              if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                  perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "GlossaryREST.getGlossaryCategory(" + categoryGuid + ")");
              }
  
              AtlasGlossaryCategory ret = glossaryService.getCategory(categoryGuid);
  
              if (ret == null) {
                  throw new AtlasBaseException(AtlasErrorCode.INSTANCE_GUID_NOT_FOUND);
              }
  
              return ret;
          } finally {
              AtlasPerfTracer.log(perf);
          }
      }
  
      /**
       * Create a glossary
       *
       * @param atlasGlossary Glossary definition, terms & categories can be anchored to a glossary
       * using the anchor attribute when creating the Term/Category
       * @return
       * @throws AtlasBaseException
       * @HTTP 200 If glossary creation was successful
       * @HTTP 400 If Glossary definition has invalid or missing information
       * @HTTP 409 If Glossary definition already exists (duplicate qualifiedName)
       */
      @POST
      @Timed
      public AtlasGlossary createGlossary(AtlasGlossary atlasGlossary) throws AtlasBaseException {
          AtlasPerfTracer perf = null;
          try {
              if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                  perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "GlossaryREST.createGlossary()");
              }
  
              return glossaryService.createGlossary(atlasGlossary);
          } finally {
              AtlasPerfTracer.log(perf);
          }
      }
  
      /**
       * Create a glossary term
       *
       * @param glossaryTerm Glossary term definition, a term must be anchored to a Glossary at the time of creation
       * optionally it can be categorized as well
       * @return
       * @throws AtlasBaseException
       * @HTTP 200 If glossary term creation was successful
       * @HTTP 400 If Glossary term definition has invalid or missing information
       * @HTTP 409 If Glossary term already exists (duplicate qualifiedName)
       */
      @POST
      @Path("/term")
      @Timed
      public AtlasGlossaryTerm createGlossaryTerm(AtlasGlossaryTerm glossaryTerm) throws AtlasBaseException {
          AtlasPerfTracer perf = null;
  
          try {
              if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                  perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "GlossaryREST.createGlossaryTerm()");
              }
  
              if (Objects.isNull(glossaryTerm.getAnchor())) {
                  throw new AtlasBaseException(AtlasErrorCode.MISSING_MANDATORY_ANCHOR);
              }
  
              return glossaryService.createTerm(glossaryTerm);
          } finally {
              AtlasPerfTracer.log(perf);
          }
      }
  
      /**
       * Create glossary terms in bulk
       *
       * @param glossaryTerm glossary term definitions
       * @return
       * @throws AtlasBaseException
       * @HTTP 200 If Bulk glossary terms creation was successful
       * @HTTP 400 If any glossary term definition has invalid or missing information
       */
      @POST
      @Path("/terms")
      @Timed
      public List<AtlasGlossaryTerm> createGlossaryTerms(List<AtlasGlossaryTerm> glossaryTerm) throws AtlasBaseException {
          AtlasPerfTracer perf = null;
  
          try {
              if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                  perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "GlossaryREST.createGlossaryTerms()");
              }
  
              for (AtlasGlossaryTerm term : glossaryTerm) {
                  if (Objects.isNull(term.getAnchor())) {
                      throw new AtlasBaseException(AtlasErrorCode.MISSING_MANDATORY_ANCHOR);
                  }
              }
  
              return glossaryService.createTerms(glossaryTerm);
          } finally {
              AtlasPerfTracer.log(perf);
          }
      }
  
      /**
       * Create glossary category
       *
       * @param glossaryCategory glossary category definition, a category must be anchored to a Glossary when creating
       * Optionally, terms belonging to the category and the hierarchy can also be defined during creation
       * @return
       * @throws AtlasBaseException
       * @HTTP 200 If glossary category creation was successful
       * @HTTP 400 If Glossary category definition has invalid or missing information
       * @HTTP 409 If Glossary category already exists (duplicate qualifiedName)
       */
      @POST
      @Path("/category")
      @Timed
      public AtlasGlossaryCategory createGlossaryCategory(AtlasGlossaryCategory glossaryCategory) throws AtlasBaseException {
          AtlasPerfTracer perf = null;
  
          try {
              if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                  perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "GlossaryREST.createGlossaryCategory()");
              }
  
              if (Objects.isNull(glossaryCategory.getAnchor())) {
                  throw new AtlasBaseException(AtlasErrorCode.MISSING_MANDATORY_ANCHOR);
              }
  
              return glossaryService.createCategory(glossaryCategory);
          } finally {
              AtlasPerfTracer.log(perf);
          }
      }
  
      /**
       * Create glossary category in bulk
       *
       * @param glossaryCategory glossary category definitions
       * @return
       * @throws AtlasBaseException
       * @HTTP 200 If BULK glossary category creation was successful
       * @HTTP 400 If ANY Glossary category definition has invalid or missing information
       */
      @POST
      @Path("/categories")
      @Timed
      public List<AtlasGlossaryCategory> createGlossaryCategories(List<AtlasGlossaryCategory> glossaryCategory) throws AtlasBaseException {
          AtlasPerfTracer perf = null;
  
          try {
              if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                  perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "GlossaryREST.createGlossaryCategories()");
              }
  
              for (AtlasGlossaryCategory category : glossaryCategory) {
                  if (Objects.isNull(category.getAnchor())) {
                      throw new AtlasBaseException(AtlasErrorCode.MISSING_MANDATORY_ANCHOR);
                  }
              }
  
              return glossaryService.createCategories(glossaryCategory);
          } finally {
              AtlasPerfTracer.log(perf);
          }
      }
  
      /**
       * Update the given glossary
       *
       * @param glossaryGuid unique identifier for glossary
       * @param updatedGlossary Updated glossary definition
       * @return Glossary
       * @throws AtlasBaseException
       * @HTTP 200 If glossary update was successful
       * @HTTP 404 If glossary guid in invalid
       * @HTTP 400 If Glossary definition has invalid or missing information
       */
      @PUT
      @Path("/{glossaryGuid}")
      @Timed
      public AtlasGlossary updateGlossary(@PathParam("glossaryGuid") String glossaryGuid, AtlasGlossary updatedGlossary) throws AtlasBaseException {
          Servlets.validateQueryParamLength("glossaryGuid", glossaryGuid);
  
          AtlasPerfTracer perf = null;
  
          try {
              if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                  perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "GlossaryREST.updateGlossary(" + glossaryGuid + ")");
              }
  
              updatedGlossary.setGuid(glossaryGuid);
  
              return glossaryService.updateGlossary(updatedGlossary);
          } finally {
              AtlasPerfTracer.log(perf);
          }
      }
  
      /**
       * Partially update the glossary
       *
       * @param glossaryGuid unique identifier for glossary term
       * @param partialUpdates Map containing keys as attribute names and values as corresponding attribute values
       * @return Updated glossary
       * @throws AtlasBaseException
       * @HTTP 200 If glossary partial update was successful
       * @HTTP 404 If glossary guid in invalid
       * @HTTP 400 If partial update parameters are invalid
       */
      @PUT
      @Path("/{glossaryGuid}/partial")
      @Timed
      public AtlasGlossary partialUpdateGlossary(@PathParam("glossaryGuid") String glossaryGuid, Map<String, String> partialUpdates) throws AtlasBaseException {
          Servlets.validateQueryParamLength("glossaryGuid", glossaryGuid);
  
          AtlasPerfTracer perf = null;
  
          try {
              if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                  perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "GlossaryREST.partialUpdateGlossary()");
              }
  
              if (MapUtils.isEmpty(partialUpdates)) {
                  throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, "PartialUpdates missing or empty");
              }
  
              AtlasGlossary glossary = glossaryService.getGlossary(glossaryGuid);
  
              for (Map.Entry<String, String> entry : partialUpdates.entrySet()) {
                  try {
                      glossary.setAttribute(entry.getKey(), entry.getValue());
                  } catch (IllegalArgumentException e) {
                      throw new AtlasBaseException(AtlasErrorCode.INVALID_PARTIAL_UPDATE_ATTR, entry.getKey(), "Glossary");
                  }
              }
  
              return glossaryService.updateGlossary(glossary);
          } finally {
              AtlasPerfTracer.log(perf);
          }
      }
  
      /**
       * Update the given glossary term
       *
       * @param termGuid unique identifier for glossary term
       * @param glossaryTerm updated glossary term
       * @return Updated glossary term
       * @throws AtlasBaseException
       * @HTTP 200 If glossary term update was successful
       * @HTTP 404 If glossary term guid in invalid
       * @HTTP 400 If Glossary temr definition has invalid or missing information
       */
      @PUT
      @Path("/term/{termGuid}")
      @Timed
      public AtlasGlossaryTerm updateGlossaryTerm(@PathParam("termGuid") String termGuid, AtlasGlossaryTerm glossaryTerm) throws AtlasBaseException {
          Servlets.validateQueryParamLength("termGuid", termGuid);
  
          AtlasPerfTracer perf = null;
  
          try {
              if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                  perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "GlossaryREST.updateGlossaryTerm()");
              }
  
              glossaryTerm.setGuid(termGuid);
  
              return glossaryService.updateTerm(glossaryTerm);
          } finally {
              AtlasPerfTracer.log(perf);
          }
      }
  
      /**
       * Partially update the glossary term
       *
       * @param termGuid unique identifier for glossary term
       * @param partialUpdates Map containing keys as attribute names and values as corresponding attribute values
       * @return Updated glossary term
       * @throws AtlasBaseException
       * @HTTP 200 If glossary partial update was successful
       * @HTTP 404 If glossary term guid in invalid
       * @HTTP 400 If partial attributes are invalid
       */
      @PUT
      @Path("/term/{termGuid}/partial")
      @Timed
      public AtlasGlossaryTerm partialUpdateGlossaryTerm(@PathParam("termGuid") String termGuid, Map<String, String> partialUpdates) throws AtlasBaseException {
          Servlets.validateQueryParamLength("termGuid", termGuid);
  
          AtlasPerfTracer perf = null;
  
          try {
              if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                  perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "GlossaryREST.partialUpdateGlossaryTerm()");
              }
  
              if (MapUtils.isEmpty(partialUpdates)) {
                  throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, "PartialUpdates missing or empty");
              }
  
              AtlasGlossaryTerm glossaryTerm = glossaryService.getTerm(termGuid);
  
              for (Map.Entry<String, String> entry : partialUpdates.entrySet()) {
                  try {
                      glossaryTerm.setAttribute(entry.getKey(), entry.getValue());
                  } catch (IllegalArgumentException e) {
                      throw new AtlasBaseException(AtlasErrorCode.INVALID_PARTIAL_UPDATE_ATTR, "Glossary Term", entry.getKey());
                  }
              }
  
              return glossaryService.updateTerm(glossaryTerm);
          } finally {
              AtlasPerfTracer.log(perf);
          }
      }
  
      /**
       * Update the given glossary category
       *
       * @param categoryGuid unique identifier for glossary category
       * @param glossaryCategory updated glossary category
       * @return glossary category
       * @throws AtlasBaseException
       * @HTTP 200 If glossary category partial update was successful
       * @HTTP 404 If glossary category guid in invalid
       * @HTTP 400 If Glossary category definition has invalid or missing information
       */
      @PUT
      @Path("/category/{categoryGuid}")
      @Timed
      public AtlasGlossaryCategory updateGlossaryCategory(@PathParam("categoryGuid") String categoryGuid, AtlasGlossaryCategory glossaryCategory) throws AtlasBaseException {
          Servlets.validateQueryParamLength("categoryGuid", categoryGuid);
  
          AtlasPerfTracer perf = null;
  
          try {
              if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                  perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "GlossaryREST.updateGlossaryCategory()");
              }
  
              glossaryCategory.setGuid(categoryGuid);
  
              return glossaryService.updateCategory(glossaryCategory);
          } finally {
              AtlasPerfTracer.log(perf);
          }
      }
  
      /**
       * Partially update the glossary category
       *
       * @param categoryGuid unique identifier for glossary term
       * @param partialUpdates Map containing keys as attribute names and values as corresponding attribute values
       * @return Updated glossary category
       * @throws AtlasBaseException
       * @HTTP 200 If glossary category partial update was successful
       * @HTTP 404 If glossary category guid in invalid
       * @HTTP 400 If category attributes are invalid
       */
      @PUT
      @Path("/category/{categoryGuid}/partial")
      @Timed
      public AtlasGlossaryCategory partialUpdateGlossaryCategory(@PathParam("categoryGuid") String categoryGuid, Map<String, String> partialUpdates) throws AtlasBaseException {
          Servlets.validateQueryParamLength("categoryGuid", categoryGuid);
  
          AtlasPerfTracer perf = null;
  
          try {
              if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                  perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "GlossaryREST.partialUpdateGlossaryCategory()");
              }
  
              if (MapUtils.isEmpty(partialUpdates)) {
                  throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, "PartialUpdates missing or empty");
              }
  
              AtlasGlossaryCategory glossaryCategory = glossaryService.getCategory(categoryGuid);
  
              for (Map.Entry<String, String> entry : partialUpdates.entrySet()) {
                  try {
                      glossaryCategory.setAttribute(entry.getKey(), entry.getValue());
                  } catch (IllegalArgumentException e) {
                      throw new AtlasBaseException(AtlasErrorCode.INVALID_PARTIAL_UPDATE_ATTR, "Glossary Category", entry.getKey());
                  }
              }
  
              return glossaryService.updateCategory(glossaryCategory);
          } finally {
              AtlasPerfTracer.log(perf);
          }
      }
  
      /**
       * Delete a glossary
       *
       * @param glossaryGuid unique identifier for glossary
       * @throws AtlasBaseException
       * @HTTP 204 If glossary delete was successful
       * @HTTP 404 If glossary guid in invalid
       */
      @DELETE
      @Path("/{glossaryGuid}")
      @Timed
      public void deleteGlossary(@PathParam("glossaryGuid") String glossaryGuid) throws AtlasBaseException {
          Servlets.validateQueryParamLength("glossaryGuid", glossaryGuid);
  
          AtlasPerfTracer perf = null;
  
          try {
              if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                  perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "GlossaryREST.deleteGlossary(" + glossaryGuid + ")");
              }
  
              glossaryService.deleteGlossary(glossaryGuid);
          } finally {
              AtlasPerfTracer.log(perf);
          }
      }
  
      /**
       * Delete a glossary term
       *
       * @param termGuid unique identifier for glossary term
       * @throws AtlasBaseException
       * @HTTP 204 If glossary term delete was successful
       * @HTTP 404 If glossary term guid in invalid
       */
      @DELETE
      @Path("/term/{termGuid}")
      @Timed
      public void deleteGlossaryTerm(@PathParam("termGuid") String termGuid) throws AtlasBaseException {
          Servlets.validateQueryParamLength("termGuid", termGuid);
  
          AtlasPerfTracer perf = null;
  
          try {
              if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                  perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "GlossaryREST.deleteGlossaryTerm(" + termGuid + ")");
              }
  
              glossaryService.deleteTerm(termGuid);
          } finally {
              AtlasPerfTracer.log(perf);
          }
      }
  
      /**
       * Delete a glossary category
       *
       * @param categoryGuid unique identifier for glossary category
       * @throws AtlasBaseException
       * @HTTP 204 If glossary category delete was successful
       * @HTTP 404 If glossary category guid in invalid
       */
      @DELETE
      @Path("/category/{categoryGuid}")
      @Timed
      public void deleteGlossaryCategory(@PathParam("categoryGuid") String categoryGuid) throws AtlasBaseException {
          Servlets.validateQueryParamLength("categoryGuid", categoryGuid);
  
          AtlasPerfTracer perf = null;
  
          try {
              if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                  perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "GlossaryREST.deleteGlossaryCategory(" + categoryGuid + ")");
              }
  
              glossaryService.deleteCategory(categoryGuid);
          } finally {
              AtlasPerfTracer.log(perf);
          }
      }
  
      /**
       * Get terms belonging to a specific glossary
       *
       * @param glossaryGuid unique identifier for glossary
       * @param limit page size - by default there is no paging
       * @param offset starting offset for loading terms
       * @param sort ASC(default) or DESC
       * @return List of terms associated with the glossary
       * @throws AtlasBaseException
       * @HTTP 200 List of glossary terms for the given glossary or an empty list
       * @HTTP 404 If glossary guid in invalid
       */
      @GET
      @Path("/{glossaryGuid}/terms")
      @Timed
      public List<AtlasGlossaryTerm> getGlossaryTerms(@PathParam("glossaryGuid") String glossaryGuid, @DefaultValue("-1") @QueryParam("limit") String limit, @DefaultValue("0") @QueryParam("offset") String offset, @DefaultValue("ASC") @QueryParam("sort") final String sort) throws AtlasBaseException {
          Servlets.validateQueryParamLength("glossaryGuid", glossaryGuid);
  
          AtlasPerfTracer perf = null;
  
          try {
              if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                  perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "GlossaryREST.getGlossaryTerms(" + glossaryGuid + ")");
              }
  
              return glossaryService.getGlossaryTerms(glossaryGuid, Integer.parseInt(offset), Integer.parseInt(limit), toSortOrder(sort));
          } finally {
              AtlasPerfTracer.log(perf);
          }
      }
  
      /**
       * Get term headers belonging to a specific glossary
       *
       * @param glossaryGuid unique identifier for glossary
       * @param limit page size - by default there is no paging
       * @param offset starting offset for loading terms
       * @param sort ASC(default) or DESC
       * @return List of terms associated with the glossary
       * @throws AtlasBaseException
       * @HTTP 200 List of glossary terms for the given glossary or an empty list
       * @HTTP 404 If glossary guid in invalid
       */
      @GET
      @Path("/{glossaryGuid}/terms/headers")
      @Timed
      public List<AtlasRelatedTermHeader> getGlossaryTermHeaders(@PathParam("glossaryGuid") String glossaryGuid, @DefaultValue("-1") @QueryParam("limit") String limit, @DefaultValue("0") @QueryParam("offset") String offset, @DefaultValue("ASC") @QueryParam("sort") final String sort) throws AtlasBaseException {
          Servlets.validateQueryParamLength("glossaryGuid", glossaryGuid);
  
          AtlasPerfTracer perf = null;
  
          try {
              if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                  perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "GlossaryREST.getGlossaryTermHeaders(" + glossaryGuid + ")");
              }
  
              return glossaryService.getGlossaryTermsHeaders(glossaryGuid, Integer.parseInt(offset), Integer.parseInt(limit), toSortOrder(sort));
          } finally {
              AtlasPerfTracer.log(perf);
          }
      }
  
      /**
       * Get the categories belonging to a specific glossary
       *
       * @param glossaryGuid unique identifier for glossary term
       * @param limit page size - by default there is no paging
       * @param offset offset for pagination purpose
       * @param sort ASC (default) or DESC
       * @return List of associated categories
       * @throws AtlasBaseException
       * @HTTP 200 List of glossary categories for the given glossary or an empty list
       * @HTTP 404 If glossary guid in invalid
       */
      @GET
      @Path("/{glossaryGuid}/categories")
      @Timed
      public List<AtlasGlossaryCategory> getGlossaryCategories(@PathParam("glossaryGuid") String glossaryGuid, @DefaultValue("-1") @QueryParam("limit") String limit, @DefaultValue("0") @QueryParam("offset") String offset, @DefaultValue("ASC") @QueryParam("sort") final String sort) throws AtlasBaseException {
          Servlets.validateQueryParamLength("glossaryGuid", glossaryGuid);
  
          AtlasPerfTracer perf = null;
  
          try {
              if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                  perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "GlossaryREST.getGlossaryCategories(" + glossaryGuid + ")");
              }
  
              return glossaryService.getGlossaryCategories(glossaryGuid, Integer.parseInt(offset), Integer.parseInt(limit), toSortOrder(sort));
          } finally {
              AtlasPerfTracer.log(perf);
          }
      }
  
      /**
       * Get the categories belonging to a specific glossary
       *
       * @param glossaryGuid unique identifier for glossary term
       * @param limit page size - by default there is no paging
       * @param offset offset for pagination purpose
       * @param sort ASC (default) or DESC
       * @return List of associated categories
       * @throws AtlasBaseException
       * @HTTP 200 List of glossary categories for the given glossary or an empty list
       * @HTTP 404 If glossary guid in invalid
       */
      @GET
      @Path("/{glossaryGuid}/categories/headers")
      @Timed
      public List<AtlasRelatedCategoryHeader> getGlossaryCategoriesHeaders(@PathParam("glossaryGuid") String glossaryGuid, @DefaultValue("-1") @QueryParam("limit") String limit, @DefaultValue("0") @QueryParam("offset") String offset, @DefaultValue("ASC") @QueryParam("sort") final String sort) throws AtlasBaseException {
          Servlets.validateQueryParamLength("glossaryGuid", glossaryGuid);
  
          AtlasPerfTracer perf = null;
  
          try {
              if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                  perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "GlossaryREST.getGlossaryCategoriesHeaders(" + glossaryGuid + ")");
              }
  
              return glossaryService.getGlossaryCategoriesHeaders(glossaryGuid, Integer.parseInt(offset), Integer.parseInt(limit), toSortOrder(sort));
          } finally {
              AtlasPerfTracer.log(perf);
          }
      }
  
      /**
       * Get all terms associated with the specific category
       *
       * @param categoryGuid unique identifier for glossary category
       * @param limit page size - by default there is no paging
       * @param offset offset for pagination purpose
       * @param sort ASC (default) or DESC
       * @return List of associated terms
       * @throws AtlasBaseException
       * @HTTP 200 List of terms for the given category or an empty list
       * @HTTP 404 If glossary category guid in invalid
       */
      @GET
      @Path("/category/{categoryGuid}/terms")
      @Timed
      public List<AtlasRelatedTermHeader> getCategoryTerms(@PathParam("categoryGuid") String categoryGuid, @DefaultValue("-1") @QueryParam("limit") String limit, @DefaultValue("0") @QueryParam("offset") String offset, @DefaultValue("ASC") @QueryParam("sort") final String sort) throws AtlasBaseException {
          Servlets.validateQueryParamLength("categoryGuid", categoryGuid);
  
          AtlasPerfTracer perf = null;
  
          try {
              if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                  perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "GlossaryREST.getCategoryTerms(" + categoryGuid + ")");
              }
  
              return glossaryService.getCategoryTerms(categoryGuid, Integer.parseInt(offset), Integer.parseInt(limit), toSortOrder(sort));
          } finally {
              AtlasPerfTracer.log(perf);
          }
      }
  
      /**
       * Get all related terms for a specific term
       *
       * @param termGuid unique identifier for glossary term
       * @param limit page size - by default there is no paging
       * @param offset offset for pagination purpose
       * @param sort ASC (default) or DESC
       * @return List of all related terms
       * @throws AtlasBaseException
       * @HTTP 200 List of related glossary terms for the given glossary or an empty list
       * @HTTP 404 If glossary term guid in invalid
       */
      @GET
      @Path("/terms/{termGuid}/related")
      @Timed
      public Map<AtlasGlossaryTerm.Relation, Set<AtlasRelatedTermHeader>> getRelatedTerms(@PathParam("termGuid") String termGuid, @DefaultValue("-1") @QueryParam("limit") String limit, @DefaultValue("0") @QueryParam("offset") String offset, @DefaultValue("ASC") @QueryParam("sort") final String sort) throws AtlasBaseException {
          Servlets.validateQueryParamLength("termGuid", termGuid);
  
          AtlasPerfTracer perf = null;
  
          try {
              if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                  perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "GlossaryREST.getRelatedTermsInfo(" + termGuid + ")");
              }
  
              return glossaryService.getRelatedTerms(termGuid, Integer.parseInt(offset), Integer.parseInt(limit), toSortOrder(sort));
          } finally {
              AtlasPerfTracer.log(perf);
          }
      }
  
      /**
       * Get all entity headers assigned with the specified term
       *
       * @param termGuid GUID of the term
       * @param limit page size - by default there is no paging
       * @param offset offset for pagination purpose
       * @param sort ASC (default) or DESC
       * @return
       * @throws AtlasBaseException
       * @HTTP 200 List of entity headers (if any) for the given glossary or an empty list
       * @HTTP 404 If glossary term guid in invalid
       */
      @GET
      @Path("/terms/{termGuid}/assignedEntities")
      @Timed
      public List<AtlasRelatedObjectId> getEntitiesAssignedWithTerm(@PathParam("termGuid") String termGuid, @DefaultValue("-1") @QueryParam("limit") String limit, @DefaultValue("0") @QueryParam("offset") String offset, @DefaultValue("ASC") @QueryParam("sort") final String sort) throws AtlasBaseException {
          Servlets.validateQueryParamLength("termGuid", termGuid);
  
          AtlasPerfTracer perf = null;
  
          try {
              if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                  perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "GlossaryREST.getEntitiesAssignedWithTerm(" + termGuid + ")");
              }
  
              return glossaryService.getAssignedEntities(termGuid, Integer.parseInt(offset), Integer.parseInt(limit), toSortOrder(sort));
          } finally {
              AtlasPerfTracer.log(perf);
          }
      }
  
      /**
       * Assign the given term to the provided list of entity headers
       *
       * @param termGuid Glossary term GUID
       * @param relatedObjectIds Related Entity IDs to which the term has to be associated
       * @throws AtlasBaseException
       * @HTTP 204 If the term assignment was successful
       * @HTTP 400 If ANY of the entity header is invalid
       * @HTTP 404 If glossary guid in invalid
       */
      @POST
      @Path("/terms/{termGuid}/assignedEntities")
      @Timed
      public void assignTermToEntities(@PathParam("termGuid") String termGuid, List<AtlasRelatedObjectId> relatedObjectIds) throws AtlasBaseException {
          Servlets.validateQueryParamLength("termGuid", termGuid);
  
          AtlasPerfTracer perf = null;
  
          try {
              if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                  perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "GlossaryREST.assignTermToEntities(" + termGuid + ")");
              }
  
              glossaryService.assignTermToEntities(termGuid, relatedObjectIds);
          } finally {
              AtlasPerfTracer.log(perf);
          }
      }
  
      /**
       * Remove the term assignment for the given list of entity headers
       *
       * @param termGuid Glossary term GUID
       * @param relatedObjectIds List of related entity IDs from which the term has to be dissociated
       * @throws AtlasBaseException
       * @HTTP 204 If glossary term dissociation was successful
       * @HTTP 400 If ANY of the entity header is invalid
       * @HTTP 404 If glossary term guid in invalid
       */
      @DELETE
      @Path("/terms/{termGuid}/assignedEntities")
      @Timed
      public void removeTermAssignmentFromEntities(@PathParam("termGuid") String termGuid, List<AtlasRelatedObjectId> relatedObjectIds) throws AtlasBaseException {
          removeTermFromGlossary(termGuid, relatedObjectIds);
      }
  
      /**
       * Remove the term assignment for the given list of entity headers
       *
       * @param termGuid Glossary term GUID
       * @param relatedObjectIds List of related entity IDs from which the term has to be dissociated
       * @throws AtlasBaseException
       * @HTTP 204 If glossary term dissociation was successful
       * @HTTP 400 If ANY of the entity header is invalid
       * @HTTP 404 If glossary term guid in invalid
       */
      @PUT
      @Path("/terms/{termGuid}/assignedEntities")
      @Timed
      public void disassociateTermAssignmentFromEntities(@PathParam("termGuid") String termGuid, List<AtlasRelatedObjectId> relatedObjectIds) throws AtlasBaseException {
          removeTermFromGlossary(termGuid, relatedObjectIds);
      }
  
      /**
       * Get all related categories (parent and children)
       *
       * @param categoryGuid unique identifier for glossary category
       * @param limit page size - by default there is no paging
       * @param offset offset for pagination purpose
       * @param sort ASC (default) or DESC
       * @return List of related categories
       * @throws AtlasBaseException
       */
      @GET
      @Path("/category/{categoryGuid}/related")
      @Timed
      public Map<String, List<AtlasRelatedCategoryHeader>> getRelatedCategories(@PathParam("categoryGuid") String categoryGuid, @DefaultValue("-1") @QueryParam("limit") String limit, @DefaultValue("0") @QueryParam("offset") String offset, @DefaultValue("ASC") @QueryParam("sort") final String sort) throws AtlasBaseException {
          AtlasPerfTracer perf = null;
  
          try {
              if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                  perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "GlossaryREST.getRelatedCategories()");
              }
  
              return glossaryService.getRelatedCategories(categoryGuid, Integer.parseInt(offset), Integer.parseInt(limit), toSortOrder(sort));
          } finally {
              AtlasPerfTracer.log(perf);
          }
      }
  
      /**
       * Get sample template for uploading/creating bulk AtlasGlossaryTerm
       *
       * @return Template File
       * @HTTP 400 If the provided fileType is not supported
       */
      @GET
      @Path("/import/template")
      @Produces(MediaType.APPLICATION_OCTET_STREAM)
      public StreamingOutput produceTemplate() {
          return outputStream -> outputStream.write(GlossaryTermUtils.getGlossaryTermHeaders().getBytes());
      }
  
      /**
       * Upload glossary file for creating AtlasGlossaryTerms in bulk
       *
       * @param inputStream InputStream of file
       * @param fileDetail FormDataContentDisposition metadata of file
       * @return
       * @throws AtlasBaseException
       * @HTTP 200 If glossary term creation was successful
       * @HTTP 400 If Glossary term definition has invalid or missing information
       * @HTTP 409 If Glossary term already exists (duplicate qualifiedName)
       */
      @POST
      @Path("/import")
      @Consumes(MediaType.MULTIPART_FORM_DATA)
      @Timed
      public BulkImportResponse importGlossaryData(@FormDataParam("file") InputStream inputStream, @FormDataParam("file") FormDataContentDisposition fileDetail) throws AtlasBaseException {
          return glossaryService.importGlossaryData(inputStream, fileDetail.getFileName());
      }
  
      private void removeTermFromGlossary(String termGuid, List<AtlasRelatedObjectId> relatedObjectIds) throws AtlasBaseException {
          Servlets.validateQueryParamLength("termGuid", termGuid);
  
          AtlasPerfTracer perf = null;
  
          try {
              if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
                  perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "GlossaryREST.removeTermFromGlossary(" + termGuid + ")");
              }
  
              glossaryService.removeTermFromEntities(termGuid, relatedObjectIds);
          } finally {
              AtlasPerfTracer.log(perf);
          }
      }
  
      private SortOrder toSortOrder(final String sort) {
          SortOrder ret = SortOrder.ASCENDING;
  
          if (!"ASC".equals(sort)) {
              if ("DESC".equals(sort)) {
                  ret = SortOrder.DESCENDING;
              }
          }
  
          return ret;
      }
  }
  