amplify-swift
amplify-swift copied to clipboard
Add control to which GraphQL connections of a model are loaded when constructing a query with Amplify.API.query
Is your feature request related to a problem? Please describe. The document generated with a GraphQL request makes some assumptions about which connections are loaded. It would be very helpful to make a way to indicate which connections should be added to the graphQL document.
Right now the assumption is that a connection only included if it is the connection owner and it is a required field (! in GraphQL)
ModelSchema+GraphQL.swift - if it has an association it has to be the owner to be included
var graphQLFields: [ModelField] {
sortedFields.filter { field in
!field.hasAssociation || field.isAssociationOwner
}
}
SelectionSet.swift - if it is not a required field then it isn't included in the section set
func withModelFields(_ fields: [ModelField]) {
fields.forEach { field in
let isRequiredAssociation = field.isRequired && field.isAssociationOwner
if isRequiredAssociation, let associatedModel = field.associatedModel {
let child = SelectionSet(value: .init(name: field.name, fieldType: .model))
child.withModelFields(associatedModel.schema.graphQLFields)
self.addChild(settingParentOf: child)
} else {
self.addChild(settingParentOf: .init(value: .init(name: field.graphQLName, fieldType: .value)))
}
}
addChild(settingParentOf: .init(value: .init(name: "__typename", fieldType: .value)))
}
Describe the solution you'd like Passing fields that need to be loaded when constructing a GraphQLRequest
public static func list<M: Model>(_ modelType: M.Type,
where predicate: QueryPredicate? = nil,
include connections: [String]) -> GraphQLRequest<[M]>
connections would come from ModelInstance.keys.theKey
Describe alternatives you've considered
- Changing the schema is the easiest way around this in this situation but may not be the best solution
- There could be a mode when creating a query that has a level of connections to load
- I'm sure there are a lot of other ways to accomplish this and hope that better solutions are presented
Additional context The following is a sample schema. When making a query for ChatRoomMembers the user is a part of the chatRoom connected object isn't loaded because it isn't required.
type User @model
{
id: ID!
email: String!
chatRooms: [ChatRoomMember] @connection(keyName: "byMember", fields: ["id"])
}
# Many-to-many join between ChatRooms and Users
type ChatRoomMember @model
@key(name: "byChatRoom", fields: ["chatRoomID", "userID"], queryField: "listMembersByChatRoom")
@key(name: "byMember", fields: ["userID", "chatRoomID"], queryField: "listChatRoomByMember")
{
id: ID!
userID: ID!
user: User @connection(fields: ["userID"])
chatRoomID: ID!
chatRoom: ChatRoom @connection(fields: ["chatRoomID"])
}
type ChatRoom @model
{
id: ID!
createdAt: String
updatedAt: String
name: String!
members: [ChatRoomMember] @connection(keyName: "byChatRoom", fields: ["id"])
creatorUserID: ID!
creator: User @connection(fields: ["creatorUserID"])
}
Query Code
let predicate = QueryPredicateOperation(field: "userID", operator: .equals(user.id))
let request = GraphQLRequest<ChatRoomMember>.list(ChatRoomMember.self, where: predicate)
_ = Amplify.API.query(request: request, listener: { (event) in
switch event {
case .success(let result):
switch result {
case .success(let chatroomMembers):
let chatRooms = chatroomMembers.compactMap { $0.chatRoom }
//no chatrooms because it is always nil
case .failure(let error):
print("got error getting ChatRoom Members \(error)")
}
case .failure(let error):
print("got error getting ChatRoom Members event \(error)")
}
})
Make ChatRoom Member code:
let chatroom = ChatRoom(name: title, creator: currentUser)
_ = Amplify.API.mutate(request: .create(chatroom)) { (event) in
switch event {
case .success(let result):
switch result {
case .success(let newChatRoom):
let chatRoomMember = ChatRoomMember(user: currentUser, chatRoom: newChatRoom)
_ = Amplify.API.mutate(request: .create(chatRoomMember), listener: { (memberEvent) in
switch memberEvent {
case .success(let memberResult):
switch memberResult {
case .success(let chatRoomMember):
print("saved member \(chatRoomMember)")
case .failure(let error):
print("got error \(error)")
}
case .failure(let error):
print("got error \(error)")
}
})
case .failure(let error):
print("failure: \(error)")
}
case .failure(let error):
print("error \(error)")
}
}