devops

使用GitLabApi获取远程仓库中的文件内容

2018-08-19  本文已影响0人  西瓜雪梨桔子汁

在一些实际情况中,希望能够直接像读取本地文件一样读取远程仓库中的文件内容,避免git操作失败的情况下读取的本地缓存的文件内容。由于项目使用gitLab管理配置文件,查询了GitLabApi,其提供了诸多API接口,包括常见的git操作、项目管理以及我们需要的获取文件内容等接口。

1.接口分析

查询GitLab api,可以容易找到获取文件内容的API文档:GitLab获取仓库中文件内容,可以发现,其格式要求为:

GET /projects/:id/repository/files/:file_path
curl --request GET --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v4/projects/13083/repository/files/app%2Fmodels%2Fkey%2Erb?ref=master'

通过分析可以发现,如果想获取仓库中文件内容,需要以下几个要素:

private static String GITLAB_FILECONTENT_API = "http://#{REPO_IP}/api/v3/projects/#{PROJECT_ID}/repository/files?private_token=#{PRIVATE_TOKEN}&file_path=#{FILE_PATH}&ref=#{BRANCH_NAME}";

2.获取用户的private token

GitLabApi关于获取个人口令的API可以整理为常量类:

private static String GITLAB_SESSION_API = "http://#{REPO_IP}/api/v3/session?login=#{USER_NAME}&password=#{PASSWORD}";

提供如下3个变量即可获取指定用户的token:

/**
     * 根据用户名称和密码获取gitlab的private token,为Post请求
     * 
     * @param ip    gitlab仓库的ip
     * @param userName  登陆gitlab的用户名
     * @param password  登陆gitlab的密码
     * @return  返回该用户的private token
     */
    public static String getPrivateTokenByPassword(String ip, String userName, String password) {

        /** 1.参数替换,生成获取指定用户privateToken地址 */
        //  校验参数
        Objects.requireNonNull(ip, "参数ip不能为空!");
        Objects.requireNonNull(userName, "参数userName不能为空!");
        Objects.requireNonNull(password, "参数password不能为空!");
        //  参数准备,存入map
        Map<String, String> params = new HashMap<String, String>(4);
        params.put("REPO_IP", ip);
        params.put("USER_NAME", userName);
        params.put("PASSWORD", password);
        // 调用工具类替换,得到具体的调用地址
        String reqUserTokenUrl = PlaceholderUtil.anotherReplace(GITLAB_SESSION_API, params);
        sysLogger.debug(String.format("获取用户:%s的private token地址为:%s", userName,reqUserTokenUrl));
        
        /** 2.访问url,获取指定用户的信息 */
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<String> response = restTemplate.postForEntity(reqUserTokenUrl, null,String.class);
        sysLogger.debug(String.format("响应头为:%s,响应体为:%s", response.getHeaders(), response.getBody()));
        

        /** 3.解析结果 */
        String body = response.getBody();
        JSONObject jsonBody = JsonUtil.parseObjectToJSONObject(body);
        String privateToken =jsonBody.getString("private_token");
        sysLogger.debug(String.format("获取到用户:%s的privateToken为:%s", userName, privateToken));
        
        /** 4.返回privateToken */     
        return privateToken;

    }

注意,这个接口时POST请求,正确的HTTP响应码是201,而非200.

3.获取项目的projectId

projectId可以直接到gitlab具体项目信息中查找,但是也有相关的api可以依据项目名称查询。从使用的角度讲,配置项目的名称更加方便,配置项目的projectId增加配置项,且如果提供获取projectId的方法,内部调用可以去掉该项的配置。
获取projectId的api参考:获取指定项目的projectId,需要如下3个要素:

private static String GITLAB_SINGLE_PROJECT_API = "http://#{REPO_IP}/api/v3/projects/#{PROJECT_PATH}?private_token=#{PRIVATE_TOKEN}";

然后对响应码为200的正确json返回结果信息解析即可得到,代码如下:

