Skip to content
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
46 changes: 22 additions & 24 deletions src/controllers/token.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::models::ApiToken;
use crate::schema::api_tokens;
use crate::util::{rfc3339, BytesRequest};
use crate::util::rfc3339;
use crate::views::EncodableApiTokenWithToken;

use crate::app::AppState;
Expand All @@ -18,7 +18,6 @@ use diesel::prelude::*;
use diesel_async::async_connection_wrapper::AsyncConnectionWrapper;
use http::request::Parts;
use http::StatusCode;
use serde_json as json;
use serde_json::Value;

#[derive(Deserialize)]
Expand Down Expand Up @@ -65,33 +64,32 @@ pub async fn list(
.await
}

/// Handles the `PUT /me/tokens` route.
pub async fn new(app: AppState, req: BytesRequest) -> AppResult<Json<Value>> {
let (parts, body) = req.0.into_parts();
/// The incoming serialization format for the `ApiToken` model.
#[derive(Deserialize)]
pub struct NewApiToken {
name: String,
crate_scopes: Option<Vec<String>>,
endpoint_scopes: Option<Vec<String>>,
#[serde(default, with = "rfc3339::option")]
expired_at: Option<NaiveDateTime>,
}

/// The incoming serialization format for the `ApiToken` model.
#[derive(Deserialize)]
pub struct NewApiTokenRequest {
api_token: NewApiToken,
}

/// Handles the `PUT /me/tokens` route.
pub async fn new(
app: AppState,
parts: Parts,
Json(new): Json<NewApiTokenRequest>,
) -> AppResult<Json<Value>> {
let conn = app.db_write().await?;
spawn_blocking(move || {
let conn: &mut AsyncConnectionWrapper<_> = &mut conn.into();

/// The incoming serialization format for the `ApiToken` model.
#[derive(Deserialize)]
struct NewApiToken {
name: String,
crate_scopes: Option<Vec<String>>,
endpoint_scopes: Option<Vec<String>>,
#[serde(default, with = "rfc3339::option")]
expired_at: Option<NaiveDateTime>,
}

/// The incoming serialization format for the `ApiToken` model.
#[derive(Deserialize)]
struct NewApiTokenRequest {
api_token: NewApiToken,
}

let new: NewApiTokenRequest = json::from_slice(&body)
.map_err(|e| bad_request(format!("invalid new token request: {e:?}")))?;

let name = &new.api_token.name;
if name.is_empty() {
return Err(bad_request("name must have a value"));
Expand Down
4 changes: 2 additions & 2 deletions src/tests/routes/me/tokens/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ async fn create_token_invalid_request() {
let (app, _, user) = TestApp::init().with_user();
let invalid: &[u8] = br#"{ "name": "" }"#;
let response = user.put::<()>("/api/v1/me/tokens", invalid).await;
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"invalid new token request: Error(\"missing field `api_token`\", line: 1, column: 14)"}]}"#);
assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY);
assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"Failed to deserialize the JSON body into the target type: missing field `api_token` at line 1 column 14"}]}"#);
assert!(app.emails().is_empty());
}

Expand Down