Giter Site home page Giter Site logo

mswjs / data Goto Github PK

View Code? Open in Web Editor NEW
746.0 6.0 50.0 909 KB

Data modeling and relation library for testing JavaScript applications.

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

License: MIT License

TypeScript 99.77% JavaScript 0.23%
mocking orm data-modeling api fixtures testing testing-tool

data's People

Contributors

aprillion avatar borodean avatar camchenry avatar cloud-walker avatar contigen avatar ericchernuka avatar gidesan avatar imyasha avatar indatawetrust avatar kaiwensun avatar kettanaito avatar marcosvega91 avatar mesteche avatar methuselah96 avatar msutkowski avatar olivierwilkinson avatar phryneas avatar roertbb avatar ruisaraiva19 avatar shawntax avatar sophyphreak avatar susiyaki avatar tatsushitoji avatar themagickoala avatar timkolotov avatar vantanev avatar yethee avatar yishayweb avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

data's Issues

Feature Request/Unexpected behaviour: Graphql handlers just return string ids instead of relationships

I just noticed that over in #96 while adding tests.

If I were to add a second model "Post" with a oneOf relationship "author" to "User", that would still be output as String instead of referencing the User type, which would be expected of a graphql api.

This is not a problem of my other PR, but a conceptual problem with toHandlers. It would probably have to be part of Factory instead of being part of Model, to handle these spanning relationships and quite some additional work on getGraphQLType, which is very simple right now:

export function getGraphQLType(value: any) {
const resolvedValue = typeof value === 'function' ? value() : value
switch (resolvedValue.constructor.name) {
case 'Number':
return GraphQLInt
case 'Boolean':
return GraphQLBoolean
default:
return GraphQLString
}
}

Add a "snapshot" utility

Expected behavior

The snapshot utility would memorize the state of the database (its entities) at a call time, and allow to restore to that memorized state at any further point of time (i.e. before each test).

Usage

import { factory, primaryKey, snapshot } from '@mswjs/data'

const db = factory({
  user: {
    id: primaryKey(String),
    firstName: String
  }
})

db.user.create({ firstName: 'John' })
db.user.create({ firstName: 'Kate' })

// Takes a snapshot of the database
// in this point of time.
const restore = snapshot(db)

// Perform some other actions.
db.user.create({ firstName: 'Joe' })

// Restores the database to the taken snapshot.
// Will result in a database with 2 users:
// "John" and "Kate".
restore()

GitHub

  • Originated from #51
  • Related to #85

Clear all entities

I think that for testing purposes we need a way to delete all entities between tests. As an alternative, a user can loop all entities and call the delete method.

Handle Undefined Object in `isInternalEntity`

Problem

When adding a non-entity in a relation property, the following error occurs:

  TypeError: Cannot use 'in' operator to search for '__type' in null

This also breaks dropping a db.

Expected Outcome

The code should not throw because of a null object, but error or warn the user about him using a normal object instead of an entity for a relation.

add the ability to have some "undroppable" initial data

In my current custom fake data implementation, I have the ability to seed the database with some initial data, that will remain untouched across drops:

const initialData: DatabaseData = Object.freeze({
  todos: [...],
  users: [...],
})

let data = initialData

/**
 * We need this mechanism as we need to simulate a "database"
 * reset for every test run in order to maintain a clean
 * "database" state for every test.
 *
 * @note it is crucial to not mutate the internal structure of
 * the initialData, in order to achieve this.
 */
export const resetData = () => {
  data = initialData
}

export const getData = () => data

export const setData = (transformer: (data: DatabaseData) => DatabaseData) => {
  data = transformer(data)
}

Currently using @mswjs/data, if I reset the db with drop(db), I lose ALL the data, and I think is not practical to reseed the data on each test ๐Ÿค”

Server-Client synchronization

In order to use this library for SSR scenarios, there should be a way to synchronize the DB state on the server based on the operations performed on the client.

Scenario

  1. I load a page with an empty BD on the server.
  2. I load the client, it gets the payload from the server, which gets it from the server-side DB.
  3. I perform some client-side interactions, creating a new entity in the client-side DB.
  4. I reload the page. I should see the client-side DB state being delivered from the server.

This doesn't have to be an actual synchronization, given the limitations of sharing data between environments. It may be a smart client-side DB that hydrates the values from the storage (#49). That would, however, result in the SSR mismatch with the different data between the server and the client.

Data querying is broken

@mswjs/data package version 0.1.1 does not work as expected when querying data. Example from test/query/string.test.ts

const setup = () => {
  const db = factory({
    recipe: {
      id: primaryKey(datatype.uuid),
      title: String,
      category: String
    }
  });
  db.recipe.create({
    title: "New York Pizza",
    category: "pizza"
  });
  db.recipe.create({
    title: "Chocolate Cake",
    category: "cake"
  });
  db.recipe.create({
    title: "Pizza Mozzarrela",
    category: "pizza"
  });
  return db;
};
const db = setup();
const allPizza = db.recipe.findMany({
  where: {
    category: {
      equals: "pizza"
    }
  }
});

console.log(allPizza) prints:

[
  {
    "id": "0ef2fd42-f965-4e37-b8d7-71bea0b59396",
    "title": "New York Pizza",
    "category": "pizza",
    "__type": "recipe",
    "__primaryKey": "id"
  },
  {
    "id": "e37b2c74-9fbc-4dd1-bfe3-de67d4446a33",
    "title": "Chocolate Cake",
    "category": "cake",
    "__type": "recipe",
    "__primaryKey": "id"
  },
  {
    "id": "b80457ef-1c33-4c6e-93c1-5ed3e0414377",
    "title": "Pizza Mozzarrela",
    "category": "pizza",
    "__type": "recipe",
    "__primaryKey": "id"
  }
]

expected:

[
  {
    "id": "0ef2fd42-f965-4e37-b8d7-71bea0b59396",
    "title": "New York Pizza",
    "category": "pizza",
    "__type": "recipe",
    "__primaryKey": "id"
  },
  {
    "id": "b80457ef-1c33-4c6e-93c1-5ed3e0414377",
    "title": "Pizza Mozzarrela",
    "category": "pizza",
    "__type": "recipe",
    "__primaryKey": "id"
  }
]

see example codesandbox here

Print some message as warning instead of throw an error

I was thinking if the following code is correct

invariant(
    records.size === 0,
    `Failed to execute query on the "${modelName}" model: unknown database model.`,
  )

If there is no record in the database queries will throw an error. Maeby this error can be turned into a warning message?
Methods will return a null object or an empty array and could print a message to the console. We can add also an option to quite all messages.

I create a factory with one table Todo then my todo list component will make an HTTP call on mount to get all todos, because of I don't have any entities my API will throw the error. As an alternative, we can intercept the error in the handler and return null or the empty array.

Primary key differs in server vs browser

