Skip to content
Open
Show file tree
Hide file tree
Changes from all 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

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