104f84f252
As discussed with the product team we want to enforce kebab-case file names for all files, with the exception of files which export a single class, in which case they should be PascalCase and reflect the class which they export. This will help find classes faster, and should push better naming for them too. Some files and packages have been excluded from this linting, specifically when a library or framework depends on the naming of a file for the functionality e.g. Ember, knex-migrator, adapter-manager
164 lines
4.3 KiB
JavaScript
164 lines
4.3 KiB
JavaScript
/**
|
|
* @typedef {Object} UrlService
|
|
* @prop {(resourceId: string, options) => Object} getResource
|
|
* @prop {(resourceId: string, options) => string} getUrlByResourceId
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* Translate a url into, (id+type), or a resource, and vice versa
|
|
*/
|
|
class UrlTranslator {
|
|
/**
|
|
*
|
|
* @param {Object} deps
|
|
* @param {UrlService} deps.urlService
|
|
* @param {Object} deps.urlUtils
|
|
* @param {Object} deps.models
|
|
* @param {Object} deps.models.Post
|
|
* @param {Object} deps.models.Tag
|
|
* @param {Object} deps.models.User
|
|
*/
|
|
constructor({urlService, urlUtils, models}) {
|
|
this.urlService = urlService;
|
|
this.urlUtils = urlUtils;
|
|
this.models = models;
|
|
}
|
|
|
|
/**
|
|
* Convert root relative URLs to subdirectory relative URLs
|
|
*/
|
|
stripSubdirectoryFromPath(path) {
|
|
// Bit weird, but only way to do it with the urlUtils atm
|
|
|
|
// First convert path to an absolute path
|
|
const absolute = this.urlUtils.relativeToAbsolute(path);
|
|
|
|
// Then convert it to a relative path, but without subdirectory
|
|
return this.urlUtils.absoluteToRelative(absolute, {withoutSubdirectory: true});
|
|
}
|
|
|
|
/**
|
|
* Convert root relative URLs to subdirectory relative URLs
|
|
*/
|
|
relativeToAbsolute(path) {
|
|
return this.urlUtils.relativeToAbsolute(path);
|
|
}
|
|
|
|
/**
|
|
* Gives an ordinary URL a name, e.g. / is 'homepage'
|
|
*/
|
|
getUrlTitle(url) {
|
|
return url === '/' ? 'homepage' : url;
|
|
}
|
|
|
|
/**
|
|
* Get the resource type and ID from a URLHistory item that was added by the frontend attribution script
|
|
* @param {import('./UrlHistory').UrlHistoryItem} item
|
|
* @returns {Promise<{type: string, id: string | null, url: string}|null>} Returns null if the item is invalid
|
|
*/
|
|
async getResourceDetails(item) {
|
|
if (item.type) {
|
|
const resource = await this.getResourceById(item.id, item.type);
|
|
if (resource) {
|
|
return {
|
|
type: item.type,
|
|
id: item.id,
|
|
url: this.getUrlByResourceId(item.id, {absolute: false})
|
|
};
|
|
}
|
|
|
|
// Invalid id: ignore
|
|
return null;
|
|
}
|
|
|
|
if (!item.path) {
|
|
return null;
|
|
}
|
|
|
|
const path = this.stripSubdirectoryFromPath(item.path);
|
|
return {
|
|
type: 'url',
|
|
id: null,
|
|
...this.getTypeAndIdFromPath(path),
|
|
url: path
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Get the resource type and ID from a path that was visited on the site
|
|
* @param {string} path (excluding subdirectory)
|
|
*/
|
|
getTypeAndIdFromPath(path) {
|
|
const resource = this.urlService.getResource(path);
|
|
if (!resource) {
|
|
return;
|
|
}
|
|
|
|
if (resource.config.type === 'posts') {
|
|
return {
|
|
type: 'post',
|
|
id: resource.data.id
|
|
};
|
|
}
|
|
|
|
if (resource.config.type === 'pages') {
|
|
return {
|
|
type: 'page',
|
|
id: resource.data.id
|
|
};
|
|
}
|
|
|
|
if (resource.config.type === 'tags') {
|
|
return {
|
|
type: 'tag',
|
|
id: resource.data.id
|
|
};
|
|
}
|
|
|
|
if (resource.config.type === 'authors') {
|
|
return {
|
|
type: 'author',
|
|
id: resource.data.id
|
|
};
|
|
}
|
|
}
|
|
|
|
getUrlByResourceId(id, options = {absolute: true}) {
|
|
return this.urlService.getUrlByResourceId(id, options);
|
|
}
|
|
|
|
async getResourceById(id, type) {
|
|
switch (type) {
|
|
case 'post':
|
|
case 'page': {
|
|
const post = await this.models.Post.findOne({id}, {require: false});
|
|
if (!post) {
|
|
return null;
|
|
}
|
|
|
|
return post;
|
|
}
|
|
case 'author': {
|
|
const user = await this.models.User.findOne({id}, {require: false});
|
|
if (!user) {
|
|
return null;
|
|
}
|
|
|
|
return user;
|
|
}
|
|
case 'tag': {
|
|
const tag = await this.models.Tag.findOne({id}, {require: false});
|
|
if (!tag) {
|
|
return null;
|
|
}
|
|
|
|
return tag;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
module.exports = UrlTranslator;
|