REST APIでRedmineアクセス(2)
2025/05/04
今回は具体的な操作を
チケット(Issue): offset設定で全チケットのリスト取得
  • 前回レポートのREST APIチケット取得リストで、最後のIDが[06]であることにお気付きだったでしょうか。
  • INF: HTTP request is successful for [http://localhost:3000/issues.json]
    INF: id=30,     subject=Test 30,        status=新規
    ...中略...
    INF: id=6,      subject=Test 06,        status=新規
    INF: normally finished
  • これはデフォルトパラメータ設定(limit=25, offset=0)だと最後の25のみ取得するからです。limitを超えるチケット情報を取得するには、offsetパラーメータ設定が必須となります。

  • HTTPレスポンスのJSONデータに全チケット数データがあるか覗いてみると total_count であることがわかります。
  • {
        "issues": [
        ...
        ],
        "total_count": 29,
        "offset": 0,
        "limit": 25
    }
  • なので、total_countを超えるまでoffsetをlimit(=25)毎に増やせば(*1)全てのチケットの情報を取得できます。下記はコード(Python)の抜粋です。全体コードはこちら
  •   # getメソッド引数にパラメータ設定を追加する
        sUri     = 'http://localhost:3000/issues.json'
        dHeaders = {'X-Redmine-API-Key': '8dc1f63aうんぬんかんぬん'}
        dParams  = {'offset': 0, 'limit': 25}
        ...略...
        
        iTotalCount = dParams['limit'] # 全チケット(Issue)数初期値はlimitとする
        dIssues = None                 # 戻り値初期設定
        
      # iTotalCountがoffsetより大きい場合HTTPリクエストを繰り返す
        while iTotalCount > dParams['offset']:
          # HTTPリクエスト
            jIssues = requests.get(sUri, headers=dHeaders, params=dParams)
          # 取得データを辞書に変更して追加
            dIssuesCur = jIssues.json()
            dIssues['issues'] += dIssuesCur['issues'] # (注)None時の記述を略しています
          # 全チケット数を取得
            iTotalCount = dIssues['total_count'] # 1回目レスポンス結果で正確なチケット数に
          # offsetのインクリメント
            dParams['offset'] += dParams['limit']
    
    ...
    INF: id=[6],    subject=[Test 06],      status=[新規]
    INF: id=[4],    subject=[Test 04],      status=[新規]
    INF: id=[3],    subject=[Test 03],      status=[新規]
    INF: id=[2],    subject=[Test 02],      status=[新規]
    INF: id=[1],    subject=[Test 01],      status=[新規]
    INF: normally finished
    
  • よく見ると、06 の次が 04 ... 05が抜けていますね。実は終了/closedチケットなので表示されていません。

チケット(Issue): status_id設定で全ステータスのリスト取得
  • 終了/closedチケットも含めた全てをリスト取得したい場合は、パラメータ status_id に '*' を設定します。
  •     dParams  = {'offset': 0, 'limit': 25, 'status_id': '*'}
        ...略...
        jIssues = requests.get(sUri, headers=dHeaders, params=dParams)
    
    INF: id=[6],    subject=[Test 06],      status=[新規]
    INF: id=[5],    subject=[Test 05],      status=[終了]
    INF: id=[4],    subject=[Test 04],      status=[新規]
    INF: id=[3],    subject=[Test 03],      status=[新規]
    INF: id=[2],    subject=[Test 02],      status=[新規]
    INF: id=[1],    subject=[Test 01],      status=[新規]
    INF: normally finished
    

チケット(Issue): 指定ID/番号のチケット情報取得
  • 例えばチケット30番の情報を取得するとしましょう。

  • (※)クリックで拡大

  • これはリクエストURIを '/issues/番号.json' とするだけです。#note-*(コメント)の情報も取得するには、includeパラメータにjournalsを加えます。

  • journalsの辞書配列データを取得後、notesキーの値を参照します。
  • import requests
    import json
    
    sUri     = 'http://localhost:3000/issues/30.json'
    dHeaders = {'X-Redmine-API-Key': '8dc1f63aうんぬんかんぬん'}
    dParams  = {'include': 'journals'}
    
    jIssue = requests.get(sUri, headers=dHeaders, params=dParams)
    dIssue = jIssue.json()['issue']
    
    print(f"id=[{dIssue['id']}], subject=[{dIssue['subject']}], ", end='')
    print(f"assigned=[{dIssue['assigned_to']['name']}], ", end='')
    print(f"description={dIssue['description']}")
    
    for journal in dIssue['journals']:
        print(f"notes={journal['notes']}")
    
    id=[30], subject=[Test 30], assigned=[Altmo Test], description=[Test 30]の説明
    notes=ここにまとめ記述予定
    

チケット(Issue): 指定ID/番号にコメント追加
  • 既存チケットへのコメント追加は、対象チケットIDのURI '/issues/番号.json' にデータを put します。追加するデータは、putメソッドの引数dataJSONデータを指定します。JSONデータの例は下記(*2)です。
  • {
        'issue': {
            'notes': 'REST APIで追加したコメント'
        }
    }
    
  • チケット30へコメントを追加するコード例は下記です。データ変更/追加時はデータ形式(JSON)を明示的に指定します。データはdictで作ってからdumps()メソッドでJSON変換する方が楽かと思います。
  • import requests
    import json
    
    sUri = 'http://localhost:3000/issues/30.json' # チケット30のURI
    
    dHeaders = {} # ヘッダーdict
    dHeaders['X-Redmine-API-Key'] = '8dc1f63aうんぬんかんぬん'
    dHeaders['Content-Type'] = 'application/json' # データがJSONであることを明示的に指定
    
    dData = {} # コメントdict
    dData['issue'] = {}
    dData['issue']['notes'] = 'REST APIで追加したコメント' # コメントdictデータ
    
    jData = json.dumps(dData) # コメントdict → JSONへ変換
    
    objRet = requests.put(sUri, headers=dHeaders, data=jData) # REST APIでput
    
    print(f"return status_code = {objRet.status_code}")
    
  • 成功すると status_code 204が戻ります。Redmine側を見るとコメント追加されていることがわかります。

  •   追加されたコメント
        
    (※)クリックで拡大

チケット(Issue): チケット(Issue)の追加
  • '/issues.json' URIにJSONデータを post します(putではありません)。JSONデータの例は下記です。
  • {'issue':
        {'project_id': 1}
        {'subject': 'Test 31'}
    }
    
  • project_idキーに指定する値は、Projects を参照します。今回のコード例では project_id に 1(=Sandbox) を指定しています。subjectはチケットのタイトルです。他の値は Creating an issue を参照してください。
  • import requests
    import json
    
    sUri = 'http://localhost:3000/issues.json' # issuesのURI
    
    dHeaders = {} # ヘッダーdict
    dHeaders['X-Redmine-API-Key'] = '8dc1f63aうんぬんかんぬん'
    dHeaders['Content-Type']      = 'application/json'
    
    dData = {} # チケットdict
    dData['issue'] = {}
    dData['issue']['project_id'] = 1      # project_id: 1
    dData['issue']['subject'] = 'Test 31' # チケットのタイトル
    
    jData = json.dumps(dData) # JSONへ変換
    print(json.dumps(dData, indent=2))
    
    objRet = requests.post(sUri, headers=dHeaders, data=jData) # REST APIでpost
    
    print(f"return status_code = {objRet.status_code}")
    
  • 成功すると status_code 201が戻ります。「チケット31」が指定題名[Test 31]で追加されていますね。

チケット(Issue): チケット(Issue)の削除
  • 最後はチケットの削除ですが、チケットを指すURI /issues/番号.jsondelete を送ります。下記は先程追加したチケット31を削除する例です。
  • import requests
    
    sUri = 'http://localhost:3000/issues/31.json' # チケット31のURI
    
    dHeaders = {} # ヘッダーdict
    dHeaders['X-Redmine-API-Key'] = '8dc1f63aうんぬんかんぬん'
    
    objRet = requests.delete(sUri, headers=dHeaders) # REST APIでdelete
    
    print(f"return status_code = {objRet.status_code}")
    
  • 成功すると status_code 204が戻ります。「チケット31」が無くなりました。

次回は
  • 次回は添付ファイルの処理と、Wikiの編集をレポートする予定です。
Notes
  1. limitは最大値100まで設定できます。今回は全チケット数が30のためlimitを変えていません。
  2. journalsキーを使わないのが、直感に逆らいます。
Copyright(C) 2025 Altmo
本HPについて