Code Monkey home page Code Monkey logo

inversify-express-utils's Introduction

inversify-express-utils

Join the chat at https://gitter.im/inversify/InversifyJS Build Status Test Coverage npm version Dependencies img img Known Vulnerabilities

NPM NPM

Some utilities for the development of express applications with Inversify.

Installation

You can install inversify-express-utils using npm:

npm install inversify inversify-express-utils reflect-metadata --save

The inversify-express-utils type definitions are included in the npm module and require TypeScript 2.0. Please refer to the InversifyJS documentation to learn more about the installation process.

The Basics

Step 1: Decorate your controllers

To use a class as a "controller" for your express app, simply add the @controller decorator to the class. Similarly, decorate methods of the class to serve as request handlers.

The following example will declare a controller that responds to `GET /foo'.

import * as express from "express";
import { interfaces, controller, httpGet, httpPost, httpDelete, request, queryParam, response, requestParam } from "inversify-express-utils";
import { injectable, inject } from "inversify";

@controller("/foo")
export class FooController implements interfaces.Controller {

    constructor( @inject("FooService") private fooService: FooService ) {}

    @httpGet("/")
    private index(@request() req: express.Request, @response() res: express.Response, @next() next: express.NextFunction): string {
        return this.fooService.get(req.query.id);
    }

    @httpGet("/")
    private list(@queryParam("start") start: number, @queryParam("count") count: number): string {
        return this.fooService.get(start, count);
    }

    @httpPost("/")
    private async create(@request() req: express.Request, @response() res: express.Response) {
        try {
            await this.fooService.create(req.body);
            res.sendStatus(201);
        } catch (err) {
            res.status(400).json({ error: err.message });
        }
    }

    @httpDelete("/:id")
    private delete(@requestParam("id") id: string, @response() res: express.Response): Promise<void> {
        return this.fooService.delete(id)
            .then(() => res.sendStatus(204))
            .catch((err: Error) => {
                res.status(400).json({ error: err.message });
            });
    }
}

Step 2: Configure container and server

Configure the inversify container in your composition root as usual.

Then, pass the container to the InversifyExpressServer constructor. This will allow it to register all controllers and their dependencies from your container and attach them to the express app. Then just call server.build() to prepare your app.

In order for the InversifyExpressServer to find your controllers, you must bind them to the TYPE.Controller service identifier and tag the binding with the controller's name. The Controller interface exported by inversify-express-utils is empty and solely for convenience, so feel free to implement your own if you want.

import * as bodyParser from 'body-parser';

import { Container } from 'inversify';
import { interfaces, InversifyExpressServer, TYPE } from 'inversify-express-utils';

// declare metadata by @controller annotation
import "./controllers/foo_controller";

// set up container
let container = new Container();

// set up bindings
container.bind<FooService>('FooService').to(FooService);

// create server
let server = new InversifyExpressServer(container);
server.setConfig((app) => {
  // add body parser
  app.use(bodyParser.urlencoded({
    extended: true
  }));
  app.use(bodyParser.json());
});

let app = server.build();
app.listen(3000);

Important information about the @controller decorator

Since the [email protected] release. The @injectable annotation is no longer required in classes annotated with @controller. Declaring a type binding for controllers is also no longer required in classes annotated with @controller.

⚠️ Declaring a binding is not required for Controllers but it is required to import the controller one unique time. When the controller file is imported (e.g. import "./controllers/some_controller") the class is declared and the metadata is generated. If you don't import it the metadata is never generated and therefore the controller is not found. An example of this can be found here.

If you run the application multiple times within a shared runtime process (e.g. unit testing) you might need to clean up the existing metadata before each test.

import { cleanUpMetadata } from "inversify-express-utils";

describe("Some Component", () => {

    beforeEach(() => {
        cleanUpMetadata();
    });

    it("Some test case", () => {
        // ...
    });

});

You can find an example of this in our unit tests.

Inversify express utils will throw an exception if your application doesn't have controllers. You can disable this behaviour using the forceControllers option. You can find some examples of forceControllers in our unit tests.

InversifyExpressServer

A wrapper for an express Application.

.setConfig(configFn)

Optional - exposes the express application object for convenient loading of server-level middleware.

import * as morgan from 'morgan';
// ...
let server = new InversifyExpressServer(container);

server.setConfig((app) => {
    var logger = morgan('combined')
    app.use(logger);
});

.setErrorConfig(errorConfigFn)

Optional - like .setConfig(), except this function is applied after registering all app middleware and controller routes.

let server = new InversifyExpressServer(container);
server.setErrorConfig((app) => {
    app.use((err, req, res, next) => {
        console.error(err.stack);
        res.status(500).send('Something broke!');
    });
});

.build()

Attaches all registered controllers and middleware to the express application. Returns the application instance.

// ...
let server = new InversifyExpressServer(container);
server
    .setConfig(configFn)
    .setErrorConfig(errorConfigFn)
    .build()
    .listen(3000, 'localhost', callback);

Using a custom Router

It is possible to pass a custom Router instance to InversifyExpressServer:

let container = new Container();

let router = express.Router({
    caseSensitive: false,
    mergeParams: false,
    strict: false
});

let server = new InversifyExpressServer(container, router);

By default server will serve the API at / path, but sometimes you might need to use different root namespace, for example all routes should start with /api/v1. It is possible to pass this setting via routing configuration to InversifyExpressServer

let container = new Container();

let server = new InversifyExpressServer(container, null, { rootPath: "/api/v1" });

Using a custom express application

It is possible to pass a custom express.Application instance to InversifyExpressServer:

let container = new Container();

let app = express();
//Do stuff with app

let server = new InversifyExpressServer(container, null, null, app);

Decorators

@controller(path, [middleware, ...])

Registers the decorated class as a controller with a root path, and optionally registers any global middleware for this controller.

@httpMethod(method, path, [middleware, ...])

Registers the decorated controller method as a request handler for a particular path and method, where the method name is a valid express routing method.

@SHORTCUT(path, [middleware, ...])

Shortcut decorators which are simply wrappers for @httpMethod. Right now these include @httpGet, @httpPost, @httpPut, @httpPatch, @httpHead, @httpDelete, @httpOptions, and @All. For anything more obscure, use @httpMethod (Or make a PR πŸ˜„).

@request()

Binds a method parameter to the request object.

@response()

Binds a method parameter to the response object.

@requestParam(name: string)

Binds a method parameter to request.params object or to a specific parameter if a name is passed.

@queryParam(name: string)

Binds a method parameter to request.query or to a specific query parameter if a name is passed.

@requestBody()

Binds a method parameter to the request.body. If the bodyParser middleware is not used on the express app, this will bind the method parameter to the express request object.

@requestHeaders(name: string)

Binds a method parameter to the request headers.

@cookies(name: string)

Binds a method parameter to the request cookies.

@next()

Binds a method parameter to the next() function.

@principal()

Binds a method parameter to the user principal obtained from the AuthProvider.

BaseHttpController

The BaseHttpController is a base class that provides a significant amount of helper functions in order to aid writing testable controllers. When returning a response from a method defined on one of these controllers, you may use the response object available on the httpContext property described in the next section, or you may return an HttpResponseMessage, or you may return an object that implements the IHttpActionResult interface.

The benefit of the latter two methods is that since your controller is no longer directly coupled to requiring an httpContext to send a response, unit testing controllers becomes extraordinarily simple as you no longer need to mock the entire response object, you can simply run assertions on the returned value. This API also allows us to make future improvements in this area and add in functionality that exists in similar frameworks (.NET WebAPI) such as media formatters, content negotation, etc.

import { injectable, inject } from "inversify";
import {
    controller, httpGet, BaseHttpController, HttpResponseMessage, StringContent
} from "inversify-express-utils";

@controller("/")
class ExampleController extends BaseHttpController {
    @httpGet("/")
    public async get() {
        const response = new HttpResponseMessage(200);
        response.content = new StringContent("foo");
        return response;
    }

On the BaseHttpController, we provide a litany of helper methods to ease returning common IHttpActionResults including

  • OkResult
  • OkNegotiatedContentResult
  • RedirectResult
  • ResponseMessageResult
  • StatusCodeResult
  • BadRequestErrorMessageResult
  • BadRequestResult
  • ConflictResult
  • CreatedNegotiatedContentResult
  • ExceptionResult
  • InternalServerError
  • NotFoundResult
  • JsonResult
  • StreamResult
import { injectable, inject } from "inversify";
import {
    controller, httpGet, BaseHttpController
} from "inversify-express-utils";

@controller("/")
class ExampleController extends BaseHttpController {
    @httpGet("/")
    public async get() {
        return this.ok("foo");
    }

JsonResult

In some scenarios, you'll want to set the status code of the response. This can be done by using the json helper method provided by BaseHttpController.

import {
    controller, httpGet, BaseHttpController
} from "inversify-express-utils";

@controller("/")
export class ExampleController extends BaseHttpController {
    @httpGet("/")
    public async get() {
        const content = { foo: "bar" };
        const statusCode = 403;

        return this.json(content, statusCode);
    }
}

This gives you the flexability to create your own responses while keeping unit testing simple.

import { expect } from "chai";

import { ExampleController } from "./example-controller";
import { results } from "inversify-express-utils";

describe("ExampleController", () => {
    let controller: ExampleController;

    beforeEach(() => {
        controller = new ExampleController();
    });

    describe("#get", () => {
        it("should have a status code of 403", async () => {
            const response = await controller.get();

            expect(response).to.be.an.instanceof(results.JsonResult);
            expect(response.statusCode).to.equal(403);
        });
    });
});

This example uses Mocha and Chai as a unit testing framework

StreamResult

In some cases, you'll want to proxy data stream from remote resource in response. This can be done by using the stream helper method provided by BaseHttpController. Useful in cases when you need to return large data.

import { inject } from "inversify";
import {
    controller, httpGet, BaseHttpController
} from "inversify-express-utils";
import TYPES from "../constants";
import { FileServiceInterface } from "../interfaces";

@controller("/cats")
export class CatController extends BaseHttpController {
    @inject(TYPES.FileService) private fileService: FileServiceInterface;

    @httpGet("/image")
    public async getCatImage() {
        const readableStream = this.fileService.getFileStream("cat.jpeg");

        return this.stream(content, "image/jpeg", 200);
    }
}

HttpContext

The HttpContext property allow us to access the current request, response and user with ease. HttpContext is available as a property in controllers derived from BaseHttpController.

import { injectable, inject } from "inversify";
import {
    controller, httpGet, BaseHttpController
} from "inversify-express-utils";

@controller("/")
class UserPreferencesController extends BaseHttpController {

    @inject("AuthService") private readonly _authService: AuthService;

    @httpGet("/")
    public async get() {
        const token = this.httpContext.request.headers["x-auth-token"];
        return await this._authService.getUserPreferences(token);
    }
}

If you are creating a custom controller you will need to inject HttpContext manually using the @injectHttpContext decorator:

import { injectable, inject } from "inversify";
import {
    controller, httpGet, BaseHttpController, httpContext, interfaces
} from "inversify-express-utils";

const authService = inject("AuthService")

@controller("/")
class UserPreferencesController {

    @injectHttpContext private readonly _httpContext: interfaces.HttpContext;
    @authService private readonly _authService: AuthService;

    @httpGet("/")
    public async get() {
        const token = this.httpContext.request.headers["x-auth-token"];
        return await this._authService.getUserPreferences(token);
    }
}

AuthProvider

The HttpContext will not have access to the current user if you don't create a custom AuthProvider implementation:

const server = new InversifyExpressServer(
    container, null, null, null, CustomAuthProvider
);

We need to implement the AuthProvider interface.

The AuthProvider allow us to get a user (Principal):

import { injectable, inject } from "inversify";
import { interfaces } from "inversify-express-utils";

const authService = inject("AuthService");

@injectable()
class CustomAuthProvider implements interfaces.AuthProvider {

    @authService private readonly _authService: AuthService;

    public async getUser(
        req: express.Request,
        res: express.Response,
        next: express.NextFunction
    ): Promise<interfaces.Principal> {
        const token = req.headers["x-auth-token"]
        const user = await this._authService.getUser(token);
        const principal = new Principal(user);
        return principal;
    }

}

We also need to implement the Principal interface. The Principal interface allow us to:

  • Access the details of a user
  • Check if it has access to certain resource
  • Check if it is authenticated
  • Check if it is in a user role
class Principal implements interfaces.Principal<T = unknown> {
    public details: T;
    public constructor(details: T) {
        this.details = details;
    }
    public isAuthenticated(): Promise<boolean> {
        return Promise.resolve(true);
    }
    public isResourceOwner(resourceId: unknown): Promise<boolean> {
        return Promise.resolve(resourceId === 1111);
    }
    public isInRole(role: string): Promise<boolean> {
        return Promise.resolve(role === "admin");
    }
}

We can then access the current user (Principal) via the HttpContext:

@controller("/")
class UserDetailsController extends BaseHttpController {

    @inject("AuthService") private readonly _authService: AuthService;

    @httpGet("/")
    public async getUserDetails() {
        if (this.httpContext.user.isAuthenticated()) {
            return this._authService.getUserDetails(this.httpContext.user.details.id);
        } else {
            throw new Error();
        }
    }
}

BaseMiddleware

Extending BaseMiddleware allow us to inject dependencies and to access the current HttpContext in Express middleware function.

import { BaseMiddleware } from "inversify-express-utils";

@injectable()
class LoggerMiddleware extends BaseMiddleware {
    @inject(TYPES.Logger) private readonly _logger: Logger;
    public handler(
        req: express.Request,
        res: express.Response,
        next: express.NextFunction
    ) {
        if (this.httpContext.user.isAuthenticated()) {
            this._logger.info(`${this.httpContext.user.details.email} => ${req.url}`);
        } else {
            this._logger.info(`Anonymous => ${req.url}`);
        }
        next();
    }
}

We also need to declare some type bindings:

const container = new Container();

container.bind<Logger>(TYPES.Logger)
        .to(Logger);

container.bind<LoggerMiddleware>(TYPES.LoggerMiddleware)
         .to(LoggerMiddleware);

We can then inject TYPES.LoggerMiddleware into one of our controllers.

@controller("/")
class UserDetailsController extends BaseHttpController {

    @inject("AuthService") private readonly _authService: AuthService;

    @httpGet("/", TYPES.LoggerMiddleware)
    public async getUserDetails() {
        if (this.httpContext.user.isAuthenticated()) {
            return this._authService.getUserDetails(this.httpContext.user.details.id);
        } else {
            throw new Error();
        }
    }
}

Request-scope services

Middleware extending BaseMiddleware is capable of re-binding services in the scope of a HTTP request. This is useful if you need access to a HTTP request or context-specific property in a service that doesn't have the direct access to them otherwise.

Consider the below TracingMiddleware. In this example we want to capture the X-Trace-Id header from the incoming request and make it available to our IoC services as TYPES.TraceIdValue:

import { inject, injectable } from "inversify";
import { BaseHttpController, BaseMiddleware, controller, httpGet } from "inversify-express-utils";
import * as express from "express";

const TYPES = {
    TraceId: Symbol.for("TraceIdValue"),
    TracingMiddleware: Symbol.for("TracingMiddleware"),
    Service: Symbol.for("Service"),
};

@injectable()
class TracingMiddleware extends BaseMiddleware {

    public handler(
        req: express.Request,
        res: express.Response,
        next: express.NextFunction
    ) {
        this.bind<string>(TYPES.TraceIdValue)
            .toConstantValue(`${ req.header('X-Trace-Id') }`);

        next();
    }
}

@controller("/")
class TracingTestController extends BaseHttpController {

    constructor(@inject(TYPES.Service) private readonly service: Service) {
        super();
    }

    @httpGet(
        "/",
        TYPES.TracingMiddleware
    )
    public getTest() {
        return this.service.doSomethingThatRequiresTheTraceID();
    }
}

@injectable()
class Service {
    constructor(@inject(TYPES.TraceIdValue) private readonly traceID: string) {
    }

    public doSomethingThatRequiresTheTraceID() {
        // ...
    }
}

The BaseMiddleware.bind() method will bind the TYPES.TraceIdValue if it hasn't been bound yet or re-bind if it has already been bound.

Middleware decorators

You can use the @withMiddleware() decorator to register middleware on controllers and handlers. For example:

function authenticate() {
    return withMiddleware(
        (req, res, next) => {
            if (req.user === undefined) {
                res.status(401).json({ errors: [ 'You must be logged in to access this resource.' ] })
            }
            next()
        }
    )
}

function authorizeRole(role: string) {
    return withMiddleware(
        (req, res, next) => {
            if (!req.user.roles.includes(role)) {
                res.status(403).json({ errors: [ 'Get out.' ] })
            }
            next()
         }
    )
}

@controller('/api/user')
@authenticate()
class UserController {

    @httpGet('/admin/:id')
    @authorizeRole('ADMIN')
    public getById(@requestParam('id') id: string) {
        ...
    }
}

You can also decorate controllers and handlers with middleware using BaseMiddleware identitifers:

class AuthenticationMiddleware extends BaseMiddleware {
    handler(req, res, next) {
        if (req.user === undefined) {
            res.status(401).json({ errors: [ 'User is not logged in.' ] })
        }
    }
}

container.bind<BaseMiddleware>("AuthMiddleware").to(AuthenticationMiddleware)

@controller('/api/users')
@withMiddleware("AuthMiddleware")
class UserController {
    ...
}

Route Map

If we have some controllers like for example:

@controller("/api/user")
class UserController extends BaseHttpController {
    @httpGet("/")
    public get() {
        return {};
    }
    @httpPost("/")
    public post() {
        return {};
    }
    @httpDelete("/:id")
    public delete(@requestParam("id") id: string) {
        return {};
    }
}

@controller("/api/order")
class OrderController extends BaseHttpController {
    @httpGet("/")
    public get() {
        return {};
    }
    @httpPost("/")
    public post() {
        return {};
    }
    @httpDelete("/:id")
    public delete(@requestParam("id") id: string) {
        return {};
    }
}

We can use the prettyjson function to see all the available enpoints:

import { getRouteInfo } from "inversify-express-utils";
import * as prettyjson from "prettyjson";

// ...

let server = new InversifyExpressServer(container);
let app = server.build();
const routeInfo = getRouteInfo(container);

console.log(prettyjson.render({ routes: routeInfo }));

// ...

⚠️ Please ensure that you invoke getRouteInfo after invoking server.build()!

The output formatter by prettyjson looks as follows:

routes:
  -
    controller: OrderController
    endpoints:
      -
        route: GET /api/order/
      -
        route: POST /api/order/
      -
        path: DELETE /api/order/:id
        route:
          - @requestParam id
  -
    controller: UserController
    endpoints:
      -
        route: GET /api/user/
      -
        route: POST /api/user/
      -
        route: DELETE /api/user/:id
        args:
          - @requestParam id

Examples

Some examples can be found at the inversify-express-example repository.

License

License under the MIT License (MIT)

Copyright Β© 2016-2017 Cody Simms

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.

IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

inversify-express-utils's People

Contributors

bowd avatar byongwu avatar codyjs avatar damiensynthesis avatar dcavanagh avatar dcherman avatar dependabot[bot] avatar egmacke avatar elratondefuego avatar evandroabukamel avatar ezra-quemuel avatar foxy-loxy avatar greenkeeper[bot] avatar greenkeeperio-bot avatar guidojw avatar iassal avatar jakubka avatar jameskmonger avatar jan-molak avatar jpsfs avatar kalahari avatar lholznagel avatar maxmalov avatar podarudragos avatar remojansen avatar rmblstrp avatar rohaanadvani68 avatar simplegsb avatar tioback avatar zachabney avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

inversify-express-utils's Issues

Async/await not working on service

Hello! I'm calling a method with async/await on service layer and it isn't waiting for the final promise. Is this a normal behavior? Because with 'express' it seems like it doesn't.

I'm using:
"node":"10.13.0"
"inversify": "5.0.1"
"inversify-binding-decorators": "3.2.0"
"inversify-express-utils": "6.2.0"
"typescript": "3.0.3"

To test this behavior using the https://github.com/inversify/inversify-express-example:

  1. Modify the folder 'Basic' and add this snippet to UserService.
  2. run with 'ts-node Basic/bootstrap.ts'
  3. calling curl localhost:3000/user

Behavior: the 'get' ends and the console continue printing. How do I call an async/await method and then return the response to the client??

@injectable()
export class UserService {
  private async run(){
        await this.sleep(2000);
        console.log('Free');
        await this.sleep(2000);
        console.log('Free');
        await this.sleep(2000);
        console.log('Free');
    }
    private sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

  public getUsers(): IUser[] {
    this.run().then(()=>{
      console.log('finish');
    });

    return this.userStorage;
  }

An in-range update of express is breaking the build 🚨

Version 4.14.1 of express just got published.

Branch Build failing 🚨
Dependency express
Current Version 4.14.0
Type dependency

This version is covered by your current version range and after updating it in your project the build failed.

As express is a direct dependency of this project this is very likely breaking your project right now. If other packages depend on you it’s very likely also breaking them.
I recommend you give this issue a very high priority. I’m sure you can resolve this πŸ’ͺ


Status Details
  • ❌ continuous-integration/travis-ci/push The Travis CI build could not complete due to an error Details
Release Notes 4.14.1
Commits

The new version differs by 24 commits .

There are 24 commits in total. See the full diff.

Not sure how things should work exactly?

There is a collection of frequently asked questions and of course you may always ask my humans.


Your Greenkeeper Bot 🌴

[Feature Request] Add request, response, next to BaseHttpController

For inversify-express-utils package.

To get the express req, res, next functions, we need to inject them into the action method with @request, @response, @next.

It would be much neater to have them on the BaseHttpController base class, so we can just do this.request, this.response, this.next.

Same thing for other related injectable things, like principal.

This would also avoid the "no-shadowed-variable" linting error when we have an argument like @next() next: express.NextFunction, which is really annoying.

inversify-socket-io-utils - new project submission

Hey guys, wasn't sure how else to get in contact.

I've put together a package for defining controllers and handlers using inversify for a socket.io server (https://socket.io/) based heavily on the inversify-express-utils package you have.

Setup looks like

const ioServer = new InversifySocketIOServer(
    inversifyContainer,
    httpServer,
    socketConfig
)

ioServer.build()

with an example controller looking like

@controller('/')
export class OnlineStatusController {
  constructor(
    @inject(SERVICE_TYPES.UserService) private userService: UserService
  ) {}

  @handler(CONNECT_USER)
  private async handleConnect(@socketId() socketId: string, @body() body: ConnectUserBody) {
    const {userId} = body

    // save socketId and userId somewhere

    this.userService.setUserOnline(userId)
  }

  @handler([SOCKET_IO_EVENT.DISCONNECT, DISCONNECT_USER])
  private async handleDisconnect(@socketId() socketId: string) {

    // retrieve userId given socketId
    
    this.userService.setUserOffline(userId)
  }
}

It currently supports:

  • auto discovery and binding of controllers using @controller(namespace?: string) class decorator
  • auto registration of event handlers using the @handler(events: string | string[]) method decorator
  • @socketId(): string @socket(): SocketIO.Socket @body(): any param decorators.
  • optional debug and logging feature
  • optional redis adapter feature

If this is a repo you guys would be interested in bringing in to your org, then I'd be happy to write up a proper readme and maintain it for you. I can zip up and send over the code for you to check out.

Let me know!

Passing additional data to Middleware

I am using

  • inversify 5.0.1
  • inversify-binding-decorators 4.0.0
  • inversify-express-utils 6.3.2
    The code I am working on can be seen at this Link
    The Problem
    I need to pass an additional parameter to the Middleware so that it will act as a factory,
    Example, I need to pass a role onto a middleware so that the system can check if the user has permission to access the API
    an example we need to create a middleware as follows
@provide("roles")
class RoleValidator extends BaseMiddleware {
    role(role){
        return handler(req: Request, res: Response, next: NextFunction) {
            if(req.user.role === role){
                next();
            } else {
                res.end();
            }
        }
    }
}

And the middleware is called by a controller

@controller('/test')
class UserController extends BaseHttpController {
    @httpPost('/', "roles=>'currentRole'")
    private test(
        @requestParam('param') param: string,
        res: Response,
        next: NextFunction
      ){
        //code
      }
}

How can this be achieved, this is a common scenario, that has to be used. Is it that, the feature is not supported.

Can't set headers after they are sent.

Hi. I decided to try an example. But on the request I get 204 code and an error in the logs. How I understand it before my error handler, some other one works.

@controller('/foo')
class FooController implements interfaces.Controller {
    @httpGet('/')
    private index(req: express.Request, res: express.Response, next: express.NextFunction) {
        return next(new Error());
    }
}
const container = new Container();
const server = new InversifyExpressServer(container);
server.setErrorConfig((app) => {
    app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
        res.status(500).send('Something broke!');
    });
});
const app = server.build();
app.listen(3000);
Error: Can't set headers after they are sent.
    at validateHeader (_http_outgoing.js:491:11)
    at ServerResponse.setHeader (_http_outgoing.js:498:3)
    at ServerResponse.header (E:\workplace\wx-meteor-v2\node_modules\inversify-express-utils\node_modules\express\lib\response.js:767:10)
    at ServerResponse.contentType (E:\workplace\wx-meteor-v2\node_modules\inversify-express-utils\node_modules\express\lib\response.js:595:15)
    at ServerResponse.send (E:\workplace\wx-meteor-v2\node_modules\inversify-express-utils\node_modules\express\lib\response.js:145:14)
    at app.use (E:\workplace\wx-meteor-v2\src\index.ts:37:25)
    at Layer.handle_error (E:\workplace\wx-meteor-v2\node_modules\inversify-express-utils\node_modules\express\lib\router\layer.js:71:5)
    at trim_prefix (E:\workplace\wx-meteor-v2\node_modules\inversify-express-utils\node_modules\express\lib\router\index.js:315:13)
    at E:\workplace\wx-meteor-v2\node_modules\inversify-express-utils\node_modules\express\lib\router\index.js:284:7
    at Function.process_params (E:\workplace\wx-meteor-v2\node_modules\inversify-express-utils\node_modules\express\lib\router\index.js:335:12)

An in-range update of http-status-codes is breaking the build 🚨

The dependency http-status-codes was updated from 1.3.0 to 1.3.1.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

http-status-codes is a direct dependency of this project, and it is very likely causing it to break. If other packages depend on yours, this update is probably also breaking those in turn.

Status Details
  • ❌ continuous-integration/travis-ci/push: The Travis CI build failed (Details).

Commits

The new version differs by 3 commits.

  • 338aa64 Bump patch version and publish
  • 3821d30 Added the http statuses as an enum. (#29)
  • 427963b Update README.md

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

[inversify-express-utils] AuthProvider Unhandled Promise Rejection

When an error is thrown/a promise is rejected in the AuthProvider's getUser() method, the server does not ever respond to the client and an Unhandled Promise Rejection Warning is written on the console.

Expected Behavior

The error handler of the express server should be called and respond appropriately to the client.

Current Behavior

No server response is sent and the client is left waiting when an error occurs in the AuthProvider.

Steps to Reproduce

  async getUser(req: Request, res: Response, next: NextFunction): Promise<interfaces.Principal> {
    console.log('[AUTH_MD]');
    const token = req.header('X-CONTROL-CENTER-AUTH') as string;
    if (!token) return new Principal(undefined);
    try {
      const user = await this.userService
        .validate(token);
      if (!user) {
        return Promise.reject(new Unauthorized({ path: '' }));
      }
      return new Principal(user);
    } catch (e) {
      return Promise.reject(new InternalServerError({ path: '' }));
    }
  }
}

Note: The Unauthorized and InternalServerError classes are from express-openapi-validator but the result is the same using even a simple Error.

Your Environment

  • Version used: inversify: ^5.0.1 && inversify-express-utils: ^6.3.2
  • Environment name and version (e.g. Chrome 39, node.js 5.4): Docker-node:alpine3.12

handlerFactory calls res.send() breaking controllers wanting to stream to the response body

i've got this endpoint:

  @httpGet('/')
  async getTheThing(@response() res: Response) {
    try {
      const fileStream = await this.storage.getTheThing();
      res.setHeader('content-type', 'application/tar');
      fileStream.pipe(res);
    } catch (error) {
      return res.status(404).send(error);
    }

This fails with:

Error: write after end
    at write_ (_http_outgoing.js:631:15)
    at ServerResponse.write (_http_outgoing.js:626:10)
    at IncomingMessage.ondata (_stream_readable.js:646:20)
    at IncomingMessage.emit (events.js:160:13)
    at IncomingMessage.Readable.read (_stream_readable.js:482:10)
    at flow (_stream_readable.js:853:34)
    at resume_ (_stream_readable.js:835:3)
    at process._tickCallback (internal/process/next_tick.js:152:19)

Because the handlerFactory at this line calls res.send() which closes the request while the stream is still being written.

I think if a controller uses @response() to get access to the response object then the handler factory shouldn't touch the response object at all.

[inversify-express-utils] - No support for @controller on inevrfisy-binding-decorators?

Hey folks, I'm trying to implement here the inversify-binding-decorators together with the inversify-express-utils lib. Unfortunately, I wasn't didn't manage to make my @controller bindings to work properly. They do not seem to be recognized and I'm having to load all of them through a container.load

for example:

container.load(
  buildProviderModule(),
  assetControllerContainer,
  collectionControllerContainer,
  userControllerContainer,
  trackerControllerContainer,
  quoteControllerContainer,
  indicatorControllerContainer,
  portfolioControllerContainer,
  companyControllerContainer,
  orderControllerContainer,
  newsControllerContainer
);

Expected Behavior

I was expecting to add a @provide() on any of my controllers and it's automatically bound into my inversify container, so I don't need to load all of them manually with container.load (like above)

Current Behavior

Any @provide(CLASSNAMEHERE) on my controllers simply leads to a unbound controller (route throws a 404)

Possible Solution

A temporary workaround is binding modules manually and them loading them on container.load.

For example:

export const assetControllerContainer = new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Unbind) => {
  bind<CreateAssetController>(CreateAssetController).toSelf();
  bind<FilterAssetController>(FilterAssetController).toSelf();
  bind<ReadAssetController>(ReadAssetController).toSelf();
});

then

container.load(
  buildProviderModule(),
  assetControllerContainer,
  ....
);

Latest inversify-express-utils is throwing build errors

Previous version of inversify-express-utils is throwing us build errors:
Previous version: 6.3.2
Latest version: 6.4.3 (latest)

tsc

node_modules/inversify-express-utils/node_modules/inversify/lib/annotation/injectable.d.ts:1:52 - error TS1005: ',' expected.

1 declare function injectable(): <T extends abstract new (...args: never) => unknown>(target: T) => T;
~~~
node_modules/inversify-express-utils/node_modules/inversify/lib/annotation/injectable.d.ts:1:83 - error TS1144: '{' or ';' expected.

1 declare function injectable(): <T extends abstract new (...args: never) => unknown>(target: T) => T;
~
node_modules/inversify-express-utils/node_modules/inversify/lib/annotation/injectable.d.ts:1:91 - error TS1005: ')' expected.

1 declare function injectable(): <T extends abstract new (...args: never) => unknown>(target: T) => T;
~
node_modules/inversify-express-utils/node_modules/inversify/lib/annotation/injectable.d.ts:1:94 - error TS1005: ';' expected.

1 declare function injectable(): <T extends abstract new (...args: never) => unknown>(target: T) => T;
~
node_modules/inversify-express-utils/node_modules/inversify/lib/annotation/injectable.d.ts:1:96 - error TS1128: Declaration or statement expected.

1 declare function injectable(): <T extends abstract new (...args: never) => unknown>(target: T) => T;

Can't build with the latest version, is there an update thats needed to something that I'm not aware of?

An in-range update of typescript is breaking the build 🚨

Version 2.3.3 of typescript just got published.

Branch Build failing 🚨
Dependency typescript
Current Version 2.3.2
Type devDependency

This version is covered by your current version range and after updating it in your project the build failed.

As typescript is β€œonly” a devDependency of this project it might not break production or downstream projects, but β€œonly” your build or test tools – preventing new deploys or publishes.

I recommend you give this issue a high priority. I’m sure you can resolve this πŸ’ͺ

Status Details
  • ❌ continuous-integration/travis-ci/push The Travis CI build failed Details

Release Notes TypeScript 2.3.3

This release include a set of bug fixes reported against TypeScript 2.3.2. For the complete list of fixed issues, check out the fixed issues query for TypeScript 2.3.3.

Download:

Commits

The new version differs by 84 commits.

There are 84 commits in total.

See the full diff

Not sure how things should work exactly?

There is a collection of frequently asked questions and of course you may always ask my humans.


Your Greenkeeper Bot 🌴

Release of a new npm version with peer dependency Express 4.17.0+

Hello inversify-express-utils maintainers,

I'm writing to ask if it would be possible for you to release a new version of this package into NPM from commit 8766292 or later?

My team's project depends on a change added in Express 4.17.0, published in May 2019 which added SameSite=none support to res.cookie(). inversify-express-utils last NPM release was in Jan 2019, with Express 4.16. As noted above, you've already made the upgrade we are looking for in the master branch.

My team got around this issue by forking the repository and storing a build in version control. I would like to clean this up and go back to using a public version from NPM. The only way I see to do this is to get a new public NPM version released. Kindly also let me know if there is a cleaner workaround (for example I looked at the constructor of InversifyExpressServer() thinking it may accept an express module as an argument, but it doesn't appear to support that).

Thanks,
-Andrew

Contextual injection not working with express utils decorators (inversify-express-utils)

I want to use contextual binding in my inversify-express application. I have a problem using contextual injection. Here is a minimal example. Suppose we have the following interfaces and classes

export type AuthenticationParams = {
    userId: string,
}

export interface NextInteractor<T>{
    execute(params: T): void;
}

@injectable()
export class AuthenticationInteractor<T extends AuthenticationParams>{
    constructor(@inject(TYPES.NEXT_INTERACTOR) private nextInteractor: NextInteractor<T>){}

    execute(params: T){
        console.log(`continuing as user with userId ${params.userId}`);
        this.nextInteractor.execute(params);
    }
}

And theses are some classes implementing NextInteractor

export type FirstParams = {
    userId: string
}

@injectable()
export class FirstInteractor implements NextInteractor<FirstParams>{
    execute(params: FirstParams) {
        console.log(`printing from first interactor ${params.userId}`)
    }
}


export type SecondParams = {
    userId: string,
    otherParam: string,
}

@injectable()
export class SecondInteractor implements NextInteractor<SecondParams>{
    execute(params: SecondParams) {
        console.log(`printing from second interactor, ${params.userId}, ${params.otherParam}`)
    }
}

And I use AuthenticationInteractor in the following file

@controller('/first')
export class FirstInput {
    constructor(@inject(TYPES.AUTHENTICATION_INTERACTOR) private authInteractor: AuthenticationInteractor<FirstParams>) {}

    @httpGet('/')
    get(@queryParam('userId') userId: string) {
        this.authInteractor.execute({ userId });
        return 'first url';
    }
}

@controller('/second')
export class SecondInput {
    constructor(@inject(TYPES.AUTHENTICATION_INTERACTOR) private authInteractor: AuthenticationInteractor<SecondParams>) { }

    @httpGet('/')
    get(@queryParam('userId') userId: string, @queryParam('otherParam') otherParam: string) {
        this.authInteractor.execute({ userId, otherParam });
        return 'second url';
    }
}

My intention is to call AuthenticationInteractor as the first step in every controller and only then continuing to the main logic. I hoped to achieve this with contextual binding as follows

container.bind<AuthenticationInteractor<any>>(TYPES.AUTHENTICATION_INTERACTOR).to(AuthenticationInteractor);

container.bind<NextInteractor<FirstParams>>(TYPES.NEXT_INTERACTOR).to(FirstInteractor).whenAnyAncestorIs(FirstInput);
container.bind<NextInteractor<SecondParams>>(TYPES.NEXT_INTERACTOR).to(SecondInteractor).whenAnyAncestorIs(SecondInput);

Expected Behavior

To bind nextInteractor to the correct one in each case

Current Behavior

inversify returns this error

Error: Ambiguous match found for serviceIdentifier: Symbol(NEXT_INTERACTOR)

Possible Solution

Steps to Reproduce (for bugs)

  1. clone https://gitlab.com/dadsetan.ali/inversify-express-contextual-binding-issue.git
  2. npm i
  3. npm start

Context

In my real project, I am trying to create a single Class for All authentication logic. Authentication, Authorization, token refresh, etc.

Your Environment

Stack trace

An in-range update of inversify is breaking the build 🚨

Version 3.2.0 of inversify just got published.

Branch Build failing 🚨
Dependency inversify
Current Version 3.1.0
Type devDependency

This version is covered by your current version range and after updating it in your project the build failed.

As inversify is β€œonly” a devDependency of this project it might not break production or downstream projects, but β€œonly” your build or test tools – preventing new deploys or publishes.

I recommend you give this issue a high priority. I’m sure you can resolve this πŸ’ͺ


Status Details
  • ❌ continuous-integration/travis-ci/push The Travis CI build could not complete due to an error Details
Release Notes 3.2.0

The main addition of this release is a feature that allows library and framework authors to customize the annotation phase.

Commits

@remojansen Refactoring binding (#491) … 0ee96ad
@greenkeeper Update gulp-mocha to version 4.0.0 (#494) … 83491d0
@remojansen Bug fix 496 (#502) … a92d13f
@Dirrk Update bluebird to 3.5.0 (#503) f5354a9
@Dirrk Updated sinon to 2.0.0 (#511) 96230ab
@remojansen Support for metadata middleware (#507) … d60a053

Commits

The new version differs by 7 commits .

  • 1410309 Update package.json
  • d60a053 Support for metadata middleware (#507)
  • 96230ab Updated sinon to 2.0.0 (#511)
  • f5354a9 update bluebird to 3.5.0 (#503)
  • a92d13f Bug fix 496 (#502)
  • 83491d0 chore(package): update gulp-mocha to version 4.0.0 (#494)
  • 0ee96ad Refactoring binding (#491)

See the full diff.

Not sure how things should work exactly?

There is a collection of frequently asked questions and of course you may always ask my humans.


Your Greenkeeper Bot 🌴

Registering controllers to modules instead of global scope

I've noticed that controllers are automatically registered to global scope when they are imported. A cleaner way would be to allow each package to export its controllers and then load them into the required container, as with any other dependencies.

Is there a way to do so now or would I need to implement this feature?

Incomplete example

The example in the readme is incomplete because FooService.ts is missing.

Is it available somewhere?

[inversify-express-utils]: Auth provider cannot rely on server-level middleware

Using the inversify-express-utils you can create an express application and configure:

  • an auth(orization) provider
  • server level middleware

When building the server application the http context is created before the server level middleware is registered.

Here's an excerpt of the build method.

this._app.all("*", (
  req: express.Request,
  res: express.Response,
  next: express.NextFunction
) => {
  (async () => {
      const httpContext = await _self._createHttpContext(req, res, next);
      ...
      next();
  })();
});

// register server-level middleware before anything else
if (this._configFn) {
  this._configFn.apply(undefined, [this._app]);
}

For each request the http context will be created, which in turn creates the principal. After that the server level middleware configured via the setConfig is executed.

 private async _createHttpContext(
   req: express.Request,
   res: express.Response,
   next: express.NextFunction
) {
  const principal = await this._getCurrentUser(req, res, next);
  ...
}

Let's assume a use case where you have to read data from the body in the authorization provider. If you configure the body-parser via the setConfig it's too late. This is called after the principal is created. When trying to compose a principal (getCurrentUser) you cannot ready the body as it is undefined at the time. The same probably applies for cookies as this requires the cookie-parser middleware to be setup.

JsonContent doesn't send correctly

When using the BaseHttpController helper method BaseHttpController.json(...) the resulting express call coerces the json object to a string.

This causes problems when using express middleware that validates JSON responses (in this case using openapi-validator-middleware) as the data is no-longer a JSON object.

The root cause of this appears to be that the use of HttpContent enforcing all data as a string in readAsStringAsync().

My understanding is that this is to avoid potential issues where numeric data is interpreted by express as a status code, but I believe that it would be safe to all string | object types to be sent.

I will open a PR shortly with a proposed fix for this.

Unit test

Hello,
Do You have any examples with unit test?

Handling singletons in creation of large plans

We have a complex application with a huge dependency graph. On startup, when all controllers are initialized at once using inversify-express-utils (container.getAll for controllers) we noticed that the startup takes upward of 5 minutes, with more than 6 GB of memory taken for the plan graph. Most of our bindings are Singleton.

Expected Behavior

container.getAll() should be fast

Current Behavior

container.getAll() takes > 5 minutes, a huge amount of RAM

Possible Solution

in planner.ts, we do not need to create childRequests for singleton Bindings multiple times, as there will be only instance of the object
Will be updating the issue with a pull request

Steps to Reproduce (for bugs)

  1. Application with a large dependency graph
  2. container.get() an object which is dependent on the full dependency graph

Context

Speeds up the startup of our application, and reduces startup memory utilization from ~6.5 GB to 600MB

Your Environment

node v11.13.0
inversify 5.0.1
inversify-express-utils 6.3.2
express 4.16.2

Private repo

Stack trace

None

[Inversify Express Utils] Controller is Reconstructed on Every Request

import {controller, interfaces, httpGet} from 'inversify-express-utils'

@controller('')
export class IndexController implements interfaces.Controller {
  constructor() {
    console.log('Construct index controller')
  }

  @httpGet('/hello')
  index() {
    return 'hello\n'
  }
}

In my logs I can see, that every time I make a get request to /hello, the IndexController constructor is called.
Is this behaviour expected?

Is there a way to call the constructor only once on, application start?
I have controllers that run logic in the constructor. And it would be really redundant to rerun this code on every single incoming request.

integrating inversify-express-utils with inversify-socket-utils

There is a nice utils library called inversify-socket-utils which nicely wraps a lot of socketio logic and gives the ability to use decorators in controllers to handle event emits in controllers.

The problem is both of the libraries:
Socket utils:
https://github.com/alxshelepenok/inversify-socket-utils
Express utils:
https://github.com/inversify/inversify-express-utils

Return Inversify server so I haven't figured out how could I integrate these two to run on the same server and port.

Has anyone successfully done it by any chance?

An in-range update of moq.ts is breaking the build 🚨

The devDependency moq.ts was updated from 2.8.4 to 2.8.5.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

moq.ts is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • ❌ continuous-integration/travis-ci/push: The Travis CI build failed (Details).

Release Notes for v2.8.5

2.8.5 (2019-02-23)

Bug Fixes

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Injection scope for controller (singleton instead request scoped)

This issue might be a duplicate of inversify/InversifyJS#781 which one is already closed. However, I want to consider some nuances for further project improvement.

I started experimenting with those utils and stuck exactly with injection scope of controllers.
This doesn't fit to my tasks and approaches on my servers, so I would like to discuss this moment once again.

I have some questions.

  1. Is it possible to add some parameter to InversifyExpressServer? To be able to use controller as a singleton, but by default leave request scoped.

  2. Is it possible to bind controller more explicit? I mean not just import the file, but import particular controller and inject it into container (with defined scope parameters).

Something like:

@controller('api')
class ApiController implements interfaces.Controller {}

const container = new Container();
const server = new InversifyExpressServer(container);
// or maybe allow here some factory to provide custom controllers
server.bindController(ApiController).in*Scope();

I see that we have BaseHttpController class, so, in case of singleton scope, we might restrict to add controller which extends/implements HttpBaseController, and maybe amend it:

server.bindControllerSingletonScope(ApiController);
server.bindControllerRequestScope(ApiController); // might raise type error since it is not extending BaseHttpRequest class

Just before trying to make a fork and consideration of further using of those utils, want to consider my view with author's perspective :)

Inversify Express controller inheritance fail if inheriting from more than 1 controller

After updating to the latest version of inversify and inversify-express-utils, I can't have controller inheritance working with more than 1 base controller, like BaseController -> BaseApiController -> PostController, URLs from BaseController return 404

Expected Behavior

You should be able to extend as many base controllers as you like and keep the methods and URLs

Current Behavior

The base endpoints return 404

Steps to Reproduce (for bugs)

  1. Get a fresh copy of https://github.com/inversify/inversify-express-utils and npm install
  2. Replace controller_inheritance.test.ts with my gist https://gist.github.com/aescarcha/341db9d36ccff444b8ad1368e2c834c2
  3. npm run test should fail

Context

I like splitting my controllers and using a lot of inheritances, like having a base controller, then a query controller, and a paginateable controller all inheriting the previous one

Your Environment

I'm on macOS Catalina, node 14.5.0, using all other deps from inversify-express-utils

[inversify-express-utils] allow to inject dependencies into middleware constructor

Expected Behavior

Middlewares should be created by http request scoped container.
Since by default they are not singleton it does make sense to create them using per-request container, which allows to inject request-scoped dependencies in constructor instead calling this.httpContext.container.get(...). For middlewares that are registered as singleton nothing will be changed, so it should add any performance difference.

Current Behavior

Right now middlewares in express-utils are always created using root container which does not allow to inject request scoped variables in constructor.

Possible Solution

PR: https://github.com/inversify/inversify-express-utils/pull/325/files

Steps to Reproduce (for bugs)

    it("Should allow constructor injections from http-scope in middlewares", async () => {

        const TYPES = {
            Value: Symbol.for("Value"),
            ReadValue: Symbol.for("ReadValue"),
            HttpContextValueSetMiddleware: Symbol.for("HttpContextValueSetMiddleware"),
            HttpContextValueReadMiddleware: Symbol.for("HttpContextValueReadMiddleware"),
        };

        class HttpContextValueSetMiddleware extends BaseMiddleware {
            public handler(
                req: express.Request,
                res: express.Response,
                next: express.NextFunction
            ) {
                this.bind<string>(TYPES.Value).toConstantValue(`MyValue`);
                next();
            }
        }

        class HttpContextValueReadMiddleware extends BaseMiddleware {
            constructor(@inject(TYPES.Value) private value: string) {
                super();
            }

            public handler(
                req: express.Request,
                res: express.Response,
                next: express.NextFunction
            ) {
                this.bind(TYPES.ReadValue).toConstantValue(`${this.value} is read`);
                next();
            }
        }

        @controller("/http-scope-middleware-injection-test")
        class MiddlewareInjectionTestController extends BaseHttpController {

            constructor(@inject(TYPES.ReadValue) @optional() private value: string) {
                super();
            }

            @httpGet(
                "/get-value",
                TYPES.HttpContextValueSetMiddleware,
                TYPES.HttpContextValueReadMiddleware
            )
            public getValue() {
                return this.value;
            }
        }

        const container = new Container();

        container.bind<HttpContextValueReadMiddleware>(TYPES.HttpContextValueReadMiddleware)
            .to(HttpContextValueReadMiddleware);
        container.bind<HttpContextValueSetMiddleware>(TYPES.HttpContextValueSetMiddleware)
            .to(HttpContextValueSetMiddleware);
        container.bind<string>(TYPES.Value).toConstantValue("DefaultValue");
        const app = new InversifyExpressServer(container).build();

        await supertest(app)
            .get("/http-scope-middleware-injection-test/get-value")
            .expect(200, "MyValue is read");
    });

Context

In application I would like to register some values in middlewares at the beginning of the request and then use classes that abstract HTTP specific things for the consumers. Allowing to inject dependencies into middleware constructor simplifies unit testing of those middlewares.

Your Environment

NodeJS 12.x.x
Windows

inversify-express-utils, support of express 4.17.1

I can see here that express was updated to 4.17.1 long time ago but when I install the latest [email protected] it contains dependency "express": "4.16.2"

Expected Behavior

[email protected] should use [email protected]

Current Behavior

[email protected] uses [email protected]

Possible Solution

Publish inversify-express-utils in proper way

Steps to Reproduce (for bugs)

install inversify-express-utils@latest, open its package.json, observe express version

Context

It is impossible to set response cookie with sameSite=none as this fix was provided only in express 4.17

Your Environment

  • Version used: 6.3.2
  • Environment name and version (e.g. Chrome 39, node.js 5.4): node 12.16.1
  • Operating System and version (desktop or mobile): macOS

Per-controller error handling

I couldn't find a way to handle all errors in a controller in one handler.
For example:

@controller('/user')
export default class UserController implements interfaces.Controller {
    @httpGet('/')
    private async get(@request() req: Request, @response() res: Response) {
        throw new Error('This should be handled in the error handler')
    }

    @httpPost('/')
    private async create(@request() req: Request, @response() res: Response) {
        throw new Error('This should be handled in the error handler')
    }
    
    // Ideally I would want something like
    private errorHandler(err, req, res) {
        // handle any error in the controller here
    }
}

It's not working also in application level,

app.use(function (err, req, res, next) {
  // errors in the controllers doesn't trigger this
});

Is there an elegant way to handle errors without repeating the code? (If I want for example to log any error to some logger)?

Inversify-Express-Untils Cannot Build Multiple Servers

Expected Behavior

Having two different files both building InversifyExpressServers should result in no errors, even if they have different Containers.

Current Behavior

Having two different files both building InversifyExpressServers results in an error. The only case in which it does not result in an error is if they have Containers that are set up with identical Bindings (but the constructors can be different).

Steps to Reproduce (for bugs)

  1. Create a file that creates a container and add a binding. Then create a new InversifyExpressServer with that container, then build and export that server.
  2. Create a file that creates a container and adds no bindings. Then create a new InversifyExpressServer with that empty container (any different container should work), then build and export that server.
  3. Have your entry file import both servers, and try to run one. You will get the error: Error: No matching bindings found for serviceIdentifier: Symbol(<name of first unique binding>)

Context

My app exports multiple express servers and then each one is spun up as a serverless function with gcloud. Thus I want to build all of these at the same time even though I only run one at a time. Right now my workaround is to build each server independently.

Your Environment

  • Version used:
"inversify": "^5.0.1",
 "inversify-express-utils": "^6.3.2"
  • Environment name and version (e.g. Chrome 39, node.js 5.4): Node v12.17.0
  • Operating System and version (desktop or mobile): Ubuntu

Stack trace


Error: No matching bindings found for serviceIdentifier: Symbol(<binding name>)
    at _validateActiveBindingCount (root/node_modules/inversify/lib/planning/planner.js:62:23)
    at _getActiveBindings (root/node_modules/inversify/lib/planning/planner.js:48:5)
    at _createSubRequests (root/node_modules/inversify/lib/planning/planner.js:91:26)
    at root/node_modules/inversify/lib/planning/planner.js:115:17
    at Array.forEach (<anonymous>)
    at root/node_modules/inversify/lib/planning/planner.js:114:26
    at Array.forEach (<anonymous>)
    at _createSubRequests (root/node_modules/inversify/lib/planning/planner.js:94:20)
    at Object.plan (root/node_modules/inversify/lib/planning/planner.js:136:9)
    at root/node_modules/inversify/lib/container/container.js:317:37

inversify-express-utils: BaseHttpController's conflict should take an argument

This is a feature request for inversify-express-utils. Controllers that extend BaseHttpController have access to a this.conflict method, but this method does not take any arguments. Using this.json is an easy workaround, but I would still like this.conflict to take an optional generic argument.

Expected Behavior

I would expect this.conflict to take a generic argument that allows me to explain why the conflict occurred.

Current Behavior

I cannot provide an argument, so this.conflict just returns a 409 code.

Context

Let's say I get a POST /users request. It may conflict because (1) the provided username already exists or (2) the provided email already exists. The API should return which conflict actually occurred.

An in-range update of tslint is breaking the build 🚨

Version 5.3.0 of tslint just got published.

Branch Build failing 🚨
Dependency tslint
Current Version 5.2.0
Type devDependency

This version is covered by your current version range and after updating it in your project the build failed.

As tslint is β€œonly” a devDependency of this project it might not break production or downstream projects, but β€œonly” your build or test tools – preventing new deploys or publishes.

I recommend you give this issue a high priority. I’m sure you can resolve this πŸ’ͺ

Status Details
  • ❌ continuous-integration/travis-ci/push The Travis CI build failed Details

Release Notes v5.3.0

This change may require a change to tslint.json

πŸŽ‰ Notable features & enhancements

Thanks to our contributors!

  • Andy Hanson
  • Klaus Meinhardt
  • Martin Probst
  • Filipe Silva
  • walkerburgin
  • RenΓ© Scheibe
Not sure how things should work exactly?

There is a collection of frequently asked questions and of course you may always ask my humans.


Your Greenkeeper Bot 🌴

Error trying to instantiate controller in a test (Error: No matching bindings found for serviceIdentifier: Symbol(HttpContext))

I'm trying to write a unit test for a controller that has an injected service at the constructor, that's why (unlike the docs where controllers are instantiated with the new keyword in a test) i need to instantiate it with the container.
The controller extends the BaseHttpController from inversify-express-utils I'm failing trying to do:

Test

import { expect } from "chai"
import { container } from '../../src/container/inversify.config'
import { HealthControllerV1 } from "../../src/controllers/v1/HealthControllerV1"
import { results } from "inversify-express-utils"

describe("ExampleController", () => {
    let controller: HealthControllerV1

    beforeEach(() => {
        controller = container.get<HealthControllerV1>(HealthControllerV1)
    })

    describe("Health", () => {
        it("should return 200 status with Json response", async () => {
            const response = controller.health()
            expect(response).to.be.an.instanceof(results.JsonResult)
        })
    })
})  

Container:

import { Container } from "inversify";
import { HealthControllerV1 } from "../controllers/v1/HealthControllerV1";
import { GoalsService, IGoalsService } from "../services/GoalsService";

const container = new Container()

container.bind<HealthControllerV1>(HealthControllerV1).toSelf()
container.bind<IGoalsService>('goalsService').to(GoalsService)

export { container }

Controller:

import 'reflect-metadata'
import { BaseHttpController, controller, httpGet, interfaces, results } from 'inversify-express-utils'
import { inject } from 'inversify'
import { IGoalsService } from '../../services/GoalsService'

@controller('/v1/health')
export class HealthControllerV1 extends BaseHttpController implements interfaces.Controller {

    constructor(@inject('goalsService') private goalsService: IGoalsService){super()}
    
    @httpGet('/')
    health(): results.JsonResult {
        this.goalsService.log('111')
        return this.json({ msg: 'OK' })
    }
}   

Error:

HealthControllerV1
    Health
      1) "before each" hook for "should return 200 status with Json response"


  0 passing (8ms)
  1 failing

  1) HealthControllerV1
       "before each" hook for "should return 200 status with Json response":
     Error: No matching bindings found for serviceIdentifier: Symbol(HttpContext)

My Environment

"inversify": "^5.0.5",
"inversify-express-utils": "^6.3.2",
"node": "12.20.0"
"chai": "^4.3.0",
"express": "^4.17.1",
"mocha": "^8.3.0",
"reflect-metadata": "^0.1.13",

Stack trace

 Error: No matching bindings found for serviceIdentifier: Symbol(HttpContext)
      at _validateActiveBindingCount (node_modules/inversify/lib/planning/planner.js:63:23)
      at _getActiveBindings (node_modules/inversify/lib/planning/planner.js:49:5)
      at _createSubRequests (node_modules/inversify/lib/planning/planner.js:92:26)
      at /home/marcosdipaolo/Documents/dev/goals-backend/node_modules/inversify/lib/planning/planner.js:116:17
      at Array.forEach (<anonymous>)
      at /home/marcosdipaolo/Documents/dev/goals-backend/node_modules/inversify/lib/planning/planner.js:115:26
      at Array.forEach (<anonymous>)
      at _createSubRequests (node_modules/inversify/lib/planning/planner.js:95:20)
      at Object.plan (node_modules/inversify/lib/planning/planner.js:137:9)
      at /home/marcosdipaolo/Documents/dev/goals-backend/node_modules/inversify/lib/container/container.js:319:37
      at Container._get (node_modules/inversify/lib/container/container.js:312:44)
      at Container.get (node_modules/inversify/lib/container/container.js:232:21)
      at Context.<anonymous> (test/unit/health.test.ts:10:32)
      at processImmediate (internal/timers.js:461:21)

thanks a lot

How do I get access to http server?

In order to make it work with graphql subscriptions(or socket in general), I need access to the underlying http server(not Express application).

Is there a way to do that?

An in-range update of tslint is breaking the build 🚨

Version 4.3.0 of tslint just got published.

Branch Build failing 🚨
Dependency tslint
Current Version 4.2.0
Type devDependency

This version is covered by your current version range and after updating it in your project the build failed.

As tslint is β€œonly” a devDependency of this project it might not break production or downstream projects, but β€œonly” your build or test tools – preventing new deploys or publishes.

I recommend you give this issue a high priority. I’m sure you can resolve this πŸ’ͺ


Status Details
  • ❌ continuous-integration/travis-ci/push The Travis CI build failed Details
Release Notes v4.3.0
  • Enabled additional rules in tslint:latest configuration (#1981)
  • [new-rule] space-before-function-paren (#1897)
  • [new-rule] typeof-compare (#1927)
  • [new-rule] import-spacing (#1935)
  • [new-rule] unified-signatures (#1944)
  • [new-fixer] object-literal-key-quotes (#1953)
  • [new-fixer] no-angle-bracket-type-assertion (#1979)
  • [bugfix] adjacent-overload-signature now handles static/computed function names (#1831)
  • [bugfix] file-header now handles files with only comments (#1913)
  • [bugfix] no-consecutive-blank-lines now allows blank lines in template strings (#1886)
  • [bugfix] object-literal-key-quotes no longer throws exception when using rest operator (#1916)
  • [bugfix] restrict-plus-operands no longer shows false positive in ternary operation (#1925)
  • [bugfix] prefer-for-of now handles nested for loops with reused iterator (#1926)
  • [bugfix] Exit gracefully when tsconfig.json passed as --project argument doens't have files (#1933)
  • [bugfix] object-literal-key-quotes now handles shorthand and spread properties (#1945)
  • [bugfix] arrow-parens Allow binding patterns ([x, y]) => ... and ({x, y}) => ... to have parens (#1958)
  • [bugfix] semicolon fixer now handles comma separator in interfaces and indicates failure when commas are using in interfaces (#1856)
  • [bugfix] only-arrow-functions option allow-named-functions now allows function declarations (#1961)
  • [bugfix] prefer-for-of no longer shows false positive when array is in parentheses (#1986)
  • [bugfix] prefer-const now works for TypeScript versions < 2.1.0 (#1989)
  • [enhancement] member-access narrow location of error (#1964)

Thanks to our contributors!

  • Andrii Dieiev
  • @andy-ms
  • Andy Hanson
  • Josh Goldberg
  • Klaus Meinhardt
  • Linda_pp
  • Mohsen Azimi
  • Victor Grigoriu
  • Yuichi Nukiyama
  • cameron-mcateer
Not sure how things should work exactly?

There is a collection of frequently asked questions and of course you may always ask my humans.


Your Greenkeeper Bot 🌴

An in-range update of moq.ts is breaking the build 🚨

Version 2.7.0 of moq.ts was just published.

Branch Build failing 🚨
Dependency moq.ts
Current Version 2.6.1
Type devDependency

This version is covered by your current version range and after updating it in your project the build failed.

moq.ts is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • ❌ continuous-integration/travis-ci/push: The Travis CI build failed (Details).

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Can not inject dependency in my CustomAuthProvider

i'm trying initialize AuthProvider as described on readme.

but i can not inject dependency in my CustomAuthProvider

If inject without constructor as described on readme, All injected dependencies are undefined.

@injectable()
export class CustomAuthProvider implements interfaces.AuthProvider {

    @inject(LoggerService.name) private readonly _loggerService!: LoggerService;
    @inject(DBService.name) private readonly _db!: DBService;
    @injectHttpContext private readonly _httpContext!: HttpContext;

    public async getUser(
        req: express.Request,
        res: express.Response,
        next: express.NextFunction
    ): Promise<interfaces.Principal> {
        let user: Auth.User|null = null;
        try {
            user = await this._db.SelectUserInfo(req.session.user.email);
            // this._db is undefined
        } catch (error) {
            this._loggerService.logger.error(error);
            // this._loggerService is undefined too.
        }
        const principal = new Principal(user);
        return principal;
    }

}

And if inject a dependency to CustomAuthProvider on constructor of CustomAuthProvider, error occured on line of initializing InversifyExpressServer.

//customAuthProvider.ts

@injectable()
export class CustomAuthProvider implements interfaces.AuthProvider {

    constructor (
        @inject(LoggerService.name) private readonly _loggerService: LoggerService,
        @inject(DBService.name) private readonly _db: DBService,
        @injectHttpContext private readonly _httpContext: HttpContext
    ) {
    }

    public async getUser(
        req: express.Request,
        res: express.Response,
        next: express.NextFunction
    ): Promise<interfaces.Principal> {
        let user: Auth.User|null = null;
        try {
            user = await this._db.SelectUserInfo(req.session.user.email);
        } catch (error) {
            this._loggerService.logger.error(error);
        }
        const principal = new Principal(user);
        return principal;
    }

}

// app.ts
this.container = new Container();
this.container.bind(LoggerService.name).to(LoggerService);
this.container.bind(DBService.name).to(DBService).inSingletonScope();
this.container.bind(CustomAuthProvider.name).to(CustomAuthProvider).inSingletonScope();

this.server = new InversifyExpressServer(this.container, null, null, null, CustomAuthProvider); // error is occured on this line.
//Argument of type 'typeof CustomAuthProvider' is not assignable to parameter of type 'new () => AuthProvider'.
//Types of construct signatures are incompatible.
//Type 'new (_loggerService: LoggerService, _db: DBService, _httpContext: HttpContext) => CustomAuthProvider' is not assignable to type 'new () => AuthProvider'.ts(2345)

How can i use another dependencies on CustomAuthProvider?

[inversify-express-utils] Nesting controllers and maintaining route context

I'm just getting into InversifyJS and I'm struggling to get my head around the concept of Express controllers.

Usually I write my code in such a fashion that the routing chain is executed step by step and the context of the request is incrementally stored inside the request object.

routes/index.ts

router.use("/users", usersRouter);

export default router;

routes/users/index.ts

router.use("/:userId", (req, res, next) => {
  let user = fakedSyncGetUserById(req.params.userId);

  req.context.user = user;

  next();
});

router.use("/:userId/posts", postsRouter);

router.get("/:userId", (req, res) => {
  res.status(200).json(req.context.user);
});

routes/users/posts.ts

router.get("/", (req, res) => {
  let posts = fakeGetPostsByUser(req.context.user);

  res.status(200).json(posts);
});

This way I don't have to validate the user in the controller that manages the posts. How would this be achieved in a sane way using InversifyJS?

Another issue I couldn't find the answer to is the order routes are executed, such /users/me being executed before /users/:userId is evaluated.

Create a Principal decorator

I'm trying to create a new decorator to make it simpler to obtain the Principal information, something like:

@httpGet("/")
public async getByUser(@principal() principal: Principal): Promise<any> {
    console.log(principal.details);
}

However, I'm having a hard time understanding the code and can't seem to find out where is it that the parameters values are altered by the @controller and/or @httpMethod decorators.

Would someone please point me to the right direction so I can do PR?

EDIT
As it turns out, I found previous commit #161 that shows some changes in server.ts, which I believe might be the right place to do the changes I'm proposing.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.