Skip to content

WIP: Teams #177

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 17 commits into from
Aug 18, 2015
Merged
2 changes: 1 addition & 1 deletion app/components/user-link.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ export default Ember.Component.extend({
'href': function() {
// TODO replace this with a link to a native crates.io profile
// page when they exist.
return 'https://github.com/' + this.get('user.login');
return this.get('user.url');
}.property('user'),
});
1 change: 1 addition & 0 deletions app/models/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ export default DS.Model.extend({
login: DS.attr('string'),
api_token: DS.attr('string'),
avatar: DS.attr('string'),
url: DS.attr('string'),
});
13 changes: 12 additions & 1 deletion src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use git2;
use oauth2;
use r2d2;
use s3;
use curl::http;

use {db, Config};

Expand All @@ -28,13 +29,15 @@ pub struct AppMiddleware {

impl App {
pub fn new(config: &Config) -> App {
let github = oauth2::Config::new(
let mut github = oauth2::Config::new(
&config.gh_client_id,
&config.gh_client_secret,
"https://github.com/login/oauth/authorize",
"https://github.com/login/oauth/access_token",
);

github.scopes.push(String::from("read:org"));

let db_config = r2d2::Config::builder()
.pool_size(if config.env == ::Env::Production {10} else {1})
.helper_threads(if config.env == ::Env::Production {3} else {1})
Expand All @@ -56,6 +59,14 @@ impl App {
config: config.clone(),
};
}

pub fn handle(&self) -> http::Handle {
let handle = http::handle();
match self.s3_proxy {
Some(ref proxy) => handle.proxy(&proxy[..]),
None => handle,
}
}
}

impl AppMiddleware {
Expand Down
29 changes: 29 additions & 0 deletions src/bin/migrate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,24 @@ fn migrations() -> Vec<Migration> {
try!(tx.execute("DROP INDEX index_keywords_lower_keyword", &[]));
Ok(())
}),
Migration::add_column(20150715170350, "crate_owners", "owner_kind",
"INTEGER NOT NULL DEFAULT 0"),
Migration::run(20150804170127,
"ALTER TABLE crate_owners ALTER owner_kind DROP DEFAULT",
"ALTER TABLE crate_owners ALTER owner_kind SET DEFAULT 0",
),
Migration::add_table(20150804170128, "teams", "
id SERIAL PRIMARY KEY,
login VARCHAR NOT NULL UNIQUE,
github_id INTEGER NOT NULL UNIQUE,
name VARCHAR,
avatar VARCHAR
"),
Migration::run(20150804170129,
"ALTER TABLE crate_owners RENAME user_id TO owner_id",
"ALTER TABLE crate_owners RENAME owner_id TO user_id",
),
undo_foreign_key(20150804170130, "crate_owners", "user_id", "users (id)"),
];
// NOTE: Generate a new id via `date +"%Y%m%d%H%M%S"`

Expand All @@ -469,6 +487,16 @@ fn migrations() -> Vec<Migration> {
Migration::run(id, &add, &rm)
}

fn undo_foreign_key(id: i64, table: &str, column: &str,
references: &str) -> Migration {
let add = format!("ALTER TABLE {table} ADD CONSTRAINT fk_{table}_{col}
FOREIGN KEY ({col}) REFERENCES {reference}",
table = table, col = column, reference = references);
let rm = format!("ALTER TABLE {table} DROP CONSTRAINT fk_{table}_{col}",
table = table, col = column);
Migration::run(id, &rm, &add)
}

fn index(id: i64, table: &str, column: &str) -> Migration {
let add = format!("CREATE INDEX index_{table}_{column}
ON {table} ({column})",
Expand All @@ -479,6 +507,7 @@ fn migrations() -> Vec<Migration> {
}
}

// DO NOT UPDATE OR USE FOR NEW MIGRATIONS
fn fix_duplicate_crate_owners(tx: &postgres::Transaction) -> postgres::Result<()> {
let v: Vec<(i32, i32)> = {
let stmt = try!(tx.prepare("SELECT user_id, crate_id
Expand Down
67 changes: 67 additions & 0 deletions src/http.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use curl;
use oauth2::*;
use app::App;
use util::{CargoResult, internal, ChainError, human};
use rustc_serialize::{json, Decodable};
use std::str;


/// Does all the nonsense for sending a GET to Github. Doesn't handle parsing
/// because custom error-code handling may be desirable. Use
/// parse_github_response to handle the "common" processing of responses.
pub fn github(app: &App, url: &str, auth: &Token)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function might be a lot nicer to call if it was something along the lines of:

pub fn github<T: Decodable>(app: &App, url: &str, auth: &Token)
        -> Result<T, curl::ErrCode> { 
    // ...
}

Basically something that does the decoding for you automatically and fills in an error for bad JSON and bad utf-8-ness

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The response-code handling isn't uniform, though. In particular when querying team membership, 404 is how Github reports "false". Do you want this to take a closure for (maybe) processing the error code?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made two fns, one for querying, one for parsing, so that you can intermediate evaluation of the request as desired.

-> Result<curl::http::Response, curl::ErrCode> {
info!("GITHUB HTTP: {}", url);

let url = if app.config.env == ::Env::Test {
format!("http://api.github.com{}", url)
} else {
format!("https://api.github.com{}", url)
};

app.handle()
.get(url)
.header("Accept", "application/vnd.github.v3+json")
.header("User-Agent", "hello!")
.auth_with(auth)
.exec()
}

/// Checks for normal responses
pub fn parse_github_response<T: Decodable>(resp: curl::http::Response)
-> CargoResult<T> {
match resp.get_code() {
200 => {} // Ok!
403 => {
return Err(human("It looks like you don't have permission \
to query a necessary property from Github
to complete this request. \
You may need to re-authenticate on \
crates.io to grant permission to read \
github org memberships. Just go to \
https://crates.io/login"));
}
_ => {
return Err(internal(format!("didn't get a 200 result from
github: {}", resp)));
}
}

let json = try!(str::from_utf8(resp.get_body()).ok().chain_error(||{
internal("github didn't send a utf8-response")
}));

json::decode(json).chain_error(|| {
internal("github didn't send a valid json response")
})
}

/// Gets a token with the given string as the access token, but all
/// other info null'd out. Generally, just to be fed to the `github` fn.
pub fn token(token: String) -> Token {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a comment here indicating that this is intended to pass to the function above? (e.g. the scopes/token_type are blank)

Token {
access_token: token,
scopes: Vec::new(),
token_type: String::new(),
}
}
Loading