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

GraphQL Call on Many-to-many Relationship Does Not Return Related Items #13860

Closed
3 tasks done
desert-digital opened this issue Sep 27, 2024 · 9 comments
Closed
3 tasks done
Assignees
Labels
GraphQL Related to GraphQL API issues not-reproducible Not able to reproduce the issue

Comments

@desert-digital
Copy link

Before opening, please confirm:

JavaScript Framework

Angular

Amplify APIs

GraphQL API

Amplify Version

v6

Amplify Categories

api

Backend

Amplify CLI

Environment information

# Put output below this line
  System:
    OS: macOS 12.7.6
    CPU: (8) x64 Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz
    Memory: 2.49 GB / 16.00 GB
    Shell: 6.21.00 - /bin/tcsh
  Binaries:
    Node: 20.10.0 - /usr/local/bin/node
    Yarn: 1.22.22 - ~/Development/Javascript/compass/compass-frontend/node_modules/.bin/yarn
    npm: 10.2.3 - /usr/local/bin/npm
    pnpm: 9.11.0 - /usr/local/bin/pnpm
  Browsers:
    Chrome: 129.0.6668.60
    Safari: 17.6
  npmPackages:
    @angular-devkit/build-angular: ^18.2.6 => 18.2.6 
    @angular-eslint/builder: ^18.2.0 => 18.3.1 
    @angular-eslint/eslint-plugin: ^18.2.0 => 18.3.1 
    @angular-eslint/eslint-plugin-template: ^18.2.0 => 18.3.1 
    @angular-eslint/schematics: ^18.2.0 => 18.3.1 
    @angular-eslint/template-parser: ^18.2.0 => 18.3.1 
    @angular/animations: ^18.2.6 => 18.2.6 
    @angular/cdk: ^18.2.6 => 18.2.6 
    @angular/cli: ~18.2.6 => 18.2.6 
    @angular/common: ^18.2.6 => 18.2.6 
    @angular/compiler: ^18.2.6 => 18.2.6 
    @angular/compiler-cli: ^18.2.6 => 18.2.6 
    @angular/core: ^18.2.6 => 18.2.6 
    @angular/forms: ^18.2.6 => 18.2.6 
    @angular/material: ^18.2.5 => 18.2.6 
    @angular/platform-browser: ^18.2.6 => 18.2.6 
    @angular/platform-browser-dynamic: ^18.2.6 => 18.2.6 
    @angular/router: ^18.2.6 => 18.2.6 
    @aws-amplify/api: ^6.0.28 => 6.0.51 
    @aws-amplify/api/internals:  undefined ()
    @aws-amplify/api/server:  undefined ()
    @aws-amplify/auth: ^6.0.27 => 6.4.2 
    @aws-amplify/auth/cognito:  undefined ()
    @aws-amplify/auth/cognito/server:  undefined ()
    @aws-amplify/auth/enable-oauth-listener:  undefined ()
    @aws-amplify/auth/server:  undefined ()
    @aws-amplify/core: ^6.0.27 => 6.4.2 
    @aws-amplify/core/internals/adapter-core:  undefined ()
    @aws-amplify/core/internals/aws-client-utils:  undefined ()
    @aws-amplify/core/internals/aws-client-utils/composers:  undefined ()
    @aws-amplify/core/internals/aws-clients/cognitoIdentity:  undefined ()
    @aws-amplify/core/internals/aws-clients/pinpoint:  undefined ()
    @aws-amplify/core/internals/providers/pinpoint:  undefined ()
    @aws-amplify/core/internals/utils:  undefined ()
    @aws-amplify/core/server:  undefined ()
    @aws-amplify/storage: ^6.0.27 => 6.6.7 
    @aws-amplify/storage/s3:  undefined ()
    @aws-amplify/storage/s3/server:  undefined ()
    @aws-amplify/storage/server:  undefined ()
    @aws-amplify/ui-angular: ^5.0.26 => 5.0.26 
    @types/d3: ^7.4.0 => 7.4.3 
    @types/jasmine: ~4.3.2 => 4.3.6 
    @typescript-eslint/eslint-plugin: ^8.0.0 => 8.7.0 
    @typescript-eslint/parser: ^8.0.0 => 8.7.0 
    amplify: ^0.0.11 => 0.0.11 
    angular: ^1.8.3 => 1.8.3 
    aws-amplify: ^6.0.28 => 6.6.2 
    aws-amplify/adapter-core:  undefined ()
    aws-amplify/analytics:  undefined ()
    aws-amplify/analytics/kinesis:  undefined ()
    aws-amplify/analytics/kinesis-firehose:  undefined ()
    aws-amplify/analytics/personalize:  undefined ()
    aws-amplify/analytics/pinpoint:  undefined ()
    aws-amplify/api:  undefined ()
    aws-amplify/api/server:  undefined ()
    aws-amplify/auth:  undefined ()
    aws-amplify/auth/cognito:  undefined ()
    aws-amplify/auth/cognito/server:  undefined ()
    aws-amplify/auth/enable-oauth-listener:  undefined ()
    aws-amplify/auth/server:  undefined ()
    aws-amplify/data:  undefined ()
    aws-amplify/data/server:  undefined ()
    aws-amplify/datastore:  undefined ()
    aws-amplify/in-app-messaging:  undefined ()
    aws-amplify/in-app-messaging/pinpoint:  undefined ()
    aws-amplify/push-notifications:  undefined ()
    aws-amplify/push-notifications/pinpoint:  undefined ()
    aws-amplify/storage:  undefined ()
    aws-amplify/storage/s3:  undefined ()
    aws-amplify/storage/s3/server:  undefined ()
    aws-amplify/storage/server:  undefined ()
    aws-amplify/utils:  undefined ()
    chart.js: ^4.3.0 => 4.4.4 
    chart.js-auto:  undefined ()
    chart.js-helpers:  undefined ()
    d3: ^7.8.5 => 7.9.0 
    eslint: ^9.11.0 => 9.11.1 
    jasmine-core: ~5.0.0 => 5.0.1 (4.6.1)
    karma: ~6.4.2 => 6.4.4 
    karma-chrome-launcher: ~3.2.0 => 3.2.0 
    karma-coverage: ~2.2.0 => 2.2.1 
    karma-coverage-coffee-example:  1.0.0 
    karma-jasmine: ~5.1.0 => 5.1.0 
    karma-jasmine-html-reporter: ~2.1.0 => 2.1.0 
    ng2-charts: ^4.1.1 => 4.1.1 
    npm-check-updates: ^16.10.12 => 16.14.20 
    rxjs: ~7.8.1 => 7.8.1 
    rxjs/ajax:  undefined ()
    rxjs/fetch:  undefined ()
    rxjs/operators:  undefined ()
    rxjs/testing:  undefined ()
    rxjs/webSocket:  undefined ()
    tslib: ^2.5.3 => 2.7.0 (2.6.3)
    typescript: ^5.2.0 => 5.5.4 
    yarn: ^1.22.0 => 1.22.22 
    zone.js: ~0.14.0 => 0.14.10 
  npmGlobalPackages:
    @angular/cli: 17.3.3
    @aws-amplify/cli: 11.0.2
    bootstrap: 3.3.7
    bower: 1.8.0
    browser-sync: 2.17.5
    corepack: 0.22.0
    firebase-tools: 3.0.8
    generator-angular: 0.15.1
    generator-bootstrap: 0.2.2
    generator-gulp-angular: 1.1.1
    generator-karma: 2.0.0
    generator-node-webkit: 2.0.0-alpha.3
    generator-nwjs: 0.0.3
    generator-oldmen: 0.1.13
    generator-react-gulp-browserify-less: 1.0.1
    grunt-cli: 1.2.0
    gulp: 3.9.1
    http-server: 0.9.0
    istanbul: 0.4.5
    jasmine-node: 1.14.5
    jekyll-cli: 0.3.9
    karma-cli: 1.0.1
    mocha: 3.2.0
    n: 5.0.1
    nmp: 1.0.3
    nodewebkit: 0.11.6
    npm-check-updates: 16.10.12
    npm: 10.2.3
    protractor: 7.0.0
    typescript: 5.4.4
    yo: 1.8.5


Describe the bug

No items are returned by GraphQL call on many-to-many relation. Items exist in the correctly named DynamoDB join table. Specific error is "TypeError: checklistModel.actionModels.items is not iterable"

The definition of the ChecklistModel and ActionModel types are below. All models in the schema file are public (to eliminate any security related errors that may be causing this issue).

type ActionModel @model @auth(rules: [{ allow: public }]){
  id: ID!
  company: String!
  name: String
  notes: String 
  duration: Int
  checklists: [ChecklistModel] @manyToMany(relationName: "ChecklistActions")
}

type ChecklistModel @model @auth(rules: [{ allow: public }]){
  id: ID!
  company: String!
  name: String
  notes: String
  duration: Int
  preCharter: Boolean
  actionModels: [ActionModel] @manyToMany(relationName: "ChecklistActions")
  workflows: [WorkflowModel] @manyToMany(relationName: "WorkflowChecklists") 
}

Expected behavior

checklistModel.actionModels.items is an array with one item (and so therefore iterable)

Reproduction steps

  1. Select 'Checklist Admin'
  2. Select a Checklist item
  3. Open the context-based menu, and select 'edit'

The error occurs when the EditChecklist component is opened.

Screen Shot 2024-09-27 at 14 11 16

Code Snippet

// Put your code below this line.

// Code used to create a Checklist Model with Action Models
// In the add-checklist component

  async onAddChecklistPressed(model: ChecklistModel, formDirective: FormGroupDirective) {
    try {
      this._checklistModelsService.createChecklistModel(model, this.checklist).then(() => {
        this._snackBar.open('Created a new checklist', 'OK', { duration: 3000 });
        this.checklistForm.reset();
        formDirective.resetForm();
        this.checklist = [];
        this._getActions();
      });
    } catch (error) {
      this._snackBar.open('Error creating the checklist', 'OK', { duration: 3000 });
    }
  }

// In the ChecklistModel service

  async createChecklistModel(checklistModel: ChecklistModel, actions: ActionModel[]) {
    const checklistMutationResult = await this.client.graphql({
      query: createChecklistModel,
      variables: {
        input: {
          company: 'seaforth',
          name: checklistModel.name,
          duration: checklistModel.duration,
          notes: checklistModel.notes,
          preCharter: checklistModel.preCharter,
        }
      }
    });

    const checklist = checklistMutationResult.data.createChecklistModel;

    for (const action of actions) {
      await this.client.graphql({
        query: createChecklistActions,
        variables: {
          input: {
            actionModelId: action.id,
            checklistModelId: checklist.id
          },
        }
      });
    }
    
    const updatedChecklist = await this.getChecklistModelFromId(checklist.id);
    console.log(updatedChecklist.actionModels);
  }

// Code to retrieve the ChecklistModel
// In the ChecklistModel service


  async getChecklistModelFromId(id: string): Promise<ChecklistModel> {
    const checklistResult = await this.client.graphql({
      query: getChecklistModel,
      variables: { id: id }
    })
    return checklistResult.data.getChecklistModel as ChecklistModel;
  }

Log output

The item from the join table is shown below. The actionModelId and checklistModelId match the ids from the corresponding ActionModel and ChecklistModel tables. The value returned from the getChecklistModelFromId is also attached.

// Put your logs below this line

Screen Shot 2024-09-27 at 14 18 49

Screen Shot 2024-09-27 at 14 19 26

Screen Shot 2024-09-27 at 14 20 29

Screen Shot 2024-09-27 at 14 31 13

aws-exports.js

No response

Manual configuration

No response

Additional configuration

No response

Mobile Device

No response

Mobile Operating System

No response

Mobile Browser

No response

Mobile Browser Version

No response

Additional information and screenshots

No response

@github-actions github-actions bot added pending-triage Issue is pending triage pending-maintainer-response Issue is pending a response from the Amplify team. labels Sep 27, 2024
@HuiSF HuiSF added the GraphQL Related to GraphQL API issues label Sep 27, 2024
@chrisbonifacio chrisbonifacio self-assigned this Oct 1, 2024
@chrisbonifacio chrisbonifacio added to-be-reproduced Used in order for Amplify to reproduce said issue and removed pending-triage Issue is pending triage labels Oct 1, 2024
@chrisbonifacio
Copy link
Member

Hi @desert-digital 👋 thanks for raising this issue and providing detailed reproduction steps!

I will attempt to reproduce and report back with any findings.

@github-actions github-actions bot removed the pending-maintainer-response Issue is pending a response from the Amplify team. label Oct 7, 2024
@desert-digital
Copy link
Author

hey @chrisbonifacio: any insight on this? thanks!

@github-actions github-actions bot added the pending-maintainer-response Issue is pending a response from the Amplify team. label Oct 18, 2024
@desert-digital
Copy link
Author

...or perhaps @HuiSF / @chrisbonifacio this is a good opportunity to move this to Gen 2? Does many-to-many work in Gen 2? I ask because the Data table at this link says "No" for manyToMany but the documentation at this link shows how a many-to-many relationship can be built. Thanks again!

@desert-digital
Copy link
Author

Hi @HuiSF and @chrisbonifacio: I am kind of stuck here. Any update? Thanks! Ian.

@chrisbonifacio
Copy link
Member

Hi @desert-digital, I was not able to reproduce the behavior described in this issue.

I deployed this graphql schema:

type ActionModel @model @auth(rules: [{ allow: public }]) {
  id: ID!
  company: String!
  name: String
  notes: String
  duration: Int
  checklists: [ChecklistModel] @manyToMany(relationName: "ChecklistActions")
}

type ChecklistModel @model @auth(rules: [{ allow: public }]) {
  id: ID!
  company: String!
  name: String
  notes: String
  duration: Int
  preCharter: Boolean
  actionModels: [ActionModel] @manyToMany(relationName: "ChecklistActions")
}

and used this code to create the checklistModel, actionModel, and the checklistAction join table record.

const getChecklistModelQuery = async (id: string) => {
    const checklistModel = await client.graphql({
      query: getChecklistModel,
      variables: {
        id,
      } as GetChecklistModelQueryVariables,
    });

    console.log(checklistModel.data.getChecklistModel);

    return checklistModel.data.getChecklistModel;
  };

  const createCheckListModelMutation = async () => {
    const result = await client.graphql<
      GraphQLQuery<CreateChecklistModelMutation>
    >({
      query: createChecklistModel,
      variables: {
        input: {
          company: "testChecklist",
        },
      } as CreateChecklistModelMutationVariables,
    });

    console.log(result.data.createChecklistModel);

    return result.data.createChecklistModel;
  };

  const createActionModelMutation = async () => {
    const result = await client.graphql<
      GraphQLQuery<CreateActionModelMutation>
    >({
      query: createActionModel,
      variables: {
        input: {
          company: "testAction",
        },
      } as CreateActionModelMutationVariables,
    });

    console.log(result.data.createActionModel);

    return result.data.createActionModel;
  };

  const createChecklistActionsMutation = async () => {
    const checklistModel = await createCheckListModelMutation();
    setId(checklistModel?.id as string);
    const actionModel = await createActionModelMutation();

    const result = await client.graphql<
      GraphQLQuery<CreateChecklistActionsMutation>
    >({
      query: createChecklistActions,
      variables: {
        input: {
          actionModelId: actionModel?.id,
          checklistModelId: checklistModel?.id,
        },
      } as CreateChecklistActionsMutationVariables,
    });

    const checklistModelQueryResult = await getChecklistModelQuery(
      checklistModel?.id as string
    );

    console.log("ChecklistAction:", result.data.createChecklistActions);
    console.log("ChecklistModel:", checklistModelQueryResult);
  };

This is the result:

CleanShot 2024-10-28 at 14 19 43@2x

The join record is created. The result of the getChecklistModel query doesn't return any related actionModels can be explained because the query does not include it in the selection set:

