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

com.checkmarx.configprovider.GitHubClient Maven / Gradle / Ivy

package com.checkmarx.configprovider;

import com.checkmarx.configprovider.dto.RepoDto;
import com.checkmarx.configprovider.interfaces.SourceControlClient;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.auth.AUTH;
import org.apache.http.client.fluent.Request;
import org.apache.http.client.utils.URIBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import java.util.Spliterator;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

@Slf4j
class GitHubClient implements SourceControlClient {
    private static final String GET_CONTENTS_TEMPLATE = "/repos/%s/%s/contents/%s";
    private static final String REF_SPECIFIER = "ref";
    private static final String ACCEPT_HEADER = "Accept";

    // Allows to get directory content response as an object with the 'entries' field (instead of just an array
    // of entries). This simplifies response handling.
    private static final String API_V3_OBJECT_HEADER = "application/vnd.github.v3.object";

    // Allows to get file contents as is (without any JSON wrapper).
    private static final String API_V3_RAW_CONTENTS_HEADER = "application/vnd.github.v3.raw";

    private static final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public String downloadFileContent(String path, String filename, RepoDto repo) {
        String result = null;
        log.info("Downloading file content for '{}'", repo.getRepoName());
        try {
            String combinedPath = Paths.get(path, filename).toString();
            combinedPath = FilenameUtils.normalize(combinedPath, true);

            URI uri = createContentsUri(repo, combinedPath);

            HttpResponse response = getContentResponse(uri, API_V3_RAW_CONTENTS_HEADER, repo);
            result = getTextFrom(response);
        } catch (Exception e) {
            log.warn("Error downloading file contents", e);
        }
        return result;
    }

    @Override
    public List getDirectoryFilenames(RepoDto repoResource, String path) {
        List result = Collections.emptyList();
        
            String effectivePath = normalize(path);
            try {
                URI uri = createContentsUri(repoResource, effectivePath);
                HttpResponse response = getContentResponse(uri, API_V3_OBJECT_HEADER, repoResource);
                result = getFilenamesFrom(response);
            } catch (Exception e) {
                log.warn("Error downloading directory contents", e);
            }

            log.info("Files found: {}", result);
        
        return result;
    }

    private static String normalize(String path) {
        String result = StringUtils.defaultString(path);
        if (result.isEmpty()) {
            log.info("Getting filenames from the root directory");
        } else {
            log.info("Getting filenames from the '{}' directory", result);
        }
        return result;
    }

    private static HttpResponse getContentResponse(URI uri, String acceptHeaderValue, RepoDto repo)
            throws IOException {
        log.debug("Getting the contents from {}", uri);

        Request request = getRequestWithAuth(repo, Request.Get(uri));

        return request.addHeader(ACCEPT_HEADER, acceptHeaderValue)
                .execute()
                .returnResponse();
    }

    private static Request getRequestWithAuth(RepoDto repo, Request request) {
        String accessToken = repo.getAccessToken();
        if (StringUtils.isNotEmpty(accessToken)) {
            log.debug("Using an access token");
            String authHeaderValue = String.format("token %s", accessToken);
            request.addHeader(AUTH.WWW_AUTH_RESP, authHeaderValue);
        } else {
            log.debug("Access token is not specified");
        }
        return request;
    }

    private static List getFilenamesFrom(HttpResponse response) throws IOException {
        List result = Collections.emptyList();

        int statusCode = response.getStatusLine().getStatusCode();
        String responseText = getTextFrom(response);

        if (statusCode == HttpStatus.SC_OK) {
            result = extractFilenamesFromJson(responseText);
        } else {
            log.warn("Error loading filenames. The response status is '{}'. Make sure that namespace, repo name " +
                    "and path are correct and that you have access to the repo.", response.getStatusLine());
        }
        return result;
    }

    private static List extractFilenamesFromJson(String responseText) throws JsonProcessingException {
        List result;
        JsonNode content = objectMapper.readTree(responseText);
        String contentType = content.get("type").asText();
        boolean contentIsDirectory = contentType.equals("dir");
        if (contentIsDirectory) {
            // Expecting the following response structure:
            // { "type": "dir", "entries": [], ... }        //NOSONAR
            Spliterator directoryEntries = content.get("entries").spliterator();

            result = StreamSupport.stream(directoryEntries, false)
                    .filter(node -> node.get("type").asText().equals("file"))
                    .map(node -> node.get("name").asText())
                    .collect(Collectors.toList());
        } else {
            log.warn("The specified path doesn't refer to a directory. Please provide a valid directory path");
            result = Collections.emptyList();
        }
        return result;
    }

    private static URI createContentsUri(RepoDto repo, String directoryPath) throws
            URISyntaxException {

        String path = String.format(GET_CONTENTS_TEMPLATE,
                repo.getNamespace(), repo.getRepoName(), directoryPath);

        return new URIBuilder(repo.getApiBaseUrl())
                .setPath(path)
                .setParameter(REF_SPECIFIER, repo.getRef())
                .build();
    }

    private static String getTextFrom(HttpResponse response) throws IOException {
        String result = null;
        InputStream contentStream = response.getEntity() != null ? response.getEntity().getContent() : null;
        if (contentStream != null) {
            result = IOUtils.toString(contentStream, StandardCharsets.UTF_8);
        }
        log.trace("Response body: {}", result);
        return result;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy