Skip to content

Commit 4f15a5d

Browse files
author
David A
authored
feat(sdk): 1882 complete google datastore store integration and tests (#3313)
* feat(sdk): 1882 complete google datastore store integration and tests Signed-off-by: David Adams <[email protected]> * improve(sdk): stricter gstore tests, minor updates Signed-off-by: David Adams <[email protected]>
1 parent 54cabe8 commit 4f15a5d

File tree

4 files changed

+174
-34
lines changed

4 files changed

+174
-34
lines changed

packages/sdk/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,14 @@
3838
],
3939
"dependencies": {
4040
"@uma/core": "^2.7.0",
41-
"ethers": "^5.4.2"
41+
"ethers": "^5.4.2",
42+
"highland": "^2.13.5"
4243
},
4344
"devDependencies": {
45+
"@types/highland": "^2.12.13",
4446
"@ethersproject/abi": "^5.4.0",
45-
"@ethersproject/contracts": "^5.4.0",
4647
"@ethersproject/abstract-provider": "^5.4.0",
48+
"@ethersproject/contracts": "^5.4.0",
4749
"@ethersproject/providers": "^5.4.2",
4850
"@size-limit/preset-small-lib": "^4.10.2",
4951
"size-limit": "^4.10.2",

packages/sdk/src/stores/google-datastore/store.e2e.ts

Lines changed: 118 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,142 @@ import Store from ".";
33

44
import { Datastore } from "@google-cloud/datastore";
55

6-
// TODO: fix e2e tests, we need to skip this for now, e2e tests are broken
7-
describe.skip("google-store", function () {
6+
type Price = {
7+
id?: string;
8+
timestamp: number;
9+
identifier: string;
10+
price: number;
11+
};
12+
function makeId(data: Price) {
13+
return [data.identifier, data.timestamp.toString().padStart(4, "0")].join("!");
14+
}
15+
function sortPrices(a: Price, b: Price) {
16+
return makeId(a) <= makeId(b) ? -1 : 1;
17+
}
18+
const prices: Price[] = [
19+
{ timestamp: 10, identifier: "a", price: 100 },
20+
{ timestamp: 11, identifier: "a", price: 99 },
21+
{ timestamp: 9, identifier: "a", price: 98 },
22+
{ timestamp: 2, identifier: "a", price: 82 },
23+
{ timestamp: 13, identifier: "a", price: 101 },
24+
25+
{ timestamp: 10, identifier: "b", price: 1000 },
26+
{ timestamp: 11, identifier: "b", price: 990 },
27+
{ timestamp: 9, identifier: "b", price: 980 },
28+
{ timestamp: 2, identifier: "b", price: 820 },
29+
{ timestamp: 13, identifier: "b", price: 1001 },
30+
];
31+
const sortedPrices = [...prices].sort(sortPrices);
32+
33+
describe("google-store", function () {
834
let store: any;
935
let datastore: Datastore;
1036
test("init", function () {
1137
datastore = new Datastore();
12-
store = Store("testing", datastore);
38+
store = Store("testing-prices", datastore);
1339
assert(store);
1440
});
15-
test("delete", async function () {
41+
test("clear", async function () {
1642
try {
17-
await store.delete("test");
43+
await store.clear();
1844
} catch (err) {
1945
// do nothing
2046
}
2147
});
22-
test("set", async function () {
23-
await store.set("test", { testing: true });
48+
test("add prices", async () => {
49+
for (const price of prices) {
50+
await store.set(makeId(price), price);
51+
}
52+
});
53+
test("entries", async function () {
54+
const result = await store.entries();
55+
assert.equal(result.length, prices.length);
56+
});
57+
test("keys", async function () {
58+
const result = await store.keys();
59+
assert.equal(result.length, prices.length);
60+
result.forEach((id: string, i: number) => {
61+
assert.equal(id, makeId(sortedPrices[i]));
62+
});
63+
});
64+
test("values", async function () {
65+
const result = await store.values();
66+
assert.equal(result.length, prices.length);
67+
result.forEach((price: Price, i: number) => {
68+
assert.equal(price.identifier, sortedPrices[i].identifier);
69+
assert.equal(price.timestamp, sortedPrices[i].timestamp);
70+
assert.equal(price.price, sortedPrices[i].price);
71+
});
2472
});
2573
test("get", async function () {
26-
const result = await store.get("test");
27-
assert.deepEqual(result, { testing: true });
74+
const result = await store.get(makeId(prices[0]));
75+
assert.deepEqual(result, prices[0]);
2876
});
2977
test("has", async function () {
30-
const result = await store.has("test");
78+
let result = await store.has(makeId(prices[0]));
3179
assert.equal(result, true);
80+
81+
result = await store.has("dne");
82+
assert.equal(result, false);
83+
});
84+
// not supported
85+
test("size", async function () {
86+
try {
87+
await store.size();
88+
} catch (err) {
89+
assert.ok(err);
90+
}
91+
});
92+
test("between a", async () => {
93+
const result = await store.between("a", "b");
94+
const answer = sortedPrices.filter((x) => x.identifier === "a");
95+
assert.equal(result.length, 5);
96+
result.forEach((price: Price, i: number) => {
97+
assert.equal(price.identifier, answer[i].identifier);
98+
assert.equal(price.timestamp, answer[i].timestamp);
99+
assert.equal(price.price, answer[i].price);
100+
});
101+
});
102+
test("between b", async () => {
103+
const result = await store.between("b", "b~");
104+
const answer = sortedPrices.filter((x) => x.identifier === "b");
105+
assert.equal(result.length, 5);
106+
result.forEach((price: Price, i: number) => {
107+
assert.equal(price.identifier, answer[i].identifier);
108+
assert.equal(price.timestamp, answer[i].timestamp);
109+
assert.equal(price.price, answer[i].price);
110+
});
111+
});
112+
test("slice a", async () => {
113+
const len = 4;
114+
const result = await store.slice("a", len);
115+
assert.equal(result.length, len);
116+
117+
const answer = sortedPrices.filter((x) => x.identifier === "a").slice(0, len);
118+
result.forEach((price: Price, i: number) => {
119+
assert.equal(price.identifier, answer[i].identifier);
120+
assert.equal(price.timestamp, answer[i].timestamp);
121+
assert.equal(price.price, answer[i].price);
122+
});
123+
});
124+
test("slice b", async () => {
125+
const len = 3;
126+
const result = await store.slice("b", len);
127+
assert.equal(result.length, len);
128+
129+
const answer = sortedPrices.filter((x) => x.identifier === "b").slice(0, len);
130+
result.forEach((price: Price, i: number) => {
131+
assert.equal(price.identifier, answer[i].identifier);
132+
assert.equal(price.timestamp, answer[i].timestamp);
133+
assert.equal(price.price, answer[i].price);
134+
});
32135
});
33136
test("delete", async function () {
34-
await store.delete("test");
35-
const result = await store.has("test");
137+
await store.delete(makeId(prices[0]));
138+
const result = await store.has(makeId(prices[0]));
36139
assert.equal(result, false);
37140
});
141+
test("clear", async function () {
142+
await store.clear();
143+
});
38144
});

packages/sdk/src/stores/google-datastore/store.ts

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
1+
import { exists } from "../../utils";
12
import type { SortedStore } from "..";
2-
import type { Datastore, Key } from "@google-cloud/datastore";
3+
import { Datastore, Key } from "@google-cloud/datastore";
4+
import highland from "highland";
35

46
// TODO: this is a work in progress which will be completed during datastore integration.
57
export default function <D>(kind: string, store: Datastore): SortedStore<string, D> {
68
function makeKey(id: string): Key {
79
return store.key([kind, id]);
810
}
11+
// return value or undefined if it doesnt exist
912
async function get(id: string) {
1013
try {
11-
const [first] = await store.get(makeKey(id));
12-
return first;
14+
const [result] = await store.get(makeKey(id));
15+
return result;
1316
} catch (err) {
1417
return;
1518
}
@@ -22,37 +25,59 @@ export default function <D>(kind: string, store: Datastore): SortedStore<string,
2225
});
2326
}
2427
async function has(id: string) {
25-
try {
26-
await get(id);
27-
return true;
28-
} catch (err) {
29-
return false;
30-
}
28+
// horribly ineficient to actually query data to see if it exists, but cannot find a better way
29+
return exists(await get(id));
3130
}
3231
async function del(id: string) {
3332
await store.delete(makeKey(id));
3433
}
3534
async function entries() {
36-
return [];
35+
const [results] = await store.createQuery(kind).run();
36+
return results.map((result) => {
37+
return [result[store.KEY].name, result];
38+
}) as [string, D][];
3739
}
3840
async function values() {
39-
return [];
41+
const [results] = await store.createQuery(kind).run();
42+
return results;
4043
}
44+
// all this does is map over full entries to return the id of the entry. have not found a better way to query this.
4145
async function keys() {
42-
return [];
46+
const [results] = await store.createQuery(kind).select("__key__").run();
47+
return results.map((result) => {
48+
return result[store.KEY].name;
49+
}) as string[];
4350
}
44-
async function size() {
45-
return 0;
51+
// theres no way to really do built into the store client. Google recommends managing a size entry yourself.
52+
async function size(): Promise<number> {
53+
throw new Error("size not supported in google store");
4654
}
47-
// TODO: implement these functions
55+
// this actually queries all values, then batches them to delete N at a time. This is a horrible way
56+
// to do this, but I could not find a better way to "drop" all kinds from the table.
4857
async function clear() {
49-
throw new Error("clear is not implemented");
58+
await highland(store.createQuery(kind).runStream())
59+
.map((val: any) => {
60+
return val[store.KEY];
61+
})
62+
.batch(500)
63+
.map(async (keys) => {
64+
return store.delete(keys);
65+
})
66+
.flatMap(highland)
67+
.collect()
68+
.toPromise(Promise);
5069
}
51-
async function slice() {
52-
return [];
70+
async function slice(id: string, length: number) {
71+
const [result] = await store.createQuery(kind).filter("__key__", ">=", makeKey(id)).limit(length).run();
72+
return result;
5373
}
54-
async function between() {
55-
return [];
74+
async function between(a: string, b: string) {
75+
const [result] = await store
76+
.createQuery(kind)
77+
.filter("__key__", ">=", makeKey(a))
78+
.filter("__key__", "<", makeKey(b))
79+
.run();
80+
return result;
5681
}
5782

5883
return {

yarn.lock

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6298,6 +6298,13 @@
62986298
dependencies:
62996299
"@types/node" "*"
63006300

6301+
"@types/highland@^2.12.13":
6302+
version "2.12.13"
6303+
resolved "https://registry.yarnpkg.com/@types/highland/-/highland-2.12.13.tgz#ec41635d9b4b7305c6ea41b7e4a37c6598cccfd5"
6304+
integrity sha512-mKESwg+EDt+wJTxl8uIiuwP6Vc0jWoo9XdpT6fLYutw56I63obI+/FTIed3WHXxAjIzkjJOgx4ESS4JuGwFpQw==
6305+
dependencies:
6306+
"@types/node" "*"
6307+
63016308
"@types/hoist-non-react-statics@^3.0.1":
63026309
version "3.3.1"
63036310
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"

0 commit comments

Comments
 (0)