From 9150e7dbc2ce4763708099e71994ee509b2dccad Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Mon, 21 Feb 2022 16:53:04 +0800 Subject: [PATCH] add tests for create/delete labels --- packages/api/src/entity/label.ts | 2 +- packages/api/src/entity/user.ts | 4 ++ packages/api/src/generated/graphql.ts | 17 +++-- packages/api/src/generated/schema.graphql | 9 ++- packages/api/src/resolvers/labels/index.ts | 78 ++++++++++------------ packages/api/src/schema.ts | 9 ++- packages/api/test/resolvers/labels.test.ts | 67 ++++++++----------- 7 files changed, 94 insertions(+), 92 deletions(-) diff --git a/packages/api/src/entity/label.ts b/packages/api/src/entity/label.ts index 27ee04da9..dec2046f4 100644 --- a/packages/api/src/entity/label.ts +++ b/packages/api/src/entity/label.ts @@ -31,7 +31,7 @@ export class Label extends BaseEntity { @Column('text') color!: string - @Column('text') + @Column('text', { nullable: true }) description?: string @CreateDateColumn() diff --git a/packages/api/src/entity/user.ts b/packages/api/src/entity/user.ts index cc6af11c3..5aa1e5d05 100644 --- a/packages/api/src/entity/user.ts +++ b/packages/api/src/entity/user.ts @@ -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[] } diff --git a/packages/api/src/generated/graphql.ts b/packages/api/src/generated/graphql.ts index 622bf586d..e854ff299 100644 --- a/packages/api/src/generated/graphql.ts +++ b/packages/api/src/generated/graphql.ts @@ -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; name: Scalars['String']; }; @@ -624,6 +626,9 @@ export type HighlightStats = { export type Label = { __typename?: 'Label'; + color: Scalars['String']; + createdAt: Scalars['Date']; + description?: Maybe; 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 = { + color?: Resolver; + createdAt?: Resolver; + description?: Resolver, ParentType, ContextType>; id?: Resolver; name?: Resolver; __isTypeOf?: IsTypeOfResolverFn; @@ -2955,7 +2958,7 @@ export type QueryResolvers>; getUserPersonalization?: Resolver; hello?: Resolver, ParentType, ContextType>; - labels?: Resolver>; + labels?: Resolver; me?: Resolver, ParentType, ContextType>; newsletterEmails?: Resolver; reminder?: Resolver>; diff --git a/packages/api/src/generated/schema.graphql b/packages/api/src/generated/schema.graphql index 55558990d..a6288950d 100644 --- a/packages/api/src/generated/schema.graphql +++ b/packages/api/src/generated/schema.graphql @@ -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! diff --git a/packages/api/src/resolvers/labels/index.ts b/packages/api/src/resolvers/labels/index.ts index e83e29b84..2852171e7 100644 --- a/packages/api/src/resolvers/labels/index.ts +++ b/packages/api/src/resolvers/labels/index.ts @@ -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( + 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, }, }) diff --git a/packages/api/src/schema.ts b/packages/api/src/schema.ts index b746475bc..ce3613b72 100755 --- a/packages/api/src/schema.ts +++ b/packages/api/src/schema.ts @@ -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! } ` diff --git a/packages/api/test/resolvers/labels.test.ts b/packages/api/test/resolvers/labels.test.ts index 339411944..8fe06fd8e 100644 --- a/packages/api/test/resolvers/labels.test.ts +++ b/packages/api/test/resolvers/labels.test.ts @@ -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', + ]) }) })