aws-amplify / amplify-category-api

The AWS Amplify CLI is a toolchain for simplifying serverless web and mobile development. This plugin provides functionality for the API category, allowing for the creation and management of GraphQL and REST based backends for your amplify project.
https://docs.amplify.aws/
Apache License 2.0
89 stars 77 forks source link

Deployment timeout #2189

Closed festusyuma closed 3 months ago

festusyuma commented 10 months ago

Environment information

System:
    OS: macOS 14.2.1
    CPU: (10) arm64 Apple M1 Pro
    Memory: 47.08 MB / 16.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 18.18.2 - ~/.nvm/versions/node/v18.18.2/bin/node
    Yarn: 1.22.21 - ~/.nvm/versions/node/v18.18.2/bin/yarn
    npm: 9.8.1 - ~/.nvm/versions/node/v18.18.2/bin/npm
    pnpm: 8.10.5 - ~/.nvm/versions/node/v18.18.2/bin/pnpm
    bun: 1.0.0 - ~/.bun/bin/bun
    Watchman: Not Found
  npmPackages:
    @aws-amplify/backend: ^0.8.0 => 0.8.0 
    @aws-amplify/backend-cli: ^0.9.3 => 0.9.3 
    aws-amplify: ^6.0.6 => 6.0.7 
    aws-cdk: 2.110.1 => 2.110.1 
    aws-cdk-lib: ^2.110.1 => 2.110.1 
    typescript: ^5.3.3 => 5.3.3

Description

When deploying a schema with a couple of tables and foreing keys. the deployment process times out. I can confirm the model size is the issue becuase when I trim it down a little bit and deploy, it works, and then re-add the tables I removed and redoploy, it also works again

here's my full schema

Institution: a
    .model({
      id: a.string().required(),
      name: a.string().required(),
      teachers: a.hasMany('Teacher'),
      stats: a.hasMany('Stats'),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow.groupDefinedIn('adminRole').to(['read']),
      a.allow.groupDefinedIn('teacherRole').to(['read']),
      a.allow.groupDefinedIn('studentRole').to(['read']),
    ]),
  Profile: a
    .model({
      firstName: a.string().required(),
      lastName: a.string().required(),
      bio: a.string(),
      profileImage: a.url(),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow.owner().inField('id').to(['read', 'update', 'create']),
      a.allow.groupDefinedIn('adminRole'),
      a.allow.groupDefinedIn('teacherRole').to(['read']),
      a.allow.groupDefinedIn('studentRole').to(['read']),
    ]),
  Teacher: a
    .model({
      subject: a.string(),
      profile: a.hasOne('Profile'),
      institution: a.belongsTo('Institution'),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow.groupDefinedIn('adminRole'),
      a.allow.groupDefinedIn('teacherRole').to(['read']),
      a.allow.groupDefinedIn('studentRole').to(['read']),
    ]),
  Stats: a
    .model({
      key: a.string().required(),
      value: a.integer().required(),
      time: a.datetime().required(),
      institution: a.belongsTo('Institution'),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow.groupDefinedIn('adminRole').to(['read']),
      a.allow.groupDefinedIn('teacherRole').to(['read']),
      a.allow.groupDefinedIn('studentRole').to(['read']),
    ])
    .identifier(['key', 'time']),
  Subject: a
    .model({
      name: a.string().required(),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow
        .specificGroups([UserGroup.ADMIN, UserGroup.TEACHER, UserGroup.STUDENT])
        .to(['read']),
    ]),
  Grade: a
    .model({
      name: a.string().required(),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow
        .specificGroups([UserGroup.ADMIN, UserGroup.TEACHER, UserGroup.STUDENT])
        .to(['read']),
    ]),
  Language: a
    .model({
      name: a.string().required(),
      shortName: a.string().required(),
      flag: a.url(),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow
        .specificGroups([UserGroup.ADMIN, UserGroup.TEACHER, UserGroup.STUDENT])
        .to(['read']),
    ]),
  Question: a
    .model({
      excerpt: a.string().required(),
      grade: a.hasOne('Grade').required(),
      subject: a.hasOne('Subject').required(),
      languages: a.hasMany('QuestionLanguage'),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow
        .specificGroups([UserGroup.ADMIN, UserGroup.TEACHER, UserGroup.STUDENT])
        .to(['read']),
    ]),
  QuestionLanguage: a
    .model({
      content: a.json().required(),
      question: a.belongsTo('Question'),
      language: a.hasOne('Language'),
      options: a.hasMany('QuestionOption'),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow
        .specificGroups([UserGroup.ADMIN, UserGroup.TEACHER, UserGroup.STUDENT])
        .to(['read']),
    ]),
  QuestionOption: a
    .model({
      content: a.json().required(),
      question: a.belongsTo('QuestionLanguage'),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow
        .specificGroups([UserGroup.ADMIN, UserGroup.TEACHER, UserGroup.STUDENT])
        .to(['read']),
    ]),
  CreateUserResponse: a.customType({
    id: a
      .string()
      .required()
      .authorization([
        a.allow.specificGroups([UserGroup.SUPERADMIN, UserGroup.ADMIN]),
      ]),
  }),
  ApiBaseResponse: a.customType({
    success: a
      .boolean()
      .authorization([a.allow.specificGroup(UserGroup.SUPERADMIN)]),
  }),
  createInstitutionGroups: a
    .mutation()
    .arguments({ institution: a.string().required() })
    .returns(a.ref('ApiBaseResponse'))
    .function('adminActions')
    .authorization([a.allow.specificGroup(UserGroup.SUPERADMIN)]),
  createUser: a
    .mutation()
    .arguments({
      email: a.string().required(),
      password: a.string().required(),
      group: a.string().required(),
      institution: a.string(),
    })
    .returns(a.ref('CreateUserResponse'))
    .function('adminActions')
    .authorization([
      a.allow.specificGroups([UserGroup.SUPERADMIN, UserGroup.ADMIN]),
    ]),

