Sonarqube 이슈 엑셀 레포트 생성
Sonarqube는 널리 알려진 오픈소스 코드 품질 점검 도구이다.
공공SI 프로젝트에서도 Sonarqube
는 거의 필수로 사용되고 있다. 내부적인 소스코드 품질 향상을 위한 것도 있지만, 요샌 거의 감리 받을때도 필수로 지켜야 되고, 발주 주관기관에서도 아예 PMD나 Checkstyle, FindBug 자체 룰을 관리 하는 곳도 있다.
소스코드 분석은 다양한 툴들을 통해 할 수 있다. Maven
, Gradle
, Sonar-scanner
등을 통해 CI
서버와의 연결을 통해 많이들 자동 빌드 및 코드 점검 환경을 구축하고 있다.
관련 튜토리얼은 지천에 널리 알려 있으니 마음만 먹으면 쉽게 점검 환경을 구축할 수 있어 많은 사람들의 사랑을 받는 점검 도구이다.
포스팅을 하는 이 시점엔 5.3 버전까지 나와 있는 상태이고 추가되는 언어나 신규 룰들도 재빠르게 업데이트 되는 편이다.
근데 한가지 좀 불편한게 Excel
로 직접 Export
하는 기능은 없다. 보통 한개 프로젝트 하면 어떤 곳에선 많으면 20~30 개 시스템에 대해서 이슈 레포팅을 뽑아서 감리 수감을 해야 하는 경우나 고객 보고 용도로 레포팅을 뽑아야 하는 경우가 가끔 발생하는데 레포트 뽑기가 참 수월치가 않았다
예전에 3.x대 버전이었을 경우 CSV Export Plugin 쓰다가 Deprecate 된 후로 PDF Report Plugin도 사용은 했으나 원하는 레포트를 뽑아내기가 영 수월치 않았다.
그래서 Sonarqube
에서도 권고 하고 있는 Web Service API
를 사용해서 Excel
로 레포팅을 뽑고 있었는데 이번에 5.3버전으로 업그레이드 하면서 이슈 검색하는 API의 파라미터들이 일부 변경이 되서 약간 수정하는 겸 Excel
로 레포팅 하는 방법을 정리해 본다.
사실 스크립트를 만드는 시점에 돌아가는 것에 초점을 맞춘 스크립트이기 때문에 참고용도로 활용하기 바란다.
SonarQube Web Service API
사용할 Web Service API
는 GET api/issues/search
를 사용해서 조건에 맞게 레포트를 생성하게 된다. 기존에 4.x 버전 때에는 componentRoots
파라미터를 사용해서 검색했었는데 5.1부터 deprecate되서 componentKeys
나 projectKeys
파라미터를 사용해서 프로젝트 별로 검색이 가능하다. 설치 되어 있는 Sonarqube
에서 https://nemo.sonarqube.org/api_documentation/api로 들어가면 전체 제공되는 API
들을 확인해 볼 수 있다.
GET api/issues/search
이슈 검색을 위한 API로 검색 권한을 가지고 있어야 한다.
주요 Parameters
상세한 검색을 위해서는 더 많은 파라미터를 사용할 수 있으나, 에제에는 아래의 파라미터만 사용해서 검색조건에 활용했다.
componentKeys optional
To retrieve issues associated to a specific list of components sub-components (comma-separated list of component keys). A component can be a view, developer, project, module, directory or file. If this parameter is set, componentUuids must not be set.
Example value: my_projectprojectKeys optional
To retrieve issues associated to a specific list of projects (comma-separated list of project keys). This parameter is mostly used by the Issues page, please prefer usage of the componentKeys parameter. If this parameter is set, projectUuids must not be set.
Example value: my_projectps optional
Page size. Must be greater than 0 and less than 500
Default value: 100
Example value: 20severities optional
Comma-separated list of severities
INFO, MINOR, MAJOR, CRITICAL, BLOCKER
Example value: BLOCKER,CRITICAL
예를 들면 GET
방식으로 /api/issues/search?componentKeys=my-project-id&severities=BLOCKER&ps=100
를 호출해보면 아래아 같은 json
결과값을 리턴해준다.
{
"paging": {
"pageIndex": 1,
"pageSize": 100,
"total": 1
},
"issues": [
{
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"component": "com.github.kevinsawicki:http-request:com.github.kevinsawicki.http.HttpRequest",
"project": "com.github.kevinsawicki:http-request",
"rule": "checkstyle:com.puppycrawl.tools.checkstyle.checks.coding.MagicNumberCheck",
"status": "RESOLVED",
"resolution": "FALSE-POSITIVE",
"severity": "MINOR",
"message": "'3' is a magic number.",
"line": 530,
"textRange": {
"startLine": 81,
"endLine": 81,
"startOffset": 0,
"endOffset": 134
},
"author": "Developer 1",
"debt": "2h1min",
"creationDate": "2013-05-13T17:55:39+0200",
"updateDate": "2013-05-13T17:55:39+0200",
"tags": ["bug"],
"comments": [
{
"key": "7d7c56f5-7b5a-41b9-87f8-36fa70caa5ba",
"login": "john.smith",
"htmlText": "Must be "final"!",
"markdown": "Must be \"final\"!",
"updatable": false,
"createdAt": "2013-05-13T18:08:34+0200"
}
],
"attr": {
"jira-issue-key": "SONAR-1234"
},
"transitions": [
"unconfirm",
"resolve",
"falsepositive"
],
"actions": [
"comment"
]
}
],
"components": [
{
"key": "com.github.kevinsawicki:http-request:src/main/java/com/github/kevinsawicki/http/HttpRequest.java",
"enabled": true,
"qualifier": "FIL",
"name": "HttpRequest.java",
"longName": "src/main/java/com/github/kevinsawicki/http/HttpRequest.java",
"path": "src/main/java/com/github/kevinsawicki/http/HttpRequest.java"
},
{
"key": "com.github.kevinsawicki:http-request",
"enabled": true,
"qualifier": "TRK",
"name": "http-request",
"longName": "http-request"
}
],
"rules": [
{
"key": "checkstyle:com.puppycrawl.tools.checkstyle.checks.coding.MagicNumberCheck",
"name": "Magic Number",
"status": "READY",
"lang": "java",
"langName": "Java"
}
],
"users": [
{
"login": "admin",
"name": "Administrator",
"active": true,
"email": "admin@sonarqube.org"
}
]
}
서비스 호출 실패 시에는 아래와 같이 메세지가 전달된다.
{
"errors": [
{
"msg": "Value of parameter 'statuses' (REOPEN) must be one of: [OPEN, CONFIRMED, REOPENED, RESOLVED, CLOSED"
}
]
}
Excel 매크로 작성
Excel
로 Export
하는 매크로 작성 순서는 아래와 같다.
- Visual Basic Editor를 사용하여 매크로 모듈 생성
- 조회 파라미터 조건 설정
MSXMLl2.ServerXMLHTTP.6.0
라이브러리를 통해Web Service API
를 호출- 결과 json 오브젝트를 vbscript 오브젝트들로 변환
- VB-JSON 오픈소스 vb6 클래스 라이브러리 사용
- 워크시트에 조회해 온 내용 삽입
윈도우면 개발자 도구
- Visual Basic Editor
를 실행해서 메뉴
- 삽입
- 모듈
을 통해 모듈 하나를 생성하고 아래와 같이 코드를 추가한다.
Private Const REQUEST_COMPLETE = 4
Private Const PAGE_SIZE = 100
Private Const DATA_FIRST_ROW = 12
Private m_xmlhttp as Object
Private m_respone as String
Pirvate m_currentRow as Long
Private m_currentPageIndex as Long
' 쉬트 내용 초기화
Sub initSheet()
Dim sht as Worksheet
Dim lastRow as Long
Set sht = ThisWorkbook.Worksheets("Sheet1")
lastRow = sht.Range("A11").CurrentRegion.rows.Count
If lastRow > 1 Then
rows(DATA_FIRST_ROW & ":" & lastRow + DATA_FIRST_ROW).EntireRow.Delete
End If
End Sub
' Sonar Web Service 호출
Sub getSonarData_Click()
Dim reportQueryParamaters as String
Dim reportQuery As String
If Not IsEmpty(Range("componentKeysParameter")) Then
reportQueryParameters = reportQueryParameters & "&componentKeys=" & Range("componentKeysParameter").Value
End If
If Not IsEmpty(Range("statusesParameter")) Then
reportQueryParameters = reportQueryParameters & "&statuses=" & Range("statusesParameter").Value
End If
If Not IsEmpty(Range("severitiesParameter")) Then
reportQueryParameters = reportQueryParameters & "&severities=" & Range("severitiesParameter").Value
End If
reportQuery = Range("sonarServerURL").Value & "/api/issues/search" & "?" & reportQueryParameters + "&ps=" + PAGE_SIZE
Call getReportData(reportQuery, 1)
End Sub
' HTTP 연결을 통해 Sonar Web Service 호출
Sub getReportData(reportQuery As String, pageIndex as Long)
On Error GoTo handleError
Dim jsonObj As Dictionary
Dim jsonRows as Collection
Dim pagingInfo As Dictionary
Dim jsonRow as Dictionary
Dim jsonVal as String
Dim ws As Worksheet
Dim currentRow as Long
Dim startColumn As Long
Dim element
Dim totalCnt As Long
Set m_xmlhttp = CreateObject("MSXML2.ServerXMLHTTP.6.0")
m_xmlhttp.Open "GET", reportQuery & "&p=" & pageIndex, False
m_xmlhttp.setRequestHeader "Content-Type", "application/text"
m_xmlhttp.setRequestHeader "Accept", "application/text"
m_xmlhttp.send
If m_xmlhttp.readyState = REQUEST_COMPLETE Then
m_response = m_xmlhttp.responseText
Else
MsgBox "xmlhttp send error"
Exit Sub
end If
' VBA.json을 사용하여 json 데이터 변환
Set jsonObj = JSON.parse(m_response)
Set jsonRows = jsonObj("issues")
' 작업할 워크시트 지정
Set ws = Worksheets("Shee1")
For Each jsonRow In jsonRows
' 시작 컬럼 위치
startColumn = 1
For Each element in jsonRow.Items
jsonVal = ""
Select Case TypeName(element)
' textRange 속성은 {} 배열 Dictionary Type으로 변환되어 처리
Case "Dictionary"
For Each key in element.keys
jsonVal = jsonVal + keys + ":" + CSTR(element.Item(key)) + ","
Next
' flowm tags 속성은 [] 배열 Collection Type으로 변환되어 처리
Case "Collection"
For Each colVal in element
jsonVal = jsonVal + colVal + ","
Next
Case Else
jsonVal = element
End Select
If Right(jsonVal, 1) = "," Then
jsonVal = Mid(jsonVal, 1, Len(jsonVal) -1)
End If
ws.Cells(m_currentRow, startColumn) = jsonVal
startColumn = startColumn + 1
Next element
m_currentRow = m_currentRow + 1
Next jsonRow
' 전체 페이지 수 가져오기
Set pagingInfo = jsonObj("paging")
totalCnt = pagingInfo.Item("total")
' 전체 결과를 얻오 올때까지 재귀 호출
If totalCnt > PAGE_SIZE * pageIndex Then
Call getReportData(reportQuery, pageIndex + 1)
End If
Exit Sub
handleError:
error = MsgBox("The report cannot be generated." & chr(13) & "Please check your parameters.", vbCritical, "Error")
' error인 경우 결과 json 중에 error 배열에 담겨 있는 값을 적절히 프린트 해도 됨
End Sub
이름 정의 및 수식에 이름 사용을 사용하여 조건에 해당되는 셀을 설정하고(위의 코드에서는 componentKeysParameter
, statusesParameter
, severitiesParameter
등을 사용했다.) Web Service API를 호출하여 원하는 엑셀의 영역에 json 결과값을 적절하게 가공 후 표시하면 된다 .
몇 가지 Attribute 들의 배열 표현 형식에 따라 Dictionary나 Collection 오브젝트로 변환되는 경우가 있으니 주의하면 되겠다.
버튼
오브젝트 등에 생성한 매크로 모듈 Function을 호출하면 원하는 결과를 얻을 수 있다.
샘플에서 표현하진 않았지만 다양한 검색조건을 활용할 수도 있고 받아온 데이터는 피벗 테이블 등을 이용해 Back-data뿐만 아니라 통계자료로도 활용할 수 있다.
마치며
경함 상 엑셀로 매크로 작업은 정말 유용하게 쓰일 때가 많기 때문에 매크로 작업 때 쓰이는 몇가지 vbscript
를 다룰줄 안다면 큰 도움이 된다.
그리고 더 좋은 방법이나 아이디어 가지고 계신 분은 꼭 좀 알려주세요~~ ^^
ps. 인터넷이 안되는 PC에서 작업한 결과물을 타이핑 하다보니 실제 코드가 동작 안할 수 있으니 참조만 하시면 됩니다.