Testing Angular2 with Jasmine. Class property not being set - testing

I am currently writing tests for my Angular2 (with Typescript) application and all has been fine and dandy so far, that is until I have attempted to start testing one of my services.
This service has the Angular2 Http module injected on instantiation as shown below:
import { Injectable, EventEmitter } from 'angular2/core';
import { Http } from 'angular2/http';
import 'rxjs/add/operator/map';
import { ConfigObject } from '../ConfigObject';
import { HTTPHelper } from '../helpers/HTTPHelper';
import { Category } from '../classes/Category';
#Injectable()
export class CategoryService {
public emitter: EventEmitter<Category>;
constructor(private _http: Http) {
this.emitter = new EventEmitter();
}
private APIUrl = ConfigObject.productBox + ConfigObject.apiVersion + 'category';
getCategories(filters) {
return this._http.get(this.APIUrl + HTTPHelper.convertVarsToString(filters))
.map(res => res.json());
}
public emitCat(category): void {
this.emitter.emit(category);
}
}
This is then used to make GET requests to an API box I have created.
Here is my Jasmine test spec file for the service:
import { CategoryService } from '../../services/category.service';
import { Http } from 'angular2/http';
describe('Category service', () => {
let testCategoryService: CategoryService;
let _http: Http;
beforeEach(function() {
testCategoryService = new CategoryService(Http);
});
it('should have emitter name set', () => {
expect(testCategoryService.emitter._isScalar).toBeDefined();
});
it('should return categories', () => {
testCategoryService.getCategories({
order : 'asc',
order_by : 'name',
parent_id : 0,
});
});
});
As you can see, am including the Http object here too and injecting it into the test instantiation of my service class before each test on this line:
beforeEach(function() {
testCategoryService = new CategoryService(Http);
});
When I try and test the 'getCategories' function on my service class I get the following error:
TypeError: this._http.get is not a function
Which is odd as as far as I am concerned I am injecting the Http service into my test instantiation on the line above so this should be set in the class constructor?
Can anyone see why the Http object in my class is not being set?
Thanks

Related

How can I use type-graphql and RESTDataSource

I wonder how it is possible to use RESTDataSource in type-graphql and thus cache correctly in a redis. I would be grateful for a small example.
At the moment I use the DI container to get a service, which is extended from the RestDataSource class, but this is not the right way.
BookmarkResolver.ts
import { Resolver, FieldResolver, Root, Query, Ctx, Authorized } from 'type-graphql';
import { DealService } from '../service/DealService';
import { AvailableLocale } from '../enum/AvailableLocale';
import { Bookmark } from '../entity/Bookmark';
#Resolver(_of => Bookmark)
export class BookmarkResolver {
constructor(private dealService: DealService) {}
#FieldResolver()
async wordpressDeal(#Root() bookmark: Bookmark) {
return await this.dealService.getDealById(bookmark.item_id, AvailableLocale.STAGING);
}
}
DealService.ts
import { Service } from 'typedi';
import { AbstractService } from './AbstractService';
import { AvailableLocale } from '../enum/AvailableLocale';
#Service()
export class DealService extends AbstractService {
baseURL = process.env.DEAL_SERVICE_URL;
async getDealById(dealId: string | number, locale: AvailableLocale) {
const response = await this.get(
'deals/' + dealId,
{ locale }
);
return this.dealReducer(response);
}
dealReducer(deal: any) {
return {
id: deal.id || 0,
title: deal.title
};
}
}
AbstractService.ts
import { RESTDataSource, HTTPCache } from 'apollo-datasource-rest';
import { Service } from 'typedi';
#Service()
export class AbstractService extends RESTDataSource {
constructor() {
super();
this.httpCache = new HTTPCache();
}
}
Share the RESTDataSource via ApolloServer's context. Use it in the resolver by accessing the context with the #Ctx() decorator.
1. Define a RESTDataSource
Define the data source according to the apollo-datasource-rest example.
export class TodoDataSource extends RESTDataSource {
constructor() {
super();
this.baseURL = "https://jsonplaceholder.typicode.com/todos";
}
async getTodos(): Promise<Todo[]> {
return this.get("/");
}
}
2. Create an instance of the DataSource and put it in the Context
When you start the server, add data sources to the context by defining a function that creates the data sources.
const server = new ApolloServer({
schema,
playground: true,
dataSources: () => ({
todoDataSource: new TodoDataSource(),
}),
});
3. Access the DataSource in the resolver
Use the #Ctx() decorator to access the context in the resolver so you can use the data source.
#Resolver(Todo)
export class TodoResolver {
#Query(() => [Todo])
async todos(#Ctx() context: Context) {
return context.dataSources.todoDataSource.getTodos();
}
}
Full, runnable example at https://github.com/lauriharpf/type-graphql-restdatasource