with the error essage

Message returned: TimeoutError: {"state":"TIMEOUT","reason":"Waiter has timed out"}

edwardfoyle commented 10 months ago

@festusyuma is this timeout occurring when trying to deploy using Amplify hosting or when running sandbox locally?

festusyuma commented 10 months ago

@edwardfoyle this is currenly on sandbox, I have not trying deploying the app from scratch with the same schema but I will be doing that some time today and will let you know if it occurs

are there different timeouts set for the sandbox and the deployed branch

festusyuma commented 10 months ago

Hello @edwardfoyle . I just tried it on the amplify dashboard and it also timed out. you can download the log file here

edwardfoyle commented 10 months ago

Thanks for the additional logs. I see

CREATE_FAILED        | Custom::AmplifyDynamoDBTable        | data/amplifyData/Teacher/TeacherTable/Default/Default (TeacherTable) Received response status [FAILED] from custom resource. Message returned: TimeoutError: {"state":"TIMEOUT","reason":"Waiter has timed out"}

which would indicate this is an issue in the custom lambda resource created by the data construct. Transferring to their repo so they can triage further.

chrisbonifacio commented 10 months ago

Hi @festusyuma 👋 thanks for raising this issue. I tried deploying your schema all at once and ran into a similar issue, different Message returned from the custom resource.

CleanShot 2024-01-10 at 16 28 12

Marking this as a bug for the team to investigate further

festusyuma commented 10 months ago

@chrisbonifacio Also ran into this issue a few times, it seemed to come and go

jayarerita commented 9 months ago

I have experienced the same issue when deploying a sandbox with more than 7 models defined in it with a few relations.

Error Example

Failed resources:
amplify-gen2tester-******-sandbox-0ce9b-amplifyDataLineItemNestedStackLineItemNest-9V02KP9YK412 | 10:01:58 AM | CREATE_FAILED        | Custom::AmplifyDynamoDBTable | data/amplifyData/LineItem/LineItemTable/Default/Default (LineItemTable) Received response status [FAILED] from custom resource. Message returned: TimeoutError: {"state":"TIMEOUT","reason":"Waiter has timed out"}
    at checkExceptions (/var/runtime/node_modules/@aws-sdk/node_modules/@smithy/util-waiter/dist-cjs/waiter.js:26:30)
    at waitUntilFunctionActiveV2 (/var/runtime/node_modules/@aws-sdk/client-lambda/dist-cjs/waiters/waitForFunctionActiveV2.js:52:46)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async defaultInvokeFunction (/var/task/outbound.js:1:875)
    at async invokeUserFunction (/var/task/framework.js:1:2192)
    at async isComplete (/var/task/framework.js:1:1164)
    at async Runtime.handler (/var/task/cfn-response.js:1:1573) (RequestId: 8d9646a9-ce5f-43c9-a619-2edcdf08a7fb) 

Work Around

I was able to comment out some of the models, do the initial sandbox deploy, then uncomment and re-deploy with a successful hotswap.

Better Workaround?

Is there a way to extend the timeout? Or should I look at flattening some of my related models to better fit the use case for how Gen 2 is formatting the data storage? I see it is creating a table for each model type with a PK on the specified id.

jayarerita commented 9 months ago

No Relations

I tried to deploy the same models, but without any relations. Still receiving the same error.

festusyuma commented 9 months ago

Seems there might also be a limit on the nuber of models I can add. I tried adding some additional models to an existing schema and it threw this error

image

My current schema below. I don't think this classifies as a large schema

const schema = a.schema({
  Institution: a
    .model({
      id: a.string().required(),
      name: a.string().required(),
      teachers: a.hasMany('Teacher'),
      stats: a.hasMany('Stats'),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow.groupDefinedIn('adminRole').to(['read']),
      a.allow.groupDefinedIn('teacherRole').to(['read']),
      a.allow.groupDefinedIn('studentRole').to(['read']),
    ]),
  Profile: a
    .model({
      firstName: a.string().required(),
      lastName: a.string().required(),
      bio: a.string(),
      profileImage: a.url(),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow.owner().inField('id').to(['read', 'update', 'create']),
      a.allow.groupDefinedIn('adminRole'),
      a.allow.groupDefinedIn('teacherRole').to(['read', 'create', 'update']),
      a.allow.groupDefinedIn('studentRole').to(['read']),
    ]),
  Avatar: a
    .model({
      face: a.url(),
      full: a.url().required(),
    })
    .authorization([a.allow.public(), a.allow.private()]),
  Student: a
    .model({
      grade: a
        .hasOne('Grade')
        .authorization([
          a.allow.specificGroup(UserGroup.SUPERADMIN),
          a.allow.groupDefinedIn('adminRole'),
          a.allow
            .groupDefinedIn('teacherRole')
            .to(['read', 'create', 'update']),
          a.allow.groupDefinedIn('studentRole').to(['read']),
        ]),
      avatar: a.hasOne('Avatar'),
      primaryLanguage: a.hasOne('Language'),
      secondaryLanguage: a.hasOne('Language'),
      profile: a.hasOne('Profile'),
      onBoarded: a.boolean().default(false),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow.groupDefinedIn('adminRole'),
      a.allow.groupDefinedIn('teacherRole').to(['read', 'create', 'update']),
      a.allow.groupDefinedIn('studentRole').to(['read']),
      a.allow.owner().to(['read', 'update']),
    ]),
  Teacher: a
    .model({
      subject: a.string(),
      profile: a.hasOne('Profile'),
      institution: a.belongsTo('Institution'),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow.groupDefinedIn('adminRole'),
      a.allow.groupDefinedIn('teacherRole').to(['read']),
      a.allow.groupDefinedIn('studentRole').to(['read']),
    ]),
  Stats: a
    .model({
      key: a.string().required(),
      value: a.integer().required(),
      time: a.datetime().required(),
      institution: a.belongsTo('Institution'),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow.groupDefinedIn('adminRole').to(['read']),
      a.allow.groupDefinedIn('teacherRole').to(['read']),
      a.allow.groupDefinedIn('studentRole').to(['read']),
    ])
    .identifier(['key', 'time']),
  Subject: a
    .model({
      name: a.string().required(),
      key: a.string().required(),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow
        .specificGroups([UserGroup.ADMIN, UserGroup.TEACHER, UserGroup.STUDENT])
        .to(['read']),
    ]),
  Grade: a
    .model({
      name: a.string().required(),
      key: a.string().required(),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow
        .specificGroups([UserGroup.ADMIN, UserGroup.TEACHER, UserGroup.STUDENT])
        .to(['read']),
    ]),
  Language: a
    .model({
      name: a.string().required(),
      shortName: a.string().required(),
      tagLine: a.string().required(),
      flag: a.url().required(),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow
        .specificGroups([UserGroup.ADMIN, UserGroup.TEACHER, UserGroup.STUDENT])
        .to(['read']),
    ]),
  ComplexityType: a.enum(['EASY', 'MODERATE', 'DIFFICULT']),
  Question: a
    .model({
      excerpt: a.string().required(),
      complexity: a.ref('ComplexityType').required(),
      topic: a.string().required(),
      grade: a.hasOne('Grade').required(),
      subject: a.hasOne('Subject').required(),
      languages: a.hasMany('QuestionLanguage'),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow
        .specificGroups([UserGroup.ADMIN, UserGroup.TEACHER, UserGroup.STUDENT])
        .to(['read']),
    ]),
  QuestionLanguage: a
    .model({
      content: a.json().required(),
      imageContent: a.url(),
      correctOptionValue: a.integer(),
      optionValues: a
        .string()
        .array()
        .authorization([
          a.allow.specificGroup(UserGroup.SUPERADMIN),
          a.allow
            .specificGroups([UserGroup.ADMIN, UserGroup.STUDENT])
            .to(['read']),
        ]),
      question: a.belongsTo('Question'),
      language: a.hasOne('Language'),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow
        .specificGroups([UserGroup.ADMIN, UserGroup.TEACHER, UserGroup.STUDENT])
        .to(['read']),
    ]),
  Quiz: a
    .model({
      id: a.id().required(),
      title: a.string().required(),
      noOfQuestions: a.integer().default(0).required(),
      institution: a.id().required(),
      open: a.boolean().default(true),
      grades: a.hasMany('QuizGrade'),
      subjects: a.hasMany('QuizSubject'),
      students: a.hasMany('QuizStudent'),
      questions: a.hasMany('QuizQuestion'),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow.specificGroup(UserGroup.TEACHER).to(['create', 'read']),
      a.allow.owner().to(['create', 'update']),
      a.allow.specificGroup(UserGroup.STUDENT).to(['read']),
    ]),
  QuizGrade: a
    .model({
      grade: a.hasOne('Grade'),
      quiz: a.belongsTo('Quiz'),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow.specificGroup(UserGroup.TEACHER).to(['create', 'read']),
      a.allow.owner().to(['create', 'update']),
      a.allow.specificGroup(UserGroup.STUDENT).to(['read']),
    ]),
  QuizSubject: a
    .model({
      subject: a.hasOne('Subject'),
      quiz: a.belongsTo('Quiz'),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow.specificGroup(UserGroup.TEACHER).to(['create', 'read']),
      a.allow.owner().to(['create', 'update']),
      a.allow.specificGroup(UserGroup.STUDENT).to(['read']),
    ]),
  QuizStudent: a
    .model({
      student: a.hasOne('Student'),
      quiz: a.belongsTo('Quiz'),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow.specificGroup(UserGroup.TEACHER).to(['create', 'read']),
      a.allow.owner().to(['create', 'update']),
    ]),
  QuizQuestion: a
    .model({
      quiz: a.belongsTo('Quiz'),
      question: a.hasOne('Question'),
      defaultLanguage: a.string().required(),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow.specificGroup(UserGroup.TEACHER).to(['create', 'read']),
      a.allow.owner().to(['create', 'update']),
      a.allow.specificGroups([UserGroup.STUDENT, UserGroup.ADMIN]).to(['read']),
    ]),
  CreateUserResponse: a.customType({
    id: a
      .string()
      .required()
      .authorization([
        a.allow.specificGroups([
          UserGroup.SUPERADMIN,
          UserGroup.ADMIN,
          UserGroup.TEACHER,
        ]),
      ]),
  }),
  ApiBaseResponse: a.customType({
    success: a
      .boolean()
      .authorization([a.allow.specificGroup(UserGroup.SUPERADMIN)]),
  }),
  createInstitutionGroups: a
    .mutation()
    .arguments({ institution: a.string().required() })
    .returns(a.ref('ApiBaseResponse'))
    .function('adminActions')
    .authorization([a.allow.specificGroup(UserGroup.SUPERADMIN)]),
  createUser: a
    .mutation()
    .arguments({
      email: a.string().required(),
      password: a.string().required(),
      group: a.string().required(),
      institution: a.string(),
    })
    .returns(a.ref('CreateUserResponse'))
    .function('adminActions')
    .authorization([
      a.allow.specificGroups([
        UserGroup.SUPERADMIN,
        UserGroup.ADMIN,
        UserGroup.TEACHER,
      ]),
    ]),
  CreateQuestionsResponse: a.customType({
    questionsCreated: a.integer().required().authorization([a.allow.private()]),
  }),
  createQuestions: a
    .mutation()
    .arguments({ file: a.url().required() })
    .returns(a.ref('CreateQuestionsResponse'))
    .function('adminActions')
    .authorization([a.allow.specificGroup(UserGroup.SUPERADMIN)]),
});
chrisbonifacio commented 9 months ago

