diff --git a/README.md b/README.md
index e8e44d8..767fcb8 100644
--- a/README.md
+++ b/README.md
@@ -111,7 +111,7 @@ const data = {
preferred_format: 'hardback',
}
};
-const restServer = new FakeRest.FetchServer('http://localhost:3000');
+const restServer = new FakeRest.FetchServer({ baseUrl: 'http://localhost:3000' });
restServer.init(data);
fetchMock.mock('begin:http://localhost:3000', restServer.getHandler());
```
@@ -366,7 +366,7 @@ Operators are specified as suffixes on each filtered field. For instance, applyi
```js
// initialize a rest server with a custom base URL
-var restServer = new FakeRest.Server('http://my.custom.domain'); // // only URLs starting with my.custom.domain will be intercepted
+const restServer = new FakeRest.Server({ baseUrl: 'http://my.custom.domain' }); // only URLs starting with my.custom.domain will be intercepted
restServer.toggleLogging(); // logging is off by default, enable it to see network calls in the console
// Set all JSON data at once - only if identifier name is 'id'
restServer.init(json);
@@ -406,24 +406,45 @@ restServer.setDefaultQuery(function(resourceName) {
restServer.setBatchUrl('/batch');
// you can create more than one fake server to listen to several domains
-var restServer2 = new FakeRest.Server('http://my.other.domain');
+const restServer2 = new FakeRest.Server({ baseUrl: 'http://my.other.domain' });
// Set data collection by collection - allows to customize the identifier name
-var authorsCollection = new FakeRest.Collection([], '_id');
+const authorsCollection = new FakeRest.Collection({ items: [], identifierName: '_id' });
authorsCollection.addOne({ first_name: 'Leo', last_name: 'Tolstoi' }); // { _id: 0, first_name: 'Leo', last_name: 'Tolstoi' }
authorsCollection.addOne({ first_name: 'Jane', last_name: 'Austen' }); // { _id: 1, first_name: 'Jane', last_name: 'Austen' }
-// collections have autoincremented identifier but accept identifiers already set
+// collections have auto incremented identifiers by default but accept identifiers already set
authorsCollection.addOne({ _id: 3, first_name: 'Marcel', last_name: 'Proust' }); // { _id: 3, first_name: 'Marcel', last_name: 'Proust' }
restServer2.addCollection('authors', authorsCollection);
// collections are mutable
authorsCollection.updateOne(1, { last_name: 'Doe' }); // { _id: 1, first_name: 'Jane', last_name: 'Doe' }
authorsCollection.removeOne(3); // { _id: 3, first_name: 'Marcel', last_name: 'Proust' }
-var server = sinon.fakeServer.create();
+const server = sinon.fakeServer.create();
server.autoRespond = true;
server.respondWith(restServer.getHandler());
server.respondWith(restServer2.getHandler());
```
+## Configure Identifiers Generation
+
+By default, FakeRest uses an auto incremented sequence for the items identifiers. If you'd rather use another type of identifiers (e.g. UUIDs), you can provide your own `getNewId` function at the server level:
+
+```js
+import FakeRest from 'fakerest';
+import uuid from 'uuid';
+
+const restServer = new FakeRest.Server({ baseUrl: 'http://my.custom.domain', getNewId: () => uuid.v5() });
+```
+
+This can also be specified at the collection level:
+
+```js
+import FakeRest from 'fakerest';
+import uuid from 'uuid';
+
+const restServer = new FakeRest.Server({ baseUrl: 'http://my.custom.domain' });
+const authorsCollection = new FakeRest.Collection({ items: [], identifierName: '_id', getNewId: () => uuid.v5() });
+```
+
## Development
```sh
diff --git a/UPGRADE.md b/UPGRADE.md
new file mode 100644
index 0000000..923dc5b
--- /dev/null
+++ b/UPGRADE.md
@@ -0,0 +1,42 @@
+# Upgrading to 4.0.0
+
+## Constructors Of `FetchServer` and `Server` Take An Object
+
+For `Server`:
+
+```diff
+import { Server } from 'fakerest';
+import { data } from './data';
+
+-const server = new Server('http://myapi.com');
++const server = new Server({ baseUrl: 'http://myapi.com' });
+server.init(data);
+```
+
+For `FetchServer`:
+
+```diff
+import { FetchServer } from 'fakerest';
+import { data } from './data';
+
+-const server = new FetchServer('http://myapi.com');
++const server = new FetchServer({ baseUrl: 'http://myapi.com' });
+server.init(data);
+```
+
+## Constructor Of `Collection` Takes An Object
+
+```diff
+-const posts = new Collection([
+- { id: 1, title: 'baz' },
+- { id: 2, title: 'biz' },
+- { id: 3, title: 'boz' },
+-]);
++const posts = new Collection({
++ items: [
++ { id: 1, title: 'baz' },
++ { id: 2, title: 'biz' },
++ { id: 3, title: 'boz' },
++ ],
++});
+```
\ No newline at end of file
diff --git a/example/App.tsx b/example/App.tsx
index 0b0ed42..961b4ea 100644
--- a/example/App.tsx
+++ b/example/App.tsx
@@ -1,12 +1,41 @@
import React from 'react';
-import { Admin, ListGuesser, Resource } from 'react-admin';
+import {
+ Admin,
+ Create,
+ EditGuesser,
+ ListGuesser,
+ Resource,
+ ShowGuesser,
+} from 'react-admin';
import { dataProvider } from './dataProvider';
export const App = () => {
return (
-
-
+
+
);
};
+
+import { Edit, ReferenceInput, SimpleForm, TextInput } from 'react-admin';
+
+export const BookCreate = () => (
+
+
+
+
+
+
+);
diff --git a/example/fetchMock.ts b/example/fetchMock.ts
index c6e7e7d..aaffc9e 100644
--- a/example/fetchMock.ts
+++ b/example/fetchMock.ts
@@ -3,7 +3,9 @@ import FakeRest from 'fakerest';
import { data } from './data';
export const initializeFetchMock = () => {
- const restServer = new FakeRest.FetchServer('http://localhost:3000');
+ const restServer = new FakeRest.FetchServer({
+ baseUrl: 'http://localhost:3000',
+ });
if (window) {
// @ts-ignore
window.restServer = restServer; // give way to update data in the console
diff --git a/src/BaseServer.ts b/src/BaseServer.ts
index 72550d7..d743709 100644
--- a/src/BaseServer.ts
+++ b/src/BaseServer.ts
@@ -9,9 +9,17 @@ export class BaseServer {
batchUrl: string | null = null;
collections: Record> = {};
singles: Record> = {};
+ getNewId?: () => number | string;
- constructor(baseUrl = '') {
+ constructor({
+ baseUrl = '',
+ getNewId,
+ }: {
+ baseUrl?: string;
+ getNewId?: () => number | string;
+ } = {}) {
this.baseUrl = baseUrl;
+ this.getNewId = getNewId;
}
/**
@@ -21,7 +29,14 @@ export class BaseServer {
for (const name in data) {
const value = data[name];
if (Array.isArray(value)) {
- this.addCollection(name, new Collection(value, 'id'));
+ this.addCollection(
+ name,
+ new Collection({
+ items: value,
+ identifierName: 'id',
+ getNewId: this.getNewId,
+ }),
+ );
} else {
this.addSingle(name, new Single(value));
}
@@ -103,7 +118,7 @@ export class BaseServer {
return this.collections[name].getAll(params);
}
- getOne(name: string, identifier: number, params?: Query) {
+ getOne(name: string, identifier: string | number, params?: Query) {
return this.collections[name].getOne(identifier, params);
}
@@ -111,17 +126,21 @@ export class BaseServer {
if (!Object.prototype.hasOwnProperty.call(this.collections, name)) {
this.addCollection(
name,
- new Collection([] as CollectionItem[], 'id'),
+ new Collection({
+ items: [],
+ identifierName: 'id',
+ getNewId: this.getNewId,
+ }),
);
}
return this.collections[name].addOne(item);
}
- updateOne(name: string, identifier: number, item: CollectionItem) {
+ updateOne(name: string, identifier: string | number, item: CollectionItem) {
return this.collections[name].updateOne(identifier, item);
}
- removeOne(name: string, identifier: number) {
+ removeOne(name: string, identifier: string | number) {
return this.collections[name].removeOne(identifier);
}
@@ -205,7 +224,7 @@ export class BaseServer {
// handle collections
const matches = request.url?.match(
- new RegExp(`^${this.baseUrl}\\/([^\\/?]+)(\\/(\\d+))?(\\?.*)?$`),
+ new RegExp(`^${this.baseUrl}\\/([^\\/?]+)(\\/(\\w))?(\\?.*)?$`),
);
if (!matches) {
return { status: 404, headers: {} };
diff --git a/src/Collection.spec.ts b/src/Collection.spec.ts
index a7333a7..6acee29 100644
--- a/src/Collection.spec.ts
+++ b/src/Collection.spec.ts
@@ -5,10 +5,12 @@ import type { CollectionItem } from './types.js';
describe('Collection', () => {
describe('constructor', () => {
it('should set the initial set of data', () => {
- const collection = new Collection([
- { id: 1, name: 'foo' },
- { id: 2, name: 'bar' },
- ]);
+ const collection = new Collection({
+ items: [
+ { id: 1, name: 'foo' },
+ { id: 2, name: 'bar' },
+ ],
+ });
expect(collection.getAll()).toEqual([
{ id: 1, name: 'foo' },
{ id: 2, name: 'bar' },
@@ -27,21 +29,19 @@ describe('Collection', () => {
});
it('should return the collection size', () => {
- expect(new Collection([{}, {}]).getCount()).toEqual(2);
+ expect(new Collection({ items: [{}, {}] }).getCount()).toEqual(2);
});
it('should return the correct collection size, even when items were removed', () => {
- const collection = new Collection([{}, {}, {}]);
+ const collection = new Collection({ items: [{}, {}, {}] });
collection.removeOne(1);
expect(collection.getCount()).toEqual(2);
});
it('should accept a query object', () => {
- const collection = new Collection([
- {},
- { name: 'a' },
- { name: 'b' },
- ]);
+ const collection = new Collection({
+ items: [{}, { name: 'a' }, { name: 'b' }],
+ });
function filter(item: CollectionItem) {
return item.name === 'a' || item.name === 'b';
}
@@ -55,10 +55,12 @@ describe('Collection', () => {
});
it('should return all collections', () => {
- const collection = new Collection([
- { id: 1, name: 'foo' },
- { id: 2, name: 'bar' },
- ]);
+ const collection = new Collection({
+ items: [
+ { id: 1, name: 'foo' },
+ { id: 2, name: 'bar' },
+ ],
+ });
expect(collection.getAll()).toEqual([
{ id: 1, name: 'foo' },
{ id: 2, name: 'bar' },
@@ -75,11 +77,9 @@ describe('Collection', () => {
});
it('should sort by sort function', () => {
- const collection = new Collection([
- { name: 'c' },
- { name: 'a' },
- { name: 'b' },
- ]);
+ const collection = new Collection({
+ items: [{ name: 'c' }, { name: 'a' }, { name: 'b' }],
+ });
const expected = [
{ name: 'a', id: 1 },
{ name: 'b', id: 2 },
@@ -99,11 +99,9 @@ describe('Collection', () => {
});
it('should sort by sort name', () => {
- const collection = new Collection([
- { name: 'c' },
- { name: 'a' },
- { name: 'b' },
- ]);
+ const collection = new Collection({
+ items: [{ name: 'c' }, { name: 'a' }, { name: 'b' }],
+ });
const expected = [
{ name: 'a', id: 1 },
{ name: 'b', id: 2 },
@@ -113,11 +111,9 @@ describe('Collection', () => {
});
it('should sort by sort name and direction', () => {
- const collection = new Collection([
- { name: 'c' },
- { name: 'a' },
- { name: 'b' },
- ]);
+ const collection = new Collection({
+ items: [{ name: 'c' }, { name: 'a' }, { name: 'b' }],
+ });
let expected: CollectionItem[];
expected = [
{ name: 'a', id: 1 },
@@ -138,11 +134,9 @@ describe('Collection', () => {
});
it('should not affect further requests', () => {
- const collection = new Collection([
- { name: 'c' },
- { name: 'a' },
- { name: 'b' },
- ]);
+ const collection = new Collection({
+ items: [{ name: 'c' }, { name: 'a' }, { name: 'b' }],
+ });
collection.getAll({ sort: 'name' });
const expected = [
{ name: 'c', id: 0 },
@@ -163,11 +157,9 @@ describe('Collection', () => {
});
it('should filter by filter function', () => {
- const collection = new Collection([
- { name: 'c' },
- { name: 'a' },
- { name: 'b' },
- ]);
+ const collection = new Collection({
+ items: [{ name: 'c' }, { name: 'a' }, { name: 'b' }],
+ });
const expected = [
{ name: 'c', id: 0 },
{ name: 'b', id: 2 },
@@ -179,11 +171,9 @@ describe('Collection', () => {
});
it('should filter by filter object', () => {
- const collection = new Collection([
- { name: 'c' },
- { name: 'a' },
- { name: 'b' },
- ]);
+ const collection = new Collection({
+ items: [{ name: 'c' }, { name: 'a' }, { name: 'b' }],
+ });
const expected = [{ name: 'b', id: 2 }];
expect(collection.getAll({ filter: { name: 'b' } })).toEqual(
expected,
@@ -191,11 +181,13 @@ describe('Collection', () => {
});
it('should filter values with deep paths', () => {
- const collection = new Collection([
- { name: 'c', deep: { value: 'c' } },
- { name: 'a', deep: { value: 'a' } },
- { name: 'b', deep: { value: 'b' } },
- ]);
+ const collection = new Collection({
+ items: [
+ { name: 'c', deep: { value: 'c' } },
+ { name: 'a', deep: { value: 'a' } },
+ { name: 'b', deep: { value: 'b' } },
+ ],
+ });
const expected = [{ name: 'b', deep: { value: 'b' }, id: 2 }];
expect(
collection.getAll({ filter: { 'deep.value': 'b' } }),
@@ -203,11 +195,13 @@ describe('Collection', () => {
});
it('should filter values with objects', () => {
- const collection = new Collection([
- { name: 'c', deep: { value: 'c' } },
- { name: 'a', deep: { value: 'a' } },
- { name: 'b', deep: { value: 'b' } },
- ]);
+ const collection = new Collection({
+ items: [
+ { name: 'c', deep: { value: 'c' } },
+ { name: 'a', deep: { value: 'a' } },
+ { name: 'b', deep: { value: 'b' } },
+ ],
+ });
const expected = [{ name: 'b', deep: { value: 'b' }, id: 2 }];
expect(
collection.getAll({ filter: { deep: { value: 'b' } } }),
@@ -215,11 +209,13 @@ describe('Collection', () => {
});
it('should filter boolean values properly', () => {
- const collection = new Collection([
- { name: 'a', is: true },
- { name: 'b', is: false },
- { name: 'c', is: true },
- ]);
+ const collection = new Collection({
+ items: [
+ { name: 'a', is: true },
+ { name: 'b', is: false },
+ { name: 'c', is: true },
+ ],
+ });
const expectedFalse = [{ name: 'b', id: 1, is: false }];
const expectedTrue = [
{ name: 'a', id: 0, is: true },
@@ -240,11 +236,13 @@ describe('Collection', () => {
});
it('should filter array values properly', () => {
- const collection = new Collection([
- { tags: ['a', 'b', 'c'] },
- { tags: ['b', 'c', 'd'] },
- { tags: ['c', 'd', 'e'] },
- ]);
+ const collection = new Collection({
+ items: [
+ { tags: ['a', 'b', 'c'] },
+ { tags: ['b', 'c', 'd'] },
+ { tags: ['c', 'd', 'e'] },
+ ],
+ });
const expected = [
{ id: 0, tags: ['a', 'b', 'c'] },
{ id: 1, tags: ['b', 'c', 'd'] },
@@ -258,11 +256,13 @@ describe('Collection', () => {
});
it('should filter array values properly within deep paths', () => {
- const collection = new Collection([
- { deep: { tags: ['a', 'b', 'c'] } },
- { deep: { tags: ['b', 'c', 'd'] } },
- { deep: { tags: ['c', 'd', 'e'] } },
- ]);
+ const collection = new Collection({
+ items: [
+ { deep: { tags: ['a', 'b', 'c'] } },
+ { deep: { tags: ['b', 'c', 'd'] } },
+ { deep: { tags: ['c', 'd', 'e'] } },
+ ],
+ });
const expected = [
{ id: 0, deep: { tags: ['a', 'b', 'c'] } },
{ id: 1, deep: { tags: ['b', 'c', 'd'] } },
@@ -276,11 +276,13 @@ describe('Collection', () => {
});
it('should filter array values properly inside deep paths', () => {
- const collection = new Collection([
- { tags: { deep: ['a', 'b', 'c'] } },
- { tags: { deep: ['b', 'c', 'd'] } },
- { tags: { deep: ['c', 'd', 'e'] } },
- ]);
+ const collection = new Collection({
+ items: [
+ { tags: { deep: ['a', 'b', 'c'] } },
+ { tags: { deep: ['b', 'c', 'd'] } },
+ { tags: { deep: ['c', 'd', 'e'] } },
+ ],
+ });
const expected = [
{ id: 0, tags: { deep: ['a', 'b', 'c'] } },
{ id: 1, tags: { deep: ['b', 'c', 'd'] } },
@@ -294,11 +296,13 @@ describe('Collection', () => {
});
it('should filter array values properly with deep paths', () => {
- const collection = new Collection([
- { tags: [{ name: 'a' }, { name: 'b' }, { name: 'c' }] },
- { tags: [{ name: 'b' }, { name: 'c' }, { name: 'd' }] },
- { tags: [{ name: 'c' }, { name: 'd' }, { name: 'e' }] },
- ]);
+ const collection = new Collection({
+ items: [
+ { tags: [{ name: 'a' }, { name: 'b' }, { name: 'c' }] },
+ { tags: [{ name: 'b' }, { name: 'c' }, { name: 'd' }] },
+ { tags: [{ name: 'c' }, { name: 'd' }, { name: 'e' }] },
+ ],
+ });
const expected = [
{
id: 0,
@@ -318,11 +322,13 @@ describe('Collection', () => {
});
it('should filter array values properly when receiving several values within deep paths', () => {
- const collection = new Collection([
- { deep: { tags: ['a', 'b', 'c'] } },
- { deep: { tags: ['b', 'c', 'd'] } },
- { deep: { tags: ['c', 'd', 'e'] } },
- ]);
+ const collection = new Collection({
+ items: [
+ { deep: { tags: ['a', 'b', 'c'] } },
+ { deep: { tags: ['b', 'c', 'd'] } },
+ { deep: { tags: ['c', 'd', 'e'] } },
+ ],
+ });
const expected = [{ id: 1, deep: { tags: ['b', 'c', 'd'] } }];
expect(
collection.getAll({ filter: { 'deep.tags': ['b', 'd'] } }),
@@ -335,11 +341,13 @@ describe('Collection', () => {
});
it('should filter array values properly when receiving several values with deep paths', () => {
- const collection = new Collection([
- { tags: [{ name: 'a' }, { name: 'b' }, { name: 'c' }] },
- { tags: [{ name: 'c' }, { name: 'd' }, { name: 'e' }] },
- { tags: [{ name: 'e' }, { name: 'f' }, { name: 'g' }] },
- ]);
+ const collection = new Collection({
+ items: [
+ { tags: [{ name: 'a' }, { name: 'b' }, { name: 'c' }] },
+ { tags: [{ name: 'c' }, { name: 'd' }, { name: 'e' }] },
+ { tags: [{ name: 'e' }, { name: 'f' }, { name: 'g' }] },
+ ],
+ });
const expected = [
{
id: 0,
@@ -359,11 +367,13 @@ describe('Collection', () => {
});
it('should filter array values properly when receiving several values', () => {
- const collection = new Collection([
- { tags: ['a', 'b', 'c'] },
- { tags: ['b', 'c', 'd'] },
- { tags: ['c', 'd', 'e'] },
- ]);
+ const collection = new Collection({
+ items: [
+ { tags: ['a', 'b', 'c'] },
+ { tags: ['b', 'c', 'd'] },
+ { tags: ['c', 'd', 'e'] },
+ ],
+ });
const expected = [{ id: 1, tags: ['b', 'c', 'd'] }];
expect(
collection.getAll({ filter: { tags: ['b', 'd'] } }),
@@ -374,15 +384,17 @@ describe('Collection', () => {
});
it('should filter by the special q full-text filter', () => {
- const collection = new Collection([
- { a: 'Hello', b: 'world' },
- { a: 'helloworld', b: 'bunny' },
- { a: 'foo', b: 'bar' },
- { a: { b: 'bar' } },
- { a: '', b: '' },
- { a: null, b: null },
- {},
- ]);
+ const collection = new Collection({
+ items: [
+ { a: 'Hello', b: 'world' },
+ { a: 'helloworld', b: 'bunny' },
+ { a: 'foo', b: 'bar' },
+ { a: { b: 'bar' } },
+ { a: '', b: '' },
+ { a: null, b: null },
+ {},
+ ],
+ });
expect(collection.getAll({ filter: { q: 'hello' } })).toEqual([
{ id: 0, a: 'Hello', b: 'world' },
{ id: 1, a: 'helloworld', b: 'bunny' },
@@ -394,11 +406,9 @@ describe('Collection', () => {
});
it('should filter by range using _gte, _gt, _lte, and _lt', () => {
- const collection = new Collection([
- { v: 1 },
- { v: 2 },
- { v: 3 },
- ]);
+ const collection = new Collection({
+ items: [{ v: 1 }, { v: 2 }, { v: 3 }],
+ });
expect(collection.getAll({ filter: { v_gte: 2 } })).toEqual([
{ v: 2, id: 1 },
{ v: 3, id: 2 },
@@ -418,11 +428,9 @@ describe('Collection', () => {
});
it('should filter by inequality using _neq', () => {
- const collection = new Collection([
- { v: 1 },
- { v: 2 },
- { v: 3 },
- ]);
+ const collection = new Collection({
+ items: [{ v: 1 }, { v: 2 }, { v: 3 }],
+ });
expect(collection.getAll({ filter: { v_neq: 2 } })).toEqual([
{ v: 1, id: 0 },
{ v: 3, id: 2 },
@@ -430,22 +438,18 @@ describe('Collection', () => {
});
it('should filter by equality using _eq', () => {
- const collection = new Collection([
- { v: 1 },
- { v: 2 },
- { v: 3 },
- ]);
+ const collection = new Collection({
+ items: [{ v: 1 }, { v: 2 }, { v: 3 }],
+ });
expect(collection.getAll({ filter: { v_eq: 2 } })).toEqual([
{ v: 2, id: 1 },
]);
});
it('should filter using _eq_any', () => {
- const collection = new Collection([
- { v: 1 },
- { v: 2 },
- { v: 3 },
- ]);
+ const collection = new Collection({
+ items: [{ v: 1 }, { v: 2 }, { v: 3 }],
+ });
expect(
collection.getAll({ filter: { v_eq_any: [1, 3] } }),
).toEqual([
@@ -455,22 +459,18 @@ describe('Collection', () => {
});
it('should filter using _neq_any', () => {
- const collection = new Collection([
- { v: 1 },
- { v: 2 },
- { v: 3 },
- ]);
+ const collection = new Collection({
+ items: [{ v: 1 }, { v: 2 }, { v: 3 }],
+ });
expect(
collection.getAll({ filter: { v_neq_any: [1, 3] } }),
).toEqual([{ v: 2, id: 1 }]);
});
it('should filter using _inc_any', () => {
- const collection = new Collection([
- { v: [1, 2] },
- { v: [2, 4] },
- { v: [3, 1] },
- ]);
+ const collection = new Collection({
+ items: [{ v: [1, 2] }, { v: [2, 4] }, { v: [3, 1] }],
+ });
expect(
collection.getAll({ filter: { v_inc_any: [1, 3] } }),
).toEqual([
@@ -480,33 +480,27 @@ describe('Collection', () => {
});
it('should filter using _ninc_any', () => {
- const collection = new Collection([
- { v: [1, 2] },
- { v: [2, 4] },
- { v: [3, 1] },
- ]);
+ const collection = new Collection({
+ items: [{ v: [1, 2] }, { v: [2, 4] }, { v: [3, 1] }],
+ });
expect(
collection.getAll({ filter: { v_ninc_any: [1, 3] } }),
).toEqual([{ v: [2, 4], id: 1 }]);
});
it('should filter using _inc', () => {
- const collection = new Collection([
- { v: [1, 2] },
- { v: [2, 4] },
- { v: [3, 1] },
- ]);
+ const collection = new Collection({
+ items: [{ v: [1, 2] }, { v: [2, 4] }, { v: [3, 1] }],
+ });
expect(
collection.getAll({ filter: { v_inc: [1, 3] } }),
).toEqual([{ v: [3, 1], id: 2 }]);
});
it('should filter by text search using _q', () => {
- const collection = new Collection([
- { v: 'abCd' },
- { v: 'cDef' },
- { v: 'EFgh' },
- ]);
+ const collection = new Collection({
+ items: [{ v: 'abCd' }, { v: 'cDef' }, { v: 'EFgh' }],
+ });
expect(collection.getAll({ filter: { v_q: 'cd' } })).toEqual([
{ id: 0, v: 'abCd' },
{ id: 1, v: 'cDef' },
@@ -518,13 +512,15 @@ describe('Collection', () => {
});
it('should filter by array', () => {
- const collection = new Collection([
- { a: 'H' },
- { a: 'e' },
- { a: 'l' },
- { a: 'l' },
- { a: 'o' },
- ]);
+ const collection = new Collection({
+ items: [
+ { a: 'H' },
+ { a: 'e' },
+ { a: 'l' },
+ { a: 'l' },
+ { a: 'o' },
+ ],
+ });
expect(collection.getAll({ filter: { id: [] } })).toEqual([]);
expect(
collection.getAll({ filter: { id: [1, 2, 3] } }),
@@ -543,22 +539,18 @@ describe('Collection', () => {
});
it('should combine all filters with an AND logic', () => {
- const collection = new Collection([
- { v: 1 },
- { v: 2 },
- { v: 3 },
- ]);
+ const collection = new Collection({
+ items: [{ v: 1 }, { v: 2 }, { v: 3 }],
+ });
expect(
collection.getAll({ filter: { v_gte: 2, v_lte: 2 } }),
).toEqual([{ v: 2, id: 1 }]);
});
it('should not affect further requests', () => {
- const collection = new Collection([
- { name: 'c' },
- { name: 'a' },
- { name: 'b' },
- ]);
+ const collection = new Collection({
+ items: [{ name: 'c' }, { name: 'a' }, { name: 'b' }],
+ });
function filter(item: CollectionItem) {
return item.name !== 'a';
}
@@ -581,13 +573,15 @@ describe('Collection', () => {
}).toThrow(new Error('Unsupported range type'));
});
- const collection = new Collection([
- { id: 0, name: 'a' },
- { id: 1, name: 'b' },
- { id: 2, name: 'c' },
- { id: 3, name: 'd' },
- { id: 4, name: 'e' },
- ]);
+ const collection = new Collection({
+ items: [
+ { id: 0, name: 'a' },
+ { id: 1, name: 'b' },
+ { id: 2, name: 'c' },
+ { id: 3, name: 'd' },
+ { id: 4, name: 'e' },
+ ],
+ });
it('should return a range in the collection', () => {
let expected: CollectionItem[];
@@ -611,11 +605,13 @@ describe('Collection', () => {
});
it('should not affect further requests', () => {
- const collection = new Collection([
- { id: 0, name: 'a' },
- { id: 1, name: 'b' },
- { id: 2, name: 'c' },
- ]);
+ const collection = new Collection({
+ items: [
+ { id: 0, name: 'a' },
+ { id: 1, name: 'b' },
+ { id: 2, name: 'c' },
+ ],
+ });
collection.getAll({ range: [1] });
const expected = [
{ id: 0, name: 'a' },
@@ -628,7 +624,9 @@ describe('Collection', () => {
describe('embed query', () => {
it('should throw an error when trying to embed a non-existing collection', () => {
- const foos = new Collection([{ name: 'John', bar_id: 123 }]);
+ const foos = new Collection({
+ items: [{ name: 'John', bar_id: 123 }],
+ });
const server = new Server();
server.addCollection('foos', foos);
expect(() => {
@@ -639,8 +637,10 @@ describe('Collection', () => {
});
it('should return the original object for missing embed one', () => {
- const foos = new Collection([{ name: 'John', bar_id: 123 }]);
- const bars = new Collection([]);
+ const foos = new Collection({
+ items: [{ name: 'John', bar_id: 123 }],
+ });
+ const bars = new Collection({ items: [] });
const server = new Server();
server.addCollection('foos', foos);
server.addCollection('bars', bars);
@@ -649,15 +649,19 @@ describe('Collection', () => {
});
it('should return the object with the reference object for embed one', () => {
- const foos = new Collection([
- { name: 'John', bar_id: 123 },
- { name: 'Jane', bar_id: 456 },
- ]);
- const bars = new Collection([
- { id: 1, bar: 'nobody wants me' },
- { id: 123, bar: 'baz' },
- { id: 456, bar: 'bazz' },
- ]);
+ const foos = new Collection({
+ items: [
+ { name: 'John', bar_id: 123 },
+ { name: 'Jane', bar_id: 456 },
+ ],
+ });
+ const bars = new Collection({
+ items: [
+ { id: 1, bar: 'nobody wants me' },
+ { id: 123, bar: 'baz' },
+ { id: 456, bar: 'bazz' },
+ ],
+ });
const server = new Server();
server.addCollection('foos', foos);
server.addCollection('bars', bars);
@@ -679,7 +683,9 @@ describe('Collection', () => {
});
it('should throw an error when trying to embed many a non-existing collection', () => {
- const foos = new Collection([{ name: 'John', bar_id: 123 }]);
+ const foos = new Collection({
+ items: [{ name: 'John', bar_id: 123 }],
+ });
const server = new Server();
server.addCollection('foos', foos);
expect(() => {
@@ -690,10 +696,12 @@ describe('Collection', () => {
});
it('should return the object with an empty array for missing embed many', () => {
- const foos = new Collection([{ name: 'John', bar_id: 123 }]);
- const bars = new Collection([
- { id: 1, bar: 'nobody wants me' },
- ]);
+ const foos = new Collection({
+ items: [{ name: 'John', bar_id: 123 }],
+ });
+ const bars = new Collection({
+ items: [{ id: 1, bar: 'nobody wants me' }],
+ });
const server = new Server();
server.addCollection('foos', foos);
server.addCollection('bars', bars);
@@ -702,16 +710,20 @@ describe('Collection', () => {
});
it('should return the object with an array of references for embed many', () => {
- const foos = new Collection([
- { id: 1, name: 'John', bar_id: 123 },
- { id: 2, name: 'Jane', bar_id: 456 },
- { id: 3, name: 'Jules', bar_id: 456 },
- ]);
- const bars = new Collection([
- { id: 1, bar: 'nobody wants me' },
- { id: 123, bar: 'baz' },
- { id: 456, bar: 'bazz' },
- ]);
+ const foos = new Collection({
+ items: [
+ { id: 1, name: 'John', bar_id: 123 },
+ { id: 2, name: 'Jane', bar_id: 456 },
+ { id: 3, name: 'Jules', bar_id: 456 },
+ ],
+ });
+ const bars = new Collection({
+ items: [
+ { id: 1, bar: 'nobody wants me' },
+ { id: 123, bar: 'baz' },
+ { id: 456, bar: 'bazz' },
+ ],
+ });
const server = new Server();
server.addCollection('foos', foos);
server.addCollection('bars', bars);
@@ -735,16 +747,20 @@ describe('Collection', () => {
});
it('should return the object with an array of references for embed many using inner array', () => {
- const foos = new Collection([
- { id: 1, name: 'John' },
- { id: 2, name: 'Jane' },
- { id: 3, name: 'Jules' },
- ]);
- const bars = new Collection([
- { id: 1, bar: 'nobody wants me' },
- { id: 123, bar: 'baz', foos: [1] },
- { id: 456, bar: 'bazz', foos: [2, 3] },
- ]);
+ const foos = new Collection({
+ items: [
+ { id: 1, name: 'John' },
+ { id: 2, name: 'Jane' },
+ { id: 3, name: 'Jules' },
+ ],
+ });
+ const bars = new Collection({
+ items: [
+ { id: 1, bar: 'nobody wants me' },
+ { id: 123, bar: 'baz', foos: [1] },
+ { id: 456, bar: 'bazz', foos: [2, 3] },
+ ],
+ });
const server = new Server();
server.addCollection('foos', foos);
server.addCollection('bars', bars);
@@ -764,29 +780,35 @@ describe('Collection', () => {
});
it('should allow multiple embeds', () => {
- const books = new Collection([
- { id: 1, title: 'Pride and Prejudice', author_id: 1 },
- { id: 2, title: 'Sense and Sensibility', author_id: 1 },
- { id: 3, title: 'War and Preace', author_id: 2 },
- ]);
- const authors = new Collection([
- {
- id: 1,
- firstName: 'Jane',
- lastName: 'Austen',
- country_id: 1,
- },
- {
- id: 2,
- firstName: 'Leo',
- lastName: 'Tosltoi',
- country_id: 2,
- },
- ]);
- const countries = new Collection([
- { id: 1, name: 'England' },
- { id: 2, name: 'Russia' },
- ]);
+ const books = new Collection({
+ items: [
+ { id: 1, title: 'Pride and Prejudice', author_id: 1 },
+ { id: 2, title: 'Sense and Sensibility', author_id: 1 },
+ { id: 3, title: 'War and Preace', author_id: 2 },
+ ],
+ });
+ const authors = new Collection({
+ items: [
+ {
+ id: 1,
+ firstName: 'Jane',
+ lastName: 'Austen',
+ country_id: 1,
+ },
+ {
+ id: 2,
+ firstName: 'Leo',
+ lastName: 'Tosltoi',
+ country_id: 2,
+ },
+ ],
+ });
+ const countries = new Collection({
+ items: [
+ { id: 1, name: 'England' },
+ { id: 2, name: 'Russia' },
+ ],
+ });
const server = new Server();
server.addCollection('books', books);
server.addCollection('authors', authors);
@@ -830,11 +852,13 @@ describe('Collection', () => {
describe('composite query', () => {
it('should execute all commands of the query object', () => {
- const collection = new Collection([
- { id: 0, name: 'c', arg: false },
- { id: 1, name: 'b', arg: true },
- { id: 2, name: 'a', arg: true },
- ]);
+ const collection = new Collection({
+ items: [
+ { id: 0, name: 'c', arg: false },
+ { id: 1, name: 'b', arg: true },
+ { id: 2, name: 'a', arg: true },
+ ],
+ });
const query = {
filter: { arg: true },
sort: 'name',
@@ -857,22 +881,24 @@ describe('Collection', () => {
});
it('should return the first collection matching the identifier', () => {
- const collection = new Collection([
- { id: 1, name: 'foo' },
- { id: 2, name: 'bar' },
- ]);
+ const collection = new Collection({
+ items: [
+ { id: 1, name: 'foo' },
+ { id: 2, name: 'bar' },
+ ],
+ });
expect(collection.getOne(1)).toEqual({ id: 1, name: 'foo' });
expect(collection.getOne(2)).toEqual({ id: 2, name: 'bar' });
});
it('should use the identifierName', () => {
- const collection = new Collection(
- [
+ const collection = new Collection({
+ items: [
{ _id: 1, name: 'foo' },
{ _id: 2, name: 'bar' },
],
- '_id',
- );
+ identifierName: '_id',
+ });
expect(collection.getOne(1)).toEqual({ _id: 1, name: 'foo' });
expect(collection.getOne(2)).toEqual({ _id: 2, name: 'bar' });
});
@@ -909,9 +935,9 @@ describe('Collection', () => {
});
it('should refuse insertion with existing identifier', () => {
- const collection = new Collection([
- { name: 'foo' },
- ]);
+ const collection = new Collection({
+ items: [{ name: 'foo' }],
+ });
expect(() => {
collection.addOne({ id: 0, name: 'bar' });
}).toThrow(
@@ -938,9 +964,9 @@ describe('Collection', () => {
});
it('should return the updated item', () => {
- const collection = new Collection([
- { name: 'foo' },
- ]);
+ const collection = new Collection({
+ items: [{ name: 'foo' }],
+ });
expect(collection.updateOne(0, { id: 0, name: 'bar' })).toEqual({
id: 0,
name: 'bar',
@@ -948,10 +974,9 @@ describe('Collection', () => {
});
it('should update the item', () => {
- const collection = new Collection([
- { name: 'foo' },
- { name: 'baz' },
- ]);
+ const collection = new Collection({
+ items: [{ name: 'foo' }, { name: 'baz' }],
+ });
collection.updateOne(0, { id: 0, name: 'bar' });
expect(collection.getOne(0)).toEqual({ id: 0, name: 'bar' });
expect(collection.getOne(1)).toEqual({ id: 1, name: 'baz' });
@@ -981,11 +1006,9 @@ describe('Collection', () => {
});
it('should decrement the sequence only if the removed item is the last', () => {
- const collection = new Collection([
- { id: 0 },
- { id: 1 },
- { id: 2 },
- ]);
+ const collection = new Collection({
+ items: [{ id: 0 }, { id: 1 }, { id: 2 }],
+ });
expect(collection.sequence).toEqual(3);
collection.removeOne(2);
expect(collection.sequence).toEqual(2);
@@ -995,4 +1018,57 @@ describe('Collection', () => {
expect(r.id).toEqual(2);
});
});
+
+ describe('custom identifier generation', () => {
+ test('should use the custom identifier provided at initialization', () => {
+ const collection = new Collection({
+ items: [
+ {
+ id: '6090eb22-e140-4720-b7b2-e1416a3d2447',
+ name: 'foo',
+ },
+ {
+ id: 'fb1c2ce1-5df7-4af8-be1c-7af234b67f7d',
+ name: 'baz',
+ },
+ ],
+ identifierName: 'id',
+ });
+
+ expect(
+ collection.getOne('6090eb22-e140-4720-b7b2-e1416a3d2447'),
+ ).toEqual({
+ id: '6090eb22-e140-4720-b7b2-e1416a3d2447',
+ name: 'foo',
+ });
+ });
+
+ test('should use the custom identifier provided at insertion', () => {
+ const collection = new Collection({
+ items: [],
+ identifierName: 'id',
+ });
+
+ const item = collection.addOne({
+ id: '6090eb22-e140-4720-b7b2-e1416a3d2447',
+ name: 'foo',
+ });
+
+ expect(item.id).toEqual('6090eb22-e140-4720-b7b2-e1416a3d2447');
+ });
+
+ test('should use the custom identifier generation function at insertion', () => {
+ const collection = new Collection({
+ items: [],
+ identifierName: 'id',
+ getNewId: () => '6090eb22-e140-4720-b7b2-e1416a3d2447',
+ });
+
+ const item = collection.addOne({
+ name: 'foo',
+ });
+
+ expect(item.id).toEqual('6090eb22-e140-4720-b7b2-e1416a3d2447');
+ });
+ });
});
diff --git a/src/Collection.ts b/src/Collection.ts
index 395faa9..9be8fac 100644
--- a/src/Collection.ts
+++ b/src/Collection.ts
@@ -17,14 +17,24 @@ export class Collection {
server: BaseServer | null = null;
name: string | null = null;
identifierName = 'id';
+ getNewId: () => number | string;
- constructor(items: T[] = [], identifierName = 'id') {
+ constructor({
+ items = [],
+ identifierName = 'id',
+ getNewId,
+ }: {
+ items?: T[];
+ identifierName?: string;
+ getNewId?: () => number | string;
+ } = {}) {
if (!Array.isArray(items)) {
throw new Error(
"Can't initialize a Collection with anything else than an array of items",
);
}
this.identifierName = identifierName;
+ this.getNewId = getNewId || this.getNewIdFromSequence;
items.map(this.addOne.bind(this));
}
@@ -160,14 +170,14 @@ export class Collection {
return items;
}
- getIndex(identifier: number) {
+ getIndex(identifier: number | string) {
return this.items.findIndex(
// biome-ignore lint/suspicious/noDoubleEquals: we want implicit type coercion
(item) => item[this.identifierName] == identifier,
);
}
- getOne(identifier: number, query?: Query) {
+ getOne(identifier: number | string, query?: Query) {
const index = this.getIndex(identifier);
if (index === -1) {
throw new Error(`No item with identifier ${identifier}`);
@@ -180,29 +190,30 @@ export class Collection {
return item;
}
+ getNewIdFromSequence() {
+ return this.sequence++;
+ }
+
addOne(item: T) {
const identifier = item[this.identifierName];
- if (identifier != null && typeof identifier !== 'number') {
- throw new Error(
- `Item must have an identifier of type number, got ${typeof identifier}`,
- );
- }
if (identifier != null) {
if (this.getIndex(identifier) !== -1) {
throw new Error(
`An item with the identifier ${identifier} already exists`,
);
}
- this.sequence = Math.max(this.sequence, identifier) + 1;
+ if (typeof identifier === 'number') {
+ this.sequence = Math.max(this.sequence, identifier) + 1;
+ }
} else {
// @ts-expect-error - For some reason, TS does not accept writing a generic types with the index signature
- item[this.identifierName] = this.sequence++;
+ item[this.identifierName] = this.getNewId();
}
this.items.push(item);
return Object.assign({}, item); // clone item to avoid returning the original;
}
- updateOne(identifier: number, item: T) {
+ updateOne(identifier: number | string, item: T) {
const index = this.getIndex(identifier);
if (index === -1) {
throw new Error(`No item with identifier ${identifier}`);
@@ -213,7 +224,7 @@ export class Collection {
return Object.assign({}, this.items[index]); // clone item to avoid returning the original
}
- removeOne(identifier: number) {
+ removeOne(identifier: number | string) {
const index = this.getIndex(identifier);
if (index === -1) {
throw new Error(`No item with identifier ${identifier}`);
@@ -221,7 +232,7 @@ export class Collection {
const item = this.items[index];
this.items.splice(index, 1);
// biome-ignore lint/suspicious/noDoubleEquals: we want implicit type coercion
- if (identifier == this.sequence - 1) {
+ if (typeof identifier === 'number' && identifier == this.sequence - 1) {
this.sequence--;
}
return item;
diff --git a/src/Server.spec.ts b/src/Server.spec.ts
index 3da125c..0e6c813 100644
--- a/src/Server.spec.ts
+++ b/src/Server.spec.ts
@@ -46,10 +46,12 @@ describe('Server', () => {
describe('addCollection', () => {
it('should add a collection and index it by name', () => {
const server = new Server();
- const collection = new Collection([
- { id: 1, name: 'foo' },
- { id: 2, name: 'bar' },
- ]);
+ const collection = new Collection({
+ items: [
+ { id: 1, name: 'foo' },
+ { id: 2, name: 'bar' },
+ ],
+ });
server.addCollection('foo', collection);
const newcollection = server.getCollection('foo');
expect(newcollection).toEqual(collection);
@@ -70,14 +72,16 @@ describe('Server', () => {
const server = new Server();
server.addCollection(
'foo',
- new Collection([
- { id: 1, name: 'foo' },
- { id: 2, name: 'bar' },
- ]),
+ new Collection({
+ items: [
+ { id: 1, name: 'foo' },
+ { id: 2, name: 'bar' },
+ ],
+ }),
);
server.addCollection(
'baz',
- new Collection([{ id: 1, name: 'baz' }]),
+ new Collection({ items: [{ id: 1, name: 'baz' }] }),
);
expect(server.getAll('foo')).toEqual([
{ id: 1, name: 'foo' },
@@ -90,11 +94,13 @@ describe('Server', () => {
const server = new Server();
server.addCollection(
'foo',
- new Collection([
- { id: 0, name: 'c', arg: false },
- { id: 1, name: 'b', arg: true },
- { id: 2, name: 'a', arg: true },
- ]),
+ new Collection({
+ items: [
+ { id: 0, name: 'c', arg: false },
+ { id: 1, name: 'b', arg: true },
+ { id: 2, name: 'a', arg: true },
+ ],
+ }),
);
const params = {
filter: { arg: true },
@@ -114,7 +120,7 @@ describe('Server', () => {
const server = new Server();
server.addCollection(
'foo',
- new Collection([{ id: 1, name: 'foo' }]),
+ new Collection({ items: [{ id: 1, name: 'foo' }] }),
);
expect(() => {
server.getOne('foo', 2);
@@ -125,10 +131,12 @@ describe('Server', () => {
const server = new Server();
server.addCollection(
'foo',
- new Collection([
- { id: 1, name: 'foo' },
- { id: 2, name: 'bar' },
- ]),
+ new Collection({
+ items: [
+ { id: 1, name: 'foo' },
+ { id: 2, name: 'bar' },
+ ],
+ }),
);
expect(server.getOne('foo', 1)).toEqual({ id: 1, name: 'foo' });
expect(server.getOne('foo', 2)).toEqual({ id: 2, name: 'bar' });
@@ -138,13 +146,13 @@ describe('Server', () => {
const server = new Server();
server.addCollection(
'foo',
- new Collection(
- [
+ new Collection({
+ items: [
{ _id: 1, name: 'foo' },
{ _id: 2, name: 'bar' },
],
- '_id',
- ),
+ identifierName: '_id',
+ }),
);
expect(server.getOne('foo', 1)).toEqual({ _id: 1, name: 'foo' });
expect(server.getOne('foo', 2)).toEqual({ _id: 2, name: 'bar' });
@@ -178,10 +186,12 @@ describe('Server', () => {
});
server.addCollection(
'foo',
- new Collection([
- { id: 1, name: 'foo' },
- { id: 2, name: 'bar' },
- ]),
+ new Collection({
+ items: [
+ { id: 1, name: 'foo' },
+ { id: 2, name: 'bar' },
+ ],
+ }),
);
let request: SinonFakeXMLHttpRequest | null;
request = getFakeXMLHTTPRequest('GET', '/foo?_start=1&_end=1');
@@ -223,10 +233,12 @@ describe('Server', () => {
});
server.addCollection(
'foo',
- new Collection([
- { id: 1, name: 'foo' },
- { id: 2, name: 'bar' },
- ]),
+ new Collection({
+ items: [
+ { id: 1, name: 'foo' },
+ { id: 2, name: 'bar' },
+ ],
+ }),
);
const request = getFakeXMLHTTPRequest('GET', '/foo');
if (request == null) throw new Error('request is null');
@@ -267,10 +279,12 @@ describe('Server', () => {
const server = new Server();
server.addCollection(
'foo',
- new Collection([
- { id: 1, name: 'foo' },
- { id: 2, name: 'bar' },
- ]),
+ new Collection({
+ items: [
+ { id: 1, name: 'foo' },
+ { id: 2, name: 'bar' },
+ ],
+ }),
);
const request = getFakeXMLHTTPRequest('GET', '/foo');
if (request == null) throw new Error('request is null');
@@ -291,15 +305,17 @@ describe('Server', () => {
const server = new Server();
server.addCollection(
'foos',
- new Collection([
- { id: 0, name: 'c', arg: false },
- { id: 1, name: 'b', arg: true },
- { id: 2, name: 'a', arg: true },
- ]),
+ new Collection({
+ items: [
+ { id: 0, name: 'c', arg: false },
+ { id: 1, name: 'b', arg: true },
+ { id: 2, name: 'a', arg: true },
+ ],
+ }),
);
server.addCollection(
'bars',
- new Collection([{ id: 0, name: 'a', foo_id: 1 }]),
+ new Collection({ items: [{ id: 0, name: 'a', foo_id: 1 }] }),
);
const request = getFakeXMLHTTPRequest(
'GET',
@@ -323,7 +339,9 @@ describe('Server', () => {
const server = new Server();
server.addCollection(
'foo',
- new Collection([{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}]),
+ new Collection({
+ items: [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
+ }),
); // 11 items
let request: SinonFakeXMLHttpRequest | null;
request = getFakeXMLHTTPRequest('GET', '/foo');
@@ -375,10 +393,12 @@ describe('Server', () => {
const server = new Server();
server.addCollection(
'foo',
- new Collection([
- { id: 1, name: 'foo' },
- { id: 2, name: 'bar' },
- ]),
+ new Collection({
+ items: [
+ { id: 1, name: 'foo' },
+ { id: 2, name: 'bar' },
+ ],
+ }),
);
const request = getFakeXMLHTTPRequest(
'POST',
@@ -426,10 +446,12 @@ describe('Server', () => {
const server = new Server();
server.addCollection(
'foo',
- new Collection([
- { id: 1, name: 'foo' },
- { id: 2, name: 'bar' },
- ]),
+ new Collection({
+ items: [
+ { id: 1, name: 'foo' },
+ { id: 2, name: 'bar' },
+ ],
+ }),
);
const request = getFakeXMLHTTPRequest('GET', '/foo/2');
if (request == null) throw new Error('request is null');
@@ -456,10 +478,12 @@ describe('Server', () => {
const server = new Server();
server.addCollection(
'foo',
- new Collection([
- { id: 1, name: 'foo' },
- { id: 2, name: 'bar' },
- ]),
+ new Collection({
+ items: [
+ { id: 1, name: 'foo' },
+ { id: 2, name: 'bar' },
+ ],
+ }),
);
const request = getFakeXMLHTTPRequest(
'PUT',
@@ -483,7 +507,7 @@ describe('Server', () => {
it('should respond to PUT /foo/:id on a non-existing id with a 404', () => {
const server = new Server();
- server.addCollection('foo', new Collection([]));
+ server.addCollection('foo', new Collection({ items: [] }));
const request = getFakeXMLHTTPRequest(
'PUT',
'/foo/3',
@@ -498,10 +522,12 @@ describe('Server', () => {
const server = new Server();
server.addCollection(
'foo',
- new Collection([
- { id: 1, name: 'foo' },
- { id: 2, name: 'bar' },
- ]),
+ new Collection({
+ items: [
+ { id: 1, name: 'foo' },
+ { id: 2, name: 'bar' },
+ ],
+ }),
);
const request = getFakeXMLHTTPRequest(
'PATCH',
@@ -525,7 +551,7 @@ describe('Server', () => {
it('should respond to PATCH /foo/:id on a non-existing id with a 404', () => {
const server = new Server();
- server.addCollection('foo', new Collection([]));
+ server.addCollection('foo', new Collection({ items: [] }));
const request = getFakeXMLHTTPRequest(
'PATCH',
'/foo/3',
@@ -540,10 +566,12 @@ describe('Server', () => {
const server = new Server();
server.addCollection(
'foo',
- new Collection([
- { id: 1, name: 'foo' },
- { id: 2, name: 'bar' },
- ]),
+ new Collection({
+ items: [
+ { id: 1, name: 'foo' },
+ { id: 2, name: 'bar' },
+ ],
+ }),
);
const request = getFakeXMLHTTPRequest('DELETE', '/foo/2');
if (request == null) throw new Error('request is null');
@@ -560,7 +588,7 @@ describe('Server', () => {
it('should respond to DELETE /foo/:id on a non-existing id with a 404', () => {
const server = new Server();
- server.addCollection('foo', new Collection([]));
+ server.addCollection('foo', new Collection({ items: [] }));
const request = getFakeXMLHTTPRequest('DELETE', '/foo/3');
if (request == null) throw new Error('request is null');
server.handle(request);
@@ -631,7 +659,9 @@ describe('Server', () => {
const server = new Server();
server.addCollection(
'foo',
- new Collection([{}, {}, {}, {}, {}, {}, {}, {}, {}, {}]),
+ new Collection({
+ items: [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
+ }),
); // 10 items
server.setDefaultQuery(() => {
return { range: [2, 4] };
@@ -653,7 +683,9 @@ describe('Server', () => {
const server = new Server();
server.addCollection(
'foo',
- new Collection([{}, {}, {}, {}, {}, {}, {}, {}, {}, {}]),
+ new Collection({
+ items: [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
+ }),
); // 10 items
server.setDefaultQuery((name) => ({ range: [2, 4] }));
const request = getFakeXMLHTTPRequest('GET', '/foo?range=[0,4]');
diff --git a/src/Server.ts b/src/Server.ts
index b7053a7..ec2566b 100644
--- a/src/Server.ts
+++ b/src/Server.ts
@@ -6,10 +6,6 @@ export class Server extends BaseServer {
requestInterceptors: SinonRequestInterceptor[] = [];
responseInterceptors: SinonResponseInterceptor[] = [];
- constructor(baseUrl = '') {
- super(baseUrl);
- }
-
addRequestInterceptor(interceptor: SinonRequestInterceptor) {
this.requestInterceptors.push(interceptor);
}
diff --git a/src/Single.spec.ts b/src/Single.spec.ts
index 44001c6..93306a0 100644
--- a/src/Single.spec.ts
+++ b/src/Single.spec.ts
@@ -30,7 +30,7 @@ describe('Single', () => {
it('should return the original object for missing embed one', () => {
const foo = new Single({ name: 'foo', bar_id: 123 });
- const bars = new Collection([]);
+ const bars = new Collection({ items: [] });
const server = new Server();
server.addSingle('foo', foo);
server.addCollection('bars', bars);
@@ -40,11 +40,13 @@ describe('Single', () => {
it('should return the object with the reference object for embed one', () => {
const foo = new Single({ name: 'foo', bar_id: 123 });
- const bars = new Collection([
- { id: 1, bar: 'nobody wants me' },
- { id: 123, bar: 'baz' },
- { id: 456, bar: 'bazz' },
- ]);
+ const bars = new Collection({
+ items: [
+ { id: 1, bar: 'nobody wants me' },
+ { id: 123, bar: 'baz' },
+ { id: 456, bar: 'bazz' },
+ ],
+ });
const server = new Server();
server.addSingle('foo', foo);
server.addCollection('bars', bars);
@@ -69,11 +71,13 @@ describe('Single', () => {
it('should return the object with an array of references for embed many using inner array', () => {
const foo = new Single({ name: 'foo', bars: [1, 3] });
- const bars = new Collection([
- { id: 1, bar: 'baz' },
- { id: 2, bar: 'biz' },
- { id: 3, bar: 'boz' },
- ]);
+ const bars = new Collection({
+ items: [
+ { id: 1, bar: 'baz' },
+ { id: 2, bar: 'biz' },
+ { id: 3, bar: 'boz' },
+ ],
+ });
const server = new Server();
server.addSingle('foo', foo);
server.addCollection('bars', bars);
@@ -93,16 +97,20 @@ describe('Single', () => {
bars: [1, 3],
bazs: [4, 5],
});
- const bars = new Collection([
- { id: 1, name: 'bar1' },
- { id: 2, name: 'bar2' },
- { id: 3, name: 'bar3' },
- ]);
- const bazs = new Collection([
- { id: 4, name: 'baz1' },
- { id: 5, name: 'baz2' },
- { id: 6, name: 'baz3' },
- ]);
+ const bars = new Collection({
+ items: [
+ { id: 1, name: 'bar1' },
+ { id: 2, name: 'bar2' },
+ { id: 3, name: 'bar3' },
+ ],
+ });
+ const bazs = new Collection({
+ items: [
+ { id: 4, name: 'baz1' },
+ { id: 5, name: 'baz2' },
+ { id: 6, name: 'baz3' },
+ ],
+ });
const server = new Server();
server.addSingle('foo', foo);
server.addCollection('bars', bars);
diff --git a/src/msw.ts b/src/msw.ts
index cb49425..89113fd 100644
--- a/src/msw.ts
+++ b/src/msw.ts
@@ -5,11 +5,13 @@ import { BaseServer } from './BaseServer.js';
export const getMswHandlers = ({
baseUrl = 'http://localhost:3000',
data,
+ getNewId,
}: {
baseUrl?: string;
data: Record;
+ getNewId?: () => number | string;
}) => {
- const server = new BaseServer(baseUrl);
+ const server = new BaseServer({ baseUrl, getNewId });
server.init(data);
const collections = Object.keys(data);
@@ -27,30 +29,34 @@ const getCollectionHandlers = ({
collectionName: string;
server: BaseServer;
}) => {
- return http.all(`${baseUrl}/${collectionName}`, async ({ request }) => {
- const url = new URL(request.url);
- const params = Object.fromEntries(
- Array.from(new URLSearchParams(url.search).entries()).map(
- ([key, value]) => [key, JSON.parse(value)],
- ),
- );
- let requestJson: Record | undefined = undefined;
- try {
- const text = await request.text();
- requestJson = JSON.parse(text);
- } catch (e) {
- // not JSON, no big deal
- }
- const response = server.handleRequest({
- url: request.url.split('?')[0],
- method: request.method,
- requestJson,
- params,
- });
+ return http.all(
+ // Using a regex ensures we match all URLs that start with the collection name
+ new RegExp(`${baseUrl}/${collectionName}`),
+ async ({ request }) => {
+ const url = new URL(request.url);
+ const params = Object.fromEntries(
+ Array.from(new URLSearchParams(url.search).entries()).map(
+ ([key, value]) => [key, JSON.parse(value)],
+ ),
+ );
+ let requestJson: Record | undefined = undefined;
+ try {
+ const text = await request.text();
+ requestJson = JSON.parse(text);
+ } catch (e) {
+ // not JSON, no big deal
+ }
+ const response = server.handleRequest({
+ url: request.url.split('?')[0],
+ method: request.method,
+ requestJson,
+ params,
+ });
- return HttpResponse.json(response.body, {
- status: response.status,
- headers: response.headers,
- });
- });
+ return HttpResponse.json(response.body, {
+ status: response.status,
+ headers: response.headers,
+ });
+ },
+ );
};