Mikro ORM : What is the best way to upsert a collection - mikro-orm

In the following scenario a whole SalesOrder entity is passed in to the server
items collection of sales order entity can have new items, removed items and updated items.
just persisting the SalesOrder don't work (wont remove or update)
this is the way i got it to work. is there a better way? or an I missing something?
async save(#Body() item: SalesOrder) {
let result: SalesOrder;
result = await em.getRepository<SalesOrder>(SalesOrder).findOne(item.id);
item.items.forEach(k => k.salesOrder = item.id)
await this.em.getRepository(SalesOrderItem).nativeDelete({ salesOrder: item.id })
for (const [i, it] of item.items.entries()) {
const x = em.getRepository(SalesOrderItem).create({ ...it })
await em.persistAndFlush(x)
}
wrap(result).assign({ ...item, items: null }, {})
await em.getRepository(SalesOrder).persistAndFlush(result);
await em.flush()
}
#Entity()
export class SalesOrder extends BaseEntity {
#PrimaryKey( )
id: number;
#OneToMany(i => SalesOrderItem, t => t.salesOrder, { eager: true, cascade: [Cascade.ALL] })
items = new Collection<SalesOrderItem>(this);
}
#Entity()
export class SalesOrderItem extends BaseEntity {
#ManyToOne({ entity: () => InventoryItem, eager: true, primary: true })
item: IdentifiedReference<InventoryItem>;
#Property({ type: JsonType, columnType: 'jsonb', nullable: true })
configuration?: SalesOrderItem[]
#ManyToOne({ entity: () => SalesOrder, hidden: true, primary: true })
salesOrder: IdentifiedReference<SalesOrder>;
#Property({ nullable: false })
qty: number;
#Property({ nullable: false })
unitPrice: number;
[PrimaryKeyType]: [string, string]
}

Related

Next js Redux, Objects are not valid as a React child

Error: Objects are not valid as a React child (found: object with keys {_id, name}). If you meant to render a collection of children, use an array instead.
Tried to fix this for days and no result.
i have a model
import mongoose from 'mongoose'
const CategoriesSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true
},
parent: {
type: mongoose.Types.ObjectId,
ref: 'categories'
},
},
{
timestamps: true
})
let Dataset = mongoose.models.categories || mongoose.model('categories', CategoriesSchema)
export default Dataset
and i have getCategories like this
[getCategories ]
const getCategories = async (req, res) => {
try {
const categories = await Categories.find().populate("parent", "name");
res.json({ categories });
}
catch (err)
{
return res.status(500).json({ err: err.message });
}
};
in my Globale state i have
export const DataContext = createContext()
export const DataProvider = ({children}) => {
const initialState = {
notify: {}, auth: {}, cart: [], modal: [], orders: [], users: [], categories: []
}
const [state, dispatch] = useReducer(reducers, initialState)
useEffect(() => {
getData('categories').then(res => {
if(res.err)
return dispatch({type: 'NOTIFY', payload: {error: res.err}})
dispatch({ type: 'ADD_CATEGORIES', payload: res.categories })
})
},[])
return(
<DataContext.Provider value={{state, dispatch}}>
{children}
</DataContext.Provider>
)
}
when i call categories throw:exception
when i change dispatch in Globale state like :
dispatch({ type: 'ADD_CATEGORIES', payload: [] })
i get no elements in array :

MikroORM Can't map join-table that uses a composite primary-key that includes a composite foreign-key