@festusyuma that particular error seems to be caused by the CDK construct depending on lodash's cloneDeep method. Please follow the workaround on this comment and follow that issue to track the fix for Maximum call stack size exceeded errors

festusyuma commented 9 months ago

Hello @chrisbonifacio , thank you. though I'm not quite sure how to add that property to the gen 2 backend. could help guide me with this.

chrisbonifacio commented 9 months ago

@festusyuma ahh that's right. My apologies, you can't override that property on the underlying AmplifyGraphqlApi L3 construct as it's not exposed via the Backend we return from defineBackend or defineData. This would be a feature request or have to be fixed on our end.

festusyuma commented 9 months ago

Alright, thank you @chrisbonifacio

phani-srikar commented 9 months ago

Hi @festusyuma, follow my comment to overcome the max call stack size issue that is one of the issues discussed in this thread.

chrisbonifacio commented 9 months ago

Closing this as the fix has been released for Gen 2 as well

festusyuma commented 8 months ago

Hello @chrisbonifacio. I just tried redeploying my environment sandbox and got the timeout error again. I've attached my model and package.json

image
import { a, ClientSchema, defineData } from '@aws-amplify/backend';

import { UserGroup } from '../../types';
import { adminActions } from '../functions';

const schema = a.schema({
  Avatar: a
    .model({
      face: a.url(),
      full: a.url().required(),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow.private().to(['read']),
    ]),
  Subject: a
    .model({
      id: a.string().required(),
      name: a.string().required(),
      icon: a.url(),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow.private().to(['read']),
    ]),
  Grade: a
    .model({
      id: a.string().required(),
      name: a.string().required(),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow.private().to(['read']),
    ]),
  Language: a
    .model({
      id: a.string().required(),
      name: a.string().required(),
      tagLine: a.string().required(),
      flag: a.url().required(),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow.private().to(['read']),
    ]),
  ComplexityType: a.enum(['EASY', 'MODERATE', 'DIFFICULT', 'ALGORITHM']),
  Question: a
    .model({
      id: a.string().required(),
      excerpt: a.string().required(),
      complexity: a.ref('ComplexityType').required(),
      topic: a.string().required(),
      // relationships
      grade: a.hasOne('Grade'),
      subject: a.hasOne('Subject'),
      languages: a.hasMany('QuestionLanguage'),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow.private().to(['read']),
    ]),
  QuestionLanguage: a
    .model({
      id: a.string().required(),
      content: a.string().required(),
      imageContent: a.url(),
      correctOptionValue: a
        .string()
        .authorization([
          a.allow.specificGroup(UserGroup.SUPERADMIN),
          a.allow
            .specificGroups([UserGroup.ADMIN, UserGroup.TEACHER])
            .to(['read']),
        ]),
      optionValues: a.string().array(),
      // relationships
      question: a.belongsTo('Question'),
      language: a.hasOne('Language'),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow.private().to(['read']),
    ]),
  Institution: a
    .model({
      id: a.string().required(),
      name: a.string().required(),
      teachers: a.hasMany('Teacher'),
      students: a.hasMany('Student'),
      quizzes: a.hasMany('Quiz'),
      stats: a.hasMany('Stats'),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow.private().to(['read']),
      a.allow.owner().to(['read']),
    ]),
  Student: a
    .model({
      firstName: a.string(),
      lastName: a.string(),
      userName: a.string(),
      bio: a.string(),
      onBoarded: a.boolean().default(false),
      // relationships
      institution: a
        .belongsTo('Institution')
        .authorization([
          a.allow.specificGroup(UserGroup.SUPERADMIN),
          a.allow
            .specificGroups([UserGroup.ADMIN, UserGroup.TEACHER])
            .to(['create']),
          a.allow.private().to(['read']),
        ]),
      grade: a
        .hasOne('Grade')
        .authorization([
          a.allow.specificGroups([UserGroup.SUPERADMIN, UserGroup.ADMIN]),
          a.allow
            .specificGroup(UserGroup.TEACHER)
            .to(['read', 'create', 'update']),
          a.allow.private().to(['read']),
        ]),
      avatar: a.hasOne('Avatar'),
      primaryLanguage: a.hasOne('Language'),
      secondaryLanguage: a.hasOne('Language'),
    })
    .authorization([
      a.allow.specificGroups([UserGroup.SUPERADMIN, UserGroup.ADMIN]),
      a.allow.specificGroup(UserGroup.TEACHER).to(['create', 'update']),
      a.allow.owner().to(['update']),
      a.allow.private().to(['read']),
    ]),
  Teacher: a
    .model({
      firstName: a.string(),
      lastName: a.string(),
      // relationships
      institution: a
        .belongsTo('Institution')
        .authorization([
          a.allow.specificGroup(UserGroup.SUPERADMIN),
          a.allow
            .specificGroups([UserGroup.ADMIN, UserGroup.TEACHER])
            .to(['create']),
          a.allow.private().to(['read']),
        ]),
    })
    .authorization([
      a.allow.specificGroups([UserGroup.SUPERADMIN, UserGroup.ADMIN]),
      a.allow.owner().to(['update']),
      a.allow.private().to(['read']),
    ]),
  Stats: a
    .model({
      key: a.string().required(),
      value: a.integer().required(),
      time: a.datetime().required(),
      // relationships
      institution: a
        .belongsTo('Institution')
        .authorization([
          a.allow.specificGroup(UserGroup.SUPERADMIN),
          a.allow
            .specificGroups([UserGroup.ADMIN, UserGroup.TEACHER])
            .to(['create']),
          a.allow.private().to(['read']),
        ]),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow.private().to(['create', 'update', 'read']),
    ])
    .identifier(['key', 'time']),
  QuizStatusType: a.enum(['DRAFT', 'PUBLISHED', 'CLOSED']),
  Quiz: a
    .model({
      id: a.id().required(),
      title: a.string().required(),
      thumbnail: a.url(),
      noOfQuestions: a.integer().default(0).required(),
      students: a.string().array(),
      grades: a.string().array(),
      subjects: a.string().array(),
      status: a.ref('QuizStatusType'),
      // relationships
      institution: a
        .belongsTo('Institution')
        .authorization([
          a.allow.specificGroup(UserGroup.SUPERADMIN),
          a.allow
            .specificGroups([UserGroup.ADMIN, UserGroup.TEACHER])
            .to(['create']),
          a.allow.private().to(['read']),
        ]),
      questions: a.hasMany('QuizQuestion'),
    })
    .authorization([
      a.allow.specificGroups([UserGroup.SUPERADMIN, UserGroup.ADMIN]),
      a.allow.owner().to(['create', 'update']),
      a.allow.private().to(['read']),
    ]),
  QuizQuestion: a
    .model({
      quiz: a.belongsTo('Quiz'),
      question: a.hasOne('Question'),
      defaultLanguage: a.hasOne('Language'),
    })
    .authorization([
      a.allow.specificGroup(UserGroup.SUPERADMIN),
      a.allow
        .specificGroups([UserGroup.TEACHER, UserGroup.ADMIN])
        .to(['create', 'read', 'delete']),
      a.allow.specificGroups([UserGroup.STUDENT]).to(['read']),
    ]),
  CreateUserResponse: a.customType({
    id: a
      .string()
      .required()
      .authorization([a.allow.private(), a.allow.public()]),
  }),
  ApiBaseResponse: a.customType({
    success: a.boolean().authorization([a.allow.private(), a.allow.public()]),
    error: a.string().authorization([a.allow.private(), a.allow.public()]),
  }),
  createInstitutionGroups: a
    .mutation()
    .arguments({ institution: a.string().required() })
    .returns(a.ref('ApiBaseResponse'))
    .function('adminActions')
    .authorization([a.allow.specificGroup(UserGroup.SUPERADMIN)]),
  createUser: a
    .mutation()
    .arguments({
      email: a.string().required(),
      password: a.string().required(),
      group: a.string().required(),
      institution: a.string(),
    })
    .returns(a.ref('CreateUserResponse'))
    .function('adminActions')
    .authorization([
      a.allow.specificGroups([
        UserGroup.SUPERADMIN,
        UserGroup.ADMIN,
        UserGroup.TEACHER,
      ]),
    ]),
  uploadQuestions: a
    .mutation()
    .arguments({ url: a.string().required() })
    .returns(a.ref('ApiBaseResponse'))
    .function('adminActions')
    .authorization([a.allow.specificGroup(UserGroup.SUPERADMIN)]),
});

export type Schema = ClientSchema<typeof schema>;

export const data = defineData({
  schema,
  authorizationModes: {
    defaultAuthorizationMode: 'apiKey',
    // API Key is used for a.allow.public() rules
    apiKeyAuthorizationMode: {
      expiresInDays: 30,
    },
  },
  functions: {
    adminActions,
  },
});
{
  "dependencies": {
    "@aws-amplify/adapter-nextjs": "1.0.15",
    "@hookform/resolvers": "^3.3.2",
    "@segment/analytics-next": "^1.64.0",
    "@segment/analytics-node": "^2.0.0",
    "@tiptap/extension-placeholder": "^2.1.13",
    "@tiptap/react": "^2.1.13",
    "@tiptap/starter-kit": "^2.1.13",
    "aws-amplify": "6.0.15",
    "chart.js": "^4.4.1",
    "csv-parse": "^5.5.3",
    "luxon": "^3.4.4",
    "next": "14.1.0",
    "react": "18.2.0",
    "react-chartjs-2": "^5.2.0",
    "react-dom": "18.2.0",
    "react-hook-form": "^7.49.2",
    "react-hot-toast": "^2.4.1",
    "sharp": "^0.32.6",
    "tslib": "^2.3.0",
    "zod": "^3.22.4"
  },
  "devDependencies": {
    "@aws-amplify/backend": "^0.12.1",
    "@aws-amplify/backend-cli": "^0.11.1",
    "@aws-appsync/utils": "^1.6.0",
    "@aws-cdk/aws-apigatewayv2-alpha": "^2.110.1-alpha.0",
    "@aws-cdk/aws-apigatewayv2-integrations-alpha": "^2.110.1-alpha.0",
    "@aws-sdk/client-cognito-identity-provider": "^3.476.0",
    "@aws-sdk/client-dynamodb": "^3.490.0",
    "@aws-sdk/client-lambda": "^3.490.0",
    "@aws-sdk/lib-dynamodb": "^3.490.0",
    "@nx/esbuild": "17.2.5",
    "@nx/eslint": "17.2.1",
    "@nx/eslint-plugin": "17.2.1",
    "@nx/jest": "17.2.1",
    "@nx/js": "17.2.5",
    "@nx/next": "17.2.1",
    "@nx/node": "17.2.5",
    "@nx/react": "17.2.1",
    "@nx/workspace": "17.2.1",
    "@swc-node/register": "~1.6.7",
    "@swc/core": "~1.3.85",
    "@testing-library/react": "14.0.0",
    "@types/aws-lambda": "^8.10.130",
    "@types/jest": "^29.4.0",
    "@types/luxon": "^3.4.1",
    "@types/node": "18.16.9",
    "@types/react": "18.2.43",
    "@types/react-dom": "18.2.17",
    "@typescript-eslint/eslint-plugin": "^6.9.1",
    "@typescript-eslint/parser": "^6.9.1",
    "autoprefixer": "10.4.13",
    "aws-cdk": "2.127.0",
    "aws-cdk-lib": "^2.127.0",
    "babel-jest": "^29.4.1",
    "clsx": "^2.0.1",
    "constructs": "^10.3.0",
    "esbuild": "^0.19.2",
    "eslint": "~8.48.0",
    "eslint-config-next": "13.4.4",
    "eslint-config-prettier": "^9.0.0",
    "eslint-plugin-import": "2.27.5",
    "eslint-plugin-jsx-a11y": "6.7.1",
    "eslint-plugin-react": "7.32.2",
    "eslint-plugin-react-hooks": "4.6.0",
    "eslint-plugin-simple-import-sort": "^10.0.0",
    "jest": "^29.4.1",
    "jest-environment-jsdom": "^29.4.1",
    "nx": "17.2.1",
    "postcss": "^8.4.32",
    "prettier": "^2.6.2",
    "sass": "1.62.1",
    "source-map-support": "^0.5.21",
    "tailwind-merge": "^2.2.0",
    "tailwindcss": "3.4.1",
    "ts-jest": "^29.1.0",
    "ts-node": "10.9.1",
    "typescript": "^5.3.3"
  }
}
festusyuma commented 8 months ago

Hello @chrisbonifacio

festusyuma commented 8 months ago

Seems to work after retrying it a couple of times

festusyuma commented 8 months ago

Hey @chrisbonifacio I don't really think this is fixed. I'm still experiencing it on multiple projects. it would be nice if this could be re-opened as the issue is still present

image

chrisbonifacio commented 8 months ago

@festusyuma what version of @aws-amplify/backend are you using in the projects that reproduce the issue? If you're using latest, try installing @aws-amplify/backend@beta.

festusyuma commented 8 months ago

@chrisbonifacio I've currenyly tried most versions of the beta and still had the same issue on my PC. with 2 different apps.

tried these in the morning "@aws-amplify/backend": "0.13.0-beta.4", "@aws-amplify/backend-cli": "0.12.0-beta.4",

jFensch commented 7 months ago

@chrisbonifacio Seeing the same timeout issue with

"@aws-amplify/backend": "0.13.0-beta.5", "@aws-amplify/backend-cli": "0.12.0-beta.5",

jFensch commented 7 months ago

@chrisbonifacio Issue remains after updating

"@aws-amplify/backend": "0.13.0-beta.7" "@aws-amplify/backend-cli": "0.12.0-beta.7" "aws-amplify": "^6.0.20"

This is becoming a blocker for my team. Any guidance you could give?

festusyuma commented 7 months ago

@chrisbonifacio Can this issue be re-opened. it just happened in the amplify environment and now my stack is stuck in update_rollback_failed state for some reason

2024-03-20T00:49:26.901Z [INFO]: CloudFormationDeploymentError: The CloudFormation deployment has failed.❌ Deployment failed: Error: The stack named amplify-d2ihqtowtx2co4-main-branch-47897eb996 failed to deploy: UPDATE_ROLLBACK_FAILED (The following resource(s) failed to update: [data7552DF31]. ): Received response status [FAILED] from custom resource. Message returned: TimeoutError: {"state":"TIMEOUT","reason":"Waiter has timed out"}

amineelmoussafer commented 6 months ago

Hi @chrisbonifacio seeing the same timeout issue , any update ? :)

AaronZyLee commented 6 months ago

It turns out to be related to the CDK bug https://github.com/aws/aws-cdk/issues/26838

blinkdaffer commented 6 months ago

any update on this

pkubat commented 6 months ago

Updating to 0.13.0 & 0.12.0 from beta induced this error.

Was working with: "@aws-amplify/backend": "^0.13.0-beta.3", "@aws-amplify/backend-cli": "^0.12.0-beta.3",

Is there an ETA for fixing this? Commenting out parts of a schema is not a doable workaround. This is a pretty big blocker, as you can easily start a sandbox.

chrisbonifacio commented 3 months ago

Hey everyone, since this issue has been opened we've made significant improvements to our types performance. We are unable to reproduce this issue anymore internally with the latest versions of @aws-amplify/backend and @aws-amplify/backend-cli.

I used one of the schemas shared in this thread to try and reproduce this issue, updated to the latest Gen 2 syntax. I might've mangled the relationships in trying to get it to deploy, but it did just fine!

So, I will close this as resolved.

If you do, however, still run into this particular error for some reason, please make sure your @aws-amplify/backend and @aws-amplify/backend-cli packages are up to date.

Otherwise, please feel free to open a new issue and share your schema for us to reproduce.

const schema = a.schema({
  Avatar: a
    .model({
      face: a.url(),
      full: a.url().required(),
      studentId: a.string(),
      student: a.belongsTo("Student", "studentId"),
    })
    .authorization((allow) => [
      allow.group(UserGroup.SUPERADMIN),
      allow.authenticated().to(["read"]),
    ]),
  Subject: a
    .model({
      id: a.string().required(),
      name: a.string().required(),
      icon: a.url(),
      questionId: a.string(),
      question: a.belongsTo("Question", "questionId"),
    })
    .authorization((allow) => [
      allow.group(UserGroup.SUPERADMIN),
      allow.authenticated().to(["read"]),
    ]),
  Grade: a
    .model({
      id: a.string().required(),
      name: a.string().required(),
      studentId: a.string(),
      student: a.belongsTo("Student", "studentId"),
      questionId: a.string(),
      question: a.belongsTo("Question", "questionId"),
    })
    .authorization((allow) => [
      allow.group(UserGroup.SUPERADMIN),
      allow.authenticated().to(["read"]),
    ]),
  Language: a
    .model({
      id: a.string().required(),
      name: a.string().required(),
      tagLine: a.string().required(),
      flag: a.url().required(),
      questionLanguageId: a.string(),
      questionLanguage: a.belongsTo("QuestionLanguage", "questionLanguageId"),
      studentId: a.string(),
      student: a.belongsTo("Student", "studentId"),
      quizQuestionId: a.string(),
      quizQuestion: a.belongsTo("QuizQuestion", "quizQuestionId"),
    })
    .authorization((allow) => [
      allow.group(UserGroup.SUPERADMIN),
      allow.authenticated().to(["read"]),
    ]),
  ComplexityType: a.enum(["EASY", "MODERATE", "DIFFICULT", "ALGORITHM"]),
  Question: a
    .model({
      id: a.string().required(),
      excerpt: a.string().required(),
      complexity: a.ref("ComplexityType").required(),
      topic: a.string().required(),
      // relationships
      grade: a.hasOne("Grade", "questionId"),
      subject: a.hasOne("Subject", "questionId"),
      languages: a.hasMany("QuestionLanguage", "questionId"),
      quizQuestions: a.hasMany("QuizQuestion", "questionId"),
    })
    .authorization((allow) => [
      allow.group(UserGroup.SUPERADMIN),
      allow.authenticated().to(["read"]),
    ]),
  QuestionLanguage: a
    .model({
      id: a.string().required(),
      content: a.string().required(),
      imageContent: a.url(),
      correctOptionValue: a
        .string()
        .authorization((allow) => [
          allow.group(UserGroup.SUPERADMIN),
          allow.groups([UserGroup.ADMIN, UserGroup.TEACHER]).to(["read"]),
        ]),
      optionValues: a.string().array(),
      // relationships
      questionId: a.string(),
      question: a.belongsTo("Question", "questionId"),
      language: a.hasOne("Language", "questionLanguageId"),
    })
    .authorization((allow) => [
      allow.group(UserGroup.SUPERADMIN),
      allow.authenticated().to(["read"]),
    ]),
  Institution: a
    .model({
      id: a.string().required(),
      name: a.string().required(),
      teachers: a.hasMany("Teacher", "institutionId"),
      students: a.hasMany("Student", "institutionId"),
      quizzes: a.hasMany("Quiz", "institutionId"),
      stats: a.hasMany("Stats", "institutionId"),
    })
    .authorization((allow) => [
      allow.group(UserGroup.SUPERADMIN),
      allow.authenticated().to(["read"]),
      allow.owner().to(["read"]),
    ]),
  Student: a
    .model({
      firstName: a.string(),
      lastName: a.string(),
      userName: a.string(),
      bio: a.string(),
      onBoarded: a.boolean().default(false),
      // relationships
      institutionId: a.string(),
      institution: a
        .belongsTo("Institution", "institutionId")
        .authorization((allow) => [
          allow.group(UserGroup.SUPERADMIN),
          allow.groups([UserGroup.ADMIN, UserGroup.TEACHER]).to(["create"]),
          allow.authenticated().to(["read"]),
        ]),
      grade: a
        .hasOne("Grade", "studentId")
        .authorization((allow) => [
          allow.groups([UserGroup.SUPERADMIN, UserGroup.ADMIN]),
          allow.group(UserGroup.TEACHER).to(["read", "create", "update"]),
          allow.authenticated().to(["read"]),
        ]),
      avatar: a.hasOne("Avatar", "studentId"),
      primaryLanguage: a.hasOne("Language", "studentId"),
      secondaryLanguage: a.hasOne("Language", "studentId"),
    })
    .authorization((allow) => [
      allow.groups([UserGroup.SUPERADMIN, UserGroup.ADMIN]),
      allow.group(UserGroup.TEACHER).to(["create", "update"]),
      allow.owner().to(["update"]),
      allow.authenticated().to(["read"]),
    ]),
  Teacher: a
    .model({
      firstName: a.string(),
      lastName: a.string(),
      // relationships
      institutionId: a.string(),
      institution: a
        .belongsTo("Institution", "institutionId")
        .authorization((allow) => [
          allow.group(UserGroup.SUPERADMIN),
          allow.groups([UserGroup.ADMIN, UserGroup.TEACHER]).to(["create"]),
          allow.authenticated().to(["read"]),
        ]),
    })
    .authorization((allow) => [
      allow.groups([UserGroup.SUPERADMIN, UserGroup.ADMIN]),
      allow.owner().to(["update"]),
      allow.authenticated().to(["read"]),
    ]),
  Stats: a
    .model({
      key: a.string().required(),
      value: a.integer().required(),
      time: a.datetime().required(),
      // relationships
      institutionId: a.string(),
      institution: a
        .belongsTo("Institution", "institutionId")
        .authorization((allow) => [
          allow.group(UserGroup.SUPERADMIN),
          allow.groups([UserGroup.ADMIN, UserGroup.TEACHER]).to(["create"]),
          allow.authenticated().to(["read"]),
        ]),
    })
    .authorization((allow) => [
      allow.group(UserGroup.SUPERADMIN),
      allow.authenticated().to(["create", "update", "read"]),
    ])
    .identifier(["key", "time"]),
  QuizStatusType: a.enum(["DRAFT", "PUBLISHED", "CLOSED"]),
  Quiz: a
    .model({
      id: a.id().required(),
      title: a.string().required(),
      thumbnail: a.url(),
      noOfQuestions: a.integer().default(0).required(),
      students: a.string().array(),
      grades: a.string().array(),
      subjects: a.string().array(),
      status: a.ref("QuizStatusType"),
      // relationships
      institutionId: a.string(),
      institution: a
        .belongsTo("Institution", "institutionId")
        .authorization((allow) => [
          allow.group(UserGroup.SUPERADMIN),
          allow.groups([UserGroup.ADMIN, UserGroup.TEACHER]).to(["create"]),
          allow.authenticated().to(["read"]),
        ]),
      questions: a.hasMany("QuizQuestion", "quizId"),
    })
    .authorization((allow) => [
      allow.groups([UserGroup.SUPERADMIN, UserGroup.ADMIN]),
      allow.owner().to(["create", "update"]),
      allow.authenticated().to(["read"]),
    ]),
  QuizQuestion: a
    .model({
      quizId: a.string(),
      quiz: a.belongsTo("Quiz", "quizId"),
      questionId: a.string(),
      question: a.belongsTo("Question", "questionId"),
      defaultLanguage: a.hasOne("Language", "quizQuestionId"),
    })
    .authorization((allow) => [
      allow.group(UserGroup.SUPERADMIN),
      allow
        .groups([UserGroup.TEACHER, UserGroup.ADMIN])
        .to(["create", "read", "delete"]),
      allow.groups([UserGroup.STUDENT]).to(["read"]),
    ]),
  CreateUserResponse: a.customType({
    id: a
      .string()
      .required()
      .authorization((allow) => [allow.authenticated(), allow.publicApiKey()]),
  }),
  ApiBaseResponse: a.customType({
    success: a
      .boolean()
      .authorization((allow) => [allow.authenticated(), allow.publicApiKey()]),
    error: a
      .string()
      .authorization((allow) => [allow.authenticated(), allow.publicApiKey()]),
  }),
  createInstitutionGroups: a
    .mutation()
    .arguments({ institution: a.string().required() })
    .returns(a.ref("ApiBaseResponse"))
    .handler(a.handler.function(adminActions))

    .authorization((allow) => [allow.group(UserGroup.SUPERADMIN)]),
  createUser: a
    .mutation()
    .arguments({
      email: a.string().required(),
      password: a.string().required(),
      group: a.string().required(),
      institution: a.string(),
    })
    .returns(a.ref("CreateUserResponse"))
    .handler(a.handler.function(adminActions))

    .authorization((allow) => [
      allow.groups([UserGroup.SUPERADMIN, UserGroup.ADMIN, UserGroup.TEACHER]),
    ]),
  uploadQuestions: a
    .mutation()
    .arguments({ url: a.string().required() })
    .returns(a.ref("ApiBaseResponse"))
    .handler(a.handler.function(adminActions))
    .authorization((allow) => [allow.group(UserGroup.SUPERADMIN)]),
});
github-actions[bot] commented 3 months ago

This issue is now closed. Comments on closed issues are hard for our team to see. If you need more assistance, please open a new issue that references this one.