├── docs ├── web │ ├── .txt │ └── mandarine │ │ ├── getting-started │ │ ├── prerequisites.md │ │ └── installation.md │ │ ├── thanksto.md │ │ ├── mandarine-core │ │ ├── exposed-injectables.md │ │ ├── exposed-injectable-logger.md │ │ ├── native-components-list.md │ │ ├── mandarine-core.md │ │ └── native-components.md │ │ ├── mandarine-mvc │ │ ├── resource-handler-registry.md │ │ ├── accessing-mvc-handler.md │ │ ├── mvc-controllers.md │ │ ├── adding-oak-middleware.md │ │ ├── mvc-controllers-parameterized-routes.md │ │ ├── response-status-decorator.md │ │ ├── built-in-response-time-header.md │ │ ├── resource-resolver.md │ │ ├── mandarine-mvc-introduction.md │ │ └── entry-point.md │ │ ├── mandarine-cli │ │ ├── new.md │ │ ├── generate.md │ │ ├── run.md │ │ └── introduction.md │ │ ├── main-concepts │ │ ├── lifecycle.md │ │ ├── mandarine-json-file.md │ │ ├── hello-world.md │ │ ├── project-structure.md │ │ ├── component-service.md │ │ ├── component-component.md │ │ ├── dot-env-file.md │ │ ├── component-configuration.md │ │ ├── component-repository.md │ │ ├── getting-started-introduction.md │ │ ├── component-controllers.md │ │ ├── main-configuration.md │ │ ├── custom-properties-decorator.md │ │ ├── component-middleware.md │ │ ├── component-catch.md │ │ ├── custom-properties.md │ │ ├── environmental-variables.md │ │ ├── components.md │ │ ├── value-decorator.md │ │ └── fundamentals.md │ │ ├── mandarine-security │ │ ├── security-expressions.md │ │ ├── authentication-managerbuilder.md │ │ ├── authentication-userdetailsservice.md │ │ └── authentication-userdetails.md │ │ ├── mandarine-data │ │ ├── data-repositories.md │ │ ├── data-updating.md │ │ └── mandarine-data-introduction.md │ │ ├── faq.md │ │ └── plugins │ │ └── promise-repeater.md ├── testing │ └── helloWorld.txt └── style_guide.md ├── orange-test.json ├── appBuilder.ts ├── .gitignore ├── tests ├── integration-tests │ ├── files │ │ ├── orm-tests-mysql │ │ │ ├── mandarine.json │ │ │ └── properties.json │ │ ├── orm-tests-pg │ │ │ ├── mandarine.json │ │ │ ├── .env │ │ │ ├── tsconfig.json │ │ │ └── properties.json │ │ ├── customPropertiesConfig.json │ │ ├── cors.ts │ │ ├── customDecorator.ts │ │ ├── customPropertiesAndValue.ts │ │ ├── templates.ts │ │ ├── sessionCounter.ts │ │ ├── events.ts │ │ ├── websocketServer.ts │ │ ├── pipes.ts │ │ ├── nestedInjections.ts │ │ ├── websocketClient.ts │ │ ├── resourceHandlerStaticContent.ts │ │ ├── microservices │ │ │ ├── redis.ts │ │ │ ├── nats.ts │ │ │ └── rabbitMq.ts │ │ ├── customCatch.ts │ │ ├── timers.ts │ │ ├── routeParam.ts │ │ └── manualInjection.ts │ ├── resources │ │ └── openapi.yml │ ├── eventlistener_test.ts │ ├── nestedInjection_test.ts │ ├── manualInjection_test.ts │ ├── pipe_test.ts │ ├── customDecorator_test.ts │ ├── openapi_test.ts │ ├── valueConfigurationProperties_test.ts │ ├── resourceHandlerStaticContent_test.ts │ ├── timer_test.ts │ ├── templatest_test.ts │ ├── websocket_test.ts │ └── customCatch_test.ts ├── unit-tests │ ├── files │ │ ├── props.properties │ │ ├── yamlConf.yaml │ │ └── jsonConf.json │ ├── utils │ │ ├── yamlUtils_test.ts │ │ └── propertiesUtils_test.ts │ └── github-issues │ │ └── 210_test.ts ├── app-builder-tests │ ├── service.ts │ ├── servicewhatever.ts │ ├── controller.ts │ └── decoratorFinder.ts └── copyright.ts ├── tsconfig.json ├── main-core ├── utils │ ├── utilTypes.ts │ ├── componentUtils.ts │ ├── yamlUtils.ts │ ├── components │ │ ├── middlewareUtil.ts │ │ ├── websocketClient.ts │ │ └── websocketServer.ts │ └── propertiesUtils.ts ├── microservices │ ├── mod.ts │ └── microservices.ns.ts ├── Mandarine.miscellaneous.ns.ts ├── dependency-injection │ ├── internals │ │ └── getPipes.ts │ └── decorators │ │ ├── injectable.ts │ │ └── Inject.ts ├── exceptions │ ├── mandarineAuthException.ts │ ├── routingException.ts │ ├── componentExceptions.ts │ ├── templateEngineException.ts │ ├── invalidAnnotationError.ts │ └── mandarineSecurityException.ts ├── decorators │ ├── native-components │ │ └── override.ts │ ├── configuration-readers │ │ ├── configurationProperties.ts │ │ └── value.ts │ ├── stereotypes │ │ ├── events │ │ │ └── dom │ │ │ │ └── eventListener.ts │ │ ├── guards │ │ │ └── guard.ts │ │ ├── service │ │ │ └── service.ts │ │ ├── component │ │ │ └── component.ts │ │ ├── catch │ │ │ └── catch.ts │ │ ├── configuration │ │ │ └── configuration.ts │ │ └── middleware │ │ │ └── Middleware.ts │ └── tasks │ │ └── taskScheduler.ts ├── Mandarine.native.ns.ts ├── internals │ ├── core │ │ └── timers.ts │ ├── methods │ │ └── executePipe.ts │ └── interfaces │ │ ├── exceptionFilter.ts │ │ ├── guardTarget.ts │ │ └── middlewareTarget.ts ├── components │ ├── repository-component │ │ └── repositoryComponent.ts │ └── component-component │ │ └── componentComponent.ts ├── mandarine-native │ ├── tasks │ │ └── taskManager.ts │ └── sessions │ │ ├── mandarineSessionContainer.ts │ │ └── mandarineSessionHandlers.ts ├── proxys │ └── dependencyInjectionDecorator.ts ├── mandarineLoading.ts ├── MandarineEnvConstants.ts └── timer-registry │ └── timerRegistry.ts ├── cli.ts ├── cli └── v1 │ └── v1.0.0.ts ├── mvc-framework ├── core │ ├── interfaces │ │ ├── mandarine │ │ │ └── mandarineAnnotationMetadataContext.ts │ │ └── http │ │ │ └── cookie.ts │ ├── utils │ │ ├── mvc.utils.ts │ │ ├── templateUtils.ts │ │ └── mandarine │ │ │ └── routingUtils.ts │ ├── decorators │ │ └── stereotypes │ │ │ ├── controller │ │ │ ├── responseStatus.ts │ │ │ ├── corsMiddlewareDecorator.ts │ │ │ ├── pipeDecorator.ts │ │ │ ├── useGuards.ts │ │ │ ├── useMiddleware.ts │ │ │ └── controller.ts │ │ │ └── view-engine │ │ │ └── viewEngineDecorators.ts │ ├── mandarineMvcContext.ts │ ├── modules │ │ └── view-engine │ │ │ └── viewModel.ts │ └── middlewares │ │ ├── authMiddleware.ts │ │ └── responseTimeHeaderMiddleware.ts └── openapi │ ├── openApiMod.ts │ ├── decorators │ ├── apiServer.ts │ ├── apiExternalDoc.ts │ ├── parameter.ts │ ├── operation.ts │ ├── apiResponse.ts │ └── apiTag.ts │ └── openapi-global.ts ├── deps.ts ├── security-core ├── core │ ├── internals │ │ └── permissions │ │ │ ├── validators │ │ │ ├── isAuthenticated.ts │ │ │ └── hasRole.ts │ │ │ └── permissionValidatorsRegistry.ts │ ├── decorators │ │ └── allowOnly.ts │ ├── modules │ │ └── loginHandler.ts │ └── proxys │ │ └── securityCoreDecorators.ts ├── encoders │ └── bcryptEncoder.ts └── utils │ ├── auth.util.ts │ └── securityUtils.ts ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── workflows │ ├── copyright.yml │ ├── integration.yml │ ├── automatic_build.yml │ ├── microservices.yml │ └── ci.yml ├── orm-core ├── core │ ├── decorators │ │ ├── Repository.ts │ │ └── entityDecorators.ts │ └── exceptions │ │ └── mandarineORMException.ts ├── nosql │ └── mongoDbService.ts └── repository │ └── mandarineRepository.ts └── LICENSE /docs/web/.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /orange-test.json: -------------------------------------------------------------------------------- 1 | { 2 | "showExceptions": true 3 | } -------------------------------------------------------------------------------- /appBuilder.ts: -------------------------------------------------------------------------------- 1 | export { AppBuilder } from "./main-core/app-builder/appBuilder.ts"; -------------------------------------------------------------------------------- /docs/testing/helloWorld.txt: -------------------------------------------------------------------------------- 1 | Hello there, if you have got here, you'd better go back!. -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | .tests/ 3 | .playground/ 4 | /playground 5 | .mandarine_target/ 6 | /mandarine_target -------------------------------------------------------------------------------- /tests/integration-tests/files/orm-tests-mysql/mandarine.json: -------------------------------------------------------------------------------- 1 | { 2 | "propertiesFilePath": "./properties.json" 3 | } -------------------------------------------------------------------------------- /tests/integration-tests/files/orm-tests-pg/mandarine.json: -------------------------------------------------------------------------------- 1 | { 2 | "propertiesFilePath": "./properties.json" 3 | } -------------------------------------------------------------------------------- /tests/integration-tests/files/orm-tests-pg/.env: -------------------------------------------------------------------------------- 1 | MANDARINE_JSON_FILE=mandarine.json 2 | MANDARINE_PROPERTY_FILE=properties.json -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "experimentalDecorators": true, 4 | "emitDecoratorMetadata": true 5 | } 6 | } -------------------------------------------------------------------------------- /tests/integration-tests/files/customPropertiesConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "my": { 3 | "data": { 4 | "database": "ANY-DATABASE" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /tests/unit-tests/files/props.properties: -------------------------------------------------------------------------------- 1 | mandarine.server.host=127.0.0.1 2 | mandarine.server.port=8080 3 | mandarine.enabled=true 4 | mandarine.auth.data.config.username=andreespirela -------------------------------------------------------------------------------- /main-core/utils/utilTypes.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | export type ClassType = { new (...params: any): any }; -------------------------------------------------------------------------------- /tests/integration-tests/files/orm-tests-pg/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "experimentalDecorators": true, 4 | "emitDecoratorMetadata": true 5 | } 6 | } -------------------------------------------------------------------------------- /main-core/microservices/mod.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | export * as Microlemon from "https://deno.land/x/microlemon@v2.0.3/mod.ts"; -------------------------------------------------------------------------------- /docs/web/mandarine/getting-started/prerequisites.md: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | Please make sure that [Deno](https://deno.land) (>= 1.4.6) is installed on your operating system. 3 | > Lower versions may bring incompatibility issues. 4 | -------------------------------------------------------------------------------- /tests/unit-tests/files/yamlConf.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | mandarine: 3 | server: 4 | host: 127.0.0.1 5 | port: 8080 6 | enabled: true 7 | auth: 8 | data: 9 | config: 10 | username: andreespirela 11 | -------------------------------------------------------------------------------- /cli.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Process } from "https://raw.githubusercontent.com/mandarineorg/mandarine-cli/master/mod.ts"; 4 | 5 | if (import.meta.main) { 6 | Process(); 7 | } -------------------------------------------------------------------------------- /tests/app-builder-tests/service.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Component } from "https://deno.land/x/mandarinets@v2.3.1/mod.ts"; 4 | 5 | @Component() 6 | export class MyService2 { 7 | 8 | } -------------------------------------------------------------------------------- /cli/v1/v1.0.0.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Process } from "https://raw.githubusercontent.com/mandarineorg/mandarine-cli/v1.0.0/mod.ts"; 4 | 5 | if (import.meta.main) { 6 | Process(); 7 | } -------------------------------------------------------------------------------- /docs/web/mandarine/thanksto.md: -------------------------------------------------------------------------------- 1 | # Special Thanks To 2 | A list of people Mandarine owes a lot!. Without these awesome collaborators, Mandarine would not be the same. 3 | 4 | ---------- 5 | 6 | - [Jorge Baez](https://www.instagram.com/jabadesigner/) for his beautiful designs. -------------------------------------------------------------------------------- /tests/app-builder-tests/servicewhatever.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Component } from "https://deno.land/x/mandarinets@v2.3.1/mod.ts"; 4 | 5 | @Component() 6 | export class MyService2 { 7 | 8 | } -------------------------------------------------------------------------------- /mvc-framework/core/interfaces/mandarine/mandarineAnnotationMetadataContext.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | export interface AnnotationMetadataContext { 4 | type: "ROUTE" | "PARAM"; 5 | context: any; 6 | } -------------------------------------------------------------------------------- /docs/web/mandarine/mandarine-core/exposed-injectables.md: -------------------------------------------------------------------------------- 1 | # Exposed Injectables 2 | 3 | ----------- 4 | 5 | 6 | Mandarine has a list of injectables (components that can be used through [Dependency Injection](https://www.mandarinets.org/docs/master/mandarine/dependency-injection)) that you may use in your application. -------------------------------------------------------------------------------- /tests/unit-tests/files/jsonConf.json: -------------------------------------------------------------------------------- 1 | { 2 | "mandarine": { 3 | "server": { 4 | "host": "127.0.0.1", 5 | "port": 8080 6 | }, 7 | "enabled": true, 8 | "auth": { 9 | "data": { 10 | "config": { 11 | "username": "andreespirela" 12 | } 13 | } 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /main-core/Mandarine.miscellaneous.ns.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | export namespace MandarineMiscellaneous { 4 | 5 | export interface WalkJson { 6 | path: string; 7 | typeof: string; 8 | isArray: boolean; 9 | isPlainObject: boolean; 10 | } 11 | } -------------------------------------------------------------------------------- /tests/integration-tests/files/orm-tests-mysql/properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "mandarine": { 3 | "dataSource": { 4 | "dialect": "mysql", 5 | "data": { 6 | "host": "127.0.0.1", 7 | "port": 33306, 8 | "username": "root", 9 | "password": "Changeme1", 10 | "database": "mandarine" 11 | } 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /tests/integration-tests/files/orm-tests-pg/properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "mandarine": { 3 | "dataSource": { 4 | "dialect": "postgresql", 5 | "data": { 6 | "host": "127.0.0.1", 7 | "port": 5432, 8 | "username": "postgres", 9 | "password": "Changeme1", 10 | "database": "postgres" 11 | } 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /deps.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | // @ts-ignore 4 | export { Application, Cookies, Request, Router, Context, Middleware, isHttpError, Status, FormDataReader, send, Response, ListenOptionsTls, ListenOptionsBase } from "https://raw.githubusercontent.com/mandarineorg/mandarinets-modules/master/oak/7.3.0/mod.ts"; 5 | export { Leaf } from "https://deno.land/x/leaf@v1.0.3/mod.ts"; -------------------------------------------------------------------------------- /tests/app-builder-tests/controller.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Controller, GET, Service} from "https://deno.land/x/mandarinets@v2.3.1/mod.ts"; 4 | 5 | @Controller() 6 | export class MyController { 7 | @GET('/hello') 8 | public hello() { 9 | return "Hi"; 10 | } 11 | } 12 | 13 | @Service() 14 | export class MyService { 15 | 16 | } -------------------------------------------------------------------------------- /security-core/core/internals/permissions/validators/isAuthenticated.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import type { Mandarine } from "../../../../../main-core/Mandarine.ns.ts"; 4 | 5 | export const isAuthenticated: Mandarine.Security.Auth.PermissionValidator = (request: any, authentication: any) => { 6 | return () => { 7 | return authentication !== undefined; 8 | } 9 | } -------------------------------------------------------------------------------- /docs/web/mandarine/mandarine-core/exposed-injectable-logger.md: -------------------------------------------------------------------------------- 1 | # Logger as Injectable 2 | With Mandarine exposing its internal logger as an injectable, you may write content on the console keeping the format Mandarine has. 3 | 4 | --------- 5 | 6 | ## Usage 7 | 8 | ```typescript 9 | import { Log } from "https://deno.land/x/mandarinets@v2.3.2/mod.ts"; 10 | 11 | @Service() 12 | export class MyService { 13 | constructor(logger: Log) {} 14 | } 15 | ``` -------------------------------------------------------------------------------- /docs/web/mandarine/mandarine-mvc/resource-handler-registry.md: -------------------------------------------------------------------------------- 1 | # Resource Handler Registry 2 | 3 | ----- 4 | 5 | The Resource Handler Registry ([See interface here](https://doc.deno.land/https/raw.githubusercontent.com/mandarineorg/mandarinets/master/main-core/Mandarine.ns.ts#Mandarine.MandarineCore.IResourceHandlerRegistry)) is a container where all the [resource handlers](/docs/mandarine/resource-handler) are located. These resource handlers will be listed & processed by an internal middleware. 6 | -------------------------------------------------------------------------------- /main-core/dependency-injection/internals/getPipes.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Reflect } from "../../reflectMetadata.ts" 4 | import { MandarineConstants } from "../../mandarineConstants.ts" 5 | 6 | export const getPipes = (target: any, paramIndex: number, methodName: string) => { 7 | return Reflect.getMetadata(`${MandarineConstants.REFLECTION_MANDARINE_PIPE_FIELD}:${paramIndex}:${methodName}`, target, methodName); 8 | } -------------------------------------------------------------------------------- /main-core/exceptions/mandarineAuthException.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import type { Mandarine } from "../Mandarine.ns.ts"; 4 | 5 | export class MandarineAuthenticationException extends Error { 6 | 7 | constructor(public message: string, public authException: Mandarine.Security.Auth.AuthExceptions) { 8 | super(message); 9 | this.name = "MandarineAuthenticationException"; 10 | this.stack = (this).stack; 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /main-core/microservices/microservices.ns.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | import * as Microlemon from "./mod.ts"; 3 | 4 | export namespace Microservices { 5 | 6 | export import Transporters = Microlemon.Transporters; 7 | 8 | export namespace Clients { 9 | export const AmqpClient = Microlemon.AmqpClient; 10 | export const RedisClient = Microlemon.RedisClient; 11 | export const NatsClient = Microlemon.NatsClient; 12 | } 13 | 14 | 15 | } -------------------------------------------------------------------------------- /mvc-framework/openapi/openApiMod.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | export { ApiExternalDoc } from "./decorators/apiExternalDoc.ts"; 4 | export { ApiResponse } from "./decorators/apiResponse.ts"; 5 | export { ApiServer } from "./decorators/apiServer.ts"; 6 | export { ApiTag } from "./decorators/apiTag.ts"; 7 | export { ApiOperation } from "./decorators/operation.ts"; 8 | export { ApiParameter } from "./decorators/parameter.ts"; 9 | export { OpenAPIBuilder } from "./openApiBuilder.ts"; -------------------------------------------------------------------------------- /main-core/decorators/native-components/override.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import type { Mandarine } from "../../Mandarine.ns.ts"; 4 | import { MainCoreDecoratorProxy } from "../../proxys/mainCoreDecorator.ts"; 5 | 6 | export const Override = (type?: Mandarine.MandarineCore.NativeComponents) => { 7 | return (targetClass: any) => { 8 | MainCoreDecoratorProxy.overrideNativeComponent(targetClass, type); 9 | } 10 | }; -------------------------------------------------------------------------------- /docs/web/mandarine/mandarine-cli/new.md: -------------------------------------------------------------------------------- 1 | # `mandarine new` 2 | Creates a Mandarine-powered application based on Mandarine's Project Structure. 3 | 4 | ----- 5 | 6 | **Syntax:** 7 | 8 | ```shell script 9 | $ mandarine new [OPTIONS] 10 | ``` 11 | 12 | **Options** 13 | 14 | | Option | Description | Required | 15 | | ------ | ----------- | -------- | 16 | | `--directory (-d)` | Indicates the directory where the mandarine-powered application will be created. | No 17 | | `--force (-f)` | Forces the overwriting of existing files in directory. | No 18 | -------------------------------------------------------------------------------- /docs/web/mandarine/main-concepts/lifecycle.md: -------------------------------------------------------------------------------- 1 | # Lifecycle 2 | 3 | Mandarine's lifecycle refers to all process that is executed during Mandarine Compile Time (MCT) & the initialization of your application. 4 | 5 | The responsible cores for bootstrapping a Mandarine-powered application are: **Core** & **MVC**. The rest of the cores such as **Data** and **Security** are called indirectly. 6 | 7 | ------------- 8 |
9 | 10 | -------------------------------------------------------------------------------- /main-core/utils/componentUtils.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { ComponentsRegistryUtil } from "../components-registry/componentRegistry.util.ts"; 4 | import { Mandarine } from "../Mandarine.ns.ts"; 5 | 6 | export class ComponentUtils { 7 | 8 | public static createControllerComponent(configuration: any, classHandler: any) { 9 | ComponentsRegistryUtil.registerComponent(classHandler, Mandarine.MandarineCore.ComponentTypes.CONTROLLER, configuration, null); 10 | } 11 | 12 | } -------------------------------------------------------------------------------- /mvc-framework/openapi/decorators/apiServer.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { addOpenAPILoaderInfo } from "../openapi-global.ts"; 4 | import { OpenAPIServer } from "../openapi-spec.ts"; 5 | 6 | export const ApiServer = (apiServer: OpenAPIServer) => { 7 | return (target: any, methodName?: string) => { 8 | addOpenAPILoaderInfo(target, { 9 | type: "ApiServer", 10 | methodName: methodName, 11 | data: apiServer 12 | }) 13 | } 14 | } -------------------------------------------------------------------------------- /security-core/core/decorators/allowOnly.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import type { Mandarine } from "../../../main-core/Mandarine.ns.ts"; 4 | import { SecurityCoreDecoratorsProxy } from "../proxys/securityCoreDecorators.ts"; 5 | 6 | export const AllowOnly = (permissions: Mandarine.Security.Auth.Permissions) => { 7 | return (target: any, methodName?: string) => { 8 | SecurityCoreDecoratorsProxy.registerAllowOnlyDecorator(target, permissions, methodName); 9 | } 10 | } -------------------------------------------------------------------------------- /mvc-framework/core/utils/mvc.utils.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import type { Mandarine } from "../../../main-core/Mandarine.ns.ts"; 4 | 5 | export class MandarineMVCUtils { 6 | public static buildRequestContextAccessor(context: Mandarine.Types.RequestContext): Mandarine.Types.RequestContextAcessor { 7 | return { 8 | getFullContext: () => context, 9 | getRequest: () => context.request, 10 | getResponse: () => context.response 11 | }; 12 | } 13 | } -------------------------------------------------------------------------------- /mvc-framework/core/decorators/stereotypes/controller/responseStatus.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import type { Mandarine } from "../../../../../main-core/Mandarine.ns.ts"; 4 | import { MVCDecoratorsProxy } from "../../../proxys/mvcCoreDecorators.ts"; 5 | 6 | export const ResponseStatus = (httpCode: Mandarine.MandarineMVC.HttpStatusCode): Function => { 7 | return (target: any, methodName?: string) => { 8 | MVCDecoratorsProxy.registerResponseStatusDecorator(target, httpCode, methodName); 9 | }; 10 | } -------------------------------------------------------------------------------- /mvc-framework/core/mandarineMvcContext.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | export class MandarineMVCContext { 4 | 5 | public static mvcContextInstance: MandarineMVCContext; 6 | 7 | public routeSignatures: Array> = new Array>(); 8 | 9 | public static getInstance(): MandarineMVCContext { 10 | if(!MandarineMVCContext.mvcContextInstance) MandarineMVCContext.mvcContextInstance = new MandarineMVCContext(); 11 | return MandarineMVCContext.mvcContextInstance; 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /mvc-framework/openapi/decorators/apiExternalDoc.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { addOpenAPILoaderInfo } from "../openapi-global.ts"; 4 | import { OpenAPIExternalDocs } from "../openapi-spec.ts"; 5 | 6 | export const ApiExternalDoc = (apiExternalDoc: OpenAPIExternalDocs) => { 7 | return (target: any, methodName: string) => { 8 | addOpenAPILoaderInfo(target, { 9 | type: "ApiExternalDoc", 10 | methodName: methodName, 11 | data: apiExternalDoc 12 | }) 13 | } 14 | } -------------------------------------------------------------------------------- /docs/web/mandarine/mandarine-mvc/accessing-mvc-handler.md: -------------------------------------------------------------------------------- 1 | # Accessing MVC Handler 2 | The MVC handler is a property that gives you full access to the Oak HTTP Dispatcher handler. This may be useful when deploying your application in providers like Vercel. 3 | 4 | ---- 5 | 6 | ## API 7 | You can access the MVC handler by invoking the property `handle` part of Mandarine's MVC Core. 8 | 9 | ```typescript 10 | 11 | import { MandarineCore } from "https://deno.land/x/mandarinets@v2.3.2/mod.ts"; 12 | 13 | ... 14 | 15 | let handler = new MandarineCore().MVC().handle; 16 | 17 | export default handler; 18 | ``` -------------------------------------------------------------------------------- /mvc-framework/core/decorators/stereotypes/controller/corsMiddlewareDecorator.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import type { Mandarine } from "../../../../../main-core/Mandarine.ns.ts"; 4 | import { MVCDecoratorsProxy } from "../../../proxys/mvcCoreDecorators.ts"; 5 | 6 | export const Cors = (corsOptions: Mandarine.MandarineMVC.CorsMiddlewareOption): Function => { 7 | return (target: any, methodName?: string) => { 8 | MVCDecoratorsProxy.registerCORSMiddlewareDecorator(target, corsOptions, methodName); 9 | }; 10 | } -------------------------------------------------------------------------------- /main-core/decorators/configuration-readers/configurationProperties.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { MainCoreDecoratorProxy } from "../../proxys/mainCoreDecorator.ts"; 4 | 5 | /** 6 | * **Decorator** 7 | * Defines what configuration file component will be using. 8 | * 9 | * `@ConfigurationProperties('./src/config/myconfig.json')` 10 | */ 11 | export const ConfigurationProperties = (path: string) => { 12 | return (target: any) => { 13 | MainCoreDecoratorProxy.configurationPropertiesDecorator(target, path); 14 | } 15 | } -------------------------------------------------------------------------------- /main-core/Mandarine.native.ns.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { WebMVCConfigurer } from "./mandarine-native/mvc/webMvcConfigurer.ts"; 4 | 5 | export namespace MandarineNative { 6 | /** 7 | * Responsible for handling internal behaviors related to the MVC Core. 8 | * With `WebMvcConfigurer` you may configure the behavior of functionalities such as: 9 | * - Built-in authentication 10 | * - Resource Handlers 11 | * - Session containers 12 | * - ... 13 | */ 14 | export class WebMvcConfigurer extends WebMVCConfigurer {} 15 | } -------------------------------------------------------------------------------- /security-core/core/internals/permissions/validators/hasRole.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import type { Mandarine } from "../../../../../main-core/Mandarine.ns.ts"; 4 | 5 | export const hasRole: Mandarine.Security.Auth.PermissionValidator = (request: any, authentication: any) => { 6 | return (inputs: Array): boolean => { 7 | if(authentication) { 8 | if(authentication.AUTH_PRINCIPAL) { 9 | return authentication.AUTH_PRINCIPAL.roles.includes(inputs[0]); 10 | } 11 | } 12 | return false; 13 | } 14 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea or improvement to this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **[Context]** 11 | A clear and concise description of what you want to happen & why. 12 | 13 | **[Alternatives]** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **[Currently implemented]** 17 | List the current implementations (if any) of this requested feature in other frameworks. 18 | 19 | **[Additional context]** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /main-core/decorators/stereotypes/events/dom/eventListener.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { MainCoreDecoratorProxy } from "../../../../proxys/mainCoreDecorator.ts"; 4 | 5 | /** 6 | * **Decorator** 7 | * 8 | * Register a handler for the specified EventTarget 9 | * 10 | * ```@EventListener('event') 11 | * Target: Method``` 12 | */ 13 | export const EventListener = (eventName: string): Function => { 14 | return (target: any, methodName: string) => { 15 | MainCoreDecoratorProxy.registerEventListener(target, eventName, methodName); 16 | }; 17 | }; -------------------------------------------------------------------------------- /main-core/exceptions/routingException.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | export class RoutingException extends Error { 4 | 5 | public static EXISTENT_ACTION: string = "The action cannot be created because one already exists. Routing Methods must not have the same name."; 6 | public static NONVALID_COMPONENT: string = "The action cannot be created because the component is not a valid controller."; 7 | 8 | constructor(public message: string) { 9 | super(message); 10 | this.name = "RoutingException"; 11 | this.stack = (this).stack; 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /main-core/exceptions/componentExceptions.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | export class ComponentExceptions extends Error { 4 | 5 | public static EXISTENT_COMPONENT: string = "A component with the same name (%component%) has been registered already. Components cannot be duplicated."; 6 | public static NON_VALID_INJECTABLE: string = "Component cannot be injected because it is not a known component"; 7 | 8 | constructor(public message: string) { 9 | super(message); 10 | this.name = "ComponentExceptions"; 11 | this.stack = (this).stack; 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /main-core/decorators/stereotypes/guards/guard.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Mandarine } from "../../../Mandarine.ns.ts"; 4 | import { MainCoreDecoratorProxy } from "../../../proxys/mainCoreDecorator.ts"; 5 | 6 | /** 7 | * **Decorator** 8 | * 9 | * Register a Guard component for the DI Container 10 | * 11 | * `@Guard() 12 | * Target: class` 13 | */ 14 | export const Guard = (): Function => { 15 | return (target: any) => { 16 | MainCoreDecoratorProxy.registerMandarinePoweredComponent(target, Mandarine.MandarineCore.ComponentTypes.GUARDS, {}, null); 17 | }; 18 | }; -------------------------------------------------------------------------------- /mvc-framework/openapi/decorators/parameter.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { addOpenAPILoaderInfo, openAPIApplicationBuilder } from "../openapi-global.ts"; 4 | import { OpenAPIParameter } from "../openapi-spec.ts"; 5 | 6 | export const ApiParameter = (parameter: OpenAPIParameter) => { 7 | return (target: any, methodName: string, parameterIndex: number) => { 8 | addOpenAPILoaderInfo(target, { 9 | type: "ApiParameter", 10 | methodName: methodName, 11 | parameterIndex: parameterIndex, 12 | data: parameter 13 | }) 14 | } 15 | } -------------------------------------------------------------------------------- /main-core/exceptions/templateEngineException.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | export class TemplateEngineException extends Error { 4 | 5 | public static INVALID_TEMPLATE_PROCESSING: string = "The template could not be read because it has not been initialized or it is not valid."; 6 | public static INVALID_ENGINE: string = "The template could not be processed because the engine is not recognizable by Mandarine"; 7 | 8 | constructor(public message: string) { 9 | super(message); 10 | this.name = "TemplateEngineException"; 11 | this.stack = (this).stack; 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /docs/web/mandarine/mandarine-core/native-components-list.md: -------------------------------------------------------------------------------- 1 | # List of Native Components 2 | On this section, you will find the list of all Mandarine native components. 3 | 4 | ----- 5 | 6 | ## List 7 | 8 | | Class | Members | Description | 9 | | ----- | ------- | ----------- | 10 | | Mandarine.Native.WebMvcConfigurer | `getSessionContainer()`
`addResourceHandlers()`
`authManagerBuilder(injectedProvider: Mandarine.Security.Auth.AuthenticationManagerBuilder)`
`httpLoginBuilder(injectedProvider: Mandarine.Security.Core.Modules.LoginBuilder)` | Responsible for loading the default session container for Mandarine's session Middleware, and default resource handlers for Mandarine MVC | -------------------------------------------------------------------------------- /main-core/dependency-injection/decorators/injectable.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { DependencyInjectionDecoratorsProxy } from "../../proxys/dependencyInjectionDecorator.ts"; 4 | 5 | /** 6 | * **Decorator** 7 | * 8 | * Defines a manual injection for non-mandarine components. 9 | * https://github.com/mandarineorg/mandarinets/wiki/Manual-Components 10 | * 11 | * `@Injectable() 12 | * Target: method` 13 | */ 14 | export const Injectable = () => { 15 | return (target: any, methodName: string) => { 16 | DependencyInjectionDecoratorsProxy.registerInjectableDecorator(target, methodName); 17 | } 18 | } -------------------------------------------------------------------------------- /security-core/encoders/bcryptEncoder.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import type { Mandarine } from "../../main-core/Mandarine.ns.ts"; 4 | import * as BCrypt from "../hash/bcrypt/bcrypt.ts"; 5 | 6 | export class BcryptEncoder implements Mandarine.Security.Crypto.PasswordEncoder { 7 | 8 | private salt: string = BCrypt.gensalt(); 9 | 10 | public encode(rawPassword: string): string { 11 | return BCrypt.hashpw(rawPassword, this.salt); 12 | } 13 | 14 | public matches(rawPassword: string, encodedPassword: string): boolean { 15 | return BCrypt.checkpw(rawPassword, encodedPassword); 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /main-core/exceptions/invalidAnnotationError.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | export class InvalidAnnotationError extends Error { 4 | 5 | public static CLASS_ONLY_ANNOTATION: string = "The annotation is not valid in the current context. Annotation should only be used for class type."; 6 | public static METHOD_ONLY_ANNOTATION: string = "The annotation is not valid in the current context. Annotation should only be used for method type."; 7 | 8 | constructor(public message: string) { 9 | super(`${message}`); 10 | this.name = "InvalidAnnotationError"; 11 | this.stack = (this).stack; 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /docs/web/mandarine/main-concepts/mandarine-json-file.md: -------------------------------------------------------------------------------- 1 | # `Mandarine.json` File 2 | The `mandarine.json` file allows you to override some behaviors that Mandarine has by default, such as the default location of Mandarine's properties file. 3 | 4 | ---- 5 | 6 | ## File 7 | A `mandarine.json` file looks like this: 8 | ```typescript 9 | { 10 | propertiesFilePath: string; 11 | denoEnv: { 12 | [prop: string]: string 13 | } 14 | } 15 | ``` 16 | 17 | - propertiesFilePath 18 | - Overrides Mandarine's default properties file location. 19 | - denoEnv 20 | - Adds environmental variables to `Deno.env`. It essentially works as a [`.env` file](/docs/mandarine/dot-env-file). 21 | -------------------------------------------------------------------------------- /main-core/decorators/stereotypes/service/service.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Mandarine } from "../../../Mandarine.ns.ts"; 4 | import { MainCoreDecoratorProxy } from "../../../proxys/mainCoreDecorator.ts"; 5 | 6 | /** 7 | * **Decorator** 8 | * 9 | * Register a component type Service in the DI Container 10 | * 11 | * `@Service() 12 | * Target: class` 13 | */ 14 | export const Service = (): Function => { 15 | return (target: any, methodName: string, index: number) => { 16 | MainCoreDecoratorProxy.registerMandarinePoweredComponent(target, Mandarine.MandarineCore.ComponentTypes.SERVICE, {}, index); 17 | }; 18 | }; -------------------------------------------------------------------------------- /main-core/internals/core/timers.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | export class Timers { 4 | public static readonly MANDARINE_EXPIRED_SESSIONS: string = "MANDARINE_EXPIRED_SESSIONS"; 5 | public static readonly MANDARINE_TIMER_REGISTRY: string = "MANDARINE_TIMER_REGISTRY_{0}"; 6 | public static readonly MANDARINE_MICROSERVICE_HEALTH_INTERVAL: string = "MANDARINE_MICROSERVICE_HEALTH_INTERVAL_{0}"; 7 | public static readonly MANDARINE_AUTOMATIC_HEALTHCHECK_INTERVAL: string = "MANDARINE_AUTOMATIC_HEALTHCHECK_INTERVAL"; 8 | public static readonly MANDARINE_MVC_CACHE_EXPIRATION_INTERVAL: string = "MANDARINE_MVC_CACHE_EXPIRATION_INTERVAL"; 9 | } -------------------------------------------------------------------------------- /main-core/utils/yamlUtils.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import * as YAML from "https://deno.land/std@0.85.0/encoding/yaml.ts"; 4 | 5 | 6 | export class YamlUtils { 7 | 8 | public static yamlFileToJS(path: string) { 9 | try { 10 | const content = Deno.readTextFileSync(path); 11 | return this.yamlToJS(content); 12 | } catch { 13 | return {}; 14 | } 15 | } 16 | 17 | public static yamlToJS(yaml: string): any { 18 | return YAML.parse(yaml); 19 | } 20 | 21 | public static jsToYaml(js: any) { 22 | return YAML.stringify(js); 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /main-core/decorators/stereotypes/component/component.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Mandarine } from "../../../Mandarine.ns.ts"; 4 | import { MainCoreDecoratorProxy } from "../../../proxys/mainCoreDecorator.ts"; 5 | 6 | /** 7 | * **Decorator** 8 | * 9 | * Register a component type Component in the DI Container 10 | * 11 | * `@Component() 12 | * Target: class` 13 | */ 14 | export const Component = (): Function => { 15 | return (target: any, methodName: string, index: number) => { 16 | MainCoreDecoratorProxy.registerMandarinePoweredComponent(target, Mandarine.MandarineCore.ComponentTypes.COMPONENT, {}, index); 17 | }; 18 | }; -------------------------------------------------------------------------------- /mvc-framework/core/decorators/stereotypes/controller/pipeDecorator.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { MVCDecoratorsProxy } from "../../../proxys/mvcCoreDecorators.ts"; 4 | 5 | /** 6 | * **Decorator** 7 | * 8 | * Adds a pipe to the current parameter 9 | * 10 | * `@Pipe() 11 | * Target: Method parameter` 12 | * 13 | * @returns a processed value from its inital input based on the pipe execution. 14 | */ 15 | export const Pipe = (pipes: Array | any): Function => { 16 | return (target: any, propertyName: string, index: number) => { 17 | MVCDecoratorsProxy.registerPipeInParam(target, pipes, propertyName, index); 18 | } 19 | } -------------------------------------------------------------------------------- /main-core/decorators/stereotypes/catch/catch.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Mandarine } from "../../../Mandarine.ns.ts"; 4 | import { MainCoreDecoratorProxy } from "../../../proxys/mainCoreDecorator.ts"; 5 | 6 | /** 7 | * **Decorator** 8 | * 9 | * Register a component type Catch in the DI Container 10 | * 11 | * `@Catch() 12 | * Target: class` 13 | */ 14 | export const Catch = (exception: any): Function => { 15 | return (target: any) => { 16 | MainCoreDecoratorProxy.registerMandarinePoweredComponent(target, Mandarine.MandarineCore.ComponentTypes.CATCH, { 17 | exceptionType: exception 18 | }, null); 19 | }; 20 | }; -------------------------------------------------------------------------------- /mvc-framework/openapi/decorators/operation.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { addOpenAPILoaderInfo, openAPIApplicationBuilder } from "../openapi-global.ts"; 4 | import { OpenAPIOperationObject } from "../openapi-spec.ts"; 5 | 6 | export const ApiOperation = (operation: OpenAPIOperationObject) => { 7 | return (target: any, methodName: string, propertyDesciptor: PropertyDescriptor) => { 8 | 9 | if(!operation.operationId) operation.operationId = methodName; 10 | 11 | addOpenAPILoaderInfo(target, { 12 | type: "ApiOperation", 13 | methodName: methodName, 14 | data: operation 15 | }) 16 | } 17 | } -------------------------------------------------------------------------------- /.github/workflows/copyright.yml: -------------------------------------------------------------------------------- 1 | name: Copyright Test 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | name: Copyright header (${{ matrix.os }}) 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | matrix: 11 | os: [macOS-latest] 12 | deno: ["v1.9.0"] 13 | fail-fast: true 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Setup deno 17 | uses: denolib/setup-deno@master 18 | with: 19 | deno-version: ${{ matrix.deno }} 20 | - name: Verifying copyright header in files 21 | env: 22 | ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' 23 | run: deno test -c tsconfig.json --reload --allow-all --unstable tests/copyright.ts 24 | -------------------------------------------------------------------------------- /.github/workflows/integration.yml: -------------------------------------------------------------------------------- 1 | name: Integration tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | name: tests (${{ matrix.os }}) 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | matrix: 11 | os: [windows-latest, macOS-latest] 12 | deno: ["v1.9.0"] 13 | fail-fast: true 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Setup deno 17 | uses: denolib/setup-deno@master 18 | with: 19 | deno-version: ${{ matrix.deno }} 20 | - name: run tests 21 | env: 22 | ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' 23 | run: deno test -c tsconfig.json --reload --allow-all --unstable --fail-fast tests/integration-tests/ 24 | -------------------------------------------------------------------------------- /main-core/decorators/configuration-readers/value.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Mandarine } from "../../Mandarine.ns.ts"; 4 | import { MainCoreDecoratorProxy } from "../../proxys/mainCoreDecorator.ts"; 5 | 6 | /** 7 | * **Decorator** 8 | * Inject a value from the configuration file. 9 | * 10 | * `@Value('mandarine.server.host')` 11 | */ 12 | export const Value = (configKey: string, scope: Mandarine.MandarineCore.ValueScopes = Mandarine.MandarineCore.ValueScopes.CONFIGURATION): Function => { 13 | return (target: any, propertyName: string) => { 14 | MainCoreDecoratorProxy.valueDecorator(target, configKey, scope, propertyName); 15 | } 16 | }; -------------------------------------------------------------------------------- /main-core/decorators/stereotypes/configuration/configuration.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Mandarine } from "../../../Mandarine.ns.ts"; 4 | import { MainCoreDecoratorProxy } from "../../../proxys/mainCoreDecorator.ts"; 5 | 6 | /** 7 | * **Decorator** 8 | * 9 | * Register a component type Configuration in the DI Container 10 | * 11 | * `@Configuration() 12 | * Target: class` 13 | */ 14 | export const Configuration = (): Function => { 15 | return (target: any, methodName: string, index: number) => { 16 | MainCoreDecoratorProxy.registerMandarinePoweredComponent(target, Mandarine.MandarineCore.ComponentTypes.CONFIGURATION, {}, index); 17 | }; 18 | }; -------------------------------------------------------------------------------- /docs/web/mandarine/main-concepts/hello-world.md: -------------------------------------------------------------------------------- 1 | # Hello World 2 | Mandarine has a very simple & straightforward way to be written, this is what makes Mandarine a great framework when it comes to **architecture challenges**. 3 | 4 | ```typescript 5 | // myFile.ts 6 | 7 | import { MandarineCore, Controller, GET } from "https://deno.land/x/mandarinets@v2.3.2/mod.ts"; 8 | 9 | @Controller() 10 | export class MyController { 11 | 12 | @GET('/hello-world') 13 | public httpHandler() { 14 | return "Welcome to MandarineTS Framework!"; 15 | } 16 | 17 | } 18 | 19 | new MandarineCore().MVC().run(); 20 | ``` 21 | 22 | Terminal 23 | 24 | ```shell script 25 | $ deno run -c tsconfig.json --allow-net --allow-read --allow-env myFile.ts 26 | ``` 27 | -------------------------------------------------------------------------------- /main-core/utils/components/middlewareUtil.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { MandarineException } from "../../exceptions/mandarineException.ts"; 4 | import type { MiddlewareTarget } from "../../internals/interfaces/middlewareTarget.ts"; 5 | 6 | export class MiddlewareUtil { 7 | public static verifyImplementation(middleware: MiddlewareTarget) { 8 | let isImplementationValid = (middleware.onPostRequest && typeof middleware.onPostRequest === 'function') && (middleware.onPreRequest && typeof middleware.onPreRequest === 'function'); 9 | 10 | if(isImplementationValid == undefined) throw new MandarineException(MandarineException.MIDDLEWARE_NON_VALID_IMPL); 11 | } 12 | } -------------------------------------------------------------------------------- /mvc-framework/openapi/decorators/apiResponse.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { addOpenAPILoaderInfo, openAPIApplicationBuilder } from "../openapi-global.ts"; 4 | import { OpenAPIResponse } from "../openapi-spec.ts"; 5 | 6 | export const ApiResponse = (apiResponse: OpenAPIResponse) => { 7 | return (target: any, methodName?: string) => { 8 | 9 | if(!methodName && !apiResponse.responseCode) throw new Error("A response code (response name) must be assigned when using @ApiResponse in a class"); 10 | 11 | addOpenAPILoaderInfo(target, { 12 | type: "ApiResponse", 13 | methodName: methodName, 14 | data: apiResponse 15 | }) 16 | } 17 | } -------------------------------------------------------------------------------- /.github/workflows/automatic_build.yml: -------------------------------------------------------------------------------- 1 | name: Automatic Build & Run Integration Test 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | name: tests (${{ matrix.os }}) 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | matrix: 11 | os: [windows-latest, macOS-latest] 12 | deno: ["v1.9.0"] 13 | fail-fast: true 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Setup deno 17 | uses: denolib/setup-deno@master 18 | with: 19 | deno-version: ${{ matrix.deno }} 20 | - name: run tests 21 | env: 22 | ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' 23 | run: cd tests && cd app-builder-tests && deno test -c ../../tsconfig.json --allow-all --unstable --fail-fast decoratorFinder.ts 24 | -------------------------------------------------------------------------------- /mvc-framework/core/decorators/stereotypes/controller/useGuards.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { MandarineException } from "../../../../../main-core/exceptions/mandarineException.ts"; 4 | import { MVCDecoratorsProxy } from "../../../proxys/mvcCoreDecorators.ts"; 5 | import type { GuardTarget } from "../../../../../main-core/internals/interfaces/guardTarget.ts"; 6 | 7 | export const UseGuards = (guardsList: Array) => { 8 | return (target: any, methodName?: string) => { 9 | if(!guardsList) throw new MandarineException(MandarineException.INVALID_GUARDS_LIST_ANNOTATION); 10 | MVCDecoratorsProxy.registerUseGuardsDecorator(target, guardsList, methodName); 11 | } 12 | } -------------------------------------------------------------------------------- /docs/web/mandarine/main-concepts/project-structure.md: -------------------------------------------------------------------------------- 1 | # Mandarine Project Structure 2 | 3 | ----- 4 | 5 | Mandarine has a default project structure where your files will be located. 6 | > **It is not necessary** to follow the recommended structure. 7 | 8 | **Structure**: 9 | ```text 10 | - MyProject 11 | - src 12 | - main 13 | - mandarine 14 | - app.ts 15 | - resources 16 | - properties.json 17 | - templates 18 | - static 19 | tsconfig.json 20 | ``` 21 | 22 | > Folder Mandarine will contain more folders, each of these will be modules. 23 | 24 | >`./src/main/resources/static` will serve as the folder where static content will be located. 25 | -------------------------------------------------------------------------------- /tests/integration-tests/files/cors.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import {Controller} from "../../../mvc-framework/core/decorators/stereotypes/controller/controller.ts"; 4 | import {PUT} from "../../../mvc-framework/core/decorators/stereotypes/controller/routingDecorator.ts"; 5 | import {Cors} from "../../../mvc-framework/core/decorators/stereotypes/controller/corsMiddlewareDecorator.ts"; 6 | import {MandarineCore} from "../../../main-core/mandarineCore.ts"; 7 | 8 | @Controller() 9 | export class MyController { 10 | 11 | @PUT('/default') 12 | @Cors({origin: 'http://localhost'}) 13 | public default() { 14 | return "CORS-enabled PUT action" 15 | } 16 | } 17 | 18 | new MandarineCore().MVC().run({ port: 1228 }); 19 | -------------------------------------------------------------------------------- /main-core/decorators/stereotypes/middleware/Middleware.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Mandarine } from "../../../Mandarine.ns.ts"; 4 | import { MainCoreDecoratorProxy } from "../../../proxys/mainCoreDecorator.ts"; 5 | 6 | /** 7 | * **Decorator** 8 | * 9 | * Register a component type Middleware in the DI Container 10 | * 11 | * `@Middleware(regexRoute: RegExp) 12 | * Target: class` 13 | */ 14 | export const Middleware = (regexRoute?: RegExp): Function => { 15 | return (target: any, methodName: string, index: number) => { 16 | MainCoreDecoratorProxy.registerMandarinePoweredComponent(target, Mandarine.MandarineCore.ComponentTypes.MIDDLEWARE, { 17 | regexRoute: regexRoute 18 | }, index); 19 | }; 20 | }; -------------------------------------------------------------------------------- /docs/web/mandarine/mandarine-mvc/mvc-controllers.md: -------------------------------------------------------------------------------- 1 | # Controllers 2 | 3 | > See detailed information [here](docs/mandarine/controller). 4 | 5 | Controllers are the only type of component that can handle HTTP requests, which means, they are the only type of components that can handle the creation of routes and interpret the information a route is receiving or sending. 6 | 7 | ---- 8 | 9 | ## Routes 10 | 11 | Routes are endpoints that will be requested by users. Routes can either send to the user or receive information from the user. 12 | 13 | Routes are declared through the use of decorators on top of HTTP handlers. 14 | 15 | ## HTTP Handlers 16 | 17 | HTTP handlers are methods that are executed when an endpoint is requested. They are directly connected with the declared routes as route decorators decorate these kind of methods. 18 | -------------------------------------------------------------------------------- /mvc-framework/core/decorators/stereotypes/controller/useMiddleware.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { MandarineException } from "../../../../../main-core/exceptions/mandarineException.ts"; 4 | import { MVCDecoratorsProxy } from "../../../proxys/mvcCoreDecorators.ts"; 5 | import type { NonComponentMiddlewareTarget } from "../../../../../main-core/internals/interfaces/middlewareTarget.ts"; 6 | 7 | export const UseMiddleware = (middlewareList: Array) => { 8 | return (target: any, methodName?: string) => { 9 | if(!middlewareList) throw new MandarineException(MandarineException.INVALID_MIDDLEWARE_LIST_ANNOTATION); 10 | MVCDecoratorsProxy.registerUseMiddlewareDecorator(target, middlewareList, methodName); 11 | } 12 | } -------------------------------------------------------------------------------- /docs/web/mandarine/main-concepts/component-service.md: -------------------------------------------------------------------------------- 1 | # Components 2 | Mandarine's _Service components_ are part of Mandarine's stereotype, this means, they do not have any special functionality more than making your class a Mandarine-powered class which will then be available for dependency injection. 3 | 4 | Mandarine's stereotypes are also useful for modularity purposes since they allow you to divide your application in different layers. 5 | 6 | ----- 7 |   8 | 9 | ## Concepts 10 | - Services are part of Mandarine's Main Core. 11 | - It accepts injection and it is also injectable. 12 | 13 |   14 | 15 | ## Syntax 16 | 17 | ```typescript 18 | @Service() 19 | ``` 20 | 21 | ## Usage 22 | 23 | ```typescript 24 | import { Service } from "https://deno.land/x/mandarinets@v2.3.2/mod.ts"; 25 | 26 | @Service() 27 | export class MyService { 28 | } 29 | ``` 30 | -------------------------------------------------------------------------------- /main-core/dependency-injection/decorators/Inject.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { DependencyInjectionDecoratorsProxy } from "../../proxys/dependencyInjectionDecorator.ts"; 4 | 5 | /** 6 | * **Decorator** 7 | * 8 | * Defines the context for injection to a property. 9 | * This is used for manual injections, although, all injections should be done through the constructor of a class. 10 | * 11 | * `@Inject(injectableObject?: any) 12 | * Target: property/method parameter` 13 | */ 14 | export const Inject = (): Function => { 15 | return (target: any, propertyName: string, index: number) => { 16 | // If index is not null then it is a parameter otherwise it is a class property. 17 | DependencyInjectionDecoratorsProxy.registerInject(target, propertyName, index); 18 | }; 19 | }; -------------------------------------------------------------------------------- /mvc-framework/core/decorators/stereotypes/view-engine/viewEngineDecorators.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import type { Mandarine } from "../../../../../main-core/Mandarine.ns.ts"; 4 | import { MVCDecoratorsProxy } from "../../../proxys/mvcCoreDecorators.ts"; 5 | 6 | /** 7 | * **Decorator** 8 | * 9 | * This decorator specifies that an HTTP Handler is meant to render a file. 10 | * `@Render(template, options, engine)` 11 | * `Target: Method (Http Handler)` 12 | */ 13 | export const Render = (template: string, options?: Mandarine.MandarineMVC.TemplateEngine.RenderingOptions, engine?: Mandarine.MandarineMVC.TemplateEngine.Engines) => { 14 | return (target: any, methodName: string) => { 15 | MVCDecoratorsProxy.registerRenderHandler(target, methodName, template, engine, options); 16 | }; 17 | }; -------------------------------------------------------------------------------- /docs/web/mandarine/main-concepts/component-component.md: -------------------------------------------------------------------------------- 1 | # Components 2 | Mandarine's _regular components_ are part of Mandarine's stereotype, this means, they do not have any special functionality more than making your class a Mandarine-powered class which will then be available for dependency injection. 3 | 4 | Mandarine's stereotypes are also useful for modularity purposes since they allow you to divide your application in different layers. 5 | 6 | ----- 7 |   8 | 9 | ## Concepts 10 | - Regular Components are part of Mandarine's Main Core. 11 | - It accepts injection and it is also injectable. 12 | 13 |   14 | 15 | ## Syntax 16 | 17 | ```typescript 18 | @Component() 19 | ``` 20 | 21 | ## Usage 22 | 23 | ```typescript 24 | import { Component } from "https://deno.land/x/mandarinets@v2.3.2/mod.ts"; 25 | 26 | @Component() 27 | export class MyComponent { 28 | } 29 | ``` 30 | -------------------------------------------------------------------------------- /docs/web/mandarine/main-concepts/dot-env-file.md: -------------------------------------------------------------------------------- 1 | # `.env` File 2 | Mandarine supports a `.env` file. A `.env` file is essentially a properties file which keys & values will be added to environment variables or `Deno.env`. 3 | 4 | Environmental variables are available throughout the application and can be useful to save configurations that will be needed across different objects. 5 | 6 | ---- 7 | 8 | ## Directions 9 | In order to be able to use a `.env` file with your Mandarine-powered application, your file will **need to be located** under your current working directory (also known as the root of your project). 10 | 11 | ## File example 12 | 13 | ```properties 14 | MY_KEY=My value 15 | MY_KEY_NUMBER_2=My value number #2 16 | ANOTHER_KEY="My value 3" 17 | ``` 18 | 19 |   20 | 21 | ## Lifecycle 22 | This file is resolved at the first stage of Mandarine Compile Time (MCT) 23 | -------------------------------------------------------------------------------- /security-core/core/modules/loginHandler.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import type { Mandarine } from "../../../main-core/Mandarine.ns.ts"; 4 | 5 | export class LoginHandler implements Mandarine.Security.Auth.Handler { 6 | 7 | private buildResponse(result: any, response: any) { 8 | response.body = { 9 | status: result.status, 10 | exception: result.exception, 11 | message: result.message 12 | }; 13 | } 14 | 15 | public onSuccess(request: any, response: any, result: Mandarine.Security.Auth.AuthenticationResult) { 16 | this.buildResponse(result, response); 17 | } 18 | 19 | public onFailure(request: any, response: any, result: Mandarine.Security.Auth.AuthenticationResult) { 20 | this.buildResponse(result, response); 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /docs/web/mandarine/mandarine-mvc/adding-oak-middleware.md: -------------------------------------------------------------------------------- 1 | # Adding Oak Middleware 2 | Since version 1.2.3, Mandarine allows you to add a custom Oak middleware to your application, this is possible because Mandarine runs on top of Oak. 3 | 4 | ---- 5 | 6 | ## API 7 | Before running your application through `new MandarineCore().MVC().run()`, you can use the method `addMiddleware` part of MandarineMVC to add an [Oak Middleware](https://github.com/oakserver/oak#application-middleware-and-context) 8 | 9 | ```typescript 10 | import { MandarineCore } from "https://deno.land/x/mandarinets@v2.3.2/mod.ts"; 11 | 12 | ... 13 | 14 | let MVC = new MandarineCore().MVC(); 15 | 16 | MVC.addMiddleware(async (context: any, next: Function) => { 17 | 18 | }); 19 | ``` 20 | 21 |   22 | 23 | > Note that this is not recommended as it may affect internal functionalities of Mandarine MVC. 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /mvc-framework/openapi/openapi-global.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { OpenAPIBuilder } from "./openApiBuilder.ts"; 4 | 5 | export interface OpenAPILoaderData { 6 | type: "ApiOperation" | "ApiParameter" | "ApiResponse" | "ApiServer" | "ApiExternalDoc" | "ApiTag", 7 | methodName?: string, 8 | parameterIndex?: number, 9 | data: any 10 | } 11 | 12 | export const openAPILoaderInformation: Map> = new Map>(); 13 | 14 | export const addOpenAPILoaderInfo = (component: any, data: OpenAPILoaderData) => { 15 | if(openAPILoaderInformation.has(component)) { 16 | openAPILoaderInformation.get(component)?.push(data); 17 | } else { 18 | openAPILoaderInformation.set(component, [data]); 19 | } 20 | } 21 | 22 | export let openAPIApplicationBuilder = new OpenAPIBuilder(); -------------------------------------------------------------------------------- /main-core/internals/methods/executePipe.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import type { Mandarine } from "../../Mandarine.ns.ts"; 4 | import { ApplicationContext } from "../../application-context/mandarineApplicationContext.ts"; 5 | import { MandarineException } from "../../exceptions/mandarineException.ts"; 6 | 7 | export const executePipe = (pipe: any, valueToProcess: any) => { 8 | const pipeFromDI: Mandarine.Types.PipeTransform | undefined = ApplicationContext.getInstance().getDIFactory().getDependency(pipe); 9 | if(pipeFromDI) { 10 | valueToProcess = pipeFromDI.transform(valueToProcess); 11 | } else if(!pipeFromDI && typeof pipe === 'function') { 12 | valueToProcess = pipe(valueToProcess); 13 | } else { 14 | throw new MandarineException(MandarineException.INVALID_PIPE_EXECUTION); 15 | } 16 | return valueToProcess; 17 | } -------------------------------------------------------------------------------- /tests/integration-tests/files/customDecorator.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Controller } from "../../../mvc-framework/core/decorators/stereotypes/controller/controller.ts"; 4 | import { GET } from "../../../mvc-framework/core/decorators/stereotypes/controller/routingDecorator.ts"; 5 | import { MandarineCore } from "../../../main-core/mandarineCore.ts"; 6 | import { parameterDecoratorFactory } from "../../../mvc-framework/core/decorators/custom-decorators/decoratorsFactory.ts"; 7 | 8 | const myCustomDecorator = parameterDecoratorFactory((context: any, parameter: any) => { 9 | return parameter; 10 | }) 11 | 12 | @Controller() 13 | export class MyController { 14 | @GET('/hello-world') 15 | public httpHandler(@myCustomDecorator('HAHAHA OKEYYY ES OBER!') msg: string) { 16 | return msg; 17 | } 18 | } 19 | 20 | new MandarineCore().MVC().run({ port: 2193 }); -------------------------------------------------------------------------------- /main-core/decorators/tasks/taskScheduler.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { MainCoreDecoratorProxy } from "../../proxys/mainCoreDecorator.ts"; 4 | 5 | /** 6 | * **Decorator** 7 | * Creates a task based on a CRON Expression. 8 | * It is fired for the first time during Mandarine's start-up process 9 | * 10 | */ 11 | export const Scheduled = (cronExpression: string, timeZone?: string) => { 12 | return (target: any, methodName: string) => { 13 | MainCoreDecoratorProxy.registerScheduledTask(target, cronExpression, timeZone, methodName); 14 | } 15 | } 16 | 17 | /** 18 | * **Decorator** 19 | * Creates a timer with a fixed rate in milliseconds. 20 | * 21 | */ 22 | export const Timer = (fixedRateMs: number) => { 23 | return (target: any, methodName: string) => { 24 | MainCoreDecoratorProxy.registerTimer(target, fixedRateMs, methodName); 25 | } 26 | } -------------------------------------------------------------------------------- /docs/web/mandarine/mandarine-cli/generate.md: -------------------------------------------------------------------------------- 1 | # `mandarine generate` 2 | Generates a Mandarine-powered module. 3 | 4 | ----- 5 | 6 | **Syntax:** 7 | 8 | ```shell script 9 | $ mandarine generate [MODULE NAME] [TYPES] 10 | ``` 11 | 12 | **Arguments:** 13 | 14 | 15 | | Argument | Description | 16 | | -------- | ----------- | 17 | | [Module name] | Name of the module to create 18 | 19 |   20 | 21 | **Types:** 22 | 23 | 24 | | Type | Description | 25 | | ---- | ----------- | 26 | | --controller (-c) | Adds a Controller component to the module. 27 | | --service (-s) | Adds a Service component to the module. 28 | | --component | Adds a regular mandarine-powered component to the module. 29 | | --middleware (-m) | Adds a middleware component to the module. 30 | | --repository (-r) | Adds a repository component to the module. 31 | | --model | Adds a mandarine-powered database model to the module. 32 | | --configuration | Adds a configuration component to the module. -------------------------------------------------------------------------------- /docs/web/mandarine/mandarine-mvc/mvc-controllers-parameterized-routes.md: -------------------------------------------------------------------------------- 1 | # Parameterized Routes 2 | Parameterized routes help you develop flexible endpoints as they can receive information from the user and transmit it to the back-end by only using the URL. They are a variable in a url. 3 | 4 | ---- 5 | 6 | ## Concepts 7 | 8 | - The variables in a parameterized route must start with `:` 9 | - We can have as many parameterized routes as we want as long as all our variables start with `:` and have different names 10 | 11 |   12 | 13 | ## API 14 | 15 | ```typescript 16 | import { GET, Controller, RouteParam, MandarineCore } from "https://deno.land/x/mandarinets@v2.3.2/mod.ts"; 17 | 18 | @Controller() 19 | export class MyController { 20 | 21 | @GET('/get-user/:id') 22 | public httpHandler(@RouteParam() id: number) { 23 | return `Hello, user with id ${id}`; 24 | } 25 | 26 | } 27 | 28 | new MandarineCore().MVC().run(); 29 | ``` 30 | -------------------------------------------------------------------------------- /mvc-framework/core/modules/view-engine/viewModel.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | /** 4 | * This class serves as a modeler for templates. 5 | * A modeler is used to make working with templates & its variables easier 6 | */ 7 | export class ViewModel { 8 | 9 | private attributes: Map = new Map(); 10 | 11 | public setAttribute(attribute: string, value: any): void { 12 | this.attributes.set(attribute, value); 13 | } 14 | 15 | public deleteAttribute(attribute: string): void { 16 | this.attributes.delete(attribute); 17 | } 18 | 19 | public toObject(): object { 20 | let returnObj = {}; 21 | 22 | Array.from(this.attributes.keys()).forEach((attributeKey) => { 23 | (returnObj)[attributeKey] = this.attributes.get(attributeKey); 24 | }); 25 | return returnObj; 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /mvc-framework/core/utils/templateUtils.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import type { Mandarine } from "../../../main-core/Mandarine.ns.ts"; 4 | import { Sha256 } from "../../../security-core/hash/sha256.ts"; 5 | 6 | export class TemplateUtils { 7 | public static getTemplateKey(renderData: Mandarine.MandarineMVC.TemplateEngine.Decorators.RenderData | string): string { 8 | let key: string; 9 | if(typeof renderData === 'object') { 10 | let manual: boolean = renderData.options != undefined && renderData.options.manual == true; 11 | if(manual) { 12 | key = new Sha256().update(renderData.template).toString(); 13 | } else { 14 | key = renderData.template; 15 | } 16 | } else { 17 | key = new Sha256().update(renderData).toString(); 18 | } 19 | return key; 20 | } 21 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **[Details]** 11 | A clear and concise description of what the bug is. 12 | 13 | **[Stacktrace]** 14 | Stacktrace/Exception thrown from this bug. Ignore if any. 15 | 16 | **[Scenario]** 17 | Steps to reproduce the behavior: 18 | 1).. 19 | 2)... 20 | 21 | **[Expected behavior]** 22 | A clear and concise description of what you expected to happen. 23 | 24 | **[Screenshots]** 25 | If applicable, add screenshots to help explain your problem. 26 | 27 | **[OS]** 28 | E.g. MacOS Mojave 10.14.6 29 | 30 | **[Environment]** 31 | - Mandarine version: [e.g. **v1.1.3**] 32 | - Deno version [e.g. **1.1.2**] 33 | - Deno v8 version [e.g. **8.5.216**] 34 | - Deno typescript version [e.g. **3.9.2**] 35 | **Note**: Run `deno --version` to get some of the requested information. 36 | 37 | **[Additional]** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /docs/web/mandarine/main-concepts/component-configuration.md: -------------------------------------------------------------------------------- 1 | # Configuration 2 | Mandarine's _Configuration components_ are part of Mandarine's stereotype, this means, they do not have any special functionality more than making your class a Mandarine-powered class which will then be available for dependency injection. 3 | 4 | Mandarine's stereotypes are also useful for modularity purposes since they allow you to divide your application in different layers. 5 | 6 | ----- 7 | 8 | > Configuration components are recommended when dealing with Mandarine's internal core. 9 | 10 | ----- 11 |   12 | 13 | ## Concepts 14 | - Configuration components are part of Mandarine's Main Core. 15 | - It accepts injection and it is also injectable. 16 | 17 |   18 | 19 | ## Syntax 20 | 21 | ```typescript 22 | @Configuration() 23 | ``` 24 | 25 | ## Usage 26 | 27 | ```typescript 28 | import { Configuration } from "https://deno.land/x/mandarinets@v2.3.2/mod.ts"; 29 | 30 | @Configuration() 31 | export class MyConfiguration { 32 | } 33 | ``` 34 | -------------------------------------------------------------------------------- /main-core/components/repository-component/repositoryComponent.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import type { Mandarine } from "../../Mandarine.ns.ts"; 4 | 5 | /** 6 | * This class is used in the DI Container for Mandarine to store components annotated as @Repository 7 | */ 8 | export class RepositoryComponent implements Mandarine.MandarineCore.ComponentCommonInterface { 9 | 10 | public name?: string; 11 | public classHandler: any; 12 | public extraData: any; 13 | 14 | constructor(name?: string, classHandler?: any, extraData?: any) { 15 | this.name = name; 16 | this.classHandler = classHandler; 17 | this.extraData = extraData; 18 | } 19 | 20 | public getName() { 21 | return this.name || ""; 22 | } 23 | 24 | public getClassHandler() { 25 | return this.classHandler; 26 | } 27 | 28 | public setClassHandler(handler: any) { 29 | this.classHandler = handler; 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /docs/web/mandarine/mandarine-mvc/response-status-decorator.md: -------------------------------------------------------------------------------- 1 | # `@ResponseStatus` Decorator 2 | By using the `@ResponseStatus` decorator, it is possible to specify a response status at a controller level and HTTP handler, this means, it will be applied to all your endpoints inside your controller or an specific route if desired. 3 | 4 | ---- 5 | 6 | ## Syntax 7 | Targets: Controller | HTTP Handler 8 | ```typescript 9 | @ResponseStatus(httpCode: Mandarine.MandarineMVC.HttpStatusCode) 10 | ``` 11 | 12 |   13 | 14 | ## Usage 15 | 16 | ```typescript 17 | import { Mandarine, Controller, ResponseStatus, GET } from "https://deno.land/x/mandarinets@v2.3.2/mod.ts"; 18 | 19 | @Controller() 20 | @ResponseStatus(Mandarine.MandarineMVC.HttpStatusCode.OK) 21 | export class MyController { 22 | } 23 | 24 | @Controller() 25 | export class MyController2 { 26 | 27 | @GET('/endpoint') 28 | @ResponseStatus(Mandarine.MandarineMVC.HttpStatusCode.OK) 29 | public httpHandler() { 30 | return "Hello World" 31 | } 32 | 33 | } 34 | ``` 35 | -------------------------------------------------------------------------------- /main-core/internals/interfaces/exceptionFilter.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | /** 4 | * Provides information about an exception intercepted by a catch component. 5 | * 6 | * `getException()` returns the instance of the error thrown 7 | * `getResponse()` returns the response object in the current request 8 | * `getRequest()` returns the request object of the current request 9 | */ 10 | export interface ExceptionContext { 11 | getException(): T; 12 | getResponse(): any; 13 | getRequest(): any; 14 | getTimestamp(): string; 15 | } 16 | 17 | /** 18 | * Provides the necessary behavior to intercept an error thrown 19 | * 20 | * `catch` will be responsible for handling the error. 21 | * A first parameter will be automatically injected by Mandarine containing the Context of the exception (ExceptionContext) 22 | */ 23 | export interface ExceptionFilter { 24 | catch(exceptionContext: ExceptionContext, ...parameters: any): void; 25 | } 26 | -------------------------------------------------------------------------------- /main-core/mandarine-native/tasks/taskManager.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { CronManager } from "https://deno.land/x/little_crony@v1.0.1/mod.ts"; 4 | import { TimerRegistry } from "../../timer-registry/timerRegistry.ts"; 5 | 6 | export class TaskManager { 7 | 8 | private cronManager: CronManager = new CronManager(); 9 | private timerManager: TimerRegistry = new TimerRegistry(); 10 | 11 | /** 12 | * 13 | * Gives access to all the CRON Jobs scheduled in the Mandarine-powered application 14 | * 15 | * @returns an instance of CronManager 16 | */ 17 | public getCronManager(): CronManager { 18 | return this.cronManager; 19 | } 20 | 21 | /** 22 | * 23 | * Gives access to all the timers scheduled in the Mandarine-powered application 24 | * 25 | * @returns an instance of TimerRegistry 26 | */ 27 | public getTimerManager(): TimerRegistry { 28 | return this.timerManager; 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /orm-core/core/decorators/Repository.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { ORMCoreDecoratorProxy } from "../proxys/ormCoreDecoratorProxy.ts"; 4 | 5 | /** 6 | * **Decorator** 7 | * 8 | * Defines that an abstract class is a repository 9 | * 10 | * `@Repository() 11 | * Target: Class` 12 | */ 13 | export const Repository = (): Function => { 14 | return (target: any) => { 15 | ORMCoreDecoratorProxy.registerComponentRepositoryDecorator(target) 16 | }; 17 | } 18 | 19 | /** 20 | * **Decorator** 21 | * 22 | * Defines that a repository method should not be resolved by MQL but it is a custom query and will be considered as such. 23 | * 24 | * `@CustomQuery(query, secure?) 25 | * Target: Repository method` 26 | */ 27 | export const CustomQuery = (query: string, secure?: boolean): Function => { 28 | return (target: any, methodName: string) => { 29 | ORMCoreDecoratorProxy.registerCustomQueryDecorator(target, query, secure, methodName); 30 | } 31 | } -------------------------------------------------------------------------------- /tests/integration-tests/files/customPropertiesAndValue.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { ConfigurationProperties } from "../../../main-core/decorators/configuration-readers/configurationProperties.ts"; 4 | import { Value } from "../../../main-core/decorators/configuration-readers/value.ts"; 5 | import { MandarineCore } from "../../../main-core/mandarineCore.ts"; 6 | import { Controller } from "../../../mvc-framework/core/decorators/stereotypes/controller/controller.ts"; 7 | import { GET } from "../../../mvc-framework/core/decorators/stereotypes/controller/routingDecorator.ts"; 8 | 9 | @Controller() 10 | @ConfigurationProperties('./tests/integration-tests/files/customPropertiesConfig.json') 11 | export class MyComponent { 12 | 13 | @Value('my.data.database') 14 | // @ts-ignore 15 | public myDatabase: string; 16 | 17 | @GET('/my-custom-config') 18 | public handler() { 19 | return this.myDatabase; 20 | } 21 | 22 | } 23 | 24 | new MandarineCore().MVC().run({ port: 7751 }); -------------------------------------------------------------------------------- /docs/web/mandarine/mandarine-mvc/built-in-response-time-header.md: -------------------------------------------------------------------------------- 1 | # Response Time Header 2 | Mandarine has a built-in middleware to inject `X-Response-Time` header in the response of a request. 3 | 4 | ------------ 5 | 6 | ## Concepts 7 | The `X-Response-Time` header is used to measure how long the request took from the time it got in the server to the time the client received a response. 8 | This calculation is just a simple substraction of dates, specifically `Date.now()` which results in the following operation `(Time when the response finished) - (Time when the request got in)` 9 | 10 | ## Usage 11 | This middleware is under flag, this means, it is disabled by default and it needs to be activated in order to use make use of it. 12 | 13 | In order to use it, you must set the property `responseTimeHeader` in `mandarine.server` to **true** in your [properties file](https://www.mandarinets.org/docs/master/mandarine/custom-properties) 14 | 15 | ```json 16 | { 17 | "mandarine": { 18 | "server": { 19 | "responseTimeHeader": true 20 | } 21 | } 22 | } 23 | ``` -------------------------------------------------------------------------------- /main-core/utils/components/websocketClient.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { ApplicationContext } from "../../application-context/mandarineApplicationContext.ts"; 4 | import type { ComponentComponent } from "../../components/component-component/componentComponent.ts"; 5 | import { MandarineConstants } from "../../mandarineConstants.ts"; 6 | 7 | export class WebSocketClientUtil { 8 | 9 | public static mount(component: ComponentComponent, url: string, protocols?: Array) { 10 | component.addInternal(MandarineConstants.COMPONENT_PROPERTY_WEBSOCKET, new WebSocket(url, protocols)); 11 | ApplicationContext.getInstance().getComponentsRegistry().connectWebsocketClientProxy(component); 12 | } 13 | 14 | public static close(component: ComponentComponent, code: any, reason: any) { 15 | component.getInternal(MandarineConstants.COMPONENT_PROPERTY_WEBSOCKET).close(code, reason); 16 | component.deleteInternal(MandarineConstants.COMPONENT_PROPERTY_WEBSOCKET); 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /docs/web/mandarine/main-concepts/component-repository.md: -------------------------------------------------------------------------------- 1 | # Repository 2 | Repositories are responsible for creating a bridge between your application's layer and your database. They can handle saving, updating, deleting and retrieving data from the database with complex consults thanks to MQL (Mandarine Query Language). **Note** that Mandarine's repositories are highly dependent on MQL. 3 | 4 | ----- 5 |   6 | 7 | ## Concepts 8 | - Repositories are part of Mandarine's Data Core. 9 | - It does not accept the use of dependency injection, but **it is** injectable 10 | 11 |   12 | 13 | ## Syntax 14 | 15 | ```typescript 16 | @Repository() 17 | ``` 18 | 19 | ## Usage 20 | 21 | ```typescript 22 | import { Repository, MandarineRepository } from "https://deno.land/x/mandarinets@v2.3.2/mod.ts"; 23 | 24 | @Repository() 25 | abstract class MyRepository extends MandarineRepository { 26 | 27 | constructor() { 28 | super(YourModel); 29 | } 30 | 31 | } 32 | ``` 33 | > Where `YourModel` is the instance that represents a database table, also known as Mandarine's ORM entity. 34 | -------------------------------------------------------------------------------- /main-core/internals/interfaces/guardTarget.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import type { Mandarine } from "../../Mandarine.ns.ts"; 4 | 5 | /** 6 | * Define the behavior of a user-side guard 7 | * The guard target will be called at the time of a request. 8 | * onGuard(requestContext, ...args) will be called before executing the endpoint. 9 | * 10 | * If onGuard **returns false**, the endpoint's execution will be discarded. 11 | * In order to keep the execution cycle going, onGuard must return true. 12 | * 13 | * export class MyGuard implements GuardTarget { 14 | * onGuard(requestContext: Mandarine.Types.RequestContext) { 15 | * console.log("onGuard() called"); 16 | * return true; 17 | * } 18 | * } 19 | * 20 | */ 21 | export interface GuardTarget { 22 | onGuard(requestContext: Mandarine.Types.RequestContext, ...args: Array): boolean; 23 | } 24 | 25 | export type GuardTargetMethod = (requestContext: Mandarine.Types.RequestContext) => boolean; -------------------------------------------------------------------------------- /docs/web/mandarine/mandarine-cli/run.md: -------------------------------------------------------------------------------- 1 | # `mandarine run` 2 | Compiles & Run the mandarine application located in the current working directory. 3 | 4 | ----- 5 | 6 | **Syntax:** 7 | 8 | ```shell script 9 | $ mandarine run [OPTIONS] 10 | ``` 11 | 12 |   13 | 14 | **Options** 15 | 16 | | Option | Description | Required | 17 | | ------ | ----------- | -------- | 18 | | --entry-point | Defines where Mandarine's entry point file is located.
Default: `[DENO_CWD]/src/main/mandarine/app.ts` | No 19 | | --tsconfig | Specifies the route of tsconfig.json to be used.
Default: [DENO_CWD]/tsconfig.json | No 20 | | --allow-write | Specifies deno should use the flag --allow-write | No 21 | | --allow-read | Specifies deno should use the flag --allow-read | No 22 | | --allow-run | Specifies deno should use the flag --allow-run | No 23 | | --allow-env | Specifies deno should use the flag --allow-env | No 24 | | --allow-all | Specifies deno should use the flag --allow-all | No 25 | | --reload | Specifies deno should use the flag --reload | No 26 | | --watch | Specifies that mandarine cli reloads the project when files change --watch | No 27 | -------------------------------------------------------------------------------- /tests/integration-tests/files/templates.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Mandarine, MandarineCore } from "../../../mod.ts"; 4 | import { Controller } from "../../../mvc-framework/core/decorators/stereotypes/controller/controller.ts"; 5 | import { GET } from "../../../mvc-framework/core/decorators/stereotypes/controller/routingDecorator.ts"; 6 | import { Render } from "../../../mvc-framework/core/decorators/stereotypes/view-engine/viewEngineDecorators.ts"; 7 | 8 | @Controller() 9 | export class MyController { 10 | 11 | @GET('/manual-template-ejs') 12 | @Render(`

<%= name %>

`, { manual: true }) 13 | public httpHandler1() { 14 | return { 15 | name: "Will" 16 | }; 17 | } 18 | 19 | @GET('/manual-template-handlebars') 20 | @Render(`

{{ name }}

`, { manual: true }, Mandarine.MandarineMVC.TemplateEngine.Engines.HANDLEBARS) 21 | public httpHandler2() { 22 | return { 23 | name: "Andres" 24 | }; 25 | } 26 | 27 | } 28 | 29 | new MandarineCore().MVC().run({ port: 8090 }); -------------------------------------------------------------------------------- /tests/integration-tests/resources/openapi.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.3 2 | info: 3 | title: App 4 | version: 1.0.0 5 | servers: [] 6 | paths: 7 | '/:username': 8 | get: 9 | summary: Get user by user name 10 | responses: 11 | '400': 12 | responseCode: 400 13 | description: User not found 14 | default: 15 | description: lol 16 | operationId: httpHandler 17 | parameters: 18 | - description: The name that needs to be fetched. Use user1 for testing. 19 | required: true 20 | in: path 21 | schema: 22 | type: string 23 | name: username 24 | tags: 25 | - JAJA 26 | - Framework 27 | - NodeJS 28 | - Deno 29 | - Hello2 30 | - Hello3 31 | - Hello 32 | components: 33 | schemas: {} 34 | responses: {} 35 | parameters: {} 36 | examples: {} 37 | requestBodies: {} 38 | headers: {} 39 | securitySchemes: {} 40 | links: {} 41 | callbacks: {} 42 | tags: 43 | - name: Framework 44 | description: .NET 45 | - name: NodeJS 46 | description: Runtime 47 | -------------------------------------------------------------------------------- /docs/web/mandarine/mandarine-core/mandarine-core.md: -------------------------------------------------------------------------------- 1 | # Mandarine Core 2 | The core initialization is responsible for unifying all mandarine modules (Core, MVC, Security, Data) into one as it handles some of the most important tasks inside the framework. 3 | 4 | ---- 5 | 6 | 7 | ## Invoking Mandarine's core 8 | In order to invoke the initialization of Mandarine's core, follow the example: 9 | ```typescript 10 | import { MandarineCore } from "https://deno.land/x/mandarinets@v2.3.2/mod.ts"; 11 | 12 | /** 13 | * Code 14 | * Or Single entry-point File 15 | */ 16 | 17 | new MandarineCore(); 18 | ``` 19 | 20 | ## MVC Bridge 21 | The MVC bridge is the process of using Mandarine's MVC Core. This process is done through Mandarine's core. 22 | ```typescript 23 | import { MandarineCore } from "https://deno.land/x/mandarinets@v2.3.2/mod.ts"; 24 | 25 | /** 26 | * Code 27 | * Or Single entry-point File 28 | */ 29 | 30 | new MandarineCore().MVC(); 31 | ``` 32 | 33 | If you would like to run your Mandarine-powered MVC application, you would use the method `run` as following 34 | 35 | ```typescript 36 | new MandarineCore().MVC().run(); 37 | ``` 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 mandarineorg 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/web/mandarine/mandarine-mvc/resource-resolver.md: -------------------------------------------------------------------------------- 1 | # Resource Resolver 2 | 3 | The Resource Resolver interface ([See interface here](https://doc.deno.land/https/raw.githubusercontent.com/mandarineorg/mandarinets/master/mvc-framework/mandarine-mvc.ns.ts#MandarineMvc.HTTPResolvers.ResourceResolver)) is responsible for resolving a Resource Handler and finally returning the requested resource in form of Uint8Array which will then be reflected on the response body. The Resource Resolver implementation is used in [Resource Handlers](/docs/mandarine/resource-handler). 4 | 5 | ---- 6 | 7 | ## Methods 8 | 9 | | Name | Description | Arguments | 10 | | ---- | ----------- | --------- | 11 | | resolve | Resolves the resolve handler | httpContext (_Mandarine.Types.RequestContext_): Context of the incoming HTTP request (This parameter is injected by Mandarine's Resource Handler Middleware).
resourcePath: It is the path of the requested resource (This parameter is injected by Mandarine's Resource Handler Middleware) | 12 | 13 | ## Mandarine Default Resource Resolver 14 | Mandarine has a default resource resolves which is used to handle static content. 15 | -------------------------------------------------------------------------------- /tests/unit-tests/utils/yamlUtils_test.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Test, DenoAsserts, Orange } from "../../mod.ts"; 4 | import { YamlUtils } from "../../../main-core/utils/yamlUtils.ts"; 5 | 6 | export class PropertiesUtilsTest { 7 | 8 | @Test({ 9 | name: "Test parsing yaml file", 10 | description: "Should parse yaml to a JS object" 11 | }) 12 | public testPropertiesParsing() { 13 | const expected = { 14 | "mandarine": { 15 | "server": { 16 | "host": "127.0.0.1", 17 | "port": 8080 18 | }, 19 | "enabled": true, 20 | "auth": { 21 | "data": { 22 | "config": { 23 | "username": "andreespirela" 24 | } 25 | } 26 | } 27 | } 28 | }; 29 | const yaml = YamlUtils.jsToYaml(expected); 30 | 31 | const parsing = YamlUtils.yamlToJS(yaml); 32 | DenoAsserts.assertEquals(parsing, expected); 33 | } 34 | } -------------------------------------------------------------------------------- /docs/web/mandarine/getting-started/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | To get started, it is important for you to have Deno installed in your computer as Mandarine runs on top Deno. If you already have it installed, you can ignore the next step. 3 | 4 | ----------- 5 |   6 | 7 | # Installing Deno 8 | 9 | ## Shell (Mac, Linux): 10 | ```shell script 11 | $ curl -fsSL https://deno.land/x/install/install.sh | sh 12 | ``` 13 | 14 | ## PowerShell (Windows): 15 | ```shell script 16 | $ iwr https://deno.land/x/install/install.ps1 -useb | iex 17 | ``` 18 | 19 | For more information and installation options, please [click here](https://deno.land/#installation). 20 | 21 |   22 | 23 | # Importing Mandarine 24 | Mandarine is available through [deno.land/x](https://deno.land/x/mandarinets) & [nest.land](https://nest.land/package/MandarineTS). We do not recommend importing Mandarine from anywhere else for security concerns. 25 | 26 | **deno.land/x** 27 | ```ts 28 | import { ... } from "https://deno.land/x/mandarinets@v2.3.2/mod.ts"; 29 | ``` 30 | 31 | **nest.land** 32 | ```ts 33 | import { ... } from "https://x.nest.land/MandarineTS@2.0.0/mod.ts"; 34 | ``` -------------------------------------------------------------------------------- /docs/web/mandarine/mandarine-cli/introduction.md: -------------------------------------------------------------------------------- 1 | # Mandarine CLI 2 | Mandarine's CLI is a command interface tool used to create Mandarine-powered applications as well as Mandarine-powered modules. 3 | 4 | ----- 5 | 6 | ## Installation 7 | ```shell script 8 | $ deno install --allow-read --allow-write --allow-run -n mandarine https://deno.land/x/mandarinets/cli.ts 9 | ``` 10 | 11 | ----- 12 | 13 | ## Usage 14 | 15 | ```shell script 16 | $ mandarine [OPTIONS] [SUBCOMMAND] 17 | ``` 18 | 19 |   20 | 21 | **Options** 22 | 23 | 24 | | Option | Description | 25 | | ------ | ----------- | 26 | | `-h, --help` | Shows the help information of Mandarine's CLI. 27 | | `-v, --version` | Shows the version information of Mandarine's CLI 28 | 29 |   30 | 31 | **Subcommands** 32 | 33 | 34 | | Subcommand | Alias | Description | 35 | | ---------- | ----- | ----------- | 36 | | new | n | Creates a Mandarine-powered application (following Mandarine's project structure) in the current working directory. 37 | | generate | g | Generates a Mandarine-powered module. 38 | | run | r | Compiles & Run the mandarine application located in the current working directory. -------------------------------------------------------------------------------- /mvc-framework/core/interfaces/http/cookie.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | export interface CookieConfig { 4 | /** Expiration date of the cookie. */ 5 | expires?: Date; 6 | /** Max-Age of the Cookie. Must be integer superior to 0. */ 7 | maxAge?: number; 8 | /** Specifies those hosts to which the cookie will be sent. */ 9 | domain?: string; 10 | /** Indicates a URL path that must exist in the request. */ 11 | path?: string; 12 | /** Indicates if the cookie is made using SSL & HTTPS. */ 13 | secure?: boolean; 14 | /** Indicates that cookie is not accessible via JavaScript. **/ 15 | httpOnly?: boolean; 16 | /** Allows servers to assert that a cookie ought not to 17 | * be sent along with cross-site requests. */ 18 | sameSite?: "Strict" | "Lax" | "None"; 19 | /** Additional key value pairs with the form "key=value" */ 20 | unparsed?: string[]; 21 | } 22 | 23 | export interface Cookie extends CookieConfig { 24 | /** Name of the cookie. */ 25 | name: string; 26 | /** Value of the cookie. */ 27 | value: string; 28 | } -------------------------------------------------------------------------------- /tests/integration-tests/files/sessionCounter.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { MandarineCore } from "../../../mod.ts"; 4 | import { Controller } from "../../../mvc-framework/core/decorators/stereotypes/controller/controller.ts"; 5 | import { RequestParam, Session } from "../../../mvc-framework/core/decorators/stereotypes/controller/parameterDecorator.ts"; 6 | import { GET } from "../../../mvc-framework/core/decorators/stereotypes/controller/routingDecorator.ts"; 7 | 8 | @Controller() 9 | class TestController { 10 | 11 | @GET('/session-counter') 12 | public helloWorld(@RequestParam() request: any, @Session() session: any): object { 13 | if(session.times == undefined) session.times = 0; 14 | else session.times = session.times + 1; 15 | 16 | return { 17 | times: session.times, 18 | sessionId: `${request.sessionContext.sessionCookie.name}=${request.sessionContext.sessionCookie.value}` 19 | } 20 | } 21 | 22 | @GET('/(.*)') 23 | public ok() { 24 | return "Ok"; 25 | } 26 | } 27 | 28 | new MandarineCore().MVC().run({ port: 8084 }); -------------------------------------------------------------------------------- /docs/web/mandarine/main-concepts/getting-started-introduction.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | In this set of articles, we will examine some of the fundamental concepts & examples to use Mandarine. 3 | 4 | ---- 5 |   6 | 7 | # Knowledge Level Assumptions 8 | Mandarine is a Typescript & [Deno](https://deno.land) framework. We will assume you have a basic understanding of both Javascript & Typescript, but most importantly, of _Deno_. 9 | **If you don't feel very confident, we recommend going through [a Javascript tutorial](https://developer.mozilla.org/en-US/docs/Web/JavaScript/A_re-introduction_to_JavaScript) as well as [a Typescript tutorial](https://developer.mozilla.org/en-US/docs/Web/JavaScript/A_re-introduction_to_JavaScript)** which will provide you with some basic concepts you will be constantly using when creating a _Mandarine-powered application_. 10 | 11 | If you don't feel confident about _Deno_ and how to use it, we recommend you going through [Deno's official manual](https://deno.land/manual). 12 | 13 | If after checking out the mentioned tutorials above, you still feel confused, we recommend you to stop here and get a higher level of understanding about Deno, Typescript & Javascript. 14 | -------------------------------------------------------------------------------- /main-core/exceptions/mandarineSecurityException.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | export class MandarineSecurityException extends Error { 4 | 5 | public static readonly INVALID_USER_DETAILS_SERVICE: string = "A UserDetailsService was introduced but it could not be started because it is either undefined or an invalid service. UserDetailsService must be a Mandarine-powered component"; 6 | public static readonly UNSATISFIED_AUTHENTICATOR: string = "The current authenticator is unsatisfied. Password encoder or current `UserDetailsService` may be null or undefined"; 7 | public static readonly INVALID_LOGIN_DATA: string = "Authentication could not be performed because the request contains either invalid or null data."; 8 | public static readonly USER_DETAILS_SERVICE_INCOMPLETE: string = "A UserDetailsService was found but it is invalid in the current context. Make sure you are implementing the method `loadUserByUsername`"; 9 | 10 | constructor(public message: string, public superAlert: boolean = false) { 11 | super(message); 12 | this.name = "MandarineSecurityException"; 13 | this.stack = (this).stack; 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /mvc-framework/core/middlewares/authMiddleware.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Context } from "../../../deps.ts"; 4 | import { AuthUtils } from "../../../security-core/utils/auth.util.ts"; 5 | import { Mandarine } from "../../../main-core/Mandarine.ns.ts"; 6 | 7 | export const handleBuiltinAuth = () => { 8 | return async (context: any, next: Function) => { 9 | const typedContext: Mandarine.Types.RequestContext = context; 10 | const authCookieId = AuthUtils.findAuthCookie(typedContext); 11 | if(!authCookieId) { 12 | context.request.authentication = undefined; 13 | await next(); 14 | return; 15 | } 16 | 17 | const currentAuthSes = Mandarine.Global.getSessionContainer().store?.get(authCookieId, { touch: true }); 18 | if(currentAuthSes) { 19 | typedContext.request.authentication = { 20 | AUTH_SES_ID: currentAuthSes.sessionID, 21 | AUTH_EXPIRES: currentAuthSes.expiresAt, 22 | AUTH_PRINCIPAL: currentAuthSes.sessionData 23 | } 24 | } 25 | 26 | await next(); 27 | } 28 | } -------------------------------------------------------------------------------- /tests/integration-tests/eventlistener_test.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { CommonUtils } from "../../main-core/utils/commonUtils.ts"; 4 | import { DenoAsserts, INTEGRATION_TEST_FILES_TO_RUN_DIRECTORY, Orange, Test } from "../mod.ts"; 5 | 6 | export class EventListenerTest { 7 | 8 | public MAX_COMPILATION_TIMEOUT_SECONDS = 50; 9 | 10 | constructor() { 11 | } 12 | 13 | @Test({ 14 | name: "[IT04] Test OpenAPI from `files/event.ts`", 15 | description: "Verifies Mandarine dispatches events with access to injectables" 16 | }) 17 | public async testEventListener() { 18 | let cmd = Deno.run({ 19 | cmd: ["deno", "run", "-c", "tsconfig.json", "--allow-all", "--unstable", `${INTEGRATION_TEST_FILES_TO_RUN_DIRECTORY}/events.ts`], 20 | stdout: "piped" 21 | }); 22 | await cmd.status(); 23 | 24 | const stdoutOutput = new TextDecoder().decode(await cmd.output()); 25 | DenoAsserts.assertStringIncludes(stdoutOutput, "ANDRES"); 26 | DenoAsserts.assertStringIncludes(stdoutOutput, "ANDRES : async"); 27 | cmd.close(); 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /tests/integration-tests/files/events.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { EventListener } from "../../../main-core/decorators/stereotypes/events/dom/eventListener.ts"; 4 | import { Service } from "../../../main-core/decorators/stereotypes/service/service.ts"; 5 | import { MandarineCore } from "../../../main-core/mandarineCore.ts"; 6 | 7 | @Service() 8 | export class MyService { 9 | 10 | public getName() { 11 | return "ANDRES"; 12 | } 13 | 14 | } 15 | 16 | @Service() 17 | export class AnotherService { 18 | 19 | constructor(private readonly service: MyService) {} 20 | 21 | @EventListener('myEvent') 22 | public event() { 23 | console.log(this.service.getName()); 24 | return this.service.getName(); 25 | } 26 | 27 | @EventListener('asyncMyEvent') 28 | public async eventAsync() { 29 | console.log(this.service.getName() + " : async"); 30 | return this.service.getName() + " : async"; 31 | } 32 | } 33 | 34 | new MandarineCore().MVC().run(); 35 | 36 | dispatchEvent(new Event('myEvent')); 37 | 38 | dispatchEvent(new Event('asyncMyEvent')); 39 | 40 | setTimeout(() => Deno.exit(), 3000); 41 | -------------------------------------------------------------------------------- /tests/integration-tests/files/websocketServer.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { ApplicationContext } from "../../../main-core/application-context/mandarineApplicationContext.ts"; 4 | import { Component } from "../../../main-core/decorators/stereotypes/component/component.ts"; 5 | import { WebSocketServer, WSOnMessage } from "../../../main-core/decorators/websockets/webSocket.ts"; 6 | import { WebSocketServerManager } from "../../../main-core/mandarine-native/websocket/websocketServerManager.ts"; 7 | import { MandarineCore } from "../../../main-core/mandarineCore.ts"; 8 | import { CommonUtils } from "../../../main-core/utils/commonUtils.ts"; 9 | 10 | @Component() 11 | export class MyComponent { 12 | 13 | public salute(name: string) { 14 | return `Hello ${name} from stdout`; 15 | } 16 | 17 | } 18 | 19 | @WebSocketServer(8585) 20 | export class MyWebSocketServer { 21 | 22 | constructor(public service: MyComponent) {} 23 | 24 | @WSOnMessage() 25 | public handleIncomingMessage(data: any) { 26 | const username = data.data; 27 | console.log(this.service.salute(username)); 28 | } 29 | 30 | } 31 | 32 | new MandarineCore(); -------------------------------------------------------------------------------- /docs/web/mandarine/main-concepts/component-controllers.md: -------------------------------------------------------------------------------- 1 | # Controllers 2 | Controllers are responsible for dealing with Mandarine's HTTP Dispatcher & your application. Controllers are an special kind of component are they are the only ones who can handle the creation of endpoints as well as the communication of requests and your application's layer. 3 | 4 | ----- 5 |   6 | 7 | ## Concepts 8 | - Controllers are part of Mandarine's MVC Core 9 | - It accepts the use of dependency injection, however, this type of component is not injectable. 10 | 11 |   12 | 13 | ## Syntax 14 | 15 | ```typescript 16 | @Controller(baseRoute?: string) 17 | ``` 18 | - `baseRoute` 19 | - Optional 20 | - Base route for endpoints 21 | 22 | 23 | **Note** that if `baseRoute` is provided, all the endpoints under the controller will work under the base route. 24 | 25 |   26 | 27 | ## Usage 28 | 29 | ```typescript 30 | import { Controller } from "https://deno.land/x/mandarinets@v2.3.2/mod.ts"; 31 | 32 | @Controller() 33 | export class MyController { 34 | } 35 | ``` 36 | ---- 37 | ```typescript 38 | import { Controller } from "https://deno.land/x/mandarinets@v2.3.2/mod.ts"; 39 | 40 | @Controller('/api') 41 | export class MyController { 42 | } 43 | ``` 44 | -------------------------------------------------------------------------------- /tests/unit-tests/github-issues/210_test.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Mandarine } from "../../../main-core/Mandarine.ns.ts"; 4 | import { NativeComponentsOverrideProxy } from "../../../main-core/proxys/nativeComponentsOverrideProxy.ts"; 5 | import { Test, DenoAsserts, Orange, mockDecorator, MockCookies } from "../../mod.ts"; 6 | 7 | export class Issue210Test { 8 | @Test({ 9 | name: "Keys should be present in overriding even if they don't exist [Github: #210]", 10 | description: "Issue related to Github #210" 11 | }) 12 | public testKeysIfNotPresent() { 13 | NativeComponentsOverrideProxy.MVC.changeSessionContainer({}); 14 | 15 | let defaultContainer = Mandarine.Defaults.MandarineDefaultSessionContainer(); 16 | let mandarineGlobal: Mandarine.Global.MandarineGlobalInterface = Mandarine.Global.getMandarineGlobal(); 17 | 18 | DenoAsserts.assert(mandarineGlobal.mandarineSessionContainer.keys !== undefined); 19 | DenoAsserts.assert(Array.isArray(mandarineGlobal.mandarineSessionContainer.keys)) 20 | DenoAsserts.assert(mandarineGlobal.mandarineSessionContainer.keys.length >= 1); 21 | } 22 | } -------------------------------------------------------------------------------- /main-core/proxys/dependencyInjectionDecorator.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { ApplicationContext } from "../application-context/mandarineApplicationContext.ts"; 4 | import { DI } from "../dependency-injection/di.ns.ts"; 5 | import { DependencyInjectionUtil } from "../dependency-injection/di.util.ts"; 6 | import { Mandarine } from "../Mandarine.ns.ts"; 7 | 8 | /** 9 | * Logic behind decorators of Dependency Injection's core 10 | */ 11 | export class DependencyInjectionDecoratorsProxy { 12 | 13 | public static registerInject(targetClass: any, propertyName: string, propertyInMethodIndex: number) { 14 | DependencyInjectionUtil.defineInjectionMetadata(DI.InjectionTypes.INJECTABLE_OBJECT, targetClass, propertyName, propertyInMethodIndex); 15 | } 16 | 17 | public static registerInjectableDecorator(targetClass: any, methodName: string) { 18 | let componentExist: boolean = ApplicationContext.getInstance().getComponentsRegistry().exist(methodName); 19 | 20 | if(!componentExist) { 21 | ApplicationContext.getInstance().getComponentsRegistry().register(methodName, targetClass[methodName](), Mandarine.MandarineCore.ComponentTypes.MANUAL_COMPONENT, {}); 22 | } 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /tests/integration-tests/nestedInjection_test.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { CommonUtils } from "../../main-core/utils/commonUtils.ts"; 4 | import { DenoAsserts, Orange, Test, waitForMandarineServer } from "../mod.ts"; 5 | 6 | export class NestedInjectionTest { 7 | 8 | constructor() { 9 | Orange.setOptions(this, { 10 | hooks: { 11 | beforeEach: () => CommonUtils.sleep(2) 12 | } 13 | }) 14 | } 15 | 16 | @Test({ 17 | name: "[IT03] Test Endpoints from `files/nestedInjection.ts`", 18 | description: "Test all endpoints in file, and verifies its return values" 19 | }) 20 | public async testPiInService() { 21 | let cmd = await waitForMandarineServer("nestedInjections.ts"); 22 | 23 | let testingManualInjection = (await (await fetch("http://localhost:8083/test")).text()); 24 | 25 | let errorThrown = undefined; 26 | try { 27 | DenoAsserts.assertEquals(testingManualInjection, "3.14"); 28 | } catch(error) { 29 | errorThrown = error; 30 | } 31 | 32 | cmd.close(); 33 | if(errorThrown != undefined) { 34 | throw errorThrown; 35 | } 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /tests/unit-tests/utils/propertiesUtils_test.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Test, DenoAsserts, Orange } from "../../mod.ts"; 4 | import { PropertiesUtils } from "../../../main-core/utils/propertiesUtils.ts"; 5 | 6 | export class PropertiesUtilsTest { 7 | 8 | @Test({ 9 | name: "Test parsing properties file", 10 | description: "Should parse properties to a JS object" 11 | }) 12 | public testPropertiesParsing() { 13 | const properties = ["mandarine.server.host=127.0.0.1", "mandarine.server.port=8080", "mandarine.enabled=true", "mandarine.auth.data.config.user=andreespirela"]; 14 | const propertiesText = properties.join(`\n`); 15 | const parsing = PropertiesUtils.parse(propertiesText); 16 | DenoAsserts.assertEquals({ 17 | mandarine: { 18 | server: { 19 | host: "127.0.0.1", 20 | port: 8080 21 | }, 22 | enabled: true, 23 | auth: { 24 | data: { 25 | config: { 26 | user: "andreespirela" 27 | } 28 | } 29 | } 30 | } 31 | }, parsing); 32 | } 33 | } -------------------------------------------------------------------------------- /tests/app-builder-tests/decoratorFinder.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { AppBuilder } from "../../main-core/app-builder/appBuilder.ts"; 4 | import { CommonUtils } from "../../main-core/utils/commonUtils.ts"; 5 | import { DenoAsserts, Test } from "../mod.ts"; 6 | 7 | 8 | export class AppBuilderIntgTest { 9 | 10 | @Test({ 11 | name: "Integration test for automatic build & run", 12 | description: "Check it works in windows & mac" 13 | }) 14 | public async testint() { 15 | const process = new AppBuilder().setPort(1999).automaticBuildAndRun({ 16 | tsconfigPath: "../../tsconfig.json", 17 | flags: ["--allow-all", "--unstable"], 18 | mandarineVersion: "v2.3.1" 19 | }); 20 | 21 | CommonUtils.sleep(50); 22 | 23 | let testingManualInjection = (await (await fetch("http://localhost:1999/hello")).text()); 24 | 25 | let errorThrown = undefined; 26 | try { 27 | DenoAsserts.assertEquals(testingManualInjection, "Hi"); 28 | } catch(error) { 29 | errorThrown = error; 30 | } 31 | 32 | process.close(); 33 | if(errorThrown != undefined) { 34 | throw errorThrown; 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /tests/integration-tests/files/pipes.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Controller } from "../../../mvc-framework/core/decorators/stereotypes/controller/controller.ts"; 4 | import { GET } from "../../../mvc-framework/core/decorators/stereotypes/controller/routingDecorator.ts"; 5 | import { MandarineCore } from "../../../main-core/mandarineCore.ts"; 6 | import { Mandarine } from "../../../main-core/Mandarine.ns.ts"; 7 | import { Pipe } from "../../../mvc-framework/core/decorators/stereotypes/controller/pipeDecorator.ts"; 8 | import { QueryParam } from "../../../mvc-framework/core/decorators/stereotypes/controller/parameterDecorator.ts"; 9 | import { Component } from "../../../main-core/decorators/stereotypes/component/component.ts"; 10 | 11 | @Component() 12 | export class MymathService implements Mandarine.Types.PipeTransform { 13 | 14 | public transform(value: any): any { 15 | return value * 10.5; 16 | } 17 | 18 | } 19 | 20 | @Controller() 21 | export class MyController { 22 | @GET('/hello-world') 23 | public httpHandler(@Pipe([parseInt, MymathService]) @QueryParam('id') myPipe: number) { 24 | return { 25 | typeof: typeof myPipe, 26 | value: myPipe 27 | } 28 | } 29 | } 30 | 31 | new MandarineCore().MVC().run({ port: 5320 }); -------------------------------------------------------------------------------- /tests/integration-tests/manualInjection_test.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { CommonUtils } from "../../main-core/utils/commonUtils.ts"; 4 | import { DenoAsserts, Orange, Test, waitForMandarineServer } from "../mod.ts"; 5 | 6 | export class ManualInjectionTest { 7 | 8 | 9 | constructor() { 10 | Orange.setOptions(this, { 11 | hooks: { 12 | beforeEach: () => CommonUtils.sleep(2) 13 | } 14 | }) 15 | } 16 | 17 | @Test({ 18 | name: "[IT02] Test Endpoints from `files/manualInjection.ts`", 19 | description: "Test all endpoints in file, and verifies its return values" 20 | }) 21 | public async testManualInjectionEndpoint() { 22 | let cmd = await waitForMandarineServer("manualInjection.ts"); 23 | 24 | let testingManualInjection = (await (await fetch("http://localhost:8082/testing-manual-injection")).text()); 25 | 26 | let errorThrown = undefined; 27 | try { 28 | DenoAsserts.assertEquals(testingManualInjection, "hello Andres"); 29 | } catch(error) { 30 | errorThrown = error; 31 | } 32 | 33 | cmd.close(); 34 | if(errorThrown != undefined) { 35 | throw errorThrown; 36 | } 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /tests/integration-tests/pipe_test.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { CommonUtils } from "../../main-core/utils/commonUtils.ts"; 4 | import { DenoAsserts, Orange, Test, waitForMandarineServer } from "../mod.ts"; 5 | 6 | export class PipeTest { 7 | 8 | constructor() { 9 | Orange.setOptions(this, { 10 | hooks: { 11 | beforeEach: () => CommonUtils.sleep(2) 12 | } 13 | }) 14 | } 15 | 16 | @Test({ 17 | name: "[IT04] Test Endpoints from `files/pipes.ts`", 18 | description: "Test all endpoints in file, and verifies that transformation from pipe is working fine" 19 | }) 20 | public async testManualInjectionEndpoint() { 21 | let cmd = await waitForMandarineServer("pipes.ts"); 22 | 23 | let test1 = (await (await fetch("http://localhost:5320/hello-world?id=4")).json()); 24 | 25 | let errorThrown = undefined; 26 | try { 27 | DenoAsserts.assertEquals(test1.typeof, "number"); 28 | DenoAsserts.assertEquals(test1.value, 42); 29 | } catch(error) { 30 | errorThrown = error; 31 | } 32 | 33 | cmd.close(); 34 | if(errorThrown != undefined) { 35 | throw errorThrown; 36 | } 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /tests/integration-tests/customDecorator_test.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { CommonUtils } from "../../main-core/utils/commonUtils.ts"; 4 | import { DenoAsserts, Orange, Test, waitForMandarineServer } from "../mod.ts"; 5 | 6 | export class CustomDecoratorIntTest { 7 | 8 | constructor() { 9 | Orange.setOptions(this, { 10 | hooks: { 11 | beforeEach: () => CommonUtils.sleep(2) 12 | } 13 | }) 14 | } 15 | 16 | @Test({ 17 | name: "[IT01] Test Endpoints from `files/customDecorator.ts`", 18 | description: "Test all endpoints in file, and verifies that a simple custom decorator is working fine." 19 | }) 20 | public async testCustomDecorator() { 21 | let cmd = await waitForMandarineServer("customDecorator.ts"); 22 | 23 | let customDecoratorMsg = (await (await fetch("http://localhost:2193/hello-world")).text()); 24 | 25 | let errorThrown = undefined; 26 | try { 27 | DenoAsserts.assertEquals(customDecoratorMsg, "HAHAHA OKEYYY ES OBER!"); 28 | } catch(error) { 29 | errorThrown = error; 30 | } 31 | 32 | cmd.close(); 33 | if(errorThrown != undefined) { 34 | throw errorThrown; 35 | } 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /tests/integration-tests/openapi_test.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { CommonUtils } from "../../main-core/utils/commonUtils.ts"; 4 | import { DenoAsserts, INTEGRATION_TEST_FILES_TO_RUN_DIRECTORY, Orange, Test } from "../mod.ts"; 5 | 6 | export class OpenAPITest { 7 | 8 | public MAX_COMPILATION_TIMEOUT_SECONDS = 50; 9 | 10 | constructor() { 11 | } 12 | 13 | @Test({ 14 | name: "[IT04] Test OpenAPI from `files/openAPI.ts`", 15 | description: "Verifies Mandarine generates the right file structure for OpenAPI metadata", 16 | ignore: Deno.build.os == "windows" 17 | }) 18 | public async testOpenAPIFileGenerated() { 19 | let cmd = Deno.run({ 20 | cmd: ["deno", "run", "-c", "tsconfig.json", "--allow-all", "--unstable", `${INTEGRATION_TEST_FILES_TO_RUN_DIRECTORY}/openAPI.ts`], 21 | stdout: "null", 22 | stderr: "null", 23 | stdin: "null" 24 | }); 25 | await cmd.status(); 26 | 27 | const fileContent = Deno.readTextFileSync('./openapi.yml'); 28 | const fileContentExpected = Deno.readTextFileSync('./tests/integration-tests/resources/openapi.yml'); 29 | DenoAsserts.assertStringIncludes(fileContent, fileContentExpected); 30 | cmd.close(); 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /docs/web/mandarine/main-concepts/main-configuration.md: -------------------------------------------------------------------------------- 1 | # Typescript Configuration 2 | In order to run a _Mandarine-powered application_, it is necessary to supply a [`tsconfig.json`](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) file to `deno run`. 3 | 4 | If a `tsconfig.json` is not provided, Deno will fail to interpret Mandarine. 5 | 6 | ```javascript 7 | { 8 | "compilerOptions": { 9 | "experimentalDecorators": true, 10 | "emitDecoratorMetadata": true 11 | } 12 | } 13 | ``` 14 | 15 | > More tsconfig properties are allowed, however, the ones above **are necessary**. 16 | 17 | This configuration is passed by using the flag `--config (-c)` during `deno (run | test)`. For more information, [click here](https://deno.land/manual/getting_started/typescript#custom-typescript-compiler-options). 18 | 19 |   20 | 21 | # Deno Configuration 22 | Mandarine will always require to use the flags `--allow-net` & `--allow-read`, however, more flags may be required according to the use of different features, for example, if it is wanted to use environment variables then `--allow-env` will be required. [Click here](https://deno.land/manual/getting_started/permissions) for official information about _Deno permissions_. 23 | 24 | **Example:** 25 | ```shell script 26 | $ deno run --config tsconfig.json --allow-net --allow-read entryPoint.ts 27 | ``` 28 | 29 | -------------------------------------------------------------------------------- /main-core/mandarineLoading.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { rgb8 } from "https://deno.land/std@0.84.0/fmt/colors.ts"; 4 | import { Log } from "../logger/log.ts"; 5 | import { MandarineConstants } from "./mandarineConstants.ts"; 6 | 7 | /** 8 | * Loads the visualization of Mandarine's Compiler 9 | */ 10 | export const MandarineLoading = () => { 11 | console.log(` __ __ _ _ _______ _____ `); 12 | console.log(` | \\/ | | | (_) |__ __/ ____|`); 13 | console.log(` | \\ / | __ _ _ __ __| | __ _ _ __ _ _ __ ___ | | | (___ `); 14 | console.log(" | |\\/| |/ _` | '_ \\ / _` |/ _` | '__| | '_ \\ / _ \\ | | \\___ \\ "); 15 | console.log(" | | | | (_| | | | | (_| | (_| | | | | | | | __/ | | ____) |"); 16 | console.log(" |_| |_|\\__,_|_| |_|\\__,_|\\__,_|_| |_|_| |_|\\___| |_| |_____/ "); 17 | console.log(" "); 18 | console.log(" ================================================================= "); 19 | console.log(` ${rgb8("Mandarine.TS Framework", 208)} :: ${MandarineConstants.RELEASE_VERSION}`); 20 | console.log(""); 21 | Log.getLogger("MandarineCore").info(`Starting framework with PID ${Deno.pid}`); 22 | 23 | }; -------------------------------------------------------------------------------- /tests/integration-tests/files/nestedInjections.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Service } from "../../../main-core/decorators/stereotypes/service/service.ts"; 4 | import { Controller } from "../../../mvc-framework/core/decorators/stereotypes/controller/controller.ts"; 5 | import { GET } from "../../../mvc-framework/core/decorators/stereotypes/controller/routingDecorator.ts"; 6 | import { MandarineCore } from "../../../mod.ts"; 7 | import { Component } from "../../../main-core/decorators/stereotypes/component/component.ts"; 8 | import { Configuration } from "../../../main-core/decorators/stereotypes/configuration/configuration.ts"; 9 | 10 | @Configuration() 11 | export class Service3 { 12 | 13 | public pi() { 14 | return 3.14; 15 | } 16 | 17 | } 18 | @Component() 19 | export class Service2 { 20 | 21 | constructor(public readonly service3: Service3) {} 22 | } 23 | 24 | 25 | @Service() 26 | export class Service1 { 27 | 28 | constructor(public readonly service2: Service2) {} 29 | 30 | } 31 | 32 | 33 | @Controller() 34 | export class Controller1 { 35 | 36 | constructor(public readonly Service: Service1){} 37 | 38 | @GET('/test') 39 | public test(): number { 40 | return this.Service.service2.service3.pi(); 41 | } 42 | 43 | } 44 | 45 | new MandarineCore().MVC().run({ port: 8083 }); -------------------------------------------------------------------------------- /tests/integration-tests/valueConfigurationProperties_test.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { CommonUtils } from "../../main-core/utils/commonUtils.ts"; 4 | import { DenoAsserts, Orange, Test, waitForMandarineServer } from "../mod.ts"; 5 | 6 | export class ValueConfigurationProperties { 7 | 8 | 9 | constructor() { 10 | Orange.setOptions(this, { 11 | hooks: { 12 | beforeEach: () => CommonUtils.sleep(2) 13 | } 14 | }) 15 | } 16 | 17 | @Test({ 18 | name: "Test Endpoints from `files/customPropertiesAndValue.ts`", 19 | description: "Test all endpoints in file, and verifies that custom properties and value decorator are working fine" 20 | }) 21 | public async testValueAndConfigurationProperties() { 22 | let cmd = await waitForMandarineServer("customPropertiesAndValue.ts"); 23 | 24 | let test1 = (await (await fetch("http://localhost:7751/my-custom-config")).text()); 25 | 26 | let errorThrown = undefined; 27 | try { 28 | DenoAsserts.assertEquals(test1, "ANY-DATABASE"); 29 | } catch(error) { 30 | errorThrown = error; 31 | } 32 | 33 | cmd.close(); 34 | if(errorThrown != undefined) { 35 | throw errorThrown; 36 | } 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /tests/integration-tests/files/websocketClient.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { ApplicationContext } from "../../../main-core/application-context/mandarineApplicationContext.ts"; 4 | import { WebSocketClient, WSOnMessage, WSSend } from "../../../main-core/decorators/websockets/webSocket.ts"; 5 | import { MandarineCore } from "../../../main-core/mandarineCore.ts"; 6 | import { CommonUtils } from "../../../main-core/utils/commonUtils.ts"; 7 | 8 | @WebSocketClient("ws://localhost:8585") 9 | export class WebSocketClientClass { 10 | 11 | @WSSend() 12 | public sendProxy(data: string) {} 13 | 14 | } 15 | 16 | CommonUtils.sleep(5); 17 | new MandarineCore(); 18 | CommonUtils.sleep(5); 19 | 20 | setTimeout(() => { 21 | ApplicationContext.getInstance().getComponentsRegistry().getComponentByHandlerType(WebSocketClientClass)?.componentInstance.getClassHandler().sendProxy("Maria"); 22 | }, 2000); 23 | 24 | 25 | setTimeout(() => { 26 | ApplicationContext.getInstance().getComponentsRegistry().getComponentByHandlerType(WebSocketClientClass)?.componentInstance.getClassHandler().sendProxy("Andres"); 27 | }, 2000); 28 | 29 | 30 | setTimeout(() => { 31 | ApplicationContext.getInstance().getComponentsRegistry().getComponentByHandlerType(WebSocketClientClass)?.componentInstance.getClassHandler().sendProxy("Snejana"); 32 | }, 2000); 33 | -------------------------------------------------------------------------------- /tests/integration-tests/resourceHandlerStaticContent_test.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { CommonUtils } from "../../main-core/utils/commonUtils.ts"; 4 | import { DenoAsserts, Orange, Test, waitForMandarineServer } from "../mod.ts"; 5 | 6 | export class ResourceHandlerStaticContentTest { 7 | 8 | constructor() { 9 | Orange.setOptions(this, { 10 | hooks: { 11 | beforeEach: () => CommonUtils.sleep(2) 12 | } 13 | }) 14 | } 15 | 16 | @Test({ 17 | name: "[IT04] Test Endpoints from `files/resourceHandlerStaticContent.ts`", 18 | description: "Verifies Mandarine is serving static content" 19 | }) 20 | public async testTemplatesEndpoints() { 21 | let cmd = await waitForMandarineServer("resourceHandlerStaticContent.ts"); 22 | 23 | let resourceRequest = (await (await fetch("http://localhost:8091/docs/testing/helloWorld.txt")).text()); 24 | 25 | let errorThrown = undefined; 26 | try { 27 | DenoAsserts.assertEquals(resourceRequest, "Hello there, if you have got here, you'd better go back!."); 28 | } catch(error) { 29 | errorThrown = error; 30 | } 31 | 32 | cmd.close(); 33 | if(errorThrown != undefined) { 34 | throw errorThrown; 35 | } 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /docs/web/mandarine/main-concepts/custom-properties-decorator.md: -------------------------------------------------------------------------------- 1 | # `@ConfigurationProperties` Decorator 2 | The `@ConfigurationProperties` Decorator allows you to specify what file will be read for the specified class when using [`@Value`](https://www.mandarinets.org/docs/master/mandarine/value-decorator). This is wanted when different configuration files are used instead of the common `mandarine.json`. 3 | 4 | ----- 5 | 6 | ## Syntax 7 | ```typescript 8 | @ConfigurationProperties(filePath: string) 9 | ``` 10 | 11 | - filePath 12 | - Path of file to be used for configuration values 13 | 14 |   15 | 16 | ## Usage 17 | 18 | ```typescript 19 | // amazon.json 20 | { 21 | "AMAZON_API_KEY": "ABC123921038E203DFG203" 22 | } 23 | ``` 24 | 25 | ```typescript 26 | import { Value, ConfigurationProperties } from "https://deno.land/x/mandarinets@v2.3.2/mod.ts"; 27 | 28 | @ConfigurationProperties('amazon.json') 29 | export class ConfigurationProperties { 30 | 31 | @Value('AMAZON_API_KEY') 32 | /* It can also be static */ 33 | public customKey: string; 34 | 35 | } 36 | 37 | console.log(new ConfigurationProperties().customKey); // ABC123921038E203DFG203 38 | ``` 39 | 40 |   41 | 42 | In the example above we can see how we are using `@Value` with `@ConfigurationProperties` which is decorating the class itself. By doing this, we can bring values from different configuration files instead of Mandarine's default file. 43 | -------------------------------------------------------------------------------- /mvc-framework/openapi/decorators/apiTag.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { IndependentUtils } from "../../../main-core/utils/independentUtils.ts"; 4 | import { addOpenAPILoaderInfo, openAPIApplicationBuilder } from "../openapi-global.ts"; 5 | import { OpenAPITagObject } from "../openapi-spec.ts"; 6 | 7 | export const ApiTag = (tag: OpenAPITagObject | Array | string) => { 8 | return (target: any, methodName: string, propertyDesciptor: PropertyDescriptor) => { 9 | 10 | const consumer: Array = []; 11 | 12 | if(typeof tag === "string") { 13 | consumer.push({ name: tag }); 14 | } else { 15 | if(Array.isArray(tag)) { 16 | tag.forEach((item) => { 17 | if (typeof item === "string") { 18 | consumer.push({ name: item }); 19 | } else if (IndependentUtils.isObject(item)) { 20 | consumer.push(item); 21 | } 22 | }); 23 | } else if(IndependentUtils.isObject(tag)) { 24 | consumer.push(tag); 25 | } 26 | } 27 | 28 | addOpenAPILoaderInfo(target, { 29 | type: "ApiTag", 30 | methodName: methodName, 31 | data: consumer 32 | }) 33 | } 34 | } -------------------------------------------------------------------------------- /docs/web/mandarine/mandarine-mvc/mandarine-mvc-introduction.md: -------------------------------------------------------------------------------- 1 | # Mandarine MVC 2 | 3 | Mandarine MVC is part of the 4 cores inside the framework. Mandarine MVC specifically serves to everything related to a Mandarine-powered HTTP server. 4 | 5 | ----- 6 | 7 | ## Lifecycle 8 | Information & Diagram about Mandarine's lifecycle is found [here](/docs/mandarine/lifecycle). 9 | 10 | ----- 11 | 12 | ## Oak 13 | Under the hood, Mandarine makes use of [Oak](https://github.com/oakserver/oak), an HTTP dispatcher for Deno. 14 | 15 | ----- 16 | 17 | ## Must-know features 18 | - Routing Automation 19 | - Both routes & controllers are declared through the use of Decorators, making your application standardized & readable. 20 | - Template Engine 21 | - Support [EJS](https://ejs.co/) 22 | - Support [Handlerbars](https://handlebarsjs.com/) 23 | - Sessions 24 | - Built-in session engine that allows you to interact with sessions (states) across your web application. Sessions are unique for every requester. 25 | - Middleware 26 | - MVC allows you to create HTTP interceptors (Middleware) for specific routes (or pattern of routes). 27 | - Middleware are available to use pre & post request. 28 | 29 | ----- 30 | 31 | ## Server Data 32 | - By default Mandarine MVC runs on port **8080** 33 | - By default Mandarine MVC listens to **0.0.0.0** 34 | 35 | [See here](http://localhost:4200/docs/mandarine/properties) to change these values. 36 | -------------------------------------------------------------------------------- /tests/integration-tests/timer_test.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { CommonUtils } from "../../main-core/utils/commonUtils.ts"; 4 | import { Mandarine } from "../../mod.ts"; 5 | import { DenoAsserts, INTEGRATION_TEST_FILES_TO_RUN_DIRECTORY, Orange, Test } from "../mod.ts"; 6 | 7 | export class TimerTestFile { 8 | 9 | public MAX_COMPILATION_TIMEOUT_SECONDS = 50; 10 | 11 | @Test({ 12 | name: "Fixed rate timers & Cron", 13 | description: "Test that timers are working properly and have access to DI" 14 | }) 15 | public async websocketServer() { 16 | const currentDate = new Date(); 17 | let cmd = Deno.run({ 18 | cmd: ["deno", "run", "-c", "tsconfig.json", "--allow-all", "--unstable", `${INTEGRATION_TEST_FILES_TO_RUN_DIRECTORY}/timers.ts`], 19 | stdout: "piped", 20 | stderr: "inherit", 21 | stdin: "null" 22 | }); 23 | console.log("Sleeping"); 24 | CommonUtils.sleep(this.MAX_COMPILATION_TIMEOUT_SECONDS + (45)); 25 | 26 | const output = new TextDecoder().decode(await cmd.output()); 27 | Mandarine.MandarineCore.Internals.getTimersManager().clearAll(); 28 | DenoAsserts.assertStringIncludes(output, `CRON Andres`); 29 | DenoAsserts.assertStringIncludes(output, "TIMER Andres"); 30 | 31 | cmd.close(); 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /docs/web/mandarine/mandarine-security/security-expressions.md: -------------------------------------------------------------------------------- 1 | # Authentication Security Expressions 2 | Security expressions are boolean expressions that contain methods (or validators). They are passed as an string and then Mandarine's security core is responsible for processing them. 3 | 4 | ------------ 5 | 6 | ## Usage 7 | Security expressions are available for usage in [`@AllowOnly` decorator](/docs/mandarine/auth-allow-only-decorator). 8 | 9 | ## Current Built-In Security Expressions 10 | | Expression | Parameters | Description | 11 | | ---------- | ---------- | ----------- | 12 | | `isAuthenticated()` | none | Verify that there is a logged-in user (from Mandarine's built-in authentication system) in the request | 13 | | `hasRole(role)` | role: `String` | Verify that the current logged-in user contains a role | 14 | 15 | ## Examples 16 | ```typescript 17 | import { Controller, AllowOnly, GET } from "https://deno.land/x/mandarinets@v2.3.2/mod.ts"; 18 | 19 | @Controller() 20 | @AllowOnly("isAuthenticated()") 21 | export class lastcontroller { 22 | 23 | @GET('/security-expressions-2') 24 | @AllowOnly("hasRole('USER') || hasRole('ADMIN')") 25 | public handler2() { 26 | return "You are authenticated and have role USER or ADMIN" 27 | } 28 | } 29 | ``` 30 | 31 | > At the moment, security expressions are only available under the concept of Mandarine's authentication system and `@AllowOnly` connected to Mandarine's MVC core. -------------------------------------------------------------------------------- /security-core/utils/auth.util.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Mandarine } from "../../main-core/Mandarine.ns.ts"; 4 | import { MandarineConstants } from "../../main-core/mandarineConstants.ts"; 5 | 6 | export class AuthUtils { 7 | public static findAuthCookie(context: Mandarine.Types.RequestContext): string | undefined { 8 | return context.cookies.get(MandarineConstants.SECURITY_AUTH_COOKIE_NAME, { signed: true }); 9 | } 10 | 11 | public static verifyAuthenticationSatisfaction(withSessionContainer?: boolean): boolean { 12 | const getAuthManagerBuilder = Mandarine.Security.getAuthManagerBuilder(); 13 | return (getAuthManagerBuilder.passwordEncoder != undefined 14 | && getAuthManagerBuilder.userDetailsService != undefined 15 | && (withSessionContainer === false || Mandarine.Global.getSessionContainer().store != undefined)); 16 | } 17 | 18 | public static verifyHTTPLogingConfigurerSatisfaction(loginConfigurer: Mandarine.Security.Core.LoginConfigurer): boolean { 19 | return (loginConfigurer.loginProcessingUrl != undefined 20 | && loginConfigurer.logoutUrl != undefined 21 | && loginConfigurer.passwordParameter != undefined 22 | && loginConfigurer.usernameParameter != undefined); 23 | } 24 | } -------------------------------------------------------------------------------- /mvc-framework/core/middlewares/responseTimeHeaderMiddleware.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Mandarine } from "../../../main-core/Mandarine.ns.ts"; 4 | import { MandarineMvc } from "../../mandarine-mvc.ns.ts"; 5 | 6 | interface MiddlewareData { 7 | responseTimeIsPostRequest: boolean; 8 | } 9 | 10 | const defaultMiddlewareData: MiddlewareData = { 11 | responseTimeIsPostRequest: false 12 | } 13 | 14 | export const responseTimeHandler: MandarineMvc.Internal.InternalMiddlewareFunc = (context: Mandarine.Types.RequestContext, data: MiddlewareData = defaultMiddlewareData) => { 15 | const typedContext: Mandarine.Types.RequestContext = context; 16 | 17 | const config = Mandarine.Global.getMandarineConfiguration(); 18 | if(!config.mandarine.server.responseTimeHeader) return true; 19 | 20 | if(!typedContext.timeMetadata) typedContext.timeMetadata = { 21 | startedAt: 0, 22 | finishedAt: 0 23 | }; 24 | 25 | if (!data.responseTimeIsPostRequest) { 26 | typedContext.timeMetadata.startedAt = Date.now(); 27 | } else { 28 | typedContext.timeMetadata.finishedAt = Date.now(); 29 | 30 | const { finishedAt, startedAt } = typedContext.timeMetadata; 31 | 32 | const responseTime: number = finishedAt - startedAt; 33 | typedContext.response.headers.set("X-Response-Time", responseTime.toString()); 34 | 35 | } 36 | 37 | return true; 38 | }; 39 | -------------------------------------------------------------------------------- /main-core/utils/propertiesUtils.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { CommonUtils } from "./commonUtils.ts"; 4 | import { MandarineUtils } from "./mandarineUtils.ts"; 5 | 6 | export class PropertiesUtils { 7 | 8 | private static parseLine(obj: any, keys: string | Array, value: any) 9 | { 10 | keys = (typeof keys === "string") ? keys.split(".") : keys; 11 | const key = keys.shift()!; 12 | 13 | if (keys.length === 0) 14 | { 15 | obj[key] = value; 16 | return; 17 | } 18 | else if (!obj.hasOwnProperty(key)) 19 | { 20 | obj[key] = {}; 21 | } 22 | 23 | this.parseLine(obj[key], keys, CommonUtils.parseToKnownType(value)); 24 | return obj; 25 | } 26 | 27 | public static parse(input: string) { 28 | const dividedInput = MandarineUtils.parseConfigurationFile(input); 29 | const object = {}; 30 | 31 | Object.keys(dividedInput).forEach((key) => { 32 | this.parseLine(object, key, dividedInput[key]); 33 | }); 34 | 35 | return object; 36 | } 37 | 38 | public static propertiesToJS(path: string) { 39 | try { 40 | const content = Deno.readTextFileSync(path); 41 | return this.parse(content); 42 | } catch { 43 | return {}; 44 | } 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /security-core/core/proxys/securityCoreDecorators.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import type { Mandarine } from "../../../main-core/Mandarine.ns.ts"; 4 | import { MandarineException } from "../../../main-core/exceptions/mandarineException.ts"; 5 | import { Reflect } from "../../../main-core/reflectMetadata.ts"; 6 | import { MandarineConstants } from "../../../main-core/mandarineConstants.ts"; 7 | 8 | export class SecurityCoreDecoratorsProxy { 9 | 10 | public static registerAllowOnlyDecorator(targetClass: any, permissions: Mandarine.Security.Auth.Permissions, methodName: string) { 11 | let isMethod: boolean = methodName != null; 12 | if(!Array.isArray(permissions) && !(typeof permissions === 'string')) throw new MandarineException(MandarineException.INVALID_ALLOWONLY_DECORATOR_PERMISSIONS); 13 | let newPermissions; 14 | if(Array.isArray(permissions)) { 15 | newPermissions = [...permissions]; 16 | } else { 17 | newPermissions = permissions; 18 | } 19 | if(!isMethod) { 20 | Reflect.defineMetadata(MandarineConstants.REFLECTION_MANDARINE_SECURITY_ALLOWONLY_DECORATOR, newPermissions, targetClass); 21 | } else { 22 | Reflect.defineMetadata(`${MandarineConstants.REFLECTION_MANDARINE_SECURITY_ALLOWONLY_DECORATOR}:${methodName}`, newPermissions, targetClass, methodName); 23 | } 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /main-core/utils/components/websocketServer.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { ApplicationContext } from "../../application-context/mandarineApplicationContext.ts"; 4 | import type { ComponentComponent } from "../../components/component-component/componentComponent.ts"; 5 | import { MandarineConstants } from "../../mandarineConstants.ts"; 6 | import { WebSocketBase64Const } from "../../websocket/base64bundle.ts"; 7 | 8 | export class WebSocketServerUtil { 9 | 10 | public static mount(component: ComponentComponent, port: number) { 11 | component.addInternal(MandarineConstants.COMPONENT_PROPERTY_WEBSOCKET, new Worker(WebSocketBase64Const, { 12 | type: "module", 13 | deno: { 14 | namespace: true, 15 | }, 16 | })); 17 | 18 | component.getInternal(MandarineConstants.COMPONENT_PROPERTY_WEBSOCKET).postMessage({ 19 | cmd: "INITIALIZE", 20 | data: { 21 | port: port 22 | } 23 | }); 24 | 25 | ApplicationContext.getInstance().getComponentsRegistry().connectWebsocketServerProxy(component); 26 | } 27 | 28 | public static unmount(component: ComponentComponent): void { 29 | component.getInternal(MandarineConstants.COMPONENT_PROPERTY_WEBSOCKET).terminate(); 30 | component.deleteInternal(MandarineConstants.COMPONENT_PROPERTY_WEBSOCKET); 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /docs/web/mandarine/main-concepts/component-middleware.md: -------------------------------------------------------------------------------- 1 | # Middleware 2 | Middleware are responsible for adding interceptors to controllers. They are executed **before** and **after** Mandarine Request Time (MRT). They are useful for validations as they can decide whether a request should be processed. 3 | 4 | ----- 5 |   6 | 7 | ## Concepts 8 | - Middleware are part of Mandarine's MVC Core 9 | - It accepts the use of dependency injection, however, this type of component is not injectable. 10 | 11 |   12 | 13 | ## Syntax 14 | 15 | ```typescript 16 | @Middleware(regexRoute: RegExp) 17 | ``` 18 | - `regexRoute` 19 | - Required 20 | - Regular expression of HTTP endpoint url to intercept 21 | 22 | ## Usage 23 | 24 | ```typescript 25 | import { Middleware, MiddlewareTarget, ResponseParam } from "https://deno.land/x/mandarinets@v2.3.2/mod.ts"; 26 | 27 | @Middleware(new RegExp('/api/*')) 28 | export class Middleware1 implements MiddlewareTarget { 29 | 30 | // To be executed on pre-request of a request which url matches the middleware's regular expression route 31 | public onPreRequest(@ResponseParam() response: any): boolean { 32 | /** 33 | * True = the request must continue, 34 | * False = the request will stop 35 | */ 36 | return true; 37 | } 38 | 39 | // To be executed on post-request of a request which url matches the middleware's regular expression route 40 | public onPostRequest(): void { 41 | } 42 | 43 | } 44 | ``` 45 | -------------------------------------------------------------------------------- /security-core/core/internals/permissions/permissionValidatorsRegistry.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import type { Mandarine } from "../../../../main-core/Mandarine.ns.ts"; 4 | import { isAuthenticated } from "./validators/isAuthenticated.ts"; 5 | import { hasRole } from "./validators/hasRole.ts"; 6 | 7 | export class PermissionValidatorsRegistry { 8 | 9 | public static instance: PermissionValidatorsRegistry; 10 | 11 | private validators: Map = new Map(); 12 | 13 | public constructor() { 14 | this.validators.set("isauthenticated", isAuthenticated); 15 | this.validators.set("hasrole", hasRole); 16 | } 17 | 18 | public callValidator(validatorName: string, request: any, authentication: any, inputs: Array): boolean { 19 | const validator = this.validators.get(validatorName.split("(")[0]); 20 | if(validator) { 21 | return validator(request, authentication)(inputs); 22 | } 23 | return false; 24 | } 25 | 26 | public getAllValidators() { 27 | return Array.from(this.validators.values()); 28 | } 29 | 30 | public static getInstance() { 31 | if(!PermissionValidatorsRegistry.instance) PermissionValidatorsRegistry.instance = new PermissionValidatorsRegistry(); 32 | return PermissionValidatorsRegistry.instance; 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /tests/integration-tests/files/resourceHandlerStaticContent.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Configuration } from "../../../main-core/decorators/stereotypes/configuration/configuration.ts"; 4 | import { Injectable } from "../../../main-core/dependency-injection/decorators/injectable.ts"; 5 | import { Mandarine } from "../../../main-core/Mandarine.ns.ts"; 6 | import { MandarineCore, MandarineResourceResolver } from "../../../mod.ts"; 7 | import { ResourceHandler } from "../../../mvc-framework/core/internal/components/resource-handler-registry/resourceHandler.ts"; 8 | import { Override } from "../../../main-core/decorators/native-components/override.ts"; 9 | import { MandarineNative } from "../../../main-core/Mandarine.native.ns.ts"; 10 | 11 | @Override() 12 | export class WebMvcConfigurer extends Mandarine.Native.WebMvcConfigurer { 13 | 14 | public addResourceHandlers(): Mandarine.MandarineCore.IResourceHandlerRegistry { 15 | let resourceHandlerRegistry = Mandarine.Global.getResourceHandlerRegistry().getNew(); 16 | 17 | resourceHandlerRegistry.addResourceHandler( 18 | new ResourceHandler() 19 | .addResourceHandler(new RegExp("/docs/(.*)")) 20 | .addResourceHandlerLocation("./docs") 21 | .addResourceResolver(new MandarineResourceResolver()) 22 | ); 23 | 24 | return resourceHandlerRegistry; 25 | } 26 | 27 | } 28 | 29 | new MandarineCore().MVC().run({ port: 8091 }); -------------------------------------------------------------------------------- /main-core/MandarineEnvConstants.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | export class MandarineEnvironmentalConstants { 4 | 5 | public static readonly MANDARINE_JSON_FILE: string = "MANDARINE_JSON_FILE"; 6 | public static readonly MANDARINE_PROPERTY_FILE: string = "MANDARINE_PROPERTY_FILE"; 7 | 8 | public static readonly MANDARINE_SERVER_HOST: string = "MANDARINE_SERVER_HOST"; 9 | public static readonly MANDARINE_SERVER_PORT: string = "MANDARINE_SERVER_PORT"; 10 | public static readonly MANDARINE_SERVER_RESPONSE_TIME_HEADER: string = "MANDARINE_SERVER_RESPONSE_TIME_HEADER"; 11 | public static readonly MANDARINE_SERVER_SESSION_MIDDLEWARE: string = "MANDARINE_SERVER_SESSION_MIDDLEWARE"; 12 | public static readonly MANDARINE_SERVER_CORS_MIDDLEWARE: string = "MANDARINE_SERVER_CORS_MIDDLEWARE"; 13 | 14 | public static readonly MANDARINE_STATIC_CONTENT_FOLDER: string = "MANDARINE_STATIC_CONTENT_FOLDER"; 15 | 16 | public static readonly MANDARINE_AUTH_EXPIRATION_MS: string = "MANDARINE_AUTH_EXPIRATION_MS"; 17 | 18 | public static readonly MANDARINE_SESSIONS_TOUCH: string = "MANDARINE_SESSIONS_TOUCH"; 19 | public static readonly MANDARINE_SESSIONS_EXPIRATION_TIME: string = "MANDARINE_SESSIONS_EXPIRATION_TIME"; 20 | public static readonly MANDARINE_SESSIONS_EXPIRATION_INTERVAL: string = "MANDARINE_SESSIONS_EXPIRATION_INTERVAL"; 21 | public static readonly MANDARINE_SERVER_CACHE_ENABLED: string = "MANDARINE_SERVER_CACHE_ENABLED"; 22 | } -------------------------------------------------------------------------------- /tests/integration-tests/files/microservices/redis.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Microservice, MSOnMessage } from "../../../../main-core/decorators/microservices/microservice.ts"; 4 | import { MandarineCore } from "../../../../main-core/mandarineCore.ts"; 5 | import { Microlemon } from "../../../../main-core/microservices/mod.ts"; 6 | import { Controller } from "../../../../mvc-framework/core/decorators/stereotypes/controller/controller.ts"; 7 | import { GET } from "../../../../mvc-framework/core/decorators/stereotypes/controller/routingDecorator.ts"; 8 | 9 | 10 | let [resolveScope, rejectScope]: [any, any] = [,,]; 11 | 12 | const createPromise = () => new Promise((resolve, reject) => { 13 | resolveScope = resolve; 14 | rejectScope = reject; 15 | }); 16 | 17 | let promise = createPromise(); 18 | 19 | @Microservice({ 20 | transporter: Microlemon.Transporters.REDIS, 21 | configuration: { 22 | transport: "REDIS", 23 | options: { 24 | host: "127.0.0.1" 25 | } 26 | }, 27 | channels: ["myqueue"] 28 | }) 29 | export class MicroserviceClass { 30 | 31 | @MSOnMessage() 32 | public getMessage(data: any) { 33 | resolveScope(data); 34 | } 35 | 36 | } 37 | 38 | 39 | @Controller() 40 | class MicroserviceResult { 41 | 42 | @GET('/result') 43 | public async helloWorld(): Promise { 44 | return await promise; 45 | } 46 | 47 | } 48 | 49 | new MandarineCore().MVC().run({ port: 6934 }); -------------------------------------------------------------------------------- /tests/integration-tests/templatest_test.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { CommonUtils } from "../../main-core/utils/commonUtils.ts"; 4 | import { DenoAsserts, Orange, Test, waitForMandarineServer } from "../mod.ts"; 5 | 6 | export class TemplatesTest { 7 | 8 | public MAX_COMPILATION_TIMEOUT_SECONDS = 50; 9 | 10 | constructor() { 11 | Orange.setOptions(this, { 12 | hooks: { 13 | beforeEach: () => CommonUtils.sleep(2) 14 | } 15 | }) 16 | } 17 | 18 | @Test({ 19 | name: "[IT06] Test Endpoints from `files/templates.ts`", 20 | description: "Verifies manual templates are working properly" 21 | }) 22 | public async testTemplatesEndpoints() { 23 | let cmd = await waitForMandarineServer("templates.ts"); 24 | 25 | let ejsTemplate = (await (await fetch("http://localhost:8090/manual-template-ejs")).text()); 26 | let handlebarsTemplate = (await (await fetch("http://localhost:8090/manual-template-handlebars")).text()); 27 | 28 | let errorThrown = undefined; 29 | try { 30 | DenoAsserts.assertEquals(ejsTemplate, "

Will

"); 31 | DenoAsserts.assertEquals(handlebarsTemplate, "

Andres

"); 32 | } catch(error) { 33 | errorThrown = error; 34 | } 35 | 36 | cmd.close(); 37 | if(errorThrown != undefined) { 38 | throw errorThrown; 39 | } 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /orm-core/core/exceptions/mandarineORMException.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | export class MandarineORMException extends Error { 4 | 5 | public static INVALID_REPOSITORY: string = "The repository could not be loaded because it is an incorrect repository or it has an invalid model."; 6 | public static UNKNOWN_DIALECT: string = "The dialect you are trying to use is not supported by Mandarine. See more here https://mandarineframework.gitbook.io/mandarine-ts/mandarine-data/orm#dialects"; 7 | public static GENERATION_HANDLER_REQUIRED: string = "The strategy for primary key is 'MANUAL'. You must identify a handler in order to generate values"; 8 | public static IMPOSSIBLE_CONNECTION: string = "The connection could not be made because the database client did not accept it."; 9 | public static INSTANCE_IN_SAVE: string = "Saving an instance is not a valid save statement. Please save an initialized object."; 10 | public static MQL_INVALID_KEY: string = "MQL has failed to process the expression."; 11 | public static QUERY_BEFORE_CONNECTION: string = "A query has been requested but the connection is not initialized."; 12 | public static RESERVED_KEYWORD_COLNAME: string = "Name (%column%) is a reserved keyword and cannot be used for column."; 13 | 14 | constructor(public message: string, public objectName: string) { 15 | super(message + " ~ Object name: " + objectName); 16 | this.name = "MandarineORMException"; 17 | this.stack = (new Error()).stack; 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /docs/web/mandarine/main-concepts/component-catch.md: -------------------------------------------------------------------------------- 1 | # Catch 2 | A catch component is a Mandarine-powered class part of Mandarine's stereotypes. Catch components have a single responsability which is intercepting exceptions during requests. Catch component implements [**ExceptionFilter**](https://doc.deno.land/https/raw.githubusercontent.com/mandarineorg/mandarinets/develop/main-core/internals/interfaces/exceptionFilter.ts#ExceptionFilter) 3 | 4 | -------- 5 | 6 | ## Concepts 7 | - Catch components are part of Mandarine's main core. 8 | - They only catch exceptions taking place during a request of Mandarine MVC. 9 | - It is not injectable but it **does** accept injections. 10 | 11 |   12 | 13 | ## Syntax 14 | 15 | ```typescript 16 | @Catch(error: any extends Error) 17 | export class MyCatchComponent implements ExceptionFilter { 18 | } 19 | ``` 20 | 21 | ## Usage 22 | 23 | ```typescript 24 | import { Catch, ExceptionFilter, ExceptionContext } from "https://deno.land/x/mandarinets@v2.3.2/mod.ts"; 25 | 26 | @Catch(MyException) 27 | export class MyCatchComponent implements ExceptionFilter { 28 | catch(exceptionContext: ExceptionContext) { 29 | console.log("An error has occured"); 30 | exceptionContext.getResponse().body = { 31 | msg: exceptionContext.getException().toString() 32 | } 33 | } 34 | } 35 | ``` 36 | 37 | ## HTTP Parameter Decorators 38 | Catch components are invoked during a request, this means, they are completely compatible with [HTTP Parameter Decorators](/docs/master/mandarine/http-handlers) 39 | 40 | -------------------------------------------------------------------------------- /main-core/internals/interfaces/middlewareTarget.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | /** 4 | * Define the behavior of a user-side middleware 5 | * The middleware target will be called at the time of a request. 6 | * onPreRequest(...args) will be called before executing the endpoint. 7 | * onPostRequest(...args) will be called after executing the endpoint. 8 | * 9 | * If onPreRequest **returns false**, the endpoint's execution will be ignored as well as the post-request middleware. 10 | * In order to keep the execution cycle going, onPreRequest must return true. 11 | * Although in almost all use cases returning true will sound logical, Identity verifications fit in the concept of returning false. 12 | * 13 | * export class MyMiddleware implements MiddlewareTarget { 14 | * onPreRequest(@RequestParam() request: Request, @ResponseParam() response: any) { 15 | * console.log("onPreRequest() called"); 16 | * return true; 17 | * } 18 | * 19 | * onPostRequest() { 20 | * console.log("onPostRequest() called"); 21 | * } 22 | * } 23 | * 24 | */ 25 | export interface MiddlewareTarget { 26 | onPreRequest(...args: Array): boolean; 27 | onPostRequest(...args: Array): void; 28 | } 29 | 30 | export interface NonComponentMiddlewareTarget { 31 | onPreRequest(request: any, response: any): boolean; 32 | onPostRequest(request: any, response: any): void; 33 | } -------------------------------------------------------------------------------- /tests/integration-tests/files/customCatch.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Catch } from "../../../main-core/decorators/stereotypes/catch/catch.ts"; 4 | import { MandarineException } from "../../../main-core/exceptions/mandarineException.ts"; 5 | import { ExceptionFilter, ExceptionContext } from "../../../main-core/internals/interfaces/exceptionFilter.ts"; 6 | import { Controller } from "../../../mvc-framework/core/decorators/stereotypes/controller/controller.ts"; 7 | import { GET } from "../../../mvc-framework/core/decorators/stereotypes/controller/routingDecorator.ts"; 8 | import { MandarineCore } from "../../../main-core/mandarineCore.ts"; 9 | 10 | @Catch(MandarineException) 11 | export class MyExceptionFilter implements ExceptionFilter { 12 | public async catch(exceptionContext: ExceptionContext) { 13 | exceptionContext.getResponse().body = { 14 | error: "A error has occured", 15 | msg: exceptionContext.getException().toString() 16 | } 17 | } 18 | } 19 | 20 | @Controller() 21 | export class MyController { 22 | 23 | @GET('/throw') 24 | public throwError() { 25 | throw new Error("An error has been thrown"); 26 | } 27 | 28 | @GET('/throw-2') 29 | public throwError2() { 30 | throw new MandarineException("An error has been thrown"); 31 | } 32 | 33 | @GET('/do-not-throw') 34 | public dontThrow() { 35 | return "Hello world"; 36 | } 37 | 38 | } 39 | 40 | new MandarineCore().MVC().run({ port: 2490 }); -------------------------------------------------------------------------------- /docs/web/mandarine/mandarine-data/data-repositories.md: -------------------------------------------------------------------------------- 1 | # Repositories 2 | Mandarine's built-in ORM offers the concept & usability of Repositories. Repositories serve as a bridge between your database & your code, they execute tasks such as bringing information from the database to your code, deleting & saving, and more. In a nutshell, repositories allow you to execute queries programmatically, this means, you do not have to necessarily write the SQL query. 3 | 4 | ---- 5 | 6 | ## Declaring a repository 7 | Repositories are declared by using the decorator `@Repository()` . The `@Repository` decorator targets an **abstract class**. 8 | 9 | The abstract class that will represent your repository must be extended [MandarineRepository](https://doc.deno.land/https/raw.githubusercontent.com/mandarineorg/mandarinets/master/orm-core/repository/mandarineRepository.ts#MandarineRepository) . MandarineRepository is a generic class that will take `T` as your model's instance. 10 | 11 | **Syntax:** 12 | ```typescript 13 | @Repository() 14 | ``` 15 | 16 | Example 17 | ```typescript 18 | import { Repository, MandarineRepository } from "https://deno.land/x/mandarinets@v2.3.2/mod.ts"; 19 | 20 | @Repository() 21 | abstract class MyRepository extends MandarineRepository { 22 | 23 | constructor() { 24 | super(MyModel); 25 | } 26 | 27 | } 28 | ``` 29 | 30 | > **Note** that all repositories must have a constructor with a super call. This super call will take your model's instance as shown above. 31 | 32 | ```typescript 33 | constructor() { 34 | super(MyModel); 35 | } 36 | ``` 37 | -------------------------------------------------------------------------------- /tests/integration-tests/files/timers.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { ApplicationContext } from "../../../main-core/application-context/mandarineApplicationContext.ts"; 4 | import { Component } from "../../../main-core/decorators/stereotypes/component/component.ts"; 5 | import { Scheduled, Timer } from "../../../main-core/decorators/tasks/taskScheduler.ts"; 6 | import { TaskManager } from "../../../main-core/mandarine-native/tasks/taskManager.ts"; 7 | import { Mandarine } from "../../../main-core/Mandarine.ns.ts"; 8 | import { MandarineCore } from "../../../main-core/mandarineCore.ts"; 9 | 10 | @Component() 11 | export class MyService { 12 | public getName() { 13 | return "Andres"; 14 | } 15 | } 16 | 17 | @Component() 18 | export class MyComponent { 19 | 20 | constructor(public service: MyService) {} 21 | 22 | @Scheduled("* * * * *") 23 | public everyMinute() { 24 | console.log("CRON", this.service.getName()); 25 | } 26 | 27 | @Timer(30000) 28 | public every30Seconds() { 29 | console.log("TIMER", this.service.getName()); 30 | } 31 | 32 | } 33 | 34 | setTimeout(() => { 35 | ApplicationContext.getInstance().getDIFactory().getDependency(TaskManager)?.getCronManager().stopTasks(); 36 | ApplicationContext.getInstance().getDIFactory().getDependency(TaskManager)?.getCronManager().clearTasks(); 37 | Mandarine.MandarineCore.Internals.getTimersManager().clearAll(); 38 | Deno.exit(0); 39 | }, 90000); 40 | 41 | new MandarineCore(); -------------------------------------------------------------------------------- /docs/web/mandarine/main-concepts/custom-properties.md: -------------------------------------------------------------------------------- 1 | # Custom Properties 2 | As well as the [`.env`](/docs/mandarine/dot-env-file), `Mandarine.json` allows you to establish custom properties inside it. These properties can then be accessed by using the `@Value` decorator. 3 | 4 | ---- 5 | 6 | In order to set your own properties, you must create a JSON file. This JSON file needs to have & respect the structure above, this means, you can add and override values, but you cannot change the structure. 7 | Your configuration file does not have to necessarily follow Mandarine's core configuration such as the server or the template engine information. If you decide to ignore some of these properties, Mandarine will set its default values to those that have been ignored previously. 8 | 9 | In order to use your configuration file, you may: 10 | 11 | - Create a file in "_./src/main/resources_" called "**properties.json**" 12 | - Create your own file in a directory of your preference and override the default's location (the one mentioned above) by using a [`Mandarine.json` File](docs/mandarine/mandarine-json-file). 13 | 14 | After having your Mandarine's configuration file set up, you can add your own custom properties. 15 | 16 | ```typescript 17 | { 18 | "mandarine": { 19 | "server": { 20 | "port": 8000 21 | }, 22 | "myToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" 23 | }, 24 | "myCustomPropertyKey": "My custom value" 25 | } 26 | ``` 27 | 28 | > See a list of all possible properties to configure [by clicking here](https://www.mandarinets.org/docs/master/mandarine/properties) -------------------------------------------------------------------------------- /tests/integration-tests/files/microservices/nats.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Microservice, MSOnMessage } from "../../../../main-core/decorators/microservices/microservice.ts"; 4 | import { MandarineCore } from "../../../../main-core/mandarineCore.ts"; 5 | import { Microlemon } from "../../../../main-core/microservices/mod.ts"; 6 | import { Controller } from "../../../../mvc-framework/core/decorators/stereotypes/controller/controller.ts"; 7 | import { GET } from "../../../../mvc-framework/core/decorators/stereotypes/controller/routingDecorator.ts"; 8 | 9 | 10 | let [resolveScope, rejectScope]: [any, any] = [,,]; 11 | 12 | const createPromise = () => new Promise((resolve, reject) => { 13 | resolveScope = resolve; 14 | rejectScope = reject; 15 | }); 16 | 17 | let promise = createPromise(); 18 | 19 | @Microservice({ 20 | transporter: Microlemon.Transporters.NATS, 21 | configuration: { 22 | transport: "NATS", 23 | options: { 24 | host: "127.0.0.1", 25 | user: "guest", 26 | pass: "guest" 27 | } 28 | }, 29 | channels: ["myqueue"] 30 | }) 31 | export class MicroserviceClass { 32 | 33 | @MSOnMessage() 34 | public getMessage(data: any) { 35 | resolveScope(data); 36 | } 37 | 38 | } 39 | 40 | 41 | @Controller() 42 | class MicroserviceResult { 43 | 44 | @GET('/result') 45 | public async helloWorld(): Promise { 46 | return await promise; 47 | } 48 | 49 | } 50 | 51 | new MandarineCore().MVC().run({ port: 6933 }); -------------------------------------------------------------------------------- /.github/workflows/microservices.yml: -------------------------------------------------------------------------------- 1 | name: Mandarine Microservices 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | name: Mandarine Microservices (${{ matrix.os }}) 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | matrix: 11 | os: [ubuntu-latest] 12 | deno: ["v1.9.0"] 13 | redis: [6.2] 14 | fail-fast: true 15 | services: 16 | rabbitmq: 17 | image: rabbitmq:3-management 18 | ports: 19 | - 5672:5672 20 | - 15672:15672 21 | steps: 22 | - uses: actions/checkout@v2 23 | - name: Setup deno 24 | uses: denolib/setup-deno@master 25 | with: 26 | deno-version: ${{ matrix.deno }} 27 | - name: Set NATS Server Version 28 | run: echo "NATS_VERSION=v2.2.1" >> $GITHUB_ENV 29 | - name: Get nats-server 30 | run: | 31 | wget "https://github.com/nats-io/nats-server/releases/download/$NATS_VERSION/nats-server-$NATS_VERSION-linux-amd64.zip" -O tmp.zip 32 | unzip tmp.zip 33 | mv nats-server-$NATS_VERSION-linux-amd64 nats-server 34 | rm nats-server/README.md LICENSE 35 | - name: Set up Redis ${{ matrix.redis }} 36 | uses: shogo82148/actions-setup-redis@v1 37 | with: 38 | redis-version: ${{ matrix.redis }} 39 | auto-start: "true" 40 | - name: run tests 41 | env: 42 | ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' 43 | run: GITHUB=true deno test -c tsconfig.json --reload --allow-all --unstable --fail-fast tests/integration-tests/microservices.ts -------------------------------------------------------------------------------- /tests/integration-tests/files/microservices/rabbitMq.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Microservice, MSOnMessage } from "../../../../main-core/decorators/microservices/microservice.ts"; 4 | import { MandarineCore } from "../../../../main-core/mandarineCore.ts"; 5 | import { Microlemon } from "../../../../main-core/microservices/mod.ts"; 6 | import { Controller } from "../../../../mvc-framework/core/decorators/stereotypes/controller/controller.ts"; 7 | import { GET } from "../../../../mvc-framework/core/decorators/stereotypes/controller/routingDecorator.ts"; 8 | 9 | 10 | let [resolveScope, rejectScope]: [any, any] = [,,]; 11 | 12 | const createPromise = () => new Promise((resolve, reject) => { 13 | resolveScope = resolve; 14 | rejectScope = reject; 15 | }); 16 | 17 | let promise = createPromise(); 18 | 19 | @Microservice({ 20 | transporter: Microlemon.Transporters.AMQP, 21 | configuration: { 22 | transport: "AMQP", 23 | options: { 24 | host: "127.0.0.1", 25 | username: "guest", 26 | password: "guest" 27 | } 28 | }, 29 | channels: ["myqueue"] 30 | }) 31 | export class MicroserviceClass { 32 | 33 | @MSOnMessage() 34 | public getMessage(data: any) { 35 | resolveScope(data); 36 | } 37 | 38 | } 39 | 40 | 41 | @Controller() 42 | class MicroserviceResult { 43 | 44 | @GET('/result') 45 | public async helloWorld(): Promise { 46 | return await promise; 47 | } 48 | 49 | } 50 | 51 | new MandarineCore().MVC().run({ port: 6932 }); -------------------------------------------------------------------------------- /orm-core/nosql/mongoDbService.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { MongoClient, Bson, ListDatabaseInfo } from "https://raw.githubusercontent.com/mandarineorg/mandarinets-modules/master/mongo/0.21.2/mod.ts"; 4 | import { Database } from "https://raw.githubusercontent.com/mandarineorg/mandarinets-modules/master/mongo/0.21.2/src/database.ts"; 5 | import { Collection } from "https://raw.githubusercontent.com/mandarineorg/mandarinets-modules/master/mongo/0.21.2/src/collection/collection.ts"; 6 | 7 | export class MongoDBService { 8 | 9 | private client!: MongoClient; 10 | 11 | constructor(connectionUrl: string) { 12 | this.client = new MongoClient(); 13 | this.client.connect(connectionUrl); 14 | } 15 | 16 | public async listDatabases(): Promise> { 17 | return await this.client.listDatabases(); 18 | } 19 | 20 | public getDatabase(name: string): Database { 21 | return this.client.database(name); 22 | } 23 | 24 | public getCollection(db: Database | string, collectionName: string): Collection | undefined { 25 | if(typeof db === "string") { 26 | return this.getDatabase(db).collection(collectionName); 27 | } else { 28 | if(db instanceof Database) { 29 | return db.collection(collectionName); 30 | } 31 | } 32 | return undefined; 33 | } 34 | 35 | public bson() { 36 | return Bson; 37 | } 38 | 39 | public close() { 40 | this.client.close(); 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Unit Tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | name: tests (${{ matrix.os }}) 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | matrix: 11 | os: [windows-latest, macOS-latest] 12 | deno: ["v1.9.0"] 13 | fail-fast: true 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Setup deno 17 | uses: denolib/setup-deno@master 18 | with: 19 | deno-version: ${{ matrix.deno }} 20 | - name: run tests 21 | env: 22 | ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' 23 | run: deno test -c tsconfig.json --reload --allow-all --unstable --fail-fast tests/unit-tests/ 24 | - name: run session tests 25 | env: 26 | ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' 27 | run: deno test -c tsconfig.json --reload --allow-all --unstable --fail-fast tests/unit-tests/sessions.ts 28 | - name: Value decorator 29 | env: 30 | ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' 31 | run: deno test -c tsconfig.json --reload --allow-all --unstable --fail-fast tests/unit-tests/valueDecorator.ts 32 | - name: Cache manager 33 | env: 34 | ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' 35 | run: deno test -c tsconfig.json --reload --allow-all --unstable --fail-fast tests/unit-tests/cacheManager.ts 36 | - name: Configuration setter 37 | env: 38 | ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' 39 | run: deno test -c tsconfig.json --reload --allow-all --unstable --fail-fast tests/unit-tests/setConfiguration.ts 40 | -------------------------------------------------------------------------------- /security-core/utils/securityUtils.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { HmacSha256 } from "../hash/sha256.ts"; 4 | 5 | // Part of this class is a fraction of https://github.com/oakserver/oak/blob/master/tssCompare.ts 6 | /** 7 | * Contains all the util methods used by the security core 8 | */ 9 | export class SecurityUtils { 10 | public static compareArrayBuffer(a: ArrayBuffer, b: ArrayBuffer): boolean { 11 | if(!(a.byteLength === b.byteLength)) throw new Error("ArrayBuffer lengths must match."); 12 | const va = new DataView(a); 13 | const vb = new DataView(b); 14 | const length = va.byteLength; 15 | let out = 0; 16 | let i = -1; 17 | while (++i < length) { 18 | out |= va.getUint8(i) ^ vb.getUint8(i); 19 | } 20 | return out === 0; 21 | } 22 | 23 | /** Compare two strings, Uint8Arrays, ArrayBuffers, or arrays of numbers in a 24 | * way that avoids timing based attacks on the comparisons on the values. 25 | * 26 | * The function will return `true` if the values match, or `false`, if they 27 | * do not match. */ 28 | public static compare(a: string | number[] | ArrayBuffer | Uint8Array, b: string | number[] | ArrayBuffer | Uint8Array): boolean { 29 | const key = new Uint8Array(32); 30 | window.crypto.getRandomValues(key); 31 | const ah = (new HmacSha256(key)).update(a).arrayBuffer(); 32 | const bh = (new HmacSha256(key)).update(b).arrayBuffer(); 33 | return this.compareArrayBuffer(ah, bh); 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /docs/web/mandarine/mandarine-data/data-updating.md: -------------------------------------------------------------------------------- 1 | # Updating Data 2 | Updating data through the use of Mandarine-powered repositories is not hard. All it requires is to know how to save a record in your entity. [Click here for more information](http://localhost:4200/docs/mandarine/mandarine-query-language) about the `save` definer. 3 | 4 | ---- 5 | 6 | ## Updating 7 | 8 | In order to update existent data in your tables by using Mandarine-powered repositories, you will be using the same method that is used to save new information: The `save` method. 9 | 10 | `save` takes one argument which will be your model, but unlike when saving new data, this time your model's primary key cannot be null/undefined. When your model's primary key has a value, Mandarine automatically detects that the model is not meant for saving information but for updating. 11 | 12 | ## API 13 | 14 | ```typescript 15 | import { Component } from "https://deno.land/x/mandarinets@v2.3.2/mod.ts"; 16 | import { Users } from "./usersModel.ts"; 17 | import { UsersRepository } from "./usersRepository.ts"; 18 | 19 | @Component() 20 | export class MyComponent() { 21 | 22 | constructor(private readonly usersRepo: UsersRepository) { 23 | } 24 | 25 | public async updateBillsCountry() { 26 | // [0] to select the first record found 27 | let billUser: Users = await this.usersRepo.findByFirstname("Bill")[0]; 28 | // { id: 1, firstname: "Bill", lastname: "Clark", country: "Canada" } 29 | 30 | billUser.country = "Croatia"; 31 | 32 | // Updating bill's country. 33 | await this.usersRepo.save(billUser); 34 | } 35 | } 36 | 37 | ``` 38 | -------------------------------------------------------------------------------- /main-core/mandarine-native/sessions/mandarineSessionContainer.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { CommonUtils } from "../../utils/commonUtils.ts"; 4 | import { MandarineSessionHandler } from "./mandarineDefaultSessionStore.ts"; 5 | import type { MandarineSecurity } from "../../../security-core/mandarine-security.ns.ts"; 6 | 7 | export class MandarineSessionContainer implements MandarineSecurity.Sessions.SessionContainer { 8 | public cookie: MandarineSecurity.Sessions.SessionCookie; 9 | public keys: Array = new Array(); 10 | public sessionPrefix: string; 11 | public genId: Function; 12 | public resave: boolean; 13 | public rolling: boolean; 14 | public saveUninitialized: boolean; 15 | public store: MandarineSecurity.Sessions.SessionStore; 16 | 17 | constructor() { 18 | this.cookie = { 19 | path: '/', 20 | httpOnly: false, 21 | secure: false, 22 | maxAge: null 23 | }; 24 | 25 | this.keys = ["mandarine", "orange", "apple", "beer"]; 26 | 27 | this.sessionPrefix = "mandarine-session"; 28 | 29 | this.genId = CommonUtils.generateUUID; 30 | 31 | this.resave = false; 32 | 33 | this.rolling = false; 34 | 35 | this.saveUninitialized = false; 36 | 37 | this.store = new MandarineSessionHandler(); 38 | } 39 | 40 | public set(setData: any): MandarineSessionContainer { 41 | Object.keys(setData).forEach((item: any) => { 42 | (this)[item] = setData[item]; 43 | }); 44 | return this; 45 | } 46 | } -------------------------------------------------------------------------------- /tests/integration-tests/files/routeParam.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { MandarineCore } from "../../../main-core/mandarineCore.ts"; 4 | import { Controller } from "../../../mvc-framework/core/decorators/stereotypes/controller/controller.ts"; 5 | import { Parameters, RouteParam } from "../../../mvc-framework/core/decorators/stereotypes/controller/parameterDecorator.ts"; 6 | import { GET } from "../../../mvc-framework/core/decorators/stereotypes/controller/routingDecorator.ts"; 7 | 8 | @Controller() 9 | class RouteParamControllerTest { 10 | 11 | @GET('/say-hi-1/:name') 12 | public helloWorld(@RouteParam('name') personsName: string): object { 13 | return { 14 | name: personsName 15 | }; 16 | } 17 | 18 | } 19 | 20 | @Controller('/api') 21 | class RouteParamControllerWithBaseTest { 22 | 23 | @GET('/say-hi-2/:personsName') 24 | public helloWorld2(@RouteParam() personsName: string): object { 25 | return { 26 | name: personsName 27 | }; 28 | } 29 | 30 | } 31 | 32 | @Controller() 33 | class RouteParamControllerTestWithUnknownRoute { 34 | 35 | @GET('/say-hi-3/:personsName') 36 | public helloWorld2(@RouteParam() whateverRoute: string): object { 37 | return { 38 | name: (whateverRoute) ? whateverRoute : "undefined" 39 | }; 40 | } 41 | 42 | } 43 | 44 | @Controller() 45 | class ControllerGetAllParameters { 46 | 47 | @GET('/parameters/:name/:lastname') 48 | public helloWorld2(@Parameters() allParameters: any) { 49 | return allParameters; 50 | } 51 | 52 | } 53 | 54 | new MandarineCore().MVC().run({ port: 8081 }); -------------------------------------------------------------------------------- /main-core/mandarine-native/sessions/mandarineSessionHandlers.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Timers } from "../../internals/core/timers.ts"; 4 | import { Mandarine } from "../../Mandarine.ns.ts"; 5 | 6 | class MandarineSessionHandler { 7 | 8 | private expiredSessionHandler: any = undefined; 9 | 10 | public initializeExpirationHandler() { 11 | const sessionContainer = Mandarine.Global.getSessionContainer().store; 12 | if(sessionContainer) { 13 | const expirationInterval = sessionContainer.getExpirationInterval(); 14 | if(sessionContainer.getAutoclearExpiredSessions() && this.expiredSessionHandler === undefined && expirationInterval > 0) { 15 | this.expiredSessionHandler = Mandarine.MandarineCore.Internals.getTimersManager().add(Timers.MANDARINE_EXPIRED_SESSIONS, 16 | "Interval", 17 | () => sessionContainer.clearExpiredSessions(), 18 | expirationInterval); 19 | } 20 | } 21 | } 22 | 23 | public stopExpirationHandler() { 24 | Mandarine.MandarineCore.Internals.getTimersManager().delete(this.expiredSessionHandler); 25 | this.expiredSessionHandler = undefined; 26 | } 27 | 28 | public initializeSessionManager() { 29 | const sessionContainerStore = Mandarine.Global.getSessionContainer().store; 30 | if(sessionContainerStore && sessionContainerStore.launch) { 31 | sessionContainerStore.launch(); 32 | this.initializeExpirationHandler(); 33 | } 34 | } 35 | 36 | } 37 | 38 | export const sessionTimerHandlers = new MandarineSessionHandler(); -------------------------------------------------------------------------------- /tests/copyright.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { expandGlobSync } from "https://deno.land/std@0.84.0/fs/mod.ts"; 4 | import { readLines } from "https://deno.land/std@0.84.0/io/mod.ts"; 5 | import { MandarineConstants } from "../main-core/mandarineConstants.ts"; 6 | import { CommonUtils } from "../main-core/utils/commonUtils.ts"; 7 | import { Test } from "./mod.ts"; 8 | 9 | export class CopyrightTest { 10 | 11 | @Test({ 12 | name: "Check copyright", 13 | description: "Verify all files have the copyright header" 14 | }) 15 | public async checkCopyright() { 16 | 17 | const files: Array = new Array(); 18 | 19 | for(const pattern of MandarineConstants.MANDARINE_FILE_GLOBS) { 20 | for (const file of expandGlobSync(pattern)) { 21 | files.push(file.path); 22 | } 23 | } 24 | 25 | for(let i = 0; i { 22 | constructor() { 23 | super(MyModel); 24 | } 25 | } 26 | ``` 27 | */ 28 | export abstract class MandarineRepository { 29 | 30 | private modeler: RepositoryModeler; 31 | 32 | constructor(TCreator: { new (): T; }) { 33 | this.modeler = { 34 | instance: TCreator, 35 | object: new TCreator(), 36 | entity: undefined 37 | }; 38 | 39 | this.modeler.entity = ApplicationContext.getInstance().getEntityManager().entityRegistry.findEntityByInstanceType(this.modeler.instance); 40 | } 41 | 42 | public getModeler(): RepositoryModeler { 43 | return this.modeler; 44 | } 45 | 46 | public save(model: Partial) {} 47 | public findAll() {} 48 | public deleteAll() {} 49 | public countAll() {} 50 | 51 | } -------------------------------------------------------------------------------- /docs/web/mandarine/mandarine-security/authentication-managerbuilder.md: -------------------------------------------------------------------------------- 1 | # Authentication Manager Builder 2 | 3 | ------ 4 | 5 | ## Overview 6 | The authentication manager builder is a module part of [`WebMVCConfigurer`](/docs/master/mandarine/native-components-list) which allows adding an `UserDetailsService` to the authentication container which holds all the logic behind Mandarine's built-in authentication. 7 | The authentication manager builder also allows overriding the default implementation of the password encoder (By default, Mandarine makes use of [BCrypt](https://en.wikipedia.org/wiki/Bcrypt), although, this implementation can be overriden). 8 | 9 | ## `Mandarine.Security.Auth.AuthenticationManagerBuilder` interface 10 | ```typescript 11 | export interface AuthenticationManagerBuilder { 12 | userDetailsService: (implementation: any) => AuthenticationManagerBuilder; 13 | passwordEncoder: (implementation: Crypto.PasswordEncoder) => AuthenticationManagerBuilder; 14 | } 15 | ``` 16 | - `userDetailsService`: 17 | - Passes the reference to the Mandarine-powered component that implements `Mandarine.Types.UserDetailsService` 18 | - `passwordEncoder`: **Method** 19 | - Passes an instance that implements `Mandarine.Security.Crypto.PasswordEncoder`. 20 | - Default is _BCrypt_ 21 | 22 | ## Basic Usage 23 | 24 | ```typescript 25 | import { Override, Mandarine } from "https://deno.land/x/mandarinets@v2.3.2/mod.ts"; 26 | 27 | @Override() 28 | export class WebMvcConfigurer extends Mandarine.Native.WebMvcConfigurer { 29 | public authManagerBuilder(provider: Mandarine.Security.Auth.AuthenticationManagerBuilder) { 30 | provider = provider.userDetailsService(UserDetailsService); 31 | return provider; 32 | } 33 | } 34 | ``` 35 | -------------------------------------------------------------------------------- /tests/integration-tests/websocket_test.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { CommonUtils } from "../../main-core/utils/commonUtils.ts"; 4 | import { DenoAsserts, INTEGRATION_TEST_FILES_TO_RUN_DIRECTORY, Orange, Test } from "../mod.ts"; 5 | 6 | export class WebSocketTestFile { 7 | 8 | public MAX_COMPILATION_TIMEOUT_SECONDS = 50; 9 | 10 | @Test({ 11 | name: "WebSocket server", 12 | description: "Test the creation of a websocket server through Mandarine" 13 | }) 14 | public async websocketServer() { 15 | let cmd = Deno.run({ 16 | cmd: ["deno", "run", "-c", "tsconfig.json", "--allow-all", "--unstable", `${INTEGRATION_TEST_FILES_TO_RUN_DIRECTORY}/websocketServer.ts`], 17 | stdout: "piped", 18 | stderr: "inherit", 19 | stdin: "null" 20 | }); 21 | 22 | CommonUtils.sleep(this.MAX_COMPILATION_TIMEOUT_SECONDS); 23 | 24 | let cmd2 = Deno.run({ 25 | cmd: ["deno", "run", "-c", "tsconfig.json", "--allow-all", "--unstable", `${INTEGRATION_TEST_FILES_TO_RUN_DIRECTORY}/websocketClient.ts`], 26 | stdout: "null", 27 | stderr: "inherit", 28 | stdin: "null" 29 | }); 30 | 31 | CommonUtils.sleep(50); 32 | cmd2.close(); 33 | CommonUtils.sleep(10); 34 | cmd.close(); 35 | 36 | const output = new TextDecoder().decode(await cmd.output()); 37 | DenoAsserts.assertStringIncludes(output, "Hello Maria from stdout"); 38 | DenoAsserts.assertStringIncludes(output, "Hello Andres from stdout"); 39 | DenoAsserts.assertStringIncludes(output, "Hello Snejana from stdout"); 40 | 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /docs/web/mandarine/mandarine-security/authentication-userdetailsservice.md: -------------------------------------------------------------------------------- 1 | # User Details Service 2 | 3 | --------- 4 | 5 | ## Overview 6 | 7 | The User Details Service is a Mandarine-powered component (_preferably annotated with `@Service`_) which has a single responsability. This single responsability is to fetch a specific user from a collection of `Mandarine.Types.UserDetails`. 8 | 9 | An User Details Service must implement `Mandarine.Types.UserDetailsService` otherwise it will result in failure at MCT (Mandarine Compile Time). 10 | 11 | ## `Mandarine.Types.UserDetailsService` interface 12 | ```typescript 13 | export interface UserDetailsService { 14 | /** 15 | * Locates the user based on the username. 16 | * 17 | * @param username the username identifying the user whose data is required. 18 | * 19 | * @returns A user record with an implementation of UserDetails 20 | * 21 | * @throws MandarineSecurityException if no user was found. 22 | */ 23 | loadUserByUsername: (username: string) => Mandarine.Types.UserDetails; 24 | } 25 | ``` 26 | 27 | - loadUserByUsername 28 | - Fetches an user based on an username from a collection that implements `Mandarine.Types.UserDetails`. 29 | 30 |   31 | 32 | ## Basic Usage 33 | 34 | ```typescript 35 | import { Service, Mandarine } from "https://deno.land/x/mandarinets@v2.3.2/mod.ts"; 36 | 37 | @Service() 38 | export class UserDetailsServiceImplementation implements Mandarine.Security.Auth.UserDetailsService { 39 | public users: Array = new Array(); 40 | 41 | public loadUserByUsername(username: string) { 42 | return this.users.find((item: Mandarine.Types.UserDetails) => item.username === username); 43 | } 44 | } 45 | ``` -------------------------------------------------------------------------------- /mvc-framework/core/decorators/stereotypes/controller/controller.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { ApplicationContext } from "../../../../../main-core/application-context/mandarineApplicationContext.ts"; 4 | import type { Mandarine } from "../../../../../main-core/Mandarine.ns.ts"; 5 | import { ReflectUtils } from "../../../../../main-core/utils/reflectUtils.ts"; 6 | import type { ControllerComponent } from "../../../internal/components/routing/controllerContext.ts"; 7 | import { MVCDecoratorsProxy } from "../../../proxys/mvcCoreDecorators.ts"; 8 | 9 | /** 10 | * **Decorator** 11 | * 12 | * Defines that a class is a controller. 13 | * When a class is a controller, it is meant to resolve http handlers & all the internals related to mandarine HTTP server 14 | * 15 | * `@Controller(baseRoute) 16 | * Target: Class` 17 | */ 18 | export const Controller = (baseRoute?: string): Function => { 19 | return (target: any) => { 20 | let className: string = ReflectUtils.getClassName(target); 21 | let getComponentsRegistry = ApplicationContext.getInstance().getComponentsRegistry(); 22 | if(getComponentsRegistry.exist(className)) { 23 | 24 | let objectContext: Mandarine.MandarineCore.ComponentRegistryContext | undefined = getComponentsRegistry.get(className); 25 | let controllerComponent:ControllerComponent = objectContext?.componentInstance; 26 | controllerComponent?.setRoute(baseRoute); 27 | 28 | getComponentsRegistry.update(className, objectContext); 29 | } else { 30 | MVCDecoratorsProxy.registerControllerComponent(target, baseRoute); 31 | } 32 | }; 33 | }; 34 | -------------------------------------------------------------------------------- /docs/web/mandarine/mandarine-core/native-components.md: -------------------------------------------------------------------------------- 1 | # Native Components 2 | Native components are instances that Mandarine uses natively, meaning, during & after Mandarine Compile Time (MCT). Native components have several use cases as they establish internal behaviors Mandarine will have such as Resource Handlers or even the session container. 3 | 4 | ------ 5 | 6 | ## Overriding behavior 7 | Mandarine allows overriding whole or specific sections of a native class. The purpose behind this is to modify those internal behaviors Mandarine establishes as default. In order to override a native component all you need to do is to use the decorator `@Override` on top of your class and the use of `extends` to the native component you want to override (inside `Mandarine.Native`). 8 | A good example of what is described above: 9 | ```typescript 10 | import { Override, Mandarine } from "https://deno.land/x/mandarinets@v2.3.2/mod.ts"; 11 | 12 | @Override() 13 | export class WebMvcConfigurer extends Mandarine.Native.WebMvcConfigurer { 14 | .... 15 | } 16 | ``` 17 | 18 |
19 | 20 | ## `@Override` Decorator 21 | 22 | **Syntax**: 23 | ```typescript 24 | @Override(overrideType?: Mandarine.MandarineCore.NativeComponents) 25 | ``` 26 | **Target**: Class 27 | 28 | - overrideType 29 | - Used to specify what class we are trying to override. By default, it is undefined, this is because Mandarine will be capable of knowing what component you are trying to override according to the name of your class. If the name of your class is different from Mandarine's pre-defined class names, you will need to manually specify `overrideType`. 30 | 31 | ## Pre-defined class names 32 | 33 | | Class Name | Override Target | 34 | | ---------- | --------------- | 35 | | WebMvcConfigurer | Mandarine.Native.WebMvcConfigurer | -------------------------------------------------------------------------------- /docs/web/mandarine/mandarine-data/mandarine-data-introduction.md: -------------------------------------------------------------------------------- 1 | # Mandarine Data 2 | 3 | Mandarine Data is one of the four essential cores Mandarine has. Mandarine Data is responsible for creating the data layer in your application and interact directly with database clients. 4 | 5 | ## Concepts 6 | - Models / Entities 7 | - Classes that represent a table in your database, this class has columns (properties) and it will be used for your database to interact with your code and vice-versa. 8 | - Repositories 9 | - An abstract class that contains your queries as well as predefined queries such as **save**, **findAll**, **deleteAll**, **countAll** 10 | - Repositories can contain custom queries or MQL queries. 11 | - Repositories are Mandarine Components, but they are only used for database interaction purposes which means you they cannot receive injections as described here. 12 | 13 | ## Data Source 14 | In order to connect your database with your Mandarine-powered application, you need to establish the connection information through [Mandarine's properties file](/docs/mandarine/properties) 15 | 16 | **Structure:** 17 | 18 | ```typescript 19 | { 20 | "mandarine": { 21 | "dataSource": { 22 | "dialect": "postgresql", 23 | "data": { 24 | "host": "", 25 | "port": 5432, 26 | "username": "", 27 | "password": "", 28 | "database": "" 29 | } 30 | } 31 | } 32 | } 33 | ``` 34 | 35 | ----- 36 | 37 | ## Dialects 38 | 39 | [See enum here](https://doc.deno.land/https/raw.githubusercontent.com/mandarineorg/mandarinets/master/orm-core/mandarine-orm.ns.ts#MandarineORM.Dialect.Dialects) 40 | 41 | Mandarine currently supports the following dialects: 42 | - PostgreSQL 43 | -------------------------------------------------------------------------------- /tests/integration-tests/customCatch_test.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { CommonUtils } from "../../main-core/utils/commonUtils.ts"; 4 | import { DenoAsserts, Orange, Test, waitForMandarineServer } from "../mod.ts"; 5 | 6 | export class CustomCatchTest { 7 | 8 | constructor() { 9 | Orange.setOptions(this, { 10 | hooks: { 11 | beforeEach: () => CommonUtils.sleep(2) 12 | } 13 | }) 14 | } 15 | 16 | @Test({ 17 | name: "[IT01] Test Endpoints from `files/customCatch.ts`", 18 | description: "Test all endpoints in file, and verifies that a custom exception catcher is working." 19 | }) 20 | public async testCustomCatch() { 21 | let cmd = await waitForMandarineServer("customCatch.ts"); 22 | 23 | let internalServerError = (await (await fetch("http://localhost:2490/throw")).text()); 24 | let customException = (await (await fetch("http://localhost:2490/throw-2")).json()); 25 | let nothing = (await (await fetch("http://localhost:2490/do-not-throw")).text()); 26 | 27 | let errorThrown = undefined; 28 | try { 29 | DenoAsserts.assertEquals(internalServerError, "Internal Server Error"); 30 | DenoAsserts.assertEquals(customException, { 31 | error: "A error has occured", 32 | msg: "MandarineException: An error has been thrown" 33 | }); 34 | DenoAsserts.assertEquals(nothing, "Hello world"); 35 | } catch(error) { 36 | errorThrown = error; 37 | } 38 | 39 | cmd.close(); 40 | if(errorThrown != undefined) { 41 | throw errorThrown; 42 | } 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /docs/web/mandarine/mandarine-security/authentication-userdetails.md: -------------------------------------------------------------------------------- 1 | # User Details 2 | 3 | --------- 4 | 5 | ## Overview 6 | Under the hood, Mandarine makes use of `Mandarine.Types.UserDetails` interface. This interface contains the minimum of fields that Mandarine's authenticator needs in order to perform the authentication process. You can, of course, create your own implementation of `Mandarine.Types.UserDetails` by extending it which means: you may add new fields but you must keep the fields the initial interface provides. 7 | 8 | ## `Mandarine.Types.UserDetails` interface 9 | 10 | 11 | - roles: `Array | Array` 12 | - An array of string values that contains the roles present in the user, for example: `["ADMIN", "USER"]`. 13 | - If **roles** is undefined, authentication will fail. At least one value must be present. 14 | - username: `String` 15 | - Username of the present user. 16 | - If undefined or null or absent, authentication will fail. 17 | - password: `String` 18 | - Encrypted password (based on authentication's password encoder). 19 | - Used to compare incoming password against user's real password. 20 | - uid: `String | number` 21 | - Unique identifier of the user. 22 | - accountExpired: `Boolean` 23 | - Indicates whether the user's account has expired. 24 | - An expired account cannot be authenticated. 25 | - accountLocked: `Boolean` 26 | - Indicates whether the user is locked or unlocked. 27 | - A locked user cannot be authenticated. 28 | - credentialsExpired: `Boolean` 29 | - Indicates whether the user's credentials (password) has expired. 30 | - An account with expired credentials cannot be authenticated. 31 | - enabled: `Boolean` 32 | - Indicates whether the user is enabled or disabled. 33 | - A disabled user cannot be authenticated. -------------------------------------------------------------------------------- /tests/integration-tests/files/manualInjection.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Configuration } from "../../../main-core/decorators/stereotypes/configuration/configuration.ts"; 4 | import { Service } from "../../../main-core/decorators/stereotypes/service/service.ts"; 5 | import { Inject } from "../../../main-core/dependency-injection/decorators/Inject.ts"; 6 | import { Injectable } from "../../../main-core/dependency-injection/decorators/injectable.ts"; 7 | import { MandarineCore } from "../../../mod.ts"; 8 | import { Controller } from "../../../mvc-framework/core/decorators/stereotypes/controller/controller.ts"; 9 | import { GET } from "../../../mvc-framework/core/decorators/stereotypes/controller/routingDecorator.ts"; 10 | 11 | export class ManualInjectionService { 12 | 13 | public name: string; 14 | 15 | constructor(name: string) { 16 | this.name = name; 17 | } 18 | 19 | public helloWorld(): string { 20 | return `hello ${this.name}`; 21 | } 22 | 23 | } 24 | 25 | @Service() 26 | export class Service1 { 27 | 28 | @Inject() 29 | // @ts-ignore 30 | public service: ManualInjectionService; 31 | 32 | public getResult(): string { 33 | return this.service.helloWorld(); 34 | } 35 | } 36 | 37 | @Configuration() 38 | export class MainConfig { 39 | 40 | @Injectable() 41 | public ManualInjectionService() { 42 | return new ManualInjectionService("Andres"); 43 | } 44 | 45 | } 46 | 47 | @Controller() 48 | export class Controller1 { 49 | 50 | constructor(public readonly Service: Service1){} 51 | 52 | @GET('/testing-manual-injection') 53 | public test(): string { 54 | return this.Service.getResult(); 55 | } 56 | 57 | } 58 | 59 | new MandarineCore().MVC().run({port: 8082}); -------------------------------------------------------------------------------- /docs/web/mandarine/main-concepts/environmental-variables.md: -------------------------------------------------------------------------------- 1 | # Mandarine's Environmental Variables 2 | With Mandarine's environmental variables you can alter certain behaviors at Deno compile time. This may be useful for deployment services where you may need to follow certain standards. 3 | 4 | ---- 5 | 6 | # Variables 7 | 8 | - `MANDARINE_JSON_FILE` 9 | - Location of `mandarine.json` file. Useful when `mandarine.json` cannot be in the root directory. 10 | 11 | - `MANDARINE_PROPERTY_FILE` 12 | - Location of `properties.json` file. Useful when `mandarine.json` is not present or when not wanting to use `mandarine.json` to indicate the location of this file. 13 | 14 | - `MANDARINE_SERVER_HOST`: string 15 | - Specifies the host ip where Mandarine MVC should run. 16 | 17 | - `MANDARINE_SERVER_PORT`: number 18 | - Specifies the port where Mandarine MVC should run. 19 | 20 | - `MANDARINE_SERVER_RESPONSE_TIME_HEADER`: boolean 21 | - Specifies whether the `X-Response-Time` header should be enabled by default or not. 22 | 23 | - `MANDARINE_SERVER_SESSION_MIDDLEWARE`: boolean 24 | - Specifies whether the session middleware should be enabled by default or not. 25 | 26 | - `MANDARINE_STATIC_CONTENT_FOLDER`: string 27 | - Specifies the path of the folder where static content will be served if any. 28 | 29 | - `MANDARINE_AUTH_EXPIRATION_MS`: number 30 | - Amount of time an authentication session should take in order to expire (in milliseconds). 31 | 32 | - `MANDARINE_SESSIONS_TOUCH`: boolean 33 | - Whether sessions should be touched whenever they are requested. 34 | 35 | - `MANDARINE_SESSIONS_EXPIRATION_TIME`: number 36 | - Amount of time a regular session should take in order to expire. 37 | 38 | - `MANDARINE_SESSIONS_EXPIRATION_INTERVAL`: number 39 | - Amount of time for each cleaning session interval to take place. 40 | -------------------------------------------------------------------------------- /docs/style_guide.md: -------------------------------------------------------------------------------- 1 | # MandarineTS Style Guide 2 | ## Table of contents 3 | ## Copyright Headers 4 | Every file related to the functionality of Mandarine must have the following copyright header 5 | ``` 6 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 7 | ``` 8 | If the code originates elsewhere such as a third-party module or piece of code, ensure that the file has proper copyright headers. We only allow **MIT** licensed code. 9 | ## Use Camel case 10 | Example: **Use** `myFile.ts` instead of `my-file.ts` or `my_file.ts`. 11 | ## Use dashes for folders 12 | Example: **Use** `my-folder` instead of `my_folder` or `myFolder` 13 | ## Add tests for new features 14 | For testing, use [Orange testing framework](https://github.com/mandarineorg/orange). 15 | Each module should contain tests that verify all the functionalities are properly working. If a module is missing its testing files, it will be rejected. 16 | ## TODO comments 17 | TODO comments should usually include an issue or the author's github username in parentheses. Example: 18 | ``` 19 | // TODO(andreespirela): Add tests. 20 | // TODO(#123): Support Windows. 21 | // FIXME(#349): Sometimes panics. 22 | ``` 23 | ## Follow the coding standards 24 | Make sure your files are located in their proper folders, example: a file related to the MVC core **must not** be inside the main core folder. 25 | ## Be aware of the amount of LOC 26 | - Do not ever extra code something, if you are writing an utility function, make sure it exists inside Mandarine or `deno/std`. 27 | - Functionalities of module like _Decorators_ must be written in a proxy to be able to test it. 28 | # Typescript 29 | ## Interfaces & Enums 30 | Interfaces & Enums must be located under their core namespace. If you are writing an interface related to the `main-core` module, then it should be located inside `Mandarine.ns.ts` 31 | -------------------------------------------------------------------------------- /docs/web/mandarine/main-concepts/components.md: -------------------------------------------------------------------------------- 1 | # Components 2 | 3 | ---- 4 | 5 | Mandarine makes use of the concept of **Components**. A component refers to a module of a Mandarine-powered application. 6 | 7 | There are different types of components, some perform specific tasks while others are used for stereotyping your application, this essentially means: giving your application a straightforward division thus incrementing readability and sustainability. 8 | 9 | In Mandarine, everything that is desired to be _Mandarine-powered_ must be a component in order to be compatible with the different cores & features of the framework such as Dependency Injection. 10 | 11 | ## Component Types 12 | 13 | - Controller 14 | - Responsible for handling the creation of endpoints as well as the bridge with Mandarine's HTTP Dispatcher. 15 | - Middleware 16 | - Responsible for handling interceptors before & after a request made to an endpoint. 17 | - Useful for things such as validations. 18 | - Repository 19 | - Bridge between Mandarine's entity manager, your database and your application. 20 | - Mandarine's repositories are highly dependent on MQL (Mandarine Query Language). 21 | - Component 22 | - Stereotype for a Mandarine-powered class 23 | - Service 24 | - Stereotype for a Mandarine-powered class which will be used in the Application's service layer. 25 | - Configuration 26 | - Stereotype for a Mandarine-powered class to interact directly with Mandarine's core. 27 | - Catch 28 | - Stereotype for a Mandarine-powered class to catch exception during requests. 29 | - Guard 30 | - Stereotype for a Mandarine-powered class to serve as a guard for endpoints. 31 | - **Manual Component** 32 | - Serves as the bridge to bring non Mandarine-powered code into Mandarine's ecosystem. 33 | - Useful when wanting to adapt third-party libraries with Mandarine's infrastructure. 34 | 35 | -------------------------------------------------------------------------------- /docs/web/mandarine/main-concepts/value-decorator.md: -------------------------------------------------------------------------------- 1 | # `@Value` Decorator 2 | The `@Value` decorator allows you to access properties from your Mandarine's properties file in a programmatic way, this means, by using the @Value decorator, you can interact with your configuration in your components or native classes. 3 | 4 | ---- 5 | 6 | ## Syntax 7 | ```typescript 8 | @Value(propertyKey: string, scope: Mandarine.MandarineCore.ValueScopes) 9 | ``` 10 | 11 | See [Mandarine.MandarineCore.ValueScopes](https://doc.deno.land/https/raw.githubusercontent.com/mandarineorg/mandarinets/master/main-core/Mandarine.ns.ts#Mandarine.MandarineCore.ValueScopes) 12 | 13 | - propertyKey 14 | - Key reference of your property. If nested, separate by using dots (.) 15 | - scope 16 | - `CONFIGURATION`: Retrieves values from property file 17 | - `ENVIRONMENTAL`: Retrieves values from Deno environmental variables (`Deno.env`) 18 | 19 |   20 | 21 | ## Usage 22 | 23 | ```typescript 24 | // MyPropertiesFile.json 25 | { 26 | "mandarine": { 27 | "hello-message": "Bonjour!" 28 | }, 29 | "customKey": "Here goes my custom value" 30 | } 31 | ``` 32 | 33 | ```typescript 34 | import { Value } from "https://deno.land/x/mandarinets@v2.3.2/mod.ts"; 35 | 36 | export class ValueDecoratorTest { 37 | 38 | @Value('customKey') 39 | public static customKey: string; 40 | 41 | @Value('mandarine.hello-message') 42 | public bonjour: string; 43 | 44 | } 45 | 46 | console.log(ValueDecoratorTest.customKey); // Here goes my custom value 47 | console.log(new ValueDecoratorTest().bonjour); // Bonjour! 48 | ``` 49 | 50 |   51 | 52 | In the example above we can see how we are using `@Value` on top of two different class fields: one that is static, and one that is not. Although, it works for both. 53 | In the example above, we can also see how we are using nested properties with the `@Value` decorator. -------------------------------------------------------------------------------- /mvc-framework/core/utils/mandarine/routingUtils.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import type { Mandarine } from "../../../../main-core/Mandarine.ns.ts"; 4 | 5 | /** 6 | * Contains all the util methods that are related to the router and routings 7 | */ 8 | export class RoutingUtils { 9 | 10 | public static findQueryParams(url: string): URLSearchParams | undefined { 11 | if (url == undefined) return undefined; 12 | const searchs = url.split('?')[1]; 13 | if (searchs == undefined) return undefined; 14 | return new URLSearchParams(searchs); 15 | } 16 | 17 | public static findRouteParams(url: string): Mandarine.MandarineMVC.Routing.RoutingParams[] | null { 18 | if(url == null) return null; 19 | return url.split('/').reduce((acc: Mandarine.MandarineMVC.Routing.RoutingParams[], el, i) => { 20 | if (/:[A-Za-z1-9]{1,}/.test(el)) { 21 | const result: string = el.replace(':', ''); 22 | acc.push({ routeIndex: i, routeName: result}); 23 | } 24 | return acc; 25 | }, []); 26 | } 27 | 28 | public static findRouteParamSignature(route: string, method: Mandarine.MandarineMVC.HttpMethods): Array | null { 29 | if(route == null) return null; 30 | return route.split('/').reduce((acc: string[], el: string, i: any) => { 31 | if(acc.length === 0) acc.push(`${method}`); 32 | if (/:[A-Za-z1-9]{1,}/.test(el)) { 33 | acc.push(":param"); 34 | } else if(el && el != "") { 35 | acc.push(el) 36 | } 37 | return acc; 38 | }, []); 39 | } 40 | 41 | public static getRouteParamPattern(route: string): RegExp { 42 | return new RegExp(route.replace(/:[^\s/]+/g, '([\\w-]+)')); 43 | } 44 | } -------------------------------------------------------------------------------- /docs/web/mandarine/main-concepts/fundamentals.md: -------------------------------------------------------------------------------- 1 | # Fundamentals 2 | 3 | Across this documentation & Mandarine's development community, there are some core concepts that may be mentioned repeatedly. 4 | 5 | This set of articles will introduce some of the main concepts Mandarine uses which will give you a better understanding of the framework. 6 | 7 | -------------- 8 | 9 | ## Concepts 10 | 11 | - Mandarine Compile Time (MCT) 12 | - Period of time when Mandarine is initializing its different cores. 13 | - During _MCT_, Mandarine is responsible for resolving the application context, dependency injection, entity manager and template registry. 14 | - Mandarine Request Time (MRT) 15 | - Period of time when _Mandarine MVC_ receives a request and resolves it 16 | - Lifecycle 17 | - The different stages a framework takes in order to fully initialize 18 | - HTTP Dispatcher 19 | - Framework to manage incoming HTTP requests. 20 | - Application Context 21 | - Singleton class which serves as a bridge between Mandarine's cores & your application. 22 | - Dependency Injection (DI) 23 | - Design pattern where all the objects a class depends on are not managed by the developer but the framework. 24 | - Dependency Injection Container (DI Container) 25 | - Registry where all dependencies are saved as a singleton and then distributed across the application 26 | - Dependency Injection Factory (DI Factory) 27 | - Module responsible for connection all the dependencies and resolving them at Mandarine Compile Time (MCT) 28 | - Entity Manager 29 | - Registry & Bridge for database data. 30 | - Template Registry 31 | - Registry of all the templates to be used among endpoints. 32 | - MQL 33 | - Mandarine Query Language 34 | - Proxy Method 35 | - A proxy method is a hidden processor for one or more of your methods which essentially makes the execution of a method redirect to another method. 36 | -------------------------------------------------------------------------------- /docs/web/mandarine/mandarine-mvc/entry-point.md: -------------------------------------------------------------------------------- 1 | # Entry Point 2 | The entry point file is a file that contains the references for the different modules of your application. 3 | 4 | ----------- 5 | 6 | ## Overview 7 | The single entry point file serves as an index for all your Mandarine-powered components as well as modules that are not written in Mandarine but are required in certain sections of your Mandarine-powered application. 8 | 9 | ## Usage 10 | ```typescript 11 | import { MandarineCore } from "https://deno.land/x/mandarinets@v2.3.2/mod.ts"; 12 | 13 | const services = [/* References */]; 14 | const controllers = [/* References */]; 15 | const middleware = [/* References */]; 16 | const repositories = [/* References */]; 17 | const configurations = [/* References */]; 18 | const components = [/* References */]; 19 | const otherModules = [/* References */]; 20 | 21 | new MandarineCore().MVC().run(); 22 | ``` 23 | In the above example, we can see how we have different arrays where we will reference our modules. These arrays have no effect on the code whatsoever, their only purpose is to bring together all your modules into a single file in order for Deno to pick these and make everything work out as a whole. 24 | 25 |   26 | 27 | A good real example would look as follows: 28 | ```typescript 29 | import { MandarineCore } from "https://deno.land/x/mandarinets@v2.3.2/mod.ts"; 30 | import { Controller1, Controller2, Controller3 } from "./controllers-exports.ts"; 31 | 32 | const services = [/* References */]; 33 | const controllers = [Controller1, Controller2, Controller3]; 34 | const middleware = [/* References */]; 35 | const repositories = [/* References */]; 36 | const configurations = [/* References */]; 37 | const components = [/* References */]; 38 | const otherModules = [/* References */]; 39 | ``` 40 | There is no need to initialize these classes, you **must** only reference them (type referencing) as shown above. -------------------------------------------------------------------------------- /main-core/components/component-component/componentComponent.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import type { Mandarine } from "../../Mandarine.ns.ts"; 4 | 5 | /** 6 | * This class is used in the DI Container for Mandarine to store components annotated as @Component 7 | */ 8 | export class ComponentComponent implements Mandarine.MandarineCore.ComponentCommonInterface { 9 | 10 | public name?: string; 11 | public classHandler: any; 12 | public classHandlerPrimitive: any; 13 | public type?: Mandarine.MandarineCore.ComponentTypes; 14 | public configuration?: any; 15 | private internals: { [prop: string]: any } = {}; 16 | 17 | constructor(name: string, classHandler: any, type: Mandarine.MandarineCore.ComponentTypes, configuration?: any) { 18 | this.name = name; 19 | this.classHandler = classHandler; 20 | this.type = type; 21 | this.configuration = configuration; 22 | 23 | if(!this.classHandlerPrimitive) { 24 | this.classHandlerPrimitive = classHandler; 25 | } 26 | } 27 | 28 | public getName() { 29 | return this.name || ""; 30 | } 31 | 32 | public getClassHandler() { 33 | return this.classHandler; 34 | } 35 | 36 | public getClassHandlerPrimitive() { 37 | return this.classHandlerPrimitive; 38 | } 39 | 40 | public setClassHandler(handler: any) { 41 | this.classHandler = handler; 42 | } 43 | 44 | public addInternal(key: string, value: any) { 45 | this.internals[key] = value; 46 | } 47 | 48 | public getInternal(key: string): T { 49 | return this.internals[key]; 50 | } 51 | 52 | public deleteInternal(key: string): void { 53 | delete this.internals[key]; 54 | } 55 | 56 | public internalExists(key: string): boolean { 57 | return this.getInternal(key) !== undefined; 58 | } 59 | } -------------------------------------------------------------------------------- /orm-core/core/decorators/entityDecorators.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import type { Mandarine } from "../../../main-core/Mandarine.ns.ts"; 4 | import { ORMCoreDecoratorProxy } from "../proxys/ormCoreDecoratorProxy.ts"; 5 | 6 | /** 7 | * **Decorator** 8 | * 9 | * Defines that a class is an entity in Mandarine's ORM 10 | * 11 | * `@Table(decoratorOptions) 12 | * Target: class` 13 | */ 14 | export const Table = (decoratorOptions?: Mandarine.ORM.Entity.Decorators.Table): Function => { 15 | return (target: any) => { 16 | ORMCoreDecoratorProxy.registerTableDecorator(target, decoratorOptions); 17 | } 18 | } 19 | 20 | /** 21 | * **Decorator** 22 | * 23 | * Defines that a property is a column in an entity. 24 | * 25 | * `@Column(decoratorOptions) 26 | * Target: property` 27 | */ 28 | export const Column = (decoratorOptions?: Mandarine.ORM.Entity.Decorators.Column): Function => { 29 | return (target: any, propertyKey: string) => { 30 | ORMCoreDecoratorProxy.registerColumnDecorator(target, decoratorOptions, propertyKey); 31 | } 32 | } 33 | 34 | /** 35 | * **Decorator** 36 | * 37 | * Defines that a column is a primary key 38 | * 39 | * `@Id() 40 | * Target: property` 41 | */ 42 | export const Id = () => { 43 | return (target: any, propertyKey: string) => { 44 | ORMCoreDecoratorProxy.registerIdDecorator(target, propertyKey); 45 | } 46 | } 47 | 48 | /** 49 | * **Decorator** 50 | * 51 | * Defines the generation strategy for a primary key 52 | * 53 | * `@GeneratedValue() 54 | * Target: property` 55 | */ 56 | export const GeneratedValue = (decoratorOptions: Mandarine.ORM.Entity.Decorators.GeneratedValue) => { 57 | return (target: any, propertyKey: string) => { 58 | ORMCoreDecoratorProxy.registerGeneratedValueDecorator(target, decoratorOptions, propertyKey); 59 | } 60 | } -------------------------------------------------------------------------------- /docs/web/mandarine/faq.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | Frequently asked questions 3 | 4 | ------ 5 | 6 | ## Where can I find legacy documentation? 7 | Legacy documentation can be found [here](https://mandarineframework.gitbook.io/mandarine-ts/) 8 | 9 |   10 | 11 | ## How can I contribute? 12 | See our [contributing guidelines](https://github.com/mandarineorg/mandarinets/blob/master/docs/contributing.md) & our [style guide](https://github.com/mandarineorg/mandarinets/blob/master/docs/style_guide.md) 13 | 14 |   15 | 16 | ## Why do I need a `tsconfig.json`? 17 | Mandarine relies on some typescript configuration properties that are not part of Deno's default values. 18 | 19 | ## When do I need a `tsconfig.json`? 20 | To be able to run any deno command like `deno run` or `deno test` you will need 21 | to provide a `tsconfig.json` file using the `--config` flag. (e.g. `deno test 22 | --config tsconfig.json`). [Click here](https://www.mandarinets.org/docs/master/mandarine/main-configuration#typescript-configuration) to find Mandarine's tsconfig.json. 23 | 24 |   25 | 26 | ## I am getting a lot of compiling errors 27 | This is probably because your project does not have a proper `tsconfig.json` file. [Click here](https://www.mandarinets.org/docs/master/mandarine/main-configuration#typescript-configuration) to find Mandarine's tsconfig.json. 28 | 29 |   30 | 31 | ## When does Mandarine Compile Time (MCT) start? 32 | Mandarine Compiling process starts at the time your application includes any component from Mandarine's repository. Decorators, Mandarine's native classes & any other mandarine reference invokes Mandarine's compiler. 33 | 34 |   35 | 36 | ## Nest.land throws error when importing, what should I do? 37 | Even though we recognize [nest.land](https://nest.land) as our main import source. We also strongly recommend to use [Deno X](https://deno.land/x) if nest.land _fails_.
The import of _deno.land/x_ is `https://deno.land/x/mandarinets@v2.3.2/mod.ts`. 38 | -------------------------------------------------------------------------------- /main-core/timer-registry/timerRegistry.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2020 The Mandarine.TS Framework authors. All rights reserved. MIT license. 2 | 3 | import { Timers } from "../internals/core/timers.ts"; 4 | import { Mandarine } from "../Mandarine.ns.ts"; 5 | import { CommonUtils } from "../utils/commonUtils.ts"; 6 | import { ClassType } from "../utils/utilTypes.ts"; 7 | 8 | export class TimerRegistry { 9 | 10 | private timers: Array = new Array(); 11 | 12 | public create(target: ClassType, methodName: string, fixedRate: number, handler: () => void) { 13 | this.timers.push({ 14 | handlerType: target, 15 | fixedRate: fixedRate, 16 | methodName: methodName, 17 | interval: Mandarine.MandarineCore.Internals.getTimersManager().add(Timers.MANDARINE_TIMER_REGISTRY.replace("{0}", CommonUtils.generateUUID()), "Interval", handler, fixedRate) 18 | }); 19 | } 20 | 21 | public get(target: ClassType, methodName: string) { 22 | return this.timers.find((item) => item.methodName === methodName && item.handlerType === target); 23 | } 24 | 25 | private disableFromCore(timerId: number) { 26 | Mandarine.MandarineCore.Internals.getTimersManager().delete(timerId); 27 | } 28 | 29 | public disable(target: ClassType, methodName: string) { 30 | const timer = this.get(target, methodName); 31 | if(timer) { 32 | this.disableFromCore(timer.interval); 33 | } 34 | } 35 | 36 | public delete(target: ClassType, methodName: string) { 37 | this.disable(target, methodName); 38 | this.timers = this.timers.filter((item) => item.handlerType !== target && item.methodName !== methodName); 39 | } 40 | 41 | public deleteAll() { 42 | this.timers.forEach((item) => this.disableFromCore(item.interval)); 43 | this.timers = []; 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /docs/web/mandarine/plugins/promise-repeater.md: -------------------------------------------------------------------------------- 1 | # Promise Repeater 2 | The promise repeater plugin allows you to "try" and repeat promises until they are resolved. It also allows you establish a margin of milliseconds between each try. 3 | 4 | ---- 5 | 6 | ## Methods 7 | 8 | | Method | Description | 9 | | ------ | ----------- | 10 | | maxAttempts | Sets limit of attempts for retrying promise 11 | | unlimitedAttempts | Sets a infinite limit of attempts 12 | | delay | Sets a delay (in milliseconds) for each retry 13 | | start / attempt | Starts the process of resolving the promise and retrying until it is resolved 14 | 15 | 16 | ## API 17 | 18 | ```typescript 19 | import { PromiseRepeater } from "https://deno.land/x/mandarinets@v2.3.2/mod.ts"; 20 | 21 | let i: number = 0; 22 | const promiseFunction: Function = async () => { 23 | i++; 24 | console.log(`Attempt ${i}`); 25 | if(i < 3) throw new Error(); 26 | console.log("Resolved"); 27 | return true; 28 | } 29 | 30 | const val1: any = await new PromiseRepeater(promiseFunction).maxAttempts(4).start(); 31 | i = 0; 32 | const val2: any = await new PromiseRepeater(promiseFunction).maxAttempts(4).delay(2000).start(); 33 | i = 0; 34 | const val3: any = await new PromiseRepeater(promiseFunction).unlimitedAttempts().delay(1000).start(); 35 | i = 0; 36 | const val4: any = await new PromiseRepeater(promiseFunction).maxAttempts(1).start(); 37 | 38 | console.log(val1); 39 | console.log(val2); 40 | console.log(val3); 41 | console.log(val4); 42 | 43 | ``` 44 | 45 | Result: 46 | 47 | ```text 48 | 49 | Attempt 1 50 | Attempt 2 51 | Attempt 3 52 | Resolved 53 | 54 | Attempt 1 // Second #2 55 | Attempt 2 // Second #4 56 | Attempt 3 // Second #6 57 | Resolved 58 | 59 | Attempt 1 60 | Attempt 2 61 | Attempt 3 62 | Resolved 63 | 64 | Attempt 1 65 | // Throw the error on method `promiseFunction` since it ran out of attemps and it was never resolved. 66 | error: Uncaught Error: 67 | if(i < 3) throw new Error(); 68 | ``` 69 | --------------------------------------------------------------------------------