use crate::openapi_utils::OpenApiOperationKind;
use crate::PathElement;
use openapiv3::{OpenAPI, Operation, Parameter, ParameterData, ParameterSchemaOrContent, ReferenceOr};
pub fn update_openapi(original_openapi_spec: &mut OpenAPI, prune_appcatalog: bool, prune_manage: bool, prune_robot: bool) -> Result<(), String> {
if prune_appcatalog {
prune_paths(original_openapi_spec, |path| path.starts_with("/appcatalog/"))?;
}
if prune_manage {
prune_paths(original_openapi_spec, |path| path.starts_with("/manage/"))?;
}
if prune_manage {
prune_paths(original_openapi_spec, |path| path.starts_with("/manage/"))?;
}
if prune_robot {
prune_paths(original_openapi_spec, |path| path.starts_with("/robot/"))?;
}
add_authorization_parameters(original_openapi_spec)?;
add_operation_ids(original_openapi_spec)?;
add_description(original_openapi_spec);
Ok(())
}
fn prune_paths(openapi: &mut OpenAPI, predicate: fn(&str) -> bool) -> Result<(), String> {
let paths = openapi.paths.paths.keys().map(|path| path.to_string()).collect::<Vec<_>>();
for path in paths {
if predicate(path.as_str()) {
openapi.paths.paths.shift_remove(path.as_str());
}
}
Ok(())
}
fn add_authorization_parameters(openapi: &mut OpenAPI) -> Result<(), String> {
for (_, path_item) in openapi.paths.paths.iter_mut() {
if let ReferenceOr::Item(path_item) = path_item {
if let Some(ref mut delete) = path_item.delete {
add_authorization_parameter(delete);
}
if let Some(ref mut get) = path_item.get {
add_authorization_parameter(get);
}
if let Some(ref mut head) = path_item.head {
add_authorization_parameter(head);
}
if let Some(ref mut options) = path_item.options {
add_authorization_parameter(options);
}
if let Some(ref mut patch) = path_item.patch {
add_authorization_parameter(patch);
}
if let Some(ref mut post) = path_item.post {
add_authorization_parameter(post);
}
if let Some(ref mut put) = path_item.put {
add_authorization_parameter(put);
}
if let Some(ref mut trace) = path_item.trace {
add_authorization_parameter(trace);
}
}
}
Ok(())
}
fn add_authorization_parameter(operation: &mut Operation) {
const STRING_SCHEMA_JSON: &str = "{ \"schema\": { \"type\": \"string\" } }";
let schema_content: ParameterSchemaOrContent = serde_json::from_str::<ParameterSchemaOrContent>(STRING_SCHEMA_JSON).unwrap();
operation.parameters.push(ReferenceOr::Item(Parameter::Header {
parameter_data: ParameterData {
name: "Authorization".to_string(),
description: Some("Authorization header (bearer token)".to_string()),
required: true,
deprecated: Some(false),
format: schema_content,
example: None,
examples: Default::default(),
explode: None,
extensions: Default::default(),
},
style: Default::default(),
}));
}
fn add_operation_ids(openapi: &mut OpenAPI) -> Result<(), String> {
for (path, path_item) in openapi.paths.paths.iter_mut() {
let path_elements = PathElement::vec_from_str(path);
if let ReferenceOr::Item(path_item) = path_item {
if let Some(ref mut delete) = path_item.delete {
add_operation_id(delete, "delete", &path_elements);
}
if let Some(ref mut get) = path_item.get {
add_operation_id(get, "get", &path_elements);
}
if let Some(ref mut head) = path_item.head {
add_operation_id(head, "head", &path_elements);
}
if let Some(ref mut options) = path_item.options {
add_operation_id(options, "options", &path_elements);
}
if let Some(ref mut patch) = path_item.patch {
add_operation_id(patch, "patch", &path_elements);
}
if let Some(ref mut post) = path_item.post {
add_operation_id(post, "post", &path_elements);
}
if let Some(ref mut put) = path_item.put {
add_operation_id(put, "put", &path_elements);
}
if let Some(ref mut trace) = path_item.trace {
add_operation_id(trace, "trace", &path_elements);
}
}
}
Ok(())
}
fn add_operation_id(operation: &mut Operation, method: &str, path_elements: &[PathElement]) {
operation.operation_id = Some(OpenApiOperation::new(method, path_elements).operation_id())
}
fn add_description(openapi: &mut OpenAPI) {
const DESC: &str = "Updated from original version (added authorization parameters and operation ids)";
if let Some(ref description) = openapi.info.description {
openapi.info.description = Some(format!("{}\n{}", description, DESC));
} else {
openapi.info.description = Some(DESC.to_string());
}
}
#[derive(Debug)]
struct OpenApiOperation {
method: String,
kind: OpenApiOperationKind,
subjects: Vec<String>,
by_parameters: Vec<String>,
}
impl OpenApiOperation {
fn new(method: &str, path_elements: &[PathElement]) -> Self {
let kind: OpenApiOperationKind = OpenApiOperationKind::from(path_elements.first().unwrap().to_string().as_str());
let subjects = path_elements
.iter()
.skip(1)
.filter_map(|element| match element {
PathElement::Literal(subject) => Some(subject.to_lowercase().replace('-', "_").to_string()),
PathElement::Variable(_) => None,
})
.collect::<Vec<_>>();
let by_parameters = path_elements
.iter()
.filter_map(|element| match element {
PathElement::Literal(_) => None,
PathElement::Variable(variable) => Some(variable.to_lowercase().replace('-', "_").to_string()),
})
.collect::<Vec<_>>();
OpenApiOperation { method: method.to_string(), kind, subjects, by_parameters }
}
fn operation_id(&self) -> String {
let kind = match self.kind {
OpenApiOperationKind::AppCatalog => "_appcatalog",
_ => "",
};
let parameters =
if self.by_parameters.is_empty() { format!("_{}", self.subjects.join("_")) } else { format!("_{}_by_{}", self.subjects.join("_"), self.by_parameters.join("_by_")) };
format!("{}{}{}", self.method, kind, parameters)
}
}