/**
     * 使用gitLab api获取指定项目的projectId,为Get请求
     * 
     * @param ip  项目仓库的ip地址
     * @param projectPath   项目的path,如:http://192.168.59.185/acountting/dispatcher-cloud.git,则projectPath为:acountting/dispatcher-cloud
     * @param privateToken  用户个人访问gitlab库时的privateToken,可以通过{@link GitLabAPIUtils#getPrivateTokenByPassword}获取
     * @return  返回目的projectId
     */
    public static String getProjectId(String ip, String projectPath, String privateToken) {
        /** 1.参数替换,生成访问获取project信息的uri地址 */
        //  校验参数
        Objects.requireNonNull(ip, "参数ip不能为空!");
        Objects.requireNonNull(projectPath, "参数projectPath不能为空!");
        Objects.requireNonNull(privateToken, "参数privateToken不能为空!");
        //  参数准备,存入map
        Map<String, String> params = new HashMap<String, String>(4);
        params.put("REPO_IP", ip);
        params.put("PRIVATE_TOKEN", privateToken);      
        // gitlab api要求项目的path需要安装uri编码格式进行编码,比如"/"编码为"%2F"
        try {
            params.put("PROJECT_PATH", URLEncoder.encode(projectPath, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(String.format("对%s进行URI编码出错!", projectPath));
        }
        // 调用工具类替换,得到具体的调用地址
        String getSingleProjectUrl = PlaceholderUtil.anotherReplace(GITLAB_SINGLE_PROJECT_API, params);
        sysLogger.debug(String.format("获取projectId的url:%s", getSingleProjectUrl));

        //  创建URI对象
        URI url = null;
        try {
            url = new URI(getSingleProjectUrl);
        } catch (URISyntaxException e) {
            throw new RuntimeException(String.format("使用%s创建URI出错!", getSingleProjectUrl));
        }
        
        /** 2.访问url,获取制定project的信息 */
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<String> reslut = restTemplate.getForEntity(url, String.class);
        sysLogger.debug(String.format("响应头为:%s,响应体为:%s", reslut.getHeaders(), reslut.getBody()));
        
        /** 3.解析结果 */
        if (reslut.getStatusCode() != HttpStatus.OK ) {
            throw new RuntimeException(String.format("请求%s出错!错误码为:%s", url, reslut.getStatusCode()));
        }
        //  如果响应码是200,说明正常拿到响应结果,解析出projectId返回即可
        JSONObject responBody = JsonUtil.parseObjectToJSONObject(reslut.getBody());
        String projectRepo = responBody.getString("http_url_to_repo");
        String projectId = responBody.getString("id");
        sysLogger.info(String.format("获取到项目:%s的projectId为:%s", projectRepo, projectId));
        
        /** 4.返回projectId */
        return projectId;
    }

需要特别注意,在方法中对配置好的project path进行url编码后,没有直接使用RestTemplate创建get请求获取项目信息,因为实践中发现会出现将本义编码好的如:src%2FHelloWorld.java变为:src%256FHelloWorld.java,具体没有深入RestTemplate源码,所以直接创建URI对象,避免这种情况出现。

4 获取仓库文件内容

api参考:gitlab获取仓库文件内容,需要提供3个参数:

private static String GITLAB_FILECONTENT_API = "http://#{REPO_IP}/api/v3/projects/#{PROJECT_ID}/repository/files?private_token=#{PRIVATE_TOKEN}&file_path=#{FILE_PATH}&ref=#{BRANCH_NAME}";

返回的内容json格式如下:

{
  "file_name": "HelloWorld.java",
  "file_path": "main/class/HelloWorld.java",
  "size": 1476,
  "encoding": "base64",
  "content": "IyA9PSBTY2hlbWEgSW5mb3...",
  "content_sha256": "4c294617b60715c1d218e61164a3abd4808a4284cbc30e6728a01ad9aada4481",
  "ref": "master",
  "blob_id": "79f7bbd25901e8334750839545a9bd021f0e4c83",
  "commit_id": "d5a3ff139356ce33e37e73add446f16869741b50",
  "last_commit_id": "570e7b2abdd848b95f2f578043fc23bd6f6fd24d"
}

特别需要注意的是返回的文件内容是base64编码,拿到结果后需要解码才能获取原始内容。如果想直接获取原始文件内容:获取原始文件内容
所以获取方法如下:

public static String getFileContentFromRepository(String ip,String projectPath,String userName,String password,
            String fileFullPath, String branchName) throws Exception {
        //  校验参数
        Objects.requireNonNull(ip, "参数ip不能为空!");
        Objects.requireNonNull(projectPath, "参数projectPath不能为空!");
        Objects.requireNonNull(userName, "参数userName不能为空!");
        Objects.requireNonNull(password, "参数password不能为空!");
        Objects.requireNonNull(fileFullPath, "参数fileFullPath不能为空!");
        Objects.requireNonNull(branchName, "参数branchName不能为空!");
        
        /** 1.依据用户名、密码获取到用户的privateToken */
        String privateToken = getPrivateTokenByPassword(ip, userName, password);
        
        /** 2.使用privateToken获取项目的projectId */
        String projectId = getProjectId(ip, projectPath, privateToken);
        
        /** 3.使用参数替换形成请求git库中文件内容的uri */
        //  参数准备,存入map
        Map<String, String> params = new HashMap<String, String>(4);
        params.put("REPO_IP", ip);
        params.put("PROJECT_ID", projectId);    
        params.put("PRIVATE_TOKEN", privateToken);  
        params.put("FILE_PATH", fileFullPath);      
        params.put("BRANCH_NAME", branchName);  
        //  使用工具类替换参数
        String reqFileCotnetUri = PlaceholderUtil.anotherReplace(GITLAB_FILECONTENT_API, params);
        sysLogger.debug(String.format("获取文件:%s的uri:%s", fileFullPath, reqFileCotnetUri));       
        
        /** 4.请求gitlab获取文件内容 */
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<String> response = restTemplate.getForEntity(reqFileCotnetUri, String.class);
        sysLogger.debug(String.format("响应头为:%s,响应体为:%s", response.getHeaders(), response.getBody()));
        
        /** 5.解析响应结果内容 */
        String body = response.getBody();
        JSONObject jsonBody = JsonUtil.parseObjectToJSONObject(body);
        String fileName = jsonBody.getString("file_name");
        String filePath = jsonBody.getString("file_path");
        String encoding = jsonBody.getString("encoding");
        String content = jsonBody.getString("content");
        String commitId = jsonBody.getString("commit_id");
        String lastCommitId = jsonBody.getString("last_commit_id");

        //  内容已经base64编码,如果需要获取原始文件内容可以参看api:https://docs.gitlab.com/ee/api/repository_files.html#get-raw-file-from-repository
        content = new String(Base64.decode(content), "UTF-8");

        sysLogger.debug(String.format(
                "获取http://%s 上%s项目 %s分支的%s文件,响应信息是:fileName :%s ,filePath:%s , 编码:%s ,内容:%s , commitId:%s ,lastCommitId :%s",
                ip,projectPath, branchName, fileFullPath, fileName, filePath, encoding, content, commitId,
                lastCommitId));

        /** 6.返回指定文件的内容 */
        sysLogger.debug(String.format("解析得到文件内容为:%s", content));
        return content;
    }
    

大致经过如下过程:

此外,考虑到项目中实际获取的是配置文件内容,为了剔除不必要的空行、注释行,提供了工具类方法对解码的原始文件内容进行处理:

public static String getCleanFileContentFromRepository(String ip,String projectPath,String userName,String password,
            String fileFullPath, String branchName) throws Exception{
        
        /**  1.获取到原始文件内容 */
        String fileContent = getFileContentFromRepository(ip, projectPath, userName, password, fileFullPath, branchName);
        if (fileContent.isEmpty()) {
            return fileContent;
        }
        
        /** 2.所有行转换为list,并过滤掉空行、#开头的注释行 */
        List<String> list = Arrays.asList(fileContent.split("\n")).stream().filter(a-> (!a.trim().isEmpty()&&!a.trim().startsWith("#"))).collect(Collectors.toList());
        
        /** 3.转为一整行字符串返回 */
        StringBuilder sb = new StringBuilder();
        int size =list.size();
        for (int i = 0; i < size; i++) {
            sb.append(list.get(i).trim());
        }
        return sb.toString();
    }

5 其它相关

5.1 替换参数工具类

对#{}包裹的参数进行替换,代码如下:

public class PlaceholderUtil {
    /** 默认替换形如#{param}的占位符 */
    private static Pattern pattern = Pattern.compile("\\#\\{.*?\\}");
    
    /**
     * 替换字符串中形如#{}的占位符
     * @param src
     * @param parameters
     * @return
     */
    public static String replace(String src, Map<String, Object> parameters) {
        Matcher paraMatcher = pattern.matcher(src);

        // 存储参数名
        String paraName = "";
        String result = new String(src);

        while (paraMatcher.find()) {
            paraName = paraMatcher.group().replaceAll("\\#\\{", "").replaceAll("\\}", "");
            Object objParam = parameters.get(paraName);
            if(objParam!=null){
                result = result.replace(paraMatcher.group(), objParam.toString());
            }
        }
        return result;
    }
    
    
    /**
     * 替换字符串中形如#{}的占位符
     * @param src
     * @param parameters
     * @return
     */
    public static String anotherReplace(String str, Map<String, String> params) {
        Map<String, Object> newParams = new HashMap<>(params);
        return replace(str, newParams);
    }
    
}

5.2 测试类:

public class GitLabAPIUtilsTest {
    
    String repoIp; 
    String privateToken;
    String projectPath1;
    String projectPath2;
    

    String userName;
    String password;
    
    String fileFullPath;
    String branchName;

    @Before
    public void setUp() throws Exception {
        repoIp = "gitlab仓库ip";
        projectPath1 = "acountting/accounting-config-repo";
        projectPath2 = "acountting/csv-filefront-cloud";
        
        userName = "用户名";
        password="密码";
        
        fileFullPath = "/apps/cmup-clearing/0055-account.config";
        branchName = "develop";
    }

    @After
    public void tearDown() throws Exception {
        repoIp = null;
        projectPath1  = null;
        projectPath2  = null;       
        userName = null;
        password = null;        
        fileFullPath = null;
        branchName = null;
    }

    @Test
    public void testGetProjectId() {
        String privateToken = GitLabAPIUtils.getPrivateTokenByPassword(repoIp, userName, password);
        String projectId = GitLabAPIUtils.getProjectId(repoIp, projectPath1, privateToken);
        System.out.println("projectId = " + projectId);
    }

    @Test
    public void testGetPrivateToken() {
        String privateToken = GitLabAPIUtils.getPrivateTokenByPassword(repoIp, userName, password);
        System.out.println("projectId = " + privateToken);
    }
    
    @Test
    public void testGetFileContent() {
        String fileContent;
        try {
            fileContent = GitLabAPIUtils.getCleanFileContentFromRepository(repoIp, projectPath1, userName, password, fileFullPath, branchName);

            System.out.println("fileContent = " + fileContent);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}
上一篇下一篇

猜你喜欢

热点阅读