Sequelize stack trace in docker-compose up

It looks like the same associaton source code (for sequelize) isgenerated twice. Why I don’t know, but it generates a stack trace and stops the Docker installation proecess.

Expected behavior

Successful creation of local DB wrapper microservice.

Actual behavior

docker-compose up crashes with message SequelizeAssociationError: You have used the alias organizationAccounts in two separate associations.

Offending source code

I grabbed the generated JS model that contains the problem, here:

Account.hasMany(models.organizationAccounts, {
      foreignKey: {
        name: 'accountIdKey',
        field: 'account_id',
      },
      as: 'organizationAccounts',
    });
    Account.hasMany(models.organizationAccounts, {
      foreignKey: {
        name: 'accountIdKey',
        field: 'account_id',
      },
      as: 'organizationAccounts',
    });

Failure Logs

~/src/docker/forest_admin$ cd "UrpAccount" && docker-compose up
Recreating urp_account ... done
Attaching to urp_account
urp_account |  
urp_account | > urpaccount@0.0.1 start /usr/src/app
urp_account | > node ./server.js
urp_account |  
urp_account | /usr/src/app/node_modules/sequelize/lib/associations/base.js:106
urp_account |       throw new AssociationError(`You have used the alias ${options.as} in two separate associations. ` +
urp_account |       ^
urp_account |  
urp_account | SequelizeAssociationError: You have used the alias organizationAccounts in two separate associations. Aliased associations must
have unique aliases.
urp_account |     at new Association (/usr/src/app/node_modules/sequelize/lib/associations/base.js:106:13)
urp_account |     at new HasMany (/usr/src/app/node_modules/sequelize/lib/associations/has-many.js:19:5)
urp_account |     at Function.hasMany (/usr/src/app/node_modules/sequelize/lib/associations/mixin.js:34:25)

Context

The default docker engine from your setup page.

  • Package Version:
  • Express Version:
  • Sequelize Version:
  • Database Dialect: Postgres
  • Database Version: 12.2
  • Project Name: UrpAccount

Hi @ArneStein,

Welcome to the ForestAdmin community and thank you for sharing your issue.

It looks like lumber detected twice the same association. To understand why and try to reproduce the issue, could you share with me the all content of the generated model and an empty sql dump of the table that produce the double association?

Thank you

Hello Lamatt,

Here is Postgres dump for the table (account):

--
-- Name: account; Type: TABLE; Schema: public; Owner: arne
--

CREATE TABLE public.account (
    description text,
    id integer DEFAULT nextval('public.id_seq'::regclass) NOT NULL,
    functions public.account_function[],
    plan_id integer,
    type integer,
    number text,
    vat_group_id integer
);


ALTER TABLE public.account OWNER TO arne;

--
-- Name: account account_pkey; Type: CONSTRAINT; Schema: public; Owner: arne
--

ALTER TABLE ONLY public.account
    ADD CONSTRAINT account_pkey PRIMARY KEY (id);


--
-- Name: account account_type_fkey; Type: FK CONSTRAINT; Schema: public; Owner: arne
--

ALTER TABLE ONLY public.account
    ADD CONSTRAINT account_type_fkey FOREIGN KEY (type) REFERENCES public.option(id);


--
-- Name: account ct_fk_account_plan; Type: FK CONSTRAINT; Schema: public; Owner: arne
--

ALTER TABLE ONLY public.account
    ADD CONSTRAINT ct_fk_account_plan FOREIGN KEY (plan_id) REFERENCES public.account_plan(id);


--
-- Name: account ct_fk_vat_code; Type: FK CONSTRAINT; Schema: public; Owner: arne
--

ALTER TABLE ONLY public.account
    ADD CONSTRAINT ct_fk_vat_code FOREIGN KEY (vat_group_id) REFERENCES public.tax_group(id) ON UPDATE CASCADE;


--
-- PostgreSQL database dump complete
--

I do the same for the referring table (organization):

--
-- Name: organization; Type: TABLE; Schema: public; Owner: arne
--

CREATE TABLE public.organization (
    id integer DEFAULT nextval('public.id_seq'::regclass) NOT NULL,
    name text,
    url text,
    description text,
    "group" text,
    plan_id integer,
    country_id integer,
    xtra jsonb,
    organization_number text
);


ALTER TABLE public.organization OWNER TO arne;

--
-- Name: organization organization_pkey; Type: CONSTRAINT; Schema: public; Owner: arne
--

ALTER TABLE ONLY public.organization
    ADD CONSTRAINT organization_pkey PRIMARY KEY (id);


--
-- Name: organization ct_fk_account_plan; Type: FK CONSTRAINT; Schema: public; Owner: arne
--

ALTER TABLE ONLY public.organization
    ADD CONSTRAINT ct_fk_account_plan FOREIGN KEY (plan_id) REFERENCES public.account_plan(id);


--
-- Name: organization organization_country_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: arne
--

ALTER TABLE ONLY public.organization
    ADD CONSTRAINT organization_country_id_fkey FOREIGN KEY (country_id) REFERENCES public.area_country(id);


--
-- PostgreSQL database dump complete
--

Here is model account.js:

// This model was generated by Lumber. However, you remain in control of your models.
// Learn how here: https://docs.forestadmin.com/documentation/v/v6/reference-guide/models/enrich-your-models
module.exports = (sequelize, DataTypes) => {
  const { Sequelize } = sequelize;
  // This section contains the fields of your model, mapped to your table's columns.
  // Learn more here: https://docs.forestadmin.com/documentation/v/v6/reference-guide/models/enrich-your-models#declaring-a-new-field-in-a-model
  const Account = sequelize.define('account', {
    description: {
      type: DataTypes.STRING,
    },
    functions: {
      type: DataTypes.ARRAY(DataTypes.ENUM(
        'cash_account',
        'vat_account',
        'cash_account',
      )),
    },
    number: {
      type: DataTypes.STRING,
    },
  }, {
    tableName: 'account',
    timestamps: false,
    schema: process.env.DATABASE_SCHEMA,
  });

  // This section contains the relationships for this model. See: https://docs.forestadmin.com/documentation/v/v6/reference-guide/relationships#adding-relationships.
  Account.associate = (models) => {
    Account.belongsTo(models.option, {
      foreignKey: {
        name: 'typeKey',
        field: 'type',
      },
      as: 'type',
    });
    Account.belongsTo(models.accountPlan, {
      foreignKey: {
        name: 'planIdKey',
        field: 'plan_id',
      },
      as: 'plan',
    });
    Account.belongsTo(models.taxGroup, {
      foreignKey: {
        name: 'vatGroupIdKey',
        field: 'vat_group_id',
      },
      as: 'vatGroup',
    });
    Account.hasMany(models.organizationAccounts, {
      foreignKey: {
        name: 'accountIdKey',
        field: 'account_id',
      },
      as: 'organizationAccounts',
    });
    Account.hasMany(models.organizationAccounts, {
      foreignKey: {
        name: 'accountIdKey',
        field: 'account_id',
      },
      as: 'organizationAccounts',
    });
    Account.hasMany(models.verificationRow, {
      foreignKey: {
        name: 'accountIdKey',
        field: 'account_id',
      },
      as: 'verificationRows',
    });
    Account.hasMany(models.verificationRow, {
      foreignKey: {
        name: 'accountIdKey',
        field: 'account_id',
      },
      as: 'verificationRows',
    });
    Account.hasMany(models.accountBalance, {
      foreignKey: {
        name: 'accountIdKey',
        field: 'account_id',
      },
      as: 'accountBalances',
    });
  };

  return Account;
};

Here is the same for organization:

// This model was generated by Lumber. However, you remain in control of your models.
// Learn how here: https://docs.forestadmin.com/documentation/v/v6/reference-guide/models/enrich-your-models
module.exports = (sequelize, DataTypes) => {
  const { Sequelize } = sequelize;
  // This section contains the fields of your model, mapped to your table's columns.
  // Learn more here: https://docs.forestadmin.com/documentation/v/v6/reference-guide/models/enrich-your-models#declaring-a-new-field-in-a-model
  const Organization = sequelize.define('organization', {
    name: {
      type: DataTypes.STRING,
    },
    url: {
      type: DataTypes.STRING,
    },
    description: {
      type: DataTypes.STRING,
    },
    group: {
      type: DataTypes.STRING,
    },
    xtra: {
      type: DataTypes.JSONB,
    },
    organizationNumber: {
      type: DataTypes.STRING,
    },
  }, {
    tableName: 'organization',
    underscored: true,
    timestamps: false,
    schema: process.env.DATABASE_SCHEMA,
  });

  // This section contains the relationships for this model. See: https://docs.forestadmin.com/documentation/v/v6/reference-guide/relationships#adding-relationships.
  Organization.associate = (models) => {
    Organization.belongsTo(models.accountPlan, {
      foreignKey: {
        name: 'planIdKey',
        field: 'plan_id',
      },
      as: 'plan',
    });
    Organization.belongsTo(models.areaCountry, {
      foreignKey: {
        name: 'countryIdKey',
        field: 'country_id',
      },
      as: 'country',
    });
    Organization.hasMany(models.user, {
      foreignKey: {
        name: 'organizationIdKey',
        field: 'organization_id',
      },
      as: 'users',
    });
    Organization.hasMany(models.user, {
      foreignKey: {
        name: 'organizationIdKey',
        field: 'organization_id',
      },
      as: 'users',
    });
    Organization.hasMany(models.organizationAccounts, {
      foreignKey: {
        name: 'organizationIdKey',
        field: 'organization_id',
      },
      as: 'organizationAccounts',
    });
    Organization.hasMany(models.organizationAccounts, {
      foreignKey: {
        name: 'organizationIdKey',
        field: 'organization_id',
      },
      as: 'organizationAccounts',
    });
    Organization.hasMany(models.accountBalance, {
      foreignKey: {
        name: 'organizationIdKey',
        field: 'organization_id',
      },
      as: 'accountBalances',
    });
    Organization.hasMany(models.serie, {
      foreignKey: {
        name: 'organizationIdKey',
        field: 'organization_id',
      },
      as: 'series',
    });
    Organization.hasMany(models.settingOrganization, {
      foreignKey: {
        name: 'organizationIdKey',
        field: 'organization_id',
      },
      as: 'settingOrganizations',
    });
    Organization.hasMany(models.setting, {
      foreignKey: {
        name: 'organizationIdKey',
        field: 'organization_id',
      },
      as: 'settings',
    });
  };

  return Organization;
};

Thanks,
Arne S.

I notice now there is a pivot table between account and organization, named organization_accounts (many to many). Here is the SQL of that:

--
-- Name: organization_accounts; Type: TABLE; Schema: public; Owner: arne
--

CREATE TABLE public.organization_accounts (
    organization_id integer,
    account_id integer
);


ALTER TABLE public.organization_accounts OWNER TO arne;

--
-- Name: organization_accounts organization_accounts_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: arne
--

ALTER TABLE ONLY public.organization_accounts
    ADD CONSTRAINT organization_accounts_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON UPDATE CASCADE;


--
-- Name: organization_accounts organization_accounts_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: arne
--

ALTER TABLE ONLY public.organization_accounts
    ADD CONSTRAINT organization_accounts_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES public.organization(id) ON UPDATE CASCADE;


--
-- PostgreSQL database dump complete
--

And here is the contents of the model for the pivot table (organization_accounts).

I see that the attributes in that model are duplicated, quite possibly the source of the error:

// This model was generated by Lumber. However, you remain in control of your models.
// Learn how here: https://docs.forestadmin.com/documentation/v/v6/reference-guide/models/enrich-your-models
module.exports = (sequelize, DataTypes) => {
  const { Sequelize } = sequelize;
  // This section contains the fields of your model, mapped to your table's columns.
  // Learn more here: https://docs.forestadmin.com/documentation/v/v6/reference-guide/models/enrich-your-models#declaring-a-new-field-in-a-model
  const OrganizationAccounts = sequelize.define('organizationAccounts', {
  }, {
    tableName: 'organization_accounts',
    underscored: true,
    timestamps: false,
    schema: process.env.DATABASE_SCHEMA,
  });
  OrganizationAccounts.removeAttribute('id');
  // This section contains the relationships for this model. See: https://docs.forestadmin.com/documentation/v/v6/reference-guide/relationships#adding-relationships.
  OrganizationAccounts.associate = (models) => {
    OrganizationAccounts.belongsTo(models.organization, {
      foreignKey: {
        name: 'organizationIdKey',
        field: 'organization_id',
      },
      as: 'organization',
    });
    OrganizationAccounts.belongsTo(models.account, {
      foreignKey: {
        name: 'accountIdKey',
        field: 'account_id',
      },
      as: 'account',
    });
    OrganizationAccounts.belongsTo(models.account, {
      foreignKey: {
        name: 'accountIdKey',
        field: 'account_id',
      },
      as: 'account',
    });
    OrganizationAccounts.belongsTo(models.organization, {
      foreignKey: {
        name: 'organizationIdKey',
        field: 'organization_id',
      },
      as: 'organization',
    });
  };

  return OrganizationAccounts;
};

Hi @ArneStein and welcome in our community :wave: :champagne: !!!

I tried to reproduce with your dump but it works perfectly fine. I’m pretty sure this is an old bug.
Did you do the docker pull forestadmin/lumber command first :thinking: ?
If it’s still not working do you have the issue with a lumber install ?

Hi,

I followed the instructions for Docker based install on your site. But I try again:

$ arne@coma-negra:~/src/docker/forest_admin/UrpAccount$ docker pull forestadmin/lumber Using default tag: latest latest: Pulling from forestadmin/lumber Digest: sha256:adc9f1a470e20520a1eddba080e109bed94cca903756742385a216b4574b0964 Status: Image is up to date for forestadmin/lumber:latest docker.io/forestadmin/lumber:latest

$ docker-compose up Starting urp_account ... done Attaching to urp_account urp_account | urp_account | > urpaccount@0.0.1 start /usr/src/app urp_account | > node ./server.js urp_account | urp_account | /usr/src/app/node_modules/sequelize/lib/associations/base.js:106 urp_account | throw new AssociationError(You have used the alias ${options.as} in two separate associations. + urp_account | ^ urp_account | urp_account | SequelizeAssociationError: You have used the alias organizationAccounts in two separate associations. Aliased associations must have unique aliases.

I will try the npm based install and see if it makes a difference. The Docker based install of FA does not work for my particular DB.

Best Regards,
Arne S.

To confirm, I do get the very same error message via npm start. However it simply uses the already generated model classes, so the error is then expected.

If the npm based approach would re-generate the models, maybe a difference? But the setup instructions here do not allow for that.

Looks like I’m stuck at this point, if we cannot figure out exactly what goes wrong, in Lumber setup, on my machine.

Best Regards,
Arne S.

Oh did not knew you were searching for a way out. You just need to remove the duplicate relationships in your models that’s all. But it really looks like you had an old image of forestadmin/lumber which is strange. Plus I don’t reproduce with the dump you’ve sent me

Hmmm … I corrected two generated model files, but then there came new similar model errors.

The image I have used was downloaded yesterday, according to your public instructions, so it should not be old.

I also now tried generating the models via the npm approach - and the problem persists.

So the issue is the same, both when generating the models in a Docker container, or in Node env on my local machine (Ubuntu 20.04).

Of course I could maybe manually keep correcting generated model files (and I felt motivated because I wanted to try your product). However, I would feel better about moving ahead, if I knew that this issue was truly resolved.

Best wishes,
Arne S.

@ArneStein lumber is only here to initialise your project. So your issue is only at the initialisation :wink:. SO you can correct them all manually :wink:
But could you share a a dump (with no data) of your database :pray: ? So I can reproduce your issue on lumber

Dump of DB structure, of course, here is pastebin pg_dump.

Yes it’s only to initialize the project, I know. But when I correct for one model, I get a new error for the next model…

Anyhow, I appreciate you looking into this. Thanks.

Hello @ArneStein,

I wasn’t able to reproduce the problem.

For your pleasure, a tar of the generated models on my side :wink:

models.tar.gz (4.3 KB)

When I compare the generated code, it looks rather different,like a different algorithm has been used on your side.

My side (models/account.js):

    Account.belongsTo(models.taxGroup, {
      foreignKey: {
        name: 'vatGroupIdKey',
        field: 'vat_group_id',
      },    
      as: 'vatGroup',
    });
    Account.hasMany(models.organizationAccounts, {
      foreignKey: {
        name: 'accountIdKey',
        field: 'account_id',
      },    
      as: 'organizationAccounts',
    });

Your side (same part, models/account.js):

   Account.belongsTo(models.taxGroup, {
      foreignKey: {
        name: 'vatGroupIdKey',
        field: 'vat_group_id',
      },
      as: 'vatGroup',
    });
    Account.belongsToMany(models.organization, {
      through: 'organizationAccounts',
      foreignKey: 'account_id',
      otherKey: 'organization_id',
    });

It has gone from has many of the pivot table (organiationAccounts) to belongsToMany of the target table (through the pivot table).

Why do we have different algorithms generating model code?

I have never attempted to install ForestAdmin ? Lumber, before two days ago.

Best Regards,
Arne S.

That sounds really strange.
But we must have a slight difference that explains the diff.

I’ve just given another try, with docker and with npm technique, and the result is the same on my machine it’s ok.

Did you have a try with NPM, in a fresh folder?

As a workaround, maybe you can use the models I provided?

Another info: we did not support Postgres 12.2 yet. We are going to run tests to check this.

Yes, I ran the NPM based install in a fresh folder too. Same models (with error) genarated, also then.

I was thinking, do you have some simple way of knowing exactly what version of Lumber I have? I’m guessing package.json would have that in it. Or I by inspecting one of the source files, involved in generating the models.

I understand I can use your provided models. However, as a senior dev, I find it difficult to leave behind something that very much appears an issue to be solved.

Hi @ArneStein,

The odds are high that the models generation difference is due to a difference of the Lumber CLI version.
You can run lumber --version to get the current lumber version you are currently using.
Let us know.

I get this version, when running command:

$ lumber --version
3.6.7

In the directory where I initiated with Docker and also the same, where I downloaded and run setup with NPM.

Hi @ArneStein. I wanted to keep you posted: I’ve just tried using your actual dump with lumber v3.6.8 (latest version) and PostgreSQL v12.12 and I still do not have the problem. For instance, the account file seems OK:

Account.belongsToMany(models.organization, {
  through: 'organizationAccounts',
  foreignKey: 'account_id',
  otherKey: 'organization_id',
});

I should have missed something, still investigating.

1 Like