11import { Response , Router , Request } from 'express'
2- import { SubmissionAttributes , Submissions } from '../../db/models'
2+ import axios from 'axios'
3+ 4+ import { SubmissionAttributes , Submissions , db } from '../../db/models'
35import { RunJob , queueJob , successListener } from '../../rabbitmq/jobqueue'
46import { isInvalidRunRequest } from '../../validators/SubmissionValidators'
7+ import { upload } from '../../utils/s3'
58import config = require( '../../../config' )
69
710const route : Router = Router ( )
811
912export type RunRequestBody = {
1013 source : string , //Base64 encoded
1114 lang : string ,
12- stdin : string
15+ stdin : string ,
16+ mode : string ,
17+ callback ?: string
1318}
1419export interface RunRequest extends Request {
1520 body : RunRequestBody
@@ -21,7 +26,77 @@ export interface RunResponse {
2126 stderr : string
2227}
2328
24- const runPool : { [ x : number ] : Response } = { }
29+ export type RunPoolElement = {
30+ mode : string ,
31+ res : Response ,
32+ callback ?: string
33+ }
34+ 35+ const runPool : { [ x : number ] : RunPoolElement } = { }
36+ 37+ const handleTimeoutForSubmission = function ( submissionId : number ) {
38+ const job = runPool [ submissionId ]
39+ const errorResponse = {
40+ id : submissionId ,
41+ code : 408 ,
42+ message : "Compile/Run timed out" ,
43+ }
44+ 45+ switch ( job . mode ) {
46+ case 'sync' :
47+ job . res . status ( 408 ) . json ( errorResponse )
48+ break ;
49+ case 'callback' :
50+ axios . post ( job . callback , errorResponse )
51+ }
52+ }
53+ 54+ const handleSuccessForSubmission = function ( result : RunResponse ) {
55+ const job = runPool [ result . id ]
56+ switch ( job . mode ) {
57+ case 'sync' :
58+ job . res . status ( 200 ) . json ( result )
59+ break ;
60+ case 'callback' :
61+ // send a post request to callback
62+ ( async ( ) => {
63+ // 1. upload the result to s3 and get the url
64+ const { url} = await upload ( result )
65+ 66+ // 2. save the url in db
67+ await Submissions . update ( < any > {
68+ outputs : [ url ]
69+ } , {
70+ where : {
71+ id : result . id
72+ }
73+ } )
74+ 75+ // make the callback request
76+ await axios . post ( job . callback , { id : result . id , outputs : [ url ] } )
77+ } ) ( )
78+ break ;
79+ }
80+ }
81+ 82+ /**
83+ * Returns a runPoolElement for request
84+ */
85+ const getRunPoolElement = function ( body : RunRequestBody , res : Response ) : RunPoolElement {
86+ switch ( body . mode ) {
87+ case 'sync' :
88+ return ( {
89+ mode : 'sync' ,
90+ res
91+ } )
92+ case 'callback' :
93+ return ( {
94+ mode : 'callback' ,
95+ res,
96+ callback : body . callback
97+ } )
98+ }
99+ }
25100
26101/**
27102 * @api {post } /runs POST /runs
@@ -33,6 +108,8 @@ const runPool: {[x: number]: Response} = {}
33108 * @apiParam {String(Base64)} source source code to run (encoded in base64)
34109 * @apiParam {Enum} lang Language of code to execute
35110 * @apiParam {String(Base64)} input [Optional] stdin input for the program (encoded in base64)
111+ * @apiParam {Enum} mode [Optional] mode for request. Default = `sync`, see: https://github.com/coding-blocks/judge-api/issues/16
112+ * @apiParam {String)} callback [Optional] callback url for request. Required for `mode = callback`
36113 *
37114 * @apiUse AvailableLangs
38115 *
@@ -41,14 +118,26 @@ const runPool: {[x: number]: Response} = {}
41118 * @apiSuccess {String(Base64)} stderr Output of stderr of execution (encoded in base64)
42119 * @apiSuccess {Number} statuscode Result of operation
43120 *
44- * @apiSuccessExample {JSON} Success-Response:
121+ * @apiSuccessExample {JSON} Success-Response(mode=sync) :
45122 * HTTP/1.1 200 OK
46123 * {
47124 * "id": 10,
48125 * "statuscode": 0,
49126 * "stdout": "NA0KMg0KMw=="
50127 * "stderr": "VHlwZUVycm9y"
51128 * }
129+ * @apiSuccessExample {JSON} Success-Response(mode=callback):
130+ * HTTP/1.1 200 OK
131+ * {
132+ * "id": 10
133+ * }
134+ *
135+ * @apiSuccessExample {JSON} Body for Callback(mode=callback):
136+ * HTTP/1.1 200 OK
137+ * {
138+ * "id": 10,
139+ * "outputs": ["http://localhost/judge-submissions/file.json"]
140+ * }
52141 */
53142route . post ( '/' , ( req , res , next ) => {
54143 const invalidRequest = isInvalidRunRequest ( req )
@@ -70,19 +159,24 @@ route.post('/', (req, res, next) => {
70159 lang : req . body . lang ,
71160 stdin : req . body . stdin
72161 } )
162+ 73163 // Put into pool and wait for judge-worker to respond
74- runPool [ submission . id ] = res
164+ runPool [ submission . id ] = getRunPoolElement ( req . body , res )
165+ 75166 setTimeout ( ( ) => {
76167 if ( runPool [ submission . id ] ) {
77- runPool [ submission . id ] . status ( 408 ) . json ( {
78- id : submission . id ,
79- code : 408 ,
80- message : "Compile/Run timed out" ,
81- } )
168+ handleTimeoutForSubmission ( submission . id )
82169 delete runPool [ submission . id ]
83170 }
84171 } , config . RUN . TIMEOUT )
85172
173+ switch ( req . body . mode ) {
174+ case 'callback' :
175+ res . json ( {
176+ id : submission . id
177+ } )
178+ }
179+ 86180 } ) . catch ( err => {
87181 res . status ( 501 ) . json ( {
88182 code : 501 ,
@@ -97,10 +191,10 @@ route.post('/', (req, res, next) => {
97191 */
98192successListener . on ( 'success' , ( result : RunResponse ) => {
99193 if ( runPool [ result . id ] ) {
100- runPool [ result . id ] . status ( 200 ) . json ( result )
194+ handleSuccessForSubmission ( result )
101195 delete runPool [ result . id ]
102196 }
103- Submissions . update ( {
197+ Submissions . update ( < any > {
104198 end_time : new Date ( )
105199 } , {
106200 where : {
0 commit comments