Merge pull request #634 from omnivore-app/OMN-626

[Omn-626] - API to edit title and/or description
This commit is contained in:
Jackson Harper 2022-05-24 09:20:15 -07:00 committed by GitHub
commit 8e30d793e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 237 additions and 0 deletions

View file

@ -823,6 +823,7 @@ export type Mutation = {
updateHighlightReply: UpdateHighlightReplyResult;
updateLabel: UpdateLabelResult;
updateLinkShareInfo: UpdateLinkShareInfoResult;
updatePage: UpdatePageResult;
updateReminder: UpdateReminderResult;
updateSharedComment: UpdateSharedCommentResult;
updateUser: UpdateUserResult;
@ -1021,6 +1022,11 @@ export type MutationUpdateLinkShareInfoArgs = {
};
export type MutationUpdatePageArgs = {
input: UpdatePageInput;
};
export type MutationUpdateReminderArgs = {
input: UpdateReminderInput;
};
@ -1822,6 +1828,32 @@ export type UpdateLinkShareInfoSuccess = {
message: Scalars['String'];
};
export type UpdatePageError = {
__typename?: 'UpdatePageError';
errorCodes: Array<UpdatePageErrorCode>;
};
export enum UpdatePageErrorCode {
BadRequest = 'BAD_REQUEST',
Forbidden = 'FORBIDDEN',
NotFound = 'NOT_FOUND',
Unauthorized = 'UNAUTHORIZED',
UpdateFailed = 'UPDATE_FAILED'
}
export type UpdatePageInput = {
description?: InputMaybe<Scalars['String']>;
pageId: Scalars['ID'];
title?: InputMaybe<Scalars['String']>;
};
export type UpdatePageResult = UpdatePageError | UpdatePageSuccess;
export type UpdatePageSuccess = {
__typename?: 'UpdatePageSuccess';
updatedPage: Page;
};
export type UpdateReminderError = {
__typename?: 'UpdateReminderError';
errorCodes: Array<UpdateReminderErrorCode>;
@ -2352,6 +2384,11 @@ export type ResolversTypes = {
UpdateLinkShareInfoInput: UpdateLinkShareInfoInput;
UpdateLinkShareInfoResult: ResolversTypes['UpdateLinkShareInfoError'] | ResolversTypes['UpdateLinkShareInfoSuccess'];
UpdateLinkShareInfoSuccess: ResolverTypeWrapper<UpdateLinkShareInfoSuccess>;
UpdatePageError: ResolverTypeWrapper<UpdatePageError>;
UpdatePageErrorCode: UpdatePageErrorCode;
UpdatePageInput: UpdatePageInput;
UpdatePageResult: ResolversTypes['UpdatePageError'] | ResolversTypes['UpdatePageSuccess'];
UpdatePageSuccess: ResolverTypeWrapper<UpdatePageSuccess>;
UpdateReminderError: ResolverTypeWrapper<UpdateReminderError>;
UpdateReminderErrorCode: UpdateReminderErrorCode;
UpdateReminderInput: UpdateReminderInput;
@ -2605,6 +2642,10 @@ export type ResolversParentTypes = {
UpdateLinkShareInfoInput: UpdateLinkShareInfoInput;
UpdateLinkShareInfoResult: ResolversParentTypes['UpdateLinkShareInfoError'] | ResolversParentTypes['UpdateLinkShareInfoSuccess'];
UpdateLinkShareInfoSuccess: UpdateLinkShareInfoSuccess;
UpdatePageError: UpdatePageError;
UpdatePageInput: UpdatePageInput;
UpdatePageResult: ResolversParentTypes['UpdatePageError'] | ResolversParentTypes['UpdatePageSuccess'];
UpdatePageSuccess: UpdatePageSuccess;
UpdateReminderError: UpdateReminderError;
UpdateReminderInput: UpdateReminderInput;
UpdateReminderResult: ResolversParentTypes['UpdateReminderError'] | ResolversParentTypes['UpdateReminderSuccess'];
@ -3236,6 +3277,7 @@ export type MutationResolvers<ContextType = ResolverContext, ParentType extends
updateHighlightReply?: Resolver<ResolversTypes['UpdateHighlightReplyResult'], ParentType, ContextType, RequireFields<MutationUpdateHighlightReplyArgs, 'input'>>;
updateLabel?: Resolver<ResolversTypes['UpdateLabelResult'], ParentType, ContextType, RequireFields<MutationUpdateLabelArgs, 'input'>>;
updateLinkShareInfo?: Resolver<ResolversTypes['UpdateLinkShareInfoResult'], ParentType, ContextType, RequireFields<MutationUpdateLinkShareInfoArgs, 'input'>>;
updatePage?: Resolver<ResolversTypes['UpdatePageResult'], ParentType, ContextType, RequireFields<MutationUpdatePageArgs, 'input'>>;
updateReminder?: Resolver<ResolversTypes['UpdateReminderResult'], ParentType, ContextType, RequireFields<MutationUpdateReminderArgs, 'input'>>;
updateSharedComment?: Resolver<ResolversTypes['UpdateSharedCommentResult'], ParentType, ContextType, RequireFields<MutationUpdateSharedCommentArgs, 'input'>>;
updateUser?: Resolver<ResolversTypes['UpdateUserResult'], ParentType, ContextType, RequireFields<MutationUpdateUserArgs, 'input'>>;
@ -3692,6 +3734,20 @@ export type UpdateLinkShareInfoSuccessResolvers<ContextType = ResolverContext, P
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
export type UpdatePageErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['UpdatePageError'] = ResolversParentTypes['UpdatePageError']> = {
errorCodes?: Resolver<Array<ResolversTypes['UpdatePageErrorCode']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
export type UpdatePageResultResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['UpdatePageResult'] = ResolversParentTypes['UpdatePageResult']> = {
__resolveType: TypeResolveFn<'UpdatePageError' | 'UpdatePageSuccess', ParentType, ContextType>;
};
export type UpdatePageSuccessResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['UpdatePageSuccess'] = ResolversParentTypes['UpdatePageSuccess']> = {
updatedPage?: Resolver<ResolversTypes['Page'], ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
export type UpdateReminderErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['UpdateReminderError'] = ResolversParentTypes['UpdateReminderError']> = {
errorCodes?: Resolver<Array<ResolversTypes['UpdateReminderErrorCode']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
@ -3996,6 +4052,9 @@ export type Resolvers<ContextType = ResolverContext> = {
UpdateLinkShareInfoError?: UpdateLinkShareInfoErrorResolvers<ContextType>;
UpdateLinkShareInfoResult?: UpdateLinkShareInfoResultResolvers<ContextType>;
UpdateLinkShareInfoSuccess?: UpdateLinkShareInfoSuccessResolvers<ContextType>;
UpdatePageError?: UpdatePageErrorResolvers<ContextType>;
UpdatePageResult?: UpdatePageResultResolvers<ContextType>;
UpdatePageSuccess?: UpdatePageSuccessResolvers<ContextType>;
UpdateReminderError?: UpdateReminderErrorResolvers<ContextType>;
UpdateReminderResult?: UpdateReminderResultResolvers<ContextType>;
UpdateReminderSuccess?: UpdateReminderSuccessResolvers<ContextType>;

View file

@ -733,6 +733,7 @@ type Mutation {
updateHighlightReply(input: UpdateHighlightReplyInput!): UpdateHighlightReplyResult!
updateLabel(input: UpdateLabelInput!): UpdateLabelResult!
updateLinkShareInfo(input: UpdateLinkShareInfoInput!): UpdateLinkShareInfoResult!
updatePage(input: UpdatePageInput!): UpdatePageResult!
updateReminder(input: UpdateReminderInput!): UpdateReminderResult!
updateSharedComment(input: UpdateSharedCommentInput!): UpdateSharedCommentResult!
updateUser(input: UpdateUserInput!): UpdateUserResult!
@ -1388,6 +1389,30 @@ type UpdateLinkShareInfoSuccess {
message: String!
}
type UpdatePageError {
errorCodes: [UpdatePageErrorCode!]!
}
enum UpdatePageErrorCode {
BAD_REQUEST
FORBIDDEN
NOT_FOUND
UNAUTHORIZED
UPDATE_FAILED
}
input UpdatePageInput {
description: String
pageId: ID!
title: String
}
union UpdatePageResult = UpdatePageError | UpdatePageSuccess
type UpdatePageSuccess {
updatedPage: Page!
}
type UpdateReminderError {
errorCodes: [UpdateReminderErrorCode!]!
}

View file

@ -77,6 +77,7 @@ import {
updateUserResolver,
uploadFileRequestResolver,
validateUsernameResolver,
updatePageResolver,
} from './index'
import { getShareInfoForArticle } from '../datalayer/links/share_info'
import {
@ -146,6 +147,7 @@ export const functionResolvers = {
setLabels: setLabelsResolver,
generateApiKey: generateApiKeyResolver,
unsubscribe: unsubscribeResolver,
updatePage: updatePageResolver,
subscribe: subscribeResolver,
},
Query: {
@ -556,5 +558,6 @@ export const functionResolvers = {
...resultResolveTypeResolver('Subscriptions'),
...resultResolveTypeResolver('Unsubscribe'),
...resultResolveTypeResolver('UpdateLabel'),
...resultResolveTypeResolver('UpdatePage'),
...resultResolveTypeResolver('Subscribe'),
}

View file

@ -15,3 +15,4 @@ export * from './reminders'
export * from './user_device_tokens'
export * from './labels'
export * from './subscriptions'
export * from './update'

View file

@ -0,0 +1,52 @@
import {
UpdatePageError,
UpdatePageErrorCode,
UpdatePageSuccess,
MutationUpdatePageArgs,
} from '../../generated/graphql'
import { authorized, userDataToUser } from '../../utils/helpers'
import { updatePage, getPageById } from '../../elastic/pages'
import { Page } from '../../entity/page'
import { Merge } from '../../util'
export type UpdatePageSuccessPartial = Merge<
UpdatePageSuccess,
{ updatedPage: Partial<Page> }
>
export const updatePageResolver = authorized<
UpdatePageSuccessPartial,
UpdatePageError,
MutationUpdatePageArgs
>(async (_, { input }, ctx) => {
const {
models,
claims: { uid },
} = ctx
const user = userDataToUser(await models.user.get(uid))
if (!user) {
return { errorCodes: [UpdatePageErrorCode.Unauthorized] }
}
const page = await getPageById(input.pageId)
if (!page) return { errorCodes: [UpdatePageErrorCode.NotFound] }
else if (page.userId !== user.id)
return { errorCodes: [UpdatePageErrorCode.Unauthorized] }
const pageData = {
id: input.pageId,
title: input.title || undefined,
description: input.description || '',
}
const updateResult = await updatePage(input.pageId, pageData, { ...ctx, uid })
if (!updateResult) return { errorCodes: [UpdatePageErrorCode.UpdateFailed] }
const updatedPage = (await getPageById(input.pageId)) as unknown as Page
return {
updatedPage: updatedPage,
__typename: 'UpdatePageSuccess',
}
})

View file

@ -498,6 +498,30 @@ const schema = gql`
union SaveResult = SaveSuccess | SaveError
input UpdatePageInput {
pageId: ID!
title: String
description: String
}
type UpdatePageSuccess {
updatedPage: Page!
}
enum UpdatePageErrorCode {
UPDATE_FAILED
UNAUTHORIZED
BAD_REQUEST
NOT_FOUND
FORBIDDEN
}
type UpdatePageError {
errorCodes: [UpdatePageErrorCode!]!
}
union UpdatePageResult = UpdatePageSuccess | UpdatePageError
# Mutation: setFollow
union SetFollowResult = SetFollowSuccess | SetFollowError
input SetFollowInput {
@ -1577,6 +1601,7 @@ const schema = gql`
deleteNewsletterEmail(newsletterEmailId: ID!): DeleteNewsletterEmailResult!
saveUrl(input: SaveUrlInput!): SaveResult!
savePage(input: SavePageInput!): SaveResult!
updatePage(input: UpdatePageInput!): UpdatePageResult!
saveFile(input: SaveFileInput!): SaveResult!
createReminder(input: CreateReminderInput!): CreateReminderResult!
updateReminder(input: UpdateReminderInput!): UpdateReminderResult!

View file

@ -0,0 +1,72 @@
import { createTestUser, deleteTestUser } from '../db'
import {
createTestElasticPage,
graphqlRequest,
request,
} from '../util'
import { expect } from 'chai'
import 'mocha'
import { User } from '../../src/entity/user'
import { Page } from '../../src/elastic/types'
describe('Update API', () => {
const username = 'fakeUser'
let user: User
let authToken: string
let page: Page
before(async () => {
// create test user and login
user = await createTestUser(username)
const res = await request
.post('/local/debug/fake-user-login')
.send({ fakeEmail: user.email })
authToken = res.body.authToken
page = await createTestElasticPage(user)
})
after(async () => {
// clean up
await deleteTestUser(username)
})
describe('update page', () => {
let query: string
let title = "New Title"
let description = "New Description"
beforeEach(() => {
query = `
mutation {
updatePage(
input: {
pageId: "${page.id}"
title: "${title}"
description: "${description}"
}
) {
... on UpdatePageSuccess {
updatedPage {
title
description
}
}
... on UpdatePageError {
errorCodes
}
}
}
`
})
it('should update page', async () => {
const res = await graphqlRequest(query, authToken).expect(200)
const updatedPage = res?.body.data.updatePage.updatedPage
expect(updatedPage?.title).to.eql(title)
expect(updatedPage?.description).to.eql(description)
})
})
})