Skip to content

get item by partition key returns error only inside lambda #412

@orr-levinger

Description

@orr-levinger

Describe the bug
This bug happens to me only when running in a lambda. When running locally this doesnt happen.
here are my libs versions:

dynamo-easy version: 8.0.0-next.3
aws v3 dynamo version: 3.445.0
Node.js Version: v18.18.0

I have a model that has a string field called status:

import {
  Model,
  PartitionKey,
  SortKey,
  LSISortKey,
  GSIPartitionKey,
  GSISortKey,
  Property,
  fromDb as fromDBB,
  PropertyMetadata,
  metadataForModel,
  Attributes,
  ModelConstructor,
} from '@shiftcoders/dynamo-easy';
import { BaseDynamoModel } from '@models/base-dynamo-model';
import { MATCH_TABLE } from '@static/consts';
import { MatchGraphQLType, Quality, RejectReason } from '@type/Match';
import type { MatchStatus } from '@type/Match';
import { Avatar, Gender } from '@type/User';
import type { MapperForType, StringAttribute } from '@shiftcoders/dynamo-easy';


@Model({ tableName: MATCH_TABLE })
export class MatchModel extends BaseDynamoModel implements MatchGraphQLType {
  static readonly byUniqueId = 'byUniqueId';
  static readonly byMatchIdUserId = 'byMatchId-userId';
  static readonly byUserIdStatus = 'byUserId-status';
  static readonly byUserIdActive = 'byUserId-active';
  static readonly byConversationId = 'byConversationId';
  @GSIPartitionKey(MatchModel.byUniqueId)
  id: string;
  @PartitionKey()
  @GSISortKey(MatchModel.byMatchIdUserId)
  userId: string;
  @SortKey()
  @GSIPartitionKey(MatchModel.byMatchIdUserId)
  matchId: string;

  @LSISortKey(MatchModel.byUserIdStatus)
  @Property({ mapper: statusMapper })
  **status: MatchStatus;**

  @LSISortKey(MatchModel.byUserIdActive)
  active: 'true' | 'false';
}

export type MatchStatus =
  | 'pending'
  | 'invited'
  | 'accepted'
  | 'failure'
  | 'success';

here is the DDB table definition:

  MatchesTable:
    DeletionPolicy: Delete
    Type: "AWS::DynamoDB::Table"
    Properties:
      Tags:
        - Key: env
          Value: ${sls:stage}
      AttributeDefinitions:
        - AttributeName: id
          AttributeType: S
        - AttributeName: status
          AttributeType: S
        - AttributeName: userId
          AttributeType: S
        - AttributeName: matchId
          AttributeType: S
        - AttributeName: active
          AttributeType: S
      KeySchema:
        - AttributeName: userId
          KeyType: HASH
        - AttributeName: matchId
          KeyType: RANGE
      LocalSecondaryIndexes:
        - IndexName: byUserId-status
          KeySchema:
            - AttributeName: userId
              KeyType: HASH
            - AttributeName: status
              KeyType: RANGE
          Projection:
            ProjectionType: ALL
        - IndexName: byUserId-active
          KeySchema:
            - AttributeName: userId
              KeyType: HASH
            - AttributeName: active
              KeyType: RANGE
          Projection:
            ProjectionType: ALL
      GlobalSecondaryIndexes:
        - IndexName: byUniqueId
          KeySchema:
            - AttributeName: id
              KeyType: HASH
          Projection:
            ProjectionType: ALL
        - IndexName: byMatchId-userId
          KeySchema:
            - AttributeName: matchId
              KeyType: HASH
            - AttributeName: userId
              KeyType: RANGE
          Projection:
            ProjectionType: ALL
      BillingMode: PAY_PER_REQUEST
      TableName: ${self:custom.base}-matches
      StreamSpecification:
        StreamViewType: NEW_AND_OLD_IMAGES

here is my table item instance:

  "userId": {
    "S": "f43276be-3a2c-4888-b2b9-9ae23471094c"
  },
  "matchId": {
    "S": "4913f5ad-198e-4fcd-b929-2f3e996ce6ce"
  },
  "active": {
    "S": "true"
  },
  "id": {
    "S": "f43276be-3a2c-4888-b2b9-9ae23471094c_4913f5ad-198e-4fcd-b929-2f3e996ce6ce"
  },
  "status": {
    "S": "invited"
  }
}

this is the query i run:

  async getMatchById(id: string) {
    return this.query().index(MatchModel.byUniqueId).wherePartitionKey(id).execSingle();
  }

only when this code runs inside lambda i get the error:
{
"errorType": "Error",
"errorMessage": "could not resolve the dynamo db type for attribute value invited",
"stack": [
"Error: could not resolve the dynamo db type for attribute value invited",
" at typeOfFromDb (/opt/nodejs/node_modules/@shiftcoders/dynamo-easy/src/mapper/util.ts:231:9)",
" at fromDbOne (/opt/nodejs/node_modules/@shiftcoders/dynamo-easy/src/mapper/mapper.ts:291:64)",
" at /opt/nodejs/node_modules/@shiftcoders/dynamo-easy/src/mapper/mapper.ts:274:20",
" at Array.forEach ()",
" at fromDb (/opt/nodejs/node_modules/@shiftcoders/dynamo-easy/src/mapper/mapper.ts:244:44)",
" at Object.fromDb (/var/task/src/functions/events/webpack:/blind-chat-backend/src/models/match-model.ts:75:14)",
" at /opt/nodejs/node_modules/@shiftcoders/dynamo-easy/src/mapper/mapper.ts:268:50",
" at Array.forEach ()",
" at fromDb (/opt/nodejs/node_modules/@shiftcoders/dynamo-easy/src/mapper/mapper.ts:244:44)",
" at /opt/nodejs/node_modules/@shiftcoders/dynamo-easy/src/dynamo/request/read-many.request.ts:222:63",
" at Array.map ()",
" at ReadManyRequest.mapFromDb (/opt/nodejs/node_modules/@shiftcoders/dynamo-easy/src/dynamo/request/read-many.request.ts:222:43)",
" at processTicksAndRejections (node:internal/process/task_queues:95:5)",
" at Runtime.v [as handler] (/var/task/src/functions/events/webpack:/blind-chat-backend/src/functions/events/get-match.ts:16:3)"
]
}

when creating a property mapper:

export const statusMapper: MapperForType<string, StringAttribute> = {
  fromDb: (attributeValue: any): any => {
    console.log('fromDb orr', attributeValue);
    return attributeValue.S;
  },
  toDb: (modelValue: any): any => {
    console.log('toDb orr', modelValue);
    try {
      return modelValue;
    } catch (e) {
      console.log('error', e);
      return modelValue;
    }
  },
};

that does nothing special just what dynamo-easy was supposed to do it works..
and prints:
fromDb orr { S: 'invited' }

also noticed the wrong mapper is chosen:
INFO dynamo.mapper.mapper (Object): map toDb {"item":"Conscientiousness"}

image

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions