'use strict'
const request = require('request')
const { exists, getBaseUrl, getComponents } = require('sesatheque-client/dist/server/sesatheques')
const appConfig = require('../config')
const myBaseId = appConfig.application.baseId
const tokens = {}
appConfig.sesatheques.forEach(({ baseId, apiToken }) => {
if (baseId && apiToken) tokens[baseId] = apiToken
})
/**
* Service d'accès aux ressources d'autres sesatheques
* @service $ressourceFetch
* @requires $cache
* @requires $ressourceRepository
*/
module.exports = function (component) {
component.service('$ressourceFetch', function ($cache, $ressourceRepository) {
/**
* Renvoie une ressource récupérée ailleurs ou ici (ça peut être un alias)
* @memberOf $ressourceFetch
* @param {string} rid (une string baseId/origine/idOrigine marche aussi)
* @param {ressourceCallback} next (renvoie une EntityRessource si c'est local et ses propriétés sinon)
*/
function fetch (rid, next) {
try {
const [baseId, id] = getComponents(rid)
if (baseId === myBaseId) {
return $ressourceRepository.load(id, function (error, ressource) {
if (error) next(error)
else if (ressource) next(null, ressource)
else next(new Error(`Aucune ressource ${rid}`))
})
}
// pas chez nous
if (!exists(baseId)) return next(new Error(`rid ${rid} invalide ou correspondant à une sesathèque inconnue`))
// on peut aller chercher ça
const baseUrl = getBaseUrl(baseId)
const options = {
uri: baseUrl + 'api/public/' + id,
gzip: true,
json: true,
timeout: 3000
}
if (tokens[baseId]) {
options.uri = baseUrl + 'api/ressource/' + id
options.headers = { 'X-ApiToken': encodeURIComponent(tokens[baseId]) }
}
request(options, function (error, response, body) {
if (error) return next(error)
if (response.statusCode === 200) {
if (body.data && body.data.rid) return next(null, body.data)
} else if (body.message) {
next(Error(body.message))
} else {
log.error(`Réponse incohérente sur ${options.uri}`, body)
next(Error('Réponse incohérente, impossible de récupérer la ressource'))
}
})
} catch (error) {
next(error)
}
}
/**
* Passe un rid à next
* @param mixedId un oid ou baseId/oid ou baseId/origine/idOrigine ou origine/idOrigine
* @param next appelé avec (error, rid)
*/
function fetchRid (mixedId, next) {
function send (error, ressource) {
if (error) next(error)
else if (ressource) next(null, ressource.rid)
else next(new Error(`aucune ressource ${mixedId}`))
}
const [baseId, id] = getComponents(mixedId)
if (baseId === myBaseId) return $ressourceRepository.load(id, send)
if (!exists(baseId)) return $ressourceRepository.load(mixedId, send)
// c'est un rid externe, faut appeler son /api/getRid?id=xxx
const baseUrl = getBaseUrl(baseId)
const options = {
uri: baseUrl + 'api/public/getRid?id=' + id,
json: true,
timeout: 3000
}
request(options, function (error, response, body) {
if (error) return next(error)
if (response.statusCode === 200 && body.data && body.data.rid) return next(null, body.data.rid)
if (body && body.message) return next(Error(body.message))
// bizarre…
log.error(`Réponse incohérente sur ${options.uri}`, body)
next(Error('Réponse incohérente, impossible de récupérer la ressource'))
})
}
/**
* Renvoie une ressource récupérée ici ou ailleurs, en allant chercher l'original si rid remonte un alias.
* Passe une erreur si la 2e ressource récupérée est encore un alias
* (un alias d'alias ne devrait pas exister)
* @memberOf $ressourceFetch
* @param {string} aliasOf
* @param {ressourceCallback} next
*/
function fetchOriginal (aliasOf, next) {
fetch(aliasOf, function (error, ressource) {
if (error) return next(error)
if (ressource.aliasOf) {
// si c'est un alias on recommence, mais une seule fois (trop risqué de mettre du récursif ici)
log.dataError(`alias d’alias (${aliasOf} => ${ressource.aliasOf})`)
fetch(ressource.aliasOf, function (error, ress2) {
if (error) return next(error)
// on a toujours une erreur ou une ressource
if (ress2.aliasOf) return next(Error(`Trop d’alias imbriqués (${aliasOf} => ${ressource.aliasOf} => ${ress2.aliasOf})`))
next(null, ress2)
})
} else {
next(null, ressource)
}
})
}
/**
* Récupère le contenu d'une url externe
* @param url {string}
* @param context {Context}
*/
function fetchURL (url, context) {
const options = {
url,
timeout: 5000,
gzip: true
}
const cacheKey = `copy${encodeURIComponent(url)}`
function sendRaw (page) {
const options = {}
if (page.contentType) options.headers = { 'Content-Type': page.contentType }
context.raw(page.body, options)
}
$cache.get(cacheKey, (error, page) => {
if (error) console.error(error)
if (page) return sendRaw(page)
request(options, (error, response, body) => {
if (error || response.statusCode !== 200) {
if (error) log.error(error)
else log.error(Error(`Réponse ${response.statusCode} sur ${url}`))
return context.plain('Impossible de récupérer la page ' + url)
}
// on met ça en cache pendant 10min
const page = {
body,
contentType: response.headers['content-type']
}
$cache.set(cacheKey, page, 600, (error) => {
if (error) log.error(error)
})
sendRaw(page)
})
})
}
return {
fetch,
fetchOriginal,
fetchRid,
fetchURL
}
})
}