Repository: xataio/client-ts
Pull request #24 (2022-03-04 12:16:51)

Fix create object methods return type
- Add link transform to ``createMany``
- Change ``create`` methods signature to return full objects




Patch:
diff --git a/client/src/index.ts b/client/src/index.ts
index 61e8fb3f..35cf03e6 100644
--- a/client/src/index.ts
+++ b/client/src/index.ts
@@ -438,28 +438,26 @@ export class RestRepository<T> extends Repository<T> {
   }
 
   async create(object: T): Promise<T> {
-    const body = { ...object } as Record<string, unknown>;
-    for (const key of Object.keys(body)) {
-      const value = body[key];
-      if (value && typeof value === 'object' && typeof (value as Record<string, unknown>).id === 'string') {
-        body[key] = (value as XataRecord).id;
-      }
-    }
+    const record = transformObjectLinks(object);
 
     const response = await this.request<{
       id: string;
       xata: { version: number };
-    }>('POST', `/tables/${this.table}/data`, body);
+    }>('POST', `/tables/${this.table}/data`, record);
     if (!response) {
       throw new Error("The server didn't return any data for the query");
     }
 
-    // TODO: Review this, not sure we are properly initializing the object
-    return this.client.initObject(this.table, response);
+    const finalObject = await this.read(response.id);
+    if (!finalObject) {
+      throw new Error('The server failed to save the record');
+    }
+
+    return finalObject;
   }
 
-  async createMany(records: T[]): Promise<T[]> {
-    // TODO: Review the id of the records
+  async createMany(objects: T[]): Promise<T[]> {
+    const records = objects.map((object) => transformObjectLinks(object));
 
     const response = await this.request<{
       recordIDs: string[];
@@ -468,8 +466,13 @@ export class RestRepository<T> extends Repository<T> {
       throw new Error("The server didn't return any data for the query");
     }
 
-    // TODO: Review this, not sure we are properly initializing the object
-    return response.recordIDs.map((record) => this.client.initObject(this.table, { id: record }));
+    // TODO: Use filer.$any() to get all the records
+    const finalObjects = await Promise.all(response.recordIDs.map((id) => this.read(id)));
+    if (finalObjects.some((object) => !object)) {
+      throw new Error('The server failed to save the record');
+    }
+
+    return finalObjects as T[];
   }
 
   async read(id: string): Promise<T | null> {
@@ -500,7 +503,6 @@ export class RestRepository<T> extends Repository<T> {
   }
 
   async delete(id: string) {
-    // TODO: Return boolean?
     await this.request('DELETE', `/tables/${this.table}/data/${id}`);
   }
 
@@ -652,3 +654,14 @@ export type Links = Record<string, Array<string[]>>;
 const isBranchStrategyBuilder = (strategy: BranchStrategy): strategy is BranchStrategyBuilder => {
   return typeof strategy === 'function';
 };
+
+// TODO: We can find a better implementation for links
+const transformObjectLinks = (object: any) => {
+  return Object.entries(object).reduce((acc, [key, value]) => {
+    if (value && typeof value === 'object' && typeof (value as Record<string, unknown>).id === 'string') {
+      return { ...acc, [key]: (value as XataRecord).id };
+    }
+
+    return { ...acc, [key]: value };
+  }, {});
+};


Test patch:
diff --git a/client/src/index.test.ts b/client/src/index.test.ts
index 1ce8e747..2763b00f 100644
--- a/client/src/index.test.ts
+++ b/client/src/index.test.ts
@@ -262,7 +262,7 @@ type ExpectedRequest = {
 
 async function expectRequest(
   users: RestRepository<User>,
-  expectedRequest: ExpectedRequest,
+  expectedRequest: ExpectedRequest[] | ExpectedRequest,
   callback: () => void,
   response?: any
 ) {
@@ -272,11 +272,17 @@ async function expectRequest(
   await callback();
 
   const { calls } = request.mock;
-  expect(calls.length).toBe(1);
-  const [method, path, body] = calls[0] as any;
-  expect(method).toBe(expectedRequest.method);
-  expect(path).toBe(expectedRequest.path);
-  expect(JSON.stringify(body)).toBe(JSON.stringify(expectedRequest.body));
+
+  const requests = Array.isArray(expectedRequest) ? expectedRequest : [expectedRequest];
+
+  expect(calls.length).toBe(requests.length);
+
+  for (let i = 0; i < calls.length; i++) {
+    const [method, path, body] = calls[i] as any;
+    expect(method).toBe(requests[i].method);
+    expect(path).toBe(requests[i].path);
+    expect(JSON.stringify(body)).toBe(JSON.stringify(requests[i].body));
+  }
 }
 
 describe('query', () => {
@@ -384,7 +390,15 @@ describe('create', () => {
 
     const created = { id: 'rec_1234', _version: 0 };
     const object = { name: 'Ada' } as User;
-    const expected = { method: 'POST', path: '/tables/users/data', body: object };
+    const expected = [
+      { method: 'POST', path: '/tables/users/data', body: object },
+      {
+        method: 'GET',
+        path: '/tables/users/data/rec_1234',
+        body: undefined
+      }
+    ];
+
     expectRequest(
       users,
       expected,
diff --git a/test/integration.test.ts b/test/integration.test.ts
index c590267e..1c12538e 100644
--- a/test/integration.test.ts
+++ b/test/integration.test.ts
@@ -237,6 +237,23 @@ describe('integration tests', () => {
     expect(users.records).toHaveLength(10);
   });
 
+  test('create single team', async () => {
+    const team = await client.db.teams.create({ name: 'Team ships' });
+
+    expect(team.id).toBeDefined();
+    expect(team.name).toBe('Team ships');
+  });
+
+  test('create multiple teams', async () => {
+    const teams = await client.db.teams.createMany([{ name: 'Team cars' }, { name: 'Team planes' }]);
+
+    expect(teams).toHaveLength(2);
+    expect(teams[0].id).toBeDefined();
+    expect(teams[0].name).toBe('Team cars');
+    expect(teams[1].id).toBeDefined();
+    expect(teams[1].name).toBe('Team planes');
+  });
+  
   test('query implements iterator', async () => {
     const owners = [];