Skip to content

Output the public API only in the stack OpenAPI schema #3308

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

Merged
merged 3 commits into from
Apr 23, 2025
Merged
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
60 changes: 24 additions & 36 deletions compiler-rs/clients_schema_to_openapi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,57 +20,45 @@ mod paths;
mod schemas;
mod utils;

use std::collections::HashSet;
use std::io::{BufWriter, Write};
use std::path::Path;
use indexmap::IndexMap;

use clients_schema::{Availabilities, Endpoint, IndexedModel, Stability};
use clients_schema::{Availabilities, Flavor, IndexedModel, Stability, Visibility};
use openapiv3::{Components, OpenAPI};
use tracing::warn;

use clients_schema::transform::ExpandConfig;
use crate::components::TypesAndComponents;

pub fn convert_schema_file(
path: impl AsRef<Path>,
filter: Option<fn(&Option<Availabilities>) -> bool>,
endpoint_filter: fn(e: &Endpoint) -> bool,
out: impl Write,
) -> anyhow::Result<()> {
// Parsing from a string is faster than using a buffered reader when there is a need for look-ahead
// See https://github.com/serde-rs/json/issues/160
let json = &std::fs::read_to_string(path)?;
let json_deser = &mut serde_json::Deserializer::from_str(json);

let mut unused = HashSet::new();
let mut model: IndexedModel = serde_ignored::deserialize(json_deser, |path| {
if let serde_ignored::Path::Map { parent: _, key } = path {
unused.insert(key);
}
})?;
if !unused.is_empty() {
let msg = unused.into_iter().collect::<Vec<_>>().join(", ");
warn!("Unknown fields found in schema.json: {}", msg);
}
/// Convert an API model into an OpenAPI v3 schema, optionally filtered for a given flavor
pub fn convert_schema(mut schema: IndexedModel, flavor: Option<Flavor>) -> anyhow::Result<OpenAPI> {
// Expand generics
schema = clients_schema::transform::expand_generics(schema, ExpandConfig::default())?;

// Filter flavor
let filter: Option<fn(&Option<Availabilities>) -> bool> = match flavor {
None => None,
Some(Flavor::Stack) => Some(|a| {
// Generate only public items for Stack
Flavor::Stack.visibility(a) == Some(Visibility::Public)
}),
Some(Flavor::Serverless) => Some(|a| {
// Generate only public items for Serverless
Flavor::Serverless.visibility(a) == Some(Visibility::Public)
}),
};

if let Some(filter) = filter {
model = clients_schema::transform::filter_availability(model, filter)?;
schema = clients_schema::transform::filter_availability(schema, filter)?;
}

model.endpoints.retain(endpoint_filter);

let openapi = convert_schema(&model)?;
serde_json::to_writer_pretty(BufWriter::new(out), &openapi)?;
Ok(())
convert_expanded_schema(&schema)
}

/// Convert an API model into an OpenAPI v3 schema. The input model must have all generics expanded, converstion
/// Convert an API model into an OpenAPI v3 schema. The input model must have all generics expanded, conversion
/// will fail otherwise.
///
/// Note: there are ways to represent [generics in JSON Schema], but its unlikely that tooling will understood it.
/// Note: there are ways to represent [generics in JSON Schema], but its unlikely that tooling will understand it.
///
/// [generics in JSON Schema]: https://json-schema.org/blog/posts/dynamicref-and-generics
pub fn convert_schema(model: &IndexedModel) -> anyhow::Result<OpenAPI> {
pub fn convert_expanded_schema(model: &IndexedModel) -> anyhow::Result<OpenAPI> {
let mut openapi = OpenAPI {
openapi: "3.0.3".into(),
info: info(model),
Expand Down
31 changes: 8 additions & 23 deletions compiler-rs/clients_schema_to_openapi/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use std::path::{Path, PathBuf};
use anyhow::bail;

use clap::{Parser, ValueEnum};
use clients_schema::{Availabilities, Visibility};
use clients_schema::Flavor;
use tracing::Level;
use tracing_subscriber::fmt::format::FmtSpan;
use tracing_subscriber::FmtSubscriber;
Expand Down Expand Up @@ -72,33 +72,18 @@ impl Cli {
std::fs::read_to_string(self.schema)?
};

let mut model: clients_schema::IndexedModel = match serde_json::from_str(&json) {
let model: clients_schema::IndexedModel = match serde_json::from_str(&json) {
Ok(indexed_model) => indexed_model,
Err(e) => bail!("cannot parse schema json: {}", e)
};

if let Some(flavor) = self.flavor {
if flavor != SchemaFlavor::All {
let filter: fn(&Option<Availabilities>) -> bool = match flavor {
SchemaFlavor::All => |_| true,
SchemaFlavor::Stack => |a| {
// Generate public and private items for Stack
clients_schema::Flavor::Stack.available(a)
},
SchemaFlavor::Serverless => |a| {
// Generate only public items for Serverless
clients_schema::Flavor::Serverless.visibility(a) == Some(Visibility::Public)
},
};

use clients_schema::transform::*;

model = expand_generics(model, ExpandConfig::default())?;
model = filter_availability(model, filter)?;
}
}
let flavor = match self.flavor {
Some(SchemaFlavor::All) | None => None,
Some(SchemaFlavor::Stack) => Some(Flavor::Stack),
Some(SchemaFlavor::Serverless) => Some(Flavor::Serverless),
};

let openapi = clients_schema_to_openapi::convert_schema(&model)?;
let openapi = clients_schema_to_openapi::convert_schema(model, flavor)?;

let output: Box<dyn std::io::Write> = {
if let Some(output) = self.output {
Expand Down
Binary file modified compiler-rs/compiler-wasm-lib/pkg/compiler_wasm_lib_bg.wasm
Binary file not shown.
23 changes: 6 additions & 17 deletions compiler-rs/compiler-wasm-lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@
// under the License.

use anyhow::bail;
use clients_schema::{Availabilities, Visibility};
use clients_schema::{Flavor, IndexedModel};
use wasm_bindgen::prelude::*;
use clients_schema::transform::ExpandConfig;

#[wasm_bindgen]
pub fn convert_schema_to_openapi(json: &str, flavor: &str) -> Result<String, String> {
Expand All @@ -27,25 +26,15 @@ pub fn convert_schema_to_openapi(json: &str, flavor: &str) -> Result<String, Str
}

fn convert0(json: &str, flavor: &str) -> anyhow::Result<String> {
let filter: Option<fn(&Option<Availabilities>) -> bool> = match flavor {
let flavor = match flavor {
"all" => None,
"stack" => Some(|a| {
// Generate public and private items for Stack
clients_schema::Flavor::Stack.available(a)
}),
"serverless" => Some(|a| {
// Generate only public items for Serverless
clients_schema::Flavor::Serverless.visibility(a) == Some(Visibility::Public)
}),
"stack" => Some(Flavor::Stack),
"serverless" => Some(Flavor::Serverless),
_ => bail!("Unknown flavor {}", flavor),
};

let mut schema = clients_schema::IndexedModel::from_reader(json.as_bytes())?;
schema = clients_schema::transform::expand_generics(schema, ExpandConfig::default())?;
if let Some(filter) = filter {
schema = clients_schema::transform::filter_availability(schema, filter)?;
}
let openapi = clients_schema_to_openapi::convert_schema(&schema)?;
let schema = IndexedModel::from_reader(json.as_bytes())?;
let openapi = clients_schema_to_openapi::convert_schema(schema, flavor)?;
let result = serde_json::to_string_pretty(&openapi)?;
Ok(result)
}
Expand Down
Loading
Loading