Simplify the API to create pages from PDFs

This creates a stub page when an upload is initiated, so the
SaveFile (or create article from file) APIs do not need to be
called.

The PDF uploaded trigger fired by GCS will mark the upload
as completed and update the page status.
This commit is contained in:
Jackson Harper 2022-05-21 12:05:28 -07:00
parent 4acb0f38f2
commit e5bc68ca9e
3 changed files with 110 additions and 57 deletions

View file

@ -5,6 +5,7 @@ import {
MutationUploadFileRequestArgs,
UploadFileStatus,
UploadFileRequestErrorCode,
ArticleSavingRequestStatus,
} from '../../generated/graphql'
import { WithDataSourcesContext } from '../types'
import {
@ -15,13 +16,17 @@ import path from 'path'
import normalizeUrl from 'normalize-url'
import { analytics } from '../../utils/analytics'
import { env } from '../../env'
import { createPage } from '../../elastic/pages'
import { PageType } from '../../elastic/types'
import { generateSlug } from '../../utils/helpers'
export const uploadFileRequestResolver: ResolverFn<
UploadFileRequestResult,
unknown,
WithDataSourcesContext,
MutationUploadFileRequestArgs
> = async (_obj, { input }, { models, kx, claims }) => {
> = async (_obj, { input }, ctx) => {
const { models, kx, claims } = ctx
let uploadFileData: { id: string | null } = {
id: null,
}
@ -39,16 +44,19 @@ export const uploadFileRequestResolver: ResolverFn<
},
})
let title: string
let fileName: string
try {
const url = normalizeUrl(new URL(input.url).href, {
stripHash: true,
stripWWW: false,
})
title = decodeURI(path.basename(new URL(url).pathname, '.pdf'))
fileName = decodeURI(path.basename(new URL(url).pathname)).replace(
/[^a-zA-Z0-9-_.]/g,
''
)
if (!fileName) {
fileName = 'content.pdf'
}
@ -73,6 +81,31 @@ export const uploadFileRequestResolver: ResolverFn<
uploadFilePathName,
input.contentType
)
const pageId = await createPage(
{
id: '',
url: input.url,
userId: claims.uid,
title: title,
hash: uploadFilePathName,
content: '',
pageType: PageType.File,
uploadFileId: uploadFileData.id,
slug: generateSlug(uploadFilePathName),
createdAt: new Date(),
savedAt: new Date(),
readingProgressPercent: 0,
readingProgressAnchorIndex: 0,
state: ArticleSavingRequestStatus.Processing,
},
ctx
)
if (!pageId) {
return { errorCodes: [UploadFileRequestErrorCode.FailedCreate] }
}
return { id: uploadFileData.id, uploadSignedUrl }
} else {
return { errorCodes: [UploadFileRequestErrorCode.FailedCreate] }

View file

@ -8,6 +8,10 @@ import {
} from '../../datalayer/pubsub'
import { Page } from '../../elastic/types'
import { getPageByParam, updatePage } from '../../elastic/pages'
import { ArticleSavingRequestStatus } from '../../generated/graphql'
import { initModels } from '../../server'
import { kx } from '../../datalayer/knex_config'
import { setClaims } from '../../datalayer/helpers'
interface UpdateContentMessage {
fileId: string
@ -65,6 +69,22 @@ export function contentServiceRouter() {
if (msg.author) pageToUpdate.author = msg.author
if (msg.description) pageToUpdate.description = msg.description
// This event is fired after the file is fully uploaded,
// so along with upadting content, we mark it as
// succeeded.
pageToUpdate.state = ArticleSavingRequestStatus.Succeeded
try {
const models = initModels(kx, false)
const uploadFileData = await kx.transaction(async (tx) => {
await setClaims(tx, page.userId)
return models.uploadFile.setFileUploadComplete(fileId, tx)
})
console.log('updated uploadFileData', uploadFileData)
} catch (error) {
console.log('error marking file upload as completed', error)
}
const result = await updatePage(page.id, pageToUpdate, {
pubsub: createPubSubClient(),
uid: page.userId,

View file

@ -31,9 +31,9 @@ export const saveFile = async (
): Promise<SaveResult> => {
console.log('saving file with input', input)
/* We do not trust the values from client, lookup upload file by querying
* with filtering on user ID and URL to verify client's uploadFileId is valid.
*/
// /* We do not trust the values from client, lookup upload file by querying
// * with filtering on user ID and URL to verify client's uploadFileId is valid.
// */
const uploadFile = await ctx.models.uploadFile.getWhere({
id: input.uploadFileId,
userId: saver.id,
@ -53,63 +53,63 @@ export const saveFile = async (
return ctx.models.uploadFile.setFileUploadComplete(input.uploadFileId, tx)
})
if (!uploadFileData || !uploadFileData.id || !uploadFileData.fileName) {
console.log('error completing upload file request', input)
return {
errorCodes: [SaveErrorCode.Unknown],
}
}
// if (!uploadFileData || !uploadFileData.id || !uploadFileData.fileName) {
// console.log('error completing upload file request', input)
// return {
// errorCodes: [SaveErrorCode.Unknown],
// }
// }
const uploadFileUrlOverride = await makeStorageFilePublic(
uploadFileData.id,
uploadFileData.fileName
)
// // const uploadFileUrlOverride = await makeStorageFilePublic(
// // uploadFileData.id,
// // uploadFileData.fileName
// // )
const matchedUserArticleRecord = await getPageByParam({
userId: saver.id,
url: uploadFileUrlOverride,
state: ArticleSavingRequestStatus.Succeeded,
})
// const matchedUserArticleRecord = await getPageByParam({
// userId: saver.id,
// url: uploadFileData.url,
// state: ArticleSavingRequestStatus.Succeeded,
// })
if (matchedUserArticleRecord) {
await updatePage(
matchedUserArticleRecord.id,
{
savedAt: new Date(),
archivedAt: null,
},
ctx
)
input.clientRequestId = matchedUserArticleRecord.id
} else {
const pageId = await createPage(
{
url: uploadFileUrlOverride,
title: uploadFile.fileName,
hash: uploadFileDetails.md5Hash,
content: '',
pageType: PageType.File,
uploadFileId: input.uploadFileId,
slug: generateSlug(uploadFile.fileName),
userId: saver.id,
id: input.clientRequestId,
createdAt: new Date(),
savedAt: new Date(),
readingProgressPercent: 0,
readingProgressAnchorIndex: 0,
state: ArticleSavingRequestStatus.Succeeded,
},
ctx
)
// if (matchedUserArticleRecord) {
// await updatePage(
// matchedUserArticleRecord.id,
// {
// savedAt: new Date(),
// archivedAt: null,
// },
// ctx
// )
// input.clientRequestId = matchedUserArticleRecord.id
// } else {
// const pageId = await createPage(
// {
// url: uploadFile.url,
// title: uploadFile.fileName,
// hash: uploadFileDetails.md5Hash,
// content: '',
// pageType: PageType.File,
// uploadFileId: input.uploadFileId,
// slug: generateSlug(uploadFile.fileName),
// userId: saver.id,
// id: input.clientRequestId,
// createdAt: new Date(),
// savedAt: new Date(),
// readingProgressPercent: 0,
// readingProgressAnchorIndex: 0,
// state: ArticleSavingRequestStatus.Succeeded,
// },
// ctx
// )
if (!pageId) {
console.log('error creating page in elastic', input)
return {
errorCodes: [SaveErrorCode.Unknown],
}
}
input.clientRequestId = pageId
}
// if (!pageId) {
// console.log('error creating page in elastic', input)
// return {
// errorCodes: [SaveErrorCode.Unknown],
// }
// }
// input.clientRequestId = pageId
// }
return {
clientRequestId: input.clientRequestId,