Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions src/drivers/default/builders/queryBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,23 @@ export class QueryBuilder<
return this.hydrate(response.data.data, response);
}

public async batchStore(items: M[]): Promise<M[]> {
const data = {
resources: items.map(x => x.$attributes)
};

const response = await this.httpClient.request<{data: Array<AllAttributes & Relations> }>(
`/batch`,
HttpMethod.POST,
null,
data
);

return response.data.data.map((attributes) => {
return this.hydrate(attributes, response);
})
}

public async update(key: Key, attributes: Attributes): Promise<M> {
const response = await this.httpClient.request<{ data: AllAttributes & Relations }>(
`/${key}`,
Expand All @@ -115,6 +132,24 @@ export class QueryBuilder<
return this.hydrate(response.data.data, response);
}

public async batchUpdate(items: M[]): Promise<M[]> {
const data = {
resources: {}
};
items.forEach((v) => data.resources[v.$getKey()] = v.$attributes);

const response = await this.httpClient.request<{ data: Array< AllAttributes & Relations > }>(
`batch`,
HttpMethod.PATCH,
null,
data
)

return response.data.data.map((attributes: AllAttributes & Relations) => {
return this.hydrate(attributes, response);
});
}

public async destroy(key: Key, force: boolean = false): Promise<M> {
const response = await this.httpClient.request<{ data: AllAttributes & Relations }>(
`/${key}`,
Expand All @@ -125,6 +160,27 @@ export class QueryBuilder<
return this.hydrate(response.data.data, response);
}

public async batchDelete(items: Key[]): Promise<M[]>
{
if (!items.length)
return [];

const data = {
resources: items
};

const response = await this.httpClient.request<{ data: Array< AllAttributes & Relations > }>(
`/batch`,
HttpMethod.DELETE,
null,
data
);

return response.data.data.map((attributes: AllAttributes & Relations) => {
return this.hydrate(attributes, response);
});
}

public async restore(key: Key): Promise<M> {
const response = await this.httpClient.request<{ data: AllAttributes & Relations }>(
`/${key}/restore`,
Expand All @@ -135,6 +191,24 @@ export class QueryBuilder<
return this.hydrate(response.data.data, response);
}

public async batchRestore(items: Key[]): Promise<M[]> {
const data = {
resources: items
};

const response = await this.httpClient.request<{ data: Array< AllAttributes & Relations > }>(
`/batch/restore`,
HttpMethod.POST,
null,
data
);

return response.data.data.map((attributes: AllAttributes & Relations) => {
return this.hydrate(attributes, response);
});
}


public with(relations: string[]): this {
this.includes = relations;

Expand Down
109 changes: 109 additions & 0 deletions tests/integration/batch.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import makeServer from './drivers/default/server';
import Post from '../stubs/models/post';
import { Orion } from '../../src/orion';

let server: any;

beforeEach(() => {
server = makeServer();
});

afterEach(() => {
server.shutdown();
});

describe('Batch tests', () => {
test('saving a couple of models', async () => {
const posts = [
new Post(),
new Post(),
]
posts[0].$attributes.title = "First";
posts[1].$attributes.title = "Second";

const res = await Post.$query().batchStore(posts);

expect(server.schema.posts.all()).toHaveLength(2);
expect(server.schema.posts.find('1').attrs.title).toBe("First")
expect(server.schema.posts.find('2').attrs.title).toBe("Second")
expect(server.schema.posts.find('1').attrs.title).toEqual(res[0].$attributes.title)
expect(server.schema.posts.find('1').attrs.created_at).toEqual(res[0].$attributes.created_at)
expect(server.schema.posts.find('2').attrs.title).toEqual(res[1].$attributes.title)
expect(server.schema.posts.find('2').attrs.created_at).toEqual(res[1].$attributes.created_at)
});

test('updating a couple of models', async () => {
const posts = [
new Post(),
new Post(),
new Post(),
]
posts[0].$attributes.title = "First";
posts[1].$attributes.title = "Second";
posts[2].$attributes.title = "Third";

let res = await Post.$query().batchStore(posts);

res[0].$attributes.title = "NewFirst";
res[1].$attributes.title = "NewSecond";

res = await Post.$query().batchUpdate([res[0],res[1]]);

expect(res).toHaveLength(2);
expect(server.schema.posts.find('1').attrs.title).toBe("NewFirst")
expect(server.schema.posts.find('2').attrs.title).toBe("NewSecond")
expect(server.schema.posts.find('1').attrs.title).toEqual(res[0].$attributes.title)
expect(server.schema.posts.find('2').attrs.title).toEqual(res[1].$attributes.title)
expect(server.schema.posts.find('3').attrs.title).toEqual("Third");

});

test('deleting a couple of models', async () => {
const posts = [
new Post(),
new Post(),
new Post(),
]
posts[0].$attributes.title = "First";
posts[1].$attributes.title = "Second";
posts[2].$attributes.title = "Third";

let res = await Post.$query().batchStore(posts);

let ModelDelete = await Post.$query().batchDelete([res[1].$getKey(), res[2].$getKey()]);

expect(server.schema.posts.find('1').attrs.deleted_at).toBeUndefined();
expect(server.schema.posts.find('2').attrs.deleted_at).toBeDefined();
expect(server.schema.posts.find('3').attrs.deleted_at).toBeDefined();
expect(server.schema.posts.find('2').attrs.title).toEqual(ModelDelete[0].$attributes.title)
expect(server.schema.posts.find('3').attrs.title).toEqual(ModelDelete[1].$attributes.title)


});

test('restoring a couple of models', async () => {
const posts = [
new Post(),
new Post(),
new Post(),
]
posts[0].$attributes.title = "First";
posts[1].$attributes.title = "Second";
posts[2].$attributes.title = "Third";

let res = await Post.$query().batchStore(posts);

// delete ID 2 & 3
let ModelDelete = await Post.$query().batchDelete([res[1].$getKey(), res[2].$getKey()]);

res = await Post.$query().batchRestore(ModelDelete.map(x => x.$getKey()));

expect(server.schema.posts.find('1').attrs.deleted_at).toBeFalsy();
expect(server.schema.posts.find('2').attrs.deleted_at).toBeFalsy();
expect(server.schema.posts.find('3').attrs.deleted_at).toBeFalsy();
expect(server.schema.posts.find('2').attrs.title).toEqual(res[0].$attributes.title);
expect(server.schema.posts.find('3').attrs.title).toEqual(res[1].$attributes.title);


});
});
68 changes: 68 additions & 0 deletions tests/integration/drivers/default/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,74 @@ export default function makeServer() {
updated: [request.params.tag_id],
};
});

this.post('/api/posts/batch', (schema: any, request) => {
const body: {
resources: any[]
} = JSON.parse(request.requestBody);

const rval: any[] = [];
for (let i = 0; i < body.resources.length; i++) {
rval.push(schema.posts.create(body.resources[i]));
}

return {data: rval};
})

this.patch('/api/posts/batch', (schema: any, request) => {
const body: {
resources: object
} = JSON.parse(request.requestBody);

const rval: any[] = [];
for (const key in body.resources) {
const attrs = body.resources[key];

const post = schema.posts.find(key);


rval.push(post.update(attrs));
}

return {data: rval};
})

this.delete('/api/posts/batch', (schema: any, request) => {
const body: {
resources: number[]
} = JSON.parse(request.requestBody);

const rval: any[] = [];
for (let i = 0; i < body.resources.length; i++) {
const id = body.resources[i];
const post = schema.posts.find(id);

post.update({ deleted_at: '2021-01-01' });

rval.push(post);
}

return {data: rval};
})

this.post('/api/posts/batch/restore', (schema: any, request) => {
const body: {
resources: number[]
} = JSON.parse(request.requestBody);

const rval: any[] = [];

for (let i = 0; i < body.resources.length; i++) {
const id = body.resources[i];
const post = schema.posts.find(id);

post.update({ deleted_at: null });

rval.push(post);
}

return {data: rval};
})
},
});
}
Expand Down