Knex / Objection / PostgreSQL - Update of foreign key not working - postgresql

I use Knex/Objection with PostgreSQL and I'm trying to update the foreign key but it returns null without any error.
Car model:
export class CarModel extends Model implements Car {
public static readonly tableName: string = 'cars';
public static readonly idColumn: string = 'car_id';
public static readonly jsonSchema: object = {
type: 'object',
properties: {
name: { type: 'string' },
carBrandId: { type: ['number', 'null'] },
lastUpdated: { type: ['string', 'null'], format: 'date-time' }
}
};
public static readonly relationMappings: RelationMappings = {
carBrandId: {
relation: Model.BelongsToOneRelation,
modelClass: 'brand.model',
join: {
from: 'brands.brand_id',
to: 'cars.fk_cars_brands'
}
}
};
public carId: number;
public name: string;
public carBrandId: number;
}
Creation of cars table:
import { Knex } from 'knex';
export function up(knex: Knex): Promise<boolean | void> {
return knex.schema.hasTable('cars').then(function(exists) {
if (!exists) {
return knex.schema.createTable('cars', (table) => {
table.specificType('car_id', 'INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY');
table.specificType('name', 'TEXT').notNullable();
table.specificType('car_brand_id', 'INTEGER').references('brand_id').inTable('brands').withKeyName('fk_cars_brands');
table.specificType('last_updated', 'TIMESTAMP');
});
}
})
}
export function down(knex: Knex): Knex.SchemaBuilder {
return knex.schema.dropTableIfExists('cars');
}
And then I use patch method in my service to update car_brand_id value from default to new added brand:
Database.getConnection().then((connection) => {
return CarModel.query(connection)
.patch({
carBrandId: 1, // row with brandId=1 exists in table brands!
lastUpdated: new Date().toISOString()
})
.where('carId', 10)
.returning('*')
.first()
});
And it returns the row where lastUpdated was updated, but carBrandId is still null:
{ carId: 10, name: 'Ferrari', carBrandId: null, lastUpdated: '<some date>' }
I would appreciate any help!

Related

why MongoDB returns a CastError when I am trying to delete an Item by id?

I am trying to make the DELETE Rest API method but I get CastError.
The problem is with the id it's of type ObjectID and I made it as a number, even when I chose string I got the same error.
Error
[Nest] 15504 - 22/12/2021, 21:34:11 ERROR [ExceptionsHandler] Cast to ObjectId failed for value "{ id: '61c32ba552a7cec272037b12' }" (type Object) at path "_id" for model "City"
CastError: Cast to ObjectId failed for value "{ id: '61c32ba552a7cec272037b12' }" (type Object) at path "_id" for model "City"
at model.Query.exec (C:\Users\ouss\Desktop\coffeeit-assessment\node_modules\mongoose\lib\query.js:4594:21)
at CitiesService.deleteCity (C:\Users\ouss\Desktop\coffeeit-assessment\src\cities\cities.service.ts:46:64)
at CitiesController.deleteCity (C:\Users\ouss\Desktop\coffeeit-assessment\src\cities\cities.controller.ts:25:31)
at C:\Users\ouss\Desktop\coffeeit-assessment\node_modules\#nestjs\core\router\router-execution-context.js:38:29
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at C:\Users\ouss\Desktop\coffeeit-assessment\node_modules\#nestjs\core\router\router-execution-context.js:46:28
at C:\Users\ouss\Desktop\coffeeit-assessment\node_modules\#nestjs\core\router\router-proxy.js:9:17
cities.service.ts
#Delete(':id')
async deleteCity(id) {
const result = await this.cityModel.deleteOne({ _id: id }).exec();
if (!result) {
throw new NotFoundException('Could not find city.');
}
}
cities.controller.ts
#Delete(':id')
deleteCity(#Param() id: number) {
return this.citiesService.deleteCity(id);
}
city.model.ts
import * as mongoose from 'mongoose';
export const CitySchema = new mongoose.Schema({
name: String,
weather: mongoose.SchemaTypes.Mixed,
});
export interface City {
id: number;
name: string;
weather: mongoose.Schema.Types.Mixed;
}
city.entity.ts
import { BaseEntity, Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
#Entity()
export class City extends BaseEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#Column()
weather: any;
}
cities.service.ts
import { ObjectId } from 'mongodb';
...
#Delete(':id')
async deleteCity(id) {
const cityId = new ObjectId(id); // Cast id to MongoDB Object id
const result = await this.cityModel.deleteOne({ _id: cityId }).exec();
if (!result) {
throw new NotFoundException('Could not find city.');
}
}

