mirror of
https://github.com/nowcommunity/nowdatabase.git
synced 2026-10-12 19:13:52 +03:00
Address filtered export review comments
This commit is contained in:
@@ -57,6 +57,39 @@ describe('DwC-A locality export (admin-only)', () => {
|
|||||||
expect(measurementCsv).toContain('"verbatimMeasurementType"')
|
expect(measurementCsv).toContain('"verbatimMeasurementType"')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('returns a filtered ZIP archive for POST requests', async () => {
|
||||||
|
const loginResult = await send<{ token: string }>('user/login', 'POST', { username: 'testSu', password: 'test' })
|
||||||
|
expect(loginResult.status).toEqual(200)
|
||||||
|
|
||||||
|
const result = await request(app)
|
||||||
|
.post('/locality/export/dwc-archive')
|
||||||
|
.set('authorization', `bearer ${loginResult.body.token}`)
|
||||||
|
.send({ ids: [21050] })
|
||||||
|
.buffer(true)
|
||||||
|
.parse(parseBinary)
|
||||||
|
|
||||||
|
expect(result.status).toEqual(200)
|
||||||
|
expect(result.headers['content-type']).toMatch(/application\/zip/i)
|
||||||
|
|
||||||
|
const zip = await JSZip.loadAsync(result.body as unknown as Buffer)
|
||||||
|
const locationCsv = await zip.file('location.csv')!.async('string')
|
||||||
|
expect(locationCsv).toContain('NOW:LOC:21050')
|
||||||
|
expect(locationCsv).not.toContain('NOW:LOC:24750')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns 400 for invalid POST id payloads', async () => {
|
||||||
|
const loginResult = await send<{ token: string }>('user/login', 'POST', { username: 'testSu', password: 'test' })
|
||||||
|
expect(loginResult.status).toEqual(200)
|
||||||
|
|
||||||
|
const result = await request(app)
|
||||||
|
.post('/locality/export/dwc-archive')
|
||||||
|
.set('authorization', `bearer ${loginResult.body.token}`)
|
||||||
|
.send({ ids: ['21050.5'] })
|
||||||
|
|
||||||
|
expect(result.status).toEqual(400)
|
||||||
|
expect(result.body).toEqual({ error: 'ids must contain only integers.' })
|
||||||
|
})
|
||||||
|
|
||||||
it('rejects non-admin requests', async () => {
|
it('rejects non-admin requests', async () => {
|
||||||
const result = await request(app).get('/locality/export/dwc-archive')
|
const result = await request(app).get('/locality/export/dwc-archive')
|
||||||
expect(result.status).toEqual(403)
|
expect(result.status).toEqual(403)
|
||||||
|
|||||||
@@ -66,6 +66,39 @@ describe('DwC-A species export (admin-only)', () => {
|
|||||||
expect(metaXml).toContain('<extension')
|
expect(metaXml).toContain('<extension')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('returns a filtered ZIP archive for POST requests', async () => {
|
||||||
|
const loginResult = await send<{ token: string }>('user/login', 'POST', { username: 'testSu', password: 'test' })
|
||||||
|
expect(loginResult.status).toEqual(200)
|
||||||
|
|
||||||
|
const result = await request(app)
|
||||||
|
.post('/species/export/dwc-archive')
|
||||||
|
.set('authorization', `bearer ${loginResult.body.token}`)
|
||||||
|
.send({ ids: [85729] })
|
||||||
|
.buffer(true)
|
||||||
|
.parse(parseBinary)
|
||||||
|
|
||||||
|
expect(result.status).toEqual(200)
|
||||||
|
expect(result.headers['content-type']).toMatch(/application\/zip/i)
|
||||||
|
|
||||||
|
const zip = await JSZip.loadAsync(result.body as unknown as Buffer)
|
||||||
|
const taxonCsv = await zip.file('taxon.csv')!.async('string')
|
||||||
|
expect(taxonCsv).toContain('NOW:85729')
|
||||||
|
expect(taxonCsv).not.toContain('NOW:85730')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns 400 for invalid POST id payloads', async () => {
|
||||||
|
const loginResult = await send<{ token: string }>('user/login', 'POST', { username: 'testSu', password: 'test' })
|
||||||
|
expect(loginResult.status).toEqual(200)
|
||||||
|
|
||||||
|
const result = await request(app)
|
||||||
|
.post('/species/export/dwc-archive')
|
||||||
|
.set('authorization', `bearer ${loginResult.body.token}`)
|
||||||
|
.send({ ids: ['85729abc'] })
|
||||||
|
|
||||||
|
expect(result.status).toEqual(400)
|
||||||
|
expect(result.body).toEqual({ error: 'ids must contain only integers.' })
|
||||||
|
})
|
||||||
|
|
||||||
it('rejects non-admin requests', async () => {
|
it('rejects non-admin requests', async () => {
|
||||||
const result = await request(app).get('/species/export/dwc-archive')
|
const result = await request(app).get('/species/export/dwc-archive')
|
||||||
expect(result.status).toEqual(403)
|
expect(result.status).toEqual(403)
|
||||||
|
|||||||
@@ -34,7 +34,9 @@ router.get('/export/dwc-archive', requireOneOf([Role.Admin]), async (_req, res)
|
|||||||
|
|
||||||
router.post('/export/dwc-archive', requireOneOf([Role.Admin]), async (req, res) => {
|
router.post('/export/dwc-archive', requireOneOf([Role.Admin]), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
return await sendDwcArchive(parseNumericIds((req.body as { ids?: unknown }).ids), res)
|
const body = req.body as { ids?: unknown } | undefined
|
||||||
|
if (!body || !('ids' in body)) throw new Error('ids must be an array.')
|
||||||
|
return await sendDwcArchive(parseNumericIds(body.ids), res)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return res.status(400).send({ error: error instanceof Error ? error.message : 'Invalid export filters.' })
|
return res.status(400).send({ error: error instanceof Error ? error.message : 'Invalid export filters.' })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,9 @@ router.get('/export/dwc-archive', requireOneOf([Role.Admin]), async (_req, res)
|
|||||||
|
|
||||||
router.post('/export/dwc-archive', requireOneOf([Role.Admin]), async (req, res) => {
|
router.post('/export/dwc-archive', requireOneOf([Role.Admin]), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
return await sendDwcArchive(parseNumericIds((req.body as { ids?: unknown }).ids), res)
|
const body = req.body as { ids?: unknown } | undefined
|
||||||
|
if (!body || !('ids' in body)) throw new Error('ids must be an array.')
|
||||||
|
return await sendDwcArchive(parseNumericIds(body.ids), res)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return res.status(400).send({ error: error instanceof Error ? error.message : 'Invalid export filters.' })
|
return res.status(400).send({ error: error instanceof Error ? error.message : 'Invalid export filters.' })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -956,11 +956,18 @@ const fetchOccurrencesForDwcDataPackageExport = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const buildDwcDataPackageZipBuffer = async (occurrenceKeys?: DwcOccurrenceKey[]): Promise<Buffer> => {
|
export const buildDwcDataPackageZipBuffer = async (occurrenceKeys?: DwcOccurrenceKey[]): Promise<Buffer> => {
|
||||||
const { nowDb } = await import('../utils/db')
|
|
||||||
const occurrences = await fetchOccurrencesForDwcDataPackageExport(occurrenceKeys)
|
const occurrences = await fetchOccurrencesForDwcDataPackageExport(occurrenceKeys)
|
||||||
const localityIds = occurrenceKeys ? [...new Set(occurrences.map(occurrence => occurrence.lid))] : undefined
|
return await buildDwcDataPackageZipBufferFromOccurrences(occurrences, Boolean(occurrenceKeys))
|
||||||
|
}
|
||||||
|
|
||||||
|
const buildDwcDataPackageZipBufferFromOccurrences = async (
|
||||||
|
occurrences: OccurrenceForDwcDpExport[],
|
||||||
|
isFilteredExport: boolean
|
||||||
|
): Promise<Buffer> => {
|
||||||
|
const { nowDb } = await import('../utils/db')
|
||||||
|
const localityIds = isFilteredExport ? [...new Set(occurrences.map(occurrence => occurrence.lid))] : undefined
|
||||||
const localities = (await nowDb.now_loc.findMany({
|
const localities = (await nowDb.now_loc.findMany({
|
||||||
where: localityIds ? { lid: { in: localityIds } } : undefined,
|
where: isFilteredExport ? { lid: { in: localityIds } } : undefined,
|
||||||
orderBy: { lid: 'asc' },
|
orderBy: { lid: 'asc' },
|
||||||
select: localitySelect,
|
select: localitySelect,
|
||||||
})) as unknown as LocalityForDwcDpExport[]
|
})) as unknown as LocalityForDwcDpExport[]
|
||||||
@@ -1129,13 +1136,9 @@ export const buildFullDarwinCoreExportZipBufferFromArchives = async ({
|
|||||||
|
|
||||||
export const buildFullDarwinCoreExportZipBuffer = async (occurrenceKeys?: DwcOccurrenceKey[]): Promise<Buffer> => {
|
export const buildFullDarwinCoreExportZipBuffer = async (occurrenceKeys?: DwcOccurrenceKey[]): Promise<Buffer> => {
|
||||||
const occurrences = occurrenceKeys ? await fetchOccurrencesForDwcDataPackageExport(occurrenceKeys) : undefined
|
const occurrences = occurrenceKeys ? await fetchOccurrencesForDwcDataPackageExport(occurrenceKeys) : undefined
|
||||||
const filteredOccurrenceKeys = occurrences?.map(occurrence => ({
|
|
||||||
lid: occurrence.lid,
|
|
||||||
speciesId: occurrence.species_id,
|
|
||||||
}))
|
|
||||||
const speciesIds = occurrences ? [...new Set(occurrences.map(occurrence => occurrence.species_id))] : undefined
|
const speciesIds = occurrences ? [...new Set(occurrences.map(occurrence => occurrence.species_id))] : undefined
|
||||||
const [dwcDataPackageZipBuffer, dwcTaxonArchiveZipBuffer] = await Promise.all([
|
const [dwcDataPackageZipBuffer, dwcTaxonArchiveZipBuffer] = await Promise.all([
|
||||||
buildDwcDataPackageZipBuffer(filteredOccurrenceKeys),
|
occurrences ? buildDwcDataPackageZipBufferFromOccurrences(occurrences, true) : buildDwcDataPackageZipBuffer(),
|
||||||
buildDwcArchiveZipBuffer(speciesIds),
|
buildDwcArchiveZipBuffer(speciesIds),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user