AWS Amplify DataStore causes syncing issues on login

0

Context

I am using AWS Amplify for a full-stack website. The front end is React JS made by Create-React-App. I use the Amplify DataStore between the AppSync GraphQl API and the application. The website manages tutors and clients to find peer tutoring sessions. In the database, there are Client and Tutor models, each with a field called authToken. Each person's Cognito login user has a sub token which is linked to the authToken field in the database.

The App Sync has permissions which allow for unauthenticated and authenticated users to access to the data.

type Client @model @auth(rules: [{allow: groups, groups: ["admins"], operations: [read, create, update, delete]}, {allow: private}, {allow: public, operations: [read]}, {allow: private, provider: iam}]) {
  id: ID!
  email: AWSEmail!
  firstName: String!
  lastName: String!
  authToken: String!
  phone: AWSPhone
  credits: Float!
  Students: [Student] @hasMany(indexName: "byClient", fields: ["id"])
}

type Tutor @model @auth(rules: [{allow: public, operations: [read, create, update]}, {allow: groups, groups: ["admins"], operations: [read, create, update, delete]}, {allow: groups, groups: ["tutors"], operations: [read, update, delete, create]}, {allow: private}, {allow: private, provider: iam}]) {
  id: ID!
  email: AWSEmail!
  firstName: String!
  lastName: String!
  authToken: String!
  phone: AWSPhone
}

Problem

In production, tutors have reported issues logging into their dashboards. Either the data doesn't load, or they are greeted with a warning screen saying that their Tutor model cannot be found.

Current code


/**
 * Takes a call back function which when the data syncing is ready, will be called to return the current user's tutor model id, if the current user is a tutor
 */
export async function setCurrentUserTutorCallback(
  setUser: (tutor: LazyTutor | null) => void
) {
  let sub = await getCurrrentUserSub();
  if (!sub || sub == null) {
    console.log(`Cannot find tutor id for sub: ${sub}`);
    setUser(null);
    return null;
  } else {
    // @ts-ignore
    DataStore.observeQuery(Tutor, (t) => t.authToken.eq(sub)).subscribe(
      async (snapshot) => {
        const { items, isSynced } = snapshot;

        if (isSynced) {
          if (items.length === 1) {
            setUser(items[0]);
          } else if (items.length === 0) {
            setUser(null);
            console.log("No tutor found for sub: " + sub);
          } else {
            setUser(null);
            console.error(`Found ${items.length} tutors for sub: ${sub}`);
            await createAnalytic("ERROR", {
              component: "authUtilities",
              function: "setCurrentUserTutorCallback",
              error: `Found ${items.length} tutors for sub: ${sub}`,
            });
          }
        }
      }
    );
  }
  return null;
}

export default function ClientDashboard() {
  const [tutor, setTutor] = useState<LazyTutor | null>(null);
  const [isLoadingTutor, setIsLoadingTutor] = useState(false);

  // used as a callback to get the current user's tutor id from a query observer in DataStore

  const setTutorCallback = (tutor: LazyTutor | null) => {
    if (tutor) {
      setTutor(tutor);
      setIsLoadingTutor(false);
      console.log("SET tutor id: " + tutor.id + " and name " + tutor.firstName);
    } else {
      setTutor(null);
    }
  };

  useEffect(() => {
    const getUserTutorId = () => {
      setCurrentUserTutorCallback(setTutorCallback);
    };
    getUserTutorId();
  }, []);
}

if (!tutor) {
  if (isLoadingTutor) {
    return (... a blinking placeholder box)
  } else {
    return (<h1> can't find your tutor client!</h1>
  }
} else {
  return (<div> normal looking dashboard </div>
}

The current frontend is now showing that the tutor's client model isn't loading, thus showing them they can't log in. I'm stumped because I thought by allowing for loading tutor data publically and privately, then using a Datastore.observeQuery(), it would wait until the data is synced, then return their tutor model.

No Answers

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.

Guidelines for Answering Questions