kibana/packages/kbn-openapi-generator
Devin W. Hurley d2dd29eb85
[8.16] [Security Solution] Fixes exception item comment validation on newline chars \n (#202063) (#203709)
# Backport

This will backport the following commits from `main` to `8.16`:
- [[Security Solution] Fixes exception item comment validation on
newline chars `\n`
(#202063)](https://github.com/elastic/kibana/pull/202063)

<!--- Backport version: 8.9.8 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Devin W.
Hurley","email":"devin.hurley@elastic.co"},"sourceCommit":{"committedDate":"2024-12-10T22:19:32Z","message":"[Security
Solution] Fixes exception item comment validation on newline chars `\\n`
(#202063)\n\n## Summary\r\n\r\nFixes:
https://github.com/elastic/kibana/issues/201820\r\n\r\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"35aeac104359eae81a233d0b8a9acaa97119d006","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["bug","review","release_note:fix","v9.0.0","Team:Detections
and Resp","Feature:Rule
Exceptions","backport:version","v8.18.0","v8.16.2","v8.17.1"],"number":202063,"url":"https://github.com/elastic/kibana/pull/202063","mergeCommit":{"message":"[Security
Solution] Fixes exception item comment validation on newline chars `\\n`
(#202063)\n\n## Summary\r\n\r\nFixes:
https://github.com/elastic/kibana/issues/201820\r\n\r\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"35aeac104359eae81a233d0b8a9acaa97119d006"}},"sourceBranch":"main","suggestedTargetBranches":["8.x","8.16","8.17"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","labelRegex":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/202063","number":202063,"mergeCommit":{"message":"[Security
Solution] Fixes exception item comment validation on newline chars `\\n`
(#202063)\n\n## Summary\r\n\r\nFixes:
https://github.com/elastic/kibana/issues/201820\r\n\r\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"35aeac104359eae81a233d0b8a9acaa97119d006"}},{"branch":"8.x","label":"v8.18.0","labelRegex":"^v8.18.0$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.16","label":"v8.16.2","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.17","label":"v8.17.1","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->
2024-12-10 23:33:06 -05:00
..
docs Import zod from @kbn/zod and add an eslint rule (#190581) 2024-08-21 04:14:55 -05:00
redocly_linter Adds AGPL 3.0 license (#192025) 2024-09-06 19:02:41 -06:00
src [8.16] [Security Solution] Fixes exception item comment validation on newline chars \n (#202063) (#203709) 2024-12-10 23:33:06 -05:00
image.png [Security Solution] Extract OpenAPI codegen to a package (#166269) 2023-09-25 10:51:40 +02:00
index.ts Adds AGPL 3.0 license (#192025) 2024-09-06 19:02:41 -06:00
jest.config.js Adds AGPL 3.0 license (#192025) 2024-09-06 19:02:41 -06:00
kibana.jsonc [Security Solution] Migrate Rules schema to OpenAPI (#167999) 2023-10-27 18:00:10 +02:00
package.json Adds AGPL 3.0 license (#192025) 2024-09-06 19:02:41 -06:00
README.md Import zod from @kbn/zod and add an eslint rule (#190581) 2024-08-21 04:14:55 -05:00
tsconfig.json [Security Solution] Extract OpenAPI codegen to a package (#166269) 2023-09-25 10:51:40 +02:00

OpenAPI Code Generator for Kibana

This code generator could be used to generate runtime types, documentation, server stub implementations, clients, and much more given OpenAPI specification.

Getting started

To start with code generation you should have OpenAPI specification describing your API endpoint request and response schemas along with common types used in your API. The code generation script supports OpenAPI 3.1.0, refer to https://swagger.io/specification/ for more details.

OpenAPI specification should be in YAML format and have .schema.yaml extension. Here's a simple example of OpenAPI specification:

openapi: 3.0.0
info:
  title: Install Prebuilt Rules API endpoint
  version: 2023-10-31
paths:
  /api/detection_engine/rules/prepackaged:
    put:
      operationId: InstallPrebuiltRules
      x-codegen-enabled: true
      summary: Installs all Elastic prebuilt rules and timelines
      tags:
        - Prebuilt Rules API
      responses:
        200:
          description: Indicates a successful call
          content:
            application/json:
              schema:
                type: object
                properties:
                  rules_installed:
                    type: integer
                    description: The number of rules installed
                    minimum: 0
                  rules_updated:
                    type: integer
                    description: The number of rules updated
                    minimum: 0
                  timelines_installed:
                    type: integer
                    description: The number of timelines installed
                    minimum: 0
                  timelines_updated:
                    type: integer
                    description: The number of timelines updated
                    minimum: 0
                required:
                  - rules_installed
                  - rules_updated
                  - timelines_installed
                  - timelines_updated

Put it anywhere in your plugin, the code generation script will traverse the whole plugin directory and find all .schema.yaml files.

Then to generate code run the following command:

node scripts/generate_openapi --rootDir ./x-pack/plugins/security_solution

Generator command output

By default it uses the zod_operation_schema template which produces runtime types for request and response schemas described in OpenAPI specification. The generated code will be placed adjacent to the .schema.yaml file and will have .gen.ts extension.

Example of generated code:

import { z } from '@kbn/zod';

/*
 * NOTICE: Do not edit this file manually.
 * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator.
 * 
 * info:
 *   title: Install Prebuilt Rules API endpoint
 *   version: 1
 */

export type InstallPrebuiltRulesResponse = z.infer<typeof InstallPrebuiltRulesResponse>;
export const InstallPrebuiltRulesResponse = z.object({
  /**
   * The number of rules installed
   */
  rules_installed: z.number().int().min(0),
  /**
   * The number of rules updated
   */
  rules_updated: z.number().int().min(0),
  /**
   * The number of timelines installed
   */
  timelines_installed: z.number().int().min(0),
  /**
   * The number of timelines updated
   */
  timelines_updated: z.number().int().min(0),
});

Programmatic API

Alternatively, you can use the code generator programmatically. You can create a script file and run it with node command. This could be useful if you want to set up code generation in your CI pipeline. Here's an example of such script:

require('../../../../../src/setup_node_env');
const { generate } = require('@kbn/openapi-generator');
const { resolve } = require('path');

const SECURITY_SOLUTION_ROOT = resolve(__dirname, '../..');

generate({
  rootDir: SECURITY_SOLUTION_ROOT, // Path to the plugin root directory
  sourceGlob: './**/*.schema.yaml', // Glob pattern to find OpenAPI specification files
  templateName: 'zod_operation_schema', // Name of the template to use
});

CI integration

To make sure that generated code is always in sync with its OpenAPI specification it is recommended to add a command to your CI pipeline that will run code generation on every pull request and commit the changes if there are any.

First, create a script that will run code generation and commit the changes. See .buildkite/scripts/steps/code_generation/security_solution_codegen.sh for an example:

#!/usr/bin/env bash

set -euo pipefail

source .buildkite/scripts/common/util.sh

.buildkite/scripts/bootstrap.sh

echo --- Security Solution OpenAPI Code Generation

(cd x-pack/plugins/security_solution && yarn openapi:generate)
check_for_changed_files "yarn openapi:generate" true

This script sets up the minimal environment required for code generation and runs the code generation script. Then it checks if there are any changes and commits them if there are any using the check_for_changed_files function.

Then add the code generation script to the build pipeline. Open the buildkite checks at.buildkite/scripts/steps/checks.sh, and add the path to your code generation script:

...
.buildkite/scripts/steps/checks/saved_objects_definition_change.sh
.buildkite/scripts/steps/code_generation/elastic_assistant_codegen.sh
.buildkite/scripts/steps/code_generation/security_solution_codegen.sh
.buildkite/scripts/steps/code_generation/osquery_codegen.sh
.buildkite/scripts/steps/checks/yarn_deduplicate.sh
...

Now on every pull request the code generation script will run and commit the changes if there are any.

OpenAPI Schema

The code generator supports the OpenAPI definitions described in the request, response, and component sections of the document.

For every API operation (GET, POST, etc) it is required to specify the operationId field. This field is used to generate the name of the generated types. For example, if the operationId is InstallPrebuiltRules then the generated types will be named InstallPrebuiltRulesResponse and InstallPrebuiltRulesRequest. If the operationId is not specified then the code generation will throw an error.

The x-codegen-enabled field is used to enable or disable code generation for the operation. If it is not specified then code generation is disabled by default. This field could be also used to disable code generation of common components described in the components section of the OpenAPI specification.

Keep in mind that disabling code generation for common components that are referenced by external OpenAPI specifications could lead to errors during code generation.

Schema files organization

It is recommended to limit the number of operations and components described in a single OpenAPI specification file. Having one HTTP operation in a single file will make it easier to maintain and will keep the generated artifacts granular for ease of reuse and better tree shaking. You can have as many OpenAPI specification files as you want.

Common components

It is common to have shared types that are used in multiple API operations. To avoid code duplication you can define common components in the components section of the OpenAPI specification and put them in a separate file. Then you can reference these components in the parameters and responses sections of the API operations.

Here's an example of the schema that references common components:

openapi: 3.0.0
info:
  title: Delete Rule API endpoint
  version: 2023-10-31
paths:
  /api/detection_engine/rules:
    delete:
      operationId: DeleteRule
      description: Deletes a single rule using the `rule_id` or `id` field.
      parameters:
        - name: id
          in: query
          required: false
          description: The rule's `id` value.
          schema:
            $ref: '../../../model/rule_schema/common_attributes.schema.yaml#/components/schemas/RuleSignatureId'
        - name: rule_id
          in: query
          required: false
          description: The rule's `rule_id` value.
          schema:
            $ref: '../../../model/rule_schema/common_attributes.schema.yaml#/components/schemas/RuleObjectId'
      responses:
        200:
          description: Indicates a successful call.
          content:
            application/json:
              schema:
                $ref: '../../../model/rule_schema/rule_schemas.schema.yaml#/components/schemas/RuleResponse'