Skip to content

Commit 30a59a9

Browse files
committed
WIP: Handle expected errors when creating tokens
1 parent 48af67c commit 30a59a9

File tree

4 files changed

+71
-9
lines changed

4 files changed

+71
-9
lines changed

app/components/api-token-row.js

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,35 @@ import Ember from 'ember';
33
export default Ember.Component.extend({
44
emptyName: Ember.computed.empty('api_token.name'),
55
disableCreate: Ember.computed.or('api_token.isSaving', 'emptyName'),
6+
serverError: null,
67

78
actions: {
89
saveToken() {
9-
this.get('api_token').save();
10+
this.get('api_token')
11+
.save()
12+
.then(() => this.set('serverError', null))
13+
.catch(err => {
14+
let msg;
15+
if (err.errors && err.errors[0] && err.errors[0].detail) {
16+
msg = `An error occurred while saving this token, ${err.errors[0].detail}`;
17+
} else {
18+
msg = 'An unknown error occurred while saving this token';
19+
}
20+
this.set('serverError', msg);
21+
});
1022
},
1123
revokeToken() {
12-
this.get('api_token').destroyRecord();
24+
this.get('api_token')
25+
.destroyRecord()
26+
.catch(err => {
27+
let msg;
28+
if (err.errors && err.errors[0] && err.errors[0].detail) {
29+
msg = `An error occurred while revoking this token, ${err.errors[0].detail}`;
30+
} else {
31+
msg = 'An unknown error occurred while revoking this token';
32+
}
33+
this.set('serverError', msg);
34+
});
1335
},
1436
}
1537
});

app/styles/me.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,4 +142,10 @@
142142
@include flex-direction(column);
143143
@include justify-content(stretch);
144144
}
145+
.error {
146+
border-top-width: 0px;
147+
font-weight: bold;
148+
color: rgb(216, 0, 41);
149+
padding: 0px 10px 10px 20px;
150+
}
145151
}

app/templates/components/api-token-row.hbs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,14 @@
5353
</div>
5454
</div>
5555

56+
{{#if serverError}}
57+
<div class='row error'>
58+
<div>
59+
{{ serverError }}
60+
</div>
61+
</div>
62+
{{/if}}
63+
5664
{{#if api_token.token}}
5765
<div class='row new-token'>
5866
<div>

src/token.rs

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::fmt;
2+
13
use conduit::{Request, Response};
24
use time::Timespec;
35
use pg::GenericConnection;
@@ -7,7 +9,7 @@ use rustc_serialize::json;
79

810
use db::RequestTransaction;
911
use user::RequestUser;
10-
use util::{RequestUtils, CargoResult, internal, ChainError, human, read_fill};
12+
use util::{RequestUtils, CargoError, CargoResult, internal, ChainError, human, read_fill};
1113
use Model;
1214

1315
/// The model representing a row in the `api_tokens` database table.
@@ -136,6 +138,28 @@ impl Model for ApiToken {
136138
fn table_name(_: Option<ApiToken>) -> &'static str { "api_tokens" }
137139
}
138140

141+
struct BadRequest<T: CargoError>(T);
142+
143+
impl<T: CargoError> CargoError for BadRequest<T> {
144+
fn description(&self) -> &str { self.0.description() }
145+
fn response(&self) -> Option<Response> {
146+
self.0.response().map(|mut response| {
147+
response.status = (400, "Bad Request");
148+
response
149+
})
150+
}
151+
}
152+
153+
impl<T: CargoError> fmt::Display for BadRequest<T> {
154+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
155+
write!(f, "Bad Request: {}", self.0)
156+
}
157+
}
158+
159+
fn bad_request<T: CargoError>(error: T) -> Box<CargoError> {
160+
Box::new(BadRequest(error))
161+
}
162+
139163
/// Handles the `GET /me/tokens` route.
140164
pub fn list(req: &mut Request) -> CargoResult<Response> {
141165
let tokens = ApiToken::find_for_user(req.tx()?, req.user()?.id)?
@@ -155,31 +179,31 @@ pub fn new(req: &mut Request) -> CargoResult<Response> {
155179
})?;
156180

157181
if length > max_post_size {
158-
return Err(human(&format_args!("max post size is: {}", max_post_size)));
182+
return Err(bad_request(human(&format_args!("max post size is: {}", max_post_size))));
159183
}
160184

161185
let mut json = vec![0; length as usize];
162186
read_fill(req.body(), &mut json)?;
163187

164188
let json = String::from_utf8(json).map_err(|_| {
165-
human("json body was not valid utf-8")
189+
bad_request(human("json body was not valid utf-8"))
166190
})?;
167191

168192
let new: NewApiTokenRequest = json::decode(&json).map_err(|e| {
169-
human(&format_args!("invalid new token request: {:?}", e))
193+
bad_request(human(&format_args!("invalid new token request: {:?}", e)))
170194
})?;
171195

172196
let name = &new.api_token.name;
173197
if name.len() < 1 {
174-
return Err(human("name must have a value"));
198+
return Err(bad_request(human("name must have a value")));
175199
}
176200

177201
let user = req.user()?;
178202

179203
let max_token_per_user = 500;
180204
let count = ApiToken::count_for_user(req.tx()?, user.id)?;
181205
if count >= max_token_per_user {
182-
return Err(human(&format_args!("maximum tokens per user is: {}", max_token_per_user)));
206+
return Err(bad_request(human(&format_args!("maximum tokens per user is: {}", max_token_per_user))));
183207
}
184208

185209
let api_token = ApiToken::insert(req.tx()?, user.id, name)?;
@@ -192,7 +216,9 @@ pub fn new(req: &mut Request) -> CargoResult<Response> {
192216
/// Handles the `DELETE /me/tokens/:id` route.
193217
pub fn revoke(req: &mut Request) -> CargoResult<Response> {
194218
let user = req.user()?;
195-
let id = req.params()["id"].parse()?;
219+
let id = req.params()["id"].parse().map_err(|e| {
220+
bad_request(human(&format_args!("invalid token id: {:?}", e)))
221+
})?;
196222

197223
ApiToken::delete(req.tx()?, user.id, id)?;
198224

0 commit comments

Comments
 (0)