AWS Data Modeling for GraphQL in Flutter - flutter

I have created two Model ProductCategory and ProductSubCategory.
Here is my Models:
ProductCategory Model:
type ProductCategory #model(subscriptions: null) #searchable #auth(rules: [{allow: public, operations: [read]}, {allow: private, operations: [read, create, update, delete]}]) {
id: ID!
name: String! #index(name: "bynameProductCategory", queryField: "bynameProductCategory", sortKeyFields: ["createdAt"])
storeId: ID #index(name: "bystoreIdProductCategory", queryField: "bystoreIdProductCategory", sortKeyFields: ["createdAt"])
store: Store #hasOne(fields: ["storeId"])
description: String
slug: String! #index(name: "byslugProductCategory", queryField: "byslugProductCategory", sortKeyFields: ["createdAt"])
isFeatured: Boolean
totalProducts: Int
priority: Int
imageUrl: String
products: [Product] #hasMany(indexName: "bycategoryIdProduct", fields: ["id"])
subCategory: [ProductSubCategory] #hasMany(indexName: "bycategoryIDProductSubCategory", fields: ["id"])
createdAt: AWSDateTime
updatedAt: AWSDateTime
}
ProductSubCategory
type ProductSubCategory #model(subscriptions: null) #searchable #auth(rules: [{allow: public, operations: [read]}, {allow: private, operations: [read, create, update, delete]}]) {
id: ID!
storeId: ID #index(name: "bystoreIdProductSubCategory", queryField: "bystoreIdProductSubCategory", sortKeyFields: ["createdAt"])
store: Store #hasOne(fields: ["storeId"])
name: String! #index(name: "bynameProductSubCategory", queryField: "bynameProductSubCategory", sortKeyFields: ["createdAt"])
description: String
categoryID: ID #index(name: "bycategoryIDProductSubCategory", queryField: "bycategoryIDProductSubCategory", sortKeyFields: ["createdAt"])
category: ProductCategory #hasOne(fields: ["categoryID"])
slug: String! #index(name: "byslugProductSubCategory", queryField: "byslugProductSubCategory", sortKeyFields: ["createdAt"])
isFeatured: Boolean
totalProducts: Int
priority: Int
imageUrl: String
products: [Product] #hasMany(indexName: "bycategoryIdProduct", fields: ["id"])
createdAt: AWSDateTime
updatedAt: AWSDateTime
}
When I call ProductSubCategory I get SubCategory data null.
Here is my code:
Future<void> getAllCategory() async {
final request = ModelQueries.list(
ProductCategory.classType,
);
final response = await Amplify.API
.query(request: request)
.response;
}
here I get subProductCategory data null.
am I doing wrong model?

Related

Can't create a nested many To many properties

I've been stuck on this problem for hours.
I'm trying to create an object linked by a many to many relationship.
However, I get this error when the function is invoked:
Unknown arg 0 in data.merchandises.create.0 for type ListingsMerchandisesCreateWithoutListingInput.
Argument merchandise for data.merchandises.create.merchandise is missing.
I don't know where that "0" comes from.
Thank you in advance for your help..
Full log of error:
Invalid `prisma[prismaModel].create()` invocation in
/app/node_modules/prisma-factory/dist/index.js:109:44
106 data = hooks.beforeCreate(data);
107 }
108 const prismaModel = (0, import_camel_case.camelCase)(modelName);
→ 109 let result = await prisma[prismaModel].create({
data: {
type: 'FOR_RENT',
name: 'consequatur',
merchandises: {
create: {
'0': {
merchandise: {
create: {
cosmetic: 'GOOD',
typology: 'NEW',
quantity: 76,
price: 80,
location: {
create: {
name: 'repellat',
line1: '600 Jerde Mews',
line2: 'Apt. 517',
line3: '79414 Lenore Harbor',
line4: 'placeat',
city: 'Port Astridshire',
postalCode: '47459-8067',
state: 'North Carolina',
country: 'Antigua and Barbuda',
other: 'iste'
}
},
user: {
create: {
email: 'Neoma41#gmail.com',
firstName: 'Casimir',
lastName: 'Kub'
}
},
product: {
create: {
name: 'Luxurious Steel Keyboard',
description: 'The Nagasaki Lander is the trademarked name of several series of Nagasaki sport bikes, that started with the 1984 ABC800J',
barcode: 'r0h7w9h1d',
barcodeType: 'EAN13',
brand: 'Bespoke',
model: 'Countach',
lenght: 25,
width: 57,
weight: 11,
capacity: 62,
impactUnit: 'UNIT',
manufacturingImpact: 31,
destructiveImpact: 81
}
}
}
}
},
+ merchandise: {
+ create?: MerchandiseCreateWithoutListingsInput | MerchandiseUncheckedCreateWithoutListingsInput,
+ connectOrCreate?: MerchandiseCreateOrConnectWithoutListingsInput,
+ connect?: MerchandiseWhereUniqueInput
+ }
}
}
}
})
Unknown arg `0` in data.merchandises.create.0 for type ListingsMerchandisesCreateWithoutListingInput.
Argument merchandise for data.merchandises.create.merchandise is missing.
The factory:
import { createFactory } from 'prisma-factory';
import { faker } from '#faker-js/faker';
import {
BarcodeType,
Cosmetic,
ImpactUnit,
Listing,
ListingType,
Prisma,
Typology,
} from '#prisma/client';
const DEFAULT_ATTRIBUTES = {
type: ListingType.FOR_RENT,
name: faker.lorem.word(),
merchandises: {
create: [
{
merchandise: {
create: {
cosmetic: Cosmetic.GOOD,
typology: Typology.NEW,
quantity: Number(faker.random.numeric(2)),
price: Number(faker.random.numeric(2)),
location: {
create: {
name: faker.lorem.word(),
line1: faker.address.streetAddress(),
line2: faker.address.secondaryAddress(),
line3: faker.address.streetAddress(),
line4: faker.lorem.word(),
city: faker.address.city(),
postalCode: faker.address.zipCode(),
state: faker.address.state(),
country: faker.address.country(),
other: faker.lorem.word(4),
},
},
user: {
create: {
email: faker.internet.email(),
firstName: faker.name.firstName(),
lastName: faker.name.lastName(),
},
},
product: {
create: {
name: faker.commerce.productName(),
description: faker.commerce.productDescription(),
barcode: faker.random.alphaNumeric(9),
barcodeType: BarcodeType.EAN13,
brand: faker.commerce.productAdjective(),
model: faker.vehicle.model(),
lenght: Number(faker.random.numeric(2)),
width: Number(faker.random.numeric(2)),
weight: Number(faker.random.numeric(2)),
capacity: Number(faker.random.numeric(2)),
impactUnit: ImpactUnit.UNIT,
manufacturingImpact: Number(faker.random.numeric(2)),
destructiveImpact: Number(faker.random.numeric(2)),
},
},
},
},
},
],
},
};
export const ListingFactory = createFactory<Prisma.ListingCreateInput, Listing>(
'listing',
DEFAULT_ATTRIBUTES,
);
concerning schema:
model Listing {
id Int #id #default(autoincrement())
type ListingType
name String
merchandises ListingsMerchandises[]
availability Availability?
category Category? #relation(fields: [categoryId], references: [id])
categoryId Int?
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
deletedAt DateTime?
}
model Merchandise {
id Int #id #default(autoincrement())
cosmetic Cosmetic
typology Typology
quantity Int
price Int
productId Int
product Product #relation(fields: [productId], references: [id])
userId Int
user User #relation(fields: [userId], references: [id])
locationId Int
location Location? #relation(fields: [locationId], references: [id])
listings ListingsMerchandises[]
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
deletedAt DateTime?
}
model ListingsMerchandises {
listingId Int
listing Listing #relation(fields: [listingId], references: [id])
merchandiseId Int
merchandise Merchandise #relation(fields: [merchandiseId], references: [id])
##id([listingId, merchandiseId])
}
I tried several formats, I even tried a connect but without success.
No type errors are thrown

How to Implement Typeorm Entity Array of Object

I want to achieve this output:
{
.....,
dependants: [{name: 'john', age: 29},{name: 'doe', age: 17}]
}
I have an entity like this:
class PartnerStaff extends BaseEntity {
constructor(
id: string,
company: string,
branch: string,
dependants: DependantDto[],
) {
super();
this.staffId = id;
this.company = company;
this.branch = branch;
this.dependants = dependants;
}
#PrimaryGeneratedColumn('increment')
id!: number;
#Column({
unique: true,
nullable: true,
})
staffId!: string;
#Column({
nullable: true,
name: 'company',
})
company!: string;
#Column()
branch!: string;
#Column('json', {nullable: true})
dependants?: DependantDto[];
}
And my dependants dto:
class DependantDto {
#IsString()
#IsNotEmpty({ message: 'dependant name is required' })
readonly name!: string;
#IsString()
#IsNotEmpty({ message: 'dependant age is required' })
readonly age!: number;
}
I am getting dependants: ['string'] on swagger.
I have tried these but still not working...
#Column('jsonb', {nullable: true})
#Column({type: 'array', nullable: true})
I was able to solve it this way....
#Column({type: 'json'})
dependants: DependantsDto[];
Then in my PartnerStaffDto, I did ...
#IsOptional()
#ApiModelProperty({
isArray: true
})
dependants: DependantsDto[];
So now I get what i was expecting ealier dependants: [{name: '', age: 0}]

Typeorm update record in OneToMany relation

I have the following two entities with a OneToMany/ManyToOne relation between them:
#Entity({ name: 'user' })
export class User {
#PrimaryGeneratedColumn('increment')
id: number;
#Column({ type: 'varchar' })
firstName: string;
#Column({ type: 'varchar' })
lastName: string;
#ManyToOne(() => Group, group => group.id)
#JoinColumn({ name: 'groupId' })
group: Group;
#Column({ type: 'integer' })
groupId: number;
}
#Entity({ name: 'group' })
export class Group {
#PrimaryGeneratedColumn('increment')
id: number;
#Column({ type: 'varchar' })
name: string;
#OneToMany(() => User, user => user.group)
members: User[];
}
When I create a new group in my Group repository, I can add existing members as follows:
const group = new Group();
group.name = 'Test';
group.members = [{ id: 1 }, { id: 2 }]; // user ids
this.save(group);
I am wondering if I can update existing members in a similar way. For example if a group with id 1 has two members:
{ id: 1, firstName: 'Member', lastName: 'One', groupId: 1 }
{ id: 2, firstName: 'Member', lastName: 'Two', groupId: 1 }
Is it possible to update a member through the OneToMany relation in a similar way as I'm adding them ? Something like (making this up):
const group = await repository.findOne(groupId, { relations: ['members'] });
group.members = [{ id: 1, firstName: 'Updated' }]; // this would update the firstName of member with id 1 if it exists in the relation
repository.save(group);
Thanks!
There is no built in functionality in typeorm to update specific related entity from the base entity. However if you want to do this then you can add a general function which updates the base table as well as it's relations.

How do I upload info associated to different Model Types using AWS & GraphQL?

I have an app that I have been working on that allows users to upload a post and other users can send messages after seeing their post. I am using AWS Amplify as a backend and I am trying to store user messages in a DynamoDB table. I am able to successfully store user info in a DynamoDB table after they create an account and successfully upload a post. My problem starts when I try to message another user about their post.
I get this error after pressing send Failed to create graphql GraphQLResponseError<ChatMessage>: GraphQL service returned a successful response containing errors: [Amplify.GraphQLError(message: "The variables input contains a field name \'chatMessageAuthorId\' that is not defined for input object type \'CreateChatMessageInput\' ", locations: nil, path: nil, extensions: nil)] Recovery suggestion: The list of `GraphQLError` contains service-specific messages
My GraphQL Schema is the following:
type User
#model
#auth(rules: [{ allow: owner, operations: [create, delete, update]}]) {
id: ID!
userSub: String!
fullName: String!
conversations: [ConvoLink] #connection(name: "UserLinks")
messages: [ChatMessage] #connection(name: "UserMessages", keyField: "authorId")
createdAt: String
updatedAt: String
}
type Conversation
#model
#auth(rules: [{ allow: owner, ownerField: "members" }]) {
id: ID!
messages: [ChatMessage] #connection(name: "ConvoMsgs", sortField: "createdAt")
associated: [ConvoLink] #connection(name: "AssociatedLinks")
name: String!
members: [String!]!
createdAt: String
updatedAt: String
}
type ChatMessage
#model
#auth(rules: [{ allow: owner, ownerField: "authorId" }]) {
id: ID!
author: User #connection(name: "UserMessages", keyField: "authorId")
authorId: String
content: String!
conversation: Conversation! #connection(name: "ConvoMsgs")
messageConversationId: ID!
createdAt: String
updatedAt: String
}
type ConvoLink
#model(
mutations: { create: "createConvoLink", update: "updateConvoLink" }
queries: null
subscriptions: null
) {
id: ID!
user: User! #connection(name: "UserLinks")
convoLinkUserId: ID
conversation: Conversation! #connection(name: "AssociatedLinks")
convoLinkConversationId: ID!
createdAt: String
updatedAt: String
}
type Post #model
#auth(rules: [{ allow: owner, operations: [create, delete, update] }]) {
id: ID!
userSub: String!
filename: String!
description: String!
dateUploaded: String!
}
And this is my Swift code to send a message to another user.
#objc func sendMessage(){
currentUserSub = AWSMobileClient.default().userSub!
guard let message = messageBox.text else{return}
let formatter = DateFormatter()
formatter.dateFormat = "h:mm a"
let hourString = formatter.string(from: Date())
let user = User(userSub: currentUserSub, fullName: userFullName)
let conversation = Conversation(messages: List<ChatMessage>.init(), associated: List<ConvoLink>.init(), name: "Chat between \(currentUserSub) & \(otherUserSub)", members: [currentUserSub,otherUserSub], createdAt: hourString, updatedAt: hourString)
let chatMessage = ChatMessage(author: user, authorId: currentUserSub, content: message, conversation: conversation, messageConversationId: "\(currentUserSub) & \(otherUserSub)", createdAt: hourString, updatedAt: hourString)
_ = Amplify.API.mutate(request: .create(chatMessage)) { event in
switch event {
case .success(let result):
switch result {
case .success(let convo):
DispatchQueue.main.async {
print("Successfully created the convo: \(convo)")
self.messageButton.setTitle("Message Sent", for: .normal)
self.messageButton.isEnabled = false
}
case .failure(let graphQLError):
DispatchQueue.main.async {
print("Failed to create graphql \(graphQLError)")
self.messageButton.setTitle("An error has occured, try again.", for: .normal)
// self.checkIfOffline()
}
}
case .failure(let apiError):
print("Failed to create a message", apiError)
// self.checkIfOffline()
}
}
hideKeyboard()
print(message)
}
Can anyone tell me what I am doing wrong? I am fairly new to GraphQL and AWS.

How to delete a record with all relevant records in prisma

I know that there are some sections related to my question in the documentation of prisma-client:
deleting objects
updating and deleting many records
However, I can't understand that how can I delete a record with all its related records in (JavaScript) prisma-client.
For example, my datamodel is something like this:
type Board {
id: ID! #unique
createdAt: DateTime!
updatedAt: DateTime!
owner: User! #relation(name: "BoardOwnershipRelation")
title: String!
description: String
taskGroups: [TaskGroup!]!
}
type TaskGroup {
id: ID! #unique
createdAt: DateTime!
updatedAt: DateTime!
owner: User! #relation(name: "TaskGroupOwnershipRelation")
board: Board!
title: String!
description: String
precedence: Int
tasks: [Task!]!
}
type Task {
id: ID! #unique
createdAt: DateTime!
updatedAt: DateTime!
owner: User! #relation(name: "TaskOwnershipRelation")
taskGroup: TaskGroup!
title: String!
description: String
dueDate: DateTime
precedence: Int
items: [TaskItem!]!
assignedTo: [User!]! #relation(name: "AssignmentRelation")
}
type TaskItem {
id: ID! #unique
createdAt: DateTime!
updatedAt: DateTime!
owner: User! #relation(name: "TaskItemOwnershipRelation")
task: Task!
title: String!
description: String
checked: Boolean!
precedence: Int
}
How can I delete a Board with all its related TaskGroups, Tasks, and TaskItems ?!
Edit:
I've recently tried this solution, which also works well.
// e.g. this is in my GraphQL resolvers async function...
await prisma.deleteManyTaskItems({
task: {
taskGroup: {
board: {
id: boardId
}
}
}
});
await prisma.deleteManyTasks({
taskGroup: {
board: {
id: boardId
}
}
});
await prisma.deleteManyTaskGroups({
board: {
id: boardId
}
});
return await prisma.deleteBoard({ id: boardId });
But, is there any better solution for my issue ???
You can use the "onDelete" argument of the #relation directive to specify what happens when you delete an entity (documentation)
You only have to change your datamodel like so:
type Board {
id: ID! #unique
createdAt: DateTime!
updatedAt: DateTime!
owner: User! #relation(name: "BoardOwnershipRelation")
title: String!
description: String
taskGroups: [TaskGroup!]! #relation(name: "BoardTaskGroups" onDelete: CASCADE)
}
type TaskGroup {
id: ID! #unique
createdAt: DateTime!
updatedAt: DateTime!
owner: User! #relation(name: "TaskGroupOwnershipRelation")
board: Board! #relation(name: "BoardTaskGroups")
title: String!
description: String
precedence: Int
tasks: [Task!]! #relation(name: "TaskGroupsTask" onDelete: CASCADE)
}
type Task {
id: ID! #unique
createdAt: DateTime!
updatedAt: DateTime!
owner: User! #relation(name: "TaskOwnershipRelation")
taskGroup: TaskGroup! #relation(name: "TaskGroupsTask")
title: String!
description: String
dueDate: DateTime
precedence: Int
items: [TaskItem!]! #relation(name: "TaskTaskItem" onDelete: CASCADE)
assignedTo: [User!]! #relation(name: "AssignmentRelation")
}
type TaskItem {
id: ID! #unique
createdAt: DateTime!
updatedAt: DateTime!
owner: User! #relation(name: "TaskItemOwnershipRelation")
task: Task! #relation(name: "TaskTaskItem")
title: String!
description: String
checked: Boolean!
precedence: Int
}
And then delete your Board. All the other deletions will be done by Prisma