LoginSignup
13
2

More than 3 years have passed since last update.

PaizaIO API でコードジャッジシステムを作ってみた

Posted at

この記事は、ニフティグループ Advent Calendar 2020 21日目の記事です。
昨日は@D_Wさんで、「GitHub Actionsでプルリク時にStoryBookをS3にデプロイする」でした。
StoryBookはまだ利用したことがなかったので、機会があれば参考にさせていただきます!

はじめに

社内のReact勉強会で「Reactを用いて何か作ってみよう」というアウトプット回があったので、その時間を使ってコードジャッジシステムを作成してみました。
ソースコードに対して、予め用意したテストケースを入力として与え、期待する結果と等しいか判定するシステムです。
image.png

コードジャッジシステムを自作しようとすると、多言語対応したソースコードの実行処理が大変ですが、Paiza IO APIを使うととても簡単にコード実行処理を実装できます。
この記事では、多言語対応のコード実行処理を簡単に実装できる Paiza IO API について解説します。

Paiza IO API について

Paiza IO APIでは以下3つのAPIが利用可能です。

  • create:任意のソースコードをAPIに投げて実行
  • get_status:投げたコードの実行状況を取得
  • get_details:投げたソースコードの実行結果を取得

それぞれの使い方を詳しくみていきます。

create:任意のソースコードをAPIに投げて実行

create はPOSTリクエストでソースコードや実行言語、標準入力の値など、実行時に必要な情報をAPIに投げて実行してもらうリクエストです。
レスポンスは実行結果ではなく、API側で管理しているセッションIDと、実行時のステータスが返ってきます。
ここで返されたセッションIDを利用して、再度実行結果をGETリクエストで取得する必要があります。

パラメータ

パラメータ 値の例 説明 必須
source_code print("hoge") 実行したいソースコード string
language python3 実行言語 string
input 10 標準入力 string
longpoll true ロングポーリングを有効にするかどうか。初期値はfalse boolean
longpoll_timeout 5.0 ロングポーリングを有効にする場合のタイムアウト時間。初期値は10.0 double
api_key guest APIキー string

実行可能言語一覧

実行可能な言語は以下の通りです。

言語
C言語 c
C++ cpp
Objective-C objective-c
Java java
Kotlin kotlin
Scala scala
Swift swift
C# csharp
Go go
Haskell haskell
Erlang erlang
Perl perl
Python python
Python3 python3
Ruby ruby
PHP php
Bash bash
R r
JavaScript javascript
CoffeeScript coffeescript
Visual Basic vb
Cobol cobol
F# fsharp
D d
Clojure clojure
Elixir elixir
MySQL mysql
Rust rust
Scheme scheme
CommonLisp commonlisp
Plain plain

ロングポーリングについて

パラメータで指定可能なロングポーリングのありとなしの違いについてです。
一言で説明すると、リクエストしたソースコードの実行完了まで待機するかしないかの違いです。

具体例を用いて説明します。
例えば、以下コードの実行結果を得ようとPaiza IO APIにリクエストを送ります。

sample.py
cnt = 0
while(cnt < 10**9):
    cnt += 1
print(cnt)

10の9乗回ループしているので、およそ1秒かかる処理です。
上記のコードをロングポーリング無しでリクエストを投げると、APIのリクエストに必要な時間が経過した後に以下のレスポンスが返ってきます。

レスポンス
{
  id: "XXXXXXXXXXXXXXX" // セッションID
  status: "running"     // 実行状態
}

一方で、ロングポーリングを有効にすると、APIのリクエストにかかる時間+コードの実行時間が経過した後に以下のレスポンスが返ってきます。

レスポンス
{
  id: "XXXXXXXXXXXXXXX" // セッションID
  status: "completed"     // 実行状態
}

つまり、完了を待つか待たないかの違いです。
APIにリクエストを投げた後に、コード実行中も別の処理を行いたい場合はロングポーリングは無効にし、コード実行リクエストから実行結果を得るまで別の処理を行わない場合は、ロングポーリングを有効にすると良いと思います。

リクエスト

JavaScriptでcreateリクエストを送るサンプルコードです。

JavaScriptでcreateリクエストを送るサンプルコード
async function postData(url = "", data = {}) {
  const response = await fetch(url, {
    method: "POST",
    mode: "cors",
    cache: "no-cache",
    credentials: "same-origin",
    headers: {
      "Content-Type": "application/json",
    },
    redirect: "follow",
    referrerPolicy: "no-referrer",
    body: JSON.stringify(data),
  });
  return response.json();
}

