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

비밀번호

댓글 작성자
 




... 121  122  123  [124]  125  126  127  128  129  130  131  132  133  134  135  ...
NoWriterDateCnt.TitleFile(s)
10822정성태7/7/201527266오류 유형: 299. The 'Visual C++ Project System Package' package did not load correctly.
10821정성태7/7/201519526오류 유형: 298. Unable to start debugging on the web server. IIS does not list a web site that matches the launched URL.
10820정성태7/7/201525474오류 유형: 297. HTTP Error 503. The service is unavailable. - 두 번째
10819정성태7/2/201528927오류 유형: 296. SQL Server Express 시작 오류 - error code 3417
10818정성태7/1/201527738오류 유형: 295. HTTP Error 503. The service is unavailable. [1]
10817정성태6/29/201531439.NET Framework: 523. C# 람다(Lambda)에서 변수 캡처 방식 [3]
10816정성태6/25/201527781.NET Framework: 522. 닷넷의 어셈블리 서명 데이터 확인 방법파일 다운로드1
10815정성태6/23/201525614Graphics: 1. 자네 나와 함께... UNITY 하지 않겠는가! [4]
10814정성태6/22/201523379.NET Framework: 521. Roslyn을 이용해 C# 문법 변형하기 (2) [5]
10813정성태6/21/201525169.NET Framework: 520. Roslyn을 이용해 C# 문법 변형하기 (1)
10812정성태6/20/201526302.NET Framework: 519. C# 6.0 오픈 소스 컴파일러 Roslyn - 빌드 및 테스트 방법 [1]
10811정성태6/20/201523166오류 유형: 294. OpenAuth 사용 시 System.Data.SqlClient.SqlException 예외가 Output 창에 출력되는 문제
10810정성태6/18/201522148개발 환경 구성: 270. Visual Studio에서 github 오픈 소스를 fork해서 테스트하는 방법 [1]
10809정성태6/18/201520063.NET Framework: 518. AllowPartiallyTrustedCallers 특성이 적용된 GAC 어셈블리에서 DynamicMethod의 calli 명령어 사용파일 다운로드1
10808정성태6/17/201522457.NET Framework: 517. calli IL 호출이 DllImport 호출보다 빠를까요? [1]파일 다운로드1
10807정성태6/16/201523530.NET Framework: 516. Microsoft.AspNet.Membership.OpenAuth 사용 시 "Local Database Runtime error occurred" 오류
10806정성태6/16/201541405.NET Framework: 515. OpenAuth.VerifyAuthentication 호출 시 The remote server returned an error: (400) Bad Request
10805정성태6/15/201522714Java: 17. 자바의 재미있는 상수 처리 방식
10804정성태6/10/201522356.NET Framework: 514. .NET CLR2 보안 모델에서의 APTCA 역할 (2)파일 다운로드1
10803정성태6/2/201524279.NET Framework: 513. UWP(Universal Windows Platform) 응용 프로그램의 새로운 라이브러리 버전 관리 해법 [2]
10802정성태6/2/201524366개발 환경 구성: 269. 마이크로소프트 온라인 강좌 소개 - Azure VPN 구성 방법 [1]
10801정성태5/31/201528918.NET Framework: 512. async/await 사용 시 hang 문제가 발생하는 경우 - 두 번째 이야기 [3]
10800정성태5/29/201524365개발 환경 구성: 268. 소개 - 프로세싱(https://processing.org/)
10799정성태5/29/201522148사물인터넷: 3. 책 소개 - 라즈베리 파이로 구현하는 사물 인터넷 프로젝트 [1]
10798정성태5/26/201521878기타: 53. 2015년 6월 10일 밤 10시 온라인 세미나 - 새로운 Windows 10 App을 개발하는 방법
10797정성태5/23/201521550VC++: 91. 자식 스레드에 자동 상속되는 TEB의 SubProcessTag 필드파일 다운로드1
... 121  122  123  [124]  125  126  127  128  129  130  131  132  133  134  135  ...