Skip to content

Commit

Permalink
sdk: fix next.js dynamic imports for feature loading
Browse files Browse the repository at this point in the history
  • Loading branch information
maany committed Jun 29, 2023
1 parent 1e917e6 commit de53879
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 67 deletions.
9 changes: 7 additions & 2 deletions src/lib/infrastructure/ioc/container-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ import SwitchAccountInputPort from "@/lib/core/port/primary/switch-account-input
import SwitchAccountUseCase from "@/lib/core/use-case/switch-account-usecase";
import SwitchAccountController, { ISwitchAccountController } from "../controller/switch-account-controller";
import SwitchAccountPresenter from "../presenter/switch-account-presenter";
import { loadFeatures } from "@/lib/sdk/ioc-helpers";
import { loadFeatures, loadFeaturesSync } from "@/lib/sdk/ioc-helpers";
import ListDidsFeature from "./features/list-dids-feature";
import LoginConfigFeature from "./features/logic-config-feature";


/**
Expand All @@ -50,7 +52,10 @@ appContainer.bind<DIDGatewayOutputPort>(GATEWAYS.DID).to(RucioDIDGateway);
appContainer.bind<EnvConfigGatewayOutputPort>(GATEWAYS.ENV_CONFIG).to(EnvConfigGateway);
appContainer.bind<StreamGatewayOutputPort>(GATEWAYS.STREAM).to(StreamingGateway).inRequestScope();

loadFeatures(appContainer)
loadFeaturesSync(appContainer, [
new ListDidsFeature(appContainer),
new LoginConfigFeature(appContainer)
])

appContainer.bind<UserPassLoginInputPort>(INPUT_PORT.USERPASS_LOGIN).to(UserPassLoginUseCase).inRequestScope();
appContainer.bind<IUserPassLoginController>(CONTROLLERS.USERPASS_LOGIN).to(UserPassLoginController);
Expand Down
63 changes: 34 additions & 29 deletions src/lib/infrastructure/ioc/features/list-dids-feature.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,47 @@
import DIDGatewayOutputPort from "@/lib/core/port/secondary/did-gateway-output-port";
import { ListDIDsError, ListDIDsRequest, ListDIDsResponse } from "@/lib/core/usecase-models/list-dids-usecase-models";
import { ListDIDsControllerParameters } from "@/lib/infrastructure/controller/list-dids-controller";
import { ListDIDsViewModel } from "@/lib/infrastructure/data/view-model/list-did";
import { BaseStreamableFeature, IOCSymbols } from "@/lib/sdk/ioc-helpers";
import GATEWAYS from "@/lib/infrastructure/ioc/ioc-symbols-gateway";
import CONTROLLERS from "@/lib/infrastructure/ioc/ioc-symbols-controllers";
import INPUT_PORT from "@/lib/infrastructure/ioc/ioc-symbols-input-port";
import USECASE_FACTORY from "@/lib/infrastructure/ioc/ioc-symbols-usecase-factory";
import { Container } from "inversify";
import ListDIDsController from "@/lib/infrastructure/controller/list-dids-controller";
import ListDIDsUseCase from "@/lib/core/use-case/list-dids-usecase";
import ListDIDsPresenter from "../../presenter/list-dids-presenter";

import DIDGatewayOutputPort from '@/lib/core/port/secondary/did-gateway-output-port'
import {
ListDIDsError,
ListDIDsRequest,
ListDIDsResponse,
} from '@/lib/core/usecase-models/list-dids-usecase-models'
import { ListDIDsControllerParameters } from '@/lib/infrastructure/controller/list-dids-controller'
import { ListDIDsViewModel } from '@/lib/infrastructure/data/view-model/list-did'
import {
BaseStreamableFeature,
IFeature,
IOCSymbols,
} from '@/lib/sdk/ioc-helpers'
import GATEWAYS from '@/lib/infrastructure/ioc/ioc-symbols-gateway'
import CONTROLLERS from '@/lib/infrastructure/ioc/ioc-symbols-controllers'
import INPUT_PORT from '@/lib/infrastructure/ioc/ioc-symbols-input-port'
import USECASE_FACTORY from '@/lib/infrastructure/ioc/ioc-symbols-usecase-factory'
import { Container } from 'inversify'
import ListDIDsController from '@/lib/infrastructure/controller/list-dids-controller'
import ListDIDsUseCase from '@/lib/core/use-case/list-dids-usecase'
import ListDIDsPresenter from '../../presenter/list-dids-presenter'

export default class ListDidsFeature extends BaseStreamableFeature<
ListDIDsControllerParameters,
ListDIDsRequest,
ListDIDsResponse,
ListDIDsError,
ListDIDsViewModel> {
constructor( appContainer: Container ) {
const didGateway = appContainer.get<DIDGatewayOutputPort>(GATEWAYS.DID)
const symbols: IOCSymbols = {
CONTROLLER: CONTROLLERS.LIST_DIDS,
USECASE_FACTORY: USECASE_FACTORY.LIST_DIDS,
INPUT_PORT: INPUT_PORT.LIST_DIDS,
}
ListDIDsViewModel
> {
constructor(appContainer: Container) {
const didGateway = appContainer.get<DIDGatewayOutputPort>(GATEWAYS.DID)
const symbols: IOCSymbols = {
CONTROLLER: CONTROLLERS.LIST_DIDS,
USECASE_FACTORY: USECASE_FACTORY.LIST_DIDS,
INPUT_PORT: INPUT_PORT.LIST_DIDS,
}
super(
appContainer,
'ListDIDs',
ListDIDsController,
ListDIDsUseCase,
[
didGateway,
],
[didGateway],
ListDIDsPresenter,
false,
symbols
symbols,
)
}

}
}
7 changes: 2 additions & 5 deletions src/lib/infrastructure/ioc/features/logic-config-feature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,15 @@ export default class LoginConfigFeature extends BaseFeature<
LoginConfigError,
LoginViewModel
> {
constructor(
appContainer: Container,

) {
constructor(appContainer: Container) {
const envGateway: EnvConfigGateway = appContainer.get<EnvConfigGatewayOutputPort>(GATEWAYS.ENV_CONFIG)
const symbols: IOCSymbols = {
CONTROLLER: CONTROLLERS.LOGIN_CONFIG,
USECASE_FACTORY: USECASE_FACTORY.LOGIN_CONFIG,
INPUT_PORT: INPUT_PORT.LOGIN_CONFIG,
}
super(
appContainer,
"LoginConfig",
LoginConfigController,
LoginConfigUseCase,
[
Expand Down
104 changes: 73 additions & 31 deletions src/lib/sdk/ioc-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@ export type IOCSymbols = {
INPUT_PORT: symbol, // A symbol for the input port dependency.
}


/**
* A base interface for loadable features in the web application.
*/
export interface IFeature{
name: string
/**
* Load the feature into the IoC container.
* @param appContainer The IoC container for the application.
*/
load(appContainer: Container): void;
}


