Skip to content

Commit adabe6d

Browse files
authored
Merge pull request #23951 from github/repo-sync
repo sync
2 parents ca37c05 + 909b405 commit adabe6d

File tree

7 files changed

+105
-81
lines changed

7 files changed

+105
-81
lines changed

lib/schema-event.js

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -401,29 +401,6 @@ const experimentSchema = {
401401
},
402402
}
403403

404-
const redirectSchema = {
405-
type: 'object',
406-
additionalProperties: false,
407-
required: ['type', 'context', 'redirect_from', 'redirect_to'],
408-
properties: {
409-
context,
410-
type: {
411-
type: 'string',
412-
pattern: '^redirect$',
413-
},
414-
redirect_from: {
415-
type: 'string',
416-
description: 'The requested href.',
417-
format: 'uri-reference',
418-
},
419-
redirect_to: {
420-
type: 'string',
421-
description: 'The destination href of the redirect.',
422-
format: 'uri-reference',
423-
},
424-
},
425-
}
426-
427404
const clipboardSchema = {
428405
type: 'object',
429406
additionalProperties: false,
@@ -498,7 +475,6 @@ export const eventSchema = {
498475
navigateSchema,
499476
surveySchema,
500477
experimentSchema,
501-
redirectSchema,
502478
clipboardSchema,
503479
printSchema,
504480
preferenceSchema,
@@ -514,7 +490,6 @@ export const hydroNames = {
514490
navigate: 'docs.v0.NavigateEvent',
515491
survey: 'docs.v0.SurveyEvent',
516492
experiment: 'docs.v0.ExperimentEvent',
517-
redirect: 'docs.v0.RedirectEvent',
518493
clipboard: 'docs.v0.ClipboardEvent',
519494
print: 'docs.v0.PrintEvent',
520495
preference: 'docs.v0.PreferenceEvent',

middleware/api/es-search.js

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,19 @@ export async function getSearchResults({
4141
includeTopics,
4242
usePrefixSearch,
4343
highlights,
44+
include,
4445
}) {
4546
if (topics && !Array.isArray(topics)) {
4647
throw new Error("'topics' has to be an array")
4748
}
49+
if (include) {
50+
if (!Array.isArray(include)) {
51+
throw new Error("'include' has to be an array")
52+
}
53+
if (!include.every((value) => typeof value === 'string')) {
54+
throw new Error("Every entry in the 'include' must be a string")
55+
}
56+
}
4857
const t0 = new Date()
4958
const client = getClient()
5059
const from = size * (page - 1)
@@ -146,7 +155,13 @@ export async function getSearchResults({
146155

147156
// const hitsAll = result.hits // ES >7.11
148157
const hitsAll = result.body // ES <=7.11
149-
const hits = getHits(hitsAll.hits.hits, { indexName, debug, includeTopics, highlightFields })
158+
const hits = getHits(hitsAll.hits.hits, {
159+
indexName,
160+
debug,
161+
includeTopics,
162+
highlightFields,
163+
include,
164+
})
150165
const t1 = new Date()
151166

152167
const meta = {
@@ -350,7 +365,7 @@ function getMatchQueries(query, { usePrefixSearch, fuzzy }) {
350365
return matchQueries
351366
}
352367

353-
function getHits(hits, { indexName, debug, includeTopics, highlightFields }) {
368+
function getHits(hits, { indexName, debug, includeTopics, highlightFields, include }) {
354369
return hits.map((hit) => {
355370
// Return `hit.highlights[...]` based on the highlight fields requested.
356371
// So if you searched with `&highlights=headings&highlights=content`
@@ -381,7 +396,9 @@ function getHits(hits, { indexName, debug, includeTopics, highlightFields }) {
381396
result.es_url = `http://localhost:9200/${indexName}/_doc/${hit._id}`
382397
}
383398
}
384-
399+
for (const field of include || []) {
400+
result[field] = hit._source[field]
401+
}
385402
return result
386403
})
387404
}

middleware/api/search.js

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ const POSSIBLE_SORTS = ['best', 'relevance']
2727
const DEFAULT_SORT = POSSIBLE_SORTS[0]
2828
const MAX_PAGE = 10
2929

30+
// There are some fields you can optionally include in the output.
31+
// These are fields available in Elasticsearch that we don't include in
32+
// the output by default. E.g. `...&include=intro`
33+
// Requesting anything that is not in this list will result in
34+
// a 400 Bad Request.
35+
const V1_ADDITIONAL_INCLUDES = ['intro', 'headings']
36+
3037
// If someone searches for `...&version=3.5` what they actually mean
3138
// is `ghes-3.5`. This is because of legacy formatting with the old search.
3239
// In some distant future we can clean up any client enough that this
@@ -204,6 +211,16 @@ const validationMiddleware = (req, res, next) => {
204211
},
205212
{ key: 'autocomplete', default_: false, cast: toBoolean },
206213
{ key: 'debug', default_: process.env.NODE_ENV === 'development', cast: toBoolean },
214+
{
215+
key: 'include',
216+
default_: [],
217+
cast: toArray,
218+
// Note: At the time of writing this general validator middleware
219+
// doesn't yet know it's being used by the v1 version.
220+
// But we don't have any other versions yet so no need to
221+
// over-engineer this more.
222+
validate: (values) => values.every((value) => V1_ADDITIONAL_INCLUDES.includes(value)),
223+
},
207224
]
208225

209226
const search = {}
@@ -247,12 +264,26 @@ function toBoolean(value) {
247264
return false
248265
}
249266

267+
function toArray(value) {
268+
return Array.isArray(value) ? value : [value]
269+
}
270+
250271
router.get(
251272
'/v1',
252273
validationMiddleware,
253274
catchMiddlewareError(async function search(req, res) {
254-
const { indexName, language, query, autocomplete, page, size, debug, sort, highlights } =
255-
req.search
275+
const {
276+
indexName,
277+
language,
278+
query,
279+
autocomplete,
280+
page,
281+
size,
282+
debug,
283+
sort,
284+
highlights,
285+
include,
286+
} = req.search
256287

257288
// The getSearchResults() function is a mix of preparing the search,
258289
// sending & receiving it, and post-processing the response from the
@@ -272,6 +303,7 @@ router.get(
272303
sort,
273304
highlights,
274305
usePrefixSearch: autocomplete,
306+
include,
275307
}
276308
try {
277309
const { meta, hits } = await timed(options)

middleware/index.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import {
1616
setLanguageFastlySurrogateKey,
1717
} from './set-fastly-surrogate-key.js'
1818
import reqUtils from './req-utils.js'
19-
import recordRedirect from './record-redirect.js'
2019
import handleErrors from './handle-errors.js'
2120
import handleInvalidPaths from './handle-invalid-paths.js'
2221
import handleNextDataPath from './handle-next-data-path.js'
@@ -210,8 +209,7 @@ export default function (app) {
210209
app.set('etag', false) // We will manage our own ETags if desired
211210

212211
// *** Config and context for redirects ***
213-
app.use(reqUtils) // Must come before record-redirect and events
214-
app.use(recordRedirect)
212+
app.use(reqUtils) // Must come before events
215213
app.use(instrument(detectLanguage, './detect-language')) // Must come before context, breadcrumbs, find-page, handle-errors, homepages
216214
app.use(asyncMiddleware(instrument(context, './context'))) // Must come before early-access-*, handle-redirects
217215
app.use(instrument(shortVersions, './contextualizers/short-versions')) // Support version shorthands

middleware/record-redirect.js

Lines changed: 0 additions & 31 deletions
This file was deleted.

tests/content/api-search.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,3 +285,53 @@ describeIfElasticsearchURL('search legacy middleware', () => {
285285
expect(results.length).toBe(0)
286286
})
287287
})
288+
289+
describeIfElasticsearchURL("additional fields with 'include'", () => {
290+
jest.setTimeout(60 * 1000)
291+
292+
test("'intro' and 'headings' are omitted by default", async () => {
293+
const sp = new URLSearchParams()
294+
sp.set('query', 'foo')
295+
const res = await get('/api/search/v1?' + sp)
296+
expect(res.statusCode).toBe(200)
297+
const results = JSON.parse(res.text)
298+
const firstKeys = Object.keys(results.hits[0])
299+
expect(firstKeys.includes('intro')).toBeFalsy()
300+
expect(firstKeys.includes('headings')).toBeFalsy()
301+
})
302+
303+
test("additionally include 'intro'", async () => {
304+
const sp = new URLSearchParams()
305+
sp.set('query', 'foo')
306+
sp.set('include', 'intro')
307+
const res = await get('/api/search/v1?' + sp)
308+
expect(res.statusCode).toBe(200)
309+
const results = JSON.parse(res.text)
310+
const firstKeys = Object.keys(results.hits[0])
311+
expect(firstKeys.includes('intro')).toBeTruthy()
312+
expect(firstKeys.includes('headings')).toBeFalsy()
313+
})
314+
315+
test("additionally include 'intro' and 'headings'", async () => {
316+
const sp = new URLSearchParams()
317+
sp.set('query', 'foo')
318+
sp.append('include', 'intro')
319+
sp.append('include', 'headings')
320+
const res = await get('/api/search/v1?' + sp)
321+
expect(res.statusCode).toBe(200)
322+
const results = JSON.parse(res.text)
323+
const firstKeys = Object.keys(results.hits[0])
324+
expect(firstKeys.includes('intro')).toBeTruthy()
325+
expect(firstKeys.includes('headings')).toBeTruthy()
326+
})
327+
328+
test("unknown 'include' is 400 bad request", async () => {
329+
const sp = new URLSearchParams()
330+
sp.set('query', 'foo')
331+
sp.set('include', 'xxxxx')
332+
const res = await get('/api/search/v1?' + sp)
333+
expect(res.statusCode).toBe(400)
334+
const results = JSON.parse(res.text)
335+
expect(results.error).toMatch(`Not a valid value (["xxxxx"]) for key 'include'`)
336+
})
337+
})

tests/routing/events.js

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -416,23 +416,6 @@ describe('POST /events', () => {
416416
checkEvent({ ...experimentExample, experiment_success: undefined }, 200))
417417
})
418418

419-
describe('redirect', () => {
420-
const redirectExample = {
421-
...baseExample,
422-
type: 'redirect',
423-
redirect_from: 'http://example.com/a',
424-
redirect_to: 'http://example.com/b',
425-
}
426-
427-
it('should record an redirect event', () => checkEvent(redirectExample, 200))
428-
429-
it('redirect_from is required url', () =>
430-
checkEvent({ ...redirectExample, redirect_from: ' ' }, 400))
431-
432-
it('redirect_to is required url', () =>
433-
checkEvent({ ...redirectExample, redirect_to: undefined }, 400))
434-
})
435-
436419
describe('clipboard', () => {
437420
const clipboardExample = {
438421
...baseExample,

0 commit comments

Comments
 (0)