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
   *
   *     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.orm.dao;
  
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.List;
  import java.util.Map;
  
  import jakarta.persistence.EntityManager;
  import jakarta.persistence.NoResultException;
  import jakarta.persistence.TypedQuery;
  import jakarta.persistence.criteria.CriteriaBuilder;
  import jakarta.persistence.criteria.CriteriaQuery;
  import jakarta.persistence.criteria.Predicate;
  import jakarta.persistence.criteria.Root;
  
  import org.apache.ambari.server.orm.RequiresSession;
  import org.apache.ambari.server.orm.entities.ServiceComponentDesiredStateEntity;
  import org.apache.ambari.server.orm.entities.ServiceComponentVersionEntity;
  import org.apache.commons.collections4.MapUtils;
  
  import com.google.inject.Inject;
  import com.google.inject.Provider;
  import com.google.inject.Singleton;
  import com.google.inject.persist.Transactional;
  
  @Singleton
  public class ServiceComponentDesiredStateDAO {
    @Inject
    Provider<EntityManager> entityManagerProvider;
  
    /**
     * DAO utilities for dealing mostly with {@link TypedQuery} results.
     */
    @Inject
    private DaoUtils daoUtils;
  
    /**
     * Gets a {@link ServiceComponentDesiredStateEntity} by its PK ID.
     *
     * @param id
     *          the ID.
     * @return the entity or {@code null} if it does not exist.
     */
    @RequiresSession
    public ServiceComponentDesiredStateEntity findById(long id) {
      return entityManagerProvider.get().find(ServiceComponentDesiredStateEntity.class, id);
    }
  
    @RequiresSession
    public List<ServiceComponentDesiredStateEntity> findAll() {
      TypedQuery<ServiceComponentDesiredStateEntity> query =
        entityManagerProvider.get().
          createQuery("SELECT scd from ServiceComponentDesiredStateEntity scd", ServiceComponentDesiredStateEntity.class);
      try {
        return query.getResultList();
      } catch (NoResultException ignored) {
      }
      return null;
    }
  
    /**
     * Finds a {@link ServiceComponentDesiredStateEntity} by a combination of
     * cluster, service, and component.
     *
     * @param clusterId
     *          the cluster ID
     * @param serviceName
     *          the service name (not {@code null})
     * @param componentName
     *          the component name (not {@code null})
     */
    @RequiresSession
    public ServiceComponentDesiredStateEntity findByName(long clusterId, String serviceName,
        String componentName) {
      EntityManager entityManager = entityManagerProvider.get();
      TypedQuery<ServiceComponentDesiredStateEntity> query = entityManager.createNamedQuery(
          "ServiceComponentDesiredStateEntity.findByName", ServiceComponentDesiredStateEntity.class);
  
      query.setParameter("clusterId", clusterId);
      query.setParameter("serviceName", serviceName);
      query.setParameter("componentName", componentName);
  
      ServiceComponentDesiredStateEntity entity = null;
      List<ServiceComponentDesiredStateEntity> entities = daoUtils.selectList(query);
      if (null != entities && !entities.isEmpty()) {
        entity = entities.get(0);
      }
  
      return entity;
    }
  
    /**
     * Finds a {@link ServiceComponentDesiredStateEntity} by a combination of cluster id, service and component names.
     * @param serviceComponentDesiredStates - component names mapped by service names and cluster ids.
     * @return all found entities according to input map.
     */
    @RequiresSession
    public List<ServiceComponentDesiredStateEntity> findByNames(Map<Long, Map<String, List<String>>> serviceComponentDesiredStates) {
      if (MapUtils.isEmpty(serviceComponentDesiredStates)) {
        return Collections.emptyList();
      }
      CriteriaBuilder cb = entityManagerProvider.get().getCriteriaBuilder();
      CriteriaQuery<ServiceComponentDesiredStateEntity> cq = cb.createQuery(ServiceComponentDesiredStateEntity.class);
      Root<ServiceComponentDesiredStateEntity> desiredStates = cq.from(ServiceComponentDesiredStateEntity.class);
  
      List<Predicate> clusters = new ArrayList<>();
      for (Map.Entry<Long, Map<String, List<String>>> cluster : serviceComponentDesiredStates.entrySet()) {
        List<Predicate> services = new ArrayList<>();
        for (Map.Entry<String, List<String>> service : cluster.getValue().entrySet()) {
          services.add(cb.and(cb.equal(desiredStates.get("serviceName"), service.getKey()),
              desiredStates.get("componentName").in(service.getValue())));
        }
        clusters.add(cb.and(cb.equal(desiredStates.get("clusterId"), cluster.getKey()),
            cb.or(services.toArray(new Predicate[services.size()]))));
      }
      cq.where(cb.or(clusters.toArray(new Predicate[clusters.size()])));
      TypedQuery<ServiceComponentDesiredStateEntity> query = entityManagerProvider.get().createQuery(cq);
      return daoUtils.selectList(query);
    }
  
    @Transactional
    public void refresh(ServiceComponentDesiredStateEntity serviceComponentDesiredStateEntity) {
      entityManagerProvider.get().refresh(serviceComponentDesiredStateEntity);
    }
  
    @Transactional
    public void create(ServiceComponentDesiredStateEntity serviceComponentDesiredStateEntity) {
      entityManagerProvider.get().persist(serviceComponentDesiredStateEntity);
    }
  
    @Transactional
    public ServiceComponentDesiredStateEntity merge(ServiceComponentDesiredStateEntity serviceComponentDesiredStateEntity) {
      return entityManagerProvider.get().merge(serviceComponentDesiredStateEntity);
    }
  
    @Transactional
    public void remove(ServiceComponentDesiredStateEntity serviceComponentDesiredStateEntity) {
      entityManagerProvider.get().remove(serviceComponentDesiredStateEntity);
    }
  
    @Transactional
    public void removeByName(long clusterId, String serviceName, String componentName) {
      ServiceComponentDesiredStateEntity entity = findByName(clusterId, serviceName, componentName);
      if (null != entity) {
        entityManagerProvider.get().remove(entity);
      }
    }
  
    /**
     * @param clusterId     the cluster id
     * @param serviceName   the service name
     * @param componentName the component name
     * @return the list of repository versions for a component
     */
    @RequiresSession
    public List<ServiceComponentVersionEntity> findVersions(long clusterId, String serviceName,
        String componentName) {
      EntityManager entityManager = entityManagerProvider.get();
      TypedQuery<ServiceComponentVersionEntity> query = entityManager.createNamedQuery(
          "ServiceComponentVersionEntity.findByComponent", ServiceComponentVersionEntity.class);
  
      query.setParameter("clusterId", clusterId);
      query.setParameter("serviceName", serviceName);
      query.setParameter("componentName", componentName);
  
      return daoUtils.selectList(query);
    }
  
    /**
     * Gets a specific version for a component
     * @param clusterId     the cluster id
     * @param serviceName   the service name
     * @param componentName the component name
     * @param version       the component version to find
     * @return the version entity, or {@code null} if not found
     */
    @RequiresSession
    public ServiceComponentVersionEntity findVersion(long clusterId, String serviceName,
        String componentName, String version) {
  
      EntityManager entityManager = entityManagerProvider.get();
      TypedQuery<ServiceComponentVersionEntity> query = entityManager.createNamedQuery(
          "ServiceComponentVersionEntity.findByComponentAndVersion", ServiceComponentVersionEntity.class);
  
      query.setParameter("clusterId", clusterId);
      query.setParameter("serviceName", serviceName);
      query.setParameter("componentName", componentName);
      query.setParameter("repoVersion", version);
  
      return daoUtils.selectSingle(query);
    }
  
  }
  