Skip to content

Commit badc857

Browse files
Move modules to moduleLoader format (calcom#23740)
1 parent 7fe80dd commit badc857

7 files changed

Lines changed: 181 additions & 44 deletions

File tree

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,11 @@
1-
import { DI_TOKENS } from "@calcom/lib/di/tokens";
21
import type { LuckyUserService } from "@calcom/lib/server/getLuckyUser";
3-
import { prismaModule } from "@calcom/prisma/prisma.module";
42

53
import { createContainer } from "../di";
6-
import { attributeRepositoryModule } from "../modules/Attribute";
7-
import { bookingRepositoryModule } from "../modules/Booking";
8-
import { hostRepositoryModule } from "../modules/Host";
9-
import { luckyUserServiceModule } from "../modules/LuckyUser";
10-
import { oooRepositoryModule } from "../modules/Ooo";
11-
import { userRepositoryModule } from "../modules/User";
4+
import { moduleLoader as luckyUserServiceModuleLoader } from "../modules/LuckyUser";
125

136
const container = createContainer();
14-
container.load(DI_TOKENS.PRISMA_MODULE, prismaModule);
15-
container.load(DI_TOKENS.BOOKING_REPOSITORY_MODULE, bookingRepositoryModule);
16-
container.load(DI_TOKENS.HOST_REPOSITORY_MODULE, hostRepositoryModule);
17-
container.load(DI_TOKENS.OOO_REPOSITORY_MODULE, oooRepositoryModule);
18-
container.load(DI_TOKENS.USER_REPOSITORY_MODULE, userRepositoryModule);
19-
container.load(DI_TOKENS.ATTRIBUTE_REPOSITORY_MODULE, attributeRepositoryModule);
20-
container.load(DI_TOKENS.LUCKY_USER_SERVICE_MODULE, luckyUserServiceModule);
217

228
export function getLuckyUserService() {
23-
return container.get<LuckyUserService>(DI_TOKENS.LUCKY_USER_SERVICE);
9+
luckyUserServiceModuleLoader.loadModule(container);
10+
return container.get<LuckyUserService>(luckyUserServiceModuleLoader.token);
2411
}

packages/lib/di/di.ts

Lines changed: 85 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import type { Container, Module } from "@evyweb/ioctopus";
22
import { createContainer, createModule } from "@evyweb/ioctopus";
33

4+
export type ModuleLoader = { token: string | symbol; loadModule: (container: Container) => void };
5+
46
/**
57
* A type-safe alternative to module.bind(token).toClass(classs, deps) that automatically ensures that all dependencies required by the Class are provided.
6-
* It assumes that dependencies are stored under the `deps` property of the Class, which is a good convention to follow anyway
8+
* It assumes that dependencies are passed to the constructor as an object map(e.g. new MyService(deps))
79
*
810
* @returns A function that can be used to load the dependencies into the container automatically.
911
*/
1012
// eslint-disable-next-line @typescript-eslint/no-explicit-any
11-
export function bindModuleToClassOnToken<TClass extends new (...args: any[]) => any>({
13+
export function bindModuleToClassOnToken<TClass extends new (deps: any) => any>({
1214
module,
1315
token,
1416
classs,
@@ -20,18 +22,92 @@ export function bindModuleToClassOnToken<TClass extends new (...args: any[]) =>
2022
token: string | symbol;
2123
classs: TClass;
2224
depsMap: Record<
23-
keyof InstanceType<TClass>["deps"],
24-
{ token: string | symbol; loadModule: (container: Container) => void }
25+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
26+
keyof (TClass extends new (deps: infer TDeps) => any ? TDeps : never),
27+
ModuleLoader
2528
>;
29+
}): (container: Container) => void;
30+
31+
/**
32+
* A type-safe alternative to module.bind(token).toClass(classs, deps) that automatically ensures that all dependencies required by the Class are provided.
33+
* It assumes that there is a single dependency, passed as argument to the constructor
34+
*
35+
* @returns A function that can be used to load the dependencies into the container automatically.
36+
*/
37+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
38+
export function bindModuleToClassOnToken<TClass extends new (deps: any) => any>({
39+
module,
40+
token,
41+
classs,
42+
dep,
43+
moduleToken,
44+
}: {
45+
module: Module;
46+
moduleToken: string | symbol;
47+
token: string | symbol;
48+
classs: TClass;
49+
dep: ModuleLoader;
50+
}): (container: Container) => void;
51+
52+
/**
53+
* A type-safe alternative to module.bind(token).toClass(classs, deps) that automatically ensures that all dependencies required by the Class are provided.
54+
* @returns A function that can be used to load the dependencies into the container automatically.
55+
*/
56+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
57+
export function bindModuleToClassOnToken<TClass extends new (deps: any) => any>({
58+
module,
59+
token,
60+
classs,
61+
depsMap,
62+
dep,
63+
moduleToken,
64+
}: {
65+
module: Module;
66+
moduleToken: string | symbol;
67+
token: string | symbol;
68+
classs: TClass;
69+
/**
70+
* When the constructor of the class accept deps as the argument which is a Record of many dependencies
71+
*/
72+
depsMap?: Record<
73+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
74+
keyof (TClass extends new (deps: infer TDeps) => any ? TDeps : never),
75+
ModuleLoader
76+
>;
77+
/**
78+
* When the constructor of the class accept a single dependency and is the only argument to constructor.
79+
*/
80+
dep?: ModuleLoader;
2681
}) {
27-
const depsObject = Object.fromEntries(Object.entries(depsMap).map(([key, value]) => [key, value.token]));
28-
module.bind(token).toClass(classs, depsObject);
82+
if (dep && depsMap) {
83+
throw new Error(
84+
"Cannot provide both 'dep' and 'depsMap'. Use 'dep' for single dependency or 'depsMap' for multiple dependencies."
85+
);
86+
}
87+
88+
if (!dep && !depsMap) {
89+
throw new Error(
90+
"Must provide either 'dep' for single dependency or 'depsMap' for multiple dependencies."
91+
);
92+
}
93+
94+
if (dep) {
95+
module.bind(token).toClass(classs, [dep.token]);
96+
} else if (depsMap) {
97+
const depsObject = Object.fromEntries(Object.entries(depsMap).map(([key, value]) => [key, value.token]));
98+
module.bind(token).toClass(classs, depsObject);
99+
}
29100

30101
return function loadModule(container: Container) {
31102
container.load(moduleToken, module);
32-
for (const key in depsMap) {
33-
const loadModule = depsMap[key as keyof typeof depsMap].loadModule;
34-
loadModule(container);
103+
104+
if (dep) {
105+
dep.loadModule(container);
106+
} else if (depsMap) {
107+
for (const key in depsMap) {
108+
const loadModule = depsMap[key as keyof typeof depsMap].loadModule;
109+
loadModule(container);
110+
}
35111
}
36112
};
37113
}
Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,21 @@
11
import { DI_TOKENS } from "@calcom/lib/di/tokens";
22
import { PrismaAttributeRepository } from "@calcom/lib/server/repository/PrismaAttributeRepository";
3+
import { moduleLoader as prismaModuleLoader } from "@calcom/prisma/prisma.module";
34

4-
import { createModule } from "../di";
5+
import { createModule, bindModuleToClassOnToken, type ModuleLoader } from "../di";
56

67
export const attributeRepositoryModule = createModule();
7-
attributeRepositoryModule
8-
.bind(DI_TOKENS.ATTRIBUTE_REPOSITORY)
9-
.toClass(PrismaAttributeRepository, [DI_TOKENS.PRISMA_CLIENT]);
8+
const token = DI_TOKENS.ATTRIBUTE_REPOSITORY;
9+
const moduleToken = DI_TOKENS.ATTRIBUTE_REPOSITORY_MODULE;
10+
const loadModule = bindModuleToClassOnToken({
11+
module: attributeRepositoryModule,
12+
moduleToken,
13+
token,
14+
classs: PrismaAttributeRepository,
15+
dep: prismaModuleLoader,
16+
});
17+
18+
export const moduleLoader: ModuleLoader = {
19+
token,
20+
loadModule,
21+
};

packages/lib/di/modules/Host.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,21 @@
11
import { DI_TOKENS } from "@calcom/lib/di/tokens";
22
import { HostRepository } from "@calcom/lib/server/repository/host";
3+
import { moduleLoader as prismaModuleLoader } from "@calcom/prisma/prisma.module";
34

4-
import { createModule } from "../di";
5+
import { createModule, bindModuleToClassOnToken, type ModuleLoader } from "../di";
56

67
export const hostRepositoryModule = createModule();
7-
hostRepositoryModule.bind(DI_TOKENS.HOST_REPOSITORY).toClass(HostRepository, [DI_TOKENS.PRISMA_CLIENT]);
8+
const token = DI_TOKENS.HOST_REPOSITORY;
9+
const moduleToken = DI_TOKENS.HOST_REPOSITORY_MODULE;
10+
const loadModule = bindModuleToClassOnToken({
11+
module: hostRepositoryModule,
12+
moduleToken,
13+
token,
14+
classs: HostRepository,
15+
dep: prismaModuleLoader,
16+
});
17+
18+
export const moduleLoader: ModuleLoader = {
19+
token,
20+
loadModule,
21+
};
Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,33 @@
11
import { DI_TOKENS } from "@calcom/lib/di/tokens";
22
import { LuckyUserService } from "@calcom/lib/server/getLuckyUser";
33

4-
import { createModule } from "../di";
5-
6-
export const luckyUserServiceModule = createModule();
7-
luckyUserServiceModule.bind(DI_TOKENS.LUCKY_USER_SERVICE).toClass(LuckyUserService, {
8-
bookingRepository: DI_TOKENS.BOOKING_REPOSITORY,
9-
hostRepository: DI_TOKENS.HOST_REPOSITORY,
10-
oooRepository: DI_TOKENS.OOO_REPOSITORY,
11-
userRepository: DI_TOKENS.USER_REPOSITORY,
12-
attributeRepository: DI_TOKENS.ATTRIBUTE_REPOSITORY,
4+
import { bindModuleToClassOnToken, createModule, type ModuleLoader } from "../di";
5+
import { moduleLoader as attributeRepositoryModuleLoader } from "./Attribute";
6+
import { moduleLoader as bookingRepositoryModuleLoader } from "./Booking";
7+
import { moduleLoader as hostRepositoryModuleLoader } from "./Host";
8+
import { moduleLoader as oooRepositoryModuleLoader } from "./Ooo";
9+
import { moduleLoader as userRepositoryModuleLoader } from "./User";
10+
11+
const thisModule = createModule();
12+
const token = DI_TOKENS.LUCKY_USER_SERVICE;
13+
const moduleToken = DI_TOKENS.LUCKY_USER_SERVICE_MODULE;
14+
const loadModule = bindModuleToClassOnToken({
15+
module: thisModule,
16+
moduleToken,
17+
token,
18+
classs: LuckyUserService,
19+
depsMap: {
20+
bookingRepository: bookingRepositoryModuleLoader,
21+
hostRepository: hostRepositoryModuleLoader,
22+
oooRepository: oooRepositoryModuleLoader,
23+
userRepository: userRepositoryModuleLoader,
24+
attributeRepository: attributeRepositoryModuleLoader,
25+
},
1326
});
27+
28+
export const moduleLoader: ModuleLoader = {
29+
token,
30+
loadModule,
31+
};
32+
33+
export type { LuckyUserService };

packages/lib/di/modules/Ooo.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,21 @@
11
import { DI_TOKENS } from "@calcom/lib/di/tokens";
22
import { PrismaOOORepository } from "@calcom/lib/server/repository/ooo";
3+
import { moduleLoader as prismaModuleLoader } from "@calcom/prisma/prisma.module";
34

4-
import { createModule } from "../di";
5+
import { createModule, bindModuleToClassOnToken, type ModuleLoader } from "../di";
56

67
export const oooRepositoryModule = createModule();
7-
oooRepositoryModule.bind(DI_TOKENS.OOO_REPOSITORY).toClass(PrismaOOORepository, [DI_TOKENS.PRISMA_CLIENT]);
8+
const token = DI_TOKENS.OOO_REPOSITORY;
9+
const moduleToken = DI_TOKENS.OOO_REPOSITORY_MODULE;
10+
const loadModule = bindModuleToClassOnToken({
11+
module: oooRepositoryModule,
12+
moduleToken,
13+
token,
14+
classs: PrismaOOORepository,
15+
dep: prismaModuleLoader,
16+
});
17+
18+
export const moduleLoader: ModuleLoader = {
19+
token,
20+
loadModule,
21+
};

packages/lib/di/modules/User.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,21 @@
11
import { DI_TOKENS } from "@calcom/lib/di/tokens";
22
import { UserRepository } from "@calcom/lib/server/repository/user";
3+
import { moduleLoader as prismaModuleLoader } from "@calcom/prisma/prisma.module";
34

4-
import { createModule } from "../di";
5+
import { createModule, bindModuleToClassOnToken, type ModuleLoader } from "../di";
56

67
export const userRepositoryModule = createModule();
7-
userRepositoryModule.bind(DI_TOKENS.USER_REPOSITORY).toClass(UserRepository, [DI_TOKENS.PRISMA_CLIENT]);
8+
const token = DI_TOKENS.USER_REPOSITORY;
9+
const moduleToken = DI_TOKENS.USER_REPOSITORY_MODULE;
10+
const loadModule = bindModuleToClassOnToken({
11+
module: userRepositoryModule,
12+
moduleToken,
13+
token,
14+
classs: UserRepository,
15+
dep: prismaModuleLoader,
16+
});
17+
18+
export const moduleLoader: ModuleLoader = {
19+
token,
20+
loadModule,
21+
};

0 commit comments

Comments
 (0)