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

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()

Related

Adding controls for nested proptypes in storybook

I am trying to add a multi-select control using argTypes in storybook for a button component which takes propTypes in below format :
type ButtonProps = {
params: { FirstParam: string; SecondParam: string };
fields: Fields;
};
And the below is the code where I am trying to specify the control type for firstParam :
export default {
title: 'components/Button',
component: Default,
argTypes: {
params: {
FirstParam: {
name: 'FirstParam',
description: 'grid styling',
control: { type: 'multi-select', disable: false },
options: ['b', 'c', 'd'],
defaultValue: 'b c d',
},
SecondParam: {
name: 'SecondParam',
description: 'button styling',
control: 'multi-select',
options: ['none', 'secondary-button'],
},
},
},
decorators: [(story) => <div className="grid grid-cols-12">{story()}</div>],
} as ComponentMeta<typeof Default>;
const Template: ComponentStory<typeof Default> = (args) => <Default {...args} />;
export const Primary = Template.bind({});
Primary.args = {
fields: {
Link: {
value: {
href: 'https://www.google.com/',
target: '_blank',
text: 'Find Installer',
},
},
},
};
But it doesn't bind the control and breaks the storybook component.
Is it possible to have controls like multi-select working for the above kind of buttonProps?

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

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

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]
}

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 }
}
});
};

Flatten object properties in Flow

I have a seemingly simple question about Flow.
Given the following objects:
const input: InputObject = {
key1: {
prop1: 'value1',
},
key2: {
prop2: 'value2',
prop3: false,
},
};
const output: OutputObject = {
prop1: 'value1',
prop2: 'value2',
prop3: false,
};
How can I get the type of OutputObject, based on InputObject?
type OutputObject = ??magic?? InputObject ??magic??;
Context
I want to type a React HOC component which will inject some properties based on a configuration object, like:
type Props = {
prop1: string,
prop2: string,
prop3: boolean,
};
class Component extends React.Component<Props> { /* */ }
Hoc({
key1: () => ({
prop1: 'value1',
}),
key2: () => ({
prop2: 'value2',
prop3: false,
}),
})(Component);
I already know how to correctly type the HOC to get it type-safe and how to get InputObject from the configuration object passed to the HOC, but I'm stuck at how to "flatten" the InputObject type to OutputObject type, so that <Component /> will be happy because it will be getting all the required properties from the HOC :)
Even more important! I want the following to be a type error:
const HocComponent = Hoc({
key1: () => ({
prop1: 'value1',
}),
key2: () => ({
prop2: 'value2',
}),
})(Component);
<HocComponent /> // Error! prop3 is required
<HocComponent prop3={true} /> // Happy again
Thank you!

Resources