Skip to content

Commit

Permalink
Merge branch 'develop' into feat/article-query
Browse files Browse the repository at this point in the history
  • Loading branch information
devformatters authored Oct 13, 2020
2 parents c5fb148 + 01678e1 commit f8e3b40
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 81 deletions.
44 changes: 44 additions & 0 deletions db/migrations/20201009133031_fix_asset_map_entity.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const article_table = 'article'
const draft_table = 'draft'
const asset_map_table = 'asset_map'
const entity_type_table = 'entity_type'

exports.up = async (knex) => {
const { id: articleTypeId } = await knex(entity_type_table)
.select('id')
.where({ table: article_table })
.first()
const { id: draftTypeId } = await knex(entity_type_table)
.select('id')
.where({ table: draft_table })
.first()

if (!articleTypeId || !draftTypeId) {
throw new Error('cannot find entity_type for article and draft tables.')
}

// link article assets back to the draft
await knex.raw(`
UPDATE
${asset_map_table}
SET
entity_id = asset_map_article_draft.draft_id,
entity_type_id = '${draftTypeId}'
FROM (
SELECT
asset_map.*,
article.id AS article_id,
draft.id AS draft_id
FROM
${asset_map_table}
LEFT JOIN ${article_table} ON article.id = entity_id
LEFT JOIN ${draft_table} ON draft.id = article.draft_id
WHERE
entity_type_id = '${articleTypeId}'
AND draft.id IS NOT NULL) AS asset_map_article_draft
WHERE
asset_map.id = asset_map_article_draft.id
`)
}

exports.down = async (knex) => {}
67 changes: 40 additions & 27 deletions src/connectors/queue/publication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ class PublicationQueue extends BaseQueue {

// mark draft as published and copy data from article
// TODO: deprecated once article table is altered
// @see {@url https://github.com/thematters/matters-server/pull/1509}
// @see {@url https://github.com/thematters/matters-server/pull/1510}
await this.draftService.baseUpdate(draft.id, {
articleId: article.id,
wordCount: article.wordCount,
Expand All @@ -116,6 +116,20 @@ class PublicationQueue extends BaseQueue {
await this.handleCollection({ draft, article })
job.progress(40)

// handle tags
const tags = await this.handleTags({ draft, article })
job.progress(50)

/**
* Handle Assets
*
* Relationship between asset_map and entity:
*
* cover -> article
* embed -> draft
*
* @see {@url https://github.com/thematters/matters-server/pull/1510}
*/
const [
{ id: draftEntityTypeId },
{ id: articleEntityTypeId },
Expand All @@ -126,20 +140,20 @@ class PublicationQueue extends BaseQueue {

// Remove unused assets
await this.deleteUnusedAssets({ draftEntityTypeId, draft })
job.progress(45)
job.progress(60)

// Swap assets from draft to article
await this.systemService.replaceAssetMapEntityTypeAndId(
draftEntityTypeId,
draft.id,
// Swap cover assets from draft to article
const coverAssets = await this.systemService.findAssetAndAssetMap({
entityTypeId: draftEntityTypeId,
entityId: draft.id,
assetType: 'cover',
})
await this.systemService.swapAssetMapEntity(
coverAssets.map((ast) => ast.id),
articleEntityTypeId,
article.id
)
job.progress(50)

// handle tags
const tags = await this.handleTags({ draft, article })
job.progress(60)
job.progress(70)

// add to search
const author = await this.userService.baseFindById(article.authorId)
Expand Down Expand Up @@ -319,27 +333,26 @@ class PublicationQueue extends BaseQueue {
draft: any
}) => {
try {
const [assetMap, uuids] = await Promise.all([
this.systemService.findAssetAndAssetMap(draftEntityTypeId, draft.id),
const [assets, uuids] = await Promise.all([
this.systemService.findAssetAndAssetMap({
entityTypeId: draftEntityTypeId,
entityId: draft.id,
}),
extractAssetDataFromHtml(draft.content),
])

const assets = assetMap.reduce(
(data: { [id: string]: string }, asset: any) => {
const isCover = draft.cover === asset.assetId
const isEmbed = uuids && uuids.includes(asset.uuid)

if (!isCover && !isEmbed) {
data[`${asset.assetId}`] = asset.path
}
const unusedAssetPaths: { [id: string]: string } = {}
assets.forEach((asset) => {
const isCover = draft.cover === asset.assetId
const isEmbed = uuids && uuids.includes(asset.uuid)

return data
},
{}
)
if (!isCover && !isEmbed) {
unusedAssetPaths[`${asset.assetId}`] = asset.path
}
})

if (assets && Object.keys(assets).length > 0) {
await this.systemService.deleteAssetAndAssetMap(assets)
if (Object.keys(unusedAssetPaths).length > 0) {
await this.systemService.deleteAssetAndAssetMap(unusedAssetPaths)
}
} catch (e) {
logger.error(e)
Expand Down
23 changes: 12 additions & 11 deletions src/connectors/queue/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,17 +107,18 @@ class UserQueue extends BaseQueue {
// delete assets
await Promise.all(
drafts.map(async (draft) => {
const assetMap = await this.systemService.findAssetAndAssetMap(
draftEntityTypeId,
draft.id
)
const assets = assetMap.reduce((data: any, asset: any) => {
data[`${asset.assetId}`] = asset.path
return data
}, {})

if (assets && Object.keys(assets).length > 0) {
await this.systemService.deleteAssetAndAssetMap(assets)
const assets = await this.systemService.findAssetAndAssetMap({
entityTypeId: draftEntityTypeId,
entityId: draft.id,
})

const assetPaths: { [id: string]: string } = {}
assets.forEach((asset) => {
assetPaths[`${asset.assetId}`] = asset.path
})

if (Object.keys(assetPaths).length > 0) {
await this.systemService.deleteAssetAndAssetMap(assetPaths)
}
})
)
Expand Down
59 changes: 33 additions & 26 deletions src/connectors/systemService.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { v4 } from 'uuid'

import {
ASSET_TYPE,
BATCH_SIZE,
SEARCH_KEY_TRUNCATE_LENGTH,
SKIPPED_LIST_ITEM_TYPES,
Expand Down Expand Up @@ -144,48 +145,54 @@ export class SystemService extends BaseService {
/**
* Find asset and asset map by given entity type and id
*/
findAssetAndAssetMap = async (entityTypeId: string, entityId: string) =>
this.knex('asset_map')
.select('asset_id', 'uuid', 'path', 'entity_id', 'type', 'created_at')
findAssetAndAssetMap = async ({
entityTypeId,
entityId,
assetType,
}: {
entityTypeId: string
entityId: string
assetType?: keyof typeof ASSET_TYPE
}) => {
let qs = this.knex('asset_map')
.select('asset_map.*', 'uuid', 'path', 'type', 'created_at')
.rightJoin('asset', 'asset_map.asset_id', 'asset.id')
.where({ entityTypeId, entityId })

if (assetType) {
qs = qs.andWhere({ type: assetType })
}

return qs
}

/**
* Update asset map by given entity type and id
* Swap entity of asset map by given ids
*/
replaceAssetMapEntityTypeAndId = async (
oldEntityTypeId: string,
oldEntityId: string,
newEntityTypeId: string,
newEntityId: string
swapAssetMapEntity = async (
assetMapIds: string[],
entityTypeId: string,
entityId: string
) =>
this.knex('asset_map')
.where({
entityTypeId: oldEntityTypeId,
entityId: oldEntityId,
})
.update({
entityTypeId: newEntityTypeId,
entityId: newEntityId,
})
this.knex('asset_map').whereIn('id', assetMapIds).update({
entityTypeId,
entityId,
})

/**
* Delete asset and asset map by a given id
* Delete asset and asset map by the given id:path maps
*/
deleteAssetAndAssetMap = async (assets: { [id: string]: string }) => {
const ids = Object.keys(assets)
deleteAssetAndAssetMap = async (assetPaths: { [id: string]: string }) => {
const ids = Object.keys(assetPaths)
const paths = Object.keys(assetPaths)

await this.knex.transaction(async (trx) => {
await trx('asset_map').whereIn('asset_id', ids).del()
await trx('asset').whereIn('id', ids).del()
})

try {
await Promise.all(
Object.values(assets).map((key: any) => {
this.aws.baseDeleteFile(key)
})
)
await Promise.all(paths.map((path) => this.aws.baseDeleteFile(path)))
} catch (e) {
logger.error(e)
}
Expand Down
20 changes: 10 additions & 10 deletions src/queries/article/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,24 @@ const resolver: ArticleToAssetsResolver = async (
const { id: articleEntityTypeId } = await systemService.baseFindEntityTypeId(
'article'
)
const articleAssetMap = await systemService.findAssetAndAssetMap(
articleEntityTypeId,
id
)
const articleAssets = await systemService.findAssetAndAssetMap({
entityTypeId: articleEntityTypeId,
entityId: id,
})

// assets belonged to linked latest draft
let draftAssetMap: any[] = []
let draftAssets: any[] = []
if (draftId) {
const { id: draftEntityTypeId } = await systemService.baseFindEntityTypeId(
'draft'
)
draftAssetMap = await systemService.findAssetAndAssetMap(
draftEntityTypeId,
draftId
)
draftAssets = await systemService.findAssetAndAssetMap({
entityTypeId: draftEntityTypeId,
entityId: draftId,
})
}

const assets = [...articleAssetMap, ...draftAssetMap].map((asset) => {
const assets = [...articleAssets, ...draftAssets].map((asset) => {
return {
...asset,
path: asset.path ? `${systemService.aws.s3Endpoint}/${asset.path}` : null,
Expand Down
12 changes: 5 additions & 7 deletions src/queries/draft/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,17 @@ const resolver: DraftToAssetsResolver = async (
const { id: draftEntityTypeId } = await systemService.baseFindEntityTypeId(
'draft'
)
const assetMap = await systemService.findAssetAndAssetMap(
draftEntityTypeId,
id
)
const assets = await systemService.findAssetAndAssetMap({
entityTypeId: draftEntityTypeId,
entityId: id,
})

const assets = assetMap.map((asset) => {
return assets.map((asset) => {
return {
...asset,
path: asset.path ? `${systemService.aws.s3Endpoint}/${asset.path}` : null,
}
})

return assets
}

export default resolver

0 comments on commit f8e3b40

Please sign in to comment.