From a9b8acbb8072e793e5837f05270f3b34250c3d5a Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Tue, 15 Jul 2025 16:36:14 -0600 Subject: [PATCH] user to organization method --- .vscode/buster.code-workspace | 6 +- packages/database/package.json | 1 + .../queries/users/users-to-organizations.ts | 107 +++++++----------- pnpm-lock.yaml | 15 +++ 4 files changed, 63 insertions(+), 66 deletions(-) diff --git a/.vscode/buster.code-workspace b/.vscode/buster.code-workspace index 689cebe60..71b5fb17d 100644 --- a/.vscode/buster.code-workspace +++ b/.vscode/buster.code-workspace @@ -37,9 +37,9 @@ "typescript.suggest.includeCompletionsForModuleExports": true, "typescript.workspaceSymbols.scope": "allOpenProjects", "typescript.references.enabled": true, - "typescript.implementationsCodeLens.enabled": true, - "typescript.referencesCodeLens.enabled": true, - "typescript.referencesCodeLens.showOnAllFunctions": true, + "typescript.implementationsCodeLens.enabled": false, + "typescript.referencesCodeLens.enabled": false, + "typescript.referencesCodeLens.showOnAllFunctions": false, "typescript.validate.enable": true, "typescript.format.enable": true, diff --git a/packages/database/package.json b/packages/database/package.json index cff05f62c..2f8ae0f62 100644 --- a/packages/database/package.json +++ b/packages/database/package.json @@ -50,6 +50,7 @@ "ai": "catalog:", "drizzle-kit": "^0.31.4", "drizzle-orm": "catalog:", + "drizzle-zod": "^0.8.2", "postgres": "^3.4.7", "zod": "catalog:" } diff --git a/packages/database/src/queries/users/users-to-organizations.ts b/packages/database/src/queries/users/users-to-organizations.ts index d4798b14e..01424f0ed 100644 --- a/packages/database/src/queries/users/users-to-organizations.ts +++ b/packages/database/src/queries/users/users-to-organizations.ts @@ -1,9 +1,12 @@ -import { and, eq, isNull, like } from 'drizzle-orm'; +import { type InferSelectModel, and, eq, isNull, like } from 'drizzle-orm'; import { z } from 'zod'; import { db } from '../../connection'; import { users, usersToOrganizations } from '../../schema'; import { getUserOrganizationId } from '../organizations/organizations'; +type RawOrganizationUser = InferSelectModel; +type RawUser = InferSelectModel; + // Input schema for type safety const GetUserToOrganizationInputSchema = z.object({ userId: z.string().uuid('User ID must be a valid UUID'), @@ -17,74 +20,52 @@ const GetUserToOrganizationInputSchema = z.object({ export type GetUserToOrganizationInput = z.infer; -// Output schema for type safety -export const UserOutputSchema = z.object({ - id: z.string().uuid(), - name: z.string().nullable(), - email: z.string(), - avatarUrl: z.string().nullable(), - role: z.string(), - status: z.string(), -}); - -export type UserWithRole = z.infer; +export type OrganizationUser = Pick & + Pick; export const getUserToOrganization = async ({ userId, filters, -}: GetUserToOrganizationInput): Promise => { +}: GetUserToOrganizationInput): Promise => { // Validate input const validated = GetUserToOrganizationInputSchema.parse({ userId, filters }); - try { - // First, get the user's organization ID using the existing function - const userOrg = await getUserOrganizationId(validated.userId); - - if (!userOrg) { - throw new Error('User not found in any organization'); - } - - const organizationId = userOrg.organizationId; - - // Build the where conditions for the join - const joinConditions = and( - eq(users.id, usersToOrganizations.userId), - eq(usersToOrganizations.organizationId, organizationId), - isNull(usersToOrganizations.deletedAt) - ); - - // Build the where conditions for filtering - const conditions = []; - if (validated.filters?.userName) { - conditions.push(like(users.name, `%${validated.filters.userName}%`)); - } - if (validated.filters?.email) { - conditions.push(like(users.email, `%${validated.filters.email}%`)); - } - - const baseQuery = db - .select({ - id: users.id, - name: users.name, - email: users.email, - avatarUrl: users.avatarUrl, - role: usersToOrganizations.role, - status: usersToOrganizations.status, - }) - .from(users) - .innerJoin(usersToOrganizations, joinConditions); - - const results = - conditions.length > 0 - ? await baseQuery.where(conditions.length === 1 ? conditions[0] : and(...conditions)) - : await baseQuery; - - // Validate and return results - return results.map((user) => UserOutputSchema.parse(user)); - } catch (error) { - if (error instanceof z.ZodError) { - throw new Error(`Invalid input: ${error.errors.map((e) => e.message).join(', ')}`); - } - throw error; + // Get the user's organization ID + const userOrg = await getUserOrganizationId(validated.userId); + if (!userOrg) { + throw new Error('User not found in any organization'); } + + // Build filter conditions + const filterConditions = []; + if (validated.filters?.userName) { + filterConditions.push(like(users.name, `%${validated.filters.userName}%`)); + } + if (validated.filters?.email) { + filterConditions.push(like(users.email, `%${validated.filters.email}%`)); + } + + // Build the complete where condition + const whereCondition = and( + eq(usersToOrganizations.organizationId, userOrg.organizationId), + isNull(usersToOrganizations.deletedAt), + ...filterConditions + ); + + // Execute the query + const results = await db + .select({ + id: users.id, + name: users.name, + email: users.email, + avatarUrl: users.avatarUrl, + role: usersToOrganizations.role, + status: usersToOrganizations.status, + }) + .from(users) + .innerJoin(usersToOrganizations, eq(users.id, usersToOrganizations.userId)) + .where(whereCondition); + + // Validate and return results + return results; }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9b7af89d1..367dde62e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -759,6 +759,9 @@ importers: drizzle-orm: specifier: 'catalog:' version: 0.44.2(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(mysql2@3.14.1)(pg@8.16.3)(postgres@3.4.7) + drizzle-zod: + specifier: ^0.8.2 + version: 0.8.2(drizzle-orm@0.44.2(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(mysql2@3.14.1)(pg@8.16.3)(postgres@3.4.7))(zod@3.25.1) postgres: specifier: ^3.4.7 version: 3.4.7 @@ -5944,6 +5947,7 @@ packages: bun@1.2.18: resolution: {integrity: sha512-OR+EpNckoJN4tHMVZPaTPxDj2RgpJgJwLruTIFYbO3bQMguLd0YrmkWKYqsiihcLgm2ehIjF/H1RLfZiRa7+qQ==} + cpu: [arm64, x64, aarch64] os: [darwin, linux, win32] hasBin: true @@ -6723,6 +6727,12 @@ packages: sqlite3: optional: true + drizzle-zod@0.8.2: + resolution: {integrity: sha512-9Do/16OjFFNrQDZgvMtxtDDwKWbFOxUAIwNPKX98SfxrP8H18vhN1BvNXbhelLcdgCE7GEaXDJqBjMExSkhpkA==} + peerDependencies: + drizzle-orm: '>=0.36.0' + zod: ^3.25.1 + dt-sql-parser@4.3.1: resolution: {integrity: sha512-WlFB9of+ChwWtc5M222jHGIpzqHx51szLe/11GAwwbA+4hRaVkMpWMf2bbYj4i855edSoTQ52zyLJVOpe+4OVg==} engines: {node: '>=18'} @@ -18481,6 +18491,11 @@ snapshots: pg: 8.16.3 postgres: 3.4.7 + drizzle-zod@0.8.2(drizzle-orm@0.44.2(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(mysql2@3.14.1)(pg@8.16.3)(postgres@3.4.7))(zod@3.25.1): + dependencies: + drizzle-orm: 0.44.2(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(mysql2@3.14.1)(pg@8.16.3)(postgres@3.4.7) + zod: 3.25.1 + dt-sql-parser@4.3.1(antlr4ng-cli@1.0.7): dependencies: antlr4-c3: 3.3.7(antlr4ng-cli@1.0.7)