Add attributes to mark specs as generated files

This commit is contained in:
Tom Ratcliffe
2025-11-28 11:22:59 +01:00
committed by Hugo Häggmark
parent 95da237f51
commit 9ce9e49da3
15 changed files with 219 additions and 79 deletions
+2 -2
View File
@@ -172,11 +172,11 @@ jobs:
- name: Generate OpenAPI specs - name: Generate OpenAPI specs
run: | run: |
extract_error_message='ERROR! OpenAPI generation failed!' extract_error_message='ERROR! OpenAPI generation failed!'
yarn generate-openapi || (echo "${extract_error_message}" && false) yarn generate:openapi || (echo "${extract_error_message}" && false)
- name: Verify generated specs - name: Verify generated specs
run: | run: |
git add -N . git add -N .
uncommited_error_message="ERROR! OpenAPI generation has not been committed. Please run 'yarn generate-openapi', commit the changes and push again." uncommited_error_message="ERROR! OpenAPI generation has not been committed. Please run 'yarn generate:openapi', commit the changes and push again."
file_diff="$(git diff --name-only ':!conf')" file_diff="$(git diff --name-only ':!conf')"
if [ -n "$file_diff" ]; then if [ -n "$file_diff" ]; then
echo "$file_diff" echo "$file_diff"
+1 -2
View File
@@ -130,8 +130,7 @@ openapi3-gen: swagger-gen ## Generates OpenApi 3 specs from the Swagger 2 alread
.PHONY: all-openapi3-gen .PHONY: all-openapi3-gen
all-openapi3-gen: openapi3-gen all-openapi3-gen: openapi3-gen
$(GO) test ./pkg/tests/apis || true $(GO) test ./pkg/tests/apis || true
yarn workspace @grafana/api-clients process-specs yarn workspace @grafana/openapi process-specs
yarn workspace @grafana/openapi copyfiles
##@ Internationalisation ##@ Internationalisation
.PHONY: i18n-extract-enterprise .PHONY: i18n-extract-enterprise
+1 -1
View File
@@ -77,7 +77,7 @@
"plugin:i18n-extract": "nx run-many -t i18n-extract --projects='tag:scope:plugin'", "plugin:i18n-extract": "nx run-many -t i18n-extract --projects='tag:scope:plugin'",
"generate-apis": "yarn workspace @grafana/api-clients generate-apis", "generate-apis": "yarn workspace @grafana/api-clients generate-apis",
"generate:api-client": "yarn workspace @grafana/api-clients generate:api-client", "generate:api-client": "yarn workspace @grafana/api-clients generate:api-client",
"generate-openapi": "yarn workspace @grafana/openapi generate-openapi" "generate:openapi": "yarn workspace @grafana/openapi generate:openapi"
}, },
"grafana": { "grafana": {
"whatsNewUrl": "https://grafana.com/docs/grafana/next/whatsnew/whats-new-in-v%[1]s-%[2]s/", "whatsNewUrl": "https://grafana.com/docs/grafana/next/whatsnew/whats-new-in-v%[1]s-%[2]s/",
+1 -2
View File
@@ -104,8 +104,7 @@
"typecheck": "tsc --emitDeclarationOnly false --noEmit", "typecheck": "tsc --emitDeclarationOnly false --noEmit",
"prepack": "cp package.json package.json.bak && node ../../scripts/prepare-npm-package.js", "prepack": "cp package.json package.json.bak && node ../../scripts/prepare-npm-package.js",
"postpack": "mv package.json.bak package.json", "postpack": "mv package.json.bak package.json",
"process-specs": "NODE_OPTIONS='--disable-warning=ExperimentalWarning' node --experimental-strip-types src/scripts/process-specs.ts", "generate-apis": "yarn workspace @grafana/openapi process-specs && NODE_OPTIONS='--disable-warning=ExperimentalWarning' rtk-query-codegen-openapi ./src/scripts/generate-rtk-apis.ts",
"generate-apis": "yarn process-specs && NODE_OPTIONS='--disable-warning=ExperimentalWarning' rtk-query-codegen-openapi ./src/scripts/generate-rtk-apis.ts",
"generate:api-client": "NODE_OPTIONS='--experimental-strip-types --disable-warning=ExperimentalWarning' plop --plopfile src/generator/plopfile.ts" "generate:api-client": "NODE_OPTIONS='--experimental-strip-types --disable-warning=ExperimentalWarning' plop --plopfile src/generator/plopfile.ts"
}, },
"devDependencies": { "devDependencies": {
@@ -45,7 +45,7 @@ If an error about a missing OpenAPI schema appears, check that:
1. The API group and version exist in the backend 1. The API group and version exist in the backend
2. The `TestIntegrationOpenAPIs` test has been run to generate the schema (step 1 in the [main API documentation](../../public/app/api/README.md)). 2. The `TestIntegrationOpenAPIs` test has been run to generate the schema (step 1 in the [main API documentation](../../public/app/api/README.md)).
3. The schema file exists at `data/openapi/<group>-<version>.json` 3. The schema file exists at `packages/grafana-openapi/src/apis/<group>-<version>.json`
### Validation Errors ### Validation Errors
@@ -66,7 +66,7 @@ export const runGenerateApis =
let command; let command;
if (isEnterprise) { if (isEnterprise) {
command = command =
'yarn workspace @grafana/api-clients process-specs && npx rtk-query-codegen-openapi ./local/generate-enterprise-apis.ts'; 'yarn workspace @grafana/openapi process-specs && npx rtk-query-codegen-openapi ./local/generate-enterprise-apis.ts';
} else { } else {
command = 'yarn workspace @grafana/api-clients generate-apis'; command = 'yarn workspace @grafana/api-clients generate-apis';
} }
@@ -30,7 +30,7 @@ const createAPIConfig = (app: string, version: string, filterEndpoints?: Endpoin
const filePath = `../clients/rtkq/${app}/${version}/endpoints.gen.ts`; const filePath = `../clients/rtkq/${app}/${version}/endpoints.gen.ts`;
return { return {
[filePath]: { [filePath]: {
schemaFile: path.join(basePath, `data/openapi/${app}.grafana.app-${version}.json`), schemaFile: path.join(basePath, `packages/grafana-openapi/src/apis/${app}.grafana.app-${version}.json`),
apiFile: `../clients/rtkq/${app}/${version}/baseAPI.ts`, apiFile: `../clients/rtkq/${app}/${version}/baseAPI.ts`,
filterEndpoints, filterEndpoints,
tag: true, tag: true,
+2
View File
@@ -0,0 +1,2 @@
api/*.json linguist-generated
apis/*.json linguist-generated
+12 -11
View File
@@ -9,6 +9,7 @@
"openapi", "openapi",
"typescript" "typescript"
], ],
"type": "module",
"sideEffects": false, "sideEffects": false,
"repository": { "repository": {
"type": "git", "type": "git",
@@ -35,24 +36,24 @@
"LICENSE_APACHE2" "LICENSE_APACHE2"
], ],
"scripts": { "scripts": {
"build": "", "build": "yarn clean && yarn create:folders && yarn copy:dist",
"bundle": "cp ./src/apis/* ./dist/apis && cp ./src/api/* ./dist/api",
"clean": "rm -rf ./dist && rm -f package.tgz", "clean": "rm -rf ./dist && rm -f package.tgz",
"copy-api": "cp ../../public/openapi3.json ./src/api", "copy:api": "cp ../../public/openapi3.json ./src/api",
"copy-apis": "cp ../../data/openapi/* ./src/apis", "copy:dist": "cp ./src/apis/* ./dist/apis && cp ./src/api/* ./dist/api",
"copyfiles": "yarn copy-api && yarn copy-apis && yarn format", "create:folders": "mkdir -p dist/api dist/apis",
"createfolders": "mkdir dist && mkdir dist/api && mkdir dist/apis", "format": "prettier \"**/*.json\" --write --log-level=warn",
"format": "prettier --list-different \"**/*.json\" --write", "generate:openapi": "make -C ../../ all-openapi3-gen",
"generate-openapi": "make -C ../../ all-openapi3-gen",
"typecheck": "", "typecheck": "",
"prepack": "yarn clean && yarn createfolders && yarn bundle", "prepack": "yarn build",
"postpack": "" "postpack": "",
"process-specs": "node src/scripts/process-specs.ts && yarn copy:api && yarn format"
}, },
"engines": { "engines": {
"node": ">= 22" "node": ">= 24 <25"
}, },
"packageManager": "yarn@4.11.0", "packageManager": "yarn@4.11.0",
"devDependencies": { "devDependencies": {
"openapi-types": "^12.1.3",
"prettier": "3.6.2" "prettier": "3.6.2"
} }
} }
+43 -47
View File
@@ -6079,6 +6079,10 @@
"is_paused": { "is_paused": {
"type": "boolean" "type": "boolean"
}, },
"message": {
"description": "Field is only populated when listing alert rule versions.",
"type": "string"
},
"metadata": { "metadata": {
"$ref": "#/components/schemas/AlertRuleMetadata" "$ref": "#/components/schemas/AlertRuleMetadata"
}, },
@@ -7604,6 +7608,28 @@
"TLSConfig": { "TLSConfig": {
"$ref": "#/components/schemas/TLSConfig" "$ref": "#/components/schemas/TLSConfig"
}, },
"audience": {
"description": "Audience optionally specifies the intended audience of the\nrequest. If empty, the value of TokenURL is used as the\nintended audience. Only used if\nGrantType is set to \"urn:ietf:params:oauth:grant-type:jwt-bearer\".",
"type": "string"
},
"claims": {
"additionalProperties": {},
"description": "Claims is a map of claims to be added to the JWT token. Only used if\nGrantType is set to \"urn:ietf:params:oauth:grant-type:jwt-bearer\".",
"type": "object"
},
"client_certificate_key": {
"$ref": "#/components/schemas/Secret"
},
"client_certificate_key_file": {
"type": "string"
},
"client_certificate_key_id": {
"type": "string"
},
"client_certificate_key_ref": {
"description": "ClientCertificateKeyRef is the name of the secret within the secret manager to use as the client\nsecret.",
"type": "string"
},
"client_id": { "client_id": {
"type": "string" "type": "string"
}, },
@@ -7623,6 +7649,14 @@
}, },
"type": "object" "type": "object"
}, },
"grant_type": {
"description": "GrantType is the OAuth2 grant type to use. It can be one of\n\"client_credentials\" or \"urn:ietf:params:oauth:grant-type:jwt-bearer\" (RFC 7523).\nDefault value is \"client_credentials\"",
"type": "string"
},
"iss": {
"description": "Iss is the OAuth client identifier used when communicating with\nthe configured OAuth provider. Default value is client_id. Only used if\nGrantType is set to \"urn:ietf:params:oauth:grant-type:jwt-bearer\".",
"type": "string"
},
"no_proxy": { "no_proxy": {
"description": "NoProxy contains addresses that should not use a proxy.", "description": "NoProxy contains addresses that should not use a proxy.",
"type": "string" "type": "string"
@@ -7643,6 +7677,10 @@
}, },
"type": "array" "type": "array"
}, },
"signature_algorithm": {
"description": "SignatureAlgorithm is the RSA algorithm used to sign JWT token. Only used if\nGrantType is set to \"urn:ietf:params:oauth:grant-type:jwt-bearer\".\nDefault value is RS256 and valid values RS256, RS384, RS512",
"type": "string"
},
"token_url": { "token_url": {
"type": "string" "type": "string"
} }
@@ -11347,6 +11385,9 @@
"teamId": { "teamId": {
"format": "int64", "format": "int64",
"type": "integer" "type": "integer"
},
"uid": {
"type": "string"
} }
}, },
"type": "object" "type": "object"
@@ -16492,7 +16533,7 @@
}, },
{ {
"in": "header", "in": "header",
"name": "x-grafana-alerting-notification-receiver", "name": "x-grafana-alerting-notification-settings",
"schema": { "schema": {
"type": "string" "type": "string"
} }
@@ -16837,7 +16878,7 @@
}, },
{ {
"in": "header", "in": "header",
"name": "x-grafana-alerting-notification-receiver", "name": "x-grafana-alerting-notification-settings",
"schema": { "schema": {
"type": "string" "type": "string"
} }
@@ -17017,51 +17058,6 @@
"tags": ["dashboards", "snapshots"] "tags": ["dashboards", "snapshots"]
} }
}, },
"/dashboards/calculate-diff": {
"post": {
"operationId": "calculateDashboardDiff",
"requestBody": {
"content": {
"application/json": {
"schema": {
"properties": {
"base": {
"$ref": "#/components/schemas/CalculateDiffTarget"
},
"diffType": {
"description": "The type of diff to return\nDescription:\n`basic`\n`json`",
"enum": ["basic", "json"],
"type": "string"
},
"new": {
"$ref": "#/components/schemas/CalculateDiffTarget"
}
},
"type": "object"
}
}
},
"required": true,
"x-originalParamName": "Body"
},
"responses": {
"200": {
"$ref": "#/components/responses/calculateDashboardDiffResponse"
},
"401": {
"$ref": "#/components/responses/unauthorisedError"
},
"403": {
"$ref": "#/components/responses/forbiddenError"
},
"500": {
"$ref": "#/components/responses/internalServerError"
}
},
"summary": "Perform diff on two dashboards.",
"tags": ["dashboards"]
}
},
"/dashboards/db": { "/dashboards/db": {
"post": { "post": {
"description": "Creates a new dashboard or updates an existing dashboard.\nNote: This endpoint is not intended for creating folders, use `POST /api/folders` for that.", "description": "Creates a new dashboard or updates an existing dashboard.\nNote: This endpoint is not intended for creating folders, use `POST /api/folders` for that.",
@@ -1723,6 +1723,15 @@
"type": "string" "type": "string"
} }
}, },
{
"name": "permission",
"in": "query",
"description": "permission needed for the resource (view, edit, admin)",
"schema": {
"type": "string",
"enum": ["view", "edit", "admin"]
}
},
{ {
"name": "sort", "name": "sort",
"in": "query", "in": "query",
@@ -3809,6 +3809,43 @@
} }
] ]
}, },
"/teams/{name}/groups": {
"get": {
"tags": ["Team"],
"description": "connect GET requests to groups of Team",
"operationId": "getTeamGroups",
"responses": {
"200": {
"description": "OK",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/GetGroups"
}
}
}
}
},
"x-kubernetes-action": "connect",
"x-kubernetes-group-version-kind": {
"group": "iam.grafana.app",
"version": "v0alpha1",
"kind": "GetGroups"
}
},
"parameters": [
{
"name": "name",
"in": "path",
"description": "name of the GetGroups",
"required": true,
"schema": {
"type": "string",
"uniqueItems": true
}
}
]
},
"/teams/{name}/members": { "/teams/{name}/members": {
"get": { "get": {
"tags": ["Team"], "tags": ["Team"],
@@ -4789,6 +4826,38 @@
} }
} }
}, },
"GetGroups": {
"type": "object",
"required": ["items"],
"properties": {
"apiVersion": {
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
"type": "string"
},
"items": {
"type": "array",
"items": {
"default": {},
"allOf": [
{
"$ref": "#/components/schemas/VersionsV0alpha1Kinds7RoutesGroupsGETResponseExternalGroupMapping"
}
]
}
},
"kind": {
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
"type": "string"
}
},
"x-kubernetes-group-version-kind": [
{
"group": "iam.grafana.app",
"kind": "GetGroups",
"version": "v0alpha1"
}
]
},
"ServiceAccount": { "ServiceAccount": {
"type": "object", "type": "object",
"required": ["metadata", "spec"], "required": ["metadata", "spec"],
@@ -5236,6 +5305,20 @@
} }
} }
}, },
"VersionsV0alpha1Kinds7RoutesGroupsGETResponseExternalGroupMapping": {
"type": "object",
"required": ["name", "externalGroup"],
"properties": {
"externalGroup": {
"type": "string",
"default": ""
},
"name": {
"type": "string",
"default": ""
}
}
},
"Unstructured": { "Unstructured": {
"type": "object", "type": "object",
"additionalProperties": true, "additionalProperties": true,
@@ -5856,6 +5939,38 @@
} }
} }
}, },
"github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GetGroups": {
"type": "object",
"required": ["items"],
"properties": {
"apiVersion": {
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
"type": "string"
},
"items": {
"type": "array",
"items": {
"default": {}
}
},
"kind": {
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
"type": "string"
}
}
},
"github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GetGroupsBody": {
"type": "object",
"required": ["items"],
"properties": {
"items": {
"type": "array",
"items": {
"default": {}
}
}
}
},
"github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRole": { "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRole": {
"type": "object", "type": "object",
"required": ["metadata", "spec"], "required": ["metadata", "spec"],
@@ -7036,6 +7151,20 @@
} }
} }
}, },
"github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.VersionsV0alpha1Kinds7RoutesGroupsGETResponseExternalGroupMapping": {
"type": "object",
"required": ["name", "externalGroup"],
"properties": {
"externalGroup": {
"type": "string",
"default": ""
},
"name": {
"type": "string",
"default": ""
}
}
},
"APIResource": { "APIResource": {
"description": "APIResource specifies the name of a resource and whether it is namespaced.", "description": "APIResource specifies the name of a resource and whether it is namespaced.",
"type": "object", "type": "object",
@@ -175,12 +175,16 @@ function processDirectory(sourceDir: string, outputDir: string) {
// Grafana root path - navigate up from this script's directory // Grafana root path - navigate up from this script's directory
const basePath = path.resolve(import.meta.dirname, '../../../..'); const basePath = path.resolve(import.meta.dirname, '../../../..');
const sourceDirs = [ const oss = {
path.join(basePath, 'pkg/tests/apis/openapi_snapshots'), source: path.join(basePath, 'pkg/tests/apis/openapi_snapshots'),
path.join(basePath, 'pkg/extensions/apiserver/tests/openapi_snapshots'), output: path.join(import.meta.dirname, '../apis'),
]; };
const outputDir = path.join(basePath, 'data/openapi');
for (const sourceDir of sourceDirs) { const enterprise = {
processDirectory(sourceDir, outputDir); source: path.join(basePath, 'pkg/extensions/apiserver/tests/openapi_snapshots'),
output: path.join(basePath, 'data/openapi'),
};
for (const config of [oss, enterprise]) {
processDirectory(config.source, config.output);
} }
+4 -4
View File
@@ -2,14 +2,14 @@ This folder contains a rendered OpenAPI file for each group/version. The “real
## Generating RTK API Clients ## Generating RTK API Clients
The RTK API clients are generated from processed OpenAPI files using the `scripts/process-specs.ts` script. The source files are in `pkg/tests/apis/openapi_snapshots`, and the processed files are stored in the `data/openapi` directory. Spec processing happens as part of `yarn generate-apis` task, but can also be triggered separately (see below). The RTK API clients are generated from processed OpenAPI files using the `packages/grafana-openapi/src/scripts/process-specs.ts` script. The source files are in `pkg/tests/apis/openapi_snapshots`, and the processed files are stored in the `packages/grafana-openapi/src/apis` directory. Spec processing happens as part of `yarn generate-apis` task, but can also be triggered separately (see below).
To generate or update the RTK API clients: To generate or update the RTK API clients:
1. _If generating or updating an RTK client for the first time_, update `scripts/generate-rtk-apis.js` so `schemaFile` points to the processed spec files, for example: 1. _If generating or updating an RTK client for the first time_, update `scripts/generate-rtk-apis.js` so `schemaFile` points to the processed spec files, for example:
```typescript ```typescript
'../public/app/features/dashboards/api/endpoints.gen.ts': { '../public/app/features/dashboards/api/endpoints.gen.ts': {
schemaFile: '../data/openapi/dashboard.grafana.app-v0alpha1.json', schemaFile: 'packages/grafana-openapi/src/apis/dashboard.grafana.app-v0alpha1.json',
}, },
``` ```
@@ -24,9 +24,9 @@ To generate or update the RTK API clients:
```bash ```bash
yarn generate-apis yarn generate-apis
``` ```
This command generates (or updates) the spec files in the `data/openapi` directory and generates the RTK API clients. This command generates (or updates) the spec files in the `packages/grafana-openapi/src/apis` directory and generates the RTK API clients.
If you want to process the OpenAPI files without generating the RTK API clients (for example, if you have a separate `generate-rtk-apis` file), run: If you want to process the OpenAPI files without generating the RTK API clients (for example, if you have a separate `generate-rtk-apis` file), run:
```bash ```bash
yarn process-specs yarn workspace @grafana/openapi process-specs
``` ```
+1
View File
@@ -3416,6 +3416,7 @@ __metadata:
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@grafana/openapi@workspace:packages/grafana-openapi" resolution: "@grafana/openapi@workspace:packages/grafana-openapi"
dependencies: dependencies:
openapi-types: "npm:^12.1.3"
prettier: "npm:3.6.2" prettier: "npm:3.6.2"
languageName: unknown languageName: unknown
linkType: soft linkType: soft