From 91636ae2bb926a33d9e689e53d5b95d464cf024c Mon Sep 17 00:00:00 2001 From: Juha Kukkonen Date: Tue, 15 Oct 2024 21:06:04 +0300 Subject: [PATCH] Add `identifier` for `Info` This commit adds `identifier` field to `Info` what was missing since upgrading to OpenAPI 3.1. Fixes #1139 --- utoipa-gen/src/openapi/info.rs | 13 +++++++++++++ utoipa-gen/tests/openapi_derive.rs | 20 ++++++++++++++++++++ utoipa/src/openapi/info.rs | 15 +++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/utoipa-gen/src/openapi/info.rs b/utoipa-gen/src/openapi/info.rs index 6973fb4e..76729550 100644 --- a/utoipa-gen/src/openapi/info.rs +++ b/utoipa-gen/src/openapi/info.rs @@ -179,6 +179,7 @@ impl ToTokens for Info<'_> { pub(super) struct License<'l> { name: Cow<'l, str>, url: Option>, + identifier: Cow<'l, str>, } impl Parse for License<'_> { @@ -200,6 +201,11 @@ impl Parse for License<'_> { parse_utils::parse_next(input, || input.parse::())?.value(), )) } + "identifier" => { + license.identifier = Cow::Owned( + parse_utils::parse_next(input, || input.parse::())?.value(), + ) + } _ => { return Err(Error::new( ident.span(), @@ -222,11 +228,18 @@ impl ToTokens for License<'_> { fn to_tokens(&self, tokens: &mut TokenStream2) { let name = &self.name; let url = self.url.as_ref().map(|url| quote! { .url(Some(#url))}); + let identifier = if !self.identifier.is_empty() { + let identifier = self.identifier.as_ref(); + quote! { .identifier(Some(#identifier))} + } else { + TokenStream2::new() + }; tokens.extend(quote! { utoipa::openapi::info::LicenseBuilder::new() .name(#name) #url + #identifier .build() }) } diff --git a/utoipa-gen/tests/openapi_derive.rs b/utoipa-gen/tests/openapi_derive.rs index 2f0d7cf7..f411a5f4 100644 --- a/utoipa-gen/tests/openapi_derive.rs +++ b/utoipa-gen/tests/openapi_derive.rs @@ -251,6 +251,26 @@ fn derive_openapi_with_servers() { ) } +#[test] +fn derive_openapi_with_licence() { + #[derive(OpenApi)] + #[openapi(info(license(name = "licence_name", identifier = "MIT"), version = "1.0.0",))] + struct ApiDoc; + + let value = serde_json::to_value(ApiDoc::openapi()).unwrap(); + let info = value.pointer("/info/license"); + dbg!(&info); + + assert_json_include!( + actual: info, + expected: + json!({ + "name": "licence_name", + "identifier": "MIT", + }) + ) +} + #[test] fn derive_openapi_with_custom_info() { #[derive(OpenApi)] diff --git a/utoipa/src/openapi/info.rs b/utoipa/src/openapi/info.rs index d6e64207..0df2bf35 100644 --- a/utoipa/src/openapi/info.rs +++ b/utoipa/src/openapi/info.rs @@ -212,6 +212,13 @@ builder! { #[serde(skip_serializing_if = "Option::is_none")] pub url: Option, + /// An [SPDX-Licenses][spdx_licence] expression for the API. The _`identifier`_ field + /// is mutually exclusive of the _`url`_ field. E.g. Apache-2.0 + /// + /// [spdx_licence]: + #[serde(skip_serializing_if = "Option::is_none")] + pub identifier: Option, + /// Optional extensions "x-something". #[serde(skip_serializing_if = "Option::is_none", flatten)] pub extensions: Option, @@ -245,6 +252,14 @@ impl LicenseBuilder { pub fn extensions(mut self, extensions: Option) -> Self { set_value!(self extensions extensions) } + + /// Set identifier of the licence as [SPDX-Licenses][spdx_licence] expression for the API. + /// The _`identifier`_ field is mutually exclusive of the _`url`_ field. E.g. Apache-2.0 + /// + /// [spdx_licence]: + pub fn identifier>(mut self, identifier: Option) -> Self { + set_value!(self identifier identifier.map(|identifier| identifier.into())) + } } #[cfg(test)]