Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
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
2 changes: 1 addition & 1 deletion bin/rerum_v1.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import config from '../config/index.js'

const port = config.PORT ?? 3001
app.set('port', port)

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

107 changes: 56 additions & 51 deletions controllers/bulk.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,43 +12,60 @@ import config from '../config/index.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/
* Sets standard JSON response headers on the Express response object.
* @param {import('express').Response} res - Express response object
*/
const bulkCreate = async function (req, res, next) {
function setJsonHeaders(res) {
res.set("Content-Type", "application/json; charset=utf-8")
}


//function to validate request body is a non-empty array
function requireNonEmptyArrayBody(req) {
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
throw { message: "The request body must be an array of objects.", status: 400 }
}
if (documents.length === 0) {
err.message = "No action on an empty array."
err.status = 400
next(createExpressError(err))
return
throw { message: "No action on an empty array.", status: 400 }
}
return documents
}


//check if an item is valid JSON object (not array)
function isValidJsonObject(d) {
return d !== null && typeof d === "object" && !Array.isArray(d)
}

/**
* 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)

let documents
try {
documents = requireNonEmptyArrayBody(req)
} catch (err) {
return next(createExpressError(err))
}

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))
return
return next(createExpressError({
message: "All objects in the body of a `/bulkCreate` must be JSON and must not contain a declared identifier property.",
status: 400
}))
}

// TODO: bulkWrite SLUGS? Maybe assign an id to each document and then use that to create the slug?
Expand Down Expand Up @@ -105,40 +122,28 @@ 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 = {}
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
setJsonHeaders(res)

let documents
try {
documents = requireNonEmptyArrayBody(req)
} catch (err) {
return next(createExpressError(err))
}

let encountered = []
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
}
// Items must have an @id, or in some cases an id will do
if (!isValidJsonObject(d)) return true
const idcheck = _contextid(d["@context"]) ? (d.id ?? d["@id"]) : d["@id"]
if(!idcheck) return d
if (!idcheck) return true // Reject items WITHOUT an id (updates need an id)
return false
})
// 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))
return
return next(createExpressError({
message: "All objects in the body of a `/bulkUpdate` must be JSON and must contain a declared identifier property.",
status: 400
}))
}
// unordered bulkWrite() operations have better performance metrics.
let bulkOps = []
Expand Down
8 changes: 4 additions & 4 deletions 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.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"debug": "~4.4.3",
"dotenv": "~17.2.3",
"express": "^5.2.1",
"express-oauth2-jwt-bearer": "~1.7.1",
"express-oauth2-jwt-bearer": "^1.7.4",
"express-urlrewrite": "~2.0.3",
"mongodb": "^7.0.0",
"morgan": "~1.10.1"
Expand Down
Loading