PowershellとOutlookVBAを使ってRedmineAPIで既存のチケットにファイルを(ほぼ)自動添付する#004

概要

 定期的にOutlookメールで通知されてくる業務カレンダーを、Redmineのチケットにアップロードして開発ベンダーに連携する作業が手作業で煩わしく、自動化したい欲求にかられたため着手した。幸いにもメールタイトルが正規表現可能(正し性善説仕様のため、送信者が表現の範疇を超えて誤字るとコケるw)

Qiitaに掲載しようと思った経緯は、Powershellでチケットの起票方法はあったがファイル添付のやり方が意外とGoogle先生に聞いてもヒットしなかったので、備忘録も兼ねて記事を書く事にしました(^q^)

(4/5回)

※この業務は2年前の物と古くRedmineのVersionは2.5

第一回 課題概要とAPI使用のための事前準備
第二回 OutlookVBAでメールトリガーを作成する
第三回 Powershellでプロキシを通過する
第四回 PowershellでRedmineチケットにファイルを添付する(前半) ←今ここ
第五回 PowershellでRedmineチケットにファイルを添付する(後半)


 いよいよ本題であるRedmineチケットにファイルを添付します(∩´∀`)∩
Redmineのチケットに添付ファイルをアップロードするには、1. まずファイルをアップロードする2. アップロードしたファイルをチケットに紐づけるという二段階右折の様な事をしないといけないのです( ゚Д゚)メンドクセー

その前に先にソース要りますか❓❓( ^^) _旦~~


ソースコード

$baseuri = ★RedmineのURL★
$apikey = ★先ほど取得したAPIキー文字列★
$T = Get-Date                                       #ファイル識別に日付文字列を使用するため宣言

### 添付ファイルの処理(OUTLOOK側で保存までは実装)
$xlsFile = Get-Item($args[0])                       #OutloolVBA側でこのスクリプトをCallする時に、添付ファイルパスを引数として渡している
write-host $xlsFile.FullName                        #確認用
Set-CustomCredentials                               #プロキシ認証

### Basic認証(RedmineがBasic認証を採用している場合のみ必須)
$BasicUSER = ★Basic認証ユーザID★
$BasicPASSWD = ★Basic認証ユーザパスワード★
$pair = "$($BasicUSER):$($BasicPASSWD)"
$encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($pair))
$basicAuthValue = "Basic $encodedCreds"

### HTTPヘッダを指定する
$Headers = @{
    Authorization = $basicAuthValue                 #Basic認証が必要な場合のみ指定する
    "X-Redmine-API-Key" = $apikey
}

### 登録したいチケットの確認(↓の例ではチケット番号7074を指定している)
$get = Invoke-RestMethod -Uri ($baseuri + "issues/7074.json?key=" + $apikey) -method get -Headers $Headers
Write-Output 'チケット:\n' + $get.issue             #確認用

### メイン処理開始
If($get.issue.subject.ToString() -match ★チケットタイトルを表す正規表現文字列★){

    ###ファイルアップロード
    $post1 = Invoke-RestMethod -Uri ($baseuri + "uploads.json") -method post -InFile $xlsFile -ContentType "application/octet-stream" -Headers $Headers
    Write-Output "token: " + $post1.upload.token    #確認用

    ###アップロードパラメタのセット
    $token = $post1.upload.token
    $fileName = $xlsFile.Name
    $issueId = $get.issue.id
    $projectId = $get.issue.project.id
    $description = $T.Year.ToString() + '-' + $T.Month.ToString() + '-' + $T.Day + ' Uploaded.'
    $contentType = if ($testFile.Name -match "xlsx") {"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"} else {"application/vnd.ms-excel"}
    $issue_json = @"
{"issue": 
    {
        "project_id": $projectId, 
        "uploads": [
            {
                "token": "$token",
                "filename": "$fileName",
                "description": "$description",
                "content_type": "$contentType"
            }
         ]
    }
}
"@

    try{
        $post2 = Invoke-RestMethod -Uri ($baseuri + "issues/" + $issueId  + ".json") -Method put -Headers $Headers -body $issue_json -ContentType "application/json"
    }catch [System.Net.WebException]{
        ### HTTPステータスコード取得
        $statusCode = $_.Exception.Response.StatusCode.value__

        ### レスポンス文字列取得
        $stream = $_.Exception.Response.GetResponseStream()
        $reader = New-Object System.IO.StreamReader $stream
        $reader.BaseStream.Position = 0
        $reader.DiscardBufferedData()
        $responseBody = $reader.ReadToEnd()
        Write-Output "results: " $responseBody      #確認用
    }

    Remove-Item $xlsFile

}else{
    Write-Host "チケット番号が違います。getしたチケット【題名】:" + $get.issue.subject
}


着手

4.PowershellからRedmineAPIでチケットにファイルをアップロードする

 それではやっている事を1セクションずつに区切って紹介します。


1. RedmineのURLとAPIキーのセット

 はじめに、普通にGUIでアクセスするRedmineと同じURLを\$baseuri変数にセットする。同様に\$apikeyに先ほど取得したAPIキー文字列をセット。

$baseuri = "[RedmineのURL]"
$apikey = "[先ほど取得したAPIキー文字列]"
$T = Get-Date                                       #ファイル識別に日付文字列を使用するため宣言しています(必須ではありません)

2. アップロードしたいファイルを取得する

 今回はOutlookVBAからこのスクリプトを実行する際に、メールに添付されているファイルのパスを第一引数として渡しているため、\$args[0]をGet-Itemメソッドで\$xlsFile変数にセットしています。その後、しれっとプロキシ認証をしています。

### 添付ファイルの処理(OUTLOOK側で保存までは実装)
$xlsFile = Get-Item($args[0])                       #OutloolVBA側でこのスクリプトをCallする時に、添付ファイルパスを引数として渡している
write-host $xlsFile.FullName                        #確認用
Set-CustomCredentials                               #プロキシ認証が必要な場合のみ必須

3. RedmineのBasic認証(必要な場合のみ)

 もしもRedmineがBasic認証を有効にしている場合、下記の通り認証情報をセットする必要があります。

### Basic認証(RedmineがBasic認証を採用している場合のみ必須)
$BasicUSER = "[Basic認証ユーザID]"
$BasicPASSWD = "[Basic認証ユーザパスワード]"
$pair = "$($BasicUSER):$($BasicPASSWD)"
$encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($pair))
$basicAuthValue = "Basic $encodedCreds"

4. Rest用HTTPヘッダの指定

 ここでは、APIキーを"X-Redmine-API-Key"(固定)というキーにセットします。また、Basic認証が必要な場合は合わせて指定します。
また、(例は割愛しますが)APIキーは下記の方法と同じ様にPOSTパラメタとして渡す事も出来ます。

### HTTPヘッダを指定する
$Headers = @{
    Authorization = $basicAuthValue                 #Basic認証が必要な場合のみ指定する
    "X-Redmine-API-Key" = $apikey
}

5. 添付したいチケットを取得する

 ここで、既存のチケット番号からチケット情報を取得しています。ファイル添付ごとに新たなチケットを立てたい場合はここの処理は不要です。

### 登録したいチケットの確認(↓の例ではチケット番号7074を指定している)
$get = Invoke-RestMethod -Uri ($baseuri + "issues/7074.json?key=" + $apikey) -method get -Headers $Headers
Write-Output 'チケット:\n' + $get.issue             #確認用

6. メイン処理開始

 ここからメイン処理となります。念のため最初に5. で取得したチケット情報が間違い無いことをチケットのタイトルで照合しています。例はタイトルですが、それ以外のプロパティで照合しても構いません。また、新規チケットの場合はここの処理は不要です。

### メイン処理開始
If($get.issue.subject.ToString() -match "[チケットタイトルを表す正規表現文字列]"){

7. ファイルのアップロード処理

 先述の通りRedmineAPIでファイルをアップロードする場合、下記2段階の処理が必要になります。このセクション7では①を説明します。

① ファイルをアップロードする
② アップロードしたファイルとチケットを紐づける

 ここでRestAPIを実行します。Restの概念の説明等細かい話はここでは割愛しますが、ファイルをアップロードするためにはHTTPのPOSTメソッドを利用します。また、URLは\$baseuriに加え、uploads.json(またはuploads.xml)固定です。jsonかXMLかは好みや環境に応じて選択してください。(本記事はjsonで記載します)

    ###ファイルアップロード
    $post1 = Invoke-RestMethod -Uri ($baseuri + "uploads.json") -method post -InFile $xlsFile -ContentType "application/octet-stream" -Headers $Headers
    Write-Output "token: " + $post1.upload.token    #確認用

 上記の通り、ファイルアップロード時のContent-Typeには必ず"application/octet-stream"を指定します。(本家サイトでもそのように記載があります
無事アップロードが成功するとInvoke-RestMethodの戻り値として、②で使用するためのトークンを含んだオブジェクトが返されます。\$post1.upload.tokenとするとトークン文字列の参照が可能です。

ここでファイルの渡し方について少しハマったため説明を加えます。

 当初、どんな具合でファイル転送をすればいいのか感覚をつかむためグーグル先生に聞いてみました。すると、既に先人がうpしているRubyやらPythonやらがヒットしたので、これらのソースコードを見ると、「ふむふむ、どうやらストリームで渡しているようだ。Content-Typeもoctet-streamだし」と思ってGet-Itemではなく、Get-Contentを使って下記の通り実装をしてみたのですが、、、

$xlsFile = Get-Item($args[0])                       #OutloolVBA側でこのスクリプトをCallする時に、添付ファイルパスを引数とし

↑の部分を
↓↓↓の様にしていました

$xlsFile = Get-Content -Stream $args[0]

これでセクション7の通りPOSTすると、、、エラーが返ってくるんですね。。。(´・ω・`)
(スクショ取り忘れた…)

で、試しに今掲載しているGet-Itemにしてみたらエラーが出なくなったというお話です(´・ω・`)
Powershell時々フシギネ(´・ω・`)


まだ続きますが、長いのでここで一旦切ります(`・ω・)
次回はUPした画像をチケットに紐づけます!

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.