Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unexpected change in Amplify.API.mutate behavior "Cannot return null for non-nullable type within parent" #3913

Open
uxian opened this issue Nov 5, 2024 · 3 comments
Labels
api Issues related to the API category bug Something isn't working

Comments

@uxian
Copy link

uxian commented Nov 5, 2024

Describe the bug

Error:

Failed to create graphql GraphQLResponseError<Like>: GraphQL service returned a partially-successful response containing errors: [Amplify.GraphQLError(message: "Cannot return null for non-nullable type: \'ID\' within parent \'User\' (/createLike/user/id)", locations: nil, path: Optional([Amplify.JSONValue.string("createLike"), Amplify.JSONValue.string("user"), Amplify.JSONValue.string("id")]), extensions: nil)]

Following code used to work, but after upgrading to newer version of Amplify, it starts to fall apart. I don't need associated entities when doing Amplify.API.mutate, and I suppose the backend shouldn't query associated entities by default, but somehow the response deserializer start to demand the associated entity which supposed to be optional.

        let like = Like(
            subjectId: subjectId,
            userId: userId,
            subjectType: subjectType,
            subjectOwnerId: subjectOwnerId
        )
        
        let request: GraphQLRequest<Like> = toLike ? .create(like) : .delete(like)
        
        let result = try await Amplify.API.mutate(request: request)

Following are model schema

type Like @model 
  @auth(rules: [
    { allow: private, provider: userPools, operations: [read] },
    { allow: owner, provider: userPools, operations: [create, update, delete], ownerField: "userId", identityClaim: "sub" }
  ]) {
  subjectId: ID! @primaryKey(sortKeyFields: ["userId"])
  userId: ID!
  subjectType: String
  subjectOwnerId: ID
  user: User @hasOne(fields: ["userId"])
}

type User @model {
  id: ID!
  name: String
  portrait: String
  bio: String
  isDeleted: Boolean
  createdAt: AWSDateTime
  updatedAt: AWSDateTime
}

Steps To Reproduce

See code above

Expected behavior

should not throw exception

Amplify Framework Version

2.29.2

Amplify Categories

API

Dependency manager

Swift PM

Swift version

5.x

CLI version

12.13.0

Xcode version

15.3

Relevant log output

<details>
<summary>Log Messages</summary>


INSERT LOG MESSAGES HERE
```

Is this a regression?

Yes

Regression additional context

No response

Platforms

iOS

OS Version

17.0

Device

iPhone14

Specific to simulators

No response

Additional context

No response

@github-actions github-actions bot added pending-triage Issue is pending triage pending-maintainer-response Issue is pending response from an Amplify team member labels Nov 5, 2024
@uxian
Copy link
Author

uxian commented Nov 5, 2024

I've just updated Amplify version to latest 2.44.0, the issue still exists

@uxian
Copy link
Author

uxian commented Nov 6, 2024

I figured the problem, the GraphQLRequest query builder tries to dump all fields into graphql query body (document), including the associated fields, however those fields with association are not returned in response. Here is my workaround, tries to remove any association from the generated graphql mutation query document. Looking forward to a permanent solution.


extension GraphQLRequest {
    public static func createFix<M: Model>(
        _ model: M) -> GraphQLRequest<M> {
            let request = create(model)
            return removeFieldsWithAssociation(request)
    }
    
    public static func deleteFix<M: Model>(
        _ model: M) -> GraphQLRequest<M> {
            let request = delete(model)
            return removeFieldsWithAssociation(request)
    }
    
    public static func updateFix<M: Model>(
        _ model: M) -> GraphQLRequest<M> {
            let request = update(model)
            return removeFieldsWithAssociation(request)
    }
    
    private static func findFieldsWithAssociation(_ schema: ModelSchema) -> [ModelField] {
        return schema.fields.values.filter { field in
            if case .belongsTo = field.association {
                return true
            }
            if case .hasMany = field.association {
                return true
            }
            if case .hasOne = field.association {
                return true
            }
            return false
        }
    }
    
    private static func removeFieldsWithAssociation<M: Model>(_ request: GraphQLRequest<M>) -> GraphQLRequest<M> {
        let document = request.document
        
        // Find fields with associations
        let fieldsToRemove = findFieldsWithAssociation(request.responseType.schema)
        
        // Remove fields from document string
        var modifiedDocument = document
        for field in fieldsToRemove {
            // Remove the field and any nested selections
            let pattern = "\(field.name)\\s*\\{[^}]*\\}"
            if let range = modifiedDocument.range(of: pattern, options: .regularExpression) {
                modifiedDocument.removeSubrange(range)
            } else {
                // If no nested selections, just remove the field name
                if let range = modifiedDocument.range(of: "\(field.name)\\s*", options: .regularExpression) {
                    modifiedDocument.removeSubrange(range)
                }
            }
        }
        
        // Create new request with modified document
        return GraphQLRequest<M>(
            document: modifiedDocument,
            variables: request.variables,
            responseType: M.self,
            decodePath: request.decodePath,
            options: request.options
        )
    }
}

@mattcreaser mattcreaser added bug Something isn't working api Issues related to the API category labels Nov 6, 2024
@mattcreaser
Copy link
Member

Hi @uxian. Glad you have a workaround available. I've marked this as a bug pending further investigation.

@github-actions github-actions bot removed the pending-maintainer-response Issue is pending response from an Amplify team member label Nov 6, 2024
@mattcreaser mattcreaser removed the pending-triage Issue is pending triage label Nov 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api Issues related to the API category bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants