1
0

Changed how data is stored quite a bit with nice query capabilities

This commit is contained in:
Zef Hemel
2022-05-17 15:54:55 +02:00
parent 9a6a86f8b5
commit dfd820cc25
21 changed files with 256 additions and 132 deletions
+3 -3
View File
@@ -93,19 +93,19 @@ test("Run a Node sandbox", async () => {
await plug.invoke("errorOut", []);
expect(true).toBe(false);
} catch (e: any) {
expect(e.message).toBe("BOOM");
expect(e.message).toContain("BOOM");
}
try {
await plug.invoke("errorOutSys", []);
expect(true).toBe(false);
} catch (e: any) {
expect(e.message).toBe("#fail");
expect(e.message).toContain("#fail");
}
try {
await plug.invoke("restrictedTest", []);
expect(true).toBe(false);
} catch (e: any) {
expect(e.message).toBe(
expect(e.message).toContain(
"Missing permission 'restricted' for syscall restrictedSyscall"
);
}
@@ -15,7 +15,8 @@ test("Test store", async () => {
});
await ensureTable(db, "test_table");
let system = new System("server");
system.registerSyscalls([], storeSyscalls(db, "test_table"));
let syscalls = storeSyscalls(db, "test_table");
system.registerSyscalls([], syscalls);
let plug = await system.load(
{
name: "test",
@@ -36,5 +37,79 @@ test("Test store", async () => {
);
expect(await plug.invoke("test1", [])).toBe("Pete");
await system.unloadAll();
let dummyCtx: any = {};
await syscalls["store.deleteAll"](dummyCtx);
await syscalls["store.batchSet"](dummyCtx, [
{
key: "pete",
value: {
age: 20,
firstName: "Pete",
lastName: "Roberts",
},
},
{
key: "petejr",
value: {
age: 8,
firstName: "Pete Jr",
lastName: "Roberts",
},
},
{
key: "petesr",
value: {
age: 78,
firstName: "Pete Sr",
lastName: "Roberts",
},
},
]);
let allRoberts = await syscalls["store.query"](dummyCtx, {
filter: [{ op: "=", prop: "lastName", value: "Roberts" }],
orderBy: "age",
orderDesc: true,
});
expect(allRoberts.length).toBe(3);
expect(allRoberts[0].key).toBe("petesr");
allRoberts = await syscalls["store.query"](dummyCtx, {
filter: [{ op: "=", prop: "lastName", value: "Roberts" }],
orderBy: "age",
limit: 1,
});
expect(allRoberts.length).toBe(1);
expect(allRoberts[0].key).toBe("petejr");
allRoberts = await syscalls["store.query"](dummyCtx, {
filter: [
{ op: ">", prop: "age", value: 10 },
{ op: "<", prop: "age", value: 30 },
],
orderBy: "age",
});
expect(allRoberts.length).toBe(1);
expect(allRoberts[0].key).toBe("pete");
// Delete the middle one
await syscalls["store.deleteQuery"](dummyCtx, {
filter: [
{ op: ">", prop: "age", value: 10 },
{ op: "<", prop: "age", value: 30 },
],
});
allRoberts = await syscalls["store.query"](dummyCtx, {});
expect(allRoberts.length).toBe(2);
await db.destroy();
await fs.unlink("test.db");
});
@@ -24,6 +24,45 @@ export async function ensureTable(db: Knex<any, unknown>, tableName: string) {
}
}
export type Query = {
filter?: Filter[];
orderBy?: string;
orderDesc?: boolean;
limit?: number;
select?: string[];
};
export type Filter = {
op: string;
prop: string;
value: any;
};
export function queryToKnex(
queryBuilder: Knex.QueryBuilder<Item, any>,
query: Query
): Knex.QueryBuilder<Item, any> {
if (query.filter) {
for (let filter of query.filter) {
queryBuilder = queryBuilder.andWhereRaw(
`json_extract(value, '$.${filter.prop}') ${filter.op} ?`,
[filter.value]
);
}
}
if (query.limit) {
queryBuilder = queryBuilder.limit(query.limit);
}
if (query.orderBy) {
queryBuilder = queryBuilder.orderByRaw(
`json_extract(value, '$.${query.orderBy}') ${
query.orderDesc ? "desc" : "asc"
}`
);
}
return queryBuilder;
}
export function storeSyscalls(
db: Knex<any, unknown>,
tableName: string
@@ -35,6 +74,9 @@ export function storeSyscalls(
"store.deletePrefix": async (ctx, prefix: string) => {
return db<Item>(tableName).andWhereLike("key", `${prefix}%`).del();
},
"store.deleteQuery": async (ctx, query: Query) => {
await queryToKnex(db<Item>(tableName), query).del();
},
"store.deleteAll": async (ctx) => {
await db<Item>(tableName).del();
},
@@ -78,6 +120,14 @@ export function storeSyscalls(
value: JSON.parse(value),
}));
},
"store.query": async (ctx, query: Query) => {
return (
await queryToKnex(db<Item>(tableName), query).select("key", "value")
).map(({ key, value }: { key: string; value: string }) => ({
key,
value: JSON.parse(value),
}));
},
};
return apiObj;
}