All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jboss.pnc.facade.providers.ArtifactProviderImpl Maven / Gradle / Ivy

There is a newer version: 3.0.4
Show newest version
/**
 * JBoss, Home of Professional Open Source.
 * Copyright 2014-2020 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed 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.jboss.pnc.facade.providers;

import org.jboss.pnc.common.maven.Gav;
import org.jboss.pnc.coordinator.maintenance.BlacklistAsyncInvoker;
import org.jboss.pnc.dto.ArtifactRef;
import org.jboss.pnc.dto.ArtifactRevision;
import org.jboss.pnc.dto.User;
import org.jboss.pnc.dto.response.ArtifactInfo;
import org.jboss.pnc.dto.response.Page;
import org.jboss.pnc.enums.ArtifactQuality;
import org.jboss.pnc.enums.BuildCategory;
import org.jboss.pnc.enums.RepositoryType;
import org.jboss.pnc.facade.providers.api.ArtifactProvider;
import org.jboss.pnc.facade.util.UserService;
import org.jboss.pnc.facade.validation.ConflictedEntryException;
import org.jboss.pnc.facade.validation.DTOValidationException;
import org.jboss.pnc.facade.validation.InvalidEntityException;
import org.jboss.pnc.facade.validation.RepositoryViolationException;
import org.jboss.pnc.mapper.api.ArtifactMapper;
import org.jboss.pnc.mapper.api.ArtifactRevisionMapper;
import org.jboss.pnc.mapper.api.BuildMapper;
import org.jboss.pnc.mapper.api.UserMapper;
import org.jboss.pnc.model.Artifact;
import org.jboss.pnc.model.ArtifactAudited;
import org.jboss.pnc.model.Artifact_;
import org.jboss.pnc.model.IdRev;
import org.jboss.pnc.model.TargetRepository;
import org.jboss.pnc.model.TargetRepository_;
import org.jboss.pnc.spi.datastore.predicates.ArtifactPredicates;
import org.jboss.pnc.spi.datastore.repositories.ArtifactAuditedRepository;
import org.jboss.pnc.spi.datastore.repositories.ArtifactRepository;
import org.jboss.pnc.spi.datastore.repositories.api.Predicate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.Tuple;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Root;

import java.time.Instant;
import java.util.ArrayList;
import java.util.Date;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import static org.jboss.pnc.common.util.StreamHelper.nullableStreamOf;
import static org.jboss.pnc.facade.providers.api.UserRoles.SYSTEM_USER;
import static org.jboss.pnc.spi.datastore.predicates.ArtifactPredicates.withBuildRecordId;
import static org.jboss.pnc.spi.datastore.predicates.ArtifactPredicates.withDependantBuildRecordId;
import static org.jboss.pnc.spi.datastore.predicates.ArtifactPredicates.withMd5;
import static org.jboss.pnc.spi.datastore.predicates.ArtifactPredicates.withSha1;
import static org.jboss.pnc.spi.datastore.predicates.ArtifactPredicates.withSha256;

/**
 * @author Jan Michalov
 */
@PermitAll
@Stateless
@SuppressWarnings("deprecation")
public class ArtifactProviderImpl
        extends AbstractUpdatableProvider
        implements ArtifactProvider {

    private static Logger logger = LoggerFactory.getLogger(ArtifactProviderImpl.class);

    private static final EnumSet USER_ALLOWED_ARTIFACT_QUALITIES = EnumSet
            .of(ArtifactQuality.NEW, ArtifactQuality.VERIFIED, ArtifactQuality.TESTED, ArtifactQuality.DEPRECATED);

    private static final EnumSet ADMIN_ALLOWED_ARTIFACT_QUALITIES = EnumSet.of(
            ArtifactQuality.NEW,
            ArtifactQuality.VERIFIED,
            ArtifactQuality.TESTED,
            ArtifactQuality.DEPRECATED,
            ArtifactQuality.BLACKLISTED,
            ArtifactQuality.DELETED);

    private static final EnumSet NOT_MODIFIABLE_ARTIFACT_QUALITIES = EnumSet
            .of(ArtifactQuality.DELETED, ArtifactQuality.BLACKLISTED);

    private static final EnumSet DA_SYNCRONIZED_ARTIFACT_QUALITIES = EnumSet
            .of(ArtifactQuality.DELETED, ArtifactQuality.BLACKLISTED);

    private static final String MVN_BLACKLIST_JSON_PAYLOAD = "{\"groupId\":\"%s\",\"artifactId\":\"%s\",\"version\":\"%s\"}";

    private ArtifactRevisionMapper artifactRevisionMapper;

    private ArtifactAuditedRepository artifactAuditedRepository;

    private BlacklistAsyncInvoker blacklistAsyncInvoker;

    private UserService userService;

    private UserMapper userMapper;

    @Inject
    private EntityManager em;

    @Inject
    public ArtifactProviderImpl(
            ArtifactRepository repository,
            ArtifactMapper mapper,
            ArtifactRevisionMapper artifactRevisionMapper,
            ArtifactAuditedRepository artifactAuditedRepository,
            BlacklistAsyncInvoker blacklistAsyncInvoker,
            UserService userService,
            UserMapper userMapper) {
        super(repository, mapper, Artifact.class);
        this.artifactRevisionMapper = artifactRevisionMapper;
        this.artifactAuditedRepository = artifactAuditedRepository;
        this.blacklistAsyncInvoker = blacklistAsyncInvoker;
        this.userService = userService;
        this.userMapper = userMapper;
    }

    @Override
    public Page getAll(
            int pageIndex,
            int pageSize,
            String sortingRsql,
            String query,
            Optional sha256,
            Optional md5,
            Optional sha1) {
        return queryForCollection(
                pageIndex,
                pageSize,
                sortingRsql,
                query,
                withSha256(sha256),
                withMd5(md5),
                withSha1(sha1));
    }

    @Override
    public Page getAllFiltered(
            int pageIndex,
            int pageSize,
            Optional identifierPattern,
            Set qualities,
            Optional repoType,
            Set buildCategories) {

        CriteriaBuilder cb = em.getCriteriaBuilder();

        CriteriaQuery query = artifactInfoQuery(cb, identifierPattern, qualities, repoType, buildCategories);
        int offset = pageIndex * pageSize;
        List artifacts = em.createQuery(query)
                .setMaxResults(pageSize)
                .setFirstResult(offset)
                .getResultList()
                .stream()
                .map(this::mapTupleToArtifactInfo)
                .collect(Collectors.toList());

        Predicate[] predicates = getPredicates(identifierPattern, qualities, repoType, buildCategories);
        int totalHits = repository.count(predicates);
        int totalPages = (totalHits + pageSize - 1) / pageSize;

        return new Page<>(pageIndex, pageSize, totalPages, totalHits, artifacts);
    }

    @Override
    @RolesAllowed(SYSTEM_USER)
    public org.jboss.pnc.dto.Artifact store(org.jboss.pnc.dto.Artifact restEntity) throws DTOValidationException {
        org.jboss.pnc.model.User currentUser = userService.currentUser();
        User user = userMapper.toDTO(currentUser);
        Instant now = Instant.now();
        return super.store(
                restEntity.toBuilder()
                        .creationUser(user)
                        .modificationUser(user)
                        .creationTime(now)
                        .modificationTime(now)
                        .build());
    }

    @Override
    protected void preUpdate(Artifact dbEntity, org.jboss.pnc.dto.Artifact restEntity) {
        if (!equalAuditedValues(dbEntity, restEntity)) {
            // Changes to audit, set the modificationUser and modificationTime to new values
            org.jboss.pnc.model.User currentUser = userService.currentUser();
            dbEntity.setModificationUser(currentUser);
            dbEntity.setModificationTime(new Date());
        }
    }

    private boolean equalAuditedValues(Artifact persisted, org.jboss.pnc.dto.Artifact toUpdate) {
        return Objects.equals(persisted.getArtifactQuality(), toUpdate.getArtifactQuality())
                && Objects.equals(persisted.getQualityLevelReason(), toUpdate.getQualityLevelReason());
    }

    @Override
    public ArtifactRevision createQualityLevelRevision(String id, String quality, String reason)
            throws DTOValidationException {

        boolean isLoggedInUserSystemUser = userService.hasLoggedInUserRole(SYSTEM_USER);

        ArtifactQuality newQuality = validateProvidedArtifactQuality(quality, isLoggedInUserSystemUser);

        org.jboss.pnc.dto.Artifact artifact = getSpecific(id);
        if (artifact == null) {
            throw new InvalidEntityException("Artifact with id: " + id + " does not exist.");
        }

        validateIfArtifactQualityIsModifiable(artifact, newQuality);

        update(id, artifact.toBuilder().artifactQuality(newQuality).qualityLevelReason(reason).build());

        ArtifactAudited latestRevision = artifactAuditedRepository.findLatestById(Integer.parseInt(id));
        if (latestRevision == null) {
            throw new RepositoryViolationException("Entity should exist in the DB");
        }

        if (DA_SYNCRONIZED_ARTIFACT_QUALITIES.contains(newQuality)) {
            String jsonPayload = createBlacklistJSONPayload(artifact);
            blacklistAsyncInvoker.notifyBlacklistToDA(jsonPayload);
        }

        return artifactRevisionMapper.toDTO(latestRevision);
    }

    private String createBlacklistJSONPayload(org.jboss.pnc.dto.Artifact artifact) {
        switch (artifact.getTargetRepository().getRepositoryType()) {
            case MAVEN: {
                Gav gav = Gav.parse(artifact.getIdentifier());
                return String
                        .format(MVN_BLACKLIST_JSON_PAYLOAD, gav.getGroupId(), gav.getArtifactId(), gav.getVersion());
            }
            default:
                return null;
        }
    }

    @Override
    @DenyAll
    public void delete(String id) throws DTOValidationException {
        throw new UnsupportedOperationException("Direct artifact manipulation is not available.");
    }

    @Override
    public Page getBuiltArtifactsForBuild(
            int pageIndex,
            int pageSize,
            String sortingRsql,
            String query,
            String buildId) {

        return queryForCollection(
                pageIndex,
                pageSize,
                sortingRsql,
                query,
                withBuildRecordId(BuildMapper.idMapper.toEntity(buildId)));
    }

    @Override
    public Page getDependantArtifactsForBuild(
            int pageIndex,
            int pageSize,
            String sortingRsql,
            String query,
            String buildId) {
        return queryForCollection(
                pageIndex,
                pageSize,
                sortingRsql,
                query,
                withDependantBuildRecordId(BuildMapper.idMapper.toEntity(buildId)));
    }

    @Override
    public Page getRevisions(int pageIndex, int pageSize, String id) {
        List auditedBuildConfigs = artifactAuditedRepository
                .findAllByIdOrderByRevDesc(Integer.valueOf(id));

        List toReturn = nullableStreamOf(auditedBuildConfigs).map(artifactRevisionMapper::toDTO)
                .skip(pageIndex * pageSize)
                .limit(pageSize)
                .collect(Collectors.toList());

        int totalHits = auditedBuildConfigs.size();
        int totalPages = (totalHits + pageSize - 1) / pageSize;

        return new Page<>(pageIndex, pageSize, totalPages, totalHits, toReturn);
    }

    @Override
    public ArtifactRevision getRevision(String id, Integer rev) {
        IdRev idRev = new IdRev(Integer.valueOf(id), rev);
        ArtifactAudited auditedArtifact = artifactAuditedRepository.queryById(idRev);

        return artifactRevisionMapper.toDTO(auditedArtifact);
    }

    private CriteriaQuery artifactInfoQuery(
            CriteriaBuilder cb,
            Optional identifierPattern,
            Set qualities,
            Optional repoType,
            Set buildCategories) {

        CriteriaQuery query = cb.createTupleQuery();

        Root artifact = query.from(org.jboss.pnc.model.Artifact.class);
        Path repository = artifact.get(Artifact_.targetRepository);
        query.multiselect(
                artifact.get(Artifact_.id),
                artifact.get(Artifact_.identifier),
                artifact.get(Artifact_.artifactQuality),
                repository.get(TargetRepository_.repositoryType),
                artifact.get(Artifact_.buildCategory));

        List predicates = new ArrayList<>(4);
        if (identifierPattern.isPresent()) {
            javax.persistence.criteria.Predicate withIdentifierLike = cb
                    .like(artifact.get(Artifact_.identifier), identifierPattern.get().replace("*", "%"));
            predicates.add(withIdentifierLike);
        }

        if (!qualities.isEmpty()) {
            javax.persistence.criteria.Predicate withQualityIn = artifact.get(Artifact_.artifactQuality).in(qualities);
            predicates.add(withQualityIn);
        }

        if (!buildCategories.isEmpty()) {
            javax.persistence.criteria.Predicate withBuildCategoryIn = artifact.get(Artifact_.buildCategory)
                    .in(buildCategories);
            predicates.add(withBuildCategoryIn);
        }

        if (repoType.isPresent()) {
            javax.persistence.criteria.Predicate withRepoType = cb.equal(
                    artifact.join(Artifact_.targetRepository).get(TargetRepository_.repositoryType),
                    repoType.get());
            predicates.add(withRepoType);
        }

        query.where(cb.and(predicates.toArray(new javax.persistence.criteria.Predicate[predicates.size()])));

        query.orderBy(cb.asc(artifact.get(Artifact_.id)));

        return query;
    }

    private Predicate[] getPredicates(
            Optional identifierPattern,
            Set qualities,
            Optional repoType,
            Set buildCategories) {

        List> predicates = new ArrayList<>(4);

        if (identifierPattern.isPresent()) {
            Predicate withIdentifierLike = ArtifactPredicates
                    .withIdentifierLike(identifierPattern.get().replace("*", "%"));
            predicates.add(withIdentifierLike);
        }

        if (!qualities.isEmpty()) {
            Predicate withQualityIn = ArtifactPredicates.withArtifactQualityIn(qualities);
            predicates.add(withQualityIn);
        }

        if (!buildCategories.isEmpty()) {
            Predicate withBuildCategoryIn = ArtifactPredicates.withBuildCategoryIn(buildCategories);
            predicates.add(withBuildCategoryIn);
        }

        if (repoType.isPresent()) {
            Predicate withRepoType = ArtifactPredicates.withTargetRepositoryRepositoryType(repoType.get());
            predicates.add(withRepoType);
        }

        return predicates.toArray(new Predicate[predicates.size()]);
    }

    private ArtifactInfo mapTupleToArtifactInfo(Tuple tuple) {
        return ArtifactInfo.builder()
                .id(tuple.get(0).toString())
                .identifier(tuple.get(1).toString())
                .artifactQuality((ArtifactQuality) tuple.get(2))
                .repositoryType((RepositoryType) tuple.get(3))
                .buildCategory((BuildCategory) tuple.get(4))
                .build();
    }

    private ArtifactQuality validateProvidedArtifactQuality(String quality, boolean isLoggedInUserSystemUser) {

        ArtifactQuality newQuality;
        try {
            newQuality = ArtifactQuality.valueOf(quality.toUpperCase());
        } catch (IllegalArgumentException e) {
            throw new InvalidEntityException("Artifact quality: " + quality + " does not exist.");
        }

        // User can specify NEW, TESTED, VERIFIED, DEPRECATED quality levels; admins can also specify DELETED and
        // BLACKLISTED
        EnumSet allowedQualities = isLoggedInUserSystemUser ? ADMIN_ALLOWED_ARTIFACT_QUALITIES
                : USER_ALLOWED_ARTIFACT_QUALITIES;

        if (!allowedQualities.contains(newQuality)) {
            throw new InvalidEntityException("Artifact quality level can be changed only to " + allowedQualities);
        }

        return newQuality;
    }

    private void validateIfArtifactQualityIsModifiable(
            org.jboss.pnc.dto.Artifact artifact,
            ArtifactQuality newQuality) {

        // If the quality level is not changing (e.g. in a bulk update after a single update), ignore the checks below
        if (artifact.getArtifactQuality().equals(newQuality)) {
            return;
        }

        // Artifacts with DELETED, BLACKLISTED quality levels cannot be changed
        if (NOT_MODIFIABLE_ARTIFACT_QUALITIES.contains(artifact.getArtifactQuality())) {
            throw new ConflictedEntryException(
                    "Artifact " + artifact.getId() + " with quality " + artifact.getArtifactQuality()
                            + " cannot be changed to another quality level.",
                    Artifact.class,
                    artifact.getId());
        }

        // If the artifact is TEMPORARY, quality level can only change to DELETED
        if (ArtifactQuality.TEMPORARY.equals(artifact.getArtifactQuality())
                && !newQuality.equals(ArtifactQuality.DELETED)) {

            throw new ConflictedEntryException(
                    "Temporary artifact " + artifact.getId() + " can only be changed to DELETED quality level.",
                    Artifact.class,
                    artifact.getId());
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy