user to organization method

This commit is contained in:
Nate Kelley 2025-07-15 16:36:14 -06:00
parent 2e73d97ffc
commit a9b8acbb80
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
4 changed files with 63 additions and 66 deletions

View File

@ -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,

View File

@ -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:"
}

View File

@ -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<typeof usersToOrganizations>;
type RawUser = InferSelectModel<typeof users>;
// 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<typeof GetUserToOrganizationInputSchema>;
// 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<typeof UserOutputSchema>;
export type OrganizationUser = Pick<RawUser, 'id' | 'name' | 'email' | 'avatarUrl'> &
Pick<RawOrganizationUser, 'role' | 'status'>;
export const getUserToOrganization = async ({
userId,
filters,
}: GetUserToOrganizationInput): Promise<UserWithRole[]> => {
}: GetUserToOrganizationInput): Promise<OrganizationUser[]> => {
// 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;
};

View File

@ -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)