refactor(server): import prisma from @prisma/client (#5863)

This commit is contained in:
liuyi
2024-02-22 07:46:57 +00:00
parent f2c5786dd0
commit 4d421a324f
24 changed files with 82 additions and 218 deletions
@@ -8,13 +8,11 @@ import {
UseGuards,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { PrismaClient } from '@prisma/client';
import type { NextAuthOptions } from 'next-auth';
import { AuthHandler } from 'next-auth/core';
import {
getRequestResponseFromContext,
PrismaService,
} from '../../fundamentals';
import { getRequestResponseFromContext } from '../../fundamentals';
import { NextAuthOptionsProvide } from './next-auth-options';
import { AuthService } from './service';
@@ -57,7 +55,7 @@ class AuthGuard implements CanActivate {
@Inject(NextAuthOptionsProvide)
private readonly nextAuthOptions: NextAuthOptions,
private readonly auth: AuthService,
private readonly prisma: PrismaService,
private readonly prisma: PrismaClient,
private readonly reflector: Reflector
) {}
@@ -1,6 +1,7 @@
import { PrismaAdapter } from '@auth/prisma-adapter';
import { FactoryProvider, Logger } from '@nestjs/common';
import { verify } from '@node-rs/argon2';
import { PrismaClient } from '@prisma/client';
import { assign, omit } from 'lodash-es';
import { NextAuthOptions } from 'next-auth';
import Credentials from 'next-auth/providers/credentials';
@@ -8,12 +9,7 @@ import Email from 'next-auth/providers/email';
import Github from 'next-auth/providers/github';
import Google from 'next-auth/providers/google';
import {
Config,
MailService,
PrismaService,
SessionService,
} from '../../fundamentals';
import { Config, MailService, SessionService } from '../../fundamentals';
import { FeatureType } from '../features';
import { Quota_FreePlanV1_1 } from '../quota';
import {
@@ -31,7 +27,7 @@ export const NextAuthOptionsProvider: FactoryProvider<NextAuthOptions> = {
provide: NextAuthOptionsProvide,
useFactory(
config: Config,
prisma: PrismaService,
prisma: PrismaClient,
mailer: MailService,
session: SessionService
) {
@@ -284,5 +280,5 @@ export const NextAuthOptionsProvider: FactoryProvider<NextAuthOptions> = {
};
return nextAuthOptions;
},
inject: [Config, PrismaService, MailService, SessionService],
inject: [Config, PrismaClient, MailService, SessionService],
};
@@ -15,7 +15,7 @@ import {
UseGuards,
} from '@nestjs/common';
import { hash, verify } from '@node-rs/argon2';
import type { User } from '@prisma/client';
import { PrismaClient, type User } from '@prisma/client';
import type { NextFunction, Request, Response } from 'express';
import { pick } from 'lodash-es';
import { nanoid } from 'nanoid';
@@ -26,7 +26,6 @@ import {
AuthThrottlerGuard,
Config,
metrics,
PrismaService,
SessionService,
Throttle,
} from '../../fundamentals';
@@ -45,7 +44,7 @@ export class NextAuthController {
constructor(
readonly config: Config,
readonly prisma: PrismaService,
readonly prisma: PrismaClient,
private readonly authService: AuthService,
@Inject(NextAuthOptionsProvide)
private readonly nextAuthOptions: NextAuthOptions,
@@ -8,13 +8,12 @@ import {
} from '@nestjs/common';
import { hash, verify } from '@node-rs/argon2';
import { Algorithm, sign, verify as jwtVerify } from '@node-rs/jsonwebtoken';
import type { User } from '@prisma/client';
import { PrismaClient, type User } from '@prisma/client';
import { nanoid } from 'nanoid';
import {
Config,
MailService,
PrismaService,
verifyChallengeResponse,
} from '../../fundamentals';
import { Quota_FreePlanV1_1 } from '../quota';
@@ -32,7 +31,7 @@ export const getUtcTimestamp = () => Math.floor(Date.now() / 1000);
export class AuthService {
constructor(
private readonly config: Config,
private readonly prisma: PrismaService,
private readonly prisma: PrismaClient,
private readonly mailer: MailService
) {}
@@ -2,14 +2,15 @@ import { randomUUID } from 'node:crypto';
import { BadRequestException } from '@nestjs/common';
import { Algorithm, sign, verify as jwtVerify } from '@node-rs/jsonwebtoken';
import { PrismaClient } from '@prisma/client';
import { JWT } from 'next-auth/jwt';
import { Config, PrismaService } from '../../../fundamentals';
import { Config } from '../../../fundamentals';
import { getUtcTimestamp, UserClaim } from '../service';
export const jwtEncode = async (
config: Config,
prisma: PrismaService,
prisma: PrismaClient,
token: JWT | undefined,
maxAge: number | undefined
) => {
@@ -2,13 +2,13 @@ import { isDeepStrictEqual } from 'node:util';
import { Injectable, Logger } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import { PrismaClient } from '@prisma/client';
import {
Config,
type EventPayload,
metrics,
OnEvent,
PrismaService,
} from '../../fundamentals';
import { QuotaService } from '../quota';
import { Permission } from '../workspaces/types';
@@ -19,7 +19,7 @@ export class DocHistoryManager {
private readonly logger = new Logger(DocHistoryManager.name);
constructor(
private readonly config: Config,
private readonly db: PrismaService,
private readonly db: PrismaClient,
private readonly quota: QuotaService
) {}
@@ -5,7 +5,7 @@ import {
OnModuleInit,
} from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import { Snapshot, Update } from '@prisma/client';
import { PrismaClient, Snapshot, Update } from '@prisma/client';
import { chunk } from 'lodash-es';
import { defer, retry } from 'rxjs';
import {
@@ -25,7 +25,6 @@ import {
mergeUpdatesInApplyWay as jwstMergeUpdates,
metrics,
OnEvent,
PrismaService,
} from '../../fundamentals';
function compare(yBinary: Buffer, jwstBinary: Buffer, strict = false): boolean {
@@ -72,7 +71,7 @@ export class DocManager implements OnModuleInit, OnModuleDestroy {
private busy = false;
constructor(
private readonly db: PrismaService,
private readonly db: PrismaClient,
private readonly config: Config,
private readonly cache: Cache,
private readonly event: EventEmitter
@@ -1,6 +1,7 @@
import { Injectable, Logger } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
import { Config, PrismaService } from '../../fundamentals';
import { Config } from '../../fundamentals';
import { FeatureService } from './service';
import { FeatureType } from './types';
@@ -12,7 +13,7 @@ export class FeatureManagementService {
constructor(
private readonly feature: FeatureService,
private readonly prisma: PrismaService,
private readonly prisma: PrismaClient,
private readonly config: Config
) {}
@@ -1,6 +1,6 @@
import { Injectable } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
import { PrismaService } from '../../fundamentals';
import { UserType } from '../users/types';
import { WorkspaceType } from '../workspaces/types';
import { FeatureConfigType, getFeature } from './feature';
@@ -8,7 +8,7 @@ import { FeatureKind, FeatureType } from './types';
@Injectable()
export class FeatureService {
constructor(private readonly prisma: PrismaService) {}
constructor(private readonly prisma: PrismaClient) {}
async getFeaturesVersion() {
const features = await this.prisma.features.findMany({
@@ -1,4 +1,5 @@
import { PrismaService } from '../../fundamentals';
import { PrismaClient } from '@prisma/client';
import { formatDate, formatSize, Quota, QuotaSchema } from './types';
const QuotaCache = new Map<number, QuotaConfig>();
@@ -6,7 +7,7 @@ const QuotaCache = new Map<number, QuotaConfig>();
export class QuotaConfig {
readonly config: Quota;
static async get(prisma: PrismaService, featureId: number) {
static async get(prisma: PrismaClient, featureId: number) {
const cachedQuota = QuotaCache.get(featureId);
if (cachedQuota) {
@@ -1,15 +1,16 @@
import { Injectable } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
import { type EventPayload, OnEvent, PrismaService } from '../../fundamentals';
import { type EventPayload, OnEvent } from '../../fundamentals';
import { FeatureKind } from '../features';
import { QuotaConfig } from './quota';
import { QuotaType } from './types';
type Transaction = Parameters<Parameters<PrismaService['$transaction']>[0]>[0];
type Transaction = Parameters<Parameters<PrismaClient['$transaction']>[0]>[0];
@Injectable()
export class QuotaService {
constructor(private readonly prisma: PrismaService) {}
constructor(private readonly prisma: PrismaClient) {}
// get activated user quota
async getUserQuota(userId: string) {
@@ -7,7 +7,7 @@ import {
ResolveField,
Resolver,
} from '@nestjs/graphql';
import type { User } from '@prisma/client';
import { PrismaClient, type User } from '@prisma/client';
import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs';
import {
@@ -15,7 +15,6 @@ import {
EventEmitter,
type FileUpload,
PaymentRequiredException,
PrismaService,
Throttle,
} from '../../fundamentals';
import { Auth, CurrentUser, Public, Publicable } from '../auth/guard';
@@ -40,7 +39,7 @@ import { UsersService } from './users';
@Resolver(() => UserType)
export class UserResolver {
constructor(
private readonly prisma: PrismaService,
private readonly prisma: PrismaClient,
private readonly storage: AvatarStorage,
private readonly users: UsersService,
private readonly feature: FeatureManagementService,
@@ -1,10 +1,9 @@
import { Injectable } from '@nestjs/common';
import { PrismaService } from '../../fundamentals';
import { PrismaClient } from '@prisma/client';
@Injectable()
export class UsersService {
constructor(private readonly prisma: PrismaService) {}
constructor(private readonly prisma: PrismaClient) {}
async findUserByEmail(email: string) {
return this.prisma.user.findFirst({
@@ -7,9 +7,10 @@ import {
Param,
Res,
} from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
import type { Response } from 'express';
import { CallTimer, PrismaService } from '../../fundamentals';
import { CallTimer } from '../../fundamentals';
import { Auth, CurrentUser, Publicable } from '../auth';
import { DocHistoryManager, DocManager } from '../doc';
import { WorkspaceBlobStorage } from '../storage';
@@ -26,7 +27,7 @@ export class WorkspacesController {
private readonly permission: PermissionService,
private readonly docManager: DocManager,
private readonly historyManager: DocHistoryManager,
private readonly prisma: PrismaService
private readonly prisma: PrismaClient
) {}
// get workspace blob
@@ -1,7 +1,6 @@
import { ForbiddenException, Injectable } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { type Prisma, PrismaClient } from '@prisma/client';
import { PrismaService } from '../../fundamentals';
import { Permission } from './types';
export enum PublicPageMode {
@@ -11,7 +10,7 @@ export enum PublicPageMode {
@Injectable()
export class PermissionService {
constructor(private readonly prisma: PrismaService) {}
constructor(private readonly prisma: PrismaClient) {}
/// Start regin: workspace permission
async get(ws: string, user: string) {
@@ -9,9 +9,12 @@ import {
ResolveField,
Resolver,
} from '@nestjs/graphql';
import type { WorkspacePage as PrismaWorkspacePage } from '@prisma/client';
import {
PrismaClient,
type WorkspacePage as PrismaWorkspacePage,
} from '@prisma/client';
import { CloudThrottlerGuard, PrismaService } from '../../../fundamentals';
import { CloudThrottlerGuard } from '../../../fundamentals';
import { Auth, CurrentUser } from '../../auth';
import { UserType } from '../../users';
import { DocID } from '../../utils/doc';
@@ -43,7 +46,7 @@ class WorkspacePage implements Partial<PrismaWorkspacePage> {
@Resolver(() => WorkspaceType)
export class PagePermissionResolver {
constructor(
private readonly prisma: PrismaService,
private readonly prisma: PrismaClient,
private readonly permission: PermissionService
) {}
@@ -15,7 +15,7 @@ import {
ResolveField,
Resolver,
} from '@nestjs/graphql';
import type { User } from '@prisma/client';
import { PrismaClient, type User } from '@prisma/client';
import { getStreamAsBuffer } from 'get-stream';
import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs';
import { applyUpdate, Doc } from 'yjs';
@@ -25,7 +25,6 @@ import {
EventEmitter,
type FileUpload,
MailService,
PrismaService,
Throttle,
} from '../../../fundamentals';
import { Auth, CurrentUser, Public } from '../../auth';
@@ -57,7 +56,7 @@ export class WorkspaceResolver {
constructor(
private readonly auth: AuthService,
private readonly mailer: MailService,
private readonly prisma: PrismaService,
private readonly prisma: PrismaClient,
private readonly permissions: PermissionService,
private readonly quota: QuotaManagementService,
private readonly users: UsersService,
@@ -21,7 +21,6 @@ export {
GlobalExceptionFilter,
OptionalModule,
} from './nestjs';
export { PrismaService } from './prisma';
export { SessionService } from './session';
export * from './storage';
export { type StorageProvider, StorageProviderFactory } from './storage';
@@ -3,16 +3,16 @@ import { PrismaClient } from '@prisma/client';
import { PrismaService } from './service';
// both `PrismaService` and `PrismaClient` can be injected
// only `PrismaClient` can be injected
const clientProvider: Provider = {
provide: PrismaClient,
useExisting: PrismaService,
useClass: PrismaService,
};
@Global()
@Module({
providers: [PrismaService, clientProvider],
exports: [PrismaService, clientProvider],
providers: [clientProvider],
exports: [clientProvider],
})
export class PrismaModule {}
export { PrismaService } from './service';
@@ -18,11 +18,12 @@ import {
Resolver,
} from '@nestjs/graphql';
import type { User, UserInvoice, UserSubscription } from '@prisma/client';
import { PrismaClient } from '@prisma/client';
import { groupBy } from 'lodash-es';
import { Auth, CurrentUser, Public } from '../../core/auth';
import { UserType } from '../../core/users';
import { Config, PrismaService } from '../../fundamentals';
import { Config } from '../../fundamentals';
import { decodeLookupKey, SubscriptionService } from './service';
import {
InvoiceStatus,
@@ -303,7 +304,7 @@ export class SubscriptionResolver {
export class UserSubscriptionResolver {
constructor(
private readonly config: Config,
private readonly db: PrismaService
private readonly db: PrismaClient
) {}
@ResolveField(() => UserSubscriptionType, { nullable: true })
@@ -7,10 +7,11 @@ import type {
UserStripeCustomer,
UserSubscription,
} from '@prisma/client';
import { PrismaClient } from '@prisma/client';
import Stripe from 'stripe';
import { FeatureManagementService } from '../../core/features';
import { EventEmitter, PrismaService } from '../../fundamentals';
import { EventEmitter } from '../../fundamentals';
import { ScheduleManager } from './schedule';
import {
InvoiceStatus,
@@ -56,7 +57,7 @@ export class SubscriptionService {
constructor(
private readonly stripe: Stripe,
private readonly db: PrismaService,
private readonly db: PrismaClient,
private readonly scheduleManager: ScheduleManager,
private readonly event: EventEmitter,
private readonly features: FeatureManagementService
+2 -3
View File
@@ -4,14 +4,13 @@ import { randomUUID } from 'node:crypto';
import { Transformer } from '@napi-rs/image';
import type { INestApplication } from '@nestjs/common';
import { hashSync } from '@node-rs/argon2';
import { type User } from '@prisma/client';
import { PrismaClient, type User } from '@prisma/client';
import ava, { type TestFn } from 'ava';
import type { Express } from 'express';
import request from 'supertest';
import { AppModule } from '../src/app.module';
import { FeatureManagementService } from '../src/core/features';
import { PrismaService } from '../src/fundamentals/prisma';
import { createTestingApp } from './utils';
const gql = '/graphql';
@@ -52,7 +51,7 @@ test.beforeEach(async t => {
imports: [AppModule],
tapModule(builder) {
builder
.overrideProvider(PrismaService)
.overrideProvider(PrismaClient)
.useClass(FakePrisma)
.overrideProvider(FeatureManagementService)
.useValue({ canEarlyAccess: () => true });
@@ -7,7 +7,6 @@ import {
getLatestMailMessage,
} from '@affine-test/kit/utils/cloud';
import { TestingModule } from '@nestjs/testing';
import { PrismaClient } from '@prisma/client';
import ava, { type TestFn } from 'ava';
import { AuthService } from '../src/core/auth/service';
@@ -20,13 +19,6 @@ const test = ava as TestFn<{
skip: boolean;
}>;
// cleanup database before each test
test.beforeEach(async () => {
const client = new PrismaClient();
await client.$connect();
await client.user.deleteMany({});
});
test.beforeEach(async t => {
t.context.module = await createTestingModule({
imports: [
+23 -146
View File
@@ -1,126 +1,11 @@
import { randomUUID } from 'node:crypto';
import type { INestApplication } from '@nestjs/common';
import { hashSync } from '@node-rs/argon2';
import { type User } from '@prisma/client';
import { PrismaClient } from '@prisma/client';
import ava, { type TestFn } from 'ava';
import Sinon from 'sinon';
import { AppModule } from '../src/app.module';
import { FeatureKind, FeatureManagementService } from '../src/core/features';
import { Quotas } from '../src/core/quota';
import { FeatureManagementService } from '../src/core/features';
import { MailService } from '../src/fundamentals/mailer';
import {
createTestingApp,
createWorkspace,
getInviteInfo,
inviteUser,
signUp,
} from './utils';
const FakePrisma = {
fakeUser: {
id: randomUUID(),
name: 'Alex Yang',
avatarUrl: '',
email: 'alex.yang@example.org',
password: hashSync('123456'),
emailVerified: new Date(),
createdAt: new Date(),
} satisfies User,
get user() {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const prisma = this;
return {
async findFirst() {
return null;
},
async create({ data }: any) {
return {
...prisma.fakeUser,
...data,
};
},
async findUnique() {
return prisma.fakeUser;
},
};
},
get workspace() {
return {
id: randomUUID(),
async create({ data }: any) {
return {
id: this.id,
public: data.public ?? false,
createdAt: new Date(),
};
},
};
},
snapshot: {
id: randomUUID(),
async create() {},
async findFirstOrThrow() {
return { id: this.id, blob: Buffer.from([0, 0]) };
},
},
get workspaceUserPermission() {
return {
id: randomUUID(),
prisma: this,
async count() {
return 1;
},
async create() {
return { id: this.id };
},
async findUniqueOrThrow() {
return { id: this.id, workspaceId: this.prisma.workspace.id };
},
async findFirst() {
return { id: this.id };
},
async findFirstOrThrow() {
return { id: this.id, user: this.prisma.fakeUser };
},
async workspaceUserPermission() {
return {
id: randomUUID(),
createdAt: new Date(),
};
},
};
},
get features() {
return {
async findFirst() {
return {
id: 1,
type: FeatureKind.Quota,
feature: Quotas[0].feature,
configs: Quotas[0].configs,
version: Quotas[0].version,
createdAt: new Date(),
};
},
};
},
get userFeatures() {
return {
async findFirst() {
return {
createdAt: new Date(),
featureId: 1,
reason: '',
expiredAt: null,
};
},
};
},
};
import { createTestingApp, createWorkspace, inviteUser, signUp } from './utils';
const test = ava as TestFn<{
app: INestApplication;
mail: MailService;
@@ -130,15 +15,11 @@ test.beforeEach(async t => {
const { module, app } = await createTestingApp({
imports: [AppModule],
tapModule: module => {
module
.overrideProvider(PrismaClient)
.useValue(FakePrisma)
.overrideProvider(FeatureManagementService)
.useValue({
hasWorkspaceFeature() {
return false;
},
});
module.overrideProvider(FeatureManagementService).useValue({
hasWorkspaceFeature() {
return false;
},
});
},
});
@@ -153,38 +34,34 @@ test.afterEach.always(async t => {
test('should send invite email', async t => {
const { mail, app } = t.context;
if (mail.hasConfigured()) {
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
const u2 = await signUp(app, 'u2', 'u2@affine.pro', '1');
const workspace = await createWorkspace(app, u1.token.token);
const inviteId = await inviteUser(
const stub = Sinon.stub(mail, 'sendMail');
await inviteUser(
app,
u1.token.token,
workspace.id,
u2.email,
'Admin'
'Admin',
true
);
const inviteInfo = await getInviteInfo(app, u1.token.token, inviteId);
t.true(stub.calledOnce);
const resp = await mail.sendInviteEmail(
'production@toeverything.info',
inviteId,
{
workspace: {
id: inviteInfo.workspace.id,
name: inviteInfo.workspace.name,
avatar: '',
},
user: {
avatar: inviteInfo.user?.avatarUrl || '',
name: inviteInfo.user?.name || '',
},
}
const args = stub.args[0][0];
t.is(args.to, u2.email);
t.true(
args.subject!.startsWith(
`${u1.name} invited you to join` /* we don't know the name of mocked workspace */
)
);
t.is(resp.accepted.length, 1, 'failed to send invite email');
}
t.pass();
});