I get an error when mapping a join table that has a composite primary-key that includes a foreign-key to a composite primary-key. I believe the problem is with UserRole.user.
Stack trace
TypeError: Cannot read property 'id' of undefined
at /Users/emurphy/projects/aperture/aap-api/node_modules/#mikro-orm/core/utils/Utils.js:369:29
at Array.map (<anonymous>)
at Function.getOrderedPrimaryKeys (/Users/emurphy/projects/aperture/aap-api/node_modules/#mikro-orm/core/utils/Utils.js:367:33)
at /Users/emurphy/projects/aperture/aap-api/node_modules/#mikro-orm/core/utils/Utils.js:371:37
at Array.map (<anonymous>)
at Function.getOrderedPrimaryKeys (/Users/emurphy/projects/aperture/aap-api/node_modules/#mikro-orm/core/utils/Utils.js:367:33)
at /Users/emurphy/projects/aperture/aap-api/node_modules/#mikro-orm/core/utils/Utils.js:371:37
at Array.map (<anonymous>)
at Function.getOrderedPrimaryKeys (/Users/emurphy/projects/aperture/aap-api/node_modules/#mikro-orm/core/utils/Utils.js:367:33)
at EntityFactory.findEntity (/Users/emurphy/projects/aperture/aap-api/node_modules/#mikro-orm/core/entity/EntityFactory.js:95:35)
To Reproduce
Create the following entities:
#Entity()
export class Organization {
#PrimaryKey({ type: 'uuid', defaultRaw: 'uuid_generate_v4()' })
id: string = v4();
#Unique()
#Property({ columnType: 'varchar' })
name: string;
#OneToMany({ entity: () => User, mappedBy: (user) => user.organization, cascade: [] })
users = new Collection<User>(this);
constructor(value: Partial<Organization> = {}) {
Object.assign(this, value);
this.users = this.users || new Collection<User>(this);
}
}
#Entity()
export class User {
#PrimaryKey({ columnType: 'varchar' })
id: string;
#Index()
#ManyToOne({
entity: () => Organization,
primary: true,
wrappedReference: true,
index: true,
cascade: [],
onDelete: 'no action',
})
organization: IdentifiedReference<Organization>;
#Property({ columnType: 'varchar' })
firstName: string;
#Property({ columnType: 'varchar' })
lastName: string;
#Property({ columnType: 'varchar' })
email: string;
#OneToMany({ entity: () => UserRole, mappedBy: (userRole) => userRole.user })
userRoles = new Collection<UserRole>(this);
[PrimaryKeyType]: [string, string];
constructor(value: Partial<User> = {}) {
Object.assign(this, value);
this.userRoles = this.userRoles || new Collection<UserRole>(this);
}
}
#Entity()
export class Role {
#PrimaryKey({ columnType: 'varchar' })
id: string;
#Property({ columnType: 'varchar' })
name: string;
#OneToMany({ entity: () => UserRole, mappedBy: (userRole) => userRole.role })
userRoles = new Collection<UserRole>(this);
constructor(value: Partial<Role> = {}) {
Object.assign(this, value);
this.userRoles = this.userRoles || new Collection<UserRole>(this);
}
}
#Entity()
export class UserRole {
#ManyToOne({
entity: () => User,
inversedBy: (x) => x.userRoles,
primary: true,
wrappedReference: true,
cascade: [],
onDelete: 'cascade',
})
user: Reference<User>;
#ManyToOne({
entity: () => Role,
inversedBy: (x) => x.userRoles,
primary: true,
wrappedReference: true,
cascade: [],
onDelete: 'no action',
})
role: IdentifiedReference<Role>;
[PrimaryKeyType]: [string, string, string];
constructor(value: Partial<UserRole> = {}) {
Object.assign(this, value);
}
}
Run either of the following queries:
// query and map just the join table
await this.em.find(UserRole, { user: { $eq: [userId, orgId] } })
// or try to populate it
await this.em.findOne(
User,
{ id: { $eq: userId }, organization: { $eq: orgId } },
{ populate: { userRoles: LoadStrategy.JOINED } },
);
Versions
Dependency
Version
node
14.15.3
typescript
4.1.5
mikro-orm
4.4.4
mikro-orm/postgresql
4.4.4
I have tried removing the UserRole.role property and the error still occurs. I have tried querying the table directly and populating it as part of finding another entity (User). I have tried using the query builder. I have tried using normal entities instead of wrapped entities. I have tried using em.map(...) on the results of a raw execute.
Am I setting it up wrong or is this just a bug in the framework? I couldn't find examples for this specific scenario.
Update
The issue was fixed here: https://github.com/mikro-orm/mikro-orm/issues/1624

loopback 4 how to avoid creating more than 5 items in a shopping cart

I am new to loopback 4, and almost all the documentation I found is for lower versions. I have a shopping cart, but I need to avoid it to have more than 5 items... how can I made this constraint?
These are my models:
import {Entity, hasMany, model, property} from '#loopback/repository';
import {Item} from './item.model';
#model()
export class Shoppingcar extends Entity {
#property({
type: 'string',
id: true,
generated: true,
})
id?: string;
#property({
type: 'string',
required: true,
})
desc: string;
#property({
type: 'string',
required: true,
}
})
shopper: string;
#hasMany(() => Item)
items: Item[];
constructor(data?: Partial<Gateway>) {
super(data);
}
}
export interface ShoppingcarRelations {
// describe navigational properties here
}
export type ShoppingcarWithRelations = Shoppingcar & ShoppingcarRelations;
and
import {Entity, model, property, belongsTo} from '#loopback/repository';
import {Shoppingcar} from './shoppingcar.model';
#model()
export class Item extends Entity {
#property({
type: 'string',
id: true,
generated: true,
})
id?: string;
#property({
type: 'string',
required: true,
})
name: string;
#property({
type: 'string',
required: true,
})
date: string;
#belongsTo(() => Shoppingcar)
shoppingcarId: string;
constructor(data?: Partial<Item>) {
super(data);
}
}
export interface ItemRelations {
// describe navigational properties here
}
export type ItemWithRelations = Item & ItemRelations;
Create one interceptor,
check for the item count in the interceptor, if the count is greater the 5 throw an error else continue with next()

Use action creator to dispatch action in another action creator

I'm wondering if there is a pattern that allows you to use action creators inside of other action creators. The modifyMassProperty action creator lets you pass any number of actions which are then iterated over and dispatched accordingly. I would very much like to be able to use this method in the getOrbitalBurn action creator since it would be semantically more appealing than using the dispatch method made available by the thunk three times in a row. I'm confident I must either have missed something, or that I'm guilty of getting tangled up in some sort of anti pattern that I concocted during one of my lesser days.
export const modifyMassProperty = (
...massProperties: MassProperty[]
): ThunkAction<void, AppState, void, Action> => (
dispatch: Dispatch<ScenarioActionTypes>
) =>
massProperties.forEach(massProperty =>
dispatch({
type: MODIFY_MASS_PROPERTY,
payload: massProperty
})
);
export const getOrbitalBurn = (
payload: { primary: string; periapsis: number; apoapsis: number },
applyBurn = true
): ThunkAction<void, AppState, void, Action> => (
dispatch: Dispatch<ScenarioActionTypes>,
getState: any
) => {
const scenario = getState().scenario;
const primary = getObjFromArrByKeyValuePair(
scenario.masses,
'name',
payload.primary
);
const orbit = orbitalInsertion(primary, payload, scenario.g);
if (applyBurn) {
const [spacecraft] = scenario.masses;
dispatch({
type: MODIFY_MASS_PROPERTY,
payload: {
name: spacecraft.name,
key: 'vx',
value: orbit.x
}
});
dispatch({
type: MODIFY_MASS_PROPERTY,
payload: {
name: spacecraft.name,
key: 'vy',
value: orbit.y
}
});
dispatch({
type: MODIFY_MASS_PROPERTY,
payload: {
name: spacecraft.name,
key: 'vz',
value: orbit.z
}
});
}
dispatch({
type: MODIFY_SCENARIO_PROPERTY,
payload: {
key: 'orbitalInsertionV',
value: { x: orbit.x, y: orbit.y, z: orbit.z }
}
});
};

Redux immutable pattern

I use react with redux.
Action:
export const updateClicked = (id, section) => {
return {
type: actionTypes.UPDATE_CLICKED,
id,
section
};
};
Please advise the best way to immutable update property in nested array:
Reducer:
const initialState = {
updates: {
html: {
id: 'html',
label: 'HTML',
count: 0,
items: [
{
id: 1,
label: 'Header',
price: 10,
bought: false
},
{
id: 2,
label: 'Sidebar',
price: 50,
bought: false
}
]
}
}
};
My action:
action = {
id: 1,
bought: true
}
I want to update bought property inside items array. I.e.:
const updateClicked= (state, action) => {
const updateSections = state.updates[action.section].items;
const updatedItems = updateSections.map(el => {
if (el.id === action.id && !el.bought) {
el.bought = true;
}
return el;
});
//How to update state???
return {}
};
Will be glad if you explain 2 ways to do this:
With es6 spread operator
With some library (like immutability-helper)
Thanks!
With es6 spread operator:
export default (state = initialState, action) => {
if (action.type !== actionTypes.UPDATE_CLICKED) return state;
return {
...state,
updates: {
...state.updates,
html: {
...state.updates.html,
items: state.updates.html.items.map((item, idx) => idx === action.id
? {...item, bought: item.bought}
: item
)
}
}
}
};

Resources