Mumuki Laboratory
Code assement web application for the Mumuki Platform
About
Laboratory is a multitenant Rails webapp for solving exercises, organized in terms of chapters and guides.
Preparing environment
1. Install essentials and base libraries
First, we need to install some software: PostgreSQL database, RabbitMQ queue, and some common Ruby on Rails native dependencies
sudo apt-get install autoconf curl git build-essential libssl-dev autoconf bison libreadline6 libreadline6-dev zlib1g zlib1g-dev postgresql libpq-dev rabbitmq-server
2. Install rbenv
rbenv is a ruby versions manager, similar to rvm, nvm, and so on.
curl -fsSL https://github.com/rbenv/rbenv-installer/raw/master/bin/rbenv-installer | bash
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc # or .bash_profile
echo 'eval "$(rbenv init -)"' >> ~/.bashrc # or .bash_profile
3. Install ruby
Now we have rbenv installed, we can install ruby and bundler
rbenv install 2.3.1
rbenv global 2.3.1
rbenv rehash
gem install bundler
4. Clone this repository
Because, err... we need to clone this repostory before developing it :stuck_out_tongue:
git clone https://github.com/mumuki/mumuki-laboratory
cd mumuki-laboratory
5. Install and setup database
We need to create a PostgreSQL role - AKA a user - who will be used by Laboratory to create and access the database
# create db user for linux users
sudo -u postgres psql <<EOF
create role mumuki with createdb login password 'mumuki';
EOF
# create db user for mac users
psql postgres
#once inside postgres server
create role mumuki with createdb login password 'mumuki';
# create schema and initial development data
./devinit
Installing and Running
Quick start
If you want to start the server quickly in developer environment, you can just do the following:
./devstart
This will install your dependencies and boot the server.
Installing the server
If you just want to install dependencies, just do:
bundle install
Running the server
You can boot the server by using the standard rackup command:
# using defaults from config/puma.rb and rackup default port 9292
bundle exec rackup
# changing port
bundle exec rackup -p 8080
# changing threads count
MUMUKI_LABORATORY_THREADS=30 bundle exec rackup
# changing workers count
MUMUKI_LABORATORY_WORKERS=4 bundle exec rackup
Or you can also start it with puma
command, which gives you more control:
# using defaults from config/puma.rb
bundle exec puma
# changing ports, workers and threads count, using puma-specific options:
bundle exec puma -w 4 -t 2:30 -p 8080
# changing ports, workers and threads count, using environment variables:
MUMUKI_LABORATORY_WORKERS=4 MUMUKI_LABORATORY_PORT=8080 MUMUKI_LABORATORY_THREADS=30 bundle exec puma
Finally, you can also start your server using rails
:
rails s
Running tests
bundle exec rspec
API Docs
Before using the API, you must create an ApiClient
using rails c
, which will generate a private JWT. Use it to authenticate API calls in any Platform application within a Authorizaion: Bearer <TOKEN>
.
Before using the API, take a look to the roles hierarchy:
.
Permissions are bound to a scope, that states in which context the operation can be performed. Scopes are simply two-level contexts, expressed as slugss <first>/<second>
, without any explicit semantic. They exact meaning depends on the role:
- student:
organization/course
- teacher and headmaster:
organization/course
- writer and editor:
organization/content
- janitor:
organization/_
- owner:
_/_
Users
Create single user
This is a generic user creation request.
Minimal permission: janitor
POST /users
Sample request body:
{
"first_name": "María",
"last_name": "Casas",
"email": "[email protected]",
"permissions": {
"student": "cpt/*:rte/*",
"teacher": "ppp/2016-2q"
}
}
Update single user
This is a way of updating user basic data. Permissions are ignored.
Minimal permission: janitor
PUT /users/:uid
Sample request body:
{
"first_name": "María",
"last_name": "Casas",
"email": "[email protected]",
"uid": "[email protected]"
}
Add student to course
Creates the student if necessary, and updates her permissions.
Minimal permission: janitor
POST /courses/:organization/:course/students
{
"first_name": "María",
"last_name": "Casas",
"email": "[email protected]",
"uid": "[email protected]"
}
Response
{
"uid": "[email protected]",
"first_name": "María",
"last_name": "Casas",
"email": "[email protected]"
}
Forbidden Response
{
"status": 403,
"error": "Exception"
}
Detach student from course
Remove student permissions from a course.
Minimal permission: janitor
POST /courses/:organization/:course/students/:uid/detach
Response: status code: 200
Not Found Response
{
"status": 404,
"error": "Couldn't find User"
}
Attach student to course
Add student permissions to a course.
Minimal permission: janitor
POST /courses/:organization/:course/students/:uid/attach
Response: status code: 200
Not Found Response
{
"status": 404,
"error": "Couldn't find User"
}
Add teacher to course
Creates the teacher if necessary, and updates her permissions.
Minimal permission: headmaster
, janitor
POST /course/:id/teachers
{
"first_name": "Erica",
"last_name": "Gonzalez",
"email": "[email protected]",
"uid": "[email protected]"
}
Add a batch of users to a course
Creates every user if necesssary, an updates permissions.
Minimal permission: janitor
POST /course/:id/batches
{
"students": [
{
"first_name": "Tupac",
"last_name": "Lincoln",
"email": "[email protected]",
"uid": "[email protected]"
}
],
"teachers": [
{
"first_name": "Erica",
"last_name": "Gonzalez",
"email": "[email protected]",
"uid": "[email protected]"
}
]
}
Detach student from course
Minimal permission: janitor
DELETE /course/:id/students/:uid
Detach teacher from course
Minimal permission: janitor
DELETE /course/:id/teachers/:uid
Destroy single user
Minimal permission: owner
DELETE /users/:uid
Courses
Create single course
Minimal permission: janitor
POST /organization/:id/courses/
{
"name":"....",
}
Archive single course
Minimal permission: janitor
DELETE /organization/:id/courses/:id
Destroy single course
Minimal permission: owner
DELETE /courses/:id
Organizations
Model
Mandatory fields
{
"name": "academy",
"contact_email": "[email protected]",
"books": [
"MumukiProject/mumuki-libro-metaprogramacion"
],
"locale": "es-AR"
}
Optional fields
{
"public": false,
"description": "...",
"login_methods": [
"facebook", "twitter", "google"
],
"logo_url": "http://mumuki.io/logo-alt-large.png",
"terms_of_service": "Al usar Mumuki aceptás que las soluciones de tus ejercicios sean registradas para ser corregidas por tu/s docente/s...",
"theme_stylesheet": ".theme { color: red }",
"extension_javascript": "doSomething = function() { }"
}
- If you set
null
topublic
,login_methods
, the values will befalse
and `["user_pass"]. - If you set
null
todescription
, the value will benull
. - If you set
null
to the others, it will be inherited from an organization called"base"
every time you query the API.
Generated fields
{
"theme_stylesheet_url": "stylesheets/academy-asjdf92j1jd8.css",
"extension_javascript_url": "javascripts/academy-jd912j8jdj19.js"
}
List all organizations
get /organizations
Sample response body:
{
"organizations": [
{ "name": "academy", "contact_email": "[email protected]", "locale": "es-AR", "login_methods": ["facebook"], "books": ["libro"], "public": true, "logo_url":"http://..." },
{ "name": "alcal", "contact_email": "[email protected]", "locale": "en-US", "login_methods": ["facebook", "github"], "books": ["book"], "public": false }
]
}
Minimal permission: None for public organizations, janitor
for user's private organizations.
Get single organization by name
get /organizations/:name
Sample response body:
{ "name": "academy", "contact_email": "[email protected]", "locale": "es-AR", "login_methods": ["facebook"], "books": ["libro"], "public": true, "logo_url":"http://..." }
Minimal permission: janitor
of the organization.
Create organization
post /organizations
... with at least the required fields.
Minimal permission: owner
of that organization
Update organization
put /organizations/:name
... with a partial update.
Minimal permission: owner
of :name