Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Upgrading Openapi to 3.1 spec #15

Closed
wants to merge 8 commits into from
Closed
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
3 changes: 1 addition & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,5 @@
"structs",
"valtree",
"valtrees"
],
"rust-analyzer.diagnostics.disabled": ["unresolved-import"]
]
}
13 changes: 5 additions & 8 deletions src/conformance/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,14 +112,11 @@ impl ConformanceTestSpec {
.ok_or(ValidationError::ParameterNotFound(param.name.clone()))?;

// validate position
let pos = match parameter.location.as_ref() {
"query" => ParamPosition::Query,
"header" => ParamPosition::Header,
"path" => ParamPosition::Path,
"cookie" => ParamPosition::Cookie,
pos_str => Err(ValidationError::InvalidParameterLocation(
pos_str.to_owned(),
))?,
let pos = match parameter.location {
crate::spec::ParamLoc::Path => ParamPosition::Path,
crate::spec::ParamLoc::Query => ParamPosition::Query,
crate::spec::ParamLoc::Header => ParamPosition::Header,
crate::spec::ParamLoc::Cookie => ParamPosition::Cookie,
};

// TODO: validate type
Expand Down
109 changes: 70 additions & 39 deletions src/spec/parameter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@ use serde::{Deserialize, Serialize};

use super::{FromRef, Ref, RefError, RefType, Spec};
use crate::Schema;
use serde::ser::SerializeStruct;

#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
#[serde(rename_all = "camelCase")]
pub enum ParamLoc {
#[default]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good change

but.... why is query the default? the spec doesn't seem to define this

Query,
Header,
Path,
Cookie,
}

// FIXME: Verify against OpenAPI 3.0.1
/// Describes a single operation parameter.
Expand All @@ -10,66 +21,55 @@ use crate::Schema;
/// and [location](https://github.com/OAI/OpenAPI-Specification/blob/HEAD/versions/3.1.0.md#parameterIn).
///
/// See <https://github.com/OAI/OpenAPI-Specification/blob/HEAD/versions/3.1.0.md#parameterObject>.
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
#[derive(Clone, Debug, Deserialize, PartialEq, Default)]
pub struct Parameter {
/// The name of the parameter.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why remove this doc?

pub name: String,

// TODO: enumify
/// The location of the parameter.
/// Possible values are "query", "header", "path" or "cookie".
#[serde(rename = "in")]
pub location: String,

#[serde(skip_serializing_if = "Option::is_none")]
pub required: Option<bool>,

#[serde(skip_serializing_if = "Option::is_none")]
pub schema: Option<Schema>,
pub location: ParamLoc,

/// A brief description of the parameter. This could contain examples
/// of use. GitHub Flavored Markdown is allowed.
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "uniqueItems")]
pub unique_items: Option<bool>,
pub description: Option<String>,

/// string, number, boolean, integer, array, file ( only for formData )
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "type")]
pub param_type: Option<String>,
pub required: Option<bool>,

/// Specifies that a parameter is deprecated and SHOULD be transitioned out of usage.
/// Default value is `false`.
#[serde(skip_serializing_if = "Option::is_none")]
pub format: Option<String>,
#[serde(default)]
pub deprecated: Option<bool>,

/// A brief description of the parameter. This could contain examples
/// of use. GitHub Flavored Markdown is allowed.
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
style: Option<ParameterStyle>,

// collectionFormat: ???
// default: ???
// maximum ?
// exclusiveMaximum ??
// minimum ??
// exclusiveMinimum ??
// maxLength ??
// minLength ??
// pattern ??
// maxItems ??
// minItems ??
// enum ??
// multipleOf ??
// allowEmptyValue ( for query / body params )
/// Describes how the parameter value will be serialized depending on the type of the parameter
/// value. Default values (based on value of in): for `query` - `form`; for `path` - `simple`; for
/// `header` - `simple`; for cookie - `form`.
#[serde(skip_serializing_if = "Option::is_none")]
style: Option<ParameterStyle>,
pub schema: Option<Schema>,
// #[serde(skip_serializing_if = "Option::is_none")]
// #[serde(rename = "uniqueItems")]
// pub unique_items: Option<bool>,
//
// /// string, number, boolean, integer, array, file ( only for formData )
// #[serde(skip_serializing_if = "Option::is_none")]
// #[serde(rename = "type")]
// pub param_type: Option<String>,
//
// #[serde(skip_serializing_if = "Option::is_none")]
// pub format: Option<String>,
}

#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
enum ParameterStyle {
Form,
Simple,
Matrix,
Label,
SpaceDelimited,
PipeDelimited,
DeepObject,
}

impl FromRef for Parameter {
Expand All @@ -91,3 +91,34 @@ impl FromRef for Parameter {
}
}
}