/**
* A base class for features in a web application. The IOC bindings for the clean architecture components
* of the feature are generated automatically.
Expand All @@ -36,7 +50,7 @@ TControllerParams extends TParameters,
TErrorModel,
TViewModel,
>
{
implements IFeature {
/**
* Creates a new instance of the `BaseFeature` class.
* @template TControllerParams The type of the parameters for the controller.
Expand All @@ -53,22 +67,28 @@ TControllerParams extends TParameters,
* @param symbols An object that contains symbols for the different types of dependencies in the IoC container.
*/
public constructor(
appContainer: Container,
Controller: new (useCaseFactory: TUseCaseFactory<TRequestModel>) => BaseController<TControllerParams, TRequestModel>,
UseCase: new (presenter: BaseOutputPort<TResponseModel, TErrorModel>, ...args: any[]) => TUseCase<TRequestModel>,
useCaseContructorArgs: any[] = [],
Presenter: new (response: NextApiResponse, session?: IronSession) => BasePresenter<TResponseModel, TErrorModel, TViewModel>,
passSessionToPresenter: boolean = false,
symbols: IOCSymbols,
) {
public name: string,
private Controller: new (useCaseFactory: TUseCaseFactory<TRequestModel>) => BaseController<TControllerParams, TRequestModel>,
private UseCase: new (presenter: BaseOutputPort<TResponseModel, TErrorModel>, ...args: any[]) => TUseCase<TRequestModel>,
private useCaseContructorArgs: any[] = [],
private Presenter: new (response: NextApiResponse, session?: IronSession) => BasePresenter<TResponseModel, TErrorModel, TViewModel>,
private passSessionToPresenter: boolean = false,
private symbols: IOCSymbols,
) {}

/**
* Load this feature into the IoC container.
* @param appContainer The IoC container for the application.
*/
load(appContainer: Container): void {
this.createIOCBindings<TControllerParams, TRequestModel, TResponseModel, TErrorModel, TViewModel>(
appContainer,
Controller,
UseCase,
useCaseContructorArgs,
Presenter,
passSessionToPresenter,
symbols,
this.Controller,
this.UseCase,
this.useCaseContructorArgs,
this.Presenter,
this.passSessionToPresenter,
this.symbols,
)
}

Expand Down Expand Up @@ -141,7 +161,7 @@ export class BaseStreamableFeature<
TResponseModel extends BaseResponseModel,
TErrorModel,
TViewModel extends BaseViewModel,
> {
> implements IFeature{
/**
* Creates a new instance of the `BaseStreamableFeature` class.
* @param appContainer The IoC container for the application.
Expand All @@ -153,22 +173,28 @@ export class BaseStreamableFeature<
* @param symbols An object that contains symbols for the different types of dependencies in the IoC container.
*/
constructor(
appContainer: Container,
Controller: new (useCaseFactory: TUseCaseFactory<TRequestModel>) => BaseController<TControllerParams, TRequestModel>,
UseCase: new (presenter: BaseStreamingOutputPort<TResponseModel, TErrorModel>, ...args: any[]) => TUseCase<TRequestModel>,
useCaseContructorArgs: any[] = [],
Presenter: new (response: NextApiResponse, session?: IronSession) => BaseStreamingPresenter<TResponseModel, TViewModel, TErrorModel>,
passSessionToPresenter: boolean = false,
symbols: IOCSymbols,
) {
public name: string,
private Controller: new (useCaseFactory: TUseCaseFactory<TRequestModel>) => BaseController<TControllerParams, TRequestModel>,
private UseCase: new (presenter: BaseStreamingOutputPort<TResponseModel, TErrorModel>, ...args: any[]) => TUseCase<TRequestModel>,
private useCaseContructorArgs: any[] = [],
private Presenter: new (response: NextApiResponse, session?: IronSession) => BaseStreamingPresenter<TResponseModel, TViewModel, TErrorModel>,
private passSessionToPresenter: boolean = false,
private symbols: IOCSymbols,
) {}

/**
* Load this feature into the IoC container.
* @param appContainer The IoC container for the application.
*/
public load(appContainer: Container): void {
this.createIOCBindings<TControllerParams, TRequestModel, TResponseModel, TErrorModel, TViewModel>(
appContainer,
Controller,
UseCase,
useCaseContructorArgs,
Presenter,
passSessionToPresenter,
symbols,
this.Controller,
this.UseCase,
this.useCaseContructorArgs,
this.Presenter,
this.passSessionToPresenter,
this.symbols,
)
}

Expand Down Expand Up @@ -225,6 +251,9 @@ export class BaseStreamableFeature<
* Loads features from the features directory into the IoC Container.
* @param appContainer The IoC container for the application.
* @param featuresDir The directory to load features from. Defaults to `src/lib/infrastructure/ioc/features`.
* @deprecated NextJS Compiler does not support server side dynamic imports.
* The modules cannot be found at runtime as .next directory contains its own dynnamic file structure.
* Use loadFeaturesSync instead.
*/
export async function loadFeatures(appContainer: Container, featuresDir?: string) {
const FEATURES_PATH = featuresDir || path.join(process.cwd(), 'src', 'lib', 'infrastructure', 'ioc', 'features');
Expand Down Expand Up @@ -257,4 +286,17 @@ export async function loadFeatures(appContainer: Container, featuresDir?: string
throw error;
}
}
}
}

export function loadFeaturesSync(appContainer: Container, features: IFeature[]) {
console.log(`Loading ${features.length} features`)
for ( const feature of features ) {
try {
console.log(`Loading feature ${feature.name}`)
feature.load(appContainer);
} catch (error) {
console.error(`Error loading feature ${feature.name}: ${error}`)
throw error;
}
}
}

0 comments on commit de53879

Please sign in to comment.