Skip to content

Commit 26e230b

Browse files
authored
Merge branch '9.0' into backport-4562-to-9.0
2 parents da5b88f + 191ef92 commit 26e230b

File tree

405 files changed

+8613
-33142
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

405 files changed

+8613
-33142
lines changed

Makefile

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ validate-no-cache: ## Validate a given endpoint request or response without loca
88

99
generate: ## Generate the output spec
1010
@echo ">> generating the spec .."
11-
@make generate-language-examples
1211
@npm run generate-schema --prefix compiler -- --spec ../specification/ --output ../output/
1312
@npm run start --prefix typescript-generator
1413

@@ -55,7 +54,9 @@ transform-to-openapi: ## Generate the OpenAPI definition from the compiled schem
5554
@npm run transform-to-openapi -- --schema output/schema/schema.json --flavor serverless --output output/openapi/elasticsearch-serverless-openapi.json
5655

5756
transform-to-openapi-for-docs: ## Generate the OpenAPI definition tailored for API docs generation
58-
@npm run transform-to-openapi -- --schema output/schema/schema.json --flavor stack --lift-enum-descriptions --merge-multipath-endpoints --multipath-redirects --output output/openapi/elasticsearch-openapi-docs.json
57+
@make generate-language-examples
58+
@make generate
59+
@npm run transform-to-openapi -- --schema output/schema/schema.json --flavor stack --lift-enum-descriptions --merge-multipath-endpoints --multipath-redirects --include-language-examples --output output/openapi/elasticsearch-openapi-docs.json
5960

6061
filter-for-serverless: ## Generate the serverless version from the compiled schema
6162
@npm run --prefix compiler filter-by-availability -- --serverless --visibility=public --input ../output/schema/schema.json --output ../output/output/openapi/elasticsearch-serverless-openapi.json
@@ -73,6 +74,10 @@ generate-language-examples:
7374
@node docs/examples/generate-language-examples.js
7475
@npm run format:fix-examples --prefix compiler
7576

77+
generate-language-examples-with-java:
78+
@node docs/examples/generate-language-examples.js java
79+
@npm run format:fix-examples --prefix compiler
80+
7681
lint-docs: ## Lint the OpenAPI documents after overlays
7782
@npx @redocly/cli lint "output/openapi/elasticsearch-*.json" --config "docs/linters/redocly.yaml" --format stylish --max-problems 500
7883

compiler-rs/clients_schema_to_openapi/src/cli.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ pub struct Cli {
3535
/// output a redirection map when merging multipath endpoints
3636
#[argh(switch)]
3737
pub multipath_redirects: bool,
38+
39+
/// include the x-codeSamples extension with language examples for all endpoints
40+
#[argh(switch)]
41+
pub include_language_examples: bool,
3842
}
3943

