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

SQL - Type instantiation is excessively deep and possibly infinite.ts(2589) #2551

Open
kekami opened this issue May 11, 2024 · 14 comments
Open
Labels

Comments

@kekami
Copy link

kekami commented May 11, 2024

Environment information

System:
  OS: macOS 14.4.1
  CPU: (10) arm64 Apple M1 Max
  Memory: 109.48 MB / 32.00 GB
  Shell: /bin/zsh
Binaries:
  Node: 20.12.2 - ~/.nvm/versions/node/v20.12.2/bin/node
  Yarn: 1.22.22 - ~/.nvm/versions/node/v20.12.2/bin/yarn
  npm: 10.5.0 - ~/.nvm/versions/node/v20.12.2/bin/npm
  pnpm: 9.1.0 - ~/.nvm/versions/node/v20.12.2/bin/pnpm
NPM Packages:
  @aws-amplify/backend: 1.0.1
  @aws-amplify/backend-cli: 1.0.2
  aws-amplify: 6.3.0
  aws-cdk: 2.137.0
  aws-cdk-lib: 2.137.0
  typescript: 5.4.5
AWS environment variables:
  AWS_SDK_LOAD_CONFIG = 1
  AWS_STS_REGIONAL_ENDPOINTS = regional
  AWS_NODEJS_CONNECTION_REUSE_ENABLED = 1
No CDK environment variables

Description

Keep on hitting Type instantiation is excessively deep and possibly infinite.ts(2589) while working with the sql schema.

Repro:

// schema.sql.ts

/* eslint-disable */
/* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. */
import { a } from "@aws-amplify/data-schema";
import { configure } from "@aws-amplify/data-schema/internals";
import { secret } from "@aws-amplify/backend";

export const schema = configure({
  database: {
    identifier: "foobar",
    engine: "postgresql",
    connectionUri: secret("SQL_CONNECTION_STRING"),
  },
}).schema({
  customers: a
    .model({
      id: a.id().required(),
      first_name: a.string(),
      last_name: a.string(),
      date_of_birth: a.string(),
      email: a.string(),
      phone: a.string(),
      owner: a.string(),
      stripe_customer_id: a.string(),
    })
    .identifier(["id"]),
  employees: a
    .model({
      id: a.id().required(),
      first_name: a.string(),
      last_name: a.string(),
      date_of_birth: a.string(),
      phone: a.string(),
      owner: a.string().required(),
      email: a.string(),
    })
    .identifier(["id"]),
  retainers: a
    .model({
      id: a.id().required(),
      customer_id: a.string().required(),
      employee_id: a.string().required(),
      owners: a.string().required().array(),
    })
    .identifier(["id"]),
  shifts: a
    .model({
      id: a.id().required(),
      start_time: a.datetime().required(),
      end_time: a.datetime().required(),
      employee_id: a.string().required(),
      retainer_id: a.string().required(),
      owners: a.string().required().array(),
    })
    .identifier(["id"]),
  spatial_ref_sys: a
    .model({
      srid: a.integer().required(),
      auth_name: a.string(),
      auth_srid: a.integer(),
      srtext: a.string(),
      proj4text: a.string(),
    })
    .identifier(["srid"]),
});

// resource.ts

import { type ClientSchema, a, defineData } from "@aws-amplify/backend";

import { schema as generatedSqlSchema } from "./schema.sql.js";

const sqlSchema = generatedSqlSchema
  .renameModels(() => [
    ["employees", "Employee"],
    ["customers", "Customer"],
    ["shifts", "Shift"],
    ["retainers", "Retainer"],
  ])
  .setRelationships((models) => [
    models.Employee.relationships({
      retainers: a.hasMany("Retainer", "employee_id"),
      shifts: a.hasMany("Shift", "employee_id"),
    }),
    models.Customer.relationships({
      retainers: a.hasMany("Retainer", "customer_id"),
    }),
    models.Retainer.relationships({
      shifts: a.hasMany("Shift", "retainer_id"),
      employee: a.belongsTo("Employee", "employee_id"),
      customer: a.belongsTo("Customer", "customer_id"),
    }),
    models.Shift.relationships({
      retainer: a.belongsTo("Retainer", "retainer_id"),
      employee: a.belongsTo("Employee", "employee_id"),
    }),
  ])
  .setAuthorization((models) => [
    models.Employee.authorization((allow) => [allow.ownerDefinedIn("owner")]),
    models.Customer.authorization((allow) => [allow.ownerDefinedIn("owner")]),
    models.Retainer.authorization((allow) => [allow.ownersDefinedIn("owners")]),
    models.Shift.authorization((allow) => [allow.ownersDefinedIn("owners")]),
  ]);

export type Schema = ClientSchema<typeof sqlSchema>;

export const data = defineData({
  schema: sqlSchema,
});

Screenshot 2024-05-11 at 19 37 07

@dpilch dpilch transferred this issue from aws-amplify/amplify-backend May 13, 2024
@AnilMaktala
Copy link

Hey @kekami, Thanks for raising this. We made some improvements in our types internally and it will address this issue. We will let you know once it is released.

@AnilMaktala AnilMaktala added bug Something isn't working pending-release and removed pending-triage labels May 14, 2024
@kekami
Copy link
Author

kekami commented May 26, 2024

Hey @AnilMaktala how is it going with the fix?

If you want me to validate the fix against my schema, which has grown significantly since this issue was posted, just let me know. I'd be happy to try it.

@renebrandel
Copy link
Contributor

@kekami - can you upgrade to the latest version? This should be resolved in latest backend / data version. Check in your package-lock that the @aws-amplify/data-schema subpackage is 1.3.0

@kekami
Copy link
Author

kekami commented May 30, 2024

@kekami - can you upgrade to the latest version? This should be resolved in latest backend / data version. Check in your package-lock that the @aws-amplify/data-schema subpackage is 1.3.0

Hi @renebrandel, I am on the latest backend, the subpackage was still on 1.2.5. However, bumping it did not help, the issue remains.

@renebrandel
Copy link
Contributor

did you remove your package-lock.json and node_modules folder?

@renebrandel
Copy link
Contributor

in my local version (with your exact schema) it seems to work now:
image

@renebrandel
Copy link
Contributor

maybe also try: npm update @aws-amplify/data-schema

@kekami
Copy link
Author

kekami commented May 30, 2024

You are right! My old schema now works. However my current one is a tad bigger 😅 And is still experiencing problems.

schema.sql.ts

/* eslint-disable */
/* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. */
import { a } from "@aws-amplify/data-schema";
import { configure } from "@aws-amplify/data-schema/internals";
import { secret } from "@aws-amplify/backend";

