Commit c609307d authored by Jean-Philippe Steinmetz's avatar Jean-Philippe Steinmetz
Browse files

ModelUtils.doTruncate now takes params/query options to selectively delete...

ModelUtils.doTruncate now takes params/query options to selectively delete specific entries in a database
Adding support for the $nin mongodb find operator
like() now performs case-insensitive comparison in all cases
parent d550407c
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
\ No newline at end of file
......@@ -6,9 +6,8 @@ import * as path from "path";
import { Logger } from "@composer-js/core";
import {
getMetadataArgsStorage,
Repository,
Like,
ILike,
LessThanOrEqual,
MoreThanOrEqual,
Not,
......@@ -164,7 +163,7 @@ class ModelUtils {
return In(args);
}
case "like":
return Like(value);
return ILike(value);
case "lt":
return LessThan(value);
case "lte":
......@@ -238,6 +237,10 @@ class ModelUtils {
const args: string[] = value.split(",");
return { $in: args };
}
case "nin": {
const args: string[] = value.split(",");
return { $nin: args };
}
case "like":
return { $regex: value, $options: "i" };
case "lt":
......
......@@ -532,11 +532,14 @@ abstract class ModelRoute<T extends BaseEntity | SimpleEntity> {
}
/**
* Attempts to remove all entries of the data model type from the datastore.
* Attempts to remove all entries of the data model type from the datastore matching the given
* parameters and query.
*
* @param params The parameters to match.
* @param query The query parameters to match.
* @param user The authenticated user performing the action, otherwise undefined.
*/
protected async doTruncate(user?: any): Promise<void> {
protected async doTruncate(params?: any, query?: any, user?: any): Promise<void> {
if (!this.repo) {
throw new Error("Repository not set or could not be found.");
}
......@@ -548,7 +551,23 @@ abstract class ModelRoute<T extends BaseEntity | SimpleEntity> {
}
try {
await this.repo.clear();
const searchQuery: any = ModelUtils.buildSearchQuery(this.modelClass, this.repo, params, query, true, user);
let objs: T[] | undefined = undefined;
if (this.repo instanceof MongoRepository && Array.isArray(searchQuery)) {
const limit: number = query.limit ? Math.min(query.limit, 1000) : 100;
const skip: number = query.skip ? query.skip : 0;
objs = await this.repo.aggregate(searchQuery).limit(limit).skip(skip).toArray();
}
else {
objs = await this.repo.find(searchQuery);
}
if (objs) {
for (const obj of objs) {
await this.repo.remove(obj);
await ACLUtils.removeACL(obj.uid);
}
}
} catch (err) {
// The error "ns not found" occurs when the collection doesn't exist yet. We can ignore this error.
if (err.message != "ns not found") {
......
......@@ -28,11 +28,11 @@ const createUser = async (firstName: string, lastName: string, age: number = 100
return await repo.save(user);
};
const createUsers = async (num: number): Promise<User[]> => {
const createUsers = async (num: number, lastName: string = "Doctor"): Promise<User[]> => {
const results: User[] = [];
for (let i = 1; i <= num; i++) {
results.push(await createUser(String(i), "Doctor", 100 * i));
results.push(await createUser(String(i), lastName, 100 * i));
}
return results;
......@@ -163,6 +163,116 @@ describe("ModelRoute Tests [MongoDB]", () => {
expect(result.body.count).toBe(users.length);
});
it("Can count documents with criteria (like-regex). [MongoDB]", async () => {
const users: User[] = await createUsers(13);
await createUser("David", "Tennant", 47);
await createUser("Matt", "Smith", 36);
const result = await request(server.getApplication()).get("/users/count?lastName=like(Doc.*)");
expect(result).toHaveProperty("body");
console.log(result.body);
expect(result.body).toHaveProperty("count");
expect(result.body.count).toBe(users.length);
});
it("Can count documents with criteria (ne). [MongoDB]", async () => {
const users: User[] = await createUsers(13);
await createUser("David", "Tennant", 47);
await createUser("Matt", "Smith", 36);
const result = await request(server.getApplication()).get("/users/count?lastName=ne(Doctor)");
expect(result).toHaveProperty("body");
console.log(result.body);
expect(result.body).toHaveProperty("count");
expect(result.body.count).toBe(2);
});
it("Can count documents with criteria (like). [MongoDB]", async () => {
const users: User[] = await createUsers(13);
await createUser("David", "Tennant", 47);
await createUser("Matt", "Smith", 36);
const result = await request(server.getApplication()).get("/users/count?lastName=like(Doc)");
expect(result).toHaveProperty("body");
console.log(result.body);
expect(result.body).toHaveProperty("count");
expect(result.body.count).toBe(users.length);
});
it("Can count documents with criteria (in). [MongoDB]", async () => {
const users: User[] = await createUsers(13);
await createUser("David", "Tennant", 47);
await createUser("Matt", "Smith", 36);
const result = await request(server.getApplication()).get("/users/count?lastName=in(Tennant,Smith)");
expect(result).toHaveProperty("body");
console.log(result.body);
expect(result.body).toHaveProperty("count");
expect(result.body.count).toBe(2);
});
it("Can count documents with criteria (nin). [MongoDB]", async () => {
const users: User[] = await createUsers(13);
await createUser("David", "Tennant", 47);
await createUser("Matt", "Smith", 36);
const result = await request(server.getApplication()).get("/users/count?lastName=nin(Tennant,Smith)");
expect(result).toHaveProperty("body");
console.log(result.body);
expect(result.body).toHaveProperty("count");
expect(result.body.count).toBe(users.length);
});
it("Can count documents with criteria (gt). [MongoDB]", async () => {
const users: User[] = await createUsers(13);
await createUser("David", "Tennant", 47);
await createUser("Matt", "Smith", 36);
const result = await request(server.getApplication()).get("/users/count?age=gt(100)");
expect(result).toHaveProperty("body");
console.log(result.body);
expect(result.body).toHaveProperty("count");
expect(result.body.count).toBe(users.length - 1);
});
it("Can count documents with criteria (gte). [MongoDB]", async () => {
const users: User[] = await createUsers(13);
await createUser("David", "Tennant", 47);
await createUser("Matt", "Smith", 36);
const result = await request(server.getApplication()).get("/users/count?age=gte(100)");
expect(result).toHaveProperty("body");
console.log(result.body);
expect(result.body).toHaveProperty("count");
expect(result.body.count).toBe(users.length);
});
it("Can count documents with criteria (lt). [MongoDB]", async () => {
const users: User[] = await createUsers(13);
await createUser("David", "Tennant", 47);
await createUser("Matt", "Smith", 36);
const result = await request(server.getApplication()).get("/users/count?age=lt(100)");
expect(result).toHaveProperty("body");
console.log(result.body);
expect(result.body).toHaveProperty("count");
expect(result.body.count).toBe(2);
});
it("Can count documents with criteria (lte). [MongoDB]", async () => {
const users: User[] = await createUsers(13);
await createUser("David", "Tennant", 47);
await createUser("Matt", "Smith", 36);
const result = await request(server.getApplication()).get("/users/count?age=lte(100)");
expect(result).toHaveProperty("body");
console.log(result.body);
expect(result.body).toHaveProperty("count");
expect(result.body.count).toBe(3);
});
it("Can count documents with criteria (range). [MongoDB]", async () => {
const users: User[] = await createUsers(13);
await createUser("David", "Tennant", 47);
await createUser("Matt", "Smith", 36);
const result = await request(server.getApplication()).get("/users/count?age=range(100,500)");
expect(result).toHaveProperty("body");
console.log(result.body);
expect(result.body).toHaveProperty("count");
expect(result.body.count).toBe(5);
});
it("Can find all documents. [MongoDB]", async () => {
const users: User[] = await createUsers(25);
const result = await request(server.getApplication()).get("/users");
......@@ -183,12 +293,23 @@ describe("ModelRoute Tests [MongoDB]", () => {
});
it("Can truncate datastore [MongoDB].", async () => {
const users: User[] = await createUsers(25);
const users: User[] = await createUsers(20, "Doctor");
await createUsers(5, "Skywalker");
const result = await request(server.getApplication()).delete("/users");
expect(result.status).toBe(204);
const count: number = await repo.count();
expect(count).toBe(0);
});
it("Can truncate datastore with criteria (eq) [MongoDB].", async () => {
const users: User[] = await createUsers(20, "Doctor");
await createUsers(5, "Skywalker");
const result = await request(server.getApplication()).delete("/users?lastName=Doctor");
expect(result.status).toBe(204);
const count: number = await repo.count();
expect(count).toBe(5);
});
});
});
......@@ -150,12 +150,24 @@ describe("ModelRoute Tests [SQL]", () => {
expect(result.body.count).toBe(items.length);
});
it.skip("Can count documents with criteria (like). [SQL]", async () => {
it("Can count documents with criteria (eq). [SQL]", async () => {
const items: Item[] = await createItems(15);
await createItem("BFG", 1, 10000);
await createItem("B-Bomb", 5, 50);
await createItem("Boomerang", 1, 100);
const result = await request(server.getApplication()).get("/items/count?name=like(Item)");
const result = await request(server.getApplication()).get("/items/count?name=B-Bomb");
expect(result).toHaveProperty("body");
console.log(result.body);
expect(result.body).toHaveProperty("count");
expect(result.body.count).toBe(1);
});
it("Can count documents with criteria (like). [SQL]", async () => {
const items: Item[] = await createItems(15);
await createItem("BFG", 1, 10000);
await createItem("B-Bomb", 5, 50);
await createItem("Boomerang", 1, 100);
const result = await request(server.getApplication()).get("/items/count?name=like(Item%)");
expect(result).toHaveProperty("body");
console.log(result.body);
expect(result.body).toHaveProperty("count");
......@@ -169,12 +181,25 @@ describe("ModelRoute Tests [SQL]", () => {
expect(result.body).toHaveLength(items.length);
});
it.skip("Can find documents with criteria (like) [SQL].", async () => {
it("Can find documents with criteria (eq) [SQL].", async () => {
const items: Item[] = await createItems(13);
await createItem("BFG", 1, 10000);
await createItem("B-Bomb", 5, 50);
await createItem("Boomerang", 1, 100);
const result = await request(server.getApplication()).get("/items?name=BFG");
expect(result).toHaveProperty("body");
expect(result.body).toHaveLength(1);
for (const item of result.body) {
expect(item.name).toContain("BFG");
}
});
it("Can find documents with criteria (like) [SQL].", async () => {
const items: Item[] = await createItems(13);
await createItem("BFG", 1, 10000);
await createItem("B-Bomb", 5, 50);
await createItem("Boomerang", 1, 100);
const result = await request(server.getApplication()).get("/items?name=like(Item)");
const result = await request(server.getApplication()).get("/items?name=like(Item%)");
expect(result).toHaveProperty("body");
expect(result.body).toHaveLength(items.length);
for (const item of result.body) {
......@@ -184,11 +209,26 @@ describe("ModelRoute Tests [SQL]", () => {
it("Can truncate datastore [SQL].", async () => {
const items: Item[] = await createItems(25);
await createItem("BFG", 1, 10000);
await createItem("B-Bomb", 5, 50);
await createItem("Boomerang", 1, 100);
const result = await request(server.getApplication()).delete("/items");
expect(result.status).toBe(204);
const count: number = await repo.count();
expect(count).toBe(0);
});
it("Can truncate datastore with criteria (in) [SQL].", async () => {
const items: Item[] = await createItems(13);
await createItem("BFG", 1, 10000);
await createItem("B-Bomb", 5, 50);
await createItem("Boomerang", 1, 100);
const result = await request(server.getApplication()).delete("/items?name=in(BFG,B-Bomb,Boomerang)");
expect(result.status).toBe(204);
const count: number = await repo.count();
expect(count).toBe(items.length);
});
});
});
......@@ -8,7 +8,7 @@ import { ModelUtils } from "../src/service_core";
import { Identifier } from "../src/decorators/ModelDecorators";
import {
Not,
Like,
ILike,
Equal,
Between,
MoreThan,
......@@ -240,7 +240,7 @@ describe("ModelUtils Tests", () => {
}}]);
});
it("Can build search query with single param (like)", () => {
it("Can build search query with single param (ILike)", () => {
const request: any = {};
request.query = {
myParam: "like(myValue)",
......@@ -319,7 +319,7 @@ describe("ModelUtils Tests", () => {
it("Can build search query with multiple params with same name.", () => {
const request: any = {};
request.query = {
param: ["Eq(myValue)", "Not(myValue2)", "Like(myValue3)"],
param: ["Eq(myValue)", "Not(myValue2)", "like(myValue3)"],
};
const query = ModelUtils.buildSearchQueryMongo(undefined, request.params, request.query, true, request.user);
......@@ -338,11 +338,10 @@ describe("ModelUtils Tests", () => {
}}]);
});
// TODO Fix me
it("Can build search query with multiple params and with same name.", () => {
const request: any = {};
request.query = {
param: ["Eq(myValue)", "Not(myValue2)", "Like(myValue3)"],
param: ["eq(myValue)", "not(myValue2)", "like(myValue3)"],
param2: "range(0,100)",
param3: "hello",
};
......@@ -663,7 +662,7 @@ describe("ModelUtils Tests", () => {
});
});
it("Can build search query with single param (like)", () => {
it("Can build search query with single param (ILike)", () => {
const request: any = {};
request.query = {
myParam: "like(myValue)",
......@@ -673,7 +672,7 @@ describe("ModelUtils Tests", () => {
expect(query).toEqual({
where: [
{
myParam: Like("myValue"),
myParam: ILike("myValue"),
},
],
skip: 0,
......@@ -777,7 +776,7 @@ describe("ModelUtils Tests", () => {
it("Can build search query with multiple params with same name.", () => {
const request: any = {};
request.query = {
param: ["Eq(myValue)", "Not(myValue2)", "Like(myValue3)"],
param: ["Eq(myValue)", "Not(myValue2)", "like(myValue3)"],
};
const query = ModelUtils.buildSearchQuerySQL(undefined, request.params, request.query, true, request.user);
......@@ -790,7 +789,7 @@ describe("ModelUtils Tests", () => {
param: Not("myValue2"),
},
{
param: Like("myValue3"),
param: ILike("myValue3"),
},
],
skip: 0,
......@@ -820,7 +819,7 @@ describe("ModelUtils Tests", () => {
param3: Equal("hello"),
},
{
param: Like("myValue3"),
param: ILike("myValue3"),
param2: Between(0, 100),
param3: Equal("hello"),
},
......
......@@ -81,8 +81,8 @@ class UserRoute extends ModelRoute<UserModel> {
}
@Delete()
protected async truncate(@User user?: any): Promise<void> {
await super.doTruncate(user);
protected async truncate(@Param() params: any, @Query() query: any, @User user?: any): Promise<void> {
await super.doTruncate(params, query, user);
}
@Put(":id")
......
......@@ -58,8 +58,8 @@ class ItemRoute extends ModelRoute<Item> {
}
@Delete()
protected async truncate(@User user?: any): Promise<void> {
await super.doTruncate(user);
protected async truncate(@Param() params: any, @Query() query: any, @User user?: any): Promise<void> {
await super.doTruncate(params, query, user);
}
@Put(":id")
......
......@@ -82,8 +82,8 @@ class UserRoute extends ModelRoute<UserModel> {
}
@Delete()
protected async truncate(@User user?: any): Promise<void> {
await super.doTruncate(user);
protected async truncate(@Param() params: any, @Query() query: any, @User user?: any): Promise<void> {
await super.doTruncate(params, query, user);
}
@Put(":id")
......
......@@ -112,8 +112,8 @@ class UserWithACLRoute extends ModelRoute<UserModel> {
}
@Delete()
protected async truncate(@User user?: any): Promise<void> {
await super.doTruncate(user);
protected async truncate(@Param() params: any, @Query() query: any, @User user?: any): Promise<void> {
await super.doTruncate(params, query, user);
}
@Put(":id")
......
......@@ -90,8 +90,8 @@ class VersionedUserRoute extends ModelRoute<UserModel> {
}
@Delete()
protected async truncate(@User user?: any): Promise<void> {
await super.doTruncate(user);
protected async truncate(@Param() params: any, @Query() query: any, @User user?: any): Promise<void> {
await super.doTruncate(params, query, user);
}
@Put(":id")
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment