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: 2 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import apiRouter from './routes/api-routes.js'
import clientRouter from './routes/client.js'
import _gog_fragmentsRouter from './routes/_gog_fragments_from_manuscript.js';
import _gog_glossesRouter from './routes/_gog_glosses_from_manuscript.js';
import _gog_idRouter from './routes/_gog_id.js';
import rest from './rest.js'
import { fileURLToPath } from 'url'
const __filename = fileURLToPath(import.meta.url)
Expand Down Expand Up @@ -83,6 +84,7 @@ app.use('/client', clientRouter)

app.use('/gog/fragmentsInManuscript', _gog_fragmentsRouter)
app.use('/gog/glossesInManuscript', _gog_glossesRouter)
app.use('/gog/id', _gog_idRouter)

/**
* Handle API errors and warnings RESTfully. All routes that don't end in res.send() will end up here.
Expand Down
68 changes: 60 additions & 8 deletions controllers/gog.js
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,8 @@ const _gog_glosses_from_manuscript = async function (req, res, next) {
*
*/
const expand = async function(primitiveEntity, GENERATOR=undefined, CREATOR=undefined){
if(!primitiveEntity?.["@id"] || primitiveEntity?.id) return primitiveEntity
// An entity is expandable if it carries a URI under either '@id' or 'id'.
if(!primitiveEntity?.["@id"] && !primitiveEntity?.id) return primitiveEntity
const targetId = primitiveEntity["@id"] ?? primitiveEntity.id ?? "unknown"
let queryObj = {
"__rerum.history.next": { $exists: true, $size: 0 }
Expand Down Expand Up @@ -380,24 +381,75 @@ const expand = async function(primitiveEntity, GENERATOR=undefined, CREATOR=unde
}

// Get the Annotations targeting this Entity from the db. Remove _id property.
// Assuming we do not need paged query here
let matches = await db.find(queryObj).toArray()
matches = matches.map(o => {
delete o._id
return o
})

// Combine the Annotation bodies with the primitive object
// Combine the Annotation bodies with the primitive object.
// Mirror DEER's client-side expand() (deer-utils.js buildValueObject)
// When more than one current Annotation asserts the same key, collect the values into an Array.
let expandedEntity = structuredClone(primitiveEntity)
for(const anno of matches){
const body = anno.body
let keys = Object.keys(body)
if(!keys || keys.length !== 1) return
let key = keys[0]
let val = body[key].value ?? body[key]
expandedEntity[key] = val
if(!body || typeof body !== "object") continue
const keys = Object.keys(body)
if(keys.length !== 1) continue
const key = keys[0]
const assertion = body[key]
const valueObject = {
value: assertion?.value ?? assertion,
source: assertion?.source ?? {
citationSource: anno["@id"] ?? anno.id,
citationNote: anno.label ?? anno.name ?? "Composed object from DEER",
comment: "Learn about the assembler for this object at https://github.com/CenterForDigitalHumanities/deer"
},
evidence: assertion?.evidence ?? anno.evidence ?? ""
}
if(expandedEntity.hasOwnProperty(key)){
expandedEntity[key] = Array.isArray(expandedEntity[key])
? [...expandedEntity[key], valueObject]
: [expandedEntity[key], valueObject]
}
else{
expandedEntity[key] = valueObject
}
}

return expandedEntity
}

export { _gog_fragments_from_manuscript, _gog_glosses_from_manuscript, expand }
/**
* GET /gog/id/:_id
* Return the RERUM object for :_id with the descriptive Annotations targeting it already merged in.
* Mirrors the caching/negotiation behavior of the GET /v1/id/:_id handler in controllers/crud.js.
* */
const expandedId = async function (req, res, next) {
res.set("Content-Type", "application/json; charset=utf-8")
const id = req.params["_id"]
try {
let match = await db.findOne({ "$or": [{ "_id": id }, { "__rerum.slug": id }] })
if (!match) {
const err = { "message": `No RERUM object with id '${id}'`, "status": 404 }
return next(utils.createExpressError(err))
}
// Same browser-caching policy as GET /v1/id/:_id so this stable URI is cached (24h).
res.set(utils.configureWebAnnoHeadersFor(match))
res.set("Cache-Control", "max-age=86400, must-revalidate")
res.set(utils.configureLastModifiedHeader(match))
// Include current version for optimistic locking (parity with GET /v1/id/:_id)
res.set("Current-Overwritten-Version", match.__rerum?.isOverwritten ?? "")
// No GENERATOR/CREATOR filter: merge every current targeting Annotation, matching the
// client's historical expand() behavior (its checkMatch is short-circuited to true).
let expanded = await expand(match)
expanded = idNegotiation(expanded)
res.location(_contextid(expanded["@context"]) ? expanded.id : expanded["@id"])
res.json(expanded)
} catch (error) {
return next(utils.createExpressError(error))
}
}

export { _gog_fragments_from_manuscript, _gog_glosses_from_manuscript, expand, expandedId }
5 changes: 3 additions & 2 deletions db-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { putUpdate, patchUpdate, patchSet, patchUnset, overwrite } from './contr
import { bulkCreate, bulkUpdate } from './controllers/bulk.js'
import { since, history, idHeadRequest, queryHeadRequest, sinceHeadRequest, historyHeadRequest } from './controllers/history.js'
import { release } from './controllers/release.js'
import { _gog_fragments_from_manuscript, _gog_glosses_from_manuscript, expand } from './controllers/gog.js'
import { _gog_fragments_from_manuscript, _gog_glosses_from_manuscript, expand, expandedId } from './controllers/gog.js'

export default {
index,
Expand Down Expand Up @@ -44,5 +44,6 @@ export default {
_gog_glosses_from_manuscript,
_gog_fragments_from_manuscript,
idNegotiation,
expand
expand,
expandedId
}
4 changes: 2 additions & 2 deletions package-lock.json

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

13 changes: 13 additions & 0 deletions routes/_gog_id.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import express from 'express'
const router = express.Router()
import controller from '../db-controller.js'

// GoG-namespaced, stable, browser-cacheable URL returning the object with its targeting Annotations merged in.
router.route('/:_id')
.get(controller.expandedId)
.all((req, res, next) => {
res.statusMessage = 'Improper request method, please use GET.'
res.status(405).end()
})

export default router
Loading