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

Preserve custom type of auto-generated id property #3602

Closed
frbuceta opened this issue Aug 25, 2019 · 26 comments
Closed

Preserve custom type of auto-generated id property #3602

frbuceta opened this issue Aug 25, 2019 · 26 comments
Assignees
Labels
bug db:MongoDB Topics specific to MongoDB db:MySQL Topics specific to MySQL db:PostgreSQL Topics specific to PostgreSQL developer-experience Issues affecting ease of use and overall experience of LB users

Comments

@frbuceta
Copy link
Contributor

Steps to reproduce

  1. Create a model with an id property with the option generated: true
    Captura de pantalla 2019-08-25 a las 16 25 52
  2. You make a find and the answer is ... uuid: NaN or id: NaN
    Captura de pantalla 2019-08-25 a las 16 26 03

Current Behavior

As I describe above, when the model is in generated: true it has to return the normal id, not NaN

Expected Behavior

Captura de pantalla 2019-08-25 a las 16 26 47

Link to reproduction sandbox

When I put generated: false ...
Captura de pantalla 2019-08-25 a las 16 26 37
Captura de pantalla 2019-08-25 a las 16 26 47

Additional information

OS: MacOS 10.14.6
Node: v12.9.0
Npm: v6.11.2
Loopback: latest ->

@loopback/cli version: 1.21.4

