Skip to content

Add routing documentations #211

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Nov 5, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions app/routes/github-authorize.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
import Ember from 'ember';
import ajax from 'ic-ajax';

/**
* This route will be called from the GitHub OAuth flow once the user has
* accepted or rejected the data access permissions. It will forward the
* temporary `code` received from the GitHub API to our own API server which
* will exchange it for an access token.
*
* After the exchange the API will return an API token and the user information.
* The result will be stored and the popup window closed. The `/login` route
* will then continue to evaluate the response.
*
* @see https://developer.github.com/v3/oauth/#github-redirects-back-to-your-site
* @see `/login` route
*/
export default Ember.Route.extend({
beforeModel: function(transition) {
return ajax('/authorize', {data: transition.queryParams}).then(function(d) {
Expand Down
13 changes: 13 additions & 0 deletions app/routes/github-login.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
import Ember from 'ember';
import ajax from 'ic-ajax';

/**
* Calling this route will query the `/authorize_url` API endpoint
* and redirect to the received URL to initiate the OAuth flow.
*
* Example URL:
* https://github.com/login/oauth/authorize?client_id=...&state=...&scope=read%3Aorg
*
* Once the user has allowed the OAuth flow access the page will redirect him
* to the `/github_authorize` route of this application.
*
* @see https://developer.github.com/v3/oauth/#redirect-users-to-request-github-access
* @see `/github_authorize` route
*/
export default Ember.Route.extend({
beforeModel: function() {
return ajax('/authorize_url').then(function(url) {
Expand Down
8 changes: 7 additions & 1 deletion app/routes/login.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import Ember from 'ember';

/**
* This route will open a popup window directed at the `/github_login` route.
* After the window has opened it will wait for the window to close and
* then evaluate whether the OAuth flow was successful.
*
* @see `/github_authorize` route
*/
export default Ember.Route.extend({
beforeModel: function(transition) {
try { localStorage.removeItem('github_response'); } catch (e) {}
Expand Down Expand Up @@ -45,4 +52,3 @@ export default Ember.Route.extend({
transition.abort();
}
});

3 changes: 2 additions & 1 deletion src/keyword.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ impl Model for Keyword {
fn table_name(_: Option<Keyword>) -> &'static str { "keywords" }
}

/// Handles the `GET /keywords` route.
pub fn index(req: &mut Request) -> CargoResult<Response> {
let conn = try!(req.tx());
let (offset, limit) = try!(req.pagination(10, 100));
Expand Down Expand Up @@ -175,6 +176,7 @@ pub fn index(req: &mut Request) -> CargoResult<Response> {
}))
}

/// Handles the `GET /keywords/:keyword_id` route.
pub fn show(req: &mut Request) -> CargoResult<Response> {
let name = &req.params()["keyword_id"];
let conn = try!(req.tx());
Expand All @@ -185,4 +187,3 @@ pub fn show(req: &mut Request) -> CargoResult<Response> {
struct R { keyword: EncodableKeyword }
Ok(req.json(&R { keyword: kw.encodable() }))
}

14 changes: 14 additions & 0 deletions src/krate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@ impl Model for Crate {
fn table_name(_: Option<Crate>) -> &'static str { "crates" }
}

/// Handles the `GET /crates` route.
#[allow(trivial_casts)]
pub fn index(req: &mut Request) -> CargoResult<Response> {
let conn = try!(req.tx());
Expand Down Expand Up @@ -574,6 +575,7 @@ pub fn index(req: &mut Request) -> CargoResult<Response> {
}))
}

/// Handles the `GET /summary` route.
pub fn summary(req: &mut Request) -> CargoResult<Response> {
let tx = try!(req.tx());
let num_crates = {
Expand Down Expand Up @@ -620,6 +622,7 @@ pub fn summary(req: &mut Request) -> CargoResult<Response> {
}))
}

/// Handles the `GET /crates/:crate_id` route.
pub fn show(req: &mut Request) -> CargoResult<Response> {
let name = &req.params()["crate_id"];
let conn = try!(req.tx());
Expand All @@ -643,6 +646,7 @@ pub fn show(req: &mut Request) -> CargoResult<Response> {
}))
}

/// Handles the `PUT /crates/new` route.
pub fn new(req: &mut Request) -> CargoResult<Response> {
let app = req.app().clone();

Expand Down Expand Up @@ -817,6 +821,7 @@ fn read_fill<R: Read + ?Sized>(r: &mut R, mut slice: &mut [u8])
Ok(())
}

/// Handles the `GET /crates/:crate_id/:version/download` route.
pub fn download(req: &mut Request) -> CargoResult<Response> {
let crate_name = &req.params()["crate_id"];
let version = &req.params()["version"];
Expand Down Expand Up @@ -872,6 +877,7 @@ pub fn download(req: &mut Request) -> CargoResult<Response> {
}
}

/// Handles the `GET /crates/:crate_id/downloads` route.
pub fn downloads(req: &mut Request) -> CargoResult<Response> {
let crate_name = &req.params()["crate_id"];
let tx = try!(req.tx());
Expand Down Expand Up @@ -931,6 +937,7 @@ fn user_and_crate(req: &mut Request) -> CargoResult<(User, Crate)> {
Ok((user.clone(), krate))
}

/// Handles the `PUT /crates/:crate_id/follow` route.
pub fn follow(req: &mut Request) -> CargoResult<Response> {
let (user, krate) = try!(user_and_crate(req));
let tx = try!(req.tx());
Expand All @@ -946,6 +953,7 @@ pub fn follow(req: &mut Request) -> CargoResult<Response> {
Ok(req.json(&R { ok: true }))
}

/// Handles the `DELETE /crates/:crate_id/follow` route.
pub fn unfollow(req: &mut Request) -> CargoResult<Response> {
let (user, krate) = try!(user_and_crate(req));
let tx = try!(req.tx());
Expand All @@ -957,6 +965,7 @@ pub fn unfollow(req: &mut Request) -> CargoResult<Response> {
Ok(req.json(&R { ok: true }))
}

/// Handles the `GET /crates/:crate_id/following` route.
pub fn following(req: &mut Request) -> CargoResult<Response> {
let (user, krate) = try!(user_and_crate(req));
let tx = try!(req.tx());
Expand All @@ -968,6 +977,7 @@ pub fn following(req: &mut Request) -> CargoResult<Response> {
Ok(req.json(&R { following: rows.next().is_some() }))
}

/// Handles the `GET /crates/:crate_id/versions` route.
pub fn versions(req: &mut Request) -> CargoResult<Response> {
let crate_name = &req.params()["crate_id"];
let tx = try!(req.tx());
Expand All @@ -981,6 +991,7 @@ pub fn versions(req: &mut Request) -> CargoResult<Response> {
Ok(req.json(&R{ versions: versions }))
}

/// Handles the `GET /crates/:crate_id/owners` route.
pub fn owners(req: &mut Request) -> CargoResult<Response> {
let crate_name = &req.params()["crate_id"];
let tx = try!(req.tx());
Expand All @@ -993,10 +1004,12 @@ pub fn owners(req: &mut Request) -> CargoResult<Response> {
Ok(req.json(&R{ users: owners }))
}

/// Handles the `PUT /crates/:crate_id/owners` route.
pub fn add_owners(req: &mut Request) -> CargoResult<Response> {
modify_owners(req, true)
}

/// Handles the `DELETE /crates/:crate_id/owners` route.
pub fn remove_owners(req: &mut Request) -> CargoResult<Response> {
modify_owners(req, false)
}
Expand Down Expand Up @@ -1054,6 +1067,7 @@ fn modify_owners(req: &mut Request, add: bool) -> CargoResult<Response> {
Ok(req.json(&R{ ok: true }))
}

/// Handles the `GET /crates/:crate_id/reverse_dependencies` route.
pub fn reverse_dependencies(req: &mut Request) -> CargoResult<Response> {
let name = &req.params()["crate_id"];
let conn = try!(req.tx());
Expand Down
6 changes: 6 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
//! This crate implements the backend server for https://crates.io/
//!
//! All implemented routes are defined in the [middleware](fn.middleware.html) function and
//! implemented in the [keyword](keyword/index.html), [krate](krate/index.html),
//! [user](user/index.html) and [version](version/index.html) modules.

#[macro_use] extern crate log;
extern crate postgres as pg;
extern crate rustc_serialize;
Expand Down
48 changes: 48 additions & 0 deletions src/user/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,23 @@ impl Model for User {
fn table_name(_: Option<User>) -> &'static str { "users" }
}

/// Handles the `GET /authorize_url` route.
///
/// This route will return an authorization URL for the GitHub OAuth flow including the crates.io
/// `client_id` and a randomly generated `state` secret.
///
/// see https://developer.github.com/v3/oauth/#redirect-users-to-request-github-access
///
/// ## Response Body Example
///
/// ```json
/// {
/// "state": "b84a63c4ea3fcb4ac84",
/// "url": "https://github.com/login/oauth/authorize?client_id=...&state=...&scope=read%3Aorg"
/// }
/// ```
pub fn github_authorize(req: &mut Request) -> CargoResult<Response> {
// Generate a random 16 char ASCII string
let state: String = thread_rng().gen_ascii_chars().take(16).collect();
req.session().insert("github_oauth_state".to_string(), state.clone());

Expand All @@ -147,6 +163,34 @@ pub fn github_authorize(req: &mut Request) -> CargoResult<Response> {
Ok(req.json(&R { url: url.to_string(), state: state }))
}

/// Handles the `GET /authorize` route.
///
/// This route is called from the GitHub API OAuth flow after the user accepted or rejected
/// the data access permissions. It will check the `state` parameter and then call the GitHub API
/// to exchange the temporary `code` for an API token. The API token is returned together with
/// the corresponding user information.
///
/// see https://developer.github.com/v3/oauth/#github-redirects-back-to-your-site
///
/// ## Query Parameters
///
/// - `code` – temporary code received from the GitHub API **(Required)**
/// - `state` – state parameter received from the GitHub API **(Required)**
///
/// ## Response Body Example
///
/// ```json
/// {
/// "api_token": "b84a63c4ea3fcb4ac84",
/// "user": {
/// "email": "[email protected]",
/// "name": "Foo Bar",
/// "login": "foobar",
/// "avatar": "https://avatars.githubusercontent.com/u/1234",
/// "url": null
/// }
/// }
/// ```
pub fn github_access_token(req: &mut Request) -> CargoResult<Response> {
// Parse the url query
let mut query = req.query();
Expand Down Expand Up @@ -197,11 +241,13 @@ pub fn github_access_token(req: &mut Request) -> CargoResult<Response> {
me(req)
}

/// Handles the `GET /logout` route.
pub fn logout(req: &mut Request) -> CargoResult<Response> {
req.session().remove(&"user_id".to_string());
Ok(req.json(&true))
}

/// Handles the `GET /me/reset_token` route.
pub fn reset_token(req: &mut Request) -> CargoResult<Response> {
let user = try!(req.user());

Expand All @@ -215,6 +261,7 @@ pub fn reset_token(req: &mut Request) -> CargoResult<Response> {
Ok(req.json(&R { api_token: token }))
}

/// Handles the `GET /me` route.
pub fn me(req: &mut Request) -> CargoResult<Response> {
let user = try!(req.user());

Expand All @@ -224,6 +271,7 @@ pub fn me(req: &mut Request) -> CargoResult<Response> {
Ok(req.json(&R{ user: user.clone().encodable(), api_token: token }))
}

/// Handles the `GET /me/updates` route.
pub fn updates(req: &mut Request) -> CargoResult<Response> {
let user = try!(req.user());
let (offset, limit) = try!(req.pagination(10, 100));
Expand Down
7 changes: 7 additions & 0 deletions src/version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ impl Model for Version {
fn table_name(_: Option<Version>) -> &'static str { "versions" }
}

/// Handles the `GET /versions` route.
pub fn index(req: &mut Request) -> CargoResult<Response> {
let conn = try!(req.tx());

Expand Down Expand Up @@ -247,6 +248,7 @@ pub fn index(req: &mut Request) -> CargoResult<Response> {
Ok(req.json(&R { versions: versions }))
}

/// Handles the `GET /versions/:version_id` route.
pub fn show(req: &mut Request) -> CargoResult<Response> {
let (version, krate) = match req.params().find("crate_id") {
Some(..) => try!(version_and_crate(req)),
Expand Down Expand Up @@ -281,6 +283,7 @@ fn version_and_crate(req: &mut Request) -> CargoResult<(Version, Crate)> {
Ok((version, krate))
}

/// Handles the `GET /crates/:crate_id/:version/dependencies` route.
pub fn dependencies(req: &mut Request) -> CargoResult<Response> {
let (version, _) = try!(version_and_crate(req));
let tx = try!(req.tx());
Expand All @@ -294,6 +297,7 @@ pub fn dependencies(req: &mut Request) -> CargoResult<Response> {
Ok(req.json(&R{ dependencies: deps }))
}

/// Handles the `GET /crates/:crate_id/:version/downloads` route.
pub fn downloads(req: &mut Request) -> CargoResult<Response> {
let (version, _) = try!(version_and_crate(req));

Expand All @@ -313,6 +317,7 @@ pub fn downloads(req: &mut Request) -> CargoResult<Response> {
Ok(req.json(&R{ version_downloads: downloads }))
}

/// Handles the `GET /crates/:crate_id/:version/authors` route.
pub fn authors(req: &mut Request) -> CargoResult<Response> {
let (version, _) = try!(version_and_crate(req));
let tx = try!(req.tx());
Expand All @@ -331,10 +336,12 @@ pub fn authors(req: &mut Request) -> CargoResult<Response> {
Ok(req.json(&R{ users: users, meta: Meta { names: names } }))
}

/// Handles the `DELETE /crates/:crate_id/:version/yank` route.
pub fn yank(req: &mut Request) -> CargoResult<Response> {
modify_yank(req, true)
}

/// Handles the `PUT /crates/:crate_id/:version/unyank` route.
pub fn unyank(req: &mut Request) -> CargoResult<Response> {
modify_yank(req, false)
}
Expand Down