Skip to content

Commit 6019818

Browse files
aspladypflandrejpk
andauthored
Use PartitonKey In update/Delete queries (#60)
* updating to use requestOptions partition key instead of id as partition key * added unit testing for partitionKey * upgraded peerDependencies: @azure/cosmos Co-authored-by: Andrej Kyselica <[email protected]>
1 parent c3d2b27 commit 6019818

6 files changed

Lines changed: 229 additions & 11 deletions

File tree

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
"ync": "0.0.9"
4242
},
4343
"peerDependencies": {
44-
"@azure/cosmos": "3.10.3"
44+
"@azure/cosmos": "^3.10.3"
4545
},
4646
"dependencies": {
4747
"apollo-datasource": "0.8.0",

src/datasource.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,30 +76,30 @@ export class CosmosDataSource<TData extends { id: string }, TContext>
7676
return response;
7777
}
7878

79-
async deleteOne(id: string) {
79+
async deleteOne(id: string, partitionKey?: string) {
8080
this.options?.logger?.info(
8181
`CosmosDataSource/deleteOne: deleting id: '${id}'`
8282
);
83-
const response = await this.container.item(id, id).delete<TData>();
83+
const response = await this.container.item(id, partitionKey).delete<TData>();
8484
await this.deleteFromCacheById(id);
8585
return response;
8686
}
8787

88-
async updateOne(updDoc: TData) {
88+
async updateOne(updDoc: TData, partitionKey?: string) {
8989
const response = await this.container
90-
.item(updDoc.id, updDoc.id)
90+
.item(updDoc.id, partitionKey)
9191
.replace(updDoc);
9292
if (response.resource) {
9393
this.primeLoader(response.resource);
9494
}
9595
return response;
9696
}
9797

98-
async updateOnePartial(id: string, contents: Partial<TData>) {
98+
async updateOnePartial(id: string, contents: Partial<TData>, partitionKey?: string) {
9999
this.options?.logger?.debug(
100100
`Updating doc id ${id} contents: ${JSON.stringify(contents, null, "")}`
101101
);
102-
const item = this.container.item(id, id);
102+
const item = this.container.item(id, partitionKey);
103103
const docItem = await item.read<TData>();
104104
const { resource } = docItem;
105105
const newResource = { ...resource, ...contents, id } as TData; // don't change the ID ever

tests/integration.test.ts

Lines changed: 190 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import withCosmosDb from "./withCosmosDb";
1+
import withCosmosDb, {CosmosDB} from "./withCosmosDb";
22
import { expect } from "chai";
33

44
import { CosmosDataSource } from "../src/datasource";
@@ -8,6 +8,7 @@ process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
88
interface UserDoc {
99
id: string;
1010
email: string;
11+
partitionKey?: string;
1112
}
1213

1314
interface Context {
@@ -67,3 +68,191 @@ describe("basic crud", () => {
6768
})
6869
);
6970
});
71+
72+
describe("partitionKey crud tests",function() {
73+
let cosmosDB: CosmosDB
74+
75+
before(async function () {
76+
cosmosDB = new CosmosDB();
77+
await cosmosDB.createDatabase();
78+
})
79+
80+
after(function() {
81+
cosmosDB.close();
82+
})
83+
describe("with a partitionKey", function() {
84+
let userDataSource: UserDataSource
85+
86+
beforeEach(async function () {
87+
const { container } = await cosmosDB.createContainer({
88+
id: "test-collection",
89+
partitionKey: "/partitionKey",
90+
});
91+
92+
userDataSource = new UserDataSource(container);
93+
userDataSource.initialize({});
94+
})
95+
96+
afterEach(async function () {
97+
userDataSource.container.delete();
98+
})
99+
100+
it("should create and find a user", async function() {
101+
const user1 = {
102+
id: "us_one",
103+
104+
partitionKey: "examplePartitionKey",
105+
};
106+
107+
// create the user
108+
const user1Resp = await userDataSource.createOne(user1);
109+
expect(user1Resp.resource?.id).to.equal("us_one");
110+
111+
// read the user by ID
112+
const user1Find1Resp = await userDataSource.findOneById(user1.id);
113+
expect(user1Find1Resp?.email).to.equal(user1.email);
114+
expect(user1Find1Resp?.id).to.equal(user1.id);
115+
})
116+
117+
it("should create and update a user and find with query", async function() {
118+
const user1 = {
119+
id: "us_one",
120+
121+
partitionKey: "examplePartitionKey",
122+
};
123+
124+
// create the user
125+
const user1Resp = await userDataSource.createOne(user1);
126+
expect(user1Resp.resource?.id).to.equal("us_one");
127+
128+
// update the user
129+
const newEmail = "[email protected]";
130+
await userDataSource.updateOne({
131+
...user1,
132+
email: newEmail,
133+
});
134+
135+
// read the user with a query
136+
const user1FindQueryResp = await userDataSource.findManyByQuery(
137+
`select * from c where c.id = '${user1.id}'`
138+
);
139+
expect(user1FindQueryResp.resources.length).to.equal(1);
140+
expect(user1FindQueryResp.resources[0].email).to.equal(newEmail);
141+
})
142+
143+
it("should create and delete user", async function() {
144+
const user1 = {
145+
id: "us_one",
146+
147+
partitionKey: "examplePartitionKey",
148+
};
149+
150+
// create the user
151+
const user1Resp = await userDataSource.createOne(user1);
152+
expect(user1Resp.resource?.id).to.equal("us_one");
153+
154+
// delete the user
155+
await userDataSource.deleteOne(user1.id, user1.partitionKey);
156+
157+
// try to read the user back out (shouldn't exist)
158+
const user1Find1Resp2 = await userDataSource.findOneById(user1.id);
159+
expect(user1Find1Resp2).to.equal(undefined);
160+
})
161+
162+
it("should create and not delete user because partitionKey isn't provided", async function() {
163+
const user1 = {
164+
id: "us_one",
165+
166+
partitionKey: "examplePartitionKey",
167+
};
168+
169+
// create the user
170+
const user1Resp = await userDataSource.createOne(user1);
171+
expect(user1Resp.resource?.id).to.equal("us_one");
172+
173+
// delete the user will fail because partitionKey isn't provided
174+
userDataSource.deleteOne(user1.id);
175+
176+
const user1Find1Resp2 = await userDataSource.findOneById(user1.id);
177+
178+
expect(user1Find1Resp2.id).to.equal(user1.id);
179+
expect(user1Find1Resp2.email).to.equal(user1.email);
180+
expect(user1Find1Resp2.partitionKey).to.equal(user1.partitionKey);
181+
})
182+
})
183+
describe("without a partitionKey", function() {
184+
let userDataSource: UserDataSource
185+
186+
beforeEach(async function () {
187+
const { container } = await cosmosDB.createContainer({
188+
id: "test-collection",
189+
});
190+
191+
userDataSource = new UserDataSource(container);
192+
userDataSource.initialize({});
193+
})
194+
195+
afterEach(async function () {
196+
userDataSource.container.delete();
197+
})
198+
199+
it("should create and find a user", async function() {
200+
const user1 = {
201+
id: "us_one",
202+
203+
};
204+
205+
// create the user
206+
const user1Resp = await userDataSource.createOne(user1);
207+
expect(user1Resp.resource?.id).to.equal("us_one");
208+
209+
// read the user by ID
210+
const user1Find1Resp = await userDataSource.findOneById(user1.id);
211+
expect(user1Find1Resp?.email).to.equal(user1.email);
212+
expect(user1Find1Resp?.id).to.equal(user1.id);
213+
})
214+
215+
it("should create and update a user and find with query", async function() {
216+
const user1 = {
217+
id: "us_one",
218+
219+
};
220+
221+
// create the user
222+
const user1Resp = await userDataSource.createOne(user1);
223+
expect(user1Resp.resource?.id).to.equal("us_one");
224+
225+
// update the user
226+
const newEmail = "[email protected]";
227+
await userDataSource.updateOne({
228+
...user1,
229+
email: newEmail,
230+
});
231+
232+
// read the user with a query
233+
const user1FindQueryResp = await userDataSource.findManyByQuery(
234+
`select * from c where c.id = '${user1.id}'`
235+
);
236+
expect(user1FindQueryResp.resources.length).to.equal(1);
237+
expect(user1FindQueryResp.resources[0].email).to.equal(newEmail);
238+
})
239+
240+
it("should create and delete user", async function() {
241+
const user1 = {
242+
id: "us_one",
243+
244+
};
245+
246+
// create the user
247+
const user1Resp = await userDataSource.createOne(user1);
248+
expect(user1Resp.resource?.id).to.equal("us_one");
249+
250+
// delete the user
251+
await userDataSource.deleteOne(user1.id);
252+
253+
// try to read the user back out (shouldn't exist)
254+
const user1Find1Resp2 = await userDataSource.findOneById(user1.id);
255+
expect(user1Find1Resp2).to.equal(undefined);
256+
})
257+
})
258+
})

tests/withCosmosDb.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { CosmosClient } from "@azure/cosmos";
1+
import { CosmosClient, Database, ContainerRequest } from "@azure/cosmos";
22
import * as net from "net";
33
import cosmosDBServer from "@zeit/cosmosdb-server";
44

@@ -27,3 +27,32 @@ export default function withCosmosDBServer<R, T extends []>(
2727
}
2828
};
2929
}
30+
31+
export class CosmosDB {
32+
#server: net.Server
33+
#database: Database
34+
#client: CosmosClient
35+
36+
constructor() {
37+
this.#server = cosmosDBServer();
38+
this.#server.listen(0)
39+
const { port } = this.#server.address() as net.AddressInfo;
40+
this.#client = new CosmosClient({
41+
endpoint: `https://localhost:${port}`,
42+
key: "test-master-key",
43+
});
44+
}
45+
async createDatabase(dbId = "test-database") {
46+
this.#database = (await this.#client.databases.create({
47+
id: dbId,
48+
})).database;
49+
}
50+
async createContainer(containerRequest: ContainerRequest) {
51+
return await this.#database.containers.create(
52+
containerRequest
53+
);
54+
}
55+
close() {
56+
this.#server.close();
57+
}
58+
}

yarn.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3957,4 +3957,4 @@ [email protected]:
39573957
yocto-queue@^0.1.0:
39583958
version "0.1.0"
39593959
resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz"
3960-
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
3960+
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==

0 commit comments

Comments
 (0)