@loopback/* dependencies:
  - @loopback/authentication: ^2.1.11
  - @loopback/boot: ^1.5.3
  - @loopback/build: ^2.0.8
  - @loopback/context: ^1.21.4
  - @loopback/core: ^1.9.3
  - @loopback/metadata: ^1.2.10
  - @loopback/openapi-spec-builder: ^1.2.10
  - @loopback/openapi-v3: ^1.9.4
  - @loopback/repository-json-schema: ^1.9.5
  - @loopback/repository: ^1.12.0
  - @loopback/rest: ^1.17.0
  - @loopback/testlab: ^1.7.4
  - @loopback/docs: ^1.29.3
  - @loopback/example-hello-world: ^1.2.11
  - @loopback/example-log-extension: ^1.2.11
  - @loopback/example-rpc-server: ^1.2.11
  - @loopback/example-todo: ^1.7.4
  - @loopback/example-soap-calculator: ^1.6.12
  - @loopback/service-proxy: ^1.3.3
  - @loopback/http-caching-proxy: ^1.1.10
  - @loopback/http-server: ^1.4.10
  - @loopback/example-todo-list: ^1.9.4
  - @loopback/dist-util: ^0.4.0
  - @loopback/rest-explorer: ^1.3.4
  - @loopback/eslint-config: ^4.0.1
  - @loopback/example-express-composition: ^1.5.4
  - @loopback/example-greeter-extension: ^1.3.11
  - @loopback/booter-lb3app: ^1.2.11
  - @loopback/example-lb3-application: ^1.1.11
  - @loopback/example-greeting-app: ^1.1.11
  - @loopback/example-context: ^1.2.11
  - @loopback/repository-tests: ^0.4.2
  - @loopback/extension-health: ^0.2.3
  - @loopback/authorization: ^0.2.0

Related Issues

See Reporting Issues for more tips on writing good issues

@frbuceta frbuceta added the bug label Aug 25, 2019
@dougal83
Copy link
Contributor

What db connector are you using? Do you have an example repo to demonstrate this issue?

@frbuceta
Copy link
Contributor Author

frbuceta commented Aug 25, 2019

I use MySQL connector, I don't know if I can create an example at codesandbox or codepen?

@hacksparrow
Copy link
Contributor

@frbuceta you can create one on GitHub itself. Please share a very basic app which reproduces the problem you are reporting.

@dhmlau dhmlau added the needs steps to reproduce Issues missing a small app and/or instructions for reproducing the problem label Aug 27, 2019
@frbuceta
Copy link
Contributor Author

@frbuceta
Copy link
Contributor Author

frbuceta commented Sep 8, 2019

someone?

@dougal83
Copy link
Contributor

dougal83 commented Sep 9, 2019

[@frbuceta] I suppose the obvious question is to ask how mysql is generating an id with a guid signature? I was under the impression that mysql sequences generated ids as integers.

Can you confirm that npm run migrate has been executed correctly to update database structure and that was performed on a clean db (or use npm run migrate -- --rebuild that can lose data)? You shouldn't be able to return 'uuid' = null if the database was clean and migrate has been run correctly.

@basavarajvs
Copy link

What db connector are you using? Do you have an example repo to demonstrate this issue?

I faced the problem with postgresql connector too.

@frbuceta
Copy link
Contributor Author

@fabien I suppose the obvious question is to ask how mysql is generating an id with a guid signature? I was under the impression that mysql sequences generated ids as integers.

Can you confirm that npm run migrate has been executed correctly to update database structure and that was performed on a clean db (or use npm run migrate -- --rebuild that can lose data)? You shouldn't be able to return 'uuid' = null if the database was clean and migrate has been run correctly.

MySQL supports UUID --> https://mysqlserverteam.com/mysql-8-0-uuid-support/
And so you force me to use it --> https://dba.stackexchange.com/questions/149059/how-to-insert-a-record-with-generated-identifiers-using-uuid

@dougal83
Copy link
Contributor

[@frbuceta] I suppose the obvious question is to ask how mysql is generating an id with a guid signature? I was under the impression that mysql sequences generated ids as integers.

MySQL supports UUID --> https://mysqlserverteam.com/mysql-8-0-uuid-support/
And so you force me to use it --> https://dba.stackexchange.com/questions/149059/how-to-insert-a-record-with-generated-identifiers-using-uuid

@frbuceta Thanks for info. Personally I would avoid using features like that as it could be more difficult to debug. Could using the non default id be the issue for the connector? Perhaps you could confirm by switching to standard id(runs correctly for me)?

@frbuceta
Copy link
Contributor Author

[@frbuceta] I suppose the obvious question is to ask how mysql is generating an id with a guid signature? I was under the impression that mysql sequences generated ids as integers.

MySQL supports UUID --> https://mysqlserverteam.com/mysql-8-0-uuid-support/
And so you force me to use it --> https://dba.stackexchange.com/questions/149059/how-to-insert-a-record-with-generated-identifiers-using-uuid

@frbuceta Thanks for info. Personally I would avoid using features like that as it could be more difficult to debug. Could using the non default id be the issue for the connector? Perhaps you could confirm by switching to standard id(runs correctly for me)?

With the standard id, if it works
On the other hand in postgres if what I am using here works
Is this a bug or is it my thing?

@dougal83
Copy link
Contributor

dougal83 commented Sep 25, 2019

Is this a bug or is it my thing?

As id is NaN, it seems to be looking for an integer and getting your guid. I'm thinking it could be the connector's ability to handle types of id... Saying that there is an issue with the connector atm, not sure if it is related. We need someone smart to have a look.

@hacksparrow, what do you think?

@bajtos bajtos added db:MySQL Topics specific to MySQL db:PostgreSQL Topics specific to PostgreSQL labels Sep 27, 2019
@bajtos bajtos changed the title Id is NaN when I tell him that it is generated Preserve custom type of auto-generated id property Sep 27, 2019
@bajtos
Copy link
Member

bajtos commented Sep 27, 2019

I am afraid this is a limitation of the current juggler. If the PK property is defined as autogenerated, juggler always overwrite the type with the default id type supplied by the connector. See here:

https://github.com/strongloop/loopback-datasource-juggler/blob/a90dc0eac4abb17f2335d0803e0828fa79e444dc/lib/datasource.js#L722-L728

    if (idProp && idProp.generated && this.connector.getDefaultIdType) {
      // Set the default id type from connector's ability
      const idType = this.connector.getDefaultIdType() || String;
      idProp.type = idType;
      modelClass.definition.rawProperties[idName].type = idType;
      modelClass.definition.properties[idName].type = idType;
    }

SQL connectors like MySQL and PostgreSQL use number as the default id type. So if the database generates a unique string id, juggler will try to convert that value to a number, and thus return NaN.

This is causing problems for MongoDB too, where a property defined as {type: 'string', mongdb: {dataType: 'ObjectID'}} is converted to {type: ObjectID}.

I think the best way forward is to modify juggler to stop overwriting user-provided id type with connector-provided type. However, I am not able to predict what can such change break.

I think the safest option is to preserve backwards compatibility and introduce a new property-definition metadata to disable type rewrite.

 if (
  idProp && 
  idProp.generated && 
  this.connector.getDefaultIdType && 
  idProp.useDefaultIdType !== false
) {
  // Set the default id type from connector's ability
  // ...
}

This can be implemented as a semver-minor change of juggler.

Then we can decide what to do in LB4:

  • We can detect generated: true and add useDefaultIdType: false to the property definition sent to juggler. This will require a semver-major release of @loopback/repository.
  • We can pass this task to app developers and require them to set useDefaultIdType: false. Personally, I dislike this option a lot - it's leaking juggler-specific ugliness to LB4 model metadata and also makes it difficult for new LB developers to discover what's the correct way of setting up a generated property.

@strongloop/loopback-next thoughts?

@bajtos bajtos added the developer-experience Issues affecting ease of use and overall experience of LB users label Sep 27, 2019
@frbuceta
Copy link
Contributor Author

I think the best way forward is to modify juggler to stop overwriting user-provided id type with connector-provided type. However, I am not able to predict what can such change break.

Couldn't we make a warning message or do something to implement this first idea?

  • We can detect generated: true and add useDefaultIdType: false to the property definition sent to juggler. This will require a semver-major release of @loopback/repository.

In case of not being able, I also think that this would be the best change

@hacksparrow
Copy link
Contributor

@frbuceta can you share the structure details of the uuid field in your database? Mine is int(11) with AUTO_INCREMENT .

Which version of loopback-connector-mysql are you using?

Your sample app autogenerated the id starting with 1 and incremented it with each new user. generated model property made no difference.

@frbuceta
Copy link
Contributor Author

frbuceta commented Oct 2, 2019

@frbuceta can you share the structure details of the uuid field in your database? Mine is int(11) with AUTO_INCREMENT .

Which version of loopback-connector-mysql are you using?

Your sample app autogenerated the id starting with 1 and incremented it with each new user. generated model property made no difference.

User Table

create table users
(
    uuid       varchar(36)                        not null
        primary key,
    first_name varchar(20)                        not null,
    last_name  varchar(20)                        not null,
    email      varchar(45)                        not null,
    password   char(60)                           null,
    updated_at datetime                           null on update CURRENT_TIMESTAMP,
    created_at datetime default CURRENT_TIMESTAMP not null
);

UUID Trigger

CREATE DEFINER = CURRENT_USER TRIGGER `classdrive`.`users_BEFORE_INSERT` BEFORE INSERT ON `users` FOR EACH ROW
BEGIN
	SET NEW.uuid = UUID();
END

Captura de pantalla 2019-10-03 a las 0 33 24

Versions:
@loopback/core 1.10.4
loopback-connector-mysql 5.4.2

@frbuceta
Copy link
Contributor Author

We can add to the list to MsSQL @bajtos

@bajtos bajtos removed the needs steps to reproduce Issues missing a small app and/or instructions for reproducing the problem label Nov 19, 2019
@frbuceta
Copy link
Contributor Author

As soon as I have a moment, I update in code.

@richardpringle
Copy link

@strongloop/loopback-next thoughts?

@bajtos, I completely agree. Leaking the implementation details into LB4 would be a mistake IMO. Using UUIDs as the primary key is pretty common in my experience. I also think that @loopback/repository could possibly offer UUID generation (in-app generation instead of in the db) if the majority of cases are centred around UUIDs.

As for Mongo... I'm not quite sure what a good solution for that would be.

@bajtos bajtos added the db:MongoDB Topics specific to MongoDB label Nov 28, 2019
@bajtos
Copy link
Member

bajtos commented Nov 28, 2019

For anybody watching this issue: loopbackio/loopback-datasource-juggler#1783 introduced a new property-level flag useDefaultIdType that instructs juggler to ignore the PK-type defined by the connector and use the type provided by the developer.

I am thinking that perhaps we can modify legacy juggler bridge to enable that flag automatically when the property metadata includes type field. I mean if the LB4 user wrote @property({type: 'string', id: true, generated: true}), then I think it's safe to assume they wanted to use string, not ObjectID (in Mongo) or number (in SQL).

Would you consider that new behavior as a breaking change?

/cc @strongloop/loopback-next

@frbuceta
Copy link
Contributor Author

frbuceta commented Dec 2, 2019

For anybody watching this issue: strongloop/loopback-datasource-juggler#1783 introduced a new property-level flag useDefaultIdType that instructs juggler to ignore the PK-type defined by the connector and use the type provided by the developer.

I am thinking that perhaps we can modify legacy juggler bridge to enable that flag automatically when the property metadata includes type field. I mean if the LB4 user wrote @property({type: 'string', id: true, generated: true}), then I think it's safe to assume they wanted to use string, not ObjectID (in Mongo) or number (in SQL).

Would you consider that new behavior as a breaking change?

/cc @strongloop/loopback-next

I am in favor of this change

@bajtos
Copy link
Member

bajtos commented Dec 5, 2019

@frbuceta would you like to contribute that change yourself?

@frbuceta
Copy link
Contributor Author

frbuceta commented Dec 5, 2019

@frbuceta would you like to contribute that change yourself?

Of course, lately I haven't had much time but tomorrow I start with it. Sorry for the delay

@agnes512
Copy link
Contributor

agnes512 commented Jan 2, 2020

I think LB4 is able to auto-generate uuid for MySQL with auto-migration. Here is my model definition:

export class Customer extends Entity {
  @property({
    id: true,
    type: 'string'
    defaultFn: 'uuidv4',
    // generated: true,  -> not needed
    // useDefaultIdType -> not needed
  })
  uuid_id: string;

  @property({
    generated: true,
    type: 'number',
  })
  int_id: number;

  @property({
    type: 'string',
  })
  name: string;
}

Ref: General property properties

As what we discussed above, for int default type DBs such as MySQL, PlstgreSQL, generated=true only auto-generates int type id.
@frbuceta Does defaultFn here meet your requirement?

(Out of scope: if a property is not the identifier, the flag generated has no effect. ( e.g int_id))

@bajtos
Copy link
Member

bajtos commented Jan 7, 2020

@agnes512 I believe that we want to let the database generate the UUID values, not LoopBack. The suggested property definition will generate the id value at LoopBack side.

  @property({
    id: true,
    type: 'string'
    defaultFn: 'uuidv4',
    // generated: true,  -> not needed
    // useDefaultIdType -> not needed
  })
  uuid_id: string;

What we want to achieve is the following property definition inside the juggler layer:

{
  id: true,
  type: 'string',
  generated: true,
}

Now because juggler is replacing any user-provided type with connector-provided default type whenever a property is an auto-generated primary key ({id: true, generated: true}), i.e. replacing type: 'string' to type: 'number' for MySQL connector, we want to add useDefaultIdType flag to disable that change.

@agnes512
Copy link
Contributor

agnes512 commented Feb 18, 2020

Note that even #4270 is merged, the flag is only preserved at the juggler level. MySQL connector needs to be updated, see https://github.com/strongloop/loopback-connector-mysql/blob/master/lib/migration.js#L629 ( PostgreSQL is able to auto generate uuid now)).

@lemchen
Copy link

lemchen commented Jun 30, 2020

useDefaultIdType: false seems doesn't work in mongodb connector, mongodb still generate default string id value

@frbuceta frbuceta closed this as completed Apr 8, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug db:MongoDB Topics specific to MongoDB db:MySQL Topics specific to MySQL db:PostgreSQL Topics specific to PostgreSQL developer-experience Issues affecting ease of use and overall experience of LB users
Projects
None yet
Development

No branches or pull requests

9 participants