How to call a http post method from a service in a parent director

My http method returns results when it is contained in my component, but does not return any results when called from a service located one directory up.
I've checked the console and there are no errors. I have tried printing to the console, which works from within the service (returns the desired data), but does not when run from within the child component.
This is the service that I'm trying to build:
import { Injectable } from '#angular/core';
import { Resturant } from '../../models/resturant.model'
import { HttpClient } from '#angular/common/http';
import { map } from 'rxjs/operators';
#Injectable({
providedIn: 'root'
})
export class GetResturantsService {
fullListresturants: Resturant[];
constructor(private http:HttpClient) { }
fetchList(){
this.http.get('https://lunchlads.firebaseio.com/posts.json')
.pipe(map(responseData =>{
const postsArray: Resturant[] = [];
for (const key in responseData) {
if (responseData.hasOwnProperty(key)){
postsArray.push({ ...responseData[key], id:key })
}
}
return postsArray;
}))
.subscribe(posts => {
// this.fullListresturants = posts;
});
}
}
This is the component which is one file down in the directory:
import { Component, OnInit } from '#angular/core';
import { Resturant } from '../../../models/resturant.model'
import { GetResturantsService } from '../get-resturants.service'
import { HttpClient } from '#angular/common/http';
//import { map } from 'rxjs/operators';
#Component({
selector: 'app-list-all',
templateUrl: './list-all.component.html',
styleUrls: ['./list-all.component.css']
})
export class ListAllComponent implements OnInit {
fullListresturants: Resturant;
constructor(private http:HttpClient, private listAllResturants:GetResturantsService) { }
ngOnInit() {
this.onfullList();
}
onfullList(){
this.fullList();
}
private fullList(){
// this.http.get('https://lunchlads.firebaseio.com/posts.json')
// .pipe(map(responseData =>{
// const postsArray: Resturant[] = [];
// for (const key in responseData) {
// if (responseData.hasOwnProperty(key)){
// postsArray.push({ ...responseData[key], id:key })
// }
// }
// return postsArray;
// }))
// .subscribe(posts => {
// // this.fullListresturants = posts;
// });
this.listAllResturants.fetchList();
}
}
The firebase backend contains roughly 10 records with a name:string, votes:number, and selected:number fields. When run from the component, the html file simply returns the name values with an *ngFor loop.
When run from the service, nothing is returned and no errors are reported in the console.
I suspect the problem lies somewhere in how I am calling the fetchList method from the component, but google and me have not been able to suss out what I'm doing wrong.
Your service should return an observable to make it work. As per your current code, you are not returning anything from GetResturantsService.fetchList(). To make it work let change the service like this:
export class GetResturantsService {
fullListresturants: Resturant[];
constructor(private http:HttpClient) { }
fetchList(){
return this.http.get('https://lunchlads.firebaseio.com/posts.json')
.pipe(map(responseData =>{
const postsArray: Resturant[] = [];
for (const key in responseData) {
if (responseData.hasOwnProperty(key)){
postsArray.push({ ...responseData[key], id:key })
}
}
return postsArray;
}));
}
}
Now in component subscribe to the observable returned from fetchList method like this:
export class ListAllComponent implements OnInit {
fullListresturants: Resturant;
constructor(private http:HttpClient, private listAllResturants:GetResturantsService) { }
ngOnInit() {
this.onfullList();
}
onfullList(){
this.fullList();
}
private fullList(){
this.listAllResturants.fetchList()
.subscribe(posts => {
//DO whatever you want to do with posts
this.fullListresturants = posts;
});
}
}
Hope it helps.

Dependencies not being injected on NestJS with Serverless and AWS

