EN JP CN

検索 API の使用

検索 API の使用

Code Review 検索を使用すると、検索条件に基づいて、リビジョン、コメント、およびアクションのリストを生成できます。また、オプションで、検索条件でフィルターされた、レポートのようなサマリーも生成できます。

出力は JSON 形式で、1 行ごとに 1 つのレコードで構成されます。すべての検索要求の最後の行は、'summary' JSON オブジェクトになります。

たとえば、簡単な検索のサマリーオブジェクトを要求すると、サマリー行には以下の内容が含まれます。

1 {"summary":{"query":"reviews:pending","item_count":28,"warnings":[]}}

フィールド

  • query - 検索の実行に使用されたクエリ
  • item_count - 返されたレコードの数
  • warnings - 警告 (パラメーター指摘、検索構文) のリスト

サマリー検索のサマリーオブジェクトは以下のとおりです。

1 {"summary" {"query":"reviews:pending","rows":1,"columns":1,"entity":"commits","x":"","sum":"","warnings":[]}}

フィールド

  • query - 上記と同様の検索クエリ
  • rows、columns - テーブルデータで返される行および列の数
  • entity、x、および sum - 選択したエンティティタイプ、x、および合計値を一覧表示します
  • warnings - 警告 (パラメーター指摘、検索構文など) のリスト

検索結果は、1 行ごとに 1 つのレコードで構成されます。レコードは、JSON 表現のコミット、コメント、またはアクションになります。

検索サマリー結果は、テーブルデータ (JSON ディクショナリ) で構成されます。各要求について、コミット、コメント、アクション、またはレビューのいずれか 1 つしか集計できません。たとえば、次のようになります。

  • entity=commits
1{"commits":{"commits":8}}
  • entity=commits&x=reviewer
1{"commits":{"reviewer":{"test1":2,"rsherk":1,"test4":2,"Nancy Jones":1,"test2":4,"test3":2}}}
  • entity=commits&x=committer&sum=reviewer
1{"reviewer":{"Nancy Jones":{"committer":{"adhocuser":0,"vzakaznikov":0,"Fred Smith":1}}}}

使用方法:

  • 検索クエリに一致するすべてのエンティティを返す単純検索:action=search&query=*
  • コミットの単純なサマリー: action=search&entity=commits
  • 検索条件に一致する項目のサマリー: action=search&query=reviews:pending&entity=commits
  • 作成者およびステート別のアクションサマリー: action=search&entity=actions&x=author&sum=state

次の例について

次の例をコピーして貼り付け、実行します。以下の例で使用するこのモジュール (kwutil) では、システムからの ltokens の読み取りを扱います。読みやすいように別々のモジュールになっています。以下のコードを注意して切り取って貼り付け、kwutil モジュールを作成してください。

import socket, re, platform, os.path

def getToken(host, port, user):
    if host is "localhost":
        host = socket.gethostname()
    userHomePath = os.path.expanduser("~")
    if re.search("Windows", platform.platform()) != None:
        userHomePath = os.environ['UserProfile']
    ltoken = os.path.normpath(os.path.join(userHomePath, ".klocwork", "ltoken"))
    ltokenFile = open(ltoken, 'r')
    for r in ltokenFile:
        rd = r.strip().split(';')
        if rd[0] == socket.getfqdn(host) and rd[1] == str(port) and rd[2] == user:
            ltokenFile.close()
            return rd[3]
    ltokenFile.close()
    return None

例:タグの割り当て

この例では、選択したリビジョンのタグの割り当て方法を示します。

import urllib, urllib2, json, sys, os.path, getpass, time

import kwutil


def assign_tags(url, user, tags, query):
    values = {"action": "assign_tags",
              "user": user,
              "tags": tags,
              "query": query}
    loginToken = kwutil.getToken(host, port, user)
    if loginToken is not None:
        values["ltoken"] = loginToken
    data = urllib.urlencode(values)
    req = urllib2.Request(url, data)
    urllib2.urlopen(req)

host = "localhost"
port = 8080
user = getpass.getuser()

tags = "tag1,tag2"
query = "revision:54321"

url = "http://%s:%d/codereview/api" % (host, port)

try:
    assign_tags(url, user, tags, query)
    print "Assigned tags!"
except urllib2.HTTPError, error:
    sys.stderr.write('ERROR: %s\n' % str(error))
    sys.stderr.write('%s\n' % error.read())

例:タグの一覧表示

この例では、リビジョンに関連付けられたすべてのタグの一覧表示方法を示します。

import urllib, urllib2, json, sys, os.path, getpass, time

import kwutil


class Tag(object):
    def __init__(self, attrs):
        self.name = attrs["name"]

    def __str__(self):
        result = "%s" % (self.name)
        return result


def from_json(json_object):
    return Tag(json_object)


def list_tags(url, user):
    values = {"action": "list_tags",
              "user": user}
    loginToken = kwutil.getToken(host, port, user)
    if loginToken is not None:
        values["ltoken"] = loginToken
    data = urllib.urlencode(values)
    req = urllib2.Request(url, data)
    response = urllib2.urlopen(req)
    result = []
    for record in response:
        result.append(json.loads(record, object_hook=from_json))
    return result


host = "localhost"
port = 8080
user = getpass.getuser()

url = "http://%s:%d/codereview/api" % (host, port)

try:
    tags = list_tags(url, user)

    print "Listing tags:"
    for tag in tags:
        print tag
except urllib2.HTTPError, error:
    sys.stderr.write('ERROR: %s\n' % str(error))
    sys.stderr.write('%s\n' % error.read())

例:タグの削除

この例では、タグの削除方法を示します。

import urllib, urllib2, json, sys, os.path, getpass, time

import kwutil

def remove_tag(url, user, tag_name):
    values = {"action": "remove_tag",
              "user": user,
              "name": tag_name}
    loginToken = kwutil.getToken(host, port, user)
    if loginToken is not None:
        values["ltoken"] = loginToken
    data = urllib.urlencode(values)
    req = urllib2.Request(url, data)
    urllib2.urlopen(req)

host = "localhost"
port = 8080
user = getpass.getuser()

tag = "tag1"

url = "http://%s:%d/codereview/api" % (host, port)

try:
    remove_tag(url, user, tag)
    print "Removed tag!"
except urllib2.HTTPError, error:
    sys.stderr.write('ERROR: %s\n' % str(error))
    sys.stderr.write('%s\n' % error.read())

例:タグ名の変更

この例では、タグ名の変更方法を示します。

import urllib, urllib2, json, sys, os.path, getpass, time
import kwutil


def rename_tag(url, user, name, new_name):
    values = {"action": "rename_tag",
              "user": user,
              "name": name,
              "new_name": new_name}
    loginToken = kwutil.getToken(host, port, user)
    if loginToken is not None:
        values["ltoken"] = loginToken
    data = urllib.urlencode(values)
    req = urllib2.Request(url, data)
    urllib2.urlopen(req)


host = "localhost"
port = 8080
user = getpass.getuser()

name = "tag1"
new_name = "tag2"

url = "http://%s:%d/codereview/api" % (host, port)

try:
    rename_tag(url, user, name, new_name)
    print "Renamed tag!"
except urllib2.HTTPError, error:
    sys.stderr.write('ERROR: %s\n' % str(error))
    sys.stderr.write('%s\n' % error.read())

例:例:タグの割り当て解除

この例では、選択したリビジョンのタグの割り当ての解除方法を示します。

import urllib, urllib2, json, sys, os.path, getpass, time

import kwutil


def unassign_tags(url, user, tags, query):
    values = {"action": "unassign_tags",
              "user": user,
              "tags": tags,
              "query": query}
    loginToken = kwutil.getToken(host, port, user)
    if loginToken is not None:
        values["ltoken"] = loginToken
    data = urllib.urlencode(values)
    req = urllib2.Request(url, data)
    urllib2.urlopen(req)


host = "localhost"
port = 8080
user = getpass.getuser()
tags = "tag1,tag2"
query = "revision:54321"

url = "http://%s:%d/codereview/api" % (host, port)

try:
    unassign_tags(url, user, tags, query)
    print "Unassigned tags!"
except urllib2.HTTPError, error:
    sys.stderr.write('ERROR: %s\n' % str(error))
    sys.stderr.write('%s\n' % error.read())

例:リビジョン、コメント、およびアクションの検索

この例では、検索条件に基づいて、リビジョン、コメント、およびアクションのリストを生成する方法を示します。この方法は、オプションで、検索条件でフィルターされた、レポートのようなサマリーの生成にも使用できます。

import urllib, urllib2, json, sys, os.path, getpass, time

import kwutil


def search(url, user, query):
    values = {"action": "search",
              "user": user,
              "query": query}
    loginToken = kwutil.getToken(host, port, user)
    if loginToken is not None:
        values["ltoken"] = loginToken
    data = urllib.urlencode(values)
    req = urllib2.Request(url, data)
    response = urllib2.urlopen(req)
    result = []
    for record in response:
        result.append(json.loads(record))
    return result

host = "localhost"
port = 8080
user = getpass.getuser()

query = "type:commit commit_type:pre user:userA"

url = "http://%s:%d/codereview/api" % (host, port)

try: 
     results = search(url, user, query)
     for result in results:
         print result
except urllib2.HTTPError, error:
    sys.stderr.write('ERROR: %s\n' % str(error))
    sys.stderr.write('%s\n' % error.read())

コードレビューをインポートするには

次の例では、指定されたプロジェクトまたはタグについて Code Review が収集したデータをインポートします。

import urllib, urllib2, json, sys, os.path, getpass, time

import kwutil

class ImportStatus(object):
    def __init__(self, project, attrs):
        self.project = project
        self.stage = attrs["stage"]
        self.progress = attrs["progress"]
        self.failed = attrs["failed"]
        self.hasWarnings = attrs["hasWarnings"]
        self.projectReady = attrs["projectReady"]
        self.complete = attrs["complete"]


    def __str__(self):
        return "Project: %s\n\tStage: %s | Progress: %s%% | Failed: %s | Warnings: %s | Project Ready: %s | Complete: %s" % (
            self.project, self.stage, self.progress, self.failed, self.hasWarnings, self.projectReady, self.complete)


def import_status(url, user):
    values = {"action": "import_status", "user": user}

    loginToken = kwutil.getToken(host, port, user)
    if loginToken is not None:
        values["ltoken"] = loginToken

    data = urllib.urlencode(values)
    req = urllib2.Request(url, data)

    response = urllib2.urlopen(req)
    importStatus = []

    for record in response:
        attrs = json.loads(record)
        for key in attrs.keys():
            importStatus.append(ImportStatus(key, attrs[key]))
    return importStatus

def import_codereview(url, user, projects, sourceURL, sourceAdmin, sourcePassword, tagMapping, tags, untaggedProject):
    values = {"user": user,
              "action": "import_codereview",
              "projects": projects,
              "sourceURL": sourceURL,
              "sourceAdmin": sourceAdmin}

    if tags is not None:
        values["tags"] = tags

    if tagMapping is not None:
        values["tagMapping"] = tagMapping

    if untaggedProject is not None:
        values["untaggedProject"] = untaggedProject

    if sourcePassword is not None:
        values["sourcePassword"] = sourcePassword

    loginToken = kwutil.getToken(host, port, user)
    if loginToken is not None:
        values["ltoken"] = loginToken

    data = urllib.urlencode(values)
    req = urllib2.Request(url, data)
    response = urllib2.urlopen(req)
    result = []
    for record in response:
        result.append(json.loads(record))

