InversifyJS : can not bind using interface - oop

I am trying to use inversify JS to inject dependencies on TypeScript App. I started by using the example of https://github.com/inversify/InversifyJS page :
// file interfaces.ts
interface Warrior {
fight(): string;
}
// file types.ts
const TYPES = {
Warrior: Symbol("Warrior")
};
export { TYPES };
// file entities.ts
import { injectable, inject } from "inversify";
import "reflect-metadata";
import { Warrior } from "./interfaces"
import { TYPES } from "./types";
#injectable()
class WarriorImpl implements Warrior {
public constructor(){
}
public fight() { return "I fight"; }
}
export { WarriorImpl };
// file inversify.config.ts
import { Container } from "inversify";
import TYPES from "./types";
import { Warrior } from "./interfaces";
import { WarriorImpl } from "./entities";
const myContainer = new Container();
myContainer.bind<Warrior>(TYPES.Warrior).to(WarriorImpl);
export { myContainer };
I applied what is provided in the example but Vscode and tsc failed at the binding line by showing this error [ts] Untyped function calls may not accept type arguments. [ts] Cannot find name 'Warrior'.

Your example is clean and correct all you have to do is to upgrade your typescript version in your package.json
"devDependencies": {
"typescript": "3.9.3"
}

You have to export the interface for it to be imported:
// file interfaces.ts
export interface Warrior {
fight(): string;
}
Your import in inversify.config.ts should be:
import { TYPES } from "./types";
Make sure you have actually installed the library (npm install inversify). The error Untyped function calls may not accept type arguments is sue to missing type information.

Related

Installing NestJS custom passport behaves differently

I created a custom passport for authentication, as described here: https://docs.nestjs.com/security/authentication.
My problem is a different behavior between importing the passport I created from a local folder versus installing it from a package. When I install it from a package, I get 500 error when providing wrong credentials (works fine with valid credentials), while getting 401 error when using it locally.
This is how I use it locally and it works:
import { Controller, Get, UseGuards } from '#nestjs/common';
import { AppService } from './app.service';
import { AuthGuard } from '#nestjs/passport';
import { ApiKeyAuthGuard } from './auth/guards/api-key-auth.guard';
#Controller()
#UseGuards(AuthGuard('api-key'))
export class AppController {
constructor(
private readonly appService: AppService,
) {}
#Get()
getHello(){}
}
But when I import it from an installed package and I provide wrong credentials, I'm getting 500 error:
import { ApiKeyAuthGuard } from 'shared-auth-package';
api-key.strategy.js:
import { PassportStrategy } from '#nestjs/passport';
import {
fromAuthHeaderAsApiKey,
Strategy,
} from '../passports/passport-api-key/strategy';
import { InjectRepository } from '#nestjs/typeorm';
import { UnauthorizedException } from '#nestjs/common';
import { Repository } from 'typeorm';
import { TokenEntity } from '../../lib/entity/token.entity';
import { UserEntity } from '../../lib/entity/user.entity';
export class ApiKeyStrategy extends PassportStrategy(Strategy, 'api-key') {
constructor(
#InjectRepository(TokenEntity, process.env.mysql_connection_name)
private tokenRepository: Repository<TokenEntity>,
) {
super({
tokenFunc: fromAuthHeaderAsApiKey(),
passReqToCallback: false,
});
}
async validate(token: string): Promise<UserEntity> {
let user: UserEntity;
const tokenEntity = await this.tokenRepository
.createQueryBuilder('t')
.innerJoinAndSelect('t.user', 'u')
.where('t.token = :token', { token })
.getOne();
if (tokenEntity && tokenEntity.user_id && tokenEntity.validateToken()) {
user = tokenEntity.user;
}
if (!user) {
throw new UnauthorizedException('Invalid credentials');
}
return user;
}
}
api-key-auth.guard.ts:
import { AuthGuard } from '#nestjs/passport';
export class ApiKeyAuthGuard extends AuthGuard('api-key') {}

How to install Express middleware (express-openapi-validator) in NestJS?

I am writing a NestJS application. Now I want to install the Express middleware express-openapi-validator.
However, I can't get it to work. There is a description for how to install the express-openapi-validator in express, but it always results in errors.
For example
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(middleware({apiSpec "./bff-api.yaml"}))
.forRoutes(OrganizationController)
}
}
results in
error TS2345: Argument of type 'OpenApiRequestHandler[]' is not assignable to parameter of type 'Function | Type<any>'.
Type 'OpenApiRequestHandler[]' is missing the following properties from type 'Type<any>': apply, call, bind, prototype, and 4 more.
How can I install this middleware in NestJS?
I added a NestJS example to express-openapi-validator (static link for posterity).
The AppModule looks basically identical, although you don't need to iterate over the middlewares:
#Module({
imports: [PingModule],
providers: [{ provide: APP_FILTER, useClass: OpenApiExceptionFilter }],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(
...OpenApiValidator.middleware({
apiSpec: join(__dirname, './api.yaml'),
}),
)
.forRoutes('*');
}
}
I also added an exception filter to convert the error from express-openapi-validator to a proper response; otherwise I would always get a 500 error. You could also use this approach to convert the error into a custom error format.
import { ArgumentsHost, Catch, ExceptionFilter } from '#nestjs/common';
import { Response } from 'express';
import { error } from 'express-openapi-validator';
#Catch(...Object.values(error))
export class OpenApiExceptionFilter implements ExceptionFilter {
catch(error: ValidationError, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
response.status(error.status).json(error);
}
}
interface ValidationError {
status: number;
message: string;
errors: Array<{
path: string;
message: string;
error_code?: string;
}>;
path?: string;
name: string;
}
I have now got it working:
configure(consumer: MiddlewareConsumer) {
middleware({
apiSpec: `${__dirname}/../api-doc/bff-api.yaml`
}).forEach(value => consumer.apply(value).forRoutes(OrganizationController))
}

How to handle types for injected properties in Vue Composition API - Typescript

import firebase from 'firebase'
import Vue from 'vue'
/* This file simply imports the needed types from firebase and forwards them */
declare module 'vue/types/vue' {
interface Vue {
$fireStore: firebase.firestore.Firestore
$fireDb: firebase.database.Database
$fireFunc: firebase.functions.Functions
$fireStorage: firebase.storage.Storage
$fireAuth: firebase.auth.Auth
$fireMess: firebase.messaging.Messaging
}
}
In normal typescript project with Vue 2, we can do this. But when use Composition API, how I can inject the properties like this in root on function setup(_, { root})?
So I cant use with root.$fireStore...
Now, I must use it with the any type like (root as any).$fireStore. So hope anyone can help my team. We are working on a project with Nuxt Composition now.
it's kind of the same in vue 3 but not the same module
declare module '#vue/runtime-core' {
interface ComponentCustomProperties {
$fireStore: firebase.firestore.Firestore
$fireDb: firebase.database.Database
$fireFunc: firebase.functions.Functions
$fireStorage: firebase.storage.Storage
$fireAuth: firebase.auth.Auth
$fireMess: firebase.messaging.Messaging
}
}
How about creating a composable for firebase?
import firebase from 'firebase'
export default function useFirebase() {
return {
fireStore: firebase.firestore.Firestore
fireDb: firebase.database.Database
fireFunc: firebase.functions.Functions
fireStorage: firebase.storage.Storage
fireAuth: firebase.auth.Auth
fireMess: firebase.messaging.Messaging
};
};
And then use it in your setup() of the component:
export default defineComponent({
setup() {
const {fireStore, fireDb, fireFunc, fireStorage, fireAuth, fireMess} = useFirebase();
}
});
Yes, after a few days, I reached out the temporary solution for this. Just add a nuxt.d.ts or your types folder like this. So you can use $apolloHelpers in your Vue Instances and Middleware...
import { SetupContext } from '#vue/composition-api'
/**
* #description Define type for $apolloHelpers --> copy from Apollo module --> inject Apollo Helpers
* #docs https://github.com/nuxt-community/apollo-module/blob/master/lib/templates/plugin.js#L141
*/
declare module '#nuxt/types' {
interface Context {
$apolloHelpers: {
onLogin(token, apolloClient, cookieAttributes, skipResetStore = false)
onLogout(apolloClient, skipResetStore = false)
getToken(tokenName?: string)
}
}
interface NuxtAppOptions {
$apolloHelpers: {
onLogin(token, apolloClient, cookieAttributes, skipResetStore = false)
onLogout(apolloClient, skipResetStore = false)
getToken(tokenName?: string)
}
}
}
declare module 'vue/types/vue' {
interface Vue {
$apolloHelpers: {
onLogin(token, apolloClient, cookieAttributes, skipResetStore = false)
onLogout(apolloClient, skipResetStore = false)
getToken(tokenName?: string)
}
}
}
I don't know if it will help you, but there is root.$store.$fireAuth
insert #nuxt/types in your tsconfig.json and the types will work

Nuxtjs using Vuex-module-decorator doesn't wordk