impl Serialize for Parameter {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut s = serializer.serialize_struct("Parameter", 10)?;

s.serialize_field("name", &self.name)?;
s.serialize_field("in", &self.location)?;
s.serialize_field("description", &self.description)?;
s.serialize_field("required", &self.required)?;
s.serialize_field("deprecated", &self.deprecated)?;
s.serialize_field("schema", &self.schema)?;
//s.serialize_field("uniqueItems", &self.unique_items)?;
//s.serialize_field("type", &self.param_type)?;
//s.serialize_field("format", &self.format)?;

if let Some(style) = &self.style {
s.serialize_field("style", style)?;
} else {
match self.location {
ParamLoc::Query | ParamLoc::Cookie => {
s.serialize_field("style", &ParameterStyle::Form)?;
}
ParamLoc::Path | ParamLoc::Header => {
s.serialize_field("style", &ParameterStyle::Simple)?;
}
}
}

s.end()
}
}
53 changes: 38 additions & 15 deletions src/spec/schema.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Schema specification for [OpenAPI 3.0.1](https://github.com/OAI/OpenAPI-Specification/blob/HEAD/versions/3.1.0.md)
//! Schema specification for [OpenAPI 3.1.0](https://github.com/OAI/OpenAPI-Specification/blob/HEAD/versions/3.1.0.md)

use std::collections::BTreeMap;

Expand Down Expand Up @@ -29,9 +29,31 @@ pub enum Type {
String,
Array,
Object,
Null,
}

// FIXME: Verify against OpenAPI 3.0
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Encoding {
Base16,
Hex,
Base32,
Base32Hex,
Base64,
Base64Url,

#[serde(rename = "quoted-printable")]
QuotedPrintable,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum SchemaOrBool {
Schema(Schema),
Bool(bool),
}

// FIXME: Verify against OpenAPI 3.1
/// The Schema Object allows the definition of input and output data types.
/// These types can be objects, but also primitives and arrays.
/// This object is an extended subset of the
Expand Down Expand Up @@ -60,9 +82,6 @@ pub struct Schema {
#[serde(skip_serializing_if = "Option::is_none")]
pub schema_type: Option<Type>,

#[serde(skip_serializing_if = "Option::is_none")]
pub nullable: Option<bool>,

//
// structure
//
Expand All @@ -77,23 +96,27 @@ pub struct Schema {
#[serde(skip_serializing_if = "BTreeMap::is_empty")]
pub properties: BTreeMap<String, ObjectOrReference<Schema>>,

/// Value can be boolean or object. Inline or referenced schema MUST be of a
/// [Schema Object](https://github.com/OAI/OpenAPI-Specification/blob/HEAD/versions/3.1.0.md#schemaObject)
/// and not a standard JSON Schema.
///
/// See <https://github.com/OAI/OpenAPI-Specification/blob/HEAD/versions/3.1.0.md#properties>.
#[serde(rename = "additionalProperties")]
#[serde(skip_serializing_if = "Option::is_none")]
pub additional_properties: Option<Box<ObjectOrReference<Schema>>>,
pub additional_properties: Option<Box<ObjectOrReference<SchemaOrBool>>>,

#[serde(rename = "contentEncoding")]
#[serde(skip_serializing_if = "Option::is_none")]
pub content_encoding: Option<Encoding>,

#[serde(rename = "contentMediaType")]
#[serde(skip_serializing_if = "Option::is_none")]
pub content_media_type: Option<String>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The value of this property MUST be a string, which MUST be a media type

might be worth making this a mime::Mime


//
// additional metadata
//
#[serde(skip_serializing_if = "Option::is_none")]
pub default: Option<serde_json::Value>,

#[serde(skip_serializing_if = "Option::is_none")]
pub example: Option<serde_json::Value>,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub examples: Vec<serde_json::Value>,

//
// validation requirements
Expand All @@ -118,14 +141,14 @@ pub struct Schema {

#[serde(rename = "exclusiveMaximum")]
#[serde(skip_serializing_if = "Option::is_none")]
pub exclusive_maximum: Option<bool>,
pub exclusive_maximum: Option<serde_json::Number>,

#[serde(skip_serializing_if = "Option::is_none")]
pub maximum: Option<serde_json::Number>,

#[serde(rename = "exclusiveMinimum")]
#[serde(skip_serializing_if = "Option::is_none")]
pub exclusive_minimum: Option<bool>,
pub exclusive_minimum: Option<serde_json::Number>,

#[serde(rename = "minLength")]
#[serde(skip_serializing_if = "Option::is_none")]
Expand Down
4 changes: 2 additions & 2 deletions src/validation/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ impl ValidationTree {
if let Some(type_) = schema.schema_type {
trace!("restricting data type: {:?}", type_);

let type_val = if let Some(nullable) = schema.nullable {
DataType::new(type_).set_nullable(nullable)
let type_val = if type_ == SchemaType::Null {
DataType::new(type_).set_nullable(true)
} else {
DataType::new(type_)
};
Expand Down
4 changes: 0 additions & 4 deletions watch.sh

This file was deleted.