Validate DTO in controller when passing data to service

I am trying to insert a validation into PUT request(to update some data stored in MongoDB):
DTO:
export enum reportFields {
'startDate',
'targetDateOfCompletion',
'duration',
}
export class updateScheduleDto {
#IsOptional()
#IsString()
readonly frequency?: string;
#IsOptional()
#IsArray()
#IsEmail({}, { each: true })
#IsString({ each: true })
readonly emails?: string[];
#IsOptional()
#IsEnum(reportFields, { each: true })
#IsArray()
#IsString({ each: true })
readonly reportFields?: string[];
#IsOptional()
#Type(() => Number)
#IsNumber()
updatedAt?: number;
}
Controller:
#Put('reports/:id/schedule')
async updateScheduleData(
#Param('id') id: string,
#Body(new ValidationPipe()) updateData: updateScheduleDto,
) {
return this.reportService.updateScheduleData(id, updateData);
}
Service:
async updateScheduleData(id: string, updateData: updateScheduleDto) {
try {
updateData.updatedAt = this.utils.getCurrentTime();
const newData = await this.reportScheduleModel.findByIdAndUpdate(
id,
updateData,
{
new: true,
},
);
console.log(`Data has been updated to ${newData}`);
return newData;
} catch (error) {
throw new Error('>>>' + error);
}
}
But the validation not working over the keys. If I pass a non-valid key(like below) in the body object, even then the program executes without any error, how do I fix this? What am I missing?
{
"emaaaalls":["randomemail123#gmail.com"]
}
You need to pass the options { forbidUnknownValues: true } to the ValidationPipe. This will make class-validator throw an error when unknown values are passed in. You can read through the options here
You can make whitelist: true in ValidationPipe options.
When set to true, this will automatically remove non-whitelisted properties (those without any decorator in the validation class).
you can read more about this https://docs.nestjs.com/techniques/validation#stripping-properties

(node:18560) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'typeFn' of undefined

I am getting this error when I am trying to resolve a field(blocks) with the #ResolveField() decorator.
page.resolver.ts
import {
Resolver,
Query,
Mutation,
Args,
ResolveField,
Parent,
} from '#nestjs/graphql';
import { PageService } from './page.service';
import { PageType } from './type/page.type';
import { CreatePageInput } from './input/create-page.input';
import { BlockService } from '../block/block.service';
import { Page } from './page.interface';
#Resolver('Page')
export class PageResolver {
constructor(
private readonly pageService: PageService,
private readonly blockService: BlockService,
) {}
#Query(() => [PageType])
pages() {
return this.pageService.getAllPages();
}
#Query(() => [PageType])
async page(#Args('id') id: string) {
return this.pageService.getPage(id);
}
#Mutation(() => PageType)
createPage(#Args('createPageInput') createPageInput: CreatePageInput) {
return this.pageService.createPage(createPageInput);
}
#ResolveField()
blocks(#Parent() page: Page) {
return this.blockService.getManyBlocks(page.blockIds);
}
}
page.interface.ts
import { Document } from 'mongoose';
export interface Page extends Document {
readonly id: string;
readonly name: string;
readonly createdAt: Date;
readonly updatedAt: Date;
readonly createdBy: string;
readonly updatedBy: string;
readonly blockIds: string[];
}
It's solved after using the function () => PageType in the #Resolver() decorator.
#Resolver(() => PageType)

Angular 2 Reactive Form nested group passed null on reset throw error

I have this form group:
Id: [null],
Nominativo: [null, [Validators.required, Validators.maxLength(100)]],
Area: fb.group({
Id: [null]
})
When i try to reset the form with this model:
Id: 1,
Nominativo: 'Test'
Area: null
the area null value throw exception "TypeError: Cannot read property 'Id' of null". My expected result is all Area value become null. I can avoid this problem? My server response with all nested model null when they are all null
I don't think there is a simple clean solution for this. You need to iterate the object properties and set the values manually. So something like this:
resetVal = {Id:1, Nominativo: 'Test', Area: null }
and when you receive the data, you first check if Area is null, if so, set the formcontrols to null in that formgroup. If not, then set the values you get from backend to your formgroup:
this.areaCtrl = this.myForm.controls.Area as FormGroup;
this.myForm.patchValue({
Id: this.resetVal.Id,
Nominativo: this.resetVal.Nominativo,
})
if(this.resetVal.Area === null) {
this.areaCtrl.patchValue({
Id: null
})
}
else {
for(let a in this.resetVal.Area) {
this.areaCtrl.patchValue({
[a]:this.resetVal.Area[a]
})
}
}
DEMO
I suggest to do something like this:
area.model.ts
export class Area {
id: string;
}
data.model.ts
export class DataModel {
id: string;
nominativo: string;
area: Area;
}
form.model.ts
export class FormModel {
id: string;
nominativo: string;
area?: Area;
}
data-model.service.ts
#Injectable()
export class DataModelService {
toFormModel(dataModel: DataModel): FormModel {
const form: FormModel = new FormModel();
form.id = dataModel.id;
form.nominativo = dataModel.nominativo;
if (dataModel.area) {
form.area = dataModel.area;
}
return form;
}
}
form.component.ts
constructor(dataModelService: DataModelService,
dataApi: dataApiService,
fb: FormBuilder) {
this.form = this.fb.group({
id: '',
nominativo: '',
area: this.fb.group({
id: ''
})
});
this.dataApi.getData()
.map((data: DataModel) => this.dataModelService.toFormModel(data))
.subscribe((formData) => {
this.form.patchValue(formData);
});
}

Angular 2: Create objects from a class

Hello I'm wondering if it's possible to create a class where you implement an interface and from there you send the data get from .get service to create a new object. Something like this
import { Component, OnInit } from '#angular/core';
import { User} from '../interfaces/user';
import {UserService} from '../services/user.service';
import { UserClass } from '../classes/user-class'
#Component({
selector: 'up-pros',
templateUrl: './pros.component.html',
providers: [UserService]
})
export class ProsComponent implements OnInit {
public users :User[];
public term: string;
constructor(private _httpService: UserService) { }
ngOnInit() {
console.log(UserClass)
this.term= 'INSTRUCTOR';
this._httpService.searchUsers(this.term)
.subscribe(
data => {this.users = new UserClass(data), console.log(data)},
error => alert(error + ' Error Get')
);
}
}
where my UserClass code is something like next one
import { User } from '../interfaces/user';
import { Address } from "../interfaces/address";
export class UserClass implements User {
public id: number
public name: string
public password: string
public lastNameA: string
public lastNameB: string
public photo: string
public telephone: string
public email: string
public userType: string
public active: string
public score: number
public createdAt: string
public updatedAt: string
public Address: Address
constructor ( id: number,
password: string,
name: string,
lastNameA: string,
lastNameB: string,
photo: string,
telephone: string,
email: string,
userType: string,
active: string,
score: number,
createdAt: string,
updatedAt: string,
Address: Address) {
this.name = name
this.password = password
this.lastNameA = lastNameA
this.lastNameB = lastNameB
this.photo = photo
this.telephone = telephone
this.email = email
this.userType = userType
this.active = active
this.score = score
this.createdAt = createdAt
this.updatedAt = updatedAt
this.Address = Address
}
}
and by the last, the interface:
import { Address } from "./address"
export interface User {
name: string;
password: string;
lastNameA: string;
lastNameB: string;
photo: string;
telephone: string;
email: string;
userType: string;
active: string;
score: number;
createdAt: string;
updatedAt: string;
Address: Address;
}
Is this possible? because if I try to do this Im getting the next error at pros-component.ts:
Supplied parameters do not match any signature of call target.
[default] Checking finished with 1 errors
My service:
import {Injectable} from '#angular/core';
import {Http, Headers} from '#angular/http';
import 'rxjs/add/operator/map';
import { User } from '../interfaces/user';
#Injectable()
export class UserService {
url= 'http://localhostapi/users';
constructor(private _http: Http){}
getUsers(){
return this._http.get(this.url)
.map(res => res.json());
}
searchUsers(term : string ){
return this._http.get('http://localhostapi/listas?user='+term)
.map(res => res.json());
}
searchUser(term : string ){
return this._http.get('http://localhostapi/users/'+term)
.map(res => res.json());
}
postUsers(user: User){
var headers = new Headers ();
headers.append('Content-Type','application/json');
return this._http.post(this.url, user, {headers: headers})
.map(res => res.json());
}
updateUsers(user: User, term: string){
var headers = new Headers ();
headers.append('Content-Type','application/json');
return this._http.put(this.url+"/"+term, user, {headers: headers})
.map(res => res.json());
}
}
If the structure of data matches the list of UserClass, you can simply do
this._httpService.searchUsers(this.term)
.subscribe(
data => {
this.users = data as User[];
console.log(data)
},
error => alert(error + ' Error Get')
);