From Dgraph Wiki
Jump to: navigation, search


What is Gru

Gru is an open source adaptive test system for screening candidates for software engineering roles. The code is located at Github.

Does Gru stand for something?

No. Gru doesn't stand for anything.

Why is it called Gru?

Felonious Gru is the main character from the movie, Despicable Me[1]. At Dgraph, we refer to the team members as Minions. Gru helps us identify and recruit the right minions.

What is Gru composed of?

Gru is composed of the server and the client. The server is written in Go and is responsible for authentication, talking to the database (Dgraph) and maintaining state during the quiz. The web client uses Angular JS as the main library. Communication happens over HTTP using JSON. We use Dgraph as the database which the server talks to.


  • Thank you for the greatest hiring quiz I have ever attempted! — Maxim Zakharov
  • It was fun quiz to close. — Gauthan
  • This is very interesting quiz. — Bramandia Ramadhana
  • I know now that actual experience and knowledge means a lot more than just gut feeling... I thank you and the people involved in structuring the quiz in this way. — Noopur Jain

Setting it up locally


Get the Gru code and checkout the develop branch.

go get
cd $GOPATH/src/
git checkout develop

Start the admin backend from gru directory like go build . && ./gru -user=admin -pass=pass -secret=0a45e5eGseF41o0719PJ39KljMK4F4v2. Note we use sendgrid for sending invite mails to the candidates. That won't work without the sendgrid key. For development purposes when the --sendgrid flag is empty, we just print out the invite link for taking the quiz to the console.


Start Dgraph on port 8080. Note we are currently building and using Dgraph from the master. So you can build Dgraph from source or ask us for the Dgraph binary for Linux/Mac.

Web Server

We use Caddy[2] as the Web server and run it on port 2020 locally. You can install caddy and then run it from inside admin/webUI folder using caddy command. You can checkout the Caddyfile in the folder for config for Caddy. The easiest method to install caddy is using

After this Gru should be up and running for you. You can visit http://localhost:2020 and use admin as username and pass as password for logging in. Go ahead and add some questions, create some quizzes and invite some candidates.


The server currently is responsible for interacting with Dgraph server for all the REST API's. We will soon move a lot of this interaction to the frontend like CRUD operations for Questions, Quizzes, Candidates. The server would then just authenticate the request and act as a proxy as far as the API's for performing CRUD operations are concerned. The server is also responsible for serving the questions to the clients and maintaining the state of the quiz for different candidates that are taking it.


The flags and their description is also available by running gru --help.

Flag Use
dgraph Dgraph server address
port Port on which the server listens
ip Public IP address of server
user Username to login to admin panel
pass Password to login to admin panel
secret Secret used to sign JWT token
sendgrid Sendgrid API Key used for sending mails


We use JSON Web Tokens[3] for authentication.

Admin Panel

The username and password for logging in to the admin panel is supplied while starting the Gru binary. In the future we have a Github/Gmail organization login. When a user tries to sign into the admin panel, upon successful authentication, we create a token and sign it using the secret which gives us a JWT Token. This JWT Token is returned to the client and required for accessing all the resources from the Server.


Each email to a candidate contains a unique string which is a combination of the unique id for the candidate and a random token of length 33 characters. When the candidate starts the quiz, this string and its content is validated and exchanged for a JWT Token which is then used to access questions, send answers in the subsequent API calls.

Rate limiting

The server limits the number of authentication requests (in validate API call) that it accepts per second in order to prevent brute force attacks to guess a valid auth token and access quiz questions. This is done using tickers and buffered channel constructs of Go.

Duplicate sessions

Note: If there are any problems with the server, it would be clearly indicated and the client would keep retrying for a minute.

Multiple sessions for a single auth token are not allowed. Hence, the same auth token cannot be used by multiple instances. In case the client crashes or is closed by the user, one has to wait for 5 seconds to restart before reconnecting with the url and continue the test at the point left.


Note:The logic for selecting the next question could be modified to suit custom needs, like showing a harder question on correct answer and easier ones on an incorrect answer. Hence the term adaptive quiz system. Needs Implementation.

The questions associated with the quiz are shuffled and shown to every candidate. There is no limit to the number of questions shown as long as the candidate can keep on answering them.


Note: We are currently using a binary built from master. If you wish to run Gru, talk to us and we can build the Dgraph binary and share it with you.

