Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Events V2: implement indexing for events #8087

Merged
merged 11 commits into from
Nov 27, 2024
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ services:
- base
environment:
- MONGO_URL=mongodb://mongo1/events
- ES_HOST=elasticsearch:9200

# User facing services
workflow:
Expand Down
5 changes: 4 additions & 1 deletion packages/events/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"build:clean": "rm -rf build"
},
"dependencies": {
"@elastic/elasticsearch": "8.16.2",
"@opencrvs/commons": "^1.3.0",
"@trpc/server": "^11.0.0-rc.532",
"app-module-path": "^2.2.0",
Expand All @@ -25,21 +26,23 @@
"zod": "^3.23.8"
},
"devDependencies": {
"@testcontainers/elasticsearch": "^10.15.0",
"@typescript-eslint/eslint-plugin": "^4.5.0",
"@typescript-eslint/parser": "^4.5.0",
"cross-env": "^7.0.0",
"eslint": "^7.11.0",
"vite-tsconfig-paths": "^3.5.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-import": "^2.17.3",
"eslint-plugin-prettier": "^4.0.0",
"lint-staged": "^15.0.0",
"mongodb-memory-server": "^10.1.2",
"nodemon": "^3.0.0",
"prettier": "2.8.8",
"testcontainers": "^10.15.0",
"ts-jest": "27.1.4",
"ts-node": "^6.1.1",
"typescript": "4.9.5",
"vite-tsconfig-paths": "^3.5.0",
"vitest": "^2.1.5"
},
"lint-staged": {
Expand Down
3 changes: 2 additions & 1 deletion packages/events/src/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@
import { cleanEnv, url } from 'envalid'

export const env = cleanEnv(process.env, {
MONGO_URL: url({ devDefault: 'mongodb://localhost/events' })
MONGO_URL: url({ devDefault: 'mongodb://localhost/events' }),
ES_HOST: url({ devDefault: 'http://localhost:9200' })
})
40 changes: 18 additions & 22 deletions packages/events/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,29 @@ import { vi } from 'vitest'
import { appRouter, t } from './router'
import {
getClient,
resetServer,
setupServer
resetServer as resetMongoServer,
setupServer as setupMongoServer
} from './storage/__mocks__/mongodb'

import {
resetServer as resetESServer,
setupServer as setupESServer
} from './storage/__mocks__/elasticsearch'

import { getUUID } from '@opencrvs/commons'

const { createCallerFactory } = t

vi.mock('@events/storage/mongodb')
vi.mock('@events/storage/elasticsearch')

beforeAll(async () => {
await setupServer()
})

afterEach(async () => {
resetServer()
})
beforeAll(() => Promise.all([setupMongoServer(), setupESServer()]), 100000)
afterEach(() => Promise.all([resetMongoServer(), resetESServer()]))

function createClient() {
const createCaller = createCallerFactory(appRouter)
const caller = createCaller({
user: { id: '1' }
user: { id: '1', primaryOfficeId: '123' }
})
return caller
}
Expand All @@ -41,8 +43,7 @@ const client = createClient()
test('event can be created and fetched', async () => {
const event = await client.event.create({
transactionId: '1',
type: 'birth',
fields: []
type: 'birth'
})

const fetchedEvent = await client.event.get(event.id)
Expand All @@ -55,14 +56,12 @@ test('creating an event is an idempotent operation', async () => {

await client.event.create({
transactionId: '1',
type: 'birth',
fields: []
type: 'birth'
})

await client.event.create({
transactionId: '1',
type: 'birth',
fields: []
type: 'birth'
})

expect(await db.collection('events').find().toArray()).toHaveLength(1)
Expand All @@ -71,14 +70,12 @@ test('creating an event is an idempotent operation', async () => {
test('stored events can be modified', async () => {
const originalEvent = await client.event.create({
transactionId: '1',
type: 'birth',
fields: []
type: 'birth'
})

const event = await client.event.patch({
id: originalEvent.id,
type: 'death',
fields: [],
transactionId: getUUID()
})

Expand All @@ -89,14 +86,13 @@ test('stored events can be modified', async () => {
test('actions can be added to created events', async () => {
const originalEvent = await client.event.create({
transactionId: '1',
type: 'birth',
fields: []
type: 'birth'
})

const event = await client.event.actions.declare({
eventId: originalEvent.id,
transactionId: '2',
fields: []
data: {}
})

expect(event.actions).toEqual([
Expand Down
3 changes: 2 additions & 1 deletion packages/events/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ const server = createHTTPServer({

return {
user: {
id: userId
id: userId,
primaryOfficeId: 'ae5be1bb-6c50-4389-a72d-4c78d19ec176'
}
}
}
Expand Down
25 changes: 16 additions & 9 deletions packages/events/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@ import {
patchEvent
} from './service/events'
import {
ActionDeclareInput,
ActionNotifyInput,
DeclareActionInput,
NotifyActionInput,
EventInput
} from '@events/schema'

const ContextSchema = z.object({
user: z.object({
id: z.string()
id: z.string(),
primaryOfficeId: z.string()
})
})

Expand All @@ -52,6 +53,7 @@ export const appRouter = router({
return createEvent(
options.input,
options.ctx.user.id,
options.ctx.user.primaryOfficeId,
options.input.transactionId
)
}),
Expand All @@ -62,18 +64,23 @@ export const appRouter = router({
return getEventById(input)
}),
actions: router({
notify: publicProcedure.input(ActionNotifyInput).mutation((options) => {
notify: publicProcedure.input(NotifyActionInput).mutation((options) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why don't we need .omit({ type }) here?

return addAction(options.input, {
eventId: options.input.eventId,
createdBy: options.ctx.user.id
})
}),
declare: publicProcedure.input(ActionDeclareInput).mutation((options) => {
return addAction(options.input, {
eventId: options.input.eventId,
createdBy: options.ctx.user.id
declare: publicProcedure
.input(DeclareActionInput.omit({ type: true }))
.mutation((options) => {
return addAction(
{ type: 'DECLARE', ...options.input },
{
eventId: options.input.eventId,
createdBy: options.ctx.user.id
}
)
})
})
})
})
})
65 changes: 58 additions & 7 deletions packages/events/src/schema/ActionDocument.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,66 @@
*
* Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS.
*/
import { z } from 'zod'
import { ActionInputFields } from './ActionInput'
import { ActionType } from '@opencrvs/commons'
import { z } from 'zod'

export const ActionDocument = z.object({
type: z.nativeEnum(ActionType),
fields: ActionInputFields,
createdBy: z.string().describe('The user who created the action'),
createdAt: z.date()
const ActionBase = z.object({
createdAt: z.date(),
createdBy: z.string(),
data: z.object({})
})

const AssignedAction = ActionBase.merge(
z.object({
type: z.literal(ActionType.ASSIGN),
assignedTo: z.string()
})
)

const UnassignedAction = ActionBase.merge(
z.object({
type: z.literal(ActionType.UNASSIGN)
})
)

const RegisterAction = ActionBase.merge(
z.object({
type: z.literal(ActionType.REGISTER).default(ActionType.REGISTER),
identifiers: z.object({
trackingId: z.string(),
registrationNumber: z.string()
})
})
)

const DeclareAction = ActionBase.merge(
z.object({
type: z.literal(ActionType.DECLARE).default(ActionType.DECLARE)
})
)

const CreatedAction = ActionBase.merge(
z.object({
type: z.literal(ActionType.CREATE),
rikukissa marked this conversation as resolved.
Show resolved Hide resolved
createdAtLocation: z.string()
})
)

const NotifiedAction = ActionBase.merge(
z.object({
type: z.literal(ActionType.NOTIFY),
createdAtLocation: z.string()
})
)

export const ActionDocument = z.discriminatedUnion('type', [
CreatedAction,
NotifiedAction,
RegisterAction,
DeclareAction,
AssignedAction,
UnassignedAction
])

export type ActionDocument = z.infer<typeof ActionDocument>
export type CreatedAction = z.infer<typeof CreatedAction>
59 changes: 28 additions & 31 deletions packages/events/src/schema/ActionInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,63 +12,60 @@
import { ActionType } from '@opencrvs/commons'
import { z } from 'zod'

export const ActionInputFields = z.array(
z.object({
id: z.string(),
value: z.union([
z.string(),
z.number(),
z.array(
z.object({
optionValues: z.array(z.string()),
type: z.string(),
data: z.string(),
fileSize: z.number()
})
)
])
})
)

const BaseActionInput = z.object({
eventId: z.string(),
transactionId: z.string(),
fields: ActionInputFields
data: z.object({})
})

const ActionCreateInput = BaseActionInput.merge(
const CreateActionInput = BaseActionInput.merge(
z.object({
type: z.literal(ActionType.CREATE).default(ActionType.CREATE)
type: z.literal(ActionType.CREATE),
createdAtLocation: z.string()
})
)

const ActionRegisterInput = BaseActionInput.merge(
const RegisterActionInput = BaseActionInput.merge(
z.object({
type: z.literal(ActionType.REGISTER).default(ActionType.REGISTER),
type: z.literal(ActionType.REGISTER),
identifiers: z.object({
trackingId: z.string(),
registrationNumber: z.string()
})
})
)

export const ActionNotifyInput = BaseActionInput.merge(
export const NotifyActionInput = BaseActionInput.merge(
z.object({
type: z.literal(ActionType.NOTIFY).default(ActionType.NOTIFY)
type: z.literal(ActionType.NOTIFY),
createdAtLocation: z.string()
})
)

export const ActionDeclareInput = BaseActionInput.merge(
export const DeclareActionInput = BaseActionInput.merge(
z.object({
type: z.literal(ActionType.DECLARE)
})
)
export const AssignActionInput = BaseActionInput.merge(
z.object({
type: z.literal(ActionType.ASSIGN),
assignedTo: z.string()
})
)
export const UnassignActionInput = BaseActionInput.merge(
z.object({
type: z.literal(ActionType.DECLARE).default(ActionType.DECLARE)
type: z.literal(ActionType.UNASSIGN)
})
)

export const ActionInput = z.discriminatedUnion('type', [
ActionCreateInput,
ActionRegisterInput,
ActionNotifyInput,
ActionDeclareInput
CreateActionInput,
RegisterActionInput,
NotifyActionInput,
DeclareActionInput,
AssignActionInput,
UnassignActionInput
])

export type ActionInput = z.infer<typeof ActionInput>
Loading
Loading