refactor(queries): leverage new query syntax of drizzle-orm

This commit is contained in:
Nicolas Meienberger 2023-05-21 08:53:32 +02:00
parent fee9f0f39b
commit 395e8874cd
8 changed files with 31 additions and 42 deletions

View file

@ -1,6 +1,7 @@
import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';
import { getConfig } from '../core/TipiConfig/TipiConfig';
import * as schema from './schema';
const connectionString = `postgresql://${getConfig().postgresUsername}:${getConfig().postgresPassword}@${getConfig().postgresHost}:${getConfig().postgresPort}/${
getConfig().postgresDatabase
@ -10,4 +11,5 @@ const pool = new Pool({
connectionString,
});
export const db = drizzle(pool);
export const db = drizzle(pool, { schema });
export type Database = typeof db;

View file

@ -1,11 +1,11 @@
import { NodePgDatabase } from 'drizzle-orm/node-postgres';
import { and, asc, eq, ne, notInArray } from 'drizzle-orm';
import { Database } from '@/server/db';
import { appTable, NewApp, AppStatus } from '../../db/schema';
export class AppQueries {
private db;
constructor(p: NodePgDatabase) {
constructor(p: Database) {
this.db = p;
}
@ -15,8 +15,7 @@ export class AppQueries {
* @param {string} appId - The id of the app to return
*/
public async getApp(appId: string) {
const apps = await this.db.select().from(appTable).where(eq(appTable.id, appId));
return apps[0];
return this.db.query.appTable.findFirst({ where: eq(appTable.id, appId) });
}
/**
@ -55,14 +54,14 @@ export class AppQueries {
* @param {AppStatus} status - The status of the apps to return
*/
public async getAppsByStatus(status: AppStatus) {
return this.db.select().from(appTable).where(eq(appTable.status, status)).orderBy(asc(appTable.id));
return this.db.query.appTable.findMany({ where: eq(appTable.status, status), orderBy: asc(appTable.id) });
}
/**
* Returns all apps installed sorted by id ascending
*/
public async getApps() {
return this.db.select().from(appTable).orderBy(asc(appTable.id));
return this.db.query.appTable.findMany({ orderBy: asc(appTable.id) });
}
/**
@ -72,10 +71,7 @@ export class AppQueries {
* @param {string} id - The id of the app to exclude
*/
public async getAppsByDomain(domain: string, id: string) {
return this.db
.select()
.from(appTable)
.where(and(eq(appTable.domain, domain), eq(appTable.exposed, true), ne(appTable.id, id)));
return this.db.query.appTable.findMany({ where: and(eq(appTable.domain, domain), eq(appTable.exposed, true), ne(appTable.id, id)) });
}
/**

View file

@ -1,11 +1,11 @@
import { NodePgDatabase } from 'drizzle-orm/node-postgres';
import { asc, eq } from 'drizzle-orm';
import { eq } from 'drizzle-orm';
import { Database } from '@/server/db';
import { userTable, NewUser } from '../../db/schema';
export class AuthQueries {
private db;
constructor(p: NodePgDatabase) {
constructor(p: Database) {
this.db = p;
}
@ -15,8 +15,7 @@ export class AuthQueries {
* @param {string} username - The username of the user to return
*/
public async getUserByUsername(username: string) {
const users = await this.db.select().from(userTable).where(eq(userTable.username, username.trim().toLowerCase()));
return users[0];
return this.db.query.userTable.findFirst({ where: eq(userTable.username, username.trim().toLowerCase()) });
}
/**
@ -25,12 +24,7 @@ export class AuthQueries {
* @param {number} id - The id of the user to return
*/
public async getUserById(id: number) {
const users = await this.db
.select()
.from(userTable)
.where(eq(userTable.id, Number(id)));
return users[0];
return this.db.query.userTable.findFirst({ where: eq(userTable.id, Number(id)) });
}
/**
@ -39,12 +33,7 @@ export class AuthQueries {
* @param {number} id - The id of the user to return
*/
public async getUserDtoById(id: number) {
const users = await this.db
.select({ id: userTable.id, username: userTable.username, totpEnabled: userTable.totpEnabled, locale: userTable.locale })
.from(userTable)
.where(eq(userTable.id, Number(id)));
return users[0];
return this.db.query.userTable.findFirst({ where: eq(userTable.id, Number(id)), columns: { id: true, username: true, totpEnabled: true, locale: true } });
}
/**
@ -74,8 +63,7 @@ export class AuthQueries {
* Returns the first operator found in the system
*/
public async getFirstOperator() {
const users = await this.db.select().from(userTable).where(eq(userTable.operator, true)).orderBy(asc(userTable.id)).limit(1);
return users[0];
return this.db.query.userTable.findFirst({ where: eq(userTable.operator, true) });
}
/**

View file

@ -1,8 +1,8 @@
import validator from 'validator';
import { NodePgDatabase } from 'drizzle-orm/node-postgres';
import { App } from '@/server/db/schema';
import { AppQueries } from '@/server/queries/apps/apps.queries';
import { TranslatedError } from '@/server/utils/errors';
import { Database } from '@/server/db';
import { checkAppRequirements, checkEnvFile, generateEnvFile, getAvailableApps, ensureAppFolder, AppInfo, getAppInfo, getUpdateInfo } from './apps.helpers';
import { getConfig } from '../../core/TipiConfig';
import { EventDispatcher } from '../../core/EventDispatcher';
@ -25,7 +25,7 @@ const filterApps = (apps: AppInfo[]): AppInfo[] => apps.sort(sortApps).filter(fi
export class AppServiceClass {
private queries;
constructor(p: NodePgDatabase) {
constructor(p: Database) {
this.queries = new AppQueries(p);
}

View file

@ -4,7 +4,7 @@ import { faker } from '@faker-js/faker';
import { TotpAuthenticator } from '@/server/utils/totp';
import { generateSessionId } from '@/server/common/session.helpers';
import { fromAny, fromPartial } from '@total-typescript/shoehorn';
import { mockInsert, mockSelect } from '@/server/tests/drizzle-helpers';
import { mockInsert, mockQuery, mockSelect } from '@/server/tests/drizzle-helpers';
import { createDatabase, clearDatabase, closeDatabase, TestDatabase } from '@/server/tests/test-utils';
import { encrypt } from '../../utils/encryption';
import { setConfig } from '../../core/TipiConfig';
@ -428,7 +428,7 @@ describe('Register', () => {
// Arrange
const req = {};
const email = faker.internet.email();
const mockDatabase = { select: mockSelect([]), insert: mockInsert([]) };
const mockDatabase = { select: mockSelect([]), insert: mockInsert([]), query: mockQuery(undefined) };
const newAuthService = new AuthServiceClass(fromAny(mockDatabase));
// Act & Assert

View file

@ -1,12 +1,12 @@
import * as argon2 from 'argon2';
import validator from 'validator';
import { TotpAuthenticator } from '@/server/utils/totp';
import { NodePgDatabase } from 'drizzle-orm/node-postgres';
import { AuthQueries } from '@/server/queries/auth/auth.queries';
import { Context } from '@/server/context';
import { TranslatedError } from '@/server/utils/errors';
import { Locales, getLocaleFromString } from '@/shared/internationalization/locales';
import { generateSessionId } from '@/server/common/session.helpers';
import { Database } from '@/server/db';
import { getConfig } from '../../core/TipiConfig';
import TipiCache from '../../core/TipiCache';
import { fileExists, unlinkFile } from '../../common/fs.helpers';
@ -21,7 +21,7 @@ type UsernamePasswordInput = {
export class AuthServiceClass {
private queries;
constructor(p: NodePgDatabase) {
constructor(p: Database) {
this.queries = new AuthQueries(p);
}

View file

@ -1,3 +1,5 @@
export const mockSelect = <T>(returnValue: T) => jest.fn(() => ({ from: jest.fn(() => ({ where: jest.fn(() => returnValue) })) }));
export const mockInsert = <T>(returnValue: T) => jest.fn(() => ({ values: jest.fn(() => ({ returning: jest.fn(() => returnValue) })) }));
export const mockQuery = <T>(returnValue: T) => ({ userTable: { findFirst: jest.fn(() => returnValue) } });

View file

@ -1,13 +1,14 @@
/* eslint-disable no-restricted-syntax */
import pg, { Pool } from 'pg';
import { NodePgDatabase, drizzle } from 'drizzle-orm/node-postgres';
import { drizzle } from 'drizzle-orm/node-postgres';
import { runPostgresMigrations } from '../run-migration';
import { getConfig } from '../core/TipiConfig';
import { appTable, userTable } from '../db/schema';
import * as schema from '../db/schema';
import { Database } from '../db';
export type TestDatabase = {
client: Pool;
db: NodePgDatabase;
db: Database;
};
/**
@ -36,7 +37,7 @@ const createDatabase = async (testsuite: string): Promise<TestDatabase> => {
connectionString: `postgresql://${getConfig().postgresUsername}:${getConfig().postgresPassword}@${getConfig().postgresHost}:${getConfig().postgresPort}/${testsuite}?connect_timeout=300`,
});
return { client, db: drizzle(client) };
return { client, db: drizzle(client, { schema }) };
};
/**
@ -45,8 +46,8 @@ const createDatabase = async (testsuite: string): Promise<TestDatabase> => {
* @param {TestDatabase} database - database to clear
*/
const clearDatabase = async (database: TestDatabase) => {
await database.db.delete(userTable);
await database.db.delete(appTable);
await database.db.delete(schema.userTable);
await database.db.delete(schema.appTable);
};
const closeDatabase = async (database: TestDatabase) => {