We use Dgraph as the backend for Gru because its easy to use and scales well. On the server, Dgraph is run using systemd so that it auto-restarts in case the instance restarts. This is the systemd unit file that we use.

Running it using systemd

# Unit file for running Dgraph server. This file should be symlinked to /etc/systemd/system/dgraph.service.
# Then you can start Dgraph like - sudo systemctl status dgraph.service.
# To view the logs run - sudo journalctl -u dgraph

Description=Dgraph server

# The path of the dgraph binary.
# Restart the service on crashes.


Note: The schema will undergo changes as we incorporate Type System for Dgraph. Right now all values are stored as strings.
# Question properties
<_xid_:root> <question> <_uid_:question> . # All questions are associated with a root node.
<_uid_:question> <name> "Question Name" .
<_uid_:question> <text> "Question text" .
<_uid_:question> <positive> "5" .
<_uid_:question> <negative> "2.5" .
<_uid_:question> <multiple> "true" . # Whether a question has single or multiple correct answers.

# Question options
<_uid_:question> <question.option> <_uid_:option> .
<_uid_:option> <name> "Option text" .
<_uid_:question> <question.correct> <_uid_:option> .
# We don't maintain the reverse edge from option -> question.

# Tags
<_uid_:question> <question.tag> <_uid_:tag> .
<_uid_:tag> <tag.question> <_uid_:question> .
<_uid_:tag> <name> "Tag name" .

# Quiz
<_xid_:root> <quiz> <_uid_:quiz> . # All questions are associated with a root node.
<_uid_:quiz> <name> "Quiz name" .
<_uid_:quiz> <duration> "Quiz duration as a string" .

# Quiz <> Question Relationship
<_uid_:quiz> <quiz.question> <_uid_:question> .

# Candidate Properties
<_uid_:candidate> <name> "Arthur Dent" .
<_uid_:candidate> <email> "[email protected]" .
<_uid_:candidate> <token> "randomstring" .
<_uid_:candidate> <validity> "2016-11-11" . # Validity of the token.
<_uid_:candidate> <complete> "false" . # Whether the candidate has completed the quiz.
<_uid_:candidate> <quiz_start> "Timestamp of when the quiz was started" .

# Candidate <> Quiz Relationship
<_uid_:candidate> <candidate.quiz> <_uid_:quiz> .
<_uid_:quiz> <quiz.candidate> <_uid_:candidate> .

# Candidate <> Question Relationship
<_uid_:candidate> <candidate.question> <_uid_:candidate_qn> .
<_uid_:candidate_qn> <question.uid> <_uid_:question> . # Link to original question.
<_uid_:question> <question.candidate> <_uid_:candidate> . # To figure out to which candidates the question was asked.
<_uid_:candidate_qn> <question.asked> "Timestamp when the question was asked" .
<_uid_:candidate_qn> <candidate.answer> "Comma separated answer ids" .
<_uid_:candidate_qn> <candidate.score> "Score for the question" .


This article has been marked as incomplete.

Reason: This section needs work.

The web client uses AngularJS.


The client does a ping every n seconds. These pings assist in determining if the connection between the client and server is healthy. They're also used to synchronize the time left.

Comparison against other tools

Gru compared to other tools
Factors Gru HackerRank
Main Focus Technical Quiz Coding
Adaptive No (Interface to make it adaptive to be added soon) No
Client Web interface Web Interface
Open Source Yes No
Cost Free Cost increases with number of candidates
Server Self hosted Hosted


This section talks about some tools that are used for the release process.

Cross-compilation using Gonative

Follow these steps to setup gonative and gox in your machine. You need to have go installed for this to work.

$ go get
$ go get
$ mkdir ~/gonative
$ cd ~/gonative
$ export GOROOT_BOOTSTRAP=/usr/local/go/
$ gonative build
$ PATH=~/gonative/go/bin/:$PATH

The OS and architecture can be specified using flags to create specific binaries. To create binaries with custom names (Example: gruserver-v0.1.2-linux-386), follow the commands below.

# In the directory from where you normally compile.
$ mkdir <Folder>
$ gox -output="<Folder>/{{.Dir}}-<version>-{{.OS}}-{{.Arch}}" .

The binaries for all the available platform will be created in the <Folder> which can then be compressed using gzip before uploading.