Everyone,
I'm trying to setup my first NestJS application. It is backed by Serverless on AWS.
I created a simple Controller that has a Service as a dependency. When I hit the endpoint with my HTTP Client, the object that should contain the Service instance is undefined. I'm not able to make it work. Could you help?
handler.ts
import { Context, Handler } from 'aws-lambda';
import { NestFactory } from '#nestjs/core';
import { AppModule } from './src/module';
import { Server } from 'http';
import { ExpressAdapter } from '#nestjs/platform-express';
import * as serverless from 'aws-serverless-express';
import * as express from 'express';
import {DB} from './src/libs/db';
let cachedServer: Server;
function bootstrapServer(): Promise<Server> {
const expressApp = express();
const adapter = new ExpressAdapter(expressApp);
return NestFactory.create(AppModule, adapter)
.then(app => app.enableCors())
.then(app => app.init())
.then(() => DB.connect())
.then(() => serverless.createServer(expressApp));
}
export const handle: Handler = (event: any, context: Context) => {
if (!cachedServer) {
bootstrapServer().then(server => {
cachedServer = server;
return serverless.proxy(server, event, context);
});
} else {
return serverless.proxy(cachedServer, event, context);
}
};
module.ts
import { Module } from '#nestjs/common';
import { EventController } from './event.controller';
import { EventService } from './event.service';
#Module({
controllers: [EventController],
providers: [EventService],
})
export class AppModule {}
event.service.ts
import { Injectable } from '#nestjs/common';
interface Event{}
#Injectable()
export class EventService {
create(event: Event) {
return {
id: Date.now()
}
}
}
event.controller.ts
import { Controller, Post, Body } from '#nestjs/common';
import { EventService } from './event.service';
interface Event { }
#Controller('event')
export class EventController {
constructor(private readonly eventService: EventService) { }
#Post()
async create(#Body() req)
{
this.eventService.create(req);
}
}
So this.eventService is always undefined. What is wrong with this implementation?
Maybe you are missing a line from tsconfig add this below:
"emitDecoratorMetadata": true
Credits to the God of Nestjs Mr. Kamil's reply:
https://stackoverflow.com/a/50121886/6301493

Applying Middleware-like mechanism to Resolvers' Queries and Mutations

I'm building a GraphQL API using Nest framework and I'm trying to implement 3rd party express middlewares (express-rate-limit and express-slow-down) into some queries and mutations.
The problem is all graphql mutations and queries use the same endpoint, so I can't explicitly tell to which query or mutations shall the middleware be applied, because you can only do that using route's path (which is the same across the API).
import { Module, NestModule, MiddlewareConsumer, RequestMethod } from '#nestjs/common'
import * as rateLimit from 'express-rate-limit'
import * as RedisStore from 'rate-limit-redis'
import { RedisClient } from 'redis'
#Module({
providers: [],
exports: [],
})
export default class SecurityModule implements NestModule
{
constructor(protected readonly redisClient: RedisClient)
{
}
configure(consumer: MiddlewareConsumer)
{
consumer.apply(
new rateLimit({
max: 300,
windowMs: 15 * 60 * 1000,
store: new RedisStore({ client: this.redisClient }),
})).forRoutes({ path: '/graphql', method: RequestMethod.ALL }) // this would apply the middleware to all queries and mutations
}
}
So I tried using both guards and interceptors for that purpose, but failed miserably.
It's a fail for an obvious reason.
The Error: Can't set headers after they are sent is thrown.
/* !!! My Interceptor would like quite identical */
import { ExecutionContext, Injectable, CanActivate } from '#nestjs/common'
import * as speedLimit from 'express-slow-down'
import { Request, Response } from 'express'
#Injectable()
export default class SpeedLimitGuard implements CanActivate
{
constructor(
protected readonly options: speedLimit.Options,
) {
}
async canActivate(context: ExecutionContext): Promise<boolean> {
const { req, res }: { req: Request, res: Response } = context.getArgs()[2]
speedLimit({ ...this.options })(req, res, req.next)
return true
}
}
import { NestInterceptor, ExecutionContext, Injectable, INestApplication, INestExpressApplication } from '#nestjs/common'
import { Observable } from 'rxjs'
import * as speedLimit from 'express-slow-down'
// import { Request, Response } from 'express'
import { ApplicationReferenceHost } from '#nestjs/core'
import { RedisClient } from 'redis'
import * as RedisStore from 'rate-limit-redis'
#Injectable()
export default class SpeedLimitInterceptor implements NestInterceptor
{
constructor(private readonly appRefHost: ApplicationReferenceHost,
private readonly redisClient: RedisClient, )
{}
intercept<T>(context: ExecutionContext, call$: Observable<T>): Observable<T>
{
// const { req: request, res: response }: { req: Request, res: Response } = context.getArgs()[2]
const httpServer = this.appRefHost.applicationRef
const app: INestApplication & INestExpressApplication = httpServer.getInstance()
app.use(speedLimit({
delayAfter: 1,
store: new RedisStore({
prefix: 'test_',
client: this.redisClient,
}),
}))
app.use((req, res, next) => {
console.log('is middleware triggered', { req, res })
next()
})
return call$
}
}
Is there any way to apply a 3rd party express middleware to a GraphQL Mutation/Query explicitly?
So from the bottom, guards are working, because I'm the living human bean that can prove it:
#Query('getHome')
#UseGuards(GraphqlGuard)
async findOneById(#Args('id') id: string): Promise<HomeEntity> {
return await this.homeService.findOneById(id);
}
and it's just working.
This is GraphqlGuard.ts
import {ExecutionContext, Injectable} from '#nestjs/common';
import {GqlExecutionContext} from '#nestjs/graphql';
import {AuthGuard} from '#nestjs/passport';
import {ExecutionContextHost} from '#nestjs/core/helpers/execution-context.host';
import {Observable} from 'rxjs';
#Injectable()
export class GraphqlGuard extends AuthGuard('jwt') {
canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
const ctx = GqlExecutionContext.create(context);
const {req} = ctx.getContext();
return super.canActivate(new ExecutionContextHost([req]));
}
}
But to live with context, you have to make it works for you, so, wherever you're passing graphql config, there is an context callback, and for me it looks like this:
context: (context) => {
let req = context.req;
if (context.connection) {
req = context.connection.context.req;
}
return {req};
}
I'm checking here connection for context from websocket. Im using global interceptors so, they're working like a charm. But you still can use #UseInterceptors(SomeInterceptor) decorator and it also works. And btw Middlewares, at the end, I doesn't need any of them guards, pipes, validators and interceptors for me was quite enough.
Regards.

Interceptor code not getting called when using http.post

I have written (copied from SO!) the following interceptor code. I want to modify the outgoing request and also intercept the response. However, I have noticed that the response never gets intercepted. Why? Is it because I am using Rxjs?
Interceptor code
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse} from "#angular/common/http";
import {Injectable} from "#angular/core";
import {Observable} from "rxjs/Observable";
import 'rxjs/add/operator/do';
#Injectable()
export class CustomInterceptor implements HttpInterceptor {
constructor() {
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
console.log("outgoing request",request);
request = request.clone({
withCredentials: true
});
console.log("new outgoing request",request);
return next
.handle(request)
.do((ev: HttpEvent<any>) => {
if (ev instanceof HttpResponse) {
console.log('processing response', ev); //I DON'T SEE THIS PRINT
}
});
}
}
I am sending the request to the server as follows
return this.http.post(/*this.API_URL+*/this.SIGNIN_USER_URL,body,httpOptions)
.map(response=>{
console.log('response from backend service',response); //I SEE THIS PRINT
let result= <ServerResponse>response;
console.log("result is "+result.result+' with additional information '+result.additionalInformation)
return result;
})
.catch(this.handleError);
Why is the interceptor code not getting hit?
Update - the provider code snippet in app.module.ts
providers: [WebToBackendInterfaceService, //some other provider
{
provide: HTTP_INTERCEPTORS,
useClass: CustomInterceptor , //interceptor provider
multi: true
}],
I added a print between do and if (console.log("got an event",ev). I can see the following message on console `got an event
{…}
​body: Object { result: "success", "additional-info": "found user" }
​headers: {…}
​lazyInit: function lazyInit()
​​lazyUpdate: null
​​normalizedNames: Map
​​​size: 0
​​​<entries>
​​​__proto__: Object { … }
​​__proto__: Object { has: has(), get: get(), keys: keys(), … }
​ok: true
​status: 200
​statusText: "OK"
​type: 4
​url: "http://localhost:9000/ws/users/signin"
​__proto__: Object { constructor: HttpResponse(), clone: clone() }`
I dont know why the if statement isn't getting executed as it seems the event is of type HttpResponse (referring to ​__proto__: Object { constructor: HttpResponse(), clone: clone() })