import "reflect-metadata"; import {ConstructorOf, Model} from "@sharkitek/core"; import {AutoRetriever} from "./AutoRetriever"; /** * Interface of a model with a repository. */ export interface ModelWithRepository { /** * Get the model repository name. */ getRepositoryName(): string; /** * Get the model repository. */ getRepository(): ModelRepository; /** * Store the model in its repository. */ store(): void; } /** * Find a model in the corresponding repository or try to retrieve it. * @param modelClass - Class of the model to find. * @param identifier - Identifier of the object to find. * @param retriever - Function called when a model with the given identifier could not be found. */ export async function find(modelClass: ConstructorOf, identifier: IdentifierType, retriever: (identifier: IdentifierType) => Promise = async () => null): Promise { // Getting model repository. const repository = (modelClass as any).getRepository() as ModelRepository; // Trying to get the model from repository. let model = repository.get(String(identifier)); if (!model) { // If there is no model with the given identifier in the repository, trying to get one and registering it if found. model = await retriever(identifier); // Trying to retrieve the model. if (!model && modelClass.prototype.autoRetrieve) // If there is not model after calling the custom retriever, trying to get one with the auto retriever if there is one. model = await (new modelClass() as unknown as AutoRetriever).autoRetrieve(identifier); // The model has been found, registering it. if (model) repository.register(model); } return model; // Returning found model, if there is one. } /** * Add repository capability to Sharkitek models. * @param repositoryName - Model class to extend. */ export function WithRepository(repositoryName: string): (modelClass: typeof Model) => ConstructorOf { return (modelClass) => ( class WithRepository extends modelClass implements ModelWithRepository { /** * Get the model repository name. */ static getRepositoryName(): string { return repositoryName; } /** * Get the model repository. */ static getRepository(): ModelRepository { return ModelsRepositories.get().getRepository(WithRepository.getRepositoryName()); } getRepositoryName(): string { return WithRepository.getRepositoryName(); } getRepository(): ModelRepository { return WithRepository.getRepository() as ModelRepository; } store(): void { WithRepository.getRepository().register(this); } } ); } /** * Models repositories. */ export class ModelsRepositories { private static instance: ModelsRepositories; /** * Get the singleton instance. */ static get(): ModelsRepositories { // If there is no instance, creating one. if (!this.instance) this.instance = new ModelsRepositories(); return this.instance; // Return the main instance. } protected constructor() {} /** * List of all repositories. */ protected repositories: Record = {}; /** * Get repository for the given name. * @param repositoryName - Name of the repository to get. */ getRepository(repositoryName: string): ModelRepository { if (!this.repositories[repositoryName]) // The repository for the given name does not exists, initializing it. this.repositories[repositoryName] = new ModelRepository(); return this.repositories[repositoryName] as ModelRepository; // Return the repository. } } /** * A model repository. */ export class ModelRepository { models: Record = {}; /** * Register the given model in the repository. * @param model - Model to register. */ register(model: T): void { this.models[String(model.getIdentifier())] = model; } /** * Get the model identified by its identifier. * @param identifier - Identifier of the model. */ get(identifier: string): T|null { return typeof this.models?.[identifier] !== "undefined" // Model exists, returning it. ? this.models[identifier] // Model does not exists, returning NULL. : null; } }