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

@dbclkclk TypeError: Cannot read properties of undefined (reading 'attributeTypes') #1628

Open
sacpat-az opened this issue Aug 9, 2023 · 3 comments

Comments

@sacpat-az
Copy link

I am running into a weird issue with dynamoose... The stack is as below:

TypeError: Cannot read properties of undefined (reading 'attributeTypes')

The errors happens in the following function of Item.js:

static isDynamoObject(object, recursive) {
function isValid(value) {
if (typeof value === "undefined" || value === null) {
return false;
}
const keys = Object.keys(value);
const key = keys[0];
const nestedResult = typeof value[key] === "object" && !(value[key] instanceof Buffer) && !(value[key] instanceof Uint8Array) ? Array.isArray(value[key]) ? value[key].every((value) => Item.isDynamoObject(value, true)) : Item.isDynamoObject(value[key]) : true;
const { Schema } = require("./Schema");
const attributeType = Schema.attributeTypes.findDynamoDBType(key);
return typeof value === "object" && keys.length === 1 && attributeType && (nestedResult || Object.keys(value[key]).length === 0 || attributeType.isSet);
}
const keys = Object.keys(object);
const values = Object.values(object);
if (keys.length === 0) {
return null;
}
else {
return recursive ? isValid(object) : values.every((value) => isValid(value));
}
}
it returns the following as undefined:

    const { Schema } = require("./Schema");

It works for one model but not for other. I am new to TypeScript and Dynamoose but pretty clueless about why it would fail to load this module only in certain situations.

Expecting Schema to be loaded... I wrote an integration test and within the same integration test, it works fine when I query one model by Id, but fails when I try to do the same for other model.

@sacpat-az
Copy link
Author

This is my UserDataSource...

import { logger, model, Schema } from "dynamoose";

import { IndexType, Schema as SchemaType } from "dynamoose/dist/Schema";

import { GraphQLError } from "graphql";

import { Condition } from "dynamoose/dist/Condition";
import { Model } from "dynamoose/dist/Model";
import { AnyItem, Item } from "dynamoose/dist/Item";

import dynamoDataSource from "../datasources/dynamodb";

export interface User extends AnyItem, Item {}

class UserDataSource {
private readonly schema: SchemaType;

private model: Model;

private readonly idpIdIndex: string;

private readonly emailIndex: string;

private readonly firstNameIndex: string;

private readonly lastNameIndex: string;

constructor(local = false) {
this.idpIdIndex = ${process.env.DYNAMODB_USERS_TABLE_NAME}-idpId-index;
this.emailIndex = ${process.env.DYNAMODB_USERS_TABLE_NAME}-email-index;
this.firstNameIndex = ${process.env.DYNAMODB_USERS_TABLE_NAME}-firstName-index;
this.lastNameIndex = ${process.env.DYNAMODB_USERS_TABLE_NAME}-lastName-index;

dynamoDataSource(local);
this.schema = new Schema({
  id: {
    type: String,
    hashKey: true,
  },
  firstName: {
    type: String,
    required: true,
    index: {
      name: this.firstNameIndex,
      type: IndexType.global,
    },
  },
  lastName: {
    type: String,
    required: true,
    index: {
      name: this.lastNameIndex,
      type: IndexType.global,
    },
  },
  email: {
    type: String,
    required: true,
    index: {
      name: this.emailIndex,
      type: IndexType.global,
    },
  },
  idpId: {
    type: String,
    index: {
      name: this.idpIdIndex,
      type: IndexType.global,
    },
  },
  active: Boolean,
  createdAt: {
    type: Number,
    required: true,
  },
  createdBy: {
    type: String,
    required: true,
  },
  roles: {
    type: Array,
    required: true,
    schema: [String],
  },
  updatedAt: Number,
  updatedBy: String,
});

this.model = model<User>(
  process.env.DYNAMODB_USERS_TABLE_NAME,
  this.schema,
);

}

async create(
id: string,
firstName: string,
lastName: string,
email: string,
role: string,
createdBy = "SYSTEM",
): Promise {
(await logger()).providers.set(console);

let response: AnyItem;

try {
  response = await this.model.create({
    id,
    firstName: firstName.toUpperCase().trim(),
    lastName: lastName.toUpperCase().trim(),
    email: email.toUpperCase().trim(),
    roles: [role],
    active: true,
    createdAt: Date.now(),
    idpId: "NA",
    createdBy,
  });
} catch (error) {
  throw new GraphQLError(error);
}

return response;

}

async searchUsers(searchTextInput: string, role: string): Promise<User[]> {
try {
const searchText = searchTextInput.toUpperCase();
const filterCondition = new Condition({
FilterExpression:
"active = :active and (contains(firstName, :searchString) or contains(lastName, :searchString) or contains(email, :searchString)) and contains (#roles , :role)",
ExpressionAttributeNames: {
"#roles": "roles",
},
ExpressionAttributeValues: {
":active": true,
":searchString": searchText,
":role": role,
},
});

  const result = await this.model.scan(filterCondition).exec();

  if (result) {
    return result;
  }
  return [];
} catch (error) {
  throw new GraphQLError(error);
}

}

async getByIdpId(idpId: string): Promise {
const result = await this.model
.query("idpId")
.eq(idpId)
.using(this.idpIdIndex)
.exec();
if (result) return result[0];

return null;

}

async findByEmail(email: string): Promise {
const result = await this.model
.query("email")
.eq(email.toUpperCase().trim())
.using(this.emailIndex)
.exec();
if (result) return result[0];
return null;
}

async filterCosmosUsers(users: User[]): Promise<User[]> {
try {
const emails = users.map((u) => u.email.toUpperCase());

  console.log("Filtering out emails if exist in Cosmos: ", emails);

  const items = await this.model
    .scan("email")
    .attributes(["email"])
    .using(this.emailIndex)
    .in(emails)
    .exec();

  if (items && items.length > 0) {
    return users.filter(
      (u) =>
        // Prettier automatically adds new line, and ESLint complains about it
        // eslint-disable-next-line
        !items.find(
          (el) =>
            // eslint-disable-next-line
            u.email.toUpperCase() === el.email,
        ),
    );
  }

  return users;
} catch (error) {
  throw new GraphQLError(error);
}

}

async update(user: Partial): Promise {
let response;

try {
  response = await this.model.update(user);
} catch (error) {
  throw new GraphQLError(error);
}

return response;

}

async getAllByIds(ids: string[]): Promise<any[]> {
const users = await this.model.batchGet(ids);
return users;
}
}

export default UserDataSource;

The following is the function which is called to look up user by IDP Id:

export async function getUserByIdpId(
userDataSource: UserDataSource,
idpId: string,
) {
const user = await userDataSource.getByIdpId(idpId);
if (user && user.active) {
return user;
}

throw new Error("Invalid User");
}

@sacpat-az
Copy link
Author

Please note, we use Apollo GraphQL Server.... So I have two integration tests... one which goes thru Apollo and the other one directly thru UserDataSource... The latter one never has any issues whatsoever.

@github-actions
Copy link
Contributor

This issue is stale because it has been open 7 days with no activity. Remove stale label or comment or this will be closed in 3 days.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants