NESTJS - How to send an object with an array and an object - javascript

i'm having a trouble with nest. What I want is a little bit difficult, and i don't know how to do that. First, with the ID of a site, I retrieve the users from this site and i want to be able to make a pagination, a sort by order (desc or asc i don't care) and to filtrate the results by value (a string). And in the output, i want to make an object with an array of the results and the synthesis. Example :
{
results : [{audit}],
syhthesis: {pageNumber: number, numberOfResults: number}
}
Honnestly, i've been trying for a while but i just cant understand how to do it. Here is my actual code :
the controller :
import { Controller, Get, Query, Param, Post, Body } from '#nestjs/common';
import { UserAuditService } from './user-audit.service';
import { UserAudit } from 'src/entities/user-audit.entity';
#Controller('useraudit')
export class UserAuditController {
constructor(private readonly userAuditService : UserAuditService){};
#Post("/userpersite/{:id}")
async getUsers(#Body()id: string, #Query('page') page: number): Promise<UserAudit[]>{
return this.userAuditService.getAuditsForSite(id, page)
}
}
the service :
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { UserAudit } from '../entities/user-audit.entity';
import { Repository } from 'typeorm';
#Injectable()
export class UserAuditService {
constructor(
#InjectRepository(UserAudit)
private readonly userAuditRepository : Repository<UserAudit>
){}
async getAuditsForSite(_siteId : string, page: number = 1) : Promise<UserAudit[]>{
return this.userAuditRepository
.find({
join : {
alias : "user-audit",
innerJoinAndSelect: {
user : "user-audit.who"
}
},
where : {
site : _siteId
},
take: 10,
skip: 10 * (page -1)
})
}
}
and the entity :
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
import { User } from './user.entity';
import { Site } from './site.entity';
#Entity('user-audit')
export class UserAudit {
#PrimaryGeneratedColumn()
id : string;
#ManyToOne(type => User, user => user.id)
who : User
#Column({ length : 100 })
action : string
#ManyToOne(type => Site, site => site.id)
site : Site
#Column({ type : 'date' })
date : Date
#Column({ length : 1000 })
before : string
#Column({ length : 1000 })
after : string
}
I have try many things in my controller, but now, i am stuck, i know i am missing something, perhaps a lot of things, so if someone can help me, it will be very thankful :)

