Skip to content

Commit 89c3773

Browse files
committed
Merge remote-tracking branch 'origin/8.19' into backport-4566-to-8.19
2 parents 1045dda + 3f91106 commit 89c3773

File tree

781 files changed

+49392
-2357
lines changed

Some content is hidden

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

781 files changed

+49392
-2357
lines changed

.github/validate-pr/index.js

Lines changed: 94 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,52 +100,63 @@ 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+
if (changedApis.length > 0) {
148+
let comment = `Following you can find the validation changes for the API${changedApis.length === 1 ? '' : 's'} you have modified.\n\n`
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`
154+
comment += `\nYou can validate ${changedApis.length === 1 ? 'this' : 'these'} API${changedApis.length === 1 ? '' : 's'} yourself by using the ${tick}make validate${tick} target.\n`
138155

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-
})
156+
core.setOutput('has_results', 'true')
157+
core.setOutput('comment_body', comment)
158+
} else {
159+
core.setOutput('has_results', 'false')
145160
}
146161

147162
core.info('Done!')
@@ -151,39 +166,69 @@ function getApi (file) {
151166
return file.split('/').slice(1, 3).filter(s => !privateNames.includes(s)).filter(Boolean).join('.')
152167
}
153168

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`
169+
function findBaselineReport(apiName, baselineValidation) {
170+
const [namespace, method] = apiName.split('.')
171+
172+
if (!baselineValidation.namespaces[namespace]) {
173+
return null
174+
}
175+
176+
return baselineValidation.namespaces[namespace].apis.find(api => api.api === method)
177+
}
178+
179+
function hasChanges(baselineReport, report) {
180+
if (!report) return false
181+
182+
return baselineReport.status !== report.status ||
183+
baselineReport.passingRequest !== report.passingRequest ||
184+
baselineReport.passingResponse !== report.passingResponse
185+
}
186+
187+
function buildDiffTableLine(change) {
188+
const { api, baseline, current } = change
189+
190+
const status = generateStatus(current.status)
191+
const request = generateRequest(current)
192+
const response = generateResponse(current)
193+
194+
const baselineStatus = generateStatus(baseline.status)
195+
const baselineRequest = generateRequest(baseline)
196+
const baselineResponse = generateResponse(baseline)
197+
198+
const statusDiff = status !== baselineStatus ? `${baselineStatus}${status}` : status
199+
const requestDiff = request !== baselineRequest ? `${baselineRequest}${request}` : request
200+
const responseDiff = response !== baselineResponse ? `${baselineResponse}${response}` : response
201+
202+
return `| ${tick}${api}${tick} | ${statusDiff} | ${requestDiff} | ${responseDiff} |\n`
157203
}
158204

159-
function generateStatus (report) {
160-
if (!report.diagnostics.hasRequestType || !report.diagnostics.hasResponseType) {
205+
206+
function generateStatus (status) {
207+
if (status === 'missing_types' || status === 'missing_request_type' || status === 'missing_response_type') {
161208
return ':orange_circle:'
162209
}
163-
if (report.totalRequest <= 0 || report.totalResponse <= 0) {
210+
if (status === 'missing_test') {
164211
return ':white_circle:'
165212
}
166-
if (report.diagnostics.request.length === 0 && report.diagnostics.response.length === 0) {
213+
if (status === 'passing') {
167214
return ':green_circle:'
168215
}
169216
return ':red_circle:'
170217
}
171218

172219
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'
220+
if (r.status === 'missing_test') return 'Missing test'
221+
if (r.status === 'missing_types' || r.status == 'missing_request_type') return 'Missing type'
176222
return `${r.passingRequest}/${r.totalRequest}`
177223
}
178224

179225
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'
226+
if (r.status === 'missing_test') return 'Missing test'
227+
if (r.status === 'missing_types' || r.status == 'missing_response_type') return 'Missing type'
183228
return `${r.passingResponse}/${r.totalResponse}`
184229
}
185230

186231
run().catch((err) => {
187232
core.error(err)
188233
process.exit(1)
189-
})
234+
})

.github/workflows/validate-pr.yml

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -61,18 +61,26 @@ jobs:
6161
fi
6262
node scripts/upload-recording/download.js --branch $branch --git
6363
node scripts/clone-elasticsearch/index.js --branch $branch
64-
env:
65-
GCS_CREDENTIALS: ${{ secrets.GCS_CREDENTIALS }}
66-
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 }}
7564
7665
- name: Run validation
66+
id: validation
7767
working-directory: ./elasticsearch-specification
7868
run: node .github/validate-pr --token ${{ secrets.GITHUB_TOKEN }}
69+
70+
- name: Find existing comment
71+
uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3.1.0
72+
id: find-comment
73+
with:
74+
issue-number: ${{ github.event.pull_request.number }}
75+
comment-author: 'github-actions[bot]'
76+
body-includes: 'Following you can find the validation changes'
77+
78+
- name: Create or update comment
79+
if: steps.validation.outputs.has_results == 'true'
80+
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0
81+
with:
82+
token: ${{ secrets.GITHUB_TOKEN }}
83+
comment-id: ${{ steps.find-comment.outputs.comment-id }}
84+
issue-number: ${{ github.event.pull_request.number }}
85+
body: ${{ steps.validation.outputs.comment_body }}
86+
edit-mode: replace

