aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--rust/README.md7
-rw-r--r--rust/TODO19
-rw-r--r--rust/fatcat-openapi2.yml1
-rw-r--r--rust/src/api_server.rs209
4 files changed, 149 insertions, 87 deletions
diff --git a/rust/README.md b/rust/README.md
index f539e4fb..794afee7 100644
--- a/rust/README.md
+++ b/rust/README.md
@@ -9,6 +9,13 @@ Things!
diesel print-schema > src/database_schema.rs
+Regenerate API schemas:
+
+ cargo swagger fatcat-openapi2.yml fatcat-api --docker-tag=v2.3.1
+ sudo chown `whoami`:`whoami` -R fatcat-api
+ # edit fatcat-api/Cargo.toml, set name to "fatcat-api"
+ cargo fmt
+
Debugging SQL errors:
psql fatcat_rs < migrations/2018-05-12-001226_init/up.sql
diff --git a/rust/TODO b/rust/TODO
index 0c81c5ea..23885dc2 100644
--- a/rust/TODO
+++ b/rust/TODO
@@ -4,13 +4,22 @@ x re-generate OpenAPI
=> cargo swagger (docker) seems to only use latest
x iron-slog
x integrate API server example into a main.rs
-- get and post for creators
-- cleanup pooled database: https://github.com/diesel-rs/diesel/pull/1466
+x get and post for creators
+x cleanup pooled database: https://github.com/diesel-rs/diesel/pull/1466
+- clean up blasse error handling a bit
+- add 404s to gets
+- wow. fix a bunch of api schema names ("FindASingleContainerByExternalIdentifer")
+- creators, releases, works, files (?)
+- one-to-many relationship (eg, works)
+- many-to-many relationship (eg, creators)
+- refactor handlers to have a proper Result<_,_> error-chain type, so I can use '?'
+- copypasta a bunch of CRUD
+- helper to calculate state of idents
then:
-- encode the remaining entities in SQL
-- openapi for other entities (heavily templated)
-
+x encode the remaining entities in SQL
+x openapi for other entities (heavily templated)
+- figure out JSON(B) in both swagger and diesel
later:
- metrics, jwt, config, sentry, testing
diff --git a/rust/fatcat-openapi2.yml b/rust/fatcat-openapi2.yml
index abf09a10..955cef65 100644
--- a/rust/fatcat-openapi2.yml
+++ b/rust/fatcat-openapi2.yml
@@ -144,6 +144,7 @@ definitions:
type: integer
editor_id:
type: integer
+ # TODO: work_edits array, etc. ["edits"]["work"] or ["work_edits"]?
changelogentry:
type: object
required:
diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs
index 8523f154..385a1eff 100644
--- a/rust/src/api_server.rs
+++ b/rust/src/api_server.rs
@@ -32,19 +32,38 @@ impl Api for Server {
_context: &Context,
) -> Box<Future<Item = ContainerIdGetResponse, Error = ApiError> + Send> {
let conn = self.db_pool.get().expect("db_pool error");
- let id = uuid::Uuid::parse_str(&id).unwrap();
+ let id = match uuid::Uuid::parse_str(&id) {
+ Ok(some_uuid) => some_uuid,
+ Err(_) => {
+ return Box::new(futures::done(Ok(
+ ContainerIdGetResponse::BadRequest(
+ Error { message: "Failed to parse UUID".to_string() }
+ ))));
+ }
+ };
- let (ident, rev): (ContainerIdentRow, ContainerRevRow) = container_ident::table
+ let res: Result<(ContainerIdentRow, ContainerRevRow), _> = container_ident::table
.find(id)
.inner_join(container_rev::table)
- .first(&conn)
- .expect("error loading container");
+ .first(&conn);
+
+ let (ident, rev) = match res {
+ Ok(r) => r,
+ Err(_) => {
+ return Box::new(futures::done(Ok(
+ // TODO: UGH, need to add 404 responses everywhere, not 400
+ //ContainerIdGetResponse::NotFound(
+ ContainerIdGetResponse::BadRequest(
+ Error { message: "No such container".to_string() }
+ ))));
+ }
+ };
let entity = ContainerEntity {
issn: rev.issn,
publisher: rev.publisher,
- parent: None, // TODO
- name: Some(rev.name), // TODO: not optional
+ parent: None, // TODO:
+ name: rev.name,
state: None, // TODO:
ident: Some(ident.id.to_string()),
revision: ident.rev_id.map(|v| v as isize),
@@ -59,31 +78,57 @@ impl Api for Server {
fn container_lookup_get(
&self,
issn: String,
- context: &Context,
+ _context: &Context,
) -> Box<Future<Item = ContainerLookupGetResponse, Error = ApiError> + Send> {
- let context = context.clone();
- println!(
- "container_lookup_get(\"{}\") - X-Span-ID: {:?}",
- issn,
- context.x_span_id.unwrap_or(String::from("<none>")).clone()
- );
- Box::new(futures::failed("Generic failure".into()))
+ let conn = self.db_pool.get().expect("db_pool error");
+
+ let res: Result<(ContainerIdentRow, ContainerRevRow), _> = container_ident::table
+ .inner_join(container_rev::table)
+ .first(&conn);
+ // XXX: actually do a filter/lookup
+
+ let (ident, rev) = match res {
+ Ok(r) => r,
+ Err(_) => {
+ return Box::new(futures::done(Ok(
+ // TODO: UGH, need to add 404 responses everywhere, not 400
+ //ContainerIdGetResponse::NotFound(
+ ContainerLookupGetResponse::BadRequest(
+ Error { message: "No such container".to_string() }
+ ))));
+ }
+ };
+
+ let entity = ContainerEntity {
+ issn: rev.issn,
+ publisher: rev.publisher,
+ parent: None, // TODO:
+ name: rev.name,
+ state: None, // TODO:
+ ident: Some(ident.id.to_string()),
+ revision: ident.rev_id.map(|v| v as isize),
+ redirect: ident.redirect_id.map(|u| u.to_string()),
+ editgroup: None,
+ };
+ Box::new(futures::done(Ok(
+ ContainerLookupGetResponse::FindASingleContainerByExternalIdentifer(entity),
+ )))
}
fn container_post(
&self,
- body: Option<models::ContainerEntity>,
+ body: models::ContainerEntity,
context: &Context,
) -> Box<Future<Item = ContainerPostResponse, Error = ApiError> + Send> {
println!("{:?}", body);
- let body = body.expect("missing body"); // TODO: required parameter
- //let editgroup_id: i64 = body.editgroup.expect("need editgroup_id") as i64; // TODO: or find/create
+ //let editgroup_id: i64 = body.editgroup.expect("need editgroup_id") as i64;
+ // TODO: or find/create
let editgroup_id = 1;
let conn = self.db_pool.get().expect("db_pool error");
- let name = body.name.unwrap();
- let issn = body.issn.unwrap();
- println!("name={} issn={}", name, issn);
+ let name = body.name;
+ let issn = body.issn;
+ println!("name={} issn={:?}", name, issn);
let edit: Vec<ContainerEditRow> = diesel::sql_query(
"WITH rev AS ( INSERT INTO container_rev (name, issn)
@@ -96,7 +141,7 @@ impl Api for Server {
($3, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev))
RETURNING *",
).bind::<diesel::sql_types::Text, _>(name)
- .bind::<diesel::sql_types::Text, _>(issn)
+ .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(issn)
.bind::<diesel::sql_types::BigInt, _>(editgroup_id)
.load(&conn)
.unwrap();
@@ -106,7 +151,7 @@ impl Api for Server {
editgroup_id: Some(edit.editgroup_id as isize),
revision: Some(edit.rev_id.unwrap() as isize),
ident: Some(edit.ident_id.to_string()),
- id: Some(edit.id as isize),
+ edit_id: Some(edit.id as isize),
};
Box::new(futures::done(Ok(ContainerPostResponse::Created(
entity_edit,
@@ -121,7 +166,7 @@ impl Api for Server {
let conn = self.db_pool.get().expect("db_pool error");
let ce = CreatorEntity {
orcid: None,
- name: None,
+ name: "Dummy Name".into(),
state: None,
ident: None,
revision: None,
@@ -149,7 +194,7 @@ impl Api for Server {
fn creator_post(
&self,
- body: Option<models::CreatorEntity>,
+ body: models::CreatorEntity,
context: &Context,
) -> Box<Future<Item = CreatorPostResponse, Error = ApiError> + Send> {
let context = context.clone();
@@ -161,181 +206,181 @@ impl Api for Server {
Box::new(futures::failed("Generic failure".into()))
}
- fn editgroup_id_accept_post(
+ fn file_id_get(
&self,
- id: i32,
+ id: String,
context: &Context,
- ) -> Box<Future<Item = EditgroupIdAcceptPostResponse, Error = ApiError> + Send> {
+ ) -> Box<Future<Item = FileIdGetResponse, Error = ApiError> + Send> {
let context = context.clone();
println!(
- "editgroup_id_accept_post({}) - X-Span-ID: {:?}",
+ "file_id_get(\"{}\") - X-Span-ID: {:?}",
id,
context.x_span_id.unwrap_or(String::from("<none>")).clone()
);
Box::new(futures::failed("Generic failure".into()))
}
- fn editgroup_id_get(
+ fn file_lookup_get(
&self,
- id: i32,
+ sha1: String,
context: &Context,
- ) -> Box<Future<Item = EditgroupIdGetResponse, Error = ApiError> + Send> {
+ ) -> Box<Future<Item = FileLookupGetResponse, Error = ApiError> + Send> {
let context = context.clone();
println!(
- "editgroup_id_get({}) - X-Span-ID: {:?}",
- id,
+ "file_lookup_get(\"{}\") - X-Span-ID: {:?}",
+ sha1,
context.x_span_id.unwrap_or(String::from("<none>")).clone()
);
Box::new(futures::failed("Generic failure".into()))
}
- fn editgroup_post(
+ fn file_post(
&self,
+ body: models::FileEntity,
context: &Context,
- ) -> Box<Future<Item = EditgroupPostResponse, Error = ApiError> + Send> {
+ ) -> Box<Future<Item = FilePostResponse, Error = ApiError> + Send> {
let context = context.clone();
println!(
- "editgroup_post() - X-Span-ID: {:?}",
+ "file_post({:?}) - X-Span-ID: {:?}",
+ body,
context.x_span_id.unwrap_or(String::from("<none>")).clone()
);
Box::new(futures::failed("Generic failure".into()))
}
- fn editor_username_changelog_get(
+ fn work_id_get(
&self,
- username: String,
+ id: String,
context: &Context,
- ) -> Box<Future<Item = EditorUsernameChangelogGetResponse, Error = ApiError> + Send> {
+ ) -> Box<Future<Item = WorkIdGetResponse, Error = ApiError> + Send> {
let context = context.clone();
println!(
- "editor_username_changelog_get(\"{}\") - X-Span-ID: {:?}",
- username,
+ "work_id_get(\"{}\") - X-Span-ID: {:?}",
+ id,
context.x_span_id.unwrap_or(String::from("<none>")).clone()
);
Box::new(futures::failed("Generic failure".into()))
}
- fn editor_username_get(
+ fn work_post(
&self,
- username: String,
+ body: models::WorkEntity,
context: &Context,
- ) -> Box<Future<Item = EditorUsernameGetResponse, Error = ApiError> + Send> {
+ ) -> Box<Future<Item = WorkPostResponse, Error = ApiError> + Send> {
let context = context.clone();
println!(
- "editor_username_get(\"{}\") - X-Span-ID: {:?}",
- username,
+ "work_post({:?}) - X-Span-ID: {:?}",
+ body,
context.x_span_id.unwrap_or(String::from("<none>")).clone()
);
Box::new(futures::failed("Generic failure".into()))
}
- fn file_id_get(
+ fn release_id_get(
&self,
id: String,
context: &Context,
- ) -> Box<Future<Item = FileIdGetResponse, Error = ApiError> + Send> {
+ ) -> Box<Future<Item = ReleaseIdGetResponse, Error = ApiError> + Send> {
let context = context.clone();
println!(
- "file_id_get(\"{}\") - X-Span-ID: {:?}",
+ "release_id_get(\"{}\") - X-Span-ID: {:?}",
id,
context.x_span_id.unwrap_or(String::from("<none>")).clone()
);
Box::new(futures::failed("Generic failure".into()))
}
- fn file_lookup_get(
+ fn release_lookup_get(
&self,
- sha1: String,
+ doi: String,
context: &Context,
- ) -> Box<Future<Item = FileLookupGetResponse, Error = ApiError> + Send> {
+ ) -> Box<Future<Item = ReleaseLookupGetResponse, Error = ApiError> + Send> {
let context = context.clone();
println!(
- "file_lookup_get(\"{}\") - X-Span-ID: {:?}",
- sha1,
+ "release_lookup_get(\"{}\") - X-Span-ID: {:?}",
+ doi,
context.x_span_id.unwrap_or(String::from("<none>")).clone()
);
Box::new(futures::failed("Generic failure".into()))
}
- fn file_post(
+ fn release_post(
&self,
- body: Option<models::FileEntity>,
+ body: models::ReleaseEntity,
context: &Context,
- ) -> Box<Future<Item = FilePostResponse, Error = ApiError> + Send> {
+ ) -> Box<Future<Item = ReleasePostResponse, Error = ApiError> + Send> {
let context = context.clone();
println!(
- "file_post({:?}) - X-Span-ID: {:?}",
+ "release_post({:?}) - X-Span-ID: {:?}",
body,
context.x_span_id.unwrap_or(String::from("<none>")).clone()
);
Box::new(futures::failed("Generic failure".into()))
}
- fn release_id_get(
+ fn editgroup_id_accept_post(
&self,
- id: String,
+ id: i32,
context: &Context,
- ) -> Box<Future<Item = ReleaseIdGetResponse, Error = ApiError> + Send> {
+ ) -> Box<Future<Item = EditgroupIdAcceptPostResponse, Error = ApiError> + Send> {
let context = context.clone();
println!(
- "release_id_get(\"{}\") - X-Span-ID: {:?}",
+ "editgroup_id_accept_post({}) - X-Span-ID: {:?}",
id,
context.x_span_id.unwrap_or(String::from("<none>")).clone()
);
Box::new(futures::failed("Generic failure".into()))
}
- fn release_lookup_get(
+ fn editgroup_id_get(
&self,
- doi: String,
+ id: i32,
context: &Context,
- ) -> Box<Future<Item = ReleaseLookupGetResponse, Error = ApiError> + Send> {
+ ) -> Box<Future<Item = EditgroupIdGetResponse, Error = ApiError> + Send> {
let context = context.clone();
println!(
- "release_lookup_get(\"{}\") - X-Span-ID: {:?}",
- doi,
+ "editgroup_id_get({}) - X-Span-ID: {:?}",
+ id,
context.x_span_id.unwrap_or(String::from("<none>")).clone()
);
Box::new(futures::failed("Generic failure".into()))
}
- fn release_post(
+ fn editgroup_post(
&self,
- body: Option<models::ReleaseEntity>,
context: &Context,
- ) -> Box<Future<Item = ReleasePostResponse, Error = ApiError> + Send> {
+ ) -> Box<Future<Item = EditgroupPostResponse, Error = ApiError> + Send> {
let context = context.clone();
println!(
- "release_post({:?}) - X-Span-ID: {:?}",
- body,
+ "editgroup_post() - X-Span-ID: {:?}",
context.x_span_id.unwrap_or(String::from("<none>")).clone()
);
Box::new(futures::failed("Generic failure".into()))
}
- fn work_id_get(
+ fn editor_username_changelog_get(
&self,
- id: String,
+ username: String,
context: &Context,
- ) -> Box<Future<Item = WorkIdGetResponse, Error = ApiError> + Send> {
+ ) -> Box<Future<Item = EditorUsernameChangelogGetResponse, Error = ApiError> + Send> {
let context = context.clone();
println!(
- "work_id_get(\"{}\") - X-Span-ID: {:?}",
- id,
+ "editor_username_changelog_get(\"{}\") - X-Span-ID: {:?}",
+ username,
context.x_span_id.unwrap_or(String::from("<none>")).clone()
);
Box::new(futures::failed("Generic failure".into()))
}
- fn work_post(
+ fn editor_username_get(
&self,
- body: Option<models::WorkEntity>,
+ username: String,
context: &Context,
- ) -> Box<Future<Item = WorkPostResponse, Error = ApiError> + Send> {
+ ) -> Box<Future<Item = EditorUsernameGetResponse, Error = ApiError> + Send> {
let context = context.clone();
println!(
- "work_post({:?}) - X-Span-ID: {:?}",
- body,
+ "editor_username_get(\"{}\") - X-Span-ID: {:?}",
+ username,
context.x_span_id.unwrap_or(String::from("<none>")).clone()
);
Box::new(futures::failed("Generic failure".into()))