Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions bin/rerum_v1.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ dotenv.config()
const port = process.env.PORT ?? 3001
app.set('port', port)


Comment thread
Mehulantony marked this conversation as resolved.
Outdated

/**
* Create HTTP server.
*/
Expand Down Expand Up @@ -74,3 +76,4 @@ function onListening() {
: 'port ' + addr.port
debug('Listening on ' + bind)
}

104 changes: 54 additions & 50 deletions controllers/bulk.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,43 +10,63 @@ import { newID, isValidID, db } from '../database/index.js'
import utils from '../utils.js'
import { _contextid, ObjectID, createExpressError, getAgentClaim, parseDocumentID, idNegotiation } from './utils.js'

/**
* Create many objects at once with the power of MongoDB bulkWrite() operations.
*
* @see https://www.mongodb.com/docs/manual/reference/method/db.collection.bulkWrite/
*/
const bulkCreate = async function (req, res, next) {
//Helper function to standardize JSON responses
function setJsonHeaders(res) {
res.set("Content-Type", "application/json; charset=utf-8")
}

//Helper function for express errors
function fail(next, message, status = 400) {
const err = { message, status }
next(createExpressError(err))
}
Comment thread
joeljoby02 marked this conversation as resolved.
Outdated

//function to validate request body is a non-empty array
function requireNonEmptyArrayBody(req, next) {
const documents = req.body
let err = {}
if (!Array.isArray(documents)) {
err.message = "The request body must be an array of objects."
err.status = 400
next(createExpressError(err))
return
fail(next, "The request body must be an array of objects.", 400)
return null
}
if (documents.length === 0) {
err.message = "No action on an empty array."
err.status = 400
next(createExpressError(err))
return
fail(next, "No action on an empty array.", 400)
return null
}
return documents
}

//check if an item is valid JSON object (not array)
function isValidJsonObject(d) {
if (d == null || Array.isArray(d) || typeof d !== "object") return false
try {
JSON.parse(JSON.stringify(d))
} catch (err) {
return false
}
return true
}

/**
* Create many objects at once with the power of MongoDB bulkWrite() operations.
*
* @see https://www.mongodb.com/docs/manual/reference/method/db.collection.bulkWrite/
*/
const bulkCreate = async function (req, res, next) {
setJsonHeaders(res)

const documents = requireNonEmptyArrayBody(req, next)
if (!documents) return

const gatekeep = documents.filter(d=> {
// Each item must be valid JSON, but can't be an array.
if(Array.isArray(d) || typeof d !== "object") return d
try {
JSON.parse(JSON.stringify(d))
} catch (err) {
return d
}
if (!isValidJsonObject(d)) return d

// Items must not have an @id, and in some cases same for id.
const idcheck = _contextid(d["@context"]) ? (d.id ?? d["@id"]) : d["@id"]
if(idcheck) return d
})
if (gatekeep.length > 0) {
err.message = "All objects in the body of a `/bulkCreate` must be JSON and must not contain a declared identifier property."
err.status = 400
next(createExpressError(err))
fail(next, "All objects in the body of a `/bulkCreate` must be JSON and must not contain a declared identifier property.", 400)
Comment thread
allenbakki marked this conversation as resolved.
Outdated
return
}

Expand Down Expand Up @@ -80,7 +100,7 @@ const bulkCreate = async function (req, res, next) {
}
try {
let dbResponse = await db.bulkWrite(bulkOps, {'ordered':false})
res.set("Content-Type", "application/json; charset=utf-8")
setJsonHeaders(res)
res.set("Link",dbResponse.result.insertedIds.map(r => `${process.env.RERUM_ID_PREFIX}${r._id}`)) // https://www.rfc-editor.org/rfc/rfc5988
res.status(201)
const estimatedResults = bulkOps.map(f=>{
Expand All @@ -104,39 +124,23 @@ const bulkCreate = async function (req, res, next) {
* @see https://www.mongodb.com/docs/manual/reference/method/db.collection.bulkWrite/
*/
const bulkUpdate = async function (req, res, next) {
res.set("Content-Type", "application/json; charset=utf-8")
const documents = req.body
let err = {}
setJsonHeaders(res)

const documents = requireNonEmptyArrayBody(req, next)
if (!documents) return

let encountered = []
if (!Array.isArray(documents)) {
err.message = "The request body must be an array of objects."
err.status = 400
next(createExpressError(err))
return
}
if (documents.length === 0) {
err.message = "No action on an empty array."
err.status = 400
next(createExpressError(err))
return
}

const gatekeep = documents.filter(d => {
// Each item must be valid JSON, but can't be an array.
if(Array.isArray(d) || typeof d !== "object") return d
try {
JSON.parse(JSON.stringify(d))
} catch (err) {
return d
}
if (!isValidJsonObject(d)) return d
// Items must have an @id, or in some cases an id will do
const idcheck = _contextid(d["@context"]) ? (d.id ?? d["@id"]) : d["@id"]
if(!idcheck) return d
})
// The empty {}s will cause this error
if (gatekeep.length > 0) {
err.message = "All objects in the body of a `/bulkUpdate` must be JSON and must contain a declared identifier property."
err.status = 400
next(createExpressError(err))
fail(next, "All objects in the body of a `/bulkUpdate` must be JSON and must contain a declared identifier property.", 400)
return
}
// unordered bulkWrite() operations have better performance metrics.
Expand Down Expand Up @@ -184,7 +188,7 @@ const bulkUpdate = async function (req, res, next) {
}
try {
let dbResponse = await db.bulkWrite(bulkOps, {'ordered':false})
res.set("Content-Type", "application/json; charset=utf-8")
setJsonHeaders(res)
res.set("Link", dbResponse.result.insertedIds.map(r => `${process.env.RERUM_ID_PREFIX}${r._id}`)) // https://www.rfc-editor.org/rfc/rfc5988
res.status(200)
const estimatedResults = bulkOps.filter(f=>f.insertOne).map(f=>{
Expand Down
1 change: 0 additions & 1 deletion package-lock.json
Comment thread
Mehulantony marked this conversation as resolved.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions routes/api-routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ import sinceRouter from './since.js';
import historyRouter from './history.js';

router.use(staticRouter)
router.use('/id',idRouter)
router.use('/api', compatabilityRouter)
router.use('/api/query', queryRouter)
router.use('/id',idRouter)//skip
Comment thread
Mehulantony marked this conversation as resolved.
Outdated
router.use('/api', compatabilityRouter)//skip
router.use('/api/query', queryRouter)//skip
router.use('/api/search', searchRouter)
router.use('/api/create', createRouter)
router.use('/api/bulkCreate', bulkCreateRouter)
Expand Down
Loading