def wait_for_import(url, user, project):
    isTimeout = False
    TIME_OUT = time.time() + 60 * 20
    incomplete = [project]

    if len(incomplete) == 0:
        return

    while True:
        for status in import_status(url, user):
            if status.project != project:
                continue

            # If all projects are complete then exit the loop
            if len(incomplete) == 0:
                break

            if status.project in incomplete:
                isTimeout = time.time() > TIME_OUT

            if status.complete or status.failed:
                print status.stage
                incomplete.pop(incomplete.index(status.project))
                break
            elif isTimeout:
                print "Import of project '%s' took longer than expected." % status.project
                print "Check if import is still progressing."
                sys.exit(-1)

        # If all projects are complete then exit the loop
        if len(incomplete) == 0:
            break

        time.sleep(10)

host = "localhost"
port = 8080
user = getpass.getuser()

projects = "projectA"
sourceURL = "http://oldhost:8080"
sourceAdmin = "old admin user name"
sourcePassword = None
tagMapping = None
tags = None
untaggedProject = None

url = "http://%s:%d/codereview/api" % (host, port)

try:
    import_codereview(url, user, projects, sourceURL, sourceAdmin, sourcePassword, tagMapping, tags, untaggedProject)
    #Code review imports have a special project key: @codereview_sync
    wait_for_import(url, user, '@codereview_sync')
    print "Imported codereview!"
except urllib2.HTTPError, error:
    sys.stderr.write('ERROR: %s\n' % str(error))
    sys.stderr.write('%s\n' % error.read())

リビジョン ID を取得するには

次の例では、検索結果のコミットからリビジョン ID のリストを取得する方法を示します。これらのリビジョン ID は、以下の 'review'、'review_override'、'add_reviewers'、および 'commit_details' アクションを使用する場合に使います。

import urllib, urllib2, json, sys, os.path, getpass, time

import kwutil

'''Get a list of <keyword keyref="shortCRprod"/> revision ids from commits in search results.
   Revision ids can be used with these API methods:
   review, review_override, add_reviewers and commit_details
'''
def get_revision_ids(url, user, query):
    values = {"action": "search",
              "user": user,
              "query": query}
    loginToken = kwutil.getToken(host, port, user)
    if loginToken is not None:
        values["ltoken"] = loginToken
    data = urllib.urlencode(values)
    req = urllib2.Request(url, data)
    response = urllib2.urlopen(req)
    revision_ids = []
    for record in response:
        r = json.loads(record)
        print r['type']
        if 'type' in r and r['type'] == 'commit':
            revision_ids.append(r['item']['revision_id'])
    return revision_ids

host = "localhost"
port = 8080
user = getpass.getuser()

url = "http://%s:%d/codereview/api" % (host, port)

try: 
    rids = get_revision_ids(url, user, "revision:111643")
    print str(rids)
except urllib2.HTTPError, error:
    sys.stderr.write('ERROR: %s\n' % str(error))
    sys.stderr.write('%s\n' % error.read())

レビューステートを更新するには

次の例では、指定されたリビジョンのレビューステートを更新します。

import urllib, urllib2, json, sys, os.path, getpass, time

import kwutil

def review(url, user, revision_id, state):
    values = {"action": "review",
              "user": user,
              "revision_id": revision_id,
              "state": state}
    loginToken = kwutil.getToken(host, port, user)
    if loginToken is not None:
        values["ltoken"] = loginToken
    data = urllib.urlencode(values)
    req = urllib2.Request(url, data)
    response = urllib2.urlopen(req)
    result = []
    for record in response:
        result.append(json.loads(record))
    return result

host = "localhost"
port = 8080
user = getpass.getuser()

url = "http://%s:%d/codereview/api" % (host, port)

'''Code Review revision id (as opposed to the SCM revision name)
   See get_revision_ids to see how to obtain revision_ids from search results'''
revision_id = 99650
state = "approve" #Can be either "approve" or "reject"

try: 
     results = review(url, user, revision_id, state)
     for result in results:
         print result
except urllib2.HTTPError, error:
    sys.stderr.write('ERROR: %s\n' % str(error))
    sys.stderr.write('%s\n' % error.read())

レビューステートをオーバーライドするには

次の例では、指定されたリビジョンのレビューステートをオーバーライドします。

import urllib, urllib2, json, sys, os.path, getpass, time

import kwutil

def review(url, user, revision_id, state):
    values = {"action": "review_override",
              "user": user,
              "revision_id": revision_id,
              "state": state}
    loginToken = kwutil.getToken(host, port, user)
    if loginToken is not None:
        values["ltoken"] = loginToken
    data = urllib.urlencode(values)
    req = urllib2.Request(url, data)
    response = urllib2.urlopen(req)
    result = []
    for record in response:
        result.append(json.loads(record))
    return result

host = "localhost"
port = 8080
user = getpass.getuser()

url = "http://%s:%d/codereview/api" % (host, port)

'''Code Review revision id (as opposed to the SCM revision name)
   See get_revision_ids to see how to obtain revision_ids from search results'''
revision_id = 99650
state = "approve" #Can be, "approve", "reject" or "clear"

try: 
     results = review(url, user, revision_id, state)
     for result in results:
         print result
except urllib2.HTTPError, error:
    sys.stderr.write('ERROR: %s\n' % str(error))
    sys.stderr.write('%s\n' % error.read())

レビュー担当者をコードレビューに追加するには

次の例では、レビュー担当者を指定されたリビジョンに追加します。

import urllib, urllib2, json, sys, os.path, getpass, time

import kwutil

def review(url, user, revision_id, users):
    values = {"action": "add_reviewers",
              "user": user,
              "revision_id": revision_id,
              "users": ",".join(users)}
    loginToken = kwutil.getToken(host, port, user)
    if loginToken is not None:
        values["ltoken"] = loginToken
    data = urllib.urlencode(values)
    req = urllib2.Request(url, data)
    response = urllib2.urlopen(req)
    result = []
    for record in response:
        result.append(json.loads(record))
    return result

host = "localhost"
port = 8080
user = getpass.getuser()

url = "http://%s:%d/codereview/api" % (host, port)

'''Code Review revision id (as opposed to the SCM revision name)
   See get_revision_ids to see how to obtain revision_ids from search results'''
revision_id = 99650
users = ["jsmith", "epresly"] #a list of reviewer names

try: 
     results = review(url, user, revision_id, users)
     for result in results:
         print result
except urllib2.HTTPError, error:
    sys.stderr.write('ERROR: %s\n' % str(error))
    sys.stderr.write('%s\n' % error.read())

コミットの詳細を確認するには

次の例では、指定されたコミットの詳細ビューを出力します。

import urllib, urllib2, json, sys, os.path, getpass, time

import kwutil

def review(url, user, revision_id, review_report):
    values = {"action": "commit_details",
              "user": user,
              "revision_id": revision_id,
              "review_report": review_report}
    loginToken = kwutil.getToken(host, port, user)
    if loginToken is not None:
        values["ltoken"] = loginToken
    data = urllib.urlencode(values)
    req = urllib2.Request(url, data)
    response = urllib2.urlopen(req)
    result = []
    for record in response:
        result.append(json.loads(record))
    return result

host = "localhost"
port = 8080
user = getpass.getuser()

url = "http://%s:%d/codereview/api" % (host, port)

'''Code Review revision id (as opposed to the SCM revision name)
   See get_revision_ids to see how to obtain revision_ids from search results'''
revision_id = 99650
review_report = False #Set to True to display related file revisions and reviews for each file

try: 
     results = review(url, user, revision_id, review_report)
     for result in results:
         print result
except urllib2.HTTPError, error:
    sys.stderr.write('ERROR: %s\n' % str(error))
    sys.stderr.write('%s\n' % error.read())