Skip to content

Commit 91f261c

Browse files
authored
fix(es): clarify bulk ingest source format is decorative (#521)
* docs(es): clarify bulk ingest source format is decorative * test(es): pin bulk ingest source format behavior * ci: add ecr fallback for trivy db download * ci: fix mirror.gcr.io trivy db namespace
1 parent 9f3f43c commit 91f261c

4 files changed

Lines changed: 70 additions & 2 deletions

File tree

.mega-linter.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,14 @@ YAML_YAMLLINT_FILTER_REGEX_EXCLUDE: "(codegen/functional/test/fixtures/|node_mod
3939

4040
COPYPASTE_JSCPD_CONFIG_FILE: .jscpd.json
4141

42+
# trivy's default DB registries (mirror.gcr.io, ghcr.io) have been hitting
43+
# TOOMANYREQUESTS during vulnerability DB download. Add the AWS ECR Public
44+
# mirror as a third fallback (setting this overrides the defaults, so they're
45+
# listed explicitly too) since it draws from a separate rate-limit pool.
46+
# Note: mirror.gcr.io proxies Docker Hub, whose org for this image is
47+
# "aquasec" (not "aquasecurity" like GHCR/ECR); using the wrong namespace
48+
# here causes a permanent MANIFEST_UNKNOWN, not a transient rate limit.
49+
TRIVY_DB_REPOSITORY: "mirror.gcr.io/aquasec/trivy-db:2,ghcr.io/aquasecurity/trivy-db:2,public.ecr.aws/aquasecurity/trivy-db:2"
50+
4251
# shellcheck: warn only until active script work stabilises
4352
BASH_SHELLCHECK_DISABLE_ERRORS: true

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/es/helpers/bulk-ingest.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ const inputSchema: Record<string, unknown> = {
3636
data_dir: { type: 'string', description: 'Path to directory of data files to ingest' },
3737
glob: { type: 'string', description: 'Glob pattern for --data-dir file matching (default: **/*.json, or **/*.csv when --source-format csv)' },
3838
no_recursive: { type: 'boolean', description: 'Do not recurse into subdirectories when using --data-dir' },
39-
source_format: { type: 'string', enum: SOURCE_FORMATS, description: 'Input file format: ndjson, json, or csv', default: 'ndjson' },
39+
source_format: { type: 'string', enum: SOURCE_FORMATS, description: 'Input file format: csv, or json-based (ndjson vs json array is auto-detected from content; "ndjson" and "json" behave identically here)', default: 'ndjson' },
4040
csv_delimiter: { type: 'string', description: 'CSV column delimiter (default: ",")' },
4141
csv_columns: { type: 'string', description: 'Comma-separated list of column names (overrides CSV header row)' },
4242
skip_header: { type: 'boolean', description: 'Skip the first row of a CSV file' },
@@ -338,6 +338,11 @@ async function streamBulkIngest (
338338
} else {
339339
// ndjson: line-by-line. json (JSON array): streamed element-by-element via
340340
// JsonArraySplitter, so a multi-GB array never gets buffered whole.
341+
// `source_format` only distinguishes csv from everything else (see the
342+
// `if` above); "ndjson" and "json" are treated identically here, and
343+
// array-vs-line format is auto-detected from the first non-empty line
344+
// rather than trusted from the flag, since either format may legitimately
345+
// start a file either way regardless of which of the two was passed.
341346
const rl = createInterface({ input: stream, crlfDelay: Infinity })
342347
let isJsonArray: boolean | null = null // null = not yet determined
343348
const arraySplitter = new JsonArraySplitter()

test/es/helpers/bulk-ingest.test.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,60 @@ describe('bulk-ingest command', () => {
322322
assert.ok(body.includes('"a"') && body.includes('"b"') && body.includes('"c"'))
323323
})
324324

325+
it('splits a JSON array into separate documents regardless of --source-format (ndjson vs json is decorative)', async () => {
326+
const docs = [{ title: 'doc1' }, { title: 'doc2' }]
327+
328+
for (const sourceFormat of ['ndjson', 'json'] as const) {
329+
const tmpDir = mkdtempSync(join(tmpdir(), 'bulk-test-'))
330+
writeFileSync(join(tmpDir, 'data.json'), JSON.stringify(docs))
331+
332+
const { transport, requests } = mockTransport([successResponse(2)])
333+
334+
await runCommand([
335+
'--index', 'test-idx',
336+
'--data-file', join(tmpDir, 'data.json'),
337+
'--source-format', sourceFormat,
338+
'--json'
339+
], makeDeps(transport))
340+
341+
assert.equal(requests.length, 1)
342+
const body = requests[0]!.params.body as string
343+
const docLines = body.trim().split('\n').filter((_, i) => i % 2 === 1)
344+
assert.deepStrictEqual(
345+
docLines.map((l) => JSON.parse(l)),
346+
docs,
347+
`expected two separate documents with --source-format ${sourceFormat}, not the array as one doc`
348+
)
349+
}
350+
})
351+
352+
it('parses an NDJSON file the same way whether --source-format is ndjson or json', async () => {
353+
const docs = [{ title: 'doc1' }, { title: 'doc2' }]
354+
355+
for (const sourceFormat of ['ndjson', 'json'] as const) {
356+
const tmpDir = mkdtempSync(join(tmpdir(), 'bulk-test-'))
357+
writeFileSync(join(tmpDir, 'data.ndjson'), docs.map((d) => JSON.stringify(d)).join('\n') + '\n')
358+
359+
const { transport, requests } = mockTransport([successResponse(2)])
360+
361+
await runCommand([
362+
'--index', 'test-idx',
363+
'--data-file', join(tmpDir, 'data.ndjson'),
364+
'--source-format', sourceFormat,
365+
'--json'
366+
], makeDeps(transport))
367+
368+
assert.equal(requests.length, 1)
369+
const body = requests[0]!.params.body as string
370+
const docLines = body.trim().split('\n').filter((_, i) => i % 2 === 1)
371+
assert.deepStrictEqual(
372+
docLines.map((l) => JSON.parse(l)),
373+
docs,
374+
`expected NDJSON to parse the same way with --source-format ${sourceFormat}`
375+
)
376+
}
377+
})
378+
325379
it('correctly splits JSON array elements containing commas and brackets inside strings', async () => {
326380
const tmpDir = mkdtempSync(join(tmpdir(), 'bulk-test-'))
327381
const docs = [

0 commit comments

Comments
 (0)