@next-k8s/tickets 1.0.24 → 1.0.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/coverage/clover.xml +158 -3
  3. package/coverage/coverage-final.json +12 -1
  4. package/coverage/lcov-report/index.html +101 -11
  5. package/coverage/lcov-report/src/app.ts.html +17 -17
  6. package/coverage/lcov-report/src/events/listeners/_queue-group-name.ts.html +88 -0
  7. package/coverage/lcov-report/src/events/listeners/index.html +131 -0
  8. package/coverage/lcov-report/src/events/listeners/order-created.ts.html +151 -0
  9. package/coverage/lcov-report/src/events/publishers/created.ts.html +106 -0
  10. package/coverage/lcov-report/src/events/publishers/index.html +131 -0
  11. package/coverage/lcov-report/src/events/publishers/updated.ts.html +106 -0
  12. package/coverage/lcov-report/src/index.html +1 -1
  13. package/coverage/lcov-report/src/models/index.html +5 -5
  14. package/coverage/lcov-report/src/models/ticket.ts.html +35 -11
  15. package/coverage/lcov-report/src/routes/create.ts.html +12 -12
  16. package/coverage/lcov-report/src/routes/find.ts.html +1 -1
  17. package/coverage/lcov-report/src/routes/get.ts.html +25 -10
  18. package/coverage/lcov-report/src/routes/index.html +8 -23
  19. package/coverage/lcov-report/src/routes/index.ts.html +8 -11
  20. package/coverage/lcov-report/src/routes/update.ts.html +13 -16
  21. package/coverage/lcov-report/src/test/index.html +1 -1
  22. package/coverage/lcov-report/src/test/utils.ts.html +3 -3
  23. package/coverage/lcov-report/ticket.ts.html +220 -0
  24. package/coverage/lcov.info +248 -0
  25. package/package.json +4 -3
  26. package/pnpm-lock.yaml +334 -18
  27. package/src/events/listeners/__test__/order-created.test.ts +44 -0
  28. package/src/events/listeners/_queue-group-name.ts +1 -0
  29. package/src/events/listeners/order-created.ts +22 -0
  30. package/src/models/__test__/ticket.test.ts +37 -0
  31. package/src/models/ticket.ts +11 -3
  32. package/src/routes/__test__/get.test.ts +15 -0
  33. package/src/routes/create.ts +2 -2
  34. package/src/routes/get.ts +5 -0
  35. package/src/routes/index.ts +1 -2
  36. package/src/routes/update.ts +2 -3
  37. package/src/routes/__test__/find.test.ts +0 -22
  38. package/src/routes/find.ts +0 -11
@@ -0,0 +1,44 @@
1
+ import mongoose from 'mongoose'
2
+ import { Message } from 'node-nats-streaming'
3
+ import { OrderCreatedEvent, OrderStatus } from '@next-k8s/common'
4
+
5
+ import Ticket from '../../../models/ticket'
6
+ import natsClient from '../../../nats-client'
7
+ import OrderCreatedListener from '../order-created'
8
+
9
+ const setup = async () => {
10
+ const listener = new OrderCreatedListener(natsClient.client)
11
+ const ticket = new Ticket({ title: 'Test Ticket', price: 21000, owner: new mongoose.Types.ObjectId().toHexString() })
12
+ await ticket.save()
13
+
14
+ const data: OrderCreatedEvent['data'] = {
15
+ id: new mongoose.Types.ObjectId().toHexString(),
16
+ version: 0,
17
+ status: OrderStatus.Created,
18
+ owner: new mongoose.Types.ObjectId().toHexString(),
19
+ expiresAt: new Date().toUTCString(),
20
+ ticket: {
21
+ id: ticket.id,
22
+ price: ticket.price
23
+ }
24
+ }
25
+
26
+ // @ts-ignore
27
+ const message: Message = { ack: jest.fn() }
28
+ return { data, listener, message, ticket }
29
+ }
30
+
31
+ describe('[Order Created] Listener', () => {
32
+ it('should set the ticket owner', async () => {
33
+ const { data, listener, message, ticket } = await setup()
34
+ await listener.onMessage(data, message)
35
+ const updatedTicket = await Ticket.findById(ticket.id)
36
+ expect(updatedTicket.orderId).toBe(data.id)
37
+ })
38
+
39
+ it('should ack the message', async () =>{
40
+ const { data, listener, message } = await setup()
41
+ await listener.onMessage(data, message)
42
+ expect(message.ack).toHaveBeenCalled()
43
+ })
44
+ })
@@ -0,0 +1 @@
1
+ export default 'tickets-service'
@@ -0,0 +1,22 @@
1
+ import { Listener, NotFoundError, OrderCreatedEvent, Subjects } from '@next-k8s/common'
2
+ import { Message } from 'node-nats-streaming'
3
+ import Ticket from '../../models/ticket'
4
+ import TicketUpdatedPublisher from '../publishers/updated'
5
+
6
+ import queueGroupName from './_queue-group-name'
7
+
8
+ export class OrderCreatedListener extends Listener<OrderCreatedEvent> {
9
+ readonly subject = Subjects.OrderCreated
10
+ queueGroupName = queueGroupName
11
+
12
+ async onMessage (data: OrderCreatedEvent['data'], msg: Message) {
13
+ const ticket = await Ticket.findById(data.ticket.id)
14
+ if (!ticket) throw new NotFoundError('Ticket not found')
15
+ ticket.set({ orderId: data.id })
16
+ await ticket.save()
17
+
18
+ msg.ack()
19
+ }
20
+ }
21
+
22
+ export default OrderCreatedListener
@@ -0,0 +1,37 @@
1
+ import mongoose from 'mongoose'
2
+ import Ticket from '../ticket'
3
+
4
+ describe('[Models] Ticket Model', () => {
5
+ it('implements optimistic concurrency control', async () => {
6
+ const ticket = new Ticket({ title: 'Test Ticket', price: 20000, owner: new mongoose.Types.ObjectId().toHexString() })
7
+ await ticket.save()
8
+
9
+ const copies = [
10
+ await Ticket.findById(ticket.id),
11
+ await Ticket.findById(ticket.id)
12
+ ]
13
+
14
+ copies[0].set({ price: 15000 })
15
+ copies[1].set({ price: 23000 })
16
+
17
+ await copies[0].save()
18
+
19
+ try {
20
+ await copies[1].save()
21
+ } catch {
22
+ return
23
+ }
24
+
25
+ throw new Error('Ticket was saved when it should not have been')
26
+ })
27
+
28
+ it('increments the version number on update', async () => {
29
+ const ticket = new Ticket({ title: 'Test Ticket', price: 20000, owner: new mongoose.Types.ObjectId().toHexString() })
30
+ await ticket.save()
31
+ expect(ticket.version).toEqual(0)
32
+ await ticket.save()
33
+ expect(ticket.version).toEqual(1)
34
+ await ticket.save()
35
+ expect(ticket.version).toEqual(2)
36
+ })
37
+ })
@@ -1,9 +1,11 @@
1
1
  import mongoose, { ObjectId } from 'mongoose'
2
+ import { updateIfCurrentPlugin } from 'mongoose-update-if-current'
2
3
 
3
4
  interface TicketAttributes {
4
- title: String;
5
- price: Number;
5
+ title: string;
6
+ price: number;
6
7
  owner: string;
8
+ orderId?: string;
7
9
  createdAt?: Date;
8
10
  updatedAt?: Date;
9
11
  }
@@ -22,10 +24,13 @@ const ticketSchema = new mongoose.Schema({
22
24
  owner: {
23
25
  type: mongoose.Schema.Types.ObjectId,
24
26
  required: true
27
+ },
28
+
29
+ orderId: {
30
+ type: String
25
31
  }
26
32
  }, {
27
33
  toJSON: {
28
- versionKey: false,
29
34
  transform (doc, ret) {
30
35
  ret.id = ret._id
31
36
  delete ret._id
@@ -34,6 +39,9 @@ const ticketSchema = new mongoose.Schema({
34
39
  }
35
40
  })
36
41
 
42
+ ticketSchema.set('versionKey', 'version')
43
+ ticketSchema.plugin(updateIfCurrentPlugin)
44
+
37
45
  export const TicketModel = mongoose.model('Ticket', ticketSchema)
38
46
  export default class Ticket extends TicketModel {
39
47
  constructor(attributes: TicketAttributes) {
@@ -5,6 +5,21 @@ import { getTokenCookie } from '@next-k8s/common'
5
5
  import app from '../../app'
6
6
  import { createTicket } from '../../test/utils'
7
7
 
8
+ describe('[List Tickets] Route: GET /api/tickets', () => {
9
+ it('should return a list of tickets', async () => {
10
+ const cookie = await getTokenCookie({ id: new mongoose.Types.ObjectId().toHexString() })
11
+ await createTicket(app, cookie)
12
+ await createTicket(app, cookie, 'Test Event 2', 40000)
13
+ const list = await request(app)
14
+ .get('/api/tickets')
15
+ .send()
16
+ .expect(200)
17
+
18
+ expect(list.body.tickets).toBeDefined()
19
+ expect(list.body.tickets.length).toEqual(2)
20
+ })
21
+ })
22
+
8
23
  describe('[Get Ticket] Route: GET /api/tickets/:id', () => {
9
24
  it('should throw a BadRequestError if ticket ID is invalid', async () => {
10
25
  await request(app)
@@ -3,7 +3,7 @@ import { body } from 'express-validator'
3
3
  import { requireAuth, validateRequest } from '@next-k8s/common'
4
4
 
5
5
  import Ticket from '../models/ticket'
6
- import { TicketCreatedPublisher } from '../events/publishers/tickets/created'
6
+ import TicketCreatedPublisher from '../events/publishers/created'
7
7
  import natsClient from '../nats-client'
8
8
 
9
9
  const router = express.Router()
@@ -17,7 +17,7 @@ router.post('/api/tickets', requireAuth, validateInput, validateRequest, async (
17
17
  const { title, price } = req.body
18
18
  const ticket = new Ticket({ title, price, owner: req.currentUser!.id })
19
19
  await ticket.save()
20
- new TicketCreatedPublisher(natsClient.client).publish({ id: ticket.id, title: ticket.title, price: ticket.price, owner: ticket.owner })
20
+ new TicketCreatedPublisher(natsClient.client).publish({ id: ticket.id, version: ticket.version, title: ticket.title, price: ticket.price, owner: ticket.owner })
21
21
  res.status(201).send({ ticket })
22
22
  })
23
23
 
package/src/routes/get.ts CHANGED
@@ -6,6 +6,11 @@ import Ticket from '../models/ticket'
6
6
 
7
7
  const router = express.Router()
8
8
 
9
+ router.get('/api/tickets', async (req: Request, res: Response) => {
10
+ const tickets = await Ticket.find({})
11
+ res.send({ tickets })
12
+ })
13
+
9
14
  router.get('/api/tickets/:id', async (req: Request, res: Response) => {
10
15
  if (!isValidObjectId(req.params.id)) throw new BadRequestError('Invalid Ticket ID')
11
16
  const ticket = await Ticket.findById(req.params.id)
@@ -1,6 +1,5 @@
1
1
  import getTicket from './get'
2
- import findTickets from './find'
3
2
  import createTicket from './create'
4
3
  import updateTicket from './update'
5
4
 
6
- export default [findTickets, getTicket, createTicket, updateTicket]
5
+ export default [getTicket, createTicket, updateTicket]
@@ -2,9 +2,8 @@ import { BadRequestError, NotFoundError, UnauthorizedError, validateRequest, req
2
2
  import express, { Request, Response } from 'express'
3
3
  import { body } from 'express-validator'
4
4
  import { isValidObjectId } from 'mongoose'
5
-
6
5
  import Ticket from '../models/ticket'
7
- import TicketUpdatedPublisher from '../events/publishers/tickets/updated'
6
+ import TicketUpdatedPublisher from '../events/publishers/updated'
8
7
  import natsClient from '../nats-client'
9
8
 
10
9
  const router = express.Router()
@@ -22,7 +21,7 @@ router.put('/api/tickets/:id', requireAuth, validateInput, validateRequest, asyn
22
21
  const { title, price } = req.body
23
22
  ticket.set({ title, price })
24
23
  await ticket.save()
25
- await new TicketUpdatedPublisher(natsClient.client).publish({ id: ticket.id, title: ticket.title, price: ticket.price, owner: ticket.owner })
24
+ await new TicketUpdatedPublisher(natsClient.client).publish({ id: ticket.id, version: ticket.version, title: ticket.title, price: ticket.price, owner: ticket.owner })
26
25
  res.json(ticket)
27
26
  })
28
27
 
@@ -1,22 +0,0 @@
1
-
2
- import mongoose from 'mongoose'
3
- import request from 'supertest'
4
- import { getTokenCookie } from '@next-k8s/common'
5
-
6
- import app from '../../app'
7
- import { createTicket } from '../../test/utils'
8
-
9
- describe('[List Tickets] Route: GET /api/tickets', () => {
10
- it('should return a list of tickets', async () => {
11
- const cookie = await getTokenCookie({ id: new mongoose.Types.ObjectId().toHexString() })
12
- await createTicket(app, cookie)
13
- await createTicket(app, cookie, 'Test Event 2', 40000)
14
- const list = await request(app)
15
- .get('/api/tickets')
16
- .send()
17
- .expect(200)
18
-
19
- expect(list.body.tickets).toBeDefined()
20
- expect(list.body.tickets.length).toEqual(2)
21
- })
22
- })
@@ -1,11 +0,0 @@
1
- import express, { Request, Response } from 'express'
2
- import Ticket from '../models/ticket'
3
-
4
- const router = express.Router()
5
-
6
- router.get('/api/tickets', async (req: Request, res: Response) => {
7
- const tickets = await Ticket.find({})
8
- res.send({ tickets })
9
- })
10
-
11
- export default router