mswjs / data

Data modeling and relation library for testing JavaScript applications.

Home Page:https://npm.im/@mswjs/data

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Properties with dots in their name are treated as paths instead of as literals

VanTanev opened this issue · comments

Given the following code:

export const db = factory({
  user: {
    'employee.id': primaryKey(() => 'abc-123'),
  }
})

db.user.create({ 'employee.id': 'some-id' })

We get this error:

Uncaught (in promise) OperationError: Failed to create a "user" entity: expected the primary key "employee.id" to have a value, but got: undefined
    at new OperationError (OperationError.js:28)
    at Object.create (factory.js:66)

This is because createModel uses lodash/set, which converts dot strings to deep properties (eg, among others):

if (propertyDefinition instanceof PrimaryKey) {
set(
properties,
propertyName,
initialValue || propertyDefinition.getValue(),
)
return properties
}

The resulting model object is:

  const user: {
    employee: {
      id: <primaryKey>
    }
  }
  // instead of 
  const user: {
    'employee.id': <primaryKey>
  }

I think that lodash/set is a poor fit, because properties with dots in them are completely valid. Is there any reason it was used other than convenience?

Hey, @VanTanev. Thanks for reporting this.

lodash.set was used to support nested objects (#113). Internally, we keep a flat map of deep object keys and use set to properly construct the end entities based on the model.

That's a valid concern that model properties that contained dots initially will be treated as nested properties by lodash. We need to account for such properties.

I suggest we preserve the original model properties by wrapping them in [] before passing to .set.

set(target, '["a.b.c"]', value)
// { "a.b.c": value }

This change will affect the following functions:

set(
properties,
propertyName,
initialValue || propertyDefinition.getValue(),
)

set(properties, propertyName, initialValue)

set(properties, propertyName, propertyDefinition())

We need also be careful about definePropertyAtPath:

set(target, parentPath, {})

@VanTanev, would you be interested in opening a pull request with the fix? Adding a simple integration test that fails at the moment would be a great start. Let me know if this interests you, I'd be happy to help you during the code review.

It's worth analyzing if nested objects are needed in createModel to begin with. Perhaps we can use plain property assignment there and utilize deep objects when creating entities.

Hey @kettanaito, I have already implemented this in #152. Is there anything you need me to improve?

@VanTanev, thank you for your work on that! I've seen the pull request, will review it as soon as I've got a spare minute. Looking forward to having this issue resolved.