Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/app.mock.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class FakeUserService extends UserService {
age: null,
email: 'dummy',
name: 'dummy',
role: 'Reader',
})
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/db/migrations/0001_add_user_role.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CREATE TYPE "user"."user_role" AS ENUM('Admin', 'Writer', 'Reader');--> statement-breakpoint
ALTER TABLE "user"."user" ADD COLUMN "role" "user"."user_role" NOT NULL;
208 changes: 208 additions & 0 deletions src/db/migrations/meta/0001_snapshot.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
{
"id": "3eb92b94-c6c5-4dcf-badf-2de64458718f",
"prevId": "4a8f3238-87cc-4b5f-ad9c-5071375bb015",
"version": "7",
"dialect": "postgresql",
"tables": {
"post.post": {
"name": "post",
"schema": "post",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"created_at": {
"name": "created_at",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
},
"title": {
"name": "title",
"type": "varchar(256)",
"primaryKey": false,
"notNull": true
},
"content": {
"name": "content",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"published": {
"name": "published",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"author_id": {
"name": "author_id",
"type": "varchar",
"primaryKey": false,
"notNull": true
}
},
"indexes": {
"author_id_idx": {
"name": "author_id_idx",
"columns": [
{
"expression": "author_id",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": false,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"user.profile": {
"name": "profile",
"schema": "user",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"bio": {
"name": "bio",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"age": {
"name": "age",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "varchar",
"primaryKey": false,
"notNull": true
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"profile_email_unique": {
"name": "profile_email_unique",
"nullsNotDistinct": false,
"columns": ["email"]
},
"profile_user_id_unique": {
"name": "profile_user_id_unique",
"nullsNotDistinct": false,
"columns": ["user_id"]
}
},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"user.user": {
"name": "user",
"schema": "user",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"age": {
"name": "age",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "varchar",
"primaryKey": false,
"notNull": true
},
"name": {
"name": "name",
"type": "varchar",
"primaryKey": false,
"notNull": true
},
"role": {
"name": "role",
"type": "user_role",
"typeSchema": "user",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"user_email_unique": {
"name": "user_email_unique",
"nullsNotDistinct": false,
"columns": ["email"]
}
},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
}
},
"enums": {
"user.user_role": {
"name": "user_role",
"schema": "user",
"values": ["Admin", "Writer", "Reader"]
}
},
"schemas": {
"post": "post",
"user": "user"
},
"sequences": {},
"roles": {},
"policies": {},
"views": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}
7 changes: 7 additions & 0 deletions src/db/migrations/meta/_journal.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@
"when": 1731603966543,
"tag": "0000_init_user_post_tables",
"breakpoints": true
},
{
"idx": 1,
"version": "7",
"when": 1744730504852,
"tag": "0001_add_user_role",
"breakpoints": true
}
]
}
5 changes: 5 additions & 0 deletions src/db/schema/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@ import { relations } from 'drizzle-orm'
import { integer, pgSchema, uuid, varchar } from 'drizzle-orm/pg-core'
import { createInsertSchema, createSelectSchema } from 'drizzle-zod'
import z from 'zod'
import { USER_ROLE_ENUM } from '../../modules/users/schemas/userSchemas.js'

export const userSchema = pgSchema('user')

export const userRoleEnum = userSchema.enum('user_role', ['Admin', 'Writer', 'Reader'])

export const user = userSchema.table('user', {
id: uuid('id').primaryKey().defaultRandom().notNull(),
age: integer('age'),
email: varchar('email').unique().notNull(),
name: varchar('name').notNull(),
role: userRoleEnum('role').notNull(),
})

const selectUserSchema = createSelectSchema(user)
Expand All @@ -22,6 +26,7 @@ const updateUserSchema = createInsertSchema(user, {
age: z.number().optional(),
email: z.string().optional(),
name: z.string().optional(),
role: USER_ROLE_ENUM.optional(),
}).omit({ id: true })
export type UpdatedUser = z.infer<typeof updateUserSchema>

Expand Down
2 changes: 2 additions & 0 deletions src/modules/users/consumers/PermissionsConsumer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { asSingletonClass } from 'opinionated-machine'
import { user as userTable } from '../../../db/schema/user.ts'
import type { PublisherManager } from '../../../infrastructure/CommonModule.ts'
import { buildQueueMessage } from '../../../utils/queueUtils.ts'
import type { UserRole } from '../schemas/userSchemas.js'
import { PermissionConsumer } from './PermissionConsumer.ts'
import type { PermissionsMessages } from './permissionsMessageSchemas.ts'

Expand All @@ -31,6 +32,7 @@ async function createUsers(drizzle: PostgresJsDatabase, userIdsToCreate: string[
id: userId,
name: userId.toString(),
email: `test${userId}@email.lt`,
role: 'Reader' as UserRole,
}
}),
)
Expand Down
11 changes: 9 additions & 2 deletions src/modules/users/controllers/UserController.e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import type { UserRepository } from '../repositories/UserRepository.ts'
import type { UserCreateDTO } from '../services/UserService.ts'
import { UserController } from './UserController.ts'

const NEW_USER_FIXTURE = { name: 'dummy', email: '[email protected]' } satisfies UserCreateDTO
const NEW_USER_FIXTURE = {
name: 'dummy',
email: '[email protected]',
role: 'Reader',
} satisfies UserCreateDTO

describe('UserController', () => {
let app: AppInstance
Expand All @@ -33,7 +37,7 @@ describe('UserController', () => {
headers: {
authorization: `Bearer ${token}`,
},
body: { name: 'dummy', email: 'test' },
body: { name: 'dummy', email: 'test', role: 'Admin' },
})

expect(response.statusCode).toBe(400)
Expand Down Expand Up @@ -81,6 +85,7 @@ describe('UserController', () => {
email: '[email protected]',
id: expect.any(String),
name: 'dummy',
role: 'Reader',
},
})
})
Expand Down Expand Up @@ -150,6 +155,7 @@ describe('UserController', () => {
const updateResponse = await injectPatch(app, UserController.contracts.updateUser, {
body: {
name: 'updated',
role: 'Admin',
},
pathParams: {
userId: id,
Expand All @@ -166,6 +172,7 @@ describe('UserController', () => {
age: null,
id,
name: 'updated',
role: 'Admin',
})
})
})
Expand Down
3 changes: 2 additions & 1 deletion src/modules/users/controllers/UserController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,14 @@ export class UserController extends AbstractController<typeof UserController.con
}

private createUser = buildFastifyPayloadRoute(postCreateUserContract, async (req, reply) => {
const { name, email, age } = req.body
const { name, email, age, role } = req.body
const { userService } = req.diScope.cradle

const createdUser = await userService.createUser({
name,
email,
age,
role,
})

return reply.status(201).send({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { type TestContext, testContextFactory } from '../../../../test/TestConte
import type { QueueManager } from '@lokalise/background-jobs-common'
import { user as userTable } from '../../../db/schema/user.ts'
import type { BullmqSupportedQueues } from '../../../infrastructure/CommonModule.ts'
import type { UserRole } from '../schemas/userSchemas.js'
import { UserImportJob } from './UserImportJob.ts'

describe('UserImportJob', () => {
Expand Down Expand Up @@ -37,6 +38,7 @@ describe('UserImportJob', () => {
name: 'name',
age: 33,
email: '[email protected]',
role: 'Admin' as UserRole,
}

const jobId = await bullmqQueueManager.schedule('UserImportJob', {
Expand Down
2 changes: 2 additions & 0 deletions src/modules/users/job-queue-processors/UserImportJob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import z from 'zod'
import type { Dependencies } from '../../../infrastructure/CommonModule.ts'
import { SERVICE_NAME } from '../../../infrastructure/config.ts'
import { AbstractEnqueuedJobProcessor } from '../../../infrastructure/jobs/AbstractEnqueuedJobProcessor.ts'
import { USER_ROLE_ENUM } from '../schemas/userSchemas.js'
import type { UserService } from '../services/UserService.ts'

export const USER_IMPORT_JOB_PAYLOAD = BASE_JOB_PAYLOAD_SCHEMA.extend({
name: z.string(),
age: z.number(),
email: z.string(),
role: USER_ROLE_ENUM,
})
type UserImportJobPayload = z.infer<typeof USER_IMPORT_JOB_PAYLOAD>

Expand Down
5 changes: 5 additions & 0 deletions src/modules/users/schemas/userSchemas.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { toNumberPreprocessor } from '@lokalise/zod-extras'
import z from 'zod'

export const USER_ROLE_ENUM = z.enum(['Admin', 'Writer', 'Reader'])
export type UserRole = z.infer<typeof USER_ROLE_ENUM>

export const USER_SCHEMA = z.object({
id: z.string(),
name: z.string(),
Expand All @@ -12,6 +15,7 @@ export const CREATE_USER_BODY_SCHEMA = z.object({
name: z.string(),
age: z.optional(z.nullable(z.preprocess(toNumberPreprocessor, z.number()))),
email: z.string().email(),
role: USER_ROLE_ENUM,
})

export const CREATE_USER_RESPONSE_BODY_SCHEMA = z.object({
Expand All @@ -21,6 +25,7 @@ export const CREATE_USER_RESPONSE_BODY_SCHEMA = z.object({
export const UPDATE_USER_BODY_SCHEMA = z.object({
name: z.optional(z.string()),
email: z.optional(z.string().email()),
role: z.optional(USER_ROLE_ENUM),
})

export const GET_USER_PARAMS_SCHEMA = z.object({
Expand Down
Loading
Loading