4044
impl Cli {
@@ -74,6 +78,7 @@ impl From<Cli> for Configuration {
7478
lift_enum_descriptions: cli.lift_enum_descriptions,
7579
merge_multipath_endpoints: cli.merge_multipath_endpoints,
7680
multipath_redirects: cli.multipath_redirects,
81+
include_language_examples: cli.include_language_examples,
7782
namespaces: if cli.namespace.is_empty() {
7883
None
7984
} else {

compiler-rs/clients_schema_to_openapi/src/lib.rs

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ pub mod cli;
2323

2424
use indexmap::IndexMap;
2525

26-
use clients_schema::{Availabilities, Flavor, IndexedModel, Stability, Visibility};
26+
use clients_schema::{Availabilities, Availability, Flavor, IndexedModel, Stability, Visibility};
2727
use openapiv3::{Components, OpenAPI};
28+
use serde_json::Value;
2829
use clients_schema::transform::ExpandConfig;
2930
use crate::components::TypesAndComponents;
3031

@@ -43,6 +44,9 @@ pub struct Configuration {
4344

4445
/// Should we output a redirect map when merging multipath endpoints?
4546
pub multipath_redirects: bool,
47+
48+
/// include the x-codeSamples extension with language examples for all endpoints
49+
pub include_language_examples: bool,
4650
}
4751

4852
pub struct OpenApiConversion {
@@ -51,7 +55,7 @@ pub struct OpenApiConversion {
5155
}
5256

5357
/// Convert an API model into an OpenAPI v3 schema, optionally filtered for a given flavor
54-
pub fn convert_schema(mut schema: IndexedModel, config: Configuration) -> anyhow::Result<OpenApiConversion> {
58+
pub fn convert_schema(mut schema: IndexedModel, config: Configuration, product_meta: IndexMap<String,String>) -> anyhow::Result<OpenApiConversion> {
5559
// Expand generics
5660
schema = clients_schema::transform::expand_generics(schema, ExpandConfig::default())?;
5761

@@ -72,7 +76,7 @@ pub fn convert_schema(mut schema: IndexedModel, config: Configuration) -> anyhow
7276
schema = clients_schema::transform::filter_availability(schema, filter)?;
7377
}
7478

75-
convert_expanded_schema(&schema, &config)
79+
convert_expanded_schema(&schema, &config, &product_meta)
7680
}
7781

7882
/// Convert an API model into an OpenAPI v3 schema. The input model must have all generics expanded, conversion
@@ -81,7 +85,7 @@ pub fn convert_schema(mut schema: IndexedModel, config: Configuration) -> anyhow
8185
/// Note: there are ways to represent [generics in JSON Schema], but its unlikely that tooling will understand it.
8286
///
8387
/// [generics in JSON Schema]: https://json-schema.org/blog/posts/dynamicref-and-generics
84-
pub fn convert_expanded_schema(model: &IndexedModel, config: &Configuration) -> anyhow::Result<OpenApiConversion> {
88+
pub fn convert_expanded_schema(model: &IndexedModel, config: &Configuration, product_meta: &IndexMap<String,String>) -> anyhow::Result<OpenApiConversion> {
8589
let mut openapi = OpenAPI {
8690
openapi: "3.0.3".into(),
8791
info: info(model),
@@ -119,7 +123,7 @@ pub fn convert_expanded_schema(model: &IndexedModel, config: &Configuration) ->
119123
continue;
120124
}
121125
}
122-
paths::add_endpoint(endpoint, &mut tac, &mut openapi.paths)?;
126+
paths::add_endpoint(endpoint, &mut tac, &mut openapi.paths, product_meta)?;
123127
}
124128

125129
// // Sort maps to ensure output stability
@@ -179,30 +183,49 @@ fn info(model: &IndexedModel) -> openapiv3::Info {
179183
}
180184
}
181185

182-
pub fn availability_as_extensions(availabilities: &Option<Availabilities>) -> IndexMap<String, serde_json::Value> {
186+
pub fn product_meta_as_extensions(namespace: &str, product_meta: &IndexMap<String,String>) -> IndexMap<String, Value> {
183187
let mut result = IndexMap::new();
188+
let mut additional_namespace= "".to_string();
189+
if let Some(meta) = product_meta.get(namespace) {
190+
additional_namespace = format!(", {meta}");
191+
}
192+
193+
let product_str = format!("elasticsearch{additional_namespace}");
194+
result.insert("x-product-feature".to_string(), Value::String(product_str));
195+
result
196+
}
184197

198+
pub fn availability_as_extensions(availabilities: &Option<Availabilities>, flavor: &Option<Flavor>) -> IndexMap<String, Value> {
199+
let mut result = IndexMap::new();
200+
convert_availabilities(availabilities, flavor, &mut result);
201+
result
202+
}
203+
204+
pub fn convert_availabilities(availabilities: &Option<Availabilities>, flavor: &Option<Flavor>, result: &mut IndexMap<String, Value>) {
185205
if let Some(avails) = availabilities {
186-
// We may have several availabilities, but since generally exists only on stateful (stack)
187-
for (_, availability) in avails {
188-
if let Some(stability) = &availability.stability {
189-
match stability {
206+
if let Some(flav) = flavor {
207+
if let Some(availability) = avails.get(flav) {
208+
let Availability {since,stability,..} = &availability;
209+
let stab = stability.clone().unwrap_or(Stability::Stable);
210+
let mut since_str = "".to_string();
211+
if let Some(since) = since {
212+
since_str = format!("; Added in {since}");
213+
}
214+
match stab {
190215
Stability::Beta => {
191-
result.insert("x-beta".to_string(), serde_json::Value::Bool(true));
216+
let beta_since = format!("Beta{since_str}");
217+
result.insert("x-state".to_string(), Value::String(beta_since));
192218
}
193219
Stability::Experimental => {
194-
result.insert("x-state".to_string(), serde_json::Value::String("Technical preview".to_string()));
220+
let exp_since = format!("Technical preview{since_str}");
221+
result.insert("x-state".to_string(), Value::String(exp_since));
195222
}
196-
Stability::Stable => {
197-
if let Some(since) = &availability.since {
198-
let stable_since = "Added in ".to_string() + since;
199-
result.insert("x-state".to_string(), serde_json::Value::String(stable_since));
200-
}
223+
Stability::Stable => {
224+
let stable_since = format!("Generally available{since_str}");
225+
result.insert("x-state".to_string(), Value::String(stable_since));
201226
}
202227
}
203228
}
204229
}
205230
}
206-
207-
result
208231
}

compiler-rs/clients_schema_to_openapi/src/main.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
// under the License.
1717

1818
use std::fs::File;
19-
use clients_schema::IndexedModel;
19+
use indexmap::IndexMap;
20+
use clients_schema::{IndexedModel};
2021
use tracing::Level;
2122
use tracing_subscriber::fmt::format::FmtSpan;
2223
use tracing_subscriber::FmtSubscriber;
@@ -32,10 +33,12 @@ fn main() -> anyhow::Result<()> {
3233
.finish();
3334
tracing::subscriber::set_global_default(subscriber)?;
3435

36+
let product_meta: IndexMap<String, String> = serde_json::from_reader(File::open("../../specification/_doc_ids/product-meta.json")?)?;
3537
let schema = IndexedModel::from_reader(File::open(&cli.schema)?)?;
3638
let output = cli.output.clone();
3739
let redirect_path = cli.redirect_path(&cli.output);
38-
let openapi = clients_schema_to_openapi::convert_schema(schema, cli.into())?;
40+
let openapi = clients_schema_to_openapi::convert_schema(schema, cli.into(), product_meta)?;
41+
serde_json::to_writer_pretty(File::create(&output)?, &openapi.openapi)?;
3942
serde_json::to_writer_pretty(File::create(&output)?, &openapi.openapi)?;
4043

4144
if let Some(redirects) = openapi.redirects {

compiler-rs/clients_schema_to_openapi/src/paths.rs

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,18 @@ use openapiv3::{
2828
MediaType, Parameter, ParameterData, ParameterSchemaOrContent, PathItem, PathStyle, Paths, QueryStyle, ReferenceOr,
2929
RequestBody, Response, Responses, StatusCode, Example
3030
};
31+
use serde_json::Value;
3132
use clients_schema::SchemaExample;
32-
3333
use crate::components::TypesAndComponents;
34+
use crate::convert_availabilities;
3435

3536
/// Add an endpoint to the OpenAPI schema. This will result in the addition of a number of elements to the
3637
/// openapi schema's `paths` and `components` sections.
3738
pub fn add_endpoint(
3839
endpoint: &clients_schema::Endpoint,
3940
tac: &mut TypesAndComponents,
4041
out: &mut Paths,
42+
product_meta: &IndexMap<String,String>
4143
) -> anyhow::Result<()> {
4244
if endpoint.request.is_none() {
4345
// tracing::warn!("Endpoint {} is missing a request -- ignored", &endpoint.name);
@@ -61,6 +63,8 @@ pub fn add_endpoint(
6163
let request = tac.model.get_request(endpoint.request.as_ref().unwrap())?;
6264

6365
fn parameter_data(prop: &Property, in_path: bool, tac: &mut TypesAndComponents) -> anyhow::Result<ParameterData> {
66+
let mut extensions: IndexMap<String,Value> = Default::default();
67+
convert_availabilities(&prop.availability, &tac.config.flavor, &mut extensions);
6468
Ok(ParameterData {
6569
name: prop.name.clone(),
6670
description: tac.property_description(prop)?,
@@ -341,33 +345,37 @@ pub fn add_endpoint(
341345
};
342346

343347
// add the x-state extension for availability
344-
let mut extensions = crate::availability_as_extensions(&endpoint.availability);
345-
346-
// add the x-codeSamples extension
347-
let mut code_samples = vec![];
348-
if let Some(examples) = request.examples.clone() {
349-
if let Some((_, example)) = examples.first() {
350-
let request_line = example.method_request.clone().unwrap_or(String::from(""));
351-
let request_body = example.value.clone().unwrap_or(String::from(""));
352-
if !request_line.is_empty() {
353-
code_samples.push(serde_json::json!({
354-
"lang": "Console",
355-
"source": request_line + "\n" + request_body.as_str(),
356-
}));
357-
}
358-
if let Some(alternatives) = example.alternatives.clone() {
359-
for alternative in alternatives.iter() {
348+
let mut extensions = crate::availability_as_extensions(&endpoint.availability, &tac.config.flavor);
349+
350+
if tac.config.include_language_examples {
351+
// add the x-codeSamples extension
352+
let mut code_samples = vec![];
353+
if let Some(examples) = request.examples.clone() {
354+
if let Some((_, example)) = examples.first() {
355+
let request_line = example.method_request.clone().unwrap_or(String::from(""));
356+
let request_body = example.value.clone().unwrap_or(String::from(""));
357+
if !request_line.is_empty() {
360358
code_samples.push(serde_json::json!({
361-
"lang": alternative.language,
362-
"source": alternative.code.as_str(),
359+
"lang": "Console",
360+
"source": request_line + "\n" + request_body.as_str(),
363361
}));
364362
}
363+
if let Some(alternatives) = example.alternatives.clone() {
364+
for alternative in alternatives.iter() {
365+
code_samples.push(serde_json::json!({
366+
"lang": alternative.language,
367+
"source": alternative.code.as_str(),
368+
}));
369+
}
370+
}
365371
}
366372
}
373+
if !code_samples.is_empty() {
374+
extensions.insert("x-codeSamples".to_string(), serde_json::json!(code_samples));
375+
}
367376
}
368-
if !code_samples.is_empty() {
369-
extensions.insert("x-codeSamples".to_string(), serde_json::json!(code_samples));
370-
}
377+
378+
extensions.append(&mut crate::product_meta_as_extensions(namespace, product_meta));
371379

372380
// Create the operation, it will be repeated if we have several methods
373381
let operation = openapiv3::Operation {
@@ -392,7 +400,7 @@ pub fn add_endpoint(
392400
deprecated: endpoint.deprecation.is_some(),
393401
security: None,
394402
servers: vec![],
395-
extensions,
403+
extensions
396404
};
397405

398406

compiler-rs/clients_schema_to_openapi/src/schemas.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ impl<'a> TypesAndComponents<'a> {
473473
data.external_docs = self.convert_external_docs(prop);
474474
data.deprecated = prop.deprecation.is_some();
475475
data.description = self.property_description(prop)?;
476-
data.extensions = crate::availability_as_extensions(&prop.availability);
476+
data.extensions = crate::availability_as_extensions(&prop.availability, &self.config.flavor);
477477
// TODO: prop.aliases as extensions
478478
// TODO: prop.server_default as extension
479479
// TODO: prop.doc_id as extension (new representation of since and stability)

compiler-rs/compiler-wasm-lib/pkg/compiler_wasm_lib.js

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
19 KB
Binary file not shown.

compiler-rs/compiler-wasm-lib/src/lib.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@
1515
// specific language governing permissions and limitations
1616
// under the License.
1717

18-
use std::path::PathBuf;
18+
use std::path::{PathBuf};
1919
use argh::FromArgs;
20-
use clients_schema::IndexedModel;
20+
use clients_schema::{IndexedModel};
2121
use wasm_bindgen::prelude::*;
22+
use clients_schema::indexmap::IndexMap;
2223
use clients_schema_to_openapi::cli::Cli;
2324

2425
/// Minimal bindings to Node's `fs` module.
@@ -63,8 +64,15 @@ pub fn convert0(cli: Cli, cwd: Option<String>) -> anyhow::Result<()> {
6364

6465
let json = node_fs::read_file_sync_to_string(&input.to_string_lossy(), "utf8");
6566
let schema = IndexedModel::from_reader(json.as_bytes())?;
67+
68+
let product_meta_path = match cwd {
69+
Some(ref cwd) => format!("{cwd}/specification/_doc_ids/product-meta.json"),
70+
None => "specification/_doc_ids/product-meta.json".to_string(),
71+
};
72+
let json_product_map = node_fs::read_file_sync_to_string(&product_meta_path, "utf8");
73+
let product_meta: IndexMap<String, String> = serde_json::from_str(&json_product_map).expect("Cannot parse product metadata file");
6674

67-
let openapi = clients_schema_to_openapi::convert_schema(schema, cli.into())?;
75+
let openapi = clients_schema_to_openapi::convert_schema(schema, cli.into(), product_meta)?;
6876

6977
let result = serde_json::to_string_pretty(&openapi.openapi)?;
7078
node_fs::write_file_sync(&output.to_string_lossy(), &result);

docs/add-new-api.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,12 +173,13 @@ Add an `examples` folder and `request` and `xxx_response` subfolders (where `xxx
173173

174174
These examples are for use in the API documentation and must adhere to the [OpenAPI 3.0 Example object specification](https://spec.openapis.org/oas/v3.0.3#example-object). They must have a `value` field that contains the request or response body.
175175
If there are multiple examples for the endpoint, they must each have a brief `summary` field, which is used as the label for the example. You can also optionaly provide an explanation in a `description` field.
176+
In order to generate curl and console examples automatically, the request examples must also contain a `method_request`.
176177

177178
For example:
178179

179180
```yaml
180181
summary: Sequence query
181-
# method_request: GET /my-data-stream/_eql/search
182+
method_request: GET /my-data-stream/_eql/search
182183
# type: request
183184
description: >
184185
Run `GET /my-data-stream/_eql/search` to search for a sequence of events.

0 commit comments

Comments
 (0)