Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일

C# - JIRA에 등록된 Project의 Version 항목 추가하는 방법

사내에서 이슈 관리 시스템인 JIRA를 사용하면서 불편한 점이 하나 나왔는데요. 이슈 생성할 때, Version 항목이 미리 등록되어 있지 않으면 그걸 또 등록하러 가야 한다는 것입니다. 이런 불편함을, 제품의 빌드 스크립트에서 버전을 JIRA에 등록하는 방법으로 해결할 수 있습니다. 그리고 이를 위해 JIRA REST API를 사용하게 된 것입니다.

C# - JIRA REST API 사용 정리
; https://www.sysnet.pe.kr/2/0/11566

자, 그럼 시작해 볼까요? ^^

우선, 해당 프로젝트에 등록된 모든 버전을 가져오는 version API를,

/rest/api/2/project/{projectIdOrKey}/versions
; https://docs.atlassian.com/software/jira/docs/api/REST/6.1.4/#d2e231

다음과 같이 작성할 수 있습니다.

public async Task<string> GetVersions(string projectKey)
{
    string url = _baseUrl + $"project/{projectKey}/versions";
    HttpResponseMessage hrm = await _httpClient.GetAsync(url);

    string text = await hrm.Content.ReadAsStringAsync();
    return text;
}

출력 결과로부터 json2csharp을 통해 다음과 같은 Entity 클래스를 구하고,

public class Version /* RootObject */
{
    public string self { get; set; }
    public string id { get; set; }
    public string name { get; set; }
    public bool archived { get; set; }
    public bool released { get; set; }
    public string releaseDate { get; set; }
    public string userReleaseDate { get; set; }
    public int projectId { get; set; }
    public bool? overdue { get; set; }
    public string description { get; set; }
    public string startDate { get; set; }
    public string userStartDate { get; set; }
}

API를 다시 다음과 같은 코드로 바꿀 수 있습니다.

public async Task<Version[]> GetVersions(string projectKey)
{
    string url = _baseUrl + $"project/{projectKey}/versions";
    HttpResponseMessage hrm = await _httpClient.GetAsync(url);

    string text = await hrm.Content.ReadAsStringAsync();
    Version [] versionList = Newtonsoft.Json.JsonConvert.DeserializeObject<Version[]>(text);

    return versionList;
}

이를 기반으로, "버전 이름"에 해당하는 항목이 등록되어 있는지 알 수 있는 API를 제공할 수 있습니다.

public async Task<Version> GetVersionByName(string projectKey, string versionName)
{
    Version [] versions = await GetVersions(projectKey);
    return versions.FirstOrDefault((e) => e.name == versionName);
}

만약 없다면 새롭게 등록해야 하는데요, 이를 위한 REST API는 다음과 같습니다.

/rest/api/2/version
; https://docs.atlassian.com/software/jira/docs/api/REST/6.1.4/#d2e3548

이때 POST로 Json 데이터를 보내야 하는데 다음과 같은 형식입니다.

{
    "description": "An excellent version",
    "name": "New Version 1",
    "archived": false,
    "released": true,
    "releaseDate": "2010-07-06",
    "userReleaseDate": "6/Jul/2010",
    "project": "PXA",
    "projectId": 10000
}

POST 데이터의 예제가 저렇게 나오긴 하지만 약간의 규칙이 있습니다.

  • releaseDate와 userReleaseDate는 동시에 보낼 수 없음. (즉, releaseDate만 보내면 됨)
  • projectId가 있으면 project 항목은 안 보내도 됨
  • 만약 startDate 필드가 있다면 반드시 releaseDate 날짜 보다 이전이어야 함.

여기서 애매한 것이, "projectId" 필드입니다. 보통 "project"는 JIRA 웹 사이트에서 이슈 번호를 통해 쉽게 인지를 하고 있지만 projectId의 경우에는 모르는 경우가 많습니다. 따라서 projectKey로 Id를 구해주는 API를 하나 만들어 두는 것이 좋습니다.

/rest/api/2/project/{projectIdOrKey}
; https://docs.atlassian.com/software/jira/docs/api/REST/6.1.4/#d2e208

