fireorm icon indicating copy to clipboard operation
fireorm copied to clipboard

not able to update map (nested objects)

Open GorvGoyl opened this issue 3 years ago • 3 comments

I'm not able to update User object which contains map of AuthData.

export class AuthData {
  id = "";
  email = "";
  displayName = "";
  emailVerified = false;
}

@Collection("users")
export class User {
  id = "";
  @Type(() => AuthData)
  authData? = new AuthData();
}

Error:

Update() requires either a single JavaScript object or an alternating list of field/value pairs that can be followed by an optional precondition. Value for argument "dataOrField" is not a valid Firestore document. Couldn't serialize object of type "AuthData" (found in field "authData"). Firestore doesn't support JavaScript objects with custom prototypes (i.e. objects that were created via the "new" operator).

I though adding @Type(() => AuthData) should be sufficient to make that error go away. is there anything else required? I also thought of adding plainToClass i.e. await userRepository.update(plainToClass(User, user); but firebase gives the same error.

GorvGoyl avatar May 25 '21 22:05 GorvGoyl

Hey! Can you reproduce the error in a minimal repo so we can take a look?

Also, I think this PR may fix the error, might get released soon.

wovalle avatar May 30 '21 12:05 wovalle

hey Wovalle, I found an easier approach than decorating maps in my collection classes in order to store data in firebase. I made a helper function and use it like this await userRepository.update(plainObject(user))

export function plainObject(obj: any): any {
  return JSON.parse(JSON.stringify(obj));
}

in your experience is this approach okay?

GorvGoyl avatar May 30 '21 18:05 GorvGoyl

Yeah I have done a similar approach but my approach might be a little overkill lol

/**
 * Cleans all JavaScript referneces and prototypes from Object
 * so it can be stored in Firestore.
 * @param input The JS Object to clean
 */
export default async function cleanObjectOfReferences(input: any) {
  const data = input;
  for (const key of Object.keys(input)) {
    const value = input[key];
    if (!value) continue;
    try {
      if (value?.constructor?.name === "Object") {
        data[key] = await cleanObjectOfReferences(value);
      } else if (value?.constructor?.name === "DocumentReference") {
        delete data[key];
      } else if (value?.constructor?.name === "Timestamp") {
        data[key] = value.toDate();
      } else if (value?.constructor?.name === "Array") {
        const cleanArray: any[] = [];
        for (const item of data[key]) {
          cleanArray.push(await cleanObjectOfReferences(item));
        }
        data[key] = cleanArray;
      } else if (
        typeof value === "object" &&
        value?.constructor?.name !== "Date"
      ) {
        data[key] = await cleanObjectOfReferences(
          JSON.parse(JSON.stringify(value))
        );
      }
    } catch (err) {
      delete data[key];
    }
  }

  return JSON.parse(JSON.stringify(data));
}

@wovalle is this where the @Serialize() thing I have been seeing issues around solves?

popcorn245 avatar May 31 '21 03:05 popcorn245