export const getChecklistModel = /* GraphQL */ `query GetChecklistModel($id: ID!) {
  getChecklistModel(id: $id) {
    id
    company
    name
    notes
    duration
    preCharter
    actionModels {
      nextToken
      __typename
    }
    createdAt
    updatedAt
    __typename
  }
}
` as GeneratedQuery<
  APITypes.GetChecklistModelQueryVariables,
  APITypes.GetChecklistModelQuery
>;

if i adjust the query to include actionModel items, i get the related actionModel items:

CleanShot 2024-10-28 at 14 25 25@2x

@github-actions github-actions bot removed the pending-maintainer-response Issue is pending a response from the Amplify team. label Oct 28, 2024
@chrisbonifacio
Copy link
Member

I tried reproducing with the latest versions of the amplify packages. Can you make sure you are using the latest versions of the amplify packages?

Try upgrading and let me know if that helps resolve the issue.

@chrisbonifacio chrisbonifacio added not-reproducible Not able to reproduce the issue pending-community-response Issue is pending a response from the author or community. and removed to-be-reproduced Used in order for Amplify to reproduce said issue labels Oct 28, 2024
@desert-digital
Copy link
Author

Hi: thanks so much @chrisbonifacio.

Just upgrading resulted in no change, unfortunately; was certainly worth a try.

I created another Checklist, and can see a new ChecklistActions record, so the creation is (still) works. When I look at the query in queries.ts, it is the same as the query above that doesn't work

export const getChecklistModel = /* GraphQL */ `query GetChecklistModel($id: ID!) {
  getChecklistModel(id: $id) {
    id
    company
    name
    notes
    duration
    preCharter
    actionModels {
      nextToken
      __typename
    }
    workflows {
      nextToken
      __typename
    }
    createdAt
    updatedAt
    __typename
  }
}
` as GeneratedQuery<
  APITypes.GetChecklistModelQueryVariables,
  APITypes.GetChecklistModelQuery
>;

Interestingly, the GraphQL query in queries.graphql is different:

query GetChecklistModel($id: ID!) {
  getChecklistModel(id: $id) {
    id
    company
    name
    notes
    duration
    preCharter
    actions {
      items {
        id
        actionModelId
        checklistModelId
        createdAt
        updatedAt
        __typename
      }
      nextToken
      __typename
    }
    workflows {
      items {
        id
        checklistModelId
        workflowModelId
        createdAt
        updatedAt
        __typename
      }
      nextToken
      __typename
    }
    createdAt
    updatedAt
    __typename
  }
}

So my next question is: shouldn't the query in queries.ts (which is generated by codegen) include the action model items?! Do I need to generate the queries using typecript instead of angular (when I created the API)?

Thanks! Ian.

@github-actions github-actions bot added pending-maintainer-response Issue is pending a response from the Amplify team. and removed pending-community-response Issue is pending a response from the author or community. labels Oct 28, 2024
@desert-digital
Copy link
Author

Is this the correct place to get the queries?

// Amplify

import { generateClient } from '@aws-amplify/api';
import {
  listChecklistModels,
  getChecklistModel,
  checklistActionsByChecklistModelId
} from '../../graphql/queries';
import {
  createChecklistModel,
  deleteChecklistModel,
  createChecklistActions,
  deleteChecklistActions
} from '../../graphql/mutations';

// Local

import { ChecklistModel, ActionModel } from '../API.service';
import { ActionsService } from './actions.service';

@desert-digital
Copy link
Author

desert-digital commented Oct 29, 2024

OK: it's working. I ran

amplify codegen configure

and selected typescript, and then the defaults. The queries.ts file (in the graphql/queries folder) was updated, and I could see the correct GraphQL is there now. Thanks so much for pointing me in the right direction!

@github-actions github-actions bot removed the pending-maintainer-response Issue is pending a response from the Amplify team. label Oct 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
GraphQL Related to GraphQL API issues not-reproducible Not able to reproduce the issue
Projects
None yet
Development

No branches or pull requests

3 participants