Skip to content

Commit

API: every name a route's path gives reaches its operation

Path parameters reached the input only when their name was on a fixed list, so `:project` and `:email` were dropped: PATCH and GET /workspaces/{ws}/projects/{project}, pinning and unpinning a project, and DELETE /user/emails/{email} all answered that nothing was given. Every path parameter now goes in under its name, decoded, before the ones that need more (a repository, a number). A test walks every route and checks each of its parameters arrives.

syntaqxcommitted Parent7829202Browse files
1 file+50−30/1 viewed
+50−3
884884 if let (Some(owner), Some(name)) = (param("owner"), param("name")) {
885885 input.insert("repo".to_owned(), Value::String(format!("{owner}/{name}")));
886886 }
887− for key in ["plan", "id", "workspace", "delivery", "workflow", "job", "setting", "username", "team", "basehead"] {
888− if let Some(value) = param(key) {
889− input.insert(key.to_owned(), Value::String(value.to_owned()));
887+ // Every other name the path gives, under that name; the ones below that
888+ // need more (a repository, a number, an encoded name) are set after.
889+ for (key, value) in &params {
890+ if !matches!(*key, "owner" | "name") {
891+ input.insert((*key).to_owned(), Value::String(percent_decoded(value)));
890892 }
891893 }
892894 // A repository of a team's workspace, named by itself.
939941
940942 use super::*;
941943
944+ /// Every name a route's path gives reaches the operation: a name left
945+ /// off the lists above is dropped, and the operation answers that it
946+ /// was not given (the project routes were, until this test).
947+ #[test]
948+ fn every_path_parameter_reaches_the_input() {
949+ for route in ROUTES.iter() {
950+ let names: Vec<&str> = route.path.split('/').filter_map(|part| part.strip_prefix(':')).collect();
951+ if names.is_empty() {
952+ continue;
953+ }
954+ let path: String = route
955+ .path
956+ .split('/')
957+ .map(|part| match part.strip_prefix(':') {
958+ Some("number" | "milestone") => "7".to_owned(),
959+ Some(name) => format!("{name}-x"),
960+ None => part.to_owned(),
961+ })
962+ .collect::<Vec<_>>()
963+ .join("/");
964+ let (found, input) = resolve(route.method, &path, &[], json!({})).unwrap();
965+ // A path two routes could take is checked under the first.
966+ if found.path != route.path {
967+ continue;
968+ }
969+ for name in names {
970+ let key = match name {
971+ "owner" | "name" => "repo",
972+ "repo" if names_has_workspace(route.path) => "repo",
973+ other => other,
974+ };
975+ assert!(
976+ input.get(key).is_some_and(|value| !value.is_null()),
977+ "{} {}: :{name} does not reach the input",
978+ route.method,
979+ route.path
980+ );
981+ }
982+ }
983+ }
984+
985+ fn names_has_workspace(path: &str) -> bool {
986+ path.split('/').any(|part| part == ":workspace")
987+ }
988+
942989 #[test]
943990 fn a_path_resolves_to_its_operation_and_input() {
944991 let (route, input) = resolve(