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)
1738정성태8/23/201423389.NET Framework: 456. C# - CAS를 이용한 Lock 래퍼 클래스파일 다운로드1
1737정성태8/20/201420841VS.NET IDE: 93. Visual Studio 2013 동기화 문제
1736정성태8/19/201426864VC++: 79. [부연] CAS Lock 알고리즘은 과연 빠른가? [2]파일 다운로드1
1735정성태8/19/201419375.NET Framework: 455. 닷넷 사용자 정의 예외 클래스의 최소 구현 코드 - 두 번째 이야기
1734정성태8/13/201421114오류 유형: 237. Windows Media Player cannot access the file. The file might be in use, you might not have access to the computer where the file is stored, or your proxy settings might not be correct.
1733정성태8/13/201427390.NET Framework: 454. EmptyWorkingSet Win32 API를 사용하는 C# 예제파일 다운로드1
1732정성태8/13/201435757Windows: 99. INetCache 폴더가 다르게 보이는 이유
1731정성태8/11/201428193개발 환경 구성: 235. 점(.)으로 시작하는 파일명을 탐색기에서 만드는 방법
1730정성태8/11/201423388개발 환경 구성: 234. Royal TS의 터미널(Terminal) 연결에서 한글이 깨지는 현상 해결 방법
1729정성태8/11/201419366오류 유형: 236. SqlConnection - The requested Performance Counter is not a custom counter, it has to be initialized as ReadOnly.
1728정성태8/8/201431612.NET Framework: 453. C# - 오피스 파워포인트(Powerpoint) 파일을 WinForm에서 보는 방법파일 다운로드1
1727정성태8/6/201421824오류 유형: 235. SignalR 오류 메시지 - Counter 'Messages Bus Messages Published Total' does not exist in the specified Category. [2]
1726정성태8/6/201420664오류 유형: 234. IIS Express에서 COM+ 사용 시 SecurityException - "Requested registry access is not allowed" 발생
1725정성태8/6/201422593오류 유형: 233. Visual Studio 2013 Update3 적용 후 Microsoft.VisualStudio.Web.PageInspector.Runtime 모듈에 대한 FileNotFoundException 예외 발생
1724정성태8/5/201427432.NET Framework: 452. .NET System.Threading.Thread 개체에서 Native Thread Id를 구하는 방법 - 두 번째 이야기 [1]파일 다운로드1
1723정성태7/29/201459815개발 환경 구성: 233. DirectX 9 예제 프로젝트 빌드하는 방법 [3]파일 다운로드1
1722정성태7/25/201422152오류 유형: 232. IIS 500 Internal Server Error - NTFS 암호화된 폴더에 웹 애플리케이션이 위치한 경우
1721정성태7/24/201425442.NET Framework: 451. 함수형 프로그래밍 개념 - 리스트 해석(List Comprehension)과 순수 함수 [2]
1720정성태7/23/201423417개발 환경 구성: 232. C:\WINDOWS\system32\LogFiles\HTTPERR 폴더에 로그 파일을 남기지 않는 설정
1719정성태7/22/201427264Math: 13. 동전을 여러 더미로 나누는 경우의 수 세기(Partition Number) - 두 번째 이야기파일 다운로드1
1718정성태7/19/201436692Math: 12. HTML에서 수학 관련 기호/수식을 표현하기 위한 방법 - MathJax.js [4]
1716정성태7/17/201436412개발 환경 구성: 231. PC 용 무료 안드로이드 에뮬레이터 - genymotion
1715정성태7/13/201431503기타: 47. 운영체제 종료 후에도 USB 외장 하드의 전원이 꺼지지 않는 경우 [3]
1714정성태7/11/201421558VS.NET IDE: 92. Visual Studio 2013을 지원하는 IL Support 확장 도구
1713정성태7/11/201445327Windows: 98. 윈도우 시스템 디스크 용량 확보를 위한 "Package Cache" 폴더 이동 [1]
1712정성태7/10/201433870.NET Framework: 450. 영문 윈도우에서 C# 콘솔 프로그램의 유니코드 출력 방법 [3]
... 121  122  123  124  125  126  127  128  129  130  131  132  133  [134]  135  ...