const FileService = require('../services/n3FileService.js')
// include our config file
const config = require('../config.js')
// File system stuff
const fs = require('fs')
// RDF stuff
const N3 = require('n3')
const {
DataFactory
} = N3
const {
namedNode
} = DataFactory
const {
quad
} = DataFactory
/**
* The baseModel contains reusable code that can be extended to other Models
* @class BaseModel
* @todo Factor out code only used on startup for fixing links that has no business being in the extended classes.
*/
class BaseModel {
/**
* @constructor
* @param {Object} req - HTTP request, used to generate the correct links from the URL of the request
*/
constructor (req) {
if (req.fullUrl == null) {
this.url = req.protocol + '://' + req.get('host') + config.serverOptions.routesPrefix
} else {
this.url = req.fullUrl
}
this.dataFolder = 'data'
/**
* @member {Object} - Key - value pairs containing default prefixes we always need
*/
this.prefixes = {
rdf: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
rdfs: 'http://www.w3.org/2000/01/rdf-schema#',
http: 'http//www.w3.org/2011/http#>',
hydra: 'http://www.w3.org/ns/hydra/context.jsonld',
oslo: 'http://oslo.example.org#',
agent: 'http://fast.example.org/agent#',
ldp: 'http://www.w3.org/ns/ldp#',
dcterms: 'http://purl.org/dc/terms/',
' ': 'http://example.org#',
ex: this.url + '/example-vocab#',
state: this.url + '/states#',
step: this.url + '/steps#',
shape: this.url + '/shapes#'
}
this.fileService = new FileService()
this.store = new N3.Store()
/**
* @member {Object} - The prefixes which depend on the server's current URL and thus always need to point to the right address.
* @todo move this out, only used on startup
*/
this.replacementPrefixes = {
state: this.url + '/states#',
step: this.url + '/steps#',
shape: this.url + '/shapes#'
}
/** @member {RegExp} - Regular expression to match prefixes that need to be adjusted to the server's url
*
* Only used once at startup in app.js
* @todo move this out
*/
this.prefixRegex = null
/** @member {RegExp} - Regular expression to match 'subpathlinks' that need to be adjusted to the server's url
*
* Only used once at startup in app.js
* @todo move this out
*/
this.subpathlinkRegex = null
}
/**
* Replaces all "@prefix..." and "ex:sublinkpath..." lines in the supplied file
* with the correct links as these links depend on the hostname of the server
*
* @param {string} file - name of the file whose contents we will be replacing
* @todo - replacing the sublinkpath links won't work if there are references to links not pointing to a 'plan'
* - pull this out of baseModel as it's only used once on startup but never in any of the derived classes
*/
replacePrefixesInFile (file) {
// Only construct the regexes once and only if this function is called as to not slow down other classes extending BaseModel
if (this.prefixRegex === null) {
// regex we will use to replace only the prefixes that depend on where it's hosted
this.prefixRegex = new RegExp(
Object.keys(this.replacementPrefixes)
.map(key => '(@prefix|PREFIX) ' + key + ': <.+> *\\.?')
.join('|'),
'g')
}
if (this.subpathlinkRegex === null) {
// TODO:
this.subpathlinkRegex = new RegExp(/ex:subpathlink.+?<(.+)\/plan/g)
}
const planUrl = 'ex:subpathlink <' + this.url + '/plan'
fs.readFile(file, 'utf8', (err, data) => {
if (err) {
return console.log(err)
}
var result = data.replace(this.prefixRegex, (match, p1, p2, p3, offset, string) => {
// check which prefix was found, return the replacement (which depends on which prefix was found)
for (const prefix in this.replacementPrefixes) {
if (match.includes(prefix)) {
return '@prefix ' + prefix + ': <' + this.replacementPrefixes[prefix] + '>.'
}
}
})
// only replaces the matched part so we only need to replace that part
result = result.replace(this.subpathlinkRegex, (match, p1, p2, p3, offset, string) => {
return planUrl
})
// write the changes back to the file
fs.writeFile(file, result, 'utf8', (err) => {
if (err) return console.log(err)
})
})
}
/**
* gets all of the items from this model and converts to jsonLD
* @param {Object} req - Request, currently unused
* @returns {Object} - Json representation of data
*/
async getAll (req) {
// define the fileService locally for this function, otherwise it can't be called in asynchronous calls
const fileService = this.fileService
// let currentURL = req.protocol + "://" + req.get('host') + req.originalUrl;
const currentURL = this.url
// let baseURL = req.protocol + "://" + req.get('host');
let quads = []
// define additional quads we want to add
const myQuad = quad(
namedNode(currentURL),
namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'),
namedNode('http://www.w3.org/ns/ldp#Container')
)
quads.push(myQuad)
// file associated with the model
const file = this.dataFolder + '/' + this.extendedFolder + '/' + this.fileName
// read quads from file associated with this model
const quadsFile = await fileService.fileToQuads(file)
quads = quads.concat(quadsFile)
// turn our quads into an N3 string with the prefixes added
const quadstring = await fileService.quadsToString(quads, this.prefixes)
if (Object.prototype.hasOwnProperty.call(req.headers, 'accept') && req.headers.accept === 'text/plain') {
return quadstring
} else {
return fileService.stringToJSONLD(quadstring)
}
}
}
module.exports = BaseModel