api Page updated: refactor to make it looks like article

This commit is contained in:
tmshv committed 2020-03-02 04:40:01 +03:00
1 parent 68b7f4225d
commit 348c3baba8
11 files changed
+160 -63

No files matched your search

@@ -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)
+45 -6
View File
@@ -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({})
+22 -26
View File
@@ -5,10 +5,6 @@ import { Tag } from './Tag'
const store = () => Data.getStore<Article>('Article')
function make(C) {
return x => new C(x)
}
export default class Article {
static async find(query, options = {}): Promise<Article[]> {
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
+2 -1
View File
@@ -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
+41 -14
View File
@@ -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<Page[]> {
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<T>(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<Page> {
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,
})
}
+11 -6
View File
@@ -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() {
+2 -1
View File
@@ -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,
+4 -1
View File
@@ -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,
+13
View File
@@ -3,3 +3,16 @@ export function unique<T>(items: T[], key: (item: T) => string): T[]{
return [...map.values()]
}
export function ensureArray<T>(value: T | T[]): T[] {
if (!value) {
return []
}
if (Array.isArray(value)) {
return value
}
return [value]
}
+16
View File
@@ -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])
}
+1
View File
@@ -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)