.gitignore

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,6 @@ 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-openapi-docs*.json
70+
output/openapi/elasticsearch*.redirects.csv

CONTRIBUTING.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ Documents under the docs/ directory will help you to change the specification. T
3939
* [Fixing a definition, the complete story](docs/validation-example.md)
4040

4141

42-
## Send your pull request
42+
## Send your pull request from a branch
4343

4444
To fix any style issues and generate the outputs (that we store in git), please run:
4545

@@ -56,5 +56,4 @@ Then, follow those steps:
5656
- If the API is unusable without the change -> every supported version
5757
- If the API is usable, but fix is on the response side -> every supported version
5858
- If the API is usable, but fix is on the request side -> no backport, unless the API is _partially_ usable and the fix unlocks a missing feature that has no workaround
59-
60-
59+
- Send the pull request from a branch, **not a fork**

Makefile

Lines changed: 18 additions & 7 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,16 +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-openapi.json" "docs/overlays/elasticsearch-openapi-overlays.yaml" > "output/openapi/elasticsearch-openapi.tmp1.json"
66-
@npx bump overlay "output/openapi/elasticsearch-openapi.tmp1.json" "docs/overlays/elasticsearch-shared-overlays.yaml" > "output/openapi/elasticsearch-openapi.tmp2.json"
67-
@npx @redocly/cli bundle output/openapi/elasticsearch-openapi.tmp2.json --ext json -o output/openapi/elasticsearch-openapi.examples.json
68-
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
6980

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

73-
lint-docs-stateful: ## Lint only the elasticsearch-openapi.examples.json file
74-
@npx @redocly/cli lint "output/openapi/elasticsearch-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
7586

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

README.md

Lines changed: 11 additions & 1 deletion
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
@@ -227,6 +234,9 @@ make validate api=xpack.info type=request branch=main
227234

228235
# this will validate the xpack.info request and response types against the 8.15 branch
229236
make validate api=xpack.info branch=8.15
237+
238+
# this will validate the xpack.info and search request and response types against the 8.15 branch
239+
make validate api=xpack.info,search branch=8.15
230240
```
231241

232242
The last command above will install all the dependencies and run, download
@@ -235,7 +245,7 @@ If you need to download the recordings again, run `make validate-no-cache api=xp
235245

236246
Once you see the errors, you can fix the original definition in `/specification`
237247
and then run the command again until the types validator does not trigger any new error.
238-
Finally open a pull request with your changes.
248+
Finally open a pull request with your changes. Please open it from a branch in the repository, and not from a fork.
239249

240250
## Documentation
241251

0 commit comments

Comments
 (0)