diff --git a/compiler-rs/clients_schema/src/lib.rs b/compiler-rs/clients_schema/src/lib.rs index 5c131b960d..0bf8ea3e64 100644 --- a/compiler-rs/clients_schema/src/lib.rs +++ b/compiler-rs/clients_schema/src/lib.rs @@ -523,6 +523,8 @@ pub struct SchemaExample { pub value: Option, pub external_value: Option, pub alternatives: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub availability: Option, } /// Common attributes for all type definitions diff --git a/compiler-rs/clients_schema/src/transform/availability.rs b/compiler-rs/clients_schema/src/transform/availability.rs index 1843b868cd..0f893e96c2 100644 --- a/compiler-rs/clients_schema/src/transform/availability.rs +++ b/compiler-rs/clients_schema/src/transform/availability.rs @@ -18,7 +18,7 @@ use std::cell::RefCell; use crate::transform::Worksheet; -use crate::{Availabilities, Body, IndexedModel, Inherits, Property, TypeDefinition, TypeName, ValueOf}; +use crate::{Availabilities, Body, IndexedModel, Inherits, Property, SchemaExample, TypeDefinition, TypeName, ValueOf}; pub struct Availability { #[allow(clippy::type_complexity)] @@ -97,11 +97,13 @@ impl Availability { self.filter_properties(&mut request.path); self.filter_properties(&mut request.query); self.filter_body(&mut request.body); + self.filter_examples(&mut request.examples); } TypeDefinition::Response(ref mut response) => { response.behaviors.iter().for_each(|i| self.filter_behaviors(i)); self.filter_body(&mut response.body); + self.filter_examples(&mut response.examples); } } } @@ -163,4 +165,10 @@ impl Availability { Body::NoBody(_) => {} } } + + fn filter_examples(&self, examples: &mut Option>) { + if let Some(examples) = examples { + examples.retain(|_, example| self.is_available(&example.availability)); + } + } } diff --git a/compiler-rs/compiler-wasm-lib/pkg/compiler_wasm_lib_bg.wasm b/compiler-rs/compiler-wasm-lib/pkg/compiler_wasm_lib_bg.wasm index ccdd23b794..e0358c1cc8 100644 Binary files a/compiler-rs/compiler-wasm-lib/pkg/compiler_wasm_lib_bg.wasm and b/compiler-rs/compiler-wasm-lib/pkg/compiler_wasm_lib_bg.wasm differ diff --git a/compiler/src/model/metamodel.ts b/compiler/src/model/metamodel.ts index f2ac9acdd5..252e57dcb3 100644 --- a/compiler/src/model/metamodel.ts +++ b/compiler/src/model/metamodel.ts @@ -287,6 +287,8 @@ export class Example { external_value?: string /** An array of alternatives for this example in other languages */ alternatives?: ExampleAlternative[] + /** Which API flavors this example applies to. Omitted means all flavors. */ + availability?: Availabilities } /** diff --git a/compiler/src/steps/add-examples.ts b/compiler/src/steps/add-examples.ts index 3b565696e6..7a2b3ed3bc 100644 --- a/compiler/src/steps/add-examples.ts +++ b/compiler/src/steps/add-examples.ts @@ -149,6 +149,7 @@ class BaseExamplesProcessor { if (BaseExamplesProcessor.languageExamples[alternativeKey] !== undefined) { example.alternatives = BaseExamplesProcessor.languageExamples[alternativeKey] } + this.normalizeExampleAvailability(example, filePath) // Some of the example files set their 'value' as a JSON string, // and some files set their 'value' as an object. For consistency, // if the value is not a JSON string, convert it to a JSON string. @@ -160,6 +161,35 @@ class BaseExamplesProcessor { } return examples } + + // Normalize the optional availability field from a YAML list of flavor names + // (e.g. [stack, serverless]) into the Availabilities object used by the metamodel. + // When omitted, the example applies to all flavors. + normalizeExampleAvailability (example: model.Example, filePath: string): void { + const rawAvailability = (example as { availability?: unknown }).availability + if (rawAvailability === undefined || rawAvailability === null) { + delete example.availability + return + } + + if (!Array.isArray(rawAvailability)) { + throw new Error(`Example availability in ${filePath} must be a list of flavor names`) + } + + const validFlavors = new Set(['stack', 'serverless']) + const availability: model.Availabilities = {} + for (const flavor of rawAvailability) { + if (typeof flavor !== 'string' || !validFlavors.has(flavor)) { + throw new Error( + `Example availability in ${filePath} contains invalid value "${String(flavor)}". ` + + 'Accepted values are "stack" and "serverless".' + ) + } + availability[flavor as keyof model.Availabilities] = {} + } + + example.availability = availability + } } /* diff --git a/compiler/src/transform/filter-by-availability.ts b/compiler/src/transform/filter-by-availability.ts index c9906a08c6..ea0ba17676 100644 --- a/compiler/src/transform/filter-by-availability.ts +++ b/compiler/src/transform/filter-by-availability.ts @@ -223,6 +223,13 @@ function filterModel (inputModel: Model, stack: boolean, serverless: boolean, vi typeDef.body.properties = typeDef.body.properties.filter(filterItem()) break } + if (typeDef.examples !== undefined) { + typeDef.examples = Object.fromEntries( + Object.entries(typeDef.examples).filter(([, example]) => + (example.availability !== undefined) ? include(example.availability) : true + ) + ) + } } }) break @@ -235,6 +242,13 @@ function filterModel (inputModel: Model, stack: boolean, serverless: boolean, vi typeDef.body.properties = typeDef.body.properties.filter(filterItem()) break } + if (typeDef.examples !== undefined) { + typeDef.examples = Object.fromEntries( + Object.entries(typeDef.examples).filter(([, example]) => + (example.availability !== undefined) ? include(example.availability) : true + ) + ) + } } }) break diff --git a/docs/add-new-api.md b/docs/add-new-api.md index e82d599805..af0a90a433 100644 --- a/docs/add-new-api.md +++ b/docs/add-new-api.md @@ -209,6 +209,12 @@ These examples are for use in the API documentation and must adhere to the [Open 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 optionally provide an explanation in a `description` field. In order to generate curl and console examples automatically, the request examples must also contain a `method_request`. +If an example applies to only Stack or Serverless Elasticsearch, add an optional `availability` field with a list of flavor names. Accepted values are `stack` and `serverless`. When omitted, the example applies to all contexts. This field works for both request and response examples. + +```yaml +availability: [stack] +``` + For example: ```yaml diff --git a/docs/examples/languageExamples.json b/docs/examples/languageExamples.json index 340228d801..8bf6ddd1a9 100644 --- a/docs/examples/languageExamples.json +++ b/docs/examples/languageExamples.json @@ -19634,5 +19634,71 @@ "language": "curl", "code": "curl -X PUT -H \"Authorization: ApiKey $ELASTIC_API_KEY\" -H \"Content-Type: application/json\" -d '{\"region_policy\":{\"allowed_regions\":[{\"csp\":\"aws\",\"region\":\"us-east-1\"},{\"csp\":\"aws\",\"region\":\"eu-west-1\"}]}}' \"$ELASTICSEARCH_URL/_inference/_region_policy\"" } + ], + "specification/indices/create/examples/request/indicesCreateRequestExample6.yaml": [ + { + "language": "Python", + "code": "resp = client.indices.create(\n index=\"test\",\n aliases={\n \"alias_1\": {},\n \"alias_2\": {\n \"filter\": {\n \"term\": {\n \"user.id\": \"kimchy\"\n }\n }\n }\n },\n)" + }, + { + "language": "JavaScript", + "code": "const response = await client.indices.create({\n index: \"test\",\n aliases: {\n alias_1: {},\n alias_2: {\n filter: {\n term: {\n \"user.id\": \"kimchy\",\n },\n },\n },\n },\n});" + }, + { + "language": "Ruby", + "code": "response = client.indices.create(\n index: \"test\",\n body: {\n \"aliases\": {\n \"alias_1\": {},\n \"alias_2\": {\n \"filter\": {\n \"term\": {\n \"user.id\": \"kimchy\"\n }\n }\n }\n }\n }\n)" + }, + { + "language": "PHP", + "code": "$resp = $client->indices()->create([\n \"index\" => \"test\",\n \"body\" => [\n \"aliases\" => [\n \"alias_1\" => new ArrayObject([]),\n \"alias_2\" => [\n \"filter\" => [\n \"term\" => [\n \"user.id\" => \"kimchy\",\n ],\n ],\n ],\n ],\n ],\n]);" + }, + { + "language": "curl", + "code": "curl -X PUT -H \"Authorization: ApiKey $ELASTIC_API_KEY\" -H \"Content-Type: application/json\" -d '{\"aliases\":{\"alias_1\":{},\"alias_2\":{\"filter\":{\"term\":{\"user.id\":\"kimchy\"}}}}}' \"$ELASTICSEARCH_URL/test\"" + } + ], + "specification/indices/create/examples/request/indicesCreateRequestExample4.yaml": [ + { + "language": "Python", + "code": "resp = client.indices.create(\n index=\"my-index-000001\",\n)" + }, + { + "language": "JavaScript", + "code": "const response = await client.indices.create({\n index: \"my-index-000001\",\n});" + }, + { + "language": "Ruby", + "code": "response = client.indices.create(\n index: \"my-index-000001\",\n body: {}\n)" + }, + { + "language": "PHP", + "code": "$resp = $client->indices()->create([\n \"index\" => \"my-index-000001\",\n \"body\" => new ArrayObject([]),\n]);" + }, + { + "language": "curl", + "code": "curl -X PUT -H \"Authorization: ApiKey $ELASTIC_API_KEY\" -H \"Content-Type: application/json\" -d '{}' \"$ELASTICSEARCH_URL/my-index-000001\"" + } + ], + "specification/indices/create/examples/request/indicesCreateRequestExample5.yaml": [ + { + "language": "Python", + "code": "resp = client.indices.create(\n index=\"test\",\n mappings={\n \"properties\": {\n \"field1\": {\n \"type\": \"text\"\n }\n }\n },\n)" + }, + { + "language": "JavaScript", + "code": "const response = await client.indices.create({\n index: \"test\",\n mappings: {\n properties: {\n field1: {\n type: \"text\",\n },\n },\n },\n});" + }, + { + "language": "Ruby", + "code": "response = client.indices.create(\n index: \"test\",\n body: {\n \"mappings\": {\n \"properties\": {\n \"field1\": {\n \"type\": \"text\"\n }\n }\n }\n }\n)" + }, + { + "language": "PHP", + "code": "$resp = $client->indices()->create([\n \"index\" => \"test\",\n \"body\" => [\n \"mappings\" => [\n \"properties\" => [\n \"field1\" => [\n \"type\" => \"text\",\n ],\n ],\n ],\n ],\n]);" + }, + { + "language": "curl", + "code": "curl -X PUT -H \"Authorization: ApiKey $ELASTIC_API_KEY\" -H \"Content-Type: application/json\" -d '{\"mappings\":{\"properties\":{\"field1\":{\"type\":\"text\"}}}}' \"$ELASTICSEARCH_URL/test\"" + } ] } \ No newline at end of file diff --git a/docs/modeling-guide.md b/docs/modeling-guide.md index f09b6d841d..6dcf4001f8 100644 --- a/docs/modeling-guide.md +++ b/docs/modeling-guide.md @@ -586,6 +586,8 @@ export class Example { } ``` +Request and response example YAML files use the same flavor names in an optional `availability` field (for example, `availability: [serverless]`). When omitted, the example applies to both Stack and Serverless. + ### Stability property for `@availability` The `stability` property can be added to an `@availability` annotation to indicate the maturity and expected backwards-compatibility of an API or property. diff --git a/specification/indices/create/examples/request/indicesCreateRequestExample1.yaml b/specification/indices/create/examples/request/indicesCreateRequestExample1.yaml index e479499a52..a96a78361e 100644 --- a/specification/indices/create/examples/request/indicesCreateRequestExample1.yaml +++ b/specification/indices/create/examples/request/indicesCreateRequestExample1.yaml @@ -1,4 +1,5 @@ summary: Create an index. +availability: [stack] method_request: PUT /my-index-000001 description: This request specifies the `number_of_shards` and `number_of_replicas`. # type: request diff --git a/specification/indices/create/examples/request/indicesCreateRequestExample2.yaml b/specification/indices/create/examples/request/indicesCreateRequestExample2.yaml index e8cd838108..dc12be337b 100644 --- a/specification/indices/create/examples/request/indicesCreateRequestExample2.yaml +++ b/specification/indices/create/examples/request/indicesCreateRequestExample2.yaml @@ -1,4 +1,5 @@ summary: Create an index with mappings. +availability: [stack] method_request: PUT /test description: You can provide mapping definitions in the create index API requests. # type: request diff --git a/specification/indices/create/examples/request/indicesCreateRequestExample3.yaml b/specification/indices/create/examples/request/indicesCreateRequestExample3.yaml index 3d588cf227..f2ad03344f 100644 --- a/specification/indices/create/examples/request/indicesCreateRequestExample3.yaml +++ b/specification/indices/create/examples/request/indicesCreateRequestExample3.yaml @@ -1,4 +1,5 @@ summary: Create an index with aliases. +availability: [stack] method_request: PUT /test description: > You can provide mapping definitions in the create index API requests. Index alias names also support date math. diff --git a/specification/indices/create/examples/request/indicesCreateRequestExample4.yaml b/specification/indices/create/examples/request/indicesCreateRequestExample4.yaml new file mode 100644 index 0000000000..fab4d8d38b --- /dev/null +++ b/specification/indices/create/examples/request/indicesCreateRequestExample4.yaml @@ -0,0 +1,6 @@ +summary: Create an index. +availability: [serverless] +method_request: PUT /my-index-000001 +description: Create an index with default settings. +# type: request +value: '{}' diff --git a/specification/indices/create/examples/request/indicesCreateRequestExample5.yaml b/specification/indices/create/examples/request/indicesCreateRequestExample5.yaml new file mode 100644 index 0000000000..8f9f6f0ca4 --- /dev/null +++ b/specification/indices/create/examples/request/indicesCreateRequestExample5.yaml @@ -0,0 +1,18 @@ +summary: Create an index with mappings. +availability: [serverless] +method_request: PUT /test +description: You can provide mapping definitions in the create index API requests. +# type: request +value: "{ + + \ \"mappings\": { + + \ \"properties\": { + + \ \"field1\": { \"type\": \"text\" } + + \ } + + \ } + + }" diff --git a/specification/indices/create/examples/request/indicesCreateRequestExample6.yaml b/specification/indices/create/examples/request/indicesCreateRequestExample6.yaml new file mode 100644 index 0000000000..2d5d5d8ed8 --- /dev/null +++ b/specification/indices/create/examples/request/indicesCreateRequestExample6.yaml @@ -0,0 +1,29 @@ +summary: Create an index with aliases. +availability: [serverless] +method_request: PUT /test +description: > + You can provide mapping definitions in the create index API requests. Index alias names also support date math. +# type: request +value: "{ + + \ \"aliases\": { + + \ \"alias_1\": {}, + + \ \"alias_2\": { + + \ \"filter\": { + + \ \"term\": { + + \ \"user.id\": \"kimchy\" + + \ } + + \ } + + \ } + + \ } + + }" diff --git a/specification/indices/rollover/examples/request/indicesRolloverRequestExample1.yaml b/specification/indices/rollover/examples/request/indicesRolloverRequestExample1.yaml index 85c704c137..cf9c9f89db 100644 --- a/specification/indices/rollover/examples/request/indicesRolloverRequestExample1.yaml +++ b/specification/indices/rollover/examples/request/indicesRolloverRequestExample1.yaml @@ -1,4 +1,5 @@ summary: Stack request +availability: [stack] method_request: POST my-data-stream/_rollover description: 'Create a new index for a data stream' # type: request diff --git a/specification/indices/rollover/examples/request/indicesRolloverRequestExample2.yaml b/specification/indices/rollover/examples/request/indicesRolloverRequestExample2.yaml index 1d1e6d20bf..604aa0f957 100644 --- a/specification/indices/rollover/examples/request/indicesRolloverRequestExample2.yaml +++ b/specification/indices/rollover/examples/request/indicesRolloverRequestExample2.yaml @@ -1,4 +1,5 @@ summary: Serverless request +availability: [serverless] method_request: POST my-data-stream/_rollover description: 'Create a new index for a data stream' value: '{}' diff --git a/typescript-generator/src/metamodel.ts b/typescript-generator/src/metamodel.ts index f2ac9acdd5..252e57dcb3 100644 --- a/typescript-generator/src/metamodel.ts +++ b/typescript-generator/src/metamodel.ts @@ -287,6 +287,8 @@ export class Example { external_value?: string /** An array of alternatives for this example in other languages */ alternatives?: ExampleAlternative[] + /** Which API flavors this example applies to. Omitted means all flavors. */ + availability?: Availabilities } /**