When @mswjs/data is used with MSW in situations when MSW handles both server- and client-side requests (i.e. with NextJS), the primary keys generated are different on the server and in the browser. This disrupts the data integrity and makes certain features like pagination unfunctional (cursor never matches, as the entity with such ID doesn't exist).

I wonder, should we handle this at the library's side somehow?

Reproduction steps

  1. Use the NextJS MSW template.
  2. Create the following database and handlers:
import { random } from 'faker'
import { rest } from 'msw'
import { factory, primaryKey } from '@mswjs/data'

const db = factory({
  book: {
    id: primaryKey(random.uuid),
    title: random.words
  }
})
db.book.create()
db.book.create()
db.book.create()
db.book.create()

export const handlers = [
  rest.get('/books', (req, res, ctx) => {
    const cursor = req.url.searchParams.get('cursor')

    const books = db.book.findMany({
      which: {},
      take: 2,
      cursor,
    })
  
    const nextCursor = books[books.length - 1].id
  
    return res(ctx.json({ cursor: nextCursor, books })
  })
]
  1. Implement a page that performs the following request:
export default function HomePage({ res }) {
  const [books, setBooks] = useState(null)

  const handleFetchMore = () => {
    fetch(`/books?cursor=${res.cursor}`).then(res => res.json()).then(setBooks)
  }

  return (
    <div>
      {res.books.map(book => <p>{book.title}</p>)}
      {books?.map(book => <p>{book.title}</p>)}
      <button onClick={handleFetchMore}>Fetch more</button>
    </div>
  )
}

HomePage.getServerSideProps = async () => {
  const res = await fetch('/books')

  return {
    props: { res }
  }
}

Support "updateMany" model method

const updatedRecords = db.user.updateMany({
  which: {
    createdAt: { gt: new Date('2020-04-01') }
  },
  data: {
    isLegacyCustomer: true
  }
})

Entity with `manyOf` Relation not Being Updated When Updating Relationship with MSW

Problem

Whenever I update an entity that is related to another through MSW, the other entity is not updated. This only happens when done through MSW. If I were to create an entity and update it imperatively (like inside a function) it would work as expected.

Reproduction steps:

  1. Create an entity outside of MSW with db.create and link with another entity in a manyOf relationship.
  2. In an MSW route, update the relationship.
  3. Query the first entity with db.findFirst.

Reproduction Sandbox:

https://codesandbox.io/s/manyof-repro-soic5?file=/src/msw.ts

Notes:

  • After debugging, the getter in defineRelationalProperties is not being called. Perhaps, the entity has detached.
  • Maybe a Proxy Object could be better than Object.defineProperties

Support pagination of the results

Offset-based pagination

db.user.findMany({
  which: { ... },
  take: 10, // the size of the page
  skip: 2 // the offset of the page
})

Cursor-based pagination

db.user.findMany({
  which: { ... },
  take: 5,
  cursor: 'abc-123'
})

Where cursor is a primary key of the record to begin pagination from.

`primaryKey` types does not support numbers

Hello! I'm trying out the package for the first time, and I've noticed that the primaryKey type does not support numbers:

export type PrimaryKeyType = string

It is intentional? I need it to be number, as the real db my backend is using has numeric ids ๐Ÿ˜…

I'll be happy to open a PR if needed โค๏ธ

Support relations when generating REST handlers

GitHub

  • Follow up #12

What

Given the following model declarations:

const db = factory({
  user: {
    id: primaryKey(String),
    posts: manyOf('post')
  },
  post: {
    slug: primaryKey(String)
  }
})

And called db.user.toHandlers, the following relation handlers must be created in addition to the default CRUD handlers:

  • GET /users/:id/posts, returns all posts that belong to the user.
  • GET /users/:id/posts/:slug, returns a post by a slug that belongs to the user.

I'd say that PUT and DELETE methods shouldn't be nested in this way, forcing you to update/delete a post instance directly (i.e. PUT /posts/:slug).

Questions

  • Should oneOf relations pluralize the model name in the handler URL?

Use "__primaryKey" instead of "__nodeId"

With the introduction of primaryKey, it effectively becomes a unique node's ID. It should be used instead of an extra __nodeId that used to be internally assigned.

Omit internal properties when returning a public record

The properties described in the InternalEntityProperties interface must not be included in the publicly returned records:

data/src/glossary.ts

Lines 46 to 49 in 3dc15fd

export interface InternalEntityProperties<ModelName extends KeyType> {
readonly __type: ModelName
readonly __primaryKey: PrimaryKeyType
}

People are likely to assert the publicly returned data directly, i.e. with expect(db.user.findFirst(...)).toEqual({...}). Including internal properties is implementation detail leakage and should be avoided.

Parse model definition when creating a model API

The model definition should be parsed on the root level of createModelApi, as it derives additional information from the model definition (properties, relations, primary key). There is no need to parse it each time when creating an entity (current behavior).

This change would also allow for various extensions (i.e. client sync and persistency) to access the model's relational properties on the root level when applying the extension. Extensions then may use relational properties information to provision a custom logic around them (i.e. properly serializing relational values into sessionStorage for client-side persistency).

This may require restructuring the data flow, as parseModelDeclaration won't accept the initial values anymore. It may return a function that applies those values to the parsed properties/relations, so it could be invoked later in .create().

Support "strict" mode on model methods

Affected methods:

  • findOne
  • findMany
  • update
  • delete

Expected behavior

When the strict: true is set on the operation and the operation fails to query for an entity in the database, throw an exception.

const db = factory({
  user: { id: random.uuid }
})

// No users have been created yet.

// This `update` operation should throw an exception
// stating that there are no user found by the given criteria.
db.user.update({
  which: { id: { equals: 'abc' } },
  data: { firstName: 'John' },
  strict: true, 
})

Export pre-populated databases

We can provide some api to generate handles, entities, models, for example I can create Pokemon API by using

import { createPokemonAPIs } from '@mswjs/data/pokemon'

createPokemonAPIs(mswInstance)

The function will create a new db with pokemon entities, populate with some data and attach to msw instance the handlers.
We can add more examples that can be used for POC applications

Support a method to generate REST API request handlers from a model

const db = factory({
  user: { ... }
})

const handlers = db.user.toHandlers()

Calling .toHandlers() method on a model generates a list of REST API request handlers. Converts a model name into plural where applicable.

// Get the list of all users.
rest.get('/users')

// Get a user by id (primary key).
rest.get('/users/:userId')

// Create a new user.
rest.post('/users')

// Partially update an existing user.
rest.put('/users/:userId')

// Delete a user by id.
rest.delete('/users/:userId')

Such handlers are integrated with MSW and can be used directly when setting up a worker/server:

import { setupWorker } from 'msw'
import { db } from './db'

setupWorker(
  ...db.user.toHandlers(),
  ...db.post.toHandlers(),
)

The generated handlers also implement their response resolver functions, properly updating the db and returning the data respective to the method in the response. For example:

rest.get('/users', (req, res, ctx) => {
  return res(ctx.json(db.user.getAll()))
})

Support "upsert" and "upsertMany" model method

An upsert method updates an entity matching a query or creates a new entity if there was none. upsertMany behaves the same with the difference that it operates on multiple entities.

const newOrUpdatedUser = db.user.upsert({
  query: {
    which: { id: { equals: 'abc-123 } }
  },
  data: {
    firstName: 'John'
  }
})

Support unique/shared relational models

Should we support unique/shared relational models?

user: {
  // A user may have one country, but multiple users can reference the same country.
  country: oneOf('country'),

  // A user may have one invite, but one invite cannot belong to multiple users.
  invite: oneOf('invite', { unique: true }),

  // A user may have multiple roles, and multiple users can reference the same role.
  roles: manyOf('role'),

  // A user may have many posts, but one post cannot belong to multiple users.
  posts: manyOf('post', { unique: true }),
}

The oneOf/manyOf API we have at the moment behaves as unique: false.

Support sorting of records

First of all, thanks for this library! It really eases the process of mocking a database.

While implementing pagination in my mock REST API, I couldn't find any documentation about sorting. Does that feature exists and is not documented? If not, are there any plans to add it already or I'm the first one with a use case for it?

Thanks in advance!

Error on Response Header Graphql

I have got this error using "db.js" with factory and create to use automatic GraphQL CRUD handlers,
and setupWorker using toHandlers function in GraphQL Mode.

Using the manual Handler "graphql.query('Users'..." the problem is not present.

This is the error:

Uncaught TypeError: list.forEach is not a function
    at Object.listToHeaders (fetch-deps.js:504)
    at prepareResponse (RequestHandler-deps.js:1529)
    at GraphQLHandler.log (graphql-deps.js:3279)
    at Object.onMockedResponseSent (index.js:1520)
    at _c (index.js:1490)

Here the browser.js:

import { setupWorker } from 'msw'
import { factory, primaryKey } from '@mswjs/data';

 const db = factory({
	user: {
	  id: primaryKey(String),
	  username: String,
	  email: String,
	},
  });
  db.user.create({
    id: '1',
    username: 'Alice',
    email: '[email protected]',
  })
  export const worker = setupWorker(...db.user.toHandlers('graphql', 'http://localhost:8000/api'));

Model doesn't support array of strings, numbers or booleans

Hi.

I cannot create models with array fields. The models support array of arrays or array of object, but not array of strings, numbers or booleans.

Here is an example:

import { primaryKey } from "@mswjs/data";

const car = {
  id: primaryKey((id: string) => id),
  name: (carName: string): string => carName,
  dimensions: () => [1, 2, 3], // <-- error
};

The browser gives the following error:

main.js:27438 Uncaught TypeError: Cannot use 'in' operator to search for '__type' in true
    at Object.isInternalEntity (isInternalEntity.js?0caa:9)
    at eval (removeInternalProperties.js?5ec5:44)
    at Array.map (<anonymous>)
    at eval (removeInternalProperties.js?5ec5:43)
    at Array.map (<anonymous>)
    at Object.removeInternalProperties (removeInternalProperties.js?5ec5:35)
    at Object.create (factory.js?898e:72)
    at handlers (person.mockRequest.ts?f8cc:34)
    at eval (handlers.ts?0e5c:21)
    at Array.flatMap (<anonymous>)

Am I doing something wrong or is this a bug?

Thanks!

Support both pagination and total results count

It should be possible to paginate the results of .findMany() but expose the total count of results for the query.

I suggest extending the .count() model method to accept a query. If given a query, the .count() method would filter the database based on that query and return the count of records matching it.

const filter = { pagesCount: { gte: 500 } }

// Returns 5 books of the second page.
const paginagedResults = db.book.findMany({
  which: filter,
  take: 5,
  offset: 10,
})

// Returns the total amount of books that match the `filter` query.
const totalBooks = db.book.count(filter)

Persistency layer

I suggest we add a client-side synchronization layer to keep multiple clients that use the same DB in sync.

Implementation

Each factory call attaches a sync middleware. That middleware establishes a BroadcastChannel with a reproducible ID that allows other DB to communicate via the same channel.

Whenever there's a change to a DB (create/update/delete), that change is signaled to the channel. Other DB subscribe to channel events and apply the occurred change to their own instances.

Motivation

Multiple tabs of the same app should treat DB as a source of truth, meaning operations performed in one tab should be reflected when communicating with the DB in another tab. Since each JavaScript runtime will instantiate its own DB instance, the DB updates must be synchronized between clients.

Does this work in Node.js?

No. The synchronization layer is designed only for client-side usage. There is nothing to sync between the client and Node. That connection should be covered in the persistance layer.

Roadmap

  • Synchornize database operations between active tabs.
  • Persist and hydrate database state in sessionStorage.

Problem with manyOf relation

I had problem with the relation manyOf, in my example i had a user that can create many courses.
The GraphQL type of field "courses" in "user" model is declared String, instead of the model type of "course" so I will receive the following error:

Query1:

query Users {
  users {
	  username
	  email
	  courses 
  }
}

Error1:
{"errors":[{"message":"String cannot represent value: [{ id: \"c407a7bd-7930-43bc-8241-2aafd058ed39\", title: \"Quisquam debitis nostrum.\", subtitle: \"Sint ut quo magni perspiciatis esse qui omnis.\", type: \"course\" }]","locations":[{"line":6,"column":8}],"path":["users",0,"courses"]}],"data":{"users":[{"username":"Rozella","email":"[email protected]","courses":null}]}}

Query2:

query Users {
  users {
	  username
	  email
	  courses {
		  title
	  }
  }
}

{"errors":[{"message":"Field \"courses\" must not have a selection since type \"String\" has no subfields.","locations":[{"line":6,"column":16}]}]}

Definition of relations:

export const db = factory({
	user: {
		id: primaryKey(String),
		username: String,
		email: String,
		courses: manyOf("course")
	},
	course: {
		id: primaryKey(String),
		title: String,
		subtitle: String,
		type: String
	}
})

Support nested where clauses

It would be nice to support nested conjunctions/disjunctions (AND/OR) for creating complex where clause queries.

Example from the Prisma docs:

const users = await prisma.user.findMany({
  where: {
    OR: [
      {
        name: {
          startsWith: 'E',
        },
      },
      {
        AND: {
          profileViews: {
            gt: 0,
          },
          role: {
            equals: 'ADMIN',
          },
        },
      },
    ],
  },
})

Haven't looked into implementing this, but would probably require checking for a special property like AND or OR and handling the property accordingly.

Support querying through one-to-many relations

const db = factory({ user: { post: manyOf('post') } })

// Returns the list of users who have at least one "post"
// with a title matching one of the provided titles.
db.user.findMany({
  which: {
    post: {
      title: {
        in: ['First post', 'Second post']
      }
    }
  }
})

Expected behavior

// Given these users
db.user.create({
  id: 1,
  posts: [db.post.create({ title: 'First post' })]
})

db.user.create({
  id: 2,
  posts: [db.post.create({ title: 'Second post' }), db.post.create({ title: 'Third post' })]
})

db.user.create({
  id: 3,
  posts: [db.post.create({ title: 'Fourth post' })]
})

// Should return the following users:
[1, 2]

Current behavior

Failed to find a comparator for the value "[...]"

use pagination with toHandlers

Hello, thanks for great library.

I need to paginate my generated data with toHandlers.

this is my handler:
export const handlers = db.post.toHandlers('rest', 'https://my.backend/posts')

and this is my query:
fetch("https://my.backend/posts?take=5")

but it doesn't work! any idea?

Support model type generic for "factory"

It should be possible to approach data modeling type-first by reusing existing TypeScript interfaces to annotate the data. This is useful when there are types generated from the server (i.e. with GraphQL Codegen).

// generated-types.ts
export interface User {
  id: string
  firstName: string
  age: number
}
import { factory, primaryKey } from '@mswjs/data'
import { User } from './generated-types'

interface ModelTypes {
  user: User
}

const db = facotory<ModelTypes>({
  // ERROR: "user" is missing the "firstName" property.
  user: {
    id: primaryKey(String),
    age: String, // ERROR, "age" must be number.
  }
})

Find APIs should possibly return undefined

Hi, I am having multiple issues, the biggest of which currently is that the findMany and findFirst APIs do not allow for the possibility of returning undefined, even though they may do so. This causes TypeScript to not be able to type check this properly and results in run-time type errors.

CodeSandbox example:
https://codesandbox.io/s/mswjs-undefined-type-error-omifi?file=/src/App.tsx

import { factory, primaryKey } from "@mswjs/data";
import {
  EntityInstance,
  PrimaryKeyDeclaration
} from "@mswjs/data/lib/glossary";
import * as faker from "faker";

const db = factory({
  user: {
    Id: primaryKey(faker.datatype.uuid),
    firstName: () => "random data here"
  }
});

// Seed the database
db.user.create({
  Id: "ab4f631c-cca4-498f-a5aa-4828352a7c69",
  firstName: "Test"
});

// Some time later, query the database:
// Get a user (e.g., GET /user/:userId call)
const user = db.user.findFirst({
  where: {
    Id: {
      equals: "ab4f631c-cca4-498f-a5aa-4828352a7dawdawdc69"
    }
  }
});

// OK
console.log("This will NOT cause a run-time type error: ", user?.firstName);

// ERROR
console.log("This will cause a run-time type error: ", user.firstName);

I think these APIs should have the type like T | undefined (where T is the entity) rather than just returning T unconditionally.

Support defining models with factory functions

Use case: sometimes it's needed to generate field value based on another field value. This could be done if factory functions in model definitions were supported.

Example:

import { random, datatype } from 'faker'
import { factory, primaryKey } from '@mswjs/data'

const db = factory({
  model() {
    const fieldValue = `fieldValue-${datatype.uuid()}`
    const anotherFieldValue = `anotherFieldValue-${fieldValue}`
    return {
      id: primaryKey(datatype.uuid),
      field: () => fieldValue,
      anotherField: () => anotherFieldValue,
    }
  },
})

export { db }

Support querying REST API resources by query parameters

What

Support querying REST API resources by their properties through URL query parameters.

const db = factory({
  car: {
    id: primaryKey(String),
   manufacturer: String
  }
})

setupServer(...db.car.toHandlers('rest'))

fetch('/cars?manufacturer=ford')

Where ?manufacturer=ford is transformed to a where query against the "manufacturer" property of the car:

modelApi.findMany({ where: { manufacturer: { equals: 'ford } } })

Internally, query parameters can be respected like so:

// A very, very rough code.
rest.get(..., (req, res, ctx) => {
  const parameters = serialize(req.url.searchParams)
  const result = modelApi.findMany({ where: reduceParams(parameters) })
})

Restructure database to a dictionary

interface Database {
  [modelName: string]: {
    [entityId: string]: Entity
  }
}

This change also implies:

  • Change in the executeQuery to account for query.which.id to reference db[modelName]query.which.id.equals].
  • Supporting the primaryKey to customize which key is considered an identifier. Alternatively, always have id as an identifier.

"Warning: Possible EventEmitter memory leak detected" occured when define many models

First, thanks for this awesome library. I'm using this library in my project and it's very useful.

I have defined a number of models using factory(), but when it exceeds 11, a warning occurs in chrome.
Is there a way to prevent this?

Describe

If I define 11 or more models using factory(), the following warning will be displayed.

events.js:46 MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 delete listeners added.
Use emitter.setMaxListeners() to increase limit

This is displayed when you define the model, and even more when you call model.create().
(As for the event, only a warning is displayed, and the mock function works normally)

Example project

https://github.com/ma-hiro/sample-msw-data

When I launch the app and access http://localhost:3000, I get a lot of warnings.

This code is the cause of the warning.
If I try removing some models, the warning disappears.

It can be suppressed by setting the EventEmitter's maxListener, but the EventEmmiter in this library is probably cannot be referenced externally.
Therefore, we are tentatively dealing with the following.

import { EventEmitter } from 'events';
EventEmitter.defaultMaxListeners = 11; // Number of models you want to define or 0(infinity)

However, since this setting affects all EventEmitters, I would like to use setMaxListeners() to set it only for specific Emitters.

Support a "Boolean" model property

const db = factory({
  post: {
    isDraft: Boolean
  }
})

db.post.findMany({
  which: { isDraft: { equals: false } }
})

Query string:

interface BooleanQuery {
  equals: boolean
  notEquals: boolean
}

Data reset

When used for testing, we will want to make the tests independent, so some mechanism should exist for resetting all data to initial state (probably outside of @mswjs/data implementation, just some documented recipe).

I would imagine most people who need independent tests already use:

afterEach(() => server.resetHandlers())

How to hook into that?

Error when calling findFirst even though nothing has changed

First, thanks for the great library. This solves the exact problem I have been working on, and we're already using MSW so this is a natural fit.

I have been running into an issue where the relations seem to "disappear" from the database, even though nothing has changed? I created a CodeSandbox which consistently crashes:
https://codesandbox.io/s/mswjs-relation-undefined-error-wkcfp?file=/src/App.tsx

import { factory, oneOf, primaryKey } from "@mswjs/data";
import * as faker from "faker";

const db = factory({
  user: {
    id: primaryKey(faker.datatype.uuid),
    firstName: String
  },
  userObject: {
    id: primaryKey(faker.datatype.uuid),
    data: String,
    user: oneOf("user")
  }
});

// Seed the database
const seededUser = db.user.create({
  id: "ab4f631c-cca4-498f-a5aa-4828352a7c69",
  firstName: "Test"
});
db.userObject.create({
  user: seededUser,
  data: "test data 1 - associated with user"
});

const queryObject = () => {
  const object = db.userObject.findFirst({
    where: {
      user: {
        id: {
          equals: "ab4f631c-cca4-498f-a5aa-4828352a7c69"
        }
      }
    }
  });
  console.log("query:", { object, user: object.user });
};

// This will work OK, user is not undefined
queryObject();

// This will NOT work ok
setTimeout(() => {
  console.log("deferred query");
  queryObject();
}, 1000);

I make the same queryObject call twice: once immediately after creation, and another 1 second after seeding the database. My expectation is that both invocations of the function should do exactly the same thing. However, what happens is that the first call works as expected, while the second invocation crashes with the error TypeError: can't access property "__type", actualValue is undefined.

Willing to help out with this, let me know what questions you have. Thanks in advance.

(I apologize for all of the issues, but I am desperately trying to figure out this issue, and I have had no luck. I'd love to use this project.)

Consider renaming `which` to `where`

The property used to filter in mswjs/data is named which, citing being heavily inspired by Prisma. However, Prisma does not use which, it uses where:

https://www.prisma.io/docs/concepts/components/prisma-client/filtering-and-sorting

Is it possible that this is a mistake?

where is also the preferred word for other ORMs (presumably because the keyword is WHERE in SQL), such as:

Non-js:

Support seeding the database

import { factory, seed } from '@mswjs/data'

const db = factory(modles)

seed(db, {
  [modelName]: seedOptions
})

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.