Skip to content

Commit 2af8c63

Browse files
committed
Merge remote-tracking branch 'origin/9.0' into backport-5147-to-9.0
2 parents 38a5d19 + d61890b commit 2af8c63

File tree

396 files changed

+61876
-10044
lines changed

Some content is hidden

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

396 files changed

+61876
-10044
lines changed

.github/validate-pr/index.js

Lines changed: 92 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import * as core from '@actions/core'
2727
import { copyFile } from 'fs/promises'
2828
import * as github from '@actions/github'
2929
import specification from '../../output/schema/schema.json' with { type: 'json' }
30+
import baselineValidation from '../../../clients-flight-recorder/recordings/types-validation/types-validation.json' with { type: 'json' }
3031
import { run as getReport } from '../../../clients-flight-recorder/scripts/types-validator/index.js'
3132
import {
3233
getNamespace,
@@ -81,10 +82,13 @@ async function run() {
8182
const specFiles = files.filter(
8283
(file) => file.includes('specification') && !file.includes('compiler/test')
8384
)
84-
const table = []
85+
const reports = new Map()
8586

8687
cd(tsValidationPath)
8788

89+
// Collect all APIs to validate
90+
const apisToValidate = new Set()
91+
8892
for (const file of specFiles) {
8993
if (file.startsWith('specification/_types')) continue
9094
if (file.startsWith('specification/_spec_utils')) continue
@@ -96,53 +100,62 @@ async function run() {
96100
.filter(endpoint => endpoint.name.split('.').filter(s => !privateNames.includes(s))[0] === getApi(file).split('.')[0])
97101
.map(endpoint => endpoint.name)
98102
for (const api of apis) {
99-
const report = await getReport({
100-
api,
101-
'generate-report': false,
102-
request: true,
103-
response: true,
104-
ci: false,
105-
verbose: false
106-
})
107-
table.push(buildTableLine(api, report))
103+
apisToValidate.add(api)
108104
}
109105
} else {
110-
const report = await getReport({
111-
api: getApi(file),
112-
'generate-report': false,
113-
request: true,
114-
response: true,
115-
ci: false,
116-
verbose: false
117-
})
118-
table.push(buildTableLine(getApi(file), report))
106+
const api = getApi(file)
107+
apisToValidate.add(api)
108+
}
109+
}
110+
111+
// Call getReport once with all APIs
112+
if (apisToValidate.size > 0) {
113+
const allApis = Array.from(apisToValidate).join(',')
114+
const report = await getReport({
115+
api: allApis,
116+
'generate-report': false,
117+
request: true,
118+
response: true,
119+
ci: false,
120+
verbose: false
121+
})
122+
123+
// Extract individual API reports from the combined result
124+
for (const api of apisToValidate) {
125+
const namespace = getNamespace(api)
126+
if (report.has(namespace)) {
127+
const namespaceReport = report.get(namespace).find(r => r.api === getName(api))
128+
if (namespaceReport) {
129+
reports.set(api, namespaceReport)
130+
}
131+
}
119132
}
120133
}
121134

122135
cd(path.join(__dirname, '..', '..'))
123136

124-
table.sort((a, b) => {
125-
if (a < b) return -1
126-
if (a > b) return 1
127-
return 0
128-
})
137+
// Compare current reports with baseline and find changes
138+
const changedApis = []
139+
for (const [apiName, report] of reports) {
140+
const baselineReport = findBaselineReport(apiName, baselineValidation)
141+
if (baselineReport && hasChanges(baselineReport, report, apiName)) {
142+
changedApis.push({ api: apiName, baseline: baselineReport, current: report })
143+
}
144+
}
145+
changedApis.sort((a, b) => a.api.localeCompare(b.api))
129146

130-
if (table.length > 0) {
131-
let comment = `Following you can find the validation results for the API${table.length === 1 ? '' : 's'} you have changed.\n\n`
147+
let comment = `Following you can find the validation changes against the target branch for the API${changedApis.length === 1 ? '' : 's'}.\n\n`
148+
if (changedApis.length > 0) {
132149
comment += '| API | Status | Request | Response |\n'
133150
comment += '| --- | --- | --- | --- |\n'
134-
for (const line of [...new Set(table)]) {
135-
comment += line
151+
for (const change of changedApis) {
152+
comment += buildDiffTableLine(change)
136153
}
137-
comment += `\nYou can validate ${table.length === 1 ? 'this' : 'these'} API${table.length === 1 ? '' : 's'} yourself by using the ${tick}make validate${tick} target.\n`
138-
139-
await octokit.rest.issues.createComment({
140-
owner: 'elastic',
141-
repo: 'elasticsearch-specification',
142-
issue_number: context.payload.pull_request.number,
143-
body: comment
144-
})
154+
} else {
155+
comment += '**No changes detected**.\n'
145156
}
157+
comment += `\nYou can validate ${changedApis.length === 1 ? 'this' : 'these'} API${changedApis.length === 1 ? '' : 's'} yourself by using the ${tick}make validate${tick} target.\n`
158+
core.setOutput('comment_body', comment)
146159

147160
core.info('Done!')
148161
}
@@ -151,35 +164,65 @@ function getApi (file) {
151164
return file.split('/').slice(1, 3).filter(s => !privateNames.includes(s)).filter(Boolean).join('.')
152165
}
153166

154-
function buildTableLine (api, report) {
155-
const apiReport = report.get(getNamespace(api)).find(r => r.api === getName(api))
156-
return `| ${tick}${api}${tick} | ${generateStatus(apiReport)} | ${generateRequest(apiReport)} | ${generateResponse(apiReport)} |\n`
167+
function findBaselineReport(apiName, baselineValidation) {
168+
const [namespace, method] = apiName.split('.')
169+
170+
if (!baselineValidation.namespaces[namespace]) {
171+
return null
172+
}
173+
174+
return baselineValidation.namespaces[namespace].apis.find(api => api.api === method)
157175
}
158176

159-
function generateStatus (report) {
160-
if (!report.diagnostics.hasRequestType || !report.diagnostics.hasResponseType) {
177+
function hasChanges(baselineReport, report) {
178+
if (!report) return false
179+
180+
return baselineReport.status !== report.status ||
181+
baselineReport.passingRequest !== report.passingRequest ||
182+
baselineReport.passingResponse !== report.passingResponse
183+
}
184+
185+
function buildDiffTableLine(change) {
186+
const { api, baseline, current } = change
187+
188+
const status = generateStatus(current.status)
189+
const request = generateRequest(current)
190+
const response = generateResponse(current)
191+
192+
const baselineStatus = generateStatus(baseline.status)
193+
const baselineRequest = generateRequest(baseline)
194+
const baselineResponse = generateResponse(baseline)
195+
196+
const statusDiff = status !== baselineStatus ? `${baselineStatus}${status}` : status
197+
const requestDiff = request !== baselineRequest ? `${baselineRequest}${request}` : request
198+
const responseDiff = response !== baselineResponse ? `${baselineResponse}${response}` : response
199+
200+
return `| ${tick}${api}${tick} | ${statusDiff} | ${requestDiff} | ${responseDiff} |\n`
201+
}
202+
203+
204+
function generateStatus (status) {
205+
if (status === 'missing_types' || status === 'missing_request_type' || status === 'missing_response_type') {
161206
return ':orange_circle:'
162207
}
163-
if (report.totalRequest <= 0 || report.totalResponse <= 0) {
208+
if (status === 'missing_test') {
164209
return ':white_circle:'
165210
}
166-
if (report.diagnostics.request.length === 0 && report.diagnostics.response.length === 0) {
211+
if (status === 'passing') {
167212
return ':green_circle:'
168213
}
169214
return ':red_circle:'
170215
}
171216

172217
function generateRequest (r) {
173-
if (r.totalRequest === -1) return 'Missing recording'
174-
if (!r.diagnostics.hasRequestType) return 'Missing type'
175-
if (r.totalRequest === 0) return 'Missing test'
218+
if (r.status === 'missing_test') return 'Missing test'
219+
if (r.status === 'missing_types' || r.status == 'missing_request_type') return 'Missing type'
176220
return `${r.passingRequest}/${r.totalRequest}`
177221
}
178222

179223
function generateResponse (r) {
180-
if (r.totalResponse === -1) return 'Missing recording'
181-
if (!r.diagnostics.hasResponseType) return 'Missing type'
182-
if (r.totalResponse === 0) return 'Missing test'
224+
if (r.status === 'missing_test') return 'Missing test'
225+
if (r.status === 'missing_types' || r.status == 'missing_response_type') return 'Missing type'
183226
return `${r.passingResponse}/${r.totalResponse}`
184227
}
185228

.github/workflows/validate-pr.yml

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,24 @@ jobs:
6464
env:
6565
GCS_CREDENTIALS: ${{ secrets.GCS_CREDENTIALS }}
6666

67-
- name: Remove previous comment
68-
uses: maheshrayas/action-pr-comment-delete@v1
69-
with:
70-
github_token: ${{ secrets.GITHUB_TOKEN }}
71-
org: elastic
72-
repo: elasticsearch-specification
73-
user: github-actions[bot]
74-
issue: ${{ github.event.number }}
75-
7667
- name: Run validation
68+
id: validation
7769
working-directory: ./elasticsearch-specification
7870
run: node .github/validate-pr --token ${{ secrets.GITHUB_TOKEN }}
71+
72+
- name: Find existing comment
73+
uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3.1.0
74+
id: find-comment
75+
with:
76+
issue-number: ${{ github.event.pull_request.number }}
77+
comment-author: 'github-actions[bot]'
78+
body-includes: 'Following you can find the validation changes'
79+
80+
- name: Create or update comment
81+
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0
82+
with:
83+
token: ${{ secrets.GITHUB_TOKEN }}
84+
comment-id: ${{ steps.find-comment.outputs.comment-id }}
85+
issue-number: ${{ github.event.pull_request.number }}
86+
body: ${{ steps.validation.outputs.comment_body }}
87+
edit-mode: replace

.gitignore

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,7 @@ output/schema/schema
6565
# Test suite outputs
6666
compiler/test/**/output/
6767

68-
# Temporary openAPI files
69-
output/openapi/elasticsearch-serverless-openapi.tmp*.json
70-
output/openapi/elasticsearch-serverless-openapi.examples.json
71-
output/openapi/elasticsearch-openapi.tmp*.json
72-
output/openapi/elasticsearch-openapi.examples.json
68+
# Temporary openAPI documentation files
69+
output/openapi/elasticsearch-serverless-openapi-docs*.json
70+
output/openapi/elasticsearch-openapi-docs*.json
71+
output/openapi/elasticsearch*.redirects.csv

Makefile

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ setup: ## Install dependencies for contrib target
4040
@npm install --prefix compiler
4141
@npm install --prefix typescript-generator
4242
@npm install @redocly/cli
43+
@npm install --prefix docs/examples
4344

4445
clean-dep: ## Clean npm dependencies
4546
@rm -rf compiler/node_modules
@@ -53,7 +54,9 @@ transform-to-openapi: ## Generate the OpenAPI definition from the compiled schem
5354
@npm run transform-to-openapi -- --schema output/schema/schema.json --flavor serverless --output output/openapi/elasticsearch-serverless-openapi.json
5455

5556
transform-to-openapi-for-docs: ## Generate the OpenAPI definition tailored for API docs generation
56-
@npm run transform-to-openapi -- --schema output/schema/schema.json --flavor stack --lift-enum-descriptions --merge-multipath-endpoints --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
5760

5861
filter-for-serverless: ## Generate the serverless version from the compiled schema
5962
@npm run --prefix compiler filter-by-availability -- --serverless --visibility=public --input ../output/schema/schema.json --output ../output/output/openapi/elasticsearch-serverless-openapi.json
@@ -62,23 +65,24 @@ dump-routes: ## Create a new schema with all generics expanded
6265
@npm run dump-routes --prefix compiler
6366

6467
overlay-docs: ## Apply overlays to OpenAPI documents
65-
@npx bump overlay "output/openapi/elasticsearch-serverless-openapi.json" "docs/overlays/elasticsearch-serverless-openapi-overlays.yaml" > "output/openapi/elasticsearch-serverless-openapi.tmp1.json"
66-
@npx bump overlay "output/openapi/elasticsearch-serverless-openapi.tmp1.json" "docs/overlays/elasticsearch-shared-overlays.yaml" > "output/openapi/elasticsearch-serverless-openapi.tmp2.json"
67-
@npx @redocly/cli bundle output/openapi/elasticsearch-serverless-openapi.tmp2.json --ext json -o output/openapi/elasticsearch-serverless-openapi.examples.json
68-
@npx bump overlay "output/openapi/elasticsearch-openapi.json" "docs/overlays/elasticsearch-openapi-overlays.yaml" > "output/openapi/elasticsearch-openapi.tmp1.json"
69-
@npx bump overlay "output/openapi/elasticsearch-openapi.tmp1.json" "docs/overlays/elasticsearch-shared-overlays.yaml" > "output/openapi/elasticsearch-openapi.tmp2.json"
70-
@npx @redocly/cli bundle output/openapi/elasticsearch-openapi.tmp2.json --ext json -o output/openapi/elasticsearch-openapi.examples.json
71-
rm output/openapi/elasticsearch-serverless-openapi.tmp*.json
72-
rm output/openapi/elasticsearch-openapi.tmp*.json
68+
@npx bump overlay "output/openapi/elasticsearch-openapi-docs.json" "docs/overlays/elasticsearch-openapi-overlays.yaml" > "output/openapi/elasticsearch-openapi-docs.tmp1.json"
69+
@npx bump overlay "output/openapi/elasticsearch-openapi-docs.tmp1.json" "docs/overlays/elasticsearch-shared-overlays.yaml" > "output/openapi/elasticsearch-openapi-docs.tmp2.json"
70+
@npx @redocly/cli bundle output/openapi/elasticsearch-openapi-docs.tmp2.json --ext json -o output/openapi/elasticsearch-openapi-docs-final.json
71+
rm output/openapi/elasticsearch-openapi-docs.tmp*.json
72+
73+
generate-language-examples:
74+
@node docs/examples/generate-language-examples.js
75+
@npm run format:fix-examples --prefix compiler
76+
77+
generate-language-examples-with-java:
78+
@node docs/examples/generate-language-examples.js java
79+
@npm run format:fix-examples --prefix compiler
7380

7481
lint-docs: ## Lint the OpenAPI documents after overlays
7582
@npx @redocly/cli lint "output/openapi/elasticsearch-*.json" --config "docs/linters/redocly.yaml" --format stylish --max-problems 500
7683

77-
lint-docs-stateful: ## Lint only the elasticsearch-openapi.examples.json file
78-
@npx @redocly/cli lint "output/openapi/elasticsearch-openapi.examples.json" --config "docs/linters/redocly.yaml" --format stylish --max-problems 500
79-
80-
lint-docs-serverless: ## Lint only the serverless OpenAPI document after overlays
81-
@npx @redocly/cli lint "output/openapi/elasticsearch-serverless-openapi.examples.json" --config "docs/linters/redocly.yaml" --format stylish --max-problems 500
84+
lint-docs-stateful: ## Lint only the elasticsearch-openapi-docs-final.json file
85+
@npx @redocly/cli lint "output/openapi/elasticsearch-openapi-docs-final.json" --config "docs/linters/redocly.yaml" --format stylish --max-problems 500
8286

8387
contrib: | generate license-check spec-format-fix transform-to-openapi filter-for-serverless lint-docs ## Pre contribution target
8488

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ Follow the steps to generate the JSON representation, then:
6060
```
6161
# Generate the OpenAPI representation
6262
$ make transform-to-openapi
63+
```
64+
65+
To generate the JSON representation that is used for documentation purposes, the commands are different:
66+
67+
```
68+
# Generate the OpenAPI files
69+
$ make transform-to-openapi-for-docs
6370
6471
# Apply fixes
6572
$ make overlay-docs
@@ -223,6 +230,9 @@ make validate api=xpack.info type=request branch=main
223230

224231
# this will validate the xpack.info request and response types against the 8.15 branch
225232
make validate api=xpack.info branch=8.15
233+
234+
# this will validate the xpack.info and search request and response types against the 8.15 branch
235+
make validate api=xpack.info,search branch=8.15
226236
```
227237

228238
The last command above will install all the dependencies and run, download

0 commit comments

Comments
 (0)