I want to use my vuex modules as classes to make my code more clean and readable. I used the section (Accessing modules with NuxtJS) at the bottom of this document: https://github.com/championswimmer/vuex-module-decorators/blob/master/README.md
I've searched for the solution for almost 3 days and tried out this link:
vuex not loading module decorated with vuex-module-decorators
but, it didn't work.
Also, I used getModule directly in the component like the solution in this issue page: https://github.com/championswimmer/vuex-module-decorators/issues/80
import CounterModule from '../store/modules/test_module';
import { getModule } from 'vuex-module-decorators';
let counterModule: CounterModule;
Then
created() {
counterModule = getModule(CounterModule, this.$store);
}
Then, accessing method elsewhere
computed: {
counter() {
return counterModule.getCount
}
}
it didn't work for me!
This is my Module in store folder in Nuxtjs project:
import { ICurrentUser } from '~/models/ICurrentUser'
import { Module, VuexModule, Mutation, MutationAction } from 'vuex-module-decorators'
#Module({ stateFactory: true, namespaced: true, name: 'CurrentUserStore' })
export default class CurrentUser extends VuexModule {
user: ICurrentUser = {
DisplayName: null,
UserId: null,
};
#Mutation
setUser(userInfo: ICurrentUser) {
this.user = userInfo;
}
get userInfo() {
return this.user;
}
}
In index.ts file in sore folder:
import { Store } from 'vuex'
import { getModule } from 'vuex-module-decorators'
import CurrentUser from './currentUser'
let currentUserStore: CurrentUser
const initializer = (store: Store<any>): void => {
debugger
currentUserStore = getModule(CurrentUser, store)
}
export const plugins = [initializer]
export {
currentUserStore,
}
I think the problem stems from this line:
currentUserStore = getModule(CurrentUser, store)
currentUserStore is created as object but properties and methods are not recognizable.
when I want to use getters or mutation I get error. For instance, "unknown mutation type" for using mutation
Probably several months late but I struggled with a similar issue, and eventually found the solution in https://github.com/championswimmer/vuex-module-decorators/issues/179
It talks about multiple requirements (which are summarised elsewhere)
The one that relates to this issue is that the file name of the module has to match the name you specify in the #Module definition.
In your case, if you rename your file from currentUser to CurrentUserStore' or change the name of the module toCurrentUser`, it should fix the issue.

How to maintain state in view models between routes in Aurelia?

I have a Menu and Search module. When I navigate between Menu and Search, I want to preserve my search results and Search.js state. I want modules to load via the router like a desktop application where state is maintained between module 'windows'.
App.html
<template>
<router-view></router-view>
</template>
Search.js
import {inject} from "aurelia-framework";
import {PortalData} from "./portalData";
import $ from 'jquery';
#inject(PortalData)
export class Search
{
constructor(portalData){
this.portalData = portalData;
this.criteria = "";
this.SearchResults = [];
}
DoSearch(startRow){
this.portalData.searchSevadars(criteria)
.then(res=> this.SearchResults = res;
}
}
Menu.js
import {inject} from "aurelia-framework";
import {PortalData} from "./portalData";
#inject(PortalData)
export class Start {
constructor(portalData){
this.portalData = portalData;
}
activate(){
return this.portalData.getApplications()
.then(apps => this.applications = apps);
}
The most obvious solution is to store state in another module.
Import some class in both views, then on search store it inside a property of that class.
By default aurelia uses singleton for injected classes, so you will have a shared instance between your views.
somestate.js
export class SomeState {
constructor(){
this.data = undefined;
}
}
import this module in both.
use data property to share data between modules;
Create a file called core.js (or something else of your choosing) in the root application folder /src with something like the following. I added some extra things here to make it more realistic but you could simplify it to meet your specific needs. My point is that this singular class could be used for a LOT of different things -- search text being just one of them.
/src/core.js
// Some example imports to support the common class
import { inject, noView } from 'aurelia-framework';
import { HttpClient, json } from 'aurelia-fetch-client';
import { I18N } from 'aurelia-i18n';
import { EventAggregator } from 'aurelia-event-aggregator';
#noView // this decorator is needed since there is no core.html
#inject(EventAggregator, I18N, HttpClient)
export class Core {
value1 = "Test data 1";
value2 = "Test data 2";
constructor(eventAggregator, i18n, httpClient) {
// store local handles
this.eventAggregator = eventAggregator;
this.i18n = i18n;
this.httpClient = httpClient;
// search info
this.SearchResults = [];
}
myCustomFunction() {
// some code here, available to any component that has core.js injected
}
}
Then, import and inject core.js into each of your other components, like this:
search.js
import {inject} from "aurelia-framework";
import {PortalData} from "./portalData";
import {Core} from "core";
import $ from 'jquery';
#inject(PortalData, Core)
export class Search
{
constructor(portalData, core){
this.portalData = portalData;
this.core = core;
this.criteria = "";
}
DoSearch(startRow){
this.portalData.searchSevadars(criteria)
.then(res=> this.core.SearchResults = res;
}
}