In the UserAuditService you can use findAndCount instead of find. It will return an array with 2 elements. The first element will be the entities and the second will be the total count. Then you need to make the appropriate response object:
async getUsers(#Body()id: string, #Query('page') page: number): Promise<object>{
const [audits, total] = await this.userAuditService.getAuditsForSite(id, page)
return {
results : audits,
syhthesis: {
pageNumber: page,
numberOfResults: total,
}
}

sorry to bother you again. So i've tried it, and it doesn't work :/ I was certain this to be the solution. So I thought of something : to work with models, but again, i am stuck. Here is the new code :
the service :
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { UserAudit } from '../entities/user-audit.entity';
import { Repository } from 'typeorm';
import { UserAuditRequest } from '../entities/user-audit-request';
#Injectable()
export class UserAuditService {
constructor(
#InjectRepository(UserAudit)
private readonly userAuditRepository : Repository<UserAudit>,
private readonly userRequest: Repository<UserAuditRequest>
){}
async getAuditsForSite(_siteId : string, page: number = 1, filter: any) : Promise<any>{
const joinAndSort = this.userAuditRepository
.findAndCount({
join : {
alias : "user-audit",
innerJoinAndSelect: {
user : "user-audit.who"
}
},
where : {
site : _siteId
},
take: 10,
skip: 10 * (page -1)
})
joinAndSort
const sortElement = this.userRequest.find({
where: {
pagination: page
}
})
sortElement
const filterElement = this.userRequest.findAndCount({
where: {
filter: filter
}
})
filterElement
}
}
the controller :
import { Controller, Get, Query, Param, Post, Body } from '#nestjs/common';
import { UserAuditService } from './user-audit.service';
import { UserAudit } from 'src/entities/user-audit.entity';
import { UserAuditRequest } from '../entities/user-audit-request';
import { ResultUserAudit } from '../entities/result-user-audit';
import { Any } from 'typeorm';
#Controller('useraudit')
export class UserAuditController {
constructor(private readonly userAuditService : UserAuditService, private readonly resultUserAudit: ResultUserAudit){};
#Post("/userpersite/:id")
async postUserPerSite(#Param()id: string,#Body() request : UserAuditRequest): Promise<ResultUserAudit>{
return await this.userAuditService.getAuditsForSite(id, request.pagination.pageNumber, request.filter);
the request model :
import { Injectable } from "#nestjs/common";
#Injectable()
export class UserAuditRequest {
constructor(){}
pagination: {
resultNumber: number;
pageNumber: number;
}
sort: {
columnSorted: any;
orderBy: any;
}
filter: {
columnFilter: any;
filtervalue: string;
}
}
and the result model :
import { Injectable } from "#nestjs/common";
import { UserAudit } from "./user-audit.entity";
#Injectable()
export class ResultUserAudit {
constructor(private userAudit: UserAudit){}
result: UserAudit[];
synthesis: {
pageNumber: number,
resultNumber: number;
}
}
I am trying to get the logic for my code, but i don't know, i just can't succed :( I see what i want to do as explain in my first post (hopefully) but i am not able to do it. I hope someone will be able to help and explain. Thank you :)

Related

Why is my Angular Service not assigning a filtered result to my property?

I setup a service called 'bankService' which is being used by my 'user' component. The 'user' component is receiving the data from the service correctly but I am unable to assign a filtered result to my 'currentAccount' property. I am filtering by 'id' from the list of 'accounts' that is being returned from my service. Any help with an explanation would be appreciated!
Model
export interface Account {
id: number;
accountHolder: string;
checking: number;
savings: number;
}
Service
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { elementAt, Observable } from 'rxjs';
import { Account } from '../models/Account';
import { Transaction } from '../models/Transaction';
#Injectable({
providedIn: 'root',
})
export class BankServiceService {
private apiUrl = 'http://localhost:5000/accounts';
constructor(private http: HttpClient) {}
getAccounts(): Observable<Account[]> {
return this.http.get<Account[]>(this.apiUrl);
}
}
Component
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { Account } from 'src/app/models/Account';
import { BankServiceService } from 'src/app/services/bank-service.service';
#Component({
selector: 'app-user',
templateUrl: './user.component.html',
styleUrls: ['./user.component.css'],
})
export class UserComponent implements OnInit {
currentAccount: Account[] = [];
accountId: number;
accountHolder: string;
checkingAmount: number;
savingsAmount: number;
constructor(
private route: ActivatedRoute,
private bankService: BankServiceService
) {}
ngOnInit(): void {
// gets the parameter for the url (the param is the account id)
this.accountId = this.route.snapshot.params['id'];
console.log('id: ', this.accountId);
// pulls in all accounts
this.bankService
.getAccounts()
.subscribe(
(accounts) =>
(this.currentAccount = accounts.filter(
(account) => account.id === this.accountId
))
);
console.log('current account: ', this.currentAccount);
}
}
If I'm not mistaking, your issue is this one:
the account id received from the backend is a number
the account id pulled from the url is a string
In the filter function you are using strict equality, and that's why no account id passes the condition in the filter callback
You can switch from strict equality to loose equality (==) or do something like this for the filter callback:
(account) => account.id.toString() === this.accountId

NestJS/TypeORM: Cannot read property 'createQueryBuilder' of undefined

Calling 'localhost:3000/contacts' (with or without parameters) at postman returns me this error and i don't know why. My backend is connected to a PostgreSQL db.
TypeError: Cannot read property 'createQueryBuilder' of undefined
at ContactsRepository.Repository.createQueryBuilder (...\Documents\Visual Studio Code Projects\funds-backend-nestjs\node_modules\typeorm\repository\Repository.js:17:29)
at ContactsRepository.getContacts (...\Documents\Visual Studio Code Projects\funds-backend-nestjs\dist\contacts\contacts.repository.js:17:34)
at ContactsService.getContacts (...\Documents\Visual Studio Code Projects\funds-backend-nestjs\dist\contacts\contacts.service.js:24:39)
at ContactsController.getContacts (...\Documents\Visual Studio Code Projects\funds-backend-nestjs\dist\contacts\contacts.controller.js:25:37)
at ...\Documents\Visual Studio Code Projects\funds-backend-nestjs\node_modules\#nestjs\core\router\router-execution-context.js:38:29
at processTicksAndRejections (internal/process/task_queues.js:93:5)
at async ...\Documents\Visual Studio Code Projects\funds-backend-nestjs\node_modules\#nestjs\core\router\router-execution-context.js:46:28
at async ...\Documents\Visual Studio Code Projects\funds-backend-nestjs\node_modules\#nestjs\core\router\router-proxy.js:9:17
My code looks like this:
#EntityRepository(Contact)
export class ContactsRepository extends Repository<Contact> {
async getContacts(filterDto: GetContactsFilterDto): Promise<Contact[]> {
const { name, search } = filterDto;
// const query = this.createQueryBuilder('contacts');
const query = await this.createQueryBuilder()
.select('contacts')
.from(Contact, 'contacts');
if (name) {
query.andWhere('contacts.name = :name', { name });
}
if (search) {
query.andWhere(
'(contacts.email LIKE :search OR contacts.telephone LIKE :search)',
{ search: `%${search}%` },
);
}
const contacts = await query.getMany();
return contacts;
}
import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
#Entity({ name: 'contacts' })
export class Contact extends BaseEntity {
#PrimaryGeneratedColumn('uuid')
id: string;
#Column()
email: string;
#Column()
html_de: string;
#Column()
html_en: string;
#Column()
name: string;
#Column()
telephone: string;
}
export class ContactsController {
constructor(private contactsService: ContactsService) {}
#Get()
getContacts(
#Query(ValidationPipe) filterDto: GetContactsFilterDto,
): Promise<ContactDto[]> {
return this.contactsService.getContacts(filterDto);
}
#Injectable()
export class ContactsService {
constructor(
#InjectRepository(ContactsRepository)
private contactsRepository: ContactsRepository,
) {}
async getContacts(filterDto: GetContactsFilterDto): Promise<Contact[]> {
return this.contactsRepository.getContacts(filterDto);
}
import { Module } from '#nestjs/common';
import { TypeOrmModule } from '#nestjs/typeorm';
import { ContactsController } from './contacts.controller';
import { ContactsRepository } from './contacts.repository';
import { ContactsService } from './contacts.service';
#Module({
controllers: [ContactsController],
imports: [TypeOrmModule.forFeature([ContactsRepository])],
providers: [ContactsRepository, ContactsService],
exports: [ContactsRepository, ContactsService],
})
export class ContactsModule {}
Somebody know how i can fix this? Regards
ContactsRepository should only be used in the TypeOrmModule.forFeature() and not added to the providers or exports array. When it is added here, the injection token for ContactsRepository no longer points to the proper instance and Nest creates the class, but doesn't have it properly extend Repository as that code is all managed by TypeORM

NestJS, TypeORM, can't fetch data

I am trying to get started with NestJS which uses TypeORM.
I have connected to a database. At least I think I have, because I was getting a lot of errors and after enough tuning of the config, the errors went away and the connection seems to be successful.
So now I want to fetch any bit of data just to get started.
There is a table in the database called RESULT_PAGE, so I just want to fetch any record from that. This is what I have tried:
result-page.entity.ts
import { Entity, PrimaryColumn, Column } from "typeorm";
#Entity()
export class ResultPage {
#PrimaryColumn()
result_page_id: number;
#Column({ length: 1 })
approval: string;
#Column({ length: 1})
manually_uploaded: string;
}
result-page.controller.ts
import { Controller, Get, Request } from '#nestjs/common';
import { ResultPageService } from './result-page.service';
#Controller('result-page')
export class ResultPageController {
constructor(
private resultPageService: ResultPageService
) { }
#Get('get-all')
getProfile() {
return this.resultPageService.findAll();
}
}
result-page.service.ts
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { Repository } from 'typeorm';
import { ResultPage } from './result-page.entity';
#Injectable()
export class ResultPageService {
constructor(
#InjectRepository(ResultPage)
private readonly resultPageRepository: Repository<ResultPage>,
) {}
findAll(): Promise<ResultPage[]> {
return this.resultPageRepository.find();
}
}
If I edit the service to look like this:
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { Repository } from 'typeorm';
import { ResultPage } from './result-page.entity';
#Injectable()
export class ResultPageService {
constructor(
#InjectRepository(ResultPage)
private readonly resultPageRepository: Repository<ResultPage>,
) {}
findAll(): Promise<string> {
return new Promise((resolve, reject) => { resolve('hello world') })
// return this.resultPageRepository.find();
}
}
then I get 'hello world', so it is definitely that the RESULT_PAGE table isn't connected
In the AppModule I am loading the entities like this
const typeOrmModuleOptions: TypeOrmModuleOptions = {
...
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true
}
I am sure that I am making some absolute noob mistake so if someone could help me out here it would be greatly appreciated. I am quite new to databases and api's so any info would help a lot. TIA
SOLVED
Solved by adding a connectString https://github.com/typeorm/typeorm/issues/3484#issuecomment-472315370
A little bit confusing the question. I miss the error and the database config.
result_page vs RESULT_PAGE: tablename
on linux/unix the tablenames are case sensitive, so you should set it in the annotation
#Entity({name: 'RESULT_PAGE'})
Please give some details to find the root cause if it was not that.

Restricting List of Items by User Account ID

Using Angular 4, Ngrx Store & AngularFire2
I am having real problems understanding how I can restrict a list of items from Firebase based on the currently logged in user account id.
I am using ngrx as well including ngrx effects.
The steps I need to follow are:
• Get Current Users UID – Auth Object
• Get User Object based on the UID from step above
• Get Company List based on the User Account ID Above
My problem is that because I am calling firebase as an Observable the call to company list is being made before I complete the first two steps.
The code is as per below, if someone can assist that would be appreciated:
The problem is in the getEntityList Method on the generic firebase service - I have marked where the problem is
Company Component
import { Component, OnInit, ChangeDetectionStrategy } from '#angular/core';
import { Observable } from 'rxjs/Rx';
import { Store } from '#ngrx/store';
import { AppState } from './../../../core/models/index';
import { CompanyModel } from './../../../core/models/index';
import { getCompanies} from './../../../core/store/actions/company.actions';
#Component({
selector: 'mj-company',
templateUrl: './company.component.html',
styleUrls: ['./company.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class CompanyComponent implements OnInit {
entityList$: Observable<CompanyModel[]>;
constructor(private store: Store<AppState>) {
this.entityList$ = this.store.select(state => state.companies);
}
ngOnInit() { this.store.dispatch(getCompanies()); }
}
Company Actions
import { CompanyModel } from './../../models';
import { Action } from '#ngrx/store';
export const ActionTypes = {
GET_COMPANIES: 'GET_COMPANIES',
GET_COMPANIES_SUCCESS: 'GET_COMPANIES_SUCCESS',
GET_COMPANIES_ERROR: 'GET_COMPANIES_ERROR'
};
export function getCompanies() {
return {
type: ActionTypes.GET_COMPANIES,
entityRef: 'companys'
}
}
}
Company Reducer
import { ActionReducer, Action } from '#ngrx/store';
import { ActionTypes } from '../actions/company.actions';
import { CompanyModel } from '../../models';
export function companyReducer(state = [<CompanyModel>{}], action: Action) {
switch (action.type) {
case ActionTypes.GET_COMPANIES:
return action.payload;
case ActionTypes.GET_COMPANIES_SUCCESS:
return action.payload;
case ActionTypes.GET_COMPANIES_ERROR:
return action.payload;
default:
return state;
}
};
Company Effect
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs/Rx';
import { ActionTypes } from '../actions/company.actions';
import { Actions, Effect } from '#ngrx/effects';
import { FirebaseDataService } from './../../services/firebase-data.service';
#Injectable()
export class CompanyEffects {
constructor(
private actions$: Actions,
private firebaseDataService: FirebaseDataService
) { }
// tslint:disable-next-line:member-ordering
#Effect() getCompanies$ = this.actions$
.ofType(ActionTypes.GET_COMPANIES)
.switchMap(action =>
this.firebaseDataService.getEntityList(action.entityRef)
.map(companies => ({ type: ActionTypes.GET_COMPANIES_SUCCESS, payload: companies }))
.catch(() => Observable.of({ type: ActionTypes.GET_COMPANIES_ERROR })));
Firebase Generic Data Service
import { Injectable } from '#angular/core';
import { AngularFireDatabase, FirebaseListObservable, FirebaseObjectObservable } from 'angularfire2/database';
import { Observable } from 'rxjs/Rx';
import { AuthService } from './auth.service';
import { FirebaseUtilityService } from './../../core/services/firebase-utility.service';
import { UserModel, CompanyModel } from './../models/index';
#Injectable()
export class FirebaseDataService {
$key: string;
loginId: string;
currentUser: any;
constructor(private db: AngularFireDatabase,
private authService: AuthService,
private firebaseService: FirebaseUtilityService) { }
// Return an observable list with optional query
getEntityList(firebaseRef: string, query = {}): FirebaseListObservable<any[]> {
this.loginId = this.authService.currentUserId;
// I get this instantly which is good
console.log('logId: ', this.loginId);
this.currentUser = this.db.object('users/' + this.loginId);
console.log('accountId: ', this.currentUser.accountId);
// This is where the problem is because at this stage the subscription above is not complete so accountId is undefined.
return this.db.list(firebaseRef, {
query: {
orderByChild: 'accountId',
equalTo: this.currentUser.accountId
}
});
// return this.db.list(firebaseRef, query);
}
// Return a single observable item
getEntity(firebaseRef: string, key: string): FirebaseObjectObservable<any> {
const itemPath = `${firebaseRef}/${key}`;
return this.db.object(itemPath)
}
// Default error handling for all actions
private handleError(error) {
console.log(error)
}
}
Problem solved - using switchMap
return this.currentUser.switchMap(user => {
return this.db.list(firebaseRef, {
query: {
orderByChild: 'accountId',
equalTo: user.accountId
}
});
})

Angular 2 Interface throwing error of non existing property

I have an Angular 2 interface books.ts
export interface Books {
artists: Object;
tracks: Object;
}
This is the my service file where I am using it with http request searchService.ts
import { Injectable } from '#angular/core';
import { Http, Response, Headers, RequestOptions } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import { Books } from 'app/pages/search-results/books';
import 'rxjs/add/operator/map'
#Injectable()
export class SearchService {
constructor(private _http:Http) { }
getBook(keyword): Observable<Books[]>{
return this._http.get('https://api.spotify.com/v1/search?q=' + keyword + '&type=track,artist')
.map((response: Response) => <Books[]> response.json());
}
}
And this is my component where I am using interface searchResults.ts
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute, Router } from '#angular/router';
import { SearchService } from 'app/shared/search/search.service';
import { Books } from 'app/pages/search-results/books';
#Component({
selector: 'app-search-results',
templateUrl: './search-results.component.html',
styleUrls: ['./search-results.component.css'],
providers: [SearchService]
})
export class SearchResultsComponent implements OnInit {
keyword: any;
sub: any;
books: Books[];
errMessage: string;
arists: Object;
constructor(private _route: ActivatedRoute, private _router: Router, private _search: SearchService) { }
ngOnInit() {
this.sub = this._route
.queryParams
.subscribe(params => {
// Defaults to 0 if no query param provided.
this.keyword = params['keyword'] || 0;
this.getBooks(this.keyword);
});
//
}
getBooks(value) {
this._search.getBook(value)
.subscribe(
res => {
this.books = res;
console.log(res.artists);
},
error => { this.errMessage = <any>error }
);
}
}
The error comes when I try to console the res.artists. The error says Property 'artists' does not exist on type 'Books[]'. I am new to Angular 2 and doesn't know how to fix that.
The response is looks like
{artists:{limit: 20, item:[]}, tracks:{limit: 20, item:[]}}
I'm not sure but I think you try to get res.artist from collection of books. You can check it by for or e.g res[0].artist to get concrete artist.
getBook function in class SearchService return an array of Books object (Books[])
so, the res in getBooks function in SearchResultsComponent will be an Array of Books.
You can console.log(res) to see detail, if you want access to artists please try with res[0].artists if the res is not an empty array
The problem is that I am getting Object in response and I am assigning it to an Array which is causing the error. I have simply changes the both types to object and it solved my problem.
From this
books: Books[];
To this
books: Books;

Categories

Resources