Redash의 동적 쿼리를 자바 스크립트에서 호출

사내에서는 Redash를 활용하고 있습니다. Redash는 고정 SQL을 실행하고 시각화하는 것 외에도 일부 값을 매개 변수화하는 동적 쿼리를 실행하는 기능도 있습니다.
이 동적 쿼리를 외부에서 API로, ChromeExtension에서 사용하려고 하면 도하마리했기 때문에 정리해 둡니다.

Redash의 동적 쿼리



다음과 같이 SQL 내에 {{hoge}} 라고 쓴 SQL을 지정하는 것으로 이용할 수 있습니다.
SELECT account_name
FROM member
WHERE login_id = '{{login_id}}'

이러한 파라미터 첨부의 SQL을 지정하면 이하와 같은 입력란이 자동으로 생성됩니다.



사실 입력란에 값을 설정하는 것 외에도 URL의 쿼리 파라미터로 지정할 수 있습니다.
http://your.redash.host/queries/<query_id>/source?p_login_id=100
지정하는 값은 p_parameter=value입니다. login_id라는 매개 변수에 100을 지정하는 경우 p_login_id=100를 URL에 추가하면됩니다.

이것을 외부에서 두드릴 수 있다면 자신의 서버를 준비하지 않고 API로 사용할 수 있습니다.
라고 생각하면 일근줄로는 가지 않았습니다.

외부에서 두드리는 방법



매개변수를 사용하지 않는 쿼리라면 쿼리 편집 화면(source) 아래에서 나오는 URL을 두드리는 것만으로 JSON과 CSV로 결과를 얻을 수 있습니다.



그러나 매개 변수가있는 쿼리의 경우 동일한 방법을 사용하면 예상 결과를 얻을 수 없습니다. 적어도 매개 변수를 지정할 수 없으며 마지막으로 성공한 쿼리의 결과가 반환 동작이 된 것 같습니다.

[Feature Request] Add support for query parameters in the API

라는 issue가 존재해, Close가 되어 있었기 때문에 그것용의 API가 있을까라고 생각하면

Actually this is already possible with the refresh API. See this for example:
htps : // 기 st. 기주 b. 이 m / 예 kfr / 에 3에 434d8cfd7f331d499 ~ cf351 ~ bf9

폐쇄되었습니다. 아무래도 refresh API를 사용해 실현할 수 있기 때문에 그것을 해라라는 것이었습니다. 의외로 이것이 귀찮습니다.

싹둑 흐르게는 다음이었습니다.
  • 지정된 ID의 쿼리에 매개 변수를 전달하여 결과를 업데이트하도록 요청 (refresh)
  • 요청에 대해 JOB ID가 반환되기 때문에 JOB 상태 폴링
  • 작업이 완료되면 query_result_id가 돌아 오기 때문에이를 사용하여 결과 얻기

  • 단지 파라미터 첨부의 결과를 원할 뿐인데 3회 이상 요구하지 않으면 안됩니다.

    issue로 나타나고 있는 파이썬에서의 샘플Redash API에서 동적 쿼리를 실행하는 모듈의 흐름을 참고로, Javascript로 실행해 보았습니다.

    USER Token 발급



    이 절차에는 각 쿼리의 show API Key에서 생성되는 키가 아니라 사용자별로 발급되는 API Key가 필요합니다.Settings > Users > ユーザ名 > API KEY에서 API Key를 확인합니다.

    Javascript로 구현



    Ajax만 할 수 있으면 좋기 때문에, jQuery등은 이용하지 않고 fetch 를 사용합니다. 물론 $.ajax 하지만 axios 하지만 같은 흐름이 된다고 생각합니다. 3회 이상 Ajax 해야 하기 때문에, 보통으로 하면 콜백 투성이로 괴롭기 때문에 async await 로 동기적으로 쓰고 있습니다.

    다음은 쿼리 ID : 99에 login_id라는 매개 변수를 전달하여 실행됩니다.
    async function accessRedash(accountId){
        const API_TOKEN = 'YOUR USER TOKEN not query token.';
        // クエリパラメータ付でrefresh apiを実行
        let response = await fetch('http://your.redash.host/api/queries/99/refresh?p_login_id=' + accountId, {
            method: 'POST',
            headers: {
                'Authorization': `Key ${API_TOKEN}`,
                'Accept': 'application/json',
                'Content-Type': 'application/json'
            }
        }).then(res => {
            return res.json();
        })
        // refresh apiの戻り値にはjobが入っている
        let job = response['job'];
        // job のステータスが3,4になるまで実行(refreshが完了すると脱出する)
        while (job.status !== 3 && job.status !== 4) {
            // job apiで最新のジョブの状況をチェック
            response = await fetch('http://your.redash.host/api/jobs/' + job['id'], {
                headers: {
                    'Authorization': `Key ${API_TOKEN}`,
                    'Accept': 'application/json',
                    'Content-Type': 'application/json'
                }
            }).then(res => {
                return res.json();
            })
            job = response['job'];
            // 0.5秒とりあえず待つ
            await sleep(500);
        }
        // jobが完了している場合query_result_idが手に入る
        const resultId = job['query_result_id'];
        // query_result_id で指定される結果をJSONで受け取る(.jsonと指定することでJSON形式になる)
        response = await fetch(`http://your.redash.host/api/queries/99/results/${resultId}.json`,{
            headers: {
                'Authorization': `Key ${API_TOKEN}`,
                'Accept': 'application/json',
                'Content-Type': 'application/json'
            }
        }).then(res => {
            return res.json();
        })
    
        // query_result.dataに配列で結果が格納されているので後は好きにする
        const resultSet = response.query_result.data;
        console.log(resultSet)
    
    }
    
    // sleep helper
    function sleep(msec){
        return new Promise(resolve => setTimeout(resolve, msec));
    }
    

    async await로 편하게 걸겠습니다만, 솔직하게 쓰고 있으면 몹시 중첩이 깊어져 힘들었을 것이다라고 생각합니다. Redash 3.0에서 시도했지만 어쨌든이 근처의 문서가 적어서 고생했기 때문에 Qiita에게 기사로 남겨 드리겠습니다.

    좋은 웹페이지 즐겨찾기