Skip to content

Commit f89b8f1

Browse files
feat(store-github): check if file exists before creating
1 parent e50e32c commit f89b8f1

File tree

3 files changed

+81
-30
lines changed

3 files changed

+81
-30
lines changed

helpers/mock-agent/store-github.js

+21-7
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,14 @@ export function mockClient() {
1010
agent.enableNetConnect(/(?:127\.0\.0\.1:\d{5})/);
1111

1212
const origin = "https://api.github.com";
13-
const path = /\/repos\/user\/repo\/contents\/\D{3}\.md/;
13+
const path = /\/repos\/user\/repo\/contents\/(foo|bar)\.txt/;
14+
const createNewResponse = {
15+
content: {
16+
path: "new.txt",
17+
html_url: "https://github.com/user/repo/blob/main/new.txt",
18+
},
19+
commit: { message: "Message" },
20+
};
1421
const createResponse = {
1522
content: {
1623
path: "foo.txt",
@@ -26,31 +33,38 @@ export function mockClient() {
2633
commit: { message: "Message" },
2734
};
2835
const readResponse = {
29-
content: "Zm9vYmFy", // ‘foobar’ Base64 encoded
36+
content: "Zm9v", // ‘foo’ Base64 encoded
3037
path: "foo.txt",
3138
sha: "1234",
3239
};
3340
const deleteResponse = {
3441
commit: { message: "Message" },
3542
};
3643

44+
// Create new file
45+
agent
46+
.get(origin)
47+
.intercept({ path: /.*new\.txt/, method: "PUT" })
48+
.reply(201, createNewResponse)
49+
.persist();
50+
3751
// Create/update file
3852
agent
3953
.get(origin)
40-
.intercept({ path: /.*foo\.md/, method: "PUT" })
54+
.intercept({ path: /.*foo\.txt/, method: "PUT" })
4155
.reply(201, createResponse)
4256
.persist();
4357

4458
// Create/update file (Unauthorized)
4559
agent
4660
.get(origin)
47-
.intercept({ path: /.*401\.md/, method: "PUT" })
61+
.intercept({ path: /.*401\.txt/, method: "PUT" })
4862
.reply(401);
4963

5064
// Update and rename file
5165
agent
5266
.get(origin)
53-
.intercept({ path: /.*bar\.md/, method: "PUT" })
67+
.intercept({ path: /.*bar\.txt/, method: "PUT" })
5468
.reply(201, updateResponse)
5569
.persist();
5670

@@ -64,14 +78,14 @@ export function mockClient() {
6478
// Read file (Unauthorized)
6579
agent
6680
.get(origin)
67-
.intercept({ path: /.*401\.md/, method: "GET" })
81+
.intercept({ path: /.*401\.txt/, method: "GET" })
6882
.reply(401)
6983
.persist();
7084

7185
// Read file (Not Found)
7286
agent
7387
.get(origin)
74-
.intercept({ path: /.*404\.md/, method: "GET" })
88+
.intercept({ path: /.*404\.txt/, method: "GET" })
7589
.reply(404)
7690
.persist();
7791

packages/store-github/index.js

+29-5
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,25 @@ export default class GithubStore {
110110
}
111111
}
112112

113+
/**
114+
* Check if file exists
115+
* @param {string} filePath - Path to file
116+
* @returns {Promise<boolean>} File exists
117+
* @see {@link https://docs.github.com/en/rest/repos/contents#get-repository-content}
118+
*/
119+
async fileExists(filePath) {
120+
const { branch, repo } = this.options;
121+
122+
try {
123+
debug(`Try reading file ${filePath} in repo ${repo}, branch ${branch}`);
124+
await this.#client(`${filePath}?ref=${branch}`);
125+
126+
return true;
127+
} catch {
128+
return false;
129+
}
130+
}
131+
113132
/**
114133
* Create file
115134
* @param {string} filePath - Path to file
@@ -124,13 +143,22 @@ export default class GithubStore {
124143

125144
let createResponse;
126145
try {
146+
const fileExists = await this.fileExists(filePath);
147+
if (fileExists) {
148+
return;
149+
}
150+
127151
debug(`Try creating file ${filePath} in repo ${repo}, branch ${branch}`);
128152
createResponse = await this.#client(filePath, "PUT", {
129153
branch,
130154
content: Buffer.from(content).toString("base64"),
131155
message,
132156
});
133-
debug(`Created file ${filePath}`);
157+
debug(`Creating file ${filePath}`);
158+
159+
const file = await createResponse.json();
160+
161+
return file.content.html_url;
134162
} catch (error) {
135163
const message = crudErrorMessage({
136164
error,
@@ -142,10 +170,6 @@ export default class GithubStore {
142170
debug(message);
143171
throw new Error(message);
144172
}
145-
146-
const file = await createResponse.json();
147-
148-
return file.content.html_url;
149173
}
150174

151175
/**

packages/store-github/test/index.js

+31-18
Original file line numberDiff line numberDiff line change
@@ -41,54 +41,67 @@ describe("store-github", async () => {
4141
assert.equal(indiekit.publication.store.info.name, "user/repo on GitHub");
4242
});
4343

44+
it("Checks if file exists", async () => {
45+
assert.equal(await github.fileExists("foo.txt"), true);
46+
assert.equal(await github.fileExists("404.txt"), false);
47+
});
48+
4449
it("Creates file", async () => {
45-
const result = await github.createFile("foo.md", "foobar", {
50+
const result = await github.createFile("new.txt", "new", {
4651
message: "Message",
4752
});
4853

49-
assert.equal(result, "https://github.com/user/repo/blob/main/foo.txt");
54+
assert.equal(result, "https://github.com/user/repo/blob/main/new.txt");
55+
});
56+
57+
it("Doesn’t create file if already exists", async () => {
58+
const result = await github.createFile("foo.txt", "foo", {
59+
message: "Message",
60+
});
61+
62+
assert.equal(result, undefined);
5063
});
5164

5265
it("Throws error creating file", async () => {
5366
await assert.rejects(
54-
github.createFile("401.md", "foobar", { message: "Message" }),
67+
github.createFile("401.txt", "foo", { message: "Message" }),
5568
(error) => {
56-
assert(error.message.includes("Could not create file 401.md"));
69+
assert(error.message.includes("Could not create file 401.txt"));
5770
return true;
5871
},
5972
);
6073
});
6174

6275
it("Reads file", async () => {
63-
assert.equal(await github.readFile("foo.md"), "foobar");
76+
assert.equal(await github.readFile("foo.txt"), "foo");
6477
});
6578

6679
it("Throws error reading file", async () => {
67-
await assert.rejects(github.readFile("404.md"), (error) => {
68-
assert(error.message.includes("Could not read file 404.md"));
80+
await assert.rejects(github.readFile("404.txt"), (error) => {
81+
assert(error.message.includes("Could not read file 404.txt"));
6982
return true;
7083
});
7184
});
7285

7386
it("Updates file", async () => {
74-
const result = await github.updateFile("foo.md", "foobar", {
87+
const result = await github.updateFile("foo.txt", "foo", {
7588
message: "Message",
7689
});
7790

7891
assert.equal(result, "https://github.com/user/repo/blob/main/foo.txt");
7992
});
8093

8194
it("Updates and renames file", async () => {
82-
const result = await github.updateFile("foo.md", "qux", {
95+
const result = await github.updateFile("foo.txt", "qux", {
8396
message: "Message",
84-
newPath: "bar.md",
97+
newPath: "bar.txt",
8598
});
8699

87100
assert.equal(result, "https://github.com/user/repo/blob/main/bar.txt");
88101
});
89102

90103
it("Creates file if original Not Found in repository", async () => {
91-
const result = await github.updateFile("bar.md", "foobar", {
104+
const result = await github.updateFile("bar.txt", "foo", {
92105
message: "Message",
93106
});
94107

@@ -97,33 +110,33 @@ describe("store-github", async () => {
97110

98111
it("Throws error updating file", async () => {
99112
await assert.rejects(
100-
github.updateFile("401.md", "foobar", { message: "Message" }),
113+
github.updateFile("401.txt", "foo", { message: "Message" }),
101114
(error) => {
102-
assert(error.message.includes("Could not read file 401.md"));
115+
assert(error.message.includes("Could not read file 401.txt"));
103116
return true;
104117
},
105118
);
106119
});
107120

108121
it("Deletes a file", async () => {
109-
assert.ok(await github.deleteFile("foo.md", { message: "Message" }));
122+
assert.ok(await github.deleteFile("foo.txt", { message: "Message" }));
110123
});
111124

112125
it("Throws error file Not Found in repository", async () => {
113126
await assert.rejects(
114-
github.deleteFile("404.md", { message: "Message" }),
127+
github.deleteFile("404.txt", { message: "Message" }),
115128
(error) => {
116-
assert(error.message.includes("Could not read file 404.md"));
129+
assert(error.message.includes("Could not read file 404.txt"));
117130
return true;
118131
},
119132
);
120133
});
121134

122135
it.skip("Throws error deleting a file", async () => {
123136
await assert.rejects(
124-
github.deleteFile("401.md", { message: "Message" }),
137+
github.deleteFile("401.txt", { message: "Message" }),
125138
(error) => {
126-
assert(error.message.includes("Could not delete file 401.md"));
139+
assert(error.message.includes("Could not delete file 401.txt"));
127140
return true;
128141
},
129142
);

0 commit comments

Comments
 (0)