From 348c3baba82cd367d6e73ecd4a16a4004c995703 Mon Sep 17 00:00:00 2001 From: Roman Timashev Date: Mon, 2 Mar 2020 04:40:01 +0300 Subject: [PATCH] api Page updated: refactor to make it looks like article --- .../hudozka-api/src/controllers/article.ts | 11 +--- modules/hudozka-api/src/controllers/page.ts | 51 +++++++++++++++-- modules/hudozka-api/src/core/Article.ts | 48 ++++++++-------- modules/hudozka-api/src/core/Image.ts | 3 +- modules/hudozka-api/src/core/Page.ts | 55 ++++++++++++++----- modules/hudozka-api/src/core/Tag.ts | 17 ++++-- modules/hudozka-api/src/factory/image.ts | 3 +- modules/hudozka-api/src/factory/page.ts | 5 +- modules/hudozka-api/src/lib/array.ts | 13 +++++ modules/hudozka-api/src/lib/url.ts | 16 ++++++ modules/hudozka-api/src/server.ts | 1 + 11 files changed, 160 insertions(+), 63 deletions(-) diff --git a/modules/hudozka-api/src/controllers/article.ts b/modules/hudozka-api/src/controllers/article.ts index 4f9a2f31..63e17ce1 100644 --- a/modules/hudozka-api/src/controllers/article.ts +++ b/modules/hudozka-api/src/controllers/article.ts @@ -2,11 +2,7 @@ import Article from '../core/Article' import { findArticles } from '../server/articles' import { Request, Response } from 'express' import { encodeArticle } from '../factory/article' - -const int = (value: string | string[]) => parseInt(Array.isArray(value) - ? value[0] - : value -) +import { readQueryInt } from '../lib/url' export async function getItem(req: Request, res: Response) { const id = req.params.slug @@ -23,9 +19,8 @@ export async function getItem(req: Request, res: Response) { } export async function getAll(req: Request, res: Response) { - const { query } = req - const page = int(query.page) - const pageSize = int(query.pageSize) + const page = readQueryInt(req, 'page') + const pageSize = readQueryInt(req, 'pageSize') const data = await findArticles(page, pageSize) diff --git a/modules/hudozka-api/src/controllers/page.ts b/modules/hudozka-api/src/controllers/page.ts index 58f99752..33ea8155 100644 --- a/modules/hudozka-api/src/controllers/page.ts +++ b/modules/hudozka-api/src/controllers/page.ts @@ -1,20 +1,27 @@ import { Request, Response } from 'express' import Page from '../core/Page' -import { dropTrailingSlash, getPathWaterfall } from '../lib/url' +import { dropTrailingSlash, getPathWaterfall, readQueryArray, readQueryInt } from '../lib/url' import { encodePage } from '../factory/page' import * as breadcrumb from './breadcrumb' +async function resolveItem(page: Page) { + const breadcrumbs = await breadcrumb.getItems(page.getUrl()) + page.setBreadcrumb(breadcrumbs) + + return page +} + export async function getItem(req: Request, res: Response) { - const page = dropTrailingSlash(req.query.page) - const breadcrumbs = await breadcrumb.getItems(page) - const resource = await Page.findByUrl(page) + const pageUrl = dropTrailingSlash(req.query.page) + const resource = await Page.findByUrl(pageUrl) if (resource) { - res.json(encodePage(resource, breadcrumbs)) + const page = await resolveItem(resource) + res.json(encodePage(page)) } else { res.status(404) res.json({ - error: `Resource ${page} not found` + error: `Resource ${pageUrl} not found` }) } } @@ -34,6 +41,38 @@ export async function getAll(req: Request, res: Response) { } } +export async function getByTags(req: Request, res: Response) { + const skip = readQueryInt(req, 'skip') + const limit = readQueryInt(req, 'limit') + + const tags = readQueryArray(req, 'tag') + if (tags.length === 0) { + res.status(400) + return res.json({ + error: `No tags in request` + }) + } + + const options = { skip, limit } + const models = await Page.find({ + tags: { $in: ['event', 'album'] }, + }, options) + + if (models) { + const pages = await Promise.all(models.map(resolveItem)) + const items = pages.map(encodePage) + + res.json({ + items, + }) + } else { + res.status(404) + res.json({ + error: `Not found` + }) + } +} + export async function getUrls(req: Request, res: Response) { const data = await Page.find({}) diff --git a/modules/hudozka-api/src/core/Article.ts b/modules/hudozka-api/src/core/Article.ts index 0f093140..3187c5f7 100644 --- a/modules/hudozka-api/src/core/Article.ts +++ b/modules/hudozka-api/src/core/Article.ts @@ -5,10 +5,6 @@ import { Tag } from './Tag' const store = () => Data.getStore
('Article') -function make(C) { - return x => new C(x) -} - export default class Article { static async find(query, options = {}): Promise { const items = await find(store(), query, options as any) @@ -63,7 +59,7 @@ export default class Article { this.date = data.date this.title = data.title this.until = data.until - this.tags = (data.tags || []).map(make(Tag)) + this.tags = (data.tags || []).map((x: string) => Tag.fromCyrillic(x)) this.content = data.content this.hash = data.hash this.type = data.type @@ -76,27 +72,27 @@ export default class Article { this.url = `/article/${this.id}` } - plain() { - return { - id: this.id, - images: this.images, - post: this.post, - folder: this.folder, - date: this.date, - title: this.title, - until: this.until, - tags: this.tags, - content: this.content, - hash: this.hash, - type: this.type, - origin: this.origin, - file: this.file, - version: this.version, - preview: this.preview, - url: this.url, - featured: this.featured, - } - } + // plain() { + // return { + // id: this.id, + // images: this.images, + // post: this.post, + // folder: this.folder, + // date: this.date, + // title: this.title, + // until: this.until, + // tags: this.tags, + // content: this.content, + // hash: this.hash, + // type: this.type, + // origin: this.origin, + // file: this.file, + // version: this.version, + // preview: this.preview, + // url: this.url, + // featured: this.featured, + // } + // } } const previewFromImage = (imgs = []) => imgs.length diff --git a/modules/hudozka-api/src/core/Image.ts b/modules/hudozka-api/src/core/Image.ts index 3e1608f2..be306683 100644 --- a/modules/hudozka-api/src/core/Image.ts +++ b/modules/hudozka-api/src/core/Image.ts @@ -2,11 +2,12 @@ import Data from './Data' import ImageArtifactType from './ImageArtifactType' import { findOne } from '../lib/store' import { mapOf } from '../server/lib' +import { ObjectId } from 'mongodb' const store = () => Data.getStore('Image') export default class Image { - static async findById(id) { + static async findById(id: ObjectId) { const data = await findOne(store(), { _id: id }) if (!data) return null diff --git a/modules/hudozka-api/src/core/Page.ts b/modules/hudozka-api/src/core/Page.ts index c19a4f74..208967b9 100644 --- a/modules/hudozka-api/src/core/Page.ts +++ b/modules/hudozka-api/src/core/Page.ts @@ -2,9 +2,17 @@ import Data from './Data' import { find as storeFind, findOne, total } from '../lib/store' import Image from './Image' import { Tag } from './Tag' +import { ObjectId } from 'mongodb' +import { Breadcrumb } from '../types' const store = () => Data.getStore('Page') +type PageDto = { + [name: string]: any + preview?: ObjectId + images: ObjectId[] +} + export default class Page { static async find(query, options = {}): Promise { const items = await storeFind(store(), query, options as any) @@ -20,7 +28,7 @@ export default class Page { : processPage(item) } - static async findByUrl(url) { + static async findByUrl(url: string) { const item = await findOne(store(), { url }) return !item @@ -41,13 +49,16 @@ export default class Page { public date: Date public tags: Tag[] public data: any - public images: any + public images: ObjectId[] public documents: any - public preview: any + public preview?: Image public tokens: any[] public description: string + public featured: boolean - constructor(data) { + private breadcrumb: Breadcrumb + + constructor(data: { [name: string]: any }) { this.id = data.id this.hash = data.hash this.file = data.file @@ -60,27 +71,43 @@ export default class Page { this.documents = data.documents this.preview = data.preview this.tokens = data.tokens - this.tags = (data.tags || []).map(x => new Tag(x)) + this.tags = (data.tags || []).map((x: string) => new Tag(x, x)) + this.featured = data.featured this.description = data.description } + + public setBreadcrumb(value: Breadcrumb) { + this.breadcrumb = value + return this + } + + public getBreadcrumb() { + return this.breadcrumb + } + + public getUrl() { + return this.url + } } -const previewFromImage = (imgs = []) => imgs.length - ? imgs[0] - : null +function firstItem(items: T[] = []): T { + return items.length + ? items[0] + : null +} -async function processPage(article) { - let preview = null - const previewId = article.preview - ? article.preview - : previewFromImage(article.images) +async function processPage(item: PageDto): Promise { + let preview: Image = null + const previewId = item.preview + ? item.preview + : firstItem(item.images) if (previewId) { preview = await Image.findById(previewId) } return new Page({ - ...article, + ...item, preview, }) } diff --git a/modules/hudozka-api/src/core/Tag.ts b/modules/hudozka-api/src/core/Tag.ts index 1e6dae59..2f121c2d 100644 --- a/modules/hudozka-api/src/core/Tag.ts +++ b/modules/hudozka-api/src/core/Tag.ts @@ -1,15 +1,20 @@ import { translit } from '../lib/translit' export class Tag { + public static fromCyrillic(tag: string) { + const slug = tag + ? translit(tag).toLowerCase() + : tag + + return new Tag(tag, slug) + } + public name: string public slug: string - constructor(tag: string) { - this.name = tag - - if (tag) { - this.slug = translit(tag).toLowerCase() - } + constructor(name: string, slug: string) { + this.name = name + this.slug = slug } public isValid() { diff --git a/modules/hudozka-api/src/factory/image.ts b/modules/hudozka-api/src/factory/image.ts index 828eaebc..9720c850 100644 --- a/modules/hudozka-api/src/factory/image.ts +++ b/modules/hudozka-api/src/factory/image.ts @@ -1,4 +1,5 @@ import ImageArtifactType from '../core/ImageArtifactType' +import Image from '../core/Image' export type ImageArtifactDto = { height: number @@ -23,7 +24,7 @@ export type ImageDto = { file: string } -export function encodeImage(image): ImageDto { +export function encodeImage(image: Image): ImageDto { const artifacts = [ ImageArtifactType.SMALL, ImageArtifactType.MEDIUM, diff --git a/modules/hudozka-api/src/factory/page.ts b/modules/hudozka-api/src/factory/page.ts index 66272eab..231b5916 100644 --- a/modules/hudozka-api/src/factory/page.ts +++ b/modules/hudozka-api/src/factory/page.ts @@ -18,12 +18,14 @@ type PageDto = { preview?: ImageDto tokens: Token[] tags: Tag[] + featured: boolean breadcrumb: Breadcrumb } -export function encodePage(page: Page, breadcrumb: Breadcrumb): PageDto { +export function encodePage(page: Page): PageDto { const preview = page.preview ? encodeImage(page.preview) : null const date = page.date ? page.date.toString() : null + const breadcrumb = page.getBreadcrumb() return { id: page.id, @@ -33,6 +35,7 @@ export function encodePage(page: Page, breadcrumb: Breadcrumb): PageDto { description: page.description, tokens: page.tokens, tags: page.tags, + featured: page.featured, preview, date, breadcrumb, diff --git a/modules/hudozka-api/src/lib/array.ts b/modules/hudozka-api/src/lib/array.ts index 65031f2a..294db4eb 100644 --- a/modules/hudozka-api/src/lib/array.ts +++ b/modules/hudozka-api/src/lib/array.ts @@ -3,3 +3,16 @@ export function unique(items: T[], key: (item: T) => string): T[]{ return [...map.values()] } + +export function ensureArray(value: T | T[]): T[] { + if (!value) { + return [] + } + + if (Array.isArray(value)) { + return value + } + + return [value] +} + diff --git a/modules/hudozka-api/src/lib/url.ts b/modules/hudozka-api/src/lib/url.ts index f6c2bd5f..4ac3bb88 100644 --- a/modules/hudozka-api/src/lib/url.ts +++ b/modules/hudozka-api/src/lib/url.ts @@ -1,3 +1,6 @@ +import { Request } from 'express' +import { ensureArray } from './array' + export function dropTrailingSlash(path: string) { if (path === '/' || path === '') { return '/' @@ -26,3 +29,16 @@ export function getPathWaterfall(path: string): string[] { return ['/', ...parts] } + +export function readQueryInt(req: Request, key: string) { + const int = (value: string | string[]) => parseInt(Array.isArray(value) + ? value[0] + : value + ) + + return int(req.query[key]) || 0 +} + +export function readQueryArray(req: Request, key: string) { + return ensureArray(req.query[key]) +} diff --git a/modules/hudozka-api/src/server.ts b/modules/hudozka-api/src/server.ts index d0dfdb6e..b8c82676 100644 --- a/modules/hudozka-api/src/server.ts +++ b/modules/hudozka-api/src/server.ts @@ -33,6 +33,7 @@ async function main() { app.get('/pages', page.getAll) app.get('/pages/urls', page.getUrls) + app.get('/pages/tags', page.getByTags) app.get('/page', page.getItem) app.get('/image', image.getItem)