diff --git a/api-design-guidelines/naming.md b/api-design-guidelines/naming.md index 6ebd58feb1..d503b408ac 100644 --- a/api-design-guidelines/naming.md +++ b/api-design-guidelines/naming.md @@ -100,6 +100,8 @@ Care should be given to ensure that: - All member APIs of a given namespace are logically related and form a coherent set. - Related functionality is not distributed across multiple arbitrary namespaces +NOTE: The endpoint namespaces are used to generate tags in the OpenAPI documents. The tags are ultimately used to group the endpoints in the API documentation. To override the default tag, use `@doc_tag`. + ### Use the global namespace sparingly The top-level global namespace should be treated with particular care. It is traditionally reserved for search and document endpoints only. A case should be made and a broader discussion carried out before new endpoints unrelated to these functions are added to the global namespace. diff --git a/compiler-rs/clients_schema/src/lib.rs b/compiler-rs/clients_schema/src/lib.rs index 17d8c70ed0..7df0bc82e7 100644 --- a/compiler-rs/clients_schema/src/lib.rs +++ b/compiler-rs/clients_schema/src/lib.rs @@ -815,6 +815,9 @@ pub struct Endpoint { #[serde(skip_serializing_if = "Option::is_none")] pub availability: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub doc_tag: Option, + /// If missing, there is not yet a request definition for this endpoint. #[serde(skip_serializing_if = "Option::is_none")] pub request: Option, diff --git a/compiler-rs/clients_schema_to_openapi/src/paths.rs b/compiler-rs/clients_schema_to_openapi/src/paths.rs index 7fc7f480a8..878646970a 100644 --- a/compiler-rs/clients_schema_to_openapi/src/paths.rs +++ b/compiler-rs/clients_schema_to_openapi/src/paths.rs @@ -196,7 +196,11 @@ pub fn add_endpoint( // Create the operation, it will be repeated if we have several methods let operation = openapiv3::Operation { - tags: vec![namespace.to_string()], + tags: if let Some(doc_tag) = &endpoint.doc_tag { + vec![doc_tag.clone()] + } else { + vec![namespace.to_string()] + }, summary: sum_desc.summary, description: sum_desc.description, // external_docs: tac.convert_external_docs(endpoint), diff --git a/compiler-rs/clients_schema_to_openapi/src/schemas.rs b/compiler-rs/clients_schema_to_openapi/src/schemas.rs index 256eb5d3b0..00d0c69b4f 100644 --- a/compiler-rs/clients_schema_to_openapi/src/schemas.rs +++ b/compiler-rs/clients_schema_to_openapi/src/schemas.rs @@ -38,7 +38,7 @@ const SCHEMA_PLACEHOLDER: ReferenceOr = ReferenceOr::Reference { /// Convert `schema.json` type and value definitions to OpenAPI schemas: /// /// The `convert_*` functions return a concrete schema and not a reference and do not store them in -/// the OpenAPI `components.schema`. This is the role of `for_type_name` hat creates and stores the +/// the OpenAPI `components.schema`. This is the role of `for_type_name` that creates and stores the /// schema and returns a reference. impl<'a> TypesAndComponents<'a> { /// Convert a value. Returns a schema reference and not a concrete schema, as values can 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 7df7ced4c3..b1c661fea0 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/build-model.ts b/compiler/src/model/build-model.ts index f59d1359fb..dffbde8b7c 100644 --- a/compiler/src/model/build-model.ts +++ b/compiler/src/model/build-model.ts @@ -67,6 +67,7 @@ export function compileEndpoints (): Record { name: api, description: spec.documentation.description, docUrl: spec.documentation.url, + docTag: spec.docTag, // Setting these values by default should be removed // when we no longer use rest-api-spec stubs as the // source of truth for stability/visibility. diff --git a/compiler/src/model/json-spec.ts b/compiler/src/model/json-spec.ts index 491321b5ec..ff4739b308 100644 --- a/compiler/src/model/json-spec.ts +++ b/compiler/src/model/json-spec.ts @@ -61,6 +61,7 @@ export interface JsonSpec { description: string required?: boolean } + docTag?: string } export default function buildJsonSpec (): Map { diff --git a/compiler/src/model/metamodel.ts b/compiler/src/model/metamodel.ts index 9e73372d1c..f67053936a 100644 --- a/compiler/src/model/metamodel.ts +++ b/compiler/src/model/metamodel.ts @@ -408,7 +408,7 @@ export class Endpoint { docId?: string deprecation?: Deprecation availability: Availabilities - + docTag?: string /** * If the request value is `null` it means that there is not yet a * request type definition for this endpoint. diff --git a/compiler/src/model/utils.ts b/compiler/src/model/utils.ts index e330242eef..60e8ab7f31 100644 --- a/compiler/src/model/utils.ts +++ b/compiler/src/model/utils.ts @@ -625,7 +625,7 @@ export function hoistRequestAnnotations ( request: model.Request, jsDocs: JSDoc[], mappings: Record, response: model.TypeName | null ): void { const knownRequestAnnotations = [ - 'rest_spec_name', 'behavior', 'class_serializer', 'index_privileges', 'cluster_privileges', 'doc_id', 'availability' + 'rest_spec_name', 'behavior', 'class_serializer', 'index_privileges', 'cluster_privileges', 'doc_id', 'availability', 'doc_tag' ] // in most of the cases the jsDocs comes in a single block, // but it can happen that the user defines multiple single line jsDoc. @@ -696,6 +696,9 @@ export function hoistRequestAnnotations ( for (const [availabilityName, availabilityValue] of Object.entries(availabilities)) { endpoint.availability[availabilityName] = availabilityValue } + } else if (tag === 'doc_tag') { + assert(jsDocs, value.trim() !== '', `Request ${request.name.name}'s @doc_tag cannot be empty`) + endpoint.docTag = value.trim() } else { assert(jsDocs, false, `Unhandled tag: '${tag}' with value: '${value}' on request ${request.name.name}`) } diff --git a/docs/modeling-guide.md b/docs/modeling-guide.md index 123a844acb..b34470daf5 100644 --- a/docs/modeling-guide.md +++ b/docs/modeling-guide.md @@ -631,6 +631,24 @@ class Request { foobar,/guide/en/example ``` +#### `@doc_tag` + +An OpenAPI tag that is used to group similar endpoints in the API documentation. +If it is absent, by default the tag is derived from the first part of the namespace. + +```ts +/** + * @rest_spec_name api + * @doc_tag my tag + */ +class Request { + ... +} +``` + +NOTE: In the OpenAPI specification, operations can have multiple tags. However, we currently support only a single tag. + + #### `@codegen_name` A custom name that can be used to display the property. Useful in Enums and diff --git a/output/openapi/elasticsearch-openapi.json b/output/openapi/elasticsearch-openapi.json index f749a2c558..e2aa74a1c3 100644 --- a/output/openapi/elasticsearch-openapi.json +++ b/output/openapi/elasticsearch-openapi.json @@ -10769,7 +10769,7 @@ }, "put": { "tags": [ - "indices" + "data stream" ], "summary": "Create a data stream", "description": "Creates a data stream.\nYou must have a matching index template with data stream enabled.", diff --git a/output/openapi/elasticsearch-serverless-openapi.json b/output/openapi/elasticsearch-serverless-openapi.json index cbb7943702..5c8733ee36 100644 --- a/output/openapi/elasticsearch-serverless-openapi.json +++ b/output/openapi/elasticsearch-serverless-openapi.json @@ -6422,7 +6422,7 @@ }, "put": { "tags": [ - "indices" + "data stream" ], "summary": "Create a data stream", "description": "Creates a data stream.\nYou must have a matching index template with data stream enabled.", diff --git a/output/schema/schema.json b/output/schema/schema.json index 0077159974..2a9e07a498 100644 --- a/output/schema/schema.json +++ b/output/schema/schema.json @@ -6148,6 +6148,7 @@ } }, "description": "Create a data stream.\nCreates a data stream.\nYou must have a matching index template with data stream enabled.", + "docTag": "data stream", "docUrl": "https://www.elastic.co/guide/en/elasticsearch/reference/master/data-streams.html", "name": "indices.create_data_stream", "privileges": { @@ -127917,7 +127918,7 @@ } } ], - "specLocation": "indices/create_data_stream/IndicesCreateDataStreamRequest.ts#L24-L57" + "specLocation": "indices/create_data_stream/IndicesCreateDataStreamRequest.ts#L24-L58" }, { "kind": "response", diff --git a/specification/indices/create_data_stream/IndicesCreateDataStreamRequest.ts b/specification/indices/create_data_stream/IndicesCreateDataStreamRequest.ts index c52129519a..9a22677a50 100644 --- a/specification/indices/create_data_stream/IndicesCreateDataStreamRequest.ts +++ b/specification/indices/create_data_stream/IndicesCreateDataStreamRequest.ts @@ -29,6 +29,7 @@ import { Duration } from '@_types/Time' * @availability stack since=7.9.0 stability=stable * @availability serverless stability=stable visibility=public * @index_privileges create_index + * @doc_tag data stream */ export interface Request extends RequestBase { path_parts: { diff --git a/typescript-generator/src/metamodel.ts b/typescript-generator/src/metamodel.ts index 9e73372d1c..f67053936a 100644 --- a/typescript-generator/src/metamodel.ts +++ b/typescript-generator/src/metamodel.ts @@ -408,7 +408,7 @@ export class Endpoint { docId?: string deprecation?: Deprecation availability: Availabilities - + docTag?: string /** * If the request value is `null` it means that there is not yet a * request type definition for this endpoint.