public async Task<Project> GetProjectByKey(string projectKey)
{
    string url = _baseUrl + $"project/{projectKey}";
    HttpResponseMessage hrm = await _httpClient.GetAsync(url);

    string text = await hrm.Content.ReadAsStringAsync();
    Project project = Newtonsoft.Json.JsonConvert.DeserializeObject<Project>(text);

    return project;
}

이쯤에서 우리가 구현하고 싶은 코드를 정리해 보면 다음과 같습니다.

{
    string newVersionName = "myapp-1.0.1";
    string projectKey = "myProject";

    Project project = await jira.GetProjectByKey(projectKey);
    if (project == null)
    {
        Console.WriteLine("NO Project: " + projectKey);
        return;
    }

    JiraEntity.Version result = await jira.GetVersionByName(projectKey, newVersionName);
    if (result == null)
    {
        // 기존에 등록된 버전이 없다면 신규 등록
        JiraEntity.Version version = new JiraEntity.Version();
        version.name = newVersionName;
        version.projectId = Int32.Parse(project.id);
        version.description = versionDesc;
        version.startDate = DateTime.Now.ToString("yyyy-MM-dd");
        SetReleaseDate(version, releaseDate);

        result = await jira.CreateVersion(version);
        if (result != null)
        {
            return;
        }
    }
    else
    {
        // 기존에 등록된 버전이 있다면 업데이트
        result.description = versionDesc;
        SetReleaseDate(result, releaseDate);

        result = await jira.UpdateVersion(result);
        if (result != null)
        {
            return;
        }
    }
}

static void SetReleaseDate(JiraEntity.Version version, string releaseDate)
{
    if (releaseDate == null)
    {
        return;
    }

    version.releaseDate = releaseDate;
    version.released = true;
}

이를 위해 새로운 버전을 등록하는 CreateVersion 메서드는 이렇게 만들어 주고,

public async Task<JiraEntity.Version> CreateVersion(JiraEntity.Version version)
{
    string url = _baseUrl + "version";

    string postData = Newtonsoft.Json.JsonConvert.SerializeObject(version);

    StringContent content = new StringContent(postData, Encoding.UTF8, "application/json");
    HttpResponseMessage hrm = await _httpClient.PostAsync(url, content);

    string text = await hrm.Content.ReadAsStringAsync();
    JiraEntity.Version registeredVersion = Newtonsoft.Json.JsonConvert.DeserializeObject<JiraEntity.Version>(text);

    return registeredVersion;
}

기존 버전 정보를 업데이트하는 UpdateVersion 메서드는 다음의 REST API를,

/rest/api/2/version/{id}
; https://docs.atlassian.com/software/jira/docs/api/REST/6.1.4/#d2e3578

PUT으로 호출하면 되므로 이렇게 만들어 줍니다.

public async Task<JiraEntity.Version> UpdateVersion(JiraEntity.Version version)
{
    string url = _baseUrl + "version/" + version.id;
    MakeVersionPutValid(version);

    string postData = Newtonsoft.Json.JsonConvert.SerializeObject(version);

    StringContent content = new StringContent(postData, Encoding.UTF8, "application/json");
    HttpResponseMessage hrm = await _httpClient.PutAsync(url, content);

    string text = await hrm.Content.ReadAsStringAsync();
    JiraEntity.Version registeredVersion = Newtonsoft.Json.JsonConvert.DeserializeObject<JiraEntity.Version>(text);

    return registeredVersion;
}

뭐... 이 정도까지 했으니, 이제 JIRA 정도는 자유자재로 다루실 수 있겠죠?!!! ^^

(첨부 파일은 이 글의 예제 코드를 포함합니다.)




참고로, REST API에 Post 호출을 했는데 다음과 같은 "HTTP Status 415 - Unsupported Media Type" 오류가 반환된다면?

<html><head><title>Apache Tomcat/7.0.29 - Error report</title><style><!--H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}A {color : black;}A.name {color : black;}HR {color : #525D76;}--></style> </head><body><h1>HTTP Status 415 - Unsupported Media Type</h1><HR size="1" noshade="noshade"><p><b>type</b> Status report</p><p><b>message</b> <u>Unsupported Media Type</u></p><p><b>description</b> <u>The server refused this request because the request entity is in a format not supported by the requested resource for the requested method (Unsupported Media Type).</u></p><HR size="1" noshade="noshade"><h3>Apache Tomcat/7.0.29</h3></body></html>

Post 요청 헤더에 "application/json"을 설정하지 않아서 그런 것입니다.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 7/2/2018]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




1  2  3  4  5  6  7  8  9  10  [11]  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13667정성태7/7/20246608닷넷: 2273. C# - 리눅스 환경에서의 Hyper-V Socket 연동 (AF_VSOCK)파일 다운로드1
13666정성태7/7/20247686Linux: 74. C++ - Vsock 예제 (Hyper-V Socket 연동)파일 다운로드1
13665정성태7/6/20247863Linux: 73. Linux 측의 socat을 이용한 Hyper-V 호스트와의 vsock 테스트파일 다운로드1
13663정성태7/5/20247470닷넷: 2272. C# - Hyper-V Socket 통신(AF_HYPERV, AF_VSOCK)의 VMID Wildcards 유형파일 다운로드1
13662정성태7/4/20247477닷넷: 2271. C# - WSL 2 VM의 VM ID를 알아내는 방법 - Host Compute System API파일 다운로드1
13661정성태7/3/20247398Linux: 72. g++ - 다른 버전의 GLIBC로 소스코드 빌드
13660정성태7/3/20247504오류 유형: 912. Visual C++ - Linux 프로젝트 빌드 오류
13659정성태7/1/20247843개발 환경 구성: 715. Windows - WSL 2 환경의 Docker Desktop 네트워크
13658정성태6/28/20248217개발 환경 구성: 714. WSL 2 인스턴스와 호스트 측의 Hyper-V에 운영 중인 VM과 네트워크 연결을 하는 방법 - 두 번째 이야기
13657정성태6/27/20247894닷넷: 2270. C# - Hyper-V Socket 통신(AF_HYPERV, AF_VSOCK)을 위한 EndPoint 사용자 정의
13656정성태6/27/20248056Windows: 264. WSL 2 VM의 swap 파일 위치
13655정성태6/24/20247837닷넷: 2269. C# - Win32 Resource 포맷 해석파일 다운로드1
13654정성태6/24/20247787오류 유형: 911. shutdown - The entered computer name is not valid or remote shutdown is not supported on the target computer.
13653정성태6/22/20247925닷넷: 2268. C# 코드에서 MAKEINTREOURCE 매크로 처리
13652정성태6/21/20249232닷넷: 2267. C# - Linux 환경에서 (Reflection 없이) DLL AssemblyFileVersion 구하는 방법파일 다운로드2
13651정성태6/19/20248472닷넷: 2266. C# - (Reflection 없이) DLL AssemblyFileVersion 구하는 방법파일 다운로드1
13650정성태6/18/20248397개발 환경 구성: 713. "WSL --debug-shell"로 살펴보는 WSL 2 VM의 리눅스 환경
13649정성태6/18/20247947오류 유형: 910. windbg - !py 확장 명령어 실행 시 "failed to find python interpreter" (2)
13648정성태6/17/20248263오류 유형: 909. C# - DynamicMethod 사용 시 System.TypeAccessException
13647정성태6/16/20249323개발 환경 구성: 712. Windows - WSL 2의 네트워크 통신 방법 - 세 번째 이야기 (같은 IP를 공유하는 WSL 2 인스턴스) [1]
13646정성태6/14/20247741오류 유형: 908. Process Explorer - "Error configuring dump resources: The system cannot find the file specified."
13645정성태6/13/20248197개발 환경 구성: 711. Visual Studio로 개발 시 기본 등록하는 dev tag 이미지로 Docker Desktop k8s에서 실행하는 방법
13644정성태6/12/20248855닷넷: 2265. C# - System.Text.Json의 기본적인 (한글 등에서의) escape 처리 [1]
13643정성태6/12/20248294오류 유형: 907. MySqlConnector 사용 시 System.IO.FileLoadException 오류
13642정성태6/11/20248196스크립트: 65. 파이썬 - asgi 버전(2, 3)에 따라 달라지는 uvicorn 호스팅
13641정성태6/11/20248659Linux: 71. Ubuntu 20.04를 22.04로 업데이트
1  2  3  4  5  6  7  8  9  10  [11]  12  13  14  15  ...