async function main(){
  const url = "http://api.paiza.io:80/runners/create";
  const data = {
    source_code: "print(input())",
    language: "python3",
    input: "hellow world",
    api_key: "guest",
  };
  const res = await postData(url, data);
}

main();

レスポンス

レスポンスはJSON形式で以下の通りです。
ここで受け取ったセッションIDを用いて、実行状態や実行結果を問い合わせることができます。

レスポンス
{
  id: "XXXXXXXXXXXXXXX" // セッションID
  status: "running"     // 実行状態(実行中:running、実行完了:completed)
}

get_status:投げたコードの実行状況を取得

get_statuscreateリクエストで投げたソースコードの実行状況を、GETリクエストで問い合わせます。
ロングポーリングが無効の場合は実行結果を問い合わせる前に、get_statusで実行状況を確認すると良いと思います。

パラメータ

パラメータ 値の例 説明 必須
id ab-CdE6FG7hI8JKl セッションID string
api_key guest APIキー string

リクエスト

JavaScriptでget_statusリクエストを送るサンプルコードです。

JavaScriptでget_statusリクエストを送るサンプルコード
async function getData(url) {
  const response = await fetch(url);
  return response.json();
}

async function main() {
  const sessionId = "XXXXXXXXXXXXXXXX";
  const url = `http://api.paiza.io/runners/get_status?id=${sessionId}&api_key=guest`;
  const response = await getData(url);
}

main();

レスポンス

レスポンスはJSON形式で以下の通りです。

レスポンス
{
  id: "XXXXXXXXXXXXXXX" // セッションID
  status: "completed"   // 実行状態(実行中:running、実行完了:completed)
}

get_details:投げたソースコードの実行結果を取得

get_detailsはGETリクエストでcreateリクエストで投げたソースコードの実行結果を取得します。
get_detailsで実行結果を得るには、リクエストしたコードの実行が完了している必要があります。

パラメータ

パラメータ 値の例 説明 必須
id ab-CdE6FG7hI8JKl セッションID string
api_key guest APIキー string

リクエスト

JavaScriptでget_detailsリクエストを送るサンプルコードです。

JavaScriptでget_detailsリクエストを送るサンプルコード
async function getData(url) {
  const response = await fetch(url);
  return response.json();
}

async function main() {
  const sessionId = "XXXXXXXXXXXXXXXX";
  const url = `http://api.paiza.io/runners/get_details?id=${sessionId}&api_key=guest`;
  const response = await getData(url);
}

main();

レスポンス

レスポンスはJSON形式で以下の通りです。

レスポンス
{
  "id": "eFES9ngIykoZovYasRlA0w",
  "language": "python3",
  "status": "completed",
  "build_stdout": null,
  "build_stderr": null,
  "build_exit_code": 0,
  "build_time": null,
  "build_memory": 9632000,
  "build_result": null,
  "stdout": "hello world\n",
  "stderr": "",
  "exit_code": 0,
  "time": "0.02",
  "memory": 9632000,
  "result": "success"
}

各プロパティの意味は以下の通りです。

パラメータ 説明
id セッションID
language 実行言語
status 実行状況。実行中の場合はrunning、実行完了の場合はcompleted
build_stdout ビルド実行後、正常終了時の標準出力
build_stderr ビルド実行後、異常終了時の標準出力
build_exit_code ビルド実行後の終了コード
build_time ビルド実行にかかった時間(秒)
build_memory ビルド実行に使ったメモリ(byte)
build_result ビルド結果。正常終了:success, 以上終了:failure, タイムアウト(最大1秒):timeout
stdout コード実行後、正常終了時の標準出力
stderr コード実行後、異常終了時の標準出力
exit_code コード実行後の終了コード
time コードの実行にかかった時間(秒)
memory コードの実行に使ったメモリ(byte)
result コードの実行結果。正常終了:success, 以上終了:failure, タイムアウト(最大1秒):timeout

おわりに

以上が、PaizaIO APIについての説明でした。
コードジャッジシステムを自作しようとすると、多言語実行処理の実装という大きな壁が存在しますが、このようなAPIが用意されているととても助かります。
明日は @mito1296 さんです。お楽しみに!

13
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
2