export const schema = configure({
    database: {
        identifier: "IDrpwRgsiPF0pVZSGUYZ6Hw",
        engine: "postgresql",
        connectionUri: secret("SQL_CONNECTION_STRING_PLAYGROUND")
    }
}).schema({
    "assignments": a.model({
        id: a.id().required(),
        contract_id: a.id().required(),
        start_date: a.date(),
        end_date: a.date(),
        owners: a.string().array(),
        created_at: a.datetime(),
        updated_at: a.datetime(),
        user_id: a.id().required(),
        owner: a.string()
    }).identifier([
        "id"
    ]),
    "billing_details": a.model({
        id: a.id().required(),
        contract_id: a.id().required(),
        billing_address: a.string(),
        payment_method: a.string(),
        card_number: a.string(),
        card_expiry_date: a.date(),
        card_cvc: a.string(),
        owners: a.string().array(),
        created_at: a.datetime(),
        updated_at: a.datetime()
    }).identifier([
        "id"
    ]),
    "certifications": a.model({
        id: a.id().required(),
        certification_name: a.string().required(),
        issued_by: a.string(),
        issue_date: a.date().required(),
        expiry_date: a.date(),
        owners: a.string().array(),
        created_at: a.datetime(),
        updated_at: a.datetime(),
        user_id: a.id().required()
    }).identifier([
        "id"
    ]),
    "contracts": a.model({
        id: a.id().required(),
        start_date: a.date(),
        end_date: a.date(),
        client_type: a.string().required(),
        user_id: a.id().required(),
        organisation_id: a.id(),
        owners: a.string().array(),
        created_at: a.datetime(),
        updated_at: a.datetime()
    }).identifier([
        "id"
    ]),
    "employments": a.model({
        id: a.id().required(),
        owners: a.string().array(),
        created_at: a.datetime(),
        updated_at: a.datetime(),
        user_id: a.id().required()
    }).identifier([
        "id"
    ]),
    "identity_verifications": a.model({
        id: a.id().required(),
        user_id: a.id().required(),
        status: a.string(),
        errormsg: a.string(),
        errorcode: a.integer(),
        owner: a.string(),
        created_at: a.datetime(),
        updated_at: a.datetime(),
        order_ref: a.string(),
        auto_start_token: a.string(),
        qr_start_token: a.string()
    }).identifier([
        "id"
    ]),
    "invoice_rows": a.model({
        id: a.id().required(),
        invoice_id: a.id().required(),
        description: a.string().required(),
        quantity: a.integer().required(),
        unit_price: a.float().required(),
        unit_type: a.string().required(),
        owners: a.string().array(),
        created_at: a.datetime(),
        updated_at: a.datetime()
    }).identifier([
        "id"
    ]),
    "invoices": a.model({
        id: a.id().required(),
        contract_id: a.id(),
        issue_date: a.date().required(),
        due_date: a.date().required(),
        amount: a.float().required(),
        status: a.string().required(),
        owners: a.string().array(),
        created_at: a.datetime(),
        updated_at: a.datetime(),
        user_id: a.id().required()
    }).identifier([
        "id"
    ]),
    "organisation_users": a.model({
        id: a.id().required(),
        organisation_id: a.id().required(),
        user_id: a.id().required(),
        owners: a.string().array(),
        created_at: a.datetime(),
        updated_at: a.datetime()
    }).identifier([
        "id"
    ]),
    "organisations": a.model({
        id: a.id().required(),
        organisation_name: a.string().required(),
        type: a.string().required(),
        contact_person_id: a.id().required(),
        owners: a.string().array(),
        created_at: a.datetime(),
        updated_at: a.datetime()
    }).identifier([
        "id"
    ]),
    "payments": a.model({
        id: a.id().required(),
        invoice_id: a.id(),
        payment_date: a.date().required(),
        amount: a.float().required(),
        owners: a.string().array(),
        created_at: a.datetime(),
        updated_at: a.datetime(),
        user_id: a.id().required()
    }).identifier([
        "id"
    ]),
    "salaries": a.model({
        id: a.id().required(),
        payment_date: a.date().required(),
        amount: a.float().required(),
        owners: a.string().array(),
        created_at: a.datetime(),
        updated_at: a.datetime(),
        user_id: a.id().required()
    }).identifier([
        "id"
    ]),
    "salary_rows": a.model({
        id: a.id().required(),
        salary_id: a.id().required(),
        description: a.string(),
        quantity: a.integer().required(),
        unit_price: a.float().required(),
        unit_type: a.string().required(),
        owners: a.string().array(),
        created_at: a.datetime(),
        updated_at: a.datetime()
    }).identifier([
        "id"
    ]),
    "shifts": a.model({
        id: a.id().required(),
        assignment_id: a.id().required(),
        start_time: a.datetime().required(),
        end_time: a.datetime().required(),
        owners: a.string().array(),
        created_at: a.datetime(),
        updated_at: a.datetime(),
        user_id: a.id().required()
    }).identifier([
        "id"
    ]),
    "time_reports": a.model({
        id: a.id().required(),
        shift_id: a.id().required(),
        start_time: a.datetime().required(),
        end_time: a.datetime().required(),
        note: a.string(),
        owners: a.string().array(),
        created_at: a.datetime(),
        updated_at: a.datetime(),
        user_id: a.id().required()
    }).identifier([
        "id"
    ]),
    "users": a.model({
        id: a.id().required(),
        username: a.string().required(),
        role: a.string().required(),
        first_name: a.string(),
        last_name: a.string(),
        email: a.string().required(),
        phone_number: a.string(),
        identity_verified: a.boolean(),
        created_at: a.datetime(),
        updated_at: a.datetime(),
        stripe_customer_id: a.string()
    }).identifier([
        "id"
    ])
});

resource.ts

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

import { schema as generatedSqlSchema } from './schema.sql.js';

const sqlSchema = generatedSqlSchema
  .renameModels(() => [
    ['assignments', 'Assignment'],
    ['billing_details', 'BillingDetail'],
    ['contracts', 'Contract'],
    ['employments', 'Employment'],
    ['identity_verifications', 'IdentityVerification'],
    ['invoices', 'Invoice'],
    ['invoice_rows', 'InvoiceRow'],
    ['organisation_users', 'OrganisationUser'],
    ['organisations', 'Organisation'],
    ['payments', 'Payment'],
    ['salaries', 'Salary'],
    ['salary_rows', 'SalaryRow'],
    ['shifts', 'Shift'],
    ['time_reports', 'TimeReport'],
    ['users', 'User'],
  ])
  .setRelationships((models) => [
    models.Assignment.relationships({
      contract: a.belongsTo('Contract', 'contract_id'),
      shifts: a.hasMany('Shift', 'assignment_id'),
      user: a.belongsTo('User', 'user_id'),
    }),

    models.Contract.relationships({
      assignments: a.hasMany('Assignment', 'contract_id'),
      user: a.belongsTo('User', 'user_id'),
      invoices: a.hasMany('Invoice', 'contract_id'),
    }),

    models.Employment.relationships({
      user: a.belongsTo('User', 'user_id'),
    }),

    models.IdentityVerification.relationships({
      user: a.belongsTo('User', 'user_id'),
    }),

    models.Invoice.relationships({
      contract: a.belongsTo('Contract', 'contract_id'),
    }),

    models.Organisation.relationships({
      contact_person: a.belongsTo('User', 'contact_person_id'),
      users: a.hasMany('OrganisationUser', 'organisation_id'),
    }),

    models.OrganisationUser.relationships({
      organisation: a.belongsTo('Organisation', 'organisation_id'),
      user: a.belongsTo('User', 'user_id'),
    }),

    models.TimeReport.relationships({
      shift: a.belongsTo('Shift', 'shift_id'),
    }),

    models.Shift.relationships({
      assignment: a.belongsTo('Assignment', 'assignment_id'),
      time_reports: a.hasMany('TimeReport', 'shift_id'),
      user: a.belongsTo('User', 'user_id'),
    }),

    models.User.relationships({
      assignments: a.hasMany('Assignment', 'user_id'),
      contracts: a.hasMany('Contract', 'user_id'),
      employents: a.hasMany('Employment', 'user_id'),
      identity_verifications: a.hasMany('IdentityVerification', 'user_id'),
      main_contact_organisations: a.hasMany(
        'Organisation',
        'contact_person_id'
      ),
      organisations: a.hasMany('OrganisationUser', 'user_id'),
      shifts: a.hasMany('Shift', 'user_id'),
    }),
  ])
  .setAuthorization((models) => [
    models.User.authorization((allow) => [
      allow.ownerDefinedIn('username'),
      allow.groups(['admin']),
      allow.authenticated().to(['read']),
    ]),
    models.Assignment.authorization((allow) => [
      allow.ownerDefinedIn('owner'),
      allow.groups(['admin']),
    ]),
    models.BillingDetail.authorization((allow) => [
      allow.authenticated(),
      allow.groups(['admin']),
    ]),
    models.Contract.authorization((allow) => [
      allow.authenticated(),
      allow.groups(['admin']),
      allow.custom(),
    ]),
    models.IdentityVerification.authorization((allow) => [
      allow.ownerDefinedIn('owner'),
      allow.authenticated().to(['listen']),
    ]),
    models.Invoice.authorization((allow) => [
      allow.authenticated(),
      allow.groups(['admin']),
    ]),
    models.InvoiceRow.authorization((allow) => [
      allow.authenticated(),
      allow.groups(['admin']),
    ]),
    models.OrganisationUser.authorization((allow) => [
      allow.authenticated(),
      allow.groups(['admin']),
    ]),
    models.Organisation.authorization((allow) => [
      allow.authenticated(),
      allow.groups(['admin']),
    ]),
    models.Payment.authorization((allow) => [
      allow.authenticated(),
      allow.groups(['admin']),
    ]),
    models.Salary.authorization((allow) => [
      allow.authenticated(),
      allow.groups(['admin']),
    ]),
    models.SalaryRow.authorization((allow) => [
      allow.authenticated(),
      allow.groups(['admin']),
    ]),
    models.Shift.authorization((allow) => [
      allow.authenticated(),
      allow.groups(['admin']),
    ]),
    models.TimeReport.authorization((allow) => [
      allow.authenticated(),
      allow.groups(['admin']),
    ])
  ])


export type Schema = ClientSchema<typeof sqlSchema>;

export const data = defineData({
  schema: sqlSchema,
  authorizationModes: {
    defaultAuthorizationMode: 'userPool',
    apiKeyAuthorizationMode: {
      expiresInDays: 30,
    },
  },
});

@kekami
Copy link
Author

kekami commented May 30, 2024

And yes I tried nuking node_modules and lock files. So should be up to date.

@kekami
Copy link
Author

kekami commented May 30, 2024

I think you have to create a massive schema to test against, since I seem to be hitting the limits after just a few weeks worth of work ^^

@renebrandel
Copy link
Contributor

Yeah, we've expanded the schema sizes quite a bit but looks like there are still some optimization we need to look into for SQL. The same set on DDB seems to function okay.

It looks like it's the combination of renaming and setting relationships. The problem seems to be if you use both.

@kekami
Copy link
Author

kekami commented May 30, 2024

Cheers for looking into it! I've ts-ignored it for now, but I do miss type safety land 🥲

Overall the SQL implementation is really enjoyable to use, and it will be rock solid when the few couples of bugs I've found are fixed 💪

@rnrnstar2
Copy link

"@aws-amplify/backend": "^1.0.3",
    "@aws-amplify/backend-cli": "^1.0.4",

Although I am updating to the latest version like this, the @aws-amplify/data-schema version does not become 1.3.0.
Updating with npm update @aws-amplify/data-schema does not change anything.

"node_modules/@aws-amplify/api-graphql": {
      "version": "4.1.4",
      "resolved": "https://registry.npmjs.org/@aws-amplify/api-graphql/-/api-graphql-4.1.4.tgz",
      "integrity": "sha512-kZemNvJhFVWjMVgWwereelEvtb+6Ruj16ntCZ6PcgmBVkH2BiH0VamH+t8WkoL8bI2mSYGtmEBhCvVXv9QFlFw==",
      "dependencies": {
        "@aws-amplify/api-rest": "4.0.33",
        "@aws-amplify/core": "6.3.1",
        "@aws-amplify/data-schema": "^1.0.0",
        "@aws-sdk/types": "3.387.0",
        "graphql": "15.8.0",
        "rxjs": "^7.8.1",
        "tslib": "^2.5.0",
        "uuid": "^9.0.0"
      }
    },

"node_modules/@aws-amplify/backend": {
      "version": "1.0.3",
      "resolved": "https://registry.npmjs.org/@aws-amplify/backend/-/backend-1.0.3.tgz",
      "integrity": "sha512-/NNEtmu59v4x8Z/vLSmZnw/2PBMQ6RlB8E9glfldf6COs0DOacx4UeoPLRc7+E7eLWpTQWl4mPdSiWdBh5jb+w==",
      "dev": true,
      "dependencies": {
        "@aws-amplify/backend-auth": "^1.0.2",
        "@aws-amplify/backend-data": "^1.0.2",
        "@aws-amplify/backend-function": "^1.0.3",
        "@aws-amplify/backend-output-schemas": "^1.1.0",
        "@aws-amplify/backend-output-storage": "^1.0.1",
        "@aws-amplify/backend-secret": "^1.0.0",
        "@aws-amplify/backend-storage": "^1.0.2",
        "@aws-amplify/client-config": "^1.0.3",
        "@aws-amplify/data-schema": "^1.0.0",
        "@aws-amplify/platform-core": "^1.0.1",
        "@aws-amplify/plugin-types": "^1.0.0",
        "@aws-sdk/client-amplify": "^3.465.0",
        "lodash.snakecase": "^4.1.1"
      },
      "peerDependencies": {
        "aws-cdk-lib": "^2.127.0",
        "constructs": "^10.0.0"
      }
    },

@javabudd
Copy link

javabudd commented Jun 5, 2024

I think you have to create a massive schema to test against, since I seem to be hitting the limits after just a few weeks worth of work ^^

I have a schema with 2 items in it throwing this issue, I don't believe that's true.

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

No branches or pull requests

6 participants