mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
add tests for create/delete labels
This commit is contained in:
parent
e74f334db9
commit
9150e7dbc2
7 changed files with 94 additions and 92 deletions
|
|
@ -31,7 +31,7 @@ export class Label extends BaseEntity {
|
|||
@Column('text')
|
||||
color!: string
|
||||
|
||||
@Column('text')
|
||||
@Column('text', { nullable: true })
|
||||
description?: string
|
||||
|
||||
@CreateDateColumn()
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import {
|
|||
import { MembershipTier, RegistrationType } from '../datalayer/user/model'
|
||||
import { NewsletterEmail } from './newsletter_email'
|
||||
import { Profile } from './profile'
|
||||
import { Label } from './label'
|
||||
|
||||
@Entity()
|
||||
export class User extends BaseEntity {
|
||||
|
|
@ -46,4 +47,7 @@ export class User extends BaseEntity {
|
|||
|
||||
@Column('varchar', { length: 255, nullable: true })
|
||||
password?: string
|
||||
|
||||
@OneToMany(() => Label, (label) => label.user)
|
||||
labels?: Label[]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -276,12 +276,14 @@ export type CreateLabelError = {
|
|||
|
||||
export enum CreateLabelErrorCode {
|
||||
BadRequest = 'BAD_REQUEST',
|
||||
LabelAlreadyExists = 'LABEL_ALREADY_EXISTS',
|
||||
NotFound = 'NOT_FOUND',
|
||||
Unauthorized = 'UNAUTHORIZED'
|
||||
}
|
||||
|
||||
export type CreateLabelInput = {
|
||||
linkId: Scalars['ID'];
|
||||
color: Scalars['String'];
|
||||
description?: InputMaybe<Scalars['String']>;
|
||||
name: Scalars['String'];
|
||||
};
|
||||
|
||||
|
|
@ -624,6 +626,9 @@ export type HighlightStats = {
|
|||
|
||||
export type Label = {
|
||||
__typename?: 'Label';
|
||||
color: Scalars['String'];
|
||||
createdAt: Scalars['Date'];
|
||||
description?: Maybe<Scalars['String']>;
|
||||
id: Scalars['ID'];
|
||||
name: Scalars['String'];
|
||||
};
|
||||
|
|
@ -1120,11 +1125,6 @@ export type QueryGetFollowingArgs = {
|
|||
};
|
||||
|
||||
|
||||
export type QueryLabelsArgs = {
|
||||
linkId: Scalars['ID'];
|
||||
};
|
||||
|
||||
|
||||
export type QueryReminderArgs = {
|
||||
linkId: Scalars['ID'];
|
||||
};
|
||||
|
|
@ -2762,6 +2762,9 @@ export type HighlightStatsResolvers<ContextType = ResolverContext, ParentType ex
|
|||
};
|
||||
|
||||
export type LabelResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['Label'] = ResolversParentTypes['Label']> = {
|
||||
color?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
createdAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
|
||||
description?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
||||
name?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
|
|
@ -2955,7 +2958,7 @@ export type QueryResolvers<ContextType = ResolverContext, ParentType extends Res
|
|||
getFollowing?: Resolver<ResolversTypes['GetFollowingResult'], ParentType, ContextType, Partial<QueryGetFollowingArgs>>;
|
||||
getUserPersonalization?: Resolver<ResolversTypes['GetUserPersonalizationResult'], ParentType, ContextType>;
|
||||
hello?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
labels?: Resolver<ResolversTypes['LabelsResult'], ParentType, ContextType, RequireFields<QueryLabelsArgs, 'linkId'>>;
|
||||
labels?: Resolver<ResolversTypes['LabelsResult'], ParentType, ContextType>;
|
||||
me?: Resolver<Maybe<ResolversTypes['User']>, ParentType, ContextType>;
|
||||
newsletterEmails?: Resolver<ResolversTypes['NewsletterEmailsResult'], ParentType, ContextType>;
|
||||
reminder?: Resolver<ResolversTypes['ReminderResult'], ParentType, ContextType, RequireFields<QueryReminderArgs, 'linkId'>>;
|
||||
|
|
|
|||
|
|
@ -234,12 +234,14 @@ type CreateLabelError {
|
|||
|
||||
enum CreateLabelErrorCode {
|
||||
BAD_REQUEST
|
||||
LABEL_ALREADY_EXISTS
|
||||
NOT_FOUND
|
||||
UNAUTHORIZED
|
||||
}
|
||||
|
||||
input CreateLabelInput {
|
||||
linkId: ID!
|
||||
color: String!
|
||||
description: String
|
||||
name: String!
|
||||
}
|
||||
|
||||
|
|
@ -548,6 +550,9 @@ type HighlightStats {
|
|||
}
|
||||
|
||||
type Label {
|
||||
color: String!
|
||||
createdAt: Date!
|
||||
description: String
|
||||
id: ID!
|
||||
name: String!
|
||||
}
|
||||
|
|
@ -787,7 +792,7 @@ type Query {
|
|||
getFollowing(userId: ID): GetFollowingResult!
|
||||
getUserPersonalization: GetUserPersonalizationResult!
|
||||
hello: String
|
||||
labels(linkId: ID!): LabelsResult!
|
||||
labels: LabelsResult!
|
||||
me: User
|
||||
newsletterEmails: NewsletterEmailsResult!
|
||||
reminder(linkId: ID!): ReminderResult!
|
||||
|
|
|
|||
|
|
@ -11,57 +11,47 @@ import {
|
|||
LabelsSuccess,
|
||||
MutationCreateLabelArgs,
|
||||
MutationDeleteLabelArgs,
|
||||
QueryLabelsArgs,
|
||||
} from '../../generated/graphql'
|
||||
import { analytics } from '../../utils/analytics'
|
||||
import { env } from '../../env'
|
||||
import { User } from '../../entity/user'
|
||||
import { Link } from '../../entity/link'
|
||||
import { Label } from '../../entity/label'
|
||||
import { getManager, getRepository } from 'typeorm'
|
||||
import { setClaims } from '../../entity/utils'
|
||||
|
||||
export const labelsResolver = authorized<
|
||||
LabelsSuccess,
|
||||
LabelsError,
|
||||
QueryLabelsArgs
|
||||
>(async (_, { linkId }, { claims: { uid }, log }) => {
|
||||
log.info('labelsResolver')
|
||||
export const labelsResolver = authorized<LabelsSuccess, LabelsError>(
|
||||
async (_obj, _params, { claims: { uid }, log }) => {
|
||||
log.info('labelsResolver')
|
||||
|
||||
analytics.track({
|
||||
userId: uid,
|
||||
event: 'labels',
|
||||
properties: {
|
||||
linkId: linkId,
|
||||
env: env.server.apiEnv,
|
||||
},
|
||||
})
|
||||
analytics.track({
|
||||
userId: uid,
|
||||
event: 'labels',
|
||||
properties: {
|
||||
env: env.server.apiEnv,
|
||||
},
|
||||
})
|
||||
|
||||
try {
|
||||
const user = await User.findOne(uid)
|
||||
if (!user) {
|
||||
return {
|
||||
errorCodes: [LabelsErrorCode.Unauthorized],
|
||||
try {
|
||||
const user = await User.findOne(uid, {
|
||||
relations: ['labels'],
|
||||
})
|
||||
if (!user) {
|
||||
return {
|
||||
errorCodes: [LabelsErrorCode.Unauthorized],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const link = await Link.findOne(linkId, { relations: ['labels'] })
|
||||
if (!link) {
|
||||
return {
|
||||
errorCodes: [LabelsErrorCode.NotFound],
|
||||
labels: user.labels || [],
|
||||
}
|
||||
} catch (error) {
|
||||
log.error(error)
|
||||
return {
|
||||
errorCodes: [LabelsErrorCode.BadRequest],
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
labels: link.labels || [],
|
||||
}
|
||||
} catch (error) {
|
||||
log.error(error)
|
||||
return {
|
||||
errorCodes: [LabelsErrorCode.BadRequest],
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
export const createLabelResolver = authorized<
|
||||
CreateLabelSuccess,
|
||||
|
|
@ -70,7 +60,7 @@ export const createLabelResolver = authorized<
|
|||
>(async (_, { input }, { claims: { uid }, log }) => {
|
||||
log.info('createLabelResolver')
|
||||
|
||||
const { linkId, name } = input
|
||||
const { name, color, description } = input
|
||||
|
||||
try {
|
||||
const user = await getRepository(User).findOne(uid)
|
||||
|
|
@ -80,18 +70,23 @@ export const createLabelResolver = authorized<
|
|||
}
|
||||
}
|
||||
|
||||
const link = await getRepository(Link).findOne(linkId)
|
||||
if (!link) {
|
||||
const existingLabel = await getRepository(Label).findOne({
|
||||
where: {
|
||||
name,
|
||||
},
|
||||
})
|
||||
if (existingLabel) {
|
||||
return {
|
||||
errorCodes: [CreateLabelErrorCode.NotFound],
|
||||
errorCodes: [CreateLabelErrorCode.LabelAlreadyExists],
|
||||
}
|
||||
}
|
||||
|
||||
const label = await getRepository(Label)
|
||||
.create({
|
||||
user,
|
||||
link,
|
||||
name,
|
||||
color,
|
||||
description: description || '',
|
||||
})
|
||||
.save()
|
||||
|
||||
|
|
@ -99,8 +94,9 @@ export const createLabelResolver = authorized<
|
|||
userId: uid,
|
||||
event: 'createLabel',
|
||||
properties: {
|
||||
linkId,
|
||||
name,
|
||||
color,
|
||||
description,
|
||||
env: env.server.apiEnv,
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1242,6 +1242,9 @@ const schema = gql`
|
|||
type Label {
|
||||
id: ID!
|
||||
name: String!
|
||||
color: String!
|
||||
description: String
|
||||
createdAt: Date!
|
||||
}
|
||||
|
||||
type LabelsSuccess {
|
||||
|
|
@ -1261,8 +1264,9 @@ const schema = gql`
|
|||
union LabelsResult = LabelsSuccess | LabelsError
|
||||
|
||||
input CreateLabelInput {
|
||||
linkId: ID!
|
||||
name: String!
|
||||
color: String!
|
||||
description: String
|
||||
}
|
||||
|
||||
type CreateLabelSuccess {
|
||||
|
|
@ -1273,6 +1277,7 @@ const schema = gql`
|
|||
UNAUTHORIZED
|
||||
BAD_REQUEST
|
||||
NOT_FOUND
|
||||
LABEL_ALREADY_EXISTS
|
||||
}
|
||||
|
||||
type CreateLabelError {
|
||||
|
|
@ -1414,7 +1419,7 @@ const schema = gql`
|
|||
articleSavingRequest(id: ID!): ArticleSavingRequestResult!
|
||||
newsletterEmails: NewsletterEmailsResult!
|
||||
reminder(linkId: ID!): ReminderResult!
|
||||
labels(linkId: ID!): LabelsResult!
|
||||
labels: LabelsResult!
|
||||
}
|
||||
`
|
||||
|
||||
|
|
|
|||
|
|
@ -36,14 +36,14 @@ describe('Labels API', () => {
|
|||
.create({
|
||||
name: 'label1',
|
||||
user: user,
|
||||
link: link,
|
||||
color: '#ffffff',
|
||||
})
|
||||
.save()
|
||||
const label2 = await getRepository(Label)
|
||||
.create({
|
||||
name: 'label2',
|
||||
user: user,
|
||||
link: link,
|
||||
color: '#eeeeee',
|
||||
})
|
||||
.save()
|
||||
labels = [label1, label2]
|
||||
|
|
@ -56,16 +56,18 @@ describe('Labels API', () => {
|
|||
|
||||
describe('GET labels', () => {
|
||||
let query: string
|
||||
let linkId: string
|
||||
|
||||
beforeEach(() => {
|
||||
query = `
|
||||
query {
|
||||
labels(linkId: "${linkId}") {
|
||||
labels {
|
||||
... on LabelsSuccess {
|
||||
labels {
|
||||
id
|
||||
name
|
||||
color
|
||||
description
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
... on LabelsError {
|
||||
|
|
@ -76,33 +78,18 @@ describe('Labels API', () => {
|
|||
`
|
||||
})
|
||||
|
||||
context('when link exists', () => {
|
||||
before(() => {
|
||||
linkId = link.id
|
||||
})
|
||||
it('should return labels', async () => {
|
||||
const res = await graphqlRequest(query, authToken).expect(200)
|
||||
|
||||
it('should return labels', async () => {
|
||||
const res = await graphqlRequest(query, authToken).expect(200)
|
||||
|
||||
expect(res.body.data.labels.labels).to.eql(
|
||||
labels.map((label) => ({
|
||||
id: label.id,
|
||||
name: label.name,
|
||||
}))
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
context('when link not exist', () => {
|
||||
before(() => {
|
||||
linkId = generateFakeUuid()
|
||||
})
|
||||
|
||||
it('should return error code NOT_FOUND', async () => {
|
||||
const res = await graphqlRequest(query, authToken).expect(200)
|
||||
|
||||
expect(res.body.data.labels.errorCodes).to.eql(['NOT_FOUND'])
|
||||
})
|
||||
expect(res.body.data.labels.labels).to.eql(
|
||||
labels.map((label) => ({
|
||||
id: label.id,
|
||||
name: label.name,
|
||||
color: label.color,
|
||||
description: label.description,
|
||||
createdAt: new Date(label.createdAt.setMilliseconds(0)).toISOString(),
|
||||
}))
|
||||
)
|
||||
})
|
||||
|
||||
it('responds status code 400 when invalid query', async () => {
|
||||
|
|
@ -122,15 +109,15 @@ describe('Labels API', () => {
|
|||
|
||||
describe('Create label', () => {
|
||||
let query: string
|
||||
let linkId: string
|
||||
let name: string
|
||||
|
||||
beforeEach(() => {
|
||||
query = `
|
||||
mutation {
|
||||
createLabel(
|
||||
input: {
|
||||
linkId: "${linkId}",
|
||||
name: "label3"
|
||||
color: "#ffffff"
|
||||
name: "${name}"
|
||||
}
|
||||
) {
|
||||
... on CreateLabelSuccess {
|
||||
|
|
@ -147,9 +134,9 @@ describe('Labels API', () => {
|
|||
`
|
||||
})
|
||||
|
||||
context('when link exists', () => {
|
||||
context('when name not exists', () => {
|
||||
before(() => {
|
||||
linkId = link.id
|
||||
name = 'label3'
|
||||
})
|
||||
|
||||
it('should create label', async () => {
|
||||
|
|
@ -161,15 +148,17 @@ describe('Labels API', () => {
|
|||
})
|
||||
})
|
||||
|
||||
context('when link not exist', () => {
|
||||
context('when name exists', () => {
|
||||
before(() => {
|
||||
linkId = generateFakeUuid()
|
||||
name = labels[0].name
|
||||
})
|
||||
|
||||
it('should return error code NOT_FOUND', async () => {
|
||||
it('should return error code LABEL_ALREADY_EXISTS', async () => {
|
||||
const res = await graphqlRequest(query, authToken).expect(200)
|
||||
|
||||
expect(res.body.data.createLabel.errorCodes).to.eql(['NOT_FOUND'])
|
||||
expect(res.body.data.createLabel.errorCodes).to.eql([
|
||||
'LABEL_ALREADY_EXISTS',
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue