aws-amplify / amplify-android

The fastest and easiest way to use AWS from your Android app.
https://docs.amplify.aws/lib/q/platform/android/
Apache License 2.0
243 stars 114 forks source link

Cannot Use Amplify-Generated Android Model Files for GraphQL API Requests #2735

Open leomuko opened 5 months ago

leomuko commented 5 months ago

Before opening, please confirm:

Language and Async Model

Java, Kotlin

Amplify Categories

GraphQL API

Gradle script dependencies

```groovy implementation 'com.amplifyframework:core:2.14.6' implementation 'com.amplifyframework:aws-api:2.14.5' ```

Environment information

``` # Put output below this line ```

Please include any relevant guides or documentation you're referencing

https://docs.amplify.aws/android/build-a-backend/graphqlapi/advanced-workflows

Describe the bug

During the development of our Android application, I utilized Amplify's codegen tool to generate model files intended for interfacing with our GraphQL API. These generated files encompass classes that either extend the Model class or are simple Java model classes.

According to the provided documentation, these generated model files should seamlessly facilitate requests to our GraphQL API. However, upon attempting to utilize these files for making requests, we encountered unexpected results. Instead of successful interactions with the API, parsing issues within Amplify arose, leading to the generation of errors.

Notably, the response is returned as a string.

It's worth noting that the files extending the Model class seem to work fine, whereas the "simple Java files" do not return expected results.

Our objective is to leverage these generated model files for Amplify requests, streamlining our process and ensuring smoother migrations of future changes. Currently, we are resorting to using custom files for parsing responses, which adds complexity to our workflow.

I am seeking clarification on the root cause of these parsing failures. I have diligently followed the steps outlined in the AWS documentation. Any insights or assistance in resolving this issue would be greatly appreciated.

Reproduction steps (if applicable)

No response

Code Snippet

fun testGetBatchTeam() : GraphQLRequest<BatchGetTeamOutput>{
        val document = "query BatchGetTeam(" +
                "    \$orgID: ID!\n" +
                "    \$roleSKs: [ID]\n" +
                "    \$deptSKs: [ID]\n" +
                "    \$locationSKs: [ID]\n" +
                "    \$memberSKs: [ID]) {\n" +
                "  BatchGetTeam(" +
                "      orgID: \$orgID\n" +
                "      roleSKs: \$roleSKs\n" +
                "      deptSKs: \$deptSKs\n" +
                "      locationSKs: \$locationSKs\n" +
                "      memberSKs: \$memberSKs) {\n" +
                "    roles {\n" +
                "      id\n" +
                "      orgID\n" +
                "      sortKey\n" +
                "      itemType\n" +
                "      createdAt\n" +
                "      createdBy\n" +
                "      updatedAt\n" +
                "      updatedBy\n" +
                "      roleName\n" +
                "      canManageOrgAccount\n" +
                "      canManageOrgInfo\n" +
                "      canManageSubscriptions\n" +
                "      canManageTeamStructure\n" +
                "      canManageMembers\n" +
                "      canRemoveMembers\n" +
                "      canManageChannels\n" +
                "      canManagePosts\n" +
                "      canCreateTeamStructure\n" +
                "      canCreateMembers\n" +
                "      canCreateChannels\n" +
                "      canCreatePublicChannels\n" +
                "      canCreateRoleBasedChannels\n" +
                "      canCreatePosts\n" +
                "      deletedAt\n" +
                "      deletedBy\n" +
                "    }\n" +
                "    depts {\n" +
                "      id\n" +
                "      orgID\n" +
                "      sortKey\n" +
                "      itemType\n" +
                "      createdAt\n" +
                "      createdBy\n" +
                "      updatedAt\n" +
                "      updatedBy\n" +
                "      deptName\n" +
                "      deletedAt\n" +
                "      deletedBy\n" +
                "    }\n" +
                "    locations {\n" +
                "      id\n" +
                "      orgID\n" +
                "      sortKey\n" +
                "      itemType\n" +
                "      createdAt\n" +
                "      createdBy\n" +
                "      updatedAt\n" +
                "      updatedBy\n" +
                "      locationName\n" +
                "      deletedAt\n" +
                "      deletedBy\n" +
                "    }\n" +
                "    members {\n" +
                "      id\n" +
                "      orgID\n" +
                "      sortKey\n" +
                "      itemType\n" +
                "      createdAt\n" +
                "      createdBy\n" +
                "      updatedAt\n" +
                "      updatedBy\n" +
                "      userID\n" +
                "      chatUserID\n" +
                "      chatSystemChannel\n" +
                "      fullName\n" +
                "      profilePhoto {\n" +
                "        cfImageHash\n" +
                "        cfImageID\n" +
                "      }\n" +
                "      employeeID\n" +
                "      assignedDepts\n" +
                "      assignedLocations\n" +
                "      assignedRoles\n" +
                "      deactivatedAt\n" +
                "      deactivatedBy\n" +
                "      deletedAt\n" +
                "      deletedBy\n" +
                "      phoneNumber\n" +
                "    }\n" +
                "    unprocessedMembers {\n" +
                "      orgID\n" +
                "      sortKey\n" +
                "    }\n" +
                "    unprocessedLocations {\n" +
                "      orgID\n" +
                "      sortKey\n" +
                "    }\n" +
                "    unprocessedDepts {\n" +
                "      orgID\n" +
                "      sortKey\n" +
                "    }\n" +
                "    unprocessedRoles {\n" +
                "      orgID\n" +
                "      sortKey\n" +
                "    }\n" +
                "  }\n" +
                "}"

        return SimpleGraphQLRequest(
            document,
            mapOf(
                "orgID" to "04a731e8-0fed-401d-80d5-c5c7244b7fc8",
                "roleSKs" to listOf("OWNER"),
                "memberSKs" to listOf("TEAM#MEMBER#2024-02-26T12:10:33.648Z")
            ),
            BatchGetTeamOutput::class.java,
            GsonVariablesSerializer()
        )
    }

package co.heyinnovation.desklessworkers.data.models.amplify.generated.model;

import androidx.core.util.ObjectsCompat;

import java.util.Objects;
import java.util.List;

/** This is an auto generated class representing the BatchGetTeamOutput type in your schema. */
public final class BatchGetTeamOutput {
  private final List<Role> roles;
  private final List<Department> depts;
  private final List<Location> locations;
  private final List<Member> members;
  private final List<UnprocessedKeysForOrganisation> unprocessedMembers;
  private final List<UnprocessedKeysForOrganisation> unprocessedLocations;
  private final List<UnprocessedKeysForOrganisation> unprocessedDepts;
  private final List<UnprocessedKeysForOrganisation> unprocessedRoles;
  public List<Role> getRoles() {
      return roles;
  }

  public List<Department> getDepts() {
      return depts;
  }

  public List<Location> getLocations() {
      return locations;
  }

  public List<Member> getMembers() {
      return members;
  }

  public List<UnprocessedKeysForOrganisation> getUnprocessedMembers() {
      return unprocessedMembers;
  }

  public List<UnprocessedKeysForOrganisation> getUnprocessedLocations() {
      return unprocessedLocations;
  }

  public List<UnprocessedKeysForOrganisation> getUnprocessedDepts() {
      return unprocessedDepts;
  }

  public List<UnprocessedKeysForOrganisation> getUnprocessedRoles() {
      return unprocessedRoles;
  }

  private BatchGetTeamOutput(List<Role> roles, List<Department> depts, List<Location> locations, List<Member> members, List<UnprocessedKeysForOrganisation> unprocessedMembers, List<UnprocessedKeysForOrganisation> unprocessedLocations, List<UnprocessedKeysForOrganisation> unprocessedDepts, List<UnprocessedKeysForOrganisation> unprocessedRoles) {
    this.roles = roles;
    this.depts = depts;
    this.locations = locations;
    this.members = members;
    this.unprocessedMembers = unprocessedMembers;
    this.unprocessedLocations = unprocessedLocations;
    this.unprocessedDepts = unprocessedDepts;
    this.unprocessedRoles = unprocessedRoles;
  }

  @Override
   public boolean equals(Object obj) {
      if (this == obj) {
        return true;
      } else if(obj == null || getClass() != obj.getClass()) {
        return false;
      } else {
      BatchGetTeamOutput batchGetTeamOutput = (BatchGetTeamOutput) obj;
      return ObjectsCompat.equals(getRoles(), batchGetTeamOutput.getRoles()) &&
              ObjectsCompat.equals(getDepts(), batchGetTeamOutput.getDepts()) &&
              ObjectsCompat.equals(getLocations(), batchGetTeamOutput.getLocations()) &&
              ObjectsCompat.equals(getMembers(), batchGetTeamOutput.getMembers()) &&
              ObjectsCompat.equals(getUnprocessedMembers(), batchGetTeamOutput.getUnprocessedMembers()) &&
              ObjectsCompat.equals(getUnprocessedLocations(), batchGetTeamOutput.getUnprocessedLocations()) &&
              ObjectsCompat.equals(getUnprocessedDepts(), batchGetTeamOutput.getUnprocessedDepts()) &&
              ObjectsCompat.equals(getUnprocessedRoles(), batchGetTeamOutput.getUnprocessedRoles());
      }
  }

  @Override
   public int hashCode() {
    return new StringBuilder()
      .append(getRoles())
      .append(getDepts())
      .append(getLocations())
      .append(getMembers())
      .append(getUnprocessedMembers())
      .append(getUnprocessedLocations())
      .append(getUnprocessedDepts())
      .append(getUnprocessedRoles())
      .toString()
      .hashCode();
  }

  public static BuildStep builder() {
      return new Builder();
  }

  public CopyOfBuilder copyOfBuilder() {
    return new CopyOfBuilder(roles,
      depts,
      locations,
      members,
      unprocessedMembers,
      unprocessedLocations,
      unprocessedDepts,
      unprocessedRoles);
  }
  public interface BuildStep {
    BatchGetTeamOutput build();
    BuildStep roles(List<Role> roles);
    BuildStep depts(List<Department> depts);
    BuildStep locations(List<Location> locations);
    BuildStep members(List<Member> members);
    BuildStep unprocessedMembers(List<UnprocessedKeysForOrganisation> unprocessedMembers);
    BuildStep unprocessedLocations(List<UnprocessedKeysForOrganisation> unprocessedLocations);
    BuildStep unprocessedDepts(List<UnprocessedKeysForOrganisation> unprocessedDepts);
    BuildStep unprocessedRoles(List<UnprocessedKeysForOrganisation> unprocessedRoles);
  }

  public static class Builder implements BuildStep {
    private List<Role> roles;
    private List<Department> depts;
    private List<Location> locations;
    private List<Member> members;
    private List<UnprocessedKeysForOrganisation> unprocessedMembers;
    private List<UnprocessedKeysForOrganisation> unprocessedLocations;
    private List<UnprocessedKeysForOrganisation> unprocessedDepts;
    private List<UnprocessedKeysForOrganisation> unprocessedRoles;
    public Builder() {

    }

    private Builder(List<Role> roles, List<Department> depts, List<Location> locations, List<Member> members, List<UnprocessedKeysForOrganisation> unprocessedMembers, List<UnprocessedKeysForOrganisation> unprocessedLocations, List<UnprocessedKeysForOrganisation> unprocessedDepts, List<UnprocessedKeysForOrganisation> unprocessedRoles) {
      this.roles = roles;
      this.depts = depts;
      this.locations = locations;
      this.members = members;
      this.unprocessedMembers = unprocessedMembers;
      this.unprocessedLocations = unprocessedLocations;
      this.unprocessedDepts = unprocessedDepts;
      this.unprocessedRoles = unprocessedRoles;
    }

    @Override
     public BatchGetTeamOutput build() {

        return new BatchGetTeamOutput(
          roles,
          depts,
          locations,
          members,
          unprocessedMembers,
          unprocessedLocations,
          unprocessedDepts,
          unprocessedRoles);
    }

    @Override
     public BuildStep roles(List<Role> roles) {
        this.roles = roles;
        return this;
    }

    @Override
     public BuildStep depts(List<Department> depts) {
        this.depts = depts;
        return this;
    }

    @Override
     public BuildStep locations(List<Location> locations) {
        this.locations = locations;
        return this;
    }

    @Override
     public BuildStep members(List<Member> members) {
        this.members = members;
        return this;
    }

    @Override
     public BuildStep unprocessedMembers(List<UnprocessedKeysForOrganisation> unprocessedMembers) {
        this.unprocessedMembers = unprocessedMembers;
        return this;
    }

    @Override
     public BuildStep unprocessedLocations(List<UnprocessedKeysForOrganisation> unprocessedLocations) {
        this.unprocessedLocations = unprocessedLocations;
        return this;
    }

    @Override
     public BuildStep unprocessedDepts(List<UnprocessedKeysForOrganisation> unprocessedDepts) {
        this.unprocessedDepts = unprocessedDepts;
        return this;
    }

    @Override
     public BuildStep unprocessedRoles(List<UnprocessedKeysForOrganisation> unprocessedRoles) {
        this.unprocessedRoles = unprocessedRoles;
        return this;
    }
  }

  public final class CopyOfBuilder extends Builder {
    private CopyOfBuilder(List<Role> roles, List<Department> depts, List<Location> locations, List<Member> members, List<UnprocessedKeysForOrganisation> unprocessedMembers, List<UnprocessedKeysForOrganisation> unprocessedLocations, List<UnprocessedKeysForOrganisation> unprocessedDepts, List<UnprocessedKeysForOrganisation> unprocessedRoles) {
      super(roles, depts, locations, members, unprocessedMembers, unprocessedLocations, unprocessedDepts, unprocessedRoles);

    }

    @Override
     public CopyOfBuilder roles(List<Role> roles) {
      return (CopyOfBuilder) super.roles(roles);
    }

    @Override
     public CopyOfBuilder depts(List<Department> depts) {
      return (CopyOfBuilder) super.depts(depts);
    }

    @Override
     public CopyOfBuilder locations(List<Location> locations) {
      return (CopyOfBuilder) super.locations(locations);
    }

    @Override
     public CopyOfBuilder members(List<Member> members) {
      return (CopyOfBuilder) super.members(members);
    }

    @Override
     public CopyOfBuilder unprocessedMembers(List<UnprocessedKeysForOrganisation> unprocessedMembers) {
      return (CopyOfBuilder) super.unprocessedMembers(unprocessedMembers);
    }

    @Override
     public CopyOfBuilder unprocessedLocations(List<UnprocessedKeysForOrganisation> unprocessedLocations) {
      return (CopyOfBuilder) super.unprocessedLocations(unprocessedLocations);
    }

    @Override
     public CopyOfBuilder unprocessedDepts(List<UnprocessedKeysForOrganisation> unprocessedDepts) {
      return (CopyOfBuilder) super.unprocessedDepts(unprocessedDepts);
    }

    @Override
     public CopyOfBuilder unprocessedRoles(List<UnprocessedKeysForOrganisation> unprocessedRoles) {
      return (CopyOfBuilder) super.unprocessedRoles(unprocessedRoles);
    }
  }

}

Log output

``` //Response is ----- GraphQLResponse{data='co.heyinnovation.desklessworkers.data.models.amplify.generated.model.BatchGetTeamOutput@2b6d2a38', errors='[]'} ```

amplifyconfiguration.json

No response

GraphQL Schema

No response

Additional information and screenshots

No response

yuhengshs commented 5 months ago

Hi @leomuko ,

Someone on our team is taking a look at the issue and will get back to you as soon as we have something.

Thanks for your patience!