Skip to content

Commit 41696f6

Browse files
🚧Fix update repo file bug (#27)
* Start work for issue #26 * fix: fix a bug where updating an existing file in the repo throws an error 6fb007 * feat: add various funcs to manage https headers * chore: improve path processing for fileExists() & updateFile funcs * test: added unit tests for fileExists & updateFile funcs * release: update release notes for version v1.0.0-preview.6 * release: update version from v1.0.0-preview.5 to v1.0.0-preview.6 * release: improve release notes * ci: improve version puller * ide: add comments to launch configs
1 parent d673bb0 commit 41696f6

File tree

10 files changed

+339
-43
lines changed

10 files changed

+339
-43
lines changed

.github/cicd/core/VersionPuller.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Utils } from "../../../core/Utils.ts";
2-
import { Directory } from "../core/Directory.ts";
2+
import { walkSync } from "../../../deps.ts";
33

44
/**
55
* Pulls the version from a json file.
@@ -9,10 +9,20 @@ export class VersionPuller {
99
* Pulls the version from a json file.
1010
* @returns The version number.
1111
*/
12-
public getVersion(fileName: string): string {
13-
const denoJsonFilePath = Directory
14-
.getFiles(".", "*", true)
15-
.find(f => f.endsWith(fileName));
12+
public getVersion(fileName: string, baseDirPath?: string): string {
13+
const searchDirPath = Utils.isNothing(baseDirPath) ? Deno.cwd() : baseDirPath;
14+
15+
const entries = walkSync(searchDirPath, {
16+
includeFiles: true,
17+
includeDirs: false,
18+
exts: [".json"],
19+
match: [/.*deno.json.*/gm],
20+
skip: [/.*node_modules.*/gm]
21+
});
22+
23+
const items = [...entries].map((entry) => entry.path);
24+
25+
const denoJsonFilePath = items.length > 0 ? items[0] : undefined;
1626

1727
if (denoJsonFilePath === undefined) {
1828
const errorMsg = `The file '${fileName}' could not be found when pulling the version number.`;
@@ -30,6 +40,6 @@ export class VersionPuller {
3040
Deno.exit(1);
3141
}
3242

33-
return jsonObj.version;
43+
return jsonObj.version.trim();
3444
}
3545
}

.vscode/launch.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"version": "0.2.0",
33
"configurations": [
4-
{
4+
{ // PLAYGROUND
55
"name": "Playground",
66
"request": "launch",
77
"type": "node",
@@ -29,7 +29,7 @@
2929
"runtimeExecutable": "${userHome}/.deno/bin/deno"
3030
},
3131
},
32-
{
32+
{ // DEBUG TEST TILE
3333
"name": "Debug Test File",
3434
"request": "launch",
3535
"type": "node",
@@ -52,7 +52,7 @@
5252
"runtimeExecutable": "${userHome}/.deno/bin/deno"
5353
},
5454
},
55-
{
55+
{ // VERSION CHECKER
5656
"name": "Version Checker",
5757
"request": "launch",
5858
"type": "node",
@@ -83,7 +83,7 @@
8383
"runtimeExecutable": "${userHome}/.deno/bin/deno"
8484
},
8585
},
86-
{
86+
{ // GET VERSION
8787
"name": "Get Version",
8888
"request": "launch",
8989
"type": "node",
@@ -109,7 +109,7 @@
109109
"runtimeExecutable": "${userHome}/.deno/bin/deno"
110110
},
111111
},
112-
{
112+
{ // CHECK RELEASE NOTES
113113
"name": "Check Release Notes",
114114
"request": "launch",
115115
"type": "node",
@@ -137,8 +137,8 @@
137137
"runtimeExecutable": "${userHome}/.deno/bin/deno"
138138
},
139139
},
140-
{
141-
"name": "Deno Check",
140+
{ // BUILD (DENO CHECK)
141+
"name": "Build (Deno Check)",
142142
"request": "launch",
143143
"type": "node",
144144
"program": "${workspaceFolder}/.github/cicd/scripts/deno-check.ts",

GitHubClients/ReleaseClient.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,8 +215,8 @@ export class ReleaseClient extends GitHubClient {
215215
const queryParams = `?name=${fileName}`;
216216
const url = `${this.baseUrl}/repos/${this.ownerName}/${this.repoName}/releases/${releaseId}/assets${queryParams}`;
217217

218-
this.headers.append("Content-Type", "application/octet-stream");
219-
this.headers.append("Content-Length", file.byteLength.toString());
218+
this.updateOrAdd("Content-Type", "application/octet-stream");
219+
this.updateOrAdd("Content-Length", file.byteLength.toString());
220220

221221
const response = await this.requestPOST(url, file);
222222

GitHubClients/RepoClient.ts

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { decodeBase64, encodeBase64 } from "../deps.ts";
1+
import { decodeBase64, encodeBase64, isAbsolute } from "../deps.ts";
22
import { GitHubHttpStatusCodes } from "../core/Enums.ts";
33
import { GitHubClient } from "../core/GitHubClient.ts";
44
import { Guard } from "../core/Guard.ts";
@@ -149,7 +149,16 @@ export class RepoClient extends GitHubClient {
149149
Guard.isNothing(relativeFilePath, funcName, "relativeFilePath");
150150

151151
relativeFilePath = relativeFilePath.trim();
152-
relativeFilePath = relativeFilePath.startsWith("/") ? relativeFilePath : `/${relativeFilePath}`;
152+
153+
if (isAbsolute(relativeFilePath)) {
154+
const errorMsg = `The relative file path '${relativeFilePath}' is not a valid relative file path.`;
155+
throw new RepoError(errorMsg);
156+
}
157+
158+
if (Utils.isNotFilePath(relativeFilePath)) {
159+
const errorMsg = `The relative file path '${relativeFilePath}' is not a valid directory path.`;
160+
throw new RepoError(errorMsg);
161+
}
153162

154163
const queryParams = `?ref=${branchName}`;
155164
const url = `${this.baseUrl}/repos/${this.ownerName}/${this.repoName}/contents${relativeFilePath}${queryParams}`;
@@ -326,11 +335,20 @@ export class RepoClient extends GitHubClient {
326335
Guard.isNothing(fileContent, funcName, "fileContent");
327336
Guard.isNothing(commitMessage, funcName, "commitMessage");
328337

329-
relativeFilePath = Utils.normalizePath(relativeFilePath);
330-
Utils.trimAllStartingValue("/", relativeFilePath);
338+
relativeFilePath = relativeFilePath.trim();
331339

332-
if (await this.fileExists(branchName, relativeFilePath)) {
333-
const errorMsg = `The file '${relativeFilePath}' already exists in the repository '${this.repoName}'.`;
340+
if (isAbsolute(relativeFilePath)) {
341+
const errorMsg = `The relative file path '${relativeFilePath}' is not a valid relative file path.`;
342+
throw new RepoError(errorMsg);
343+
}
344+
345+
if (Utils.isNotFilePath(relativeFilePath)) {
346+
const errorMsg = `The relative file path '${relativeFilePath}' is not a valid directory path.`;
347+
throw new RepoError(errorMsg);
348+
}
349+
350+
if (!await this.fileExists(branchName, relativeFilePath)) {
351+
const errorMsg = `The file '${relativeFilePath}' does not exist in the repository '${this.repoName}'.`;
334352
throw new RepoError(errorMsg);
335353
}
336354

@@ -370,15 +388,24 @@ export class RepoClient extends GitHubClient {
370388
branchName: string,
371389
relativeFilePath: string,
372390
): Promise<FileContentModel> {
373-
const funcName = "getFileContentWithResult";
391+
const funcName = "getFileContentResult";
374392
Guard.isNothing(branchName, funcName, "branchName");
375393
Guard.isNothing(relativeFilePath, funcName, "relativeFilePath");
376394

377-
relativeFilePath = relativeFilePath.trim();
378-
relativeFilePath = relativeFilePath.startsWith("/") ? relativeFilePath : `/${relativeFilePath}`;
395+
if (isAbsolute(relativeFilePath)) {
396+
const errorMsg = `The relative file path '${relativeFilePath}' is not a valid relative file path.`;
397+
throw new RepoError(errorMsg);
398+
}
399+
400+
if (Utils.isNotFilePath(relativeFilePath)) {
401+
const errorMsg = `The relative file path '${relativeFilePath}' is not a valid directory path.`;
402+
throw new RepoError(errorMsg);
403+
}
404+
405+
relativeFilePath = relativeFilePath.startsWith("./") ? relativeFilePath.substring(2) : relativeFilePath;
379406

380407
const queryParams = `?ref=${branchName}`;
381-
const url = `${this.baseUrl}/repos/${this.ownerName}/${this.repoName}/contents${relativeFilePath}${queryParams}`;
408+
const url = `${this.baseUrl}/repos/${this.ownerName}/${this.repoName}/contents/${relativeFilePath}${queryParams}`;
382409

383410
const response: Response = await this.requestGET(url);
384411

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<h1 align="center" style="color: mediumseagreen;font-weight: bold;">
2+
kd_clients Preview Release Notes - v1.0.0-preview.6
3+
</h1>
4+
5+
<h2 align="center" style="font-weight: bold;">Quick Reminder</h2>
6+
7+
<div align="center">
8+
9+
As with all software, there is always a chance for issues and bugs, especially for preview releases, which is why your input is greatly appreciated. 🙏🏼
10+
</div>
11+
12+
<h2 align="center" style="font-weight: bold;">New Features ✨</h2>
13+
14+
1. [#27](https://github.com/KinsonDigital/Velaptor/issues/27) - Added the following functions to all GitHub HTTP clients to manage headers.
15+
- `getHeader()`
16+
- `updateOrAdd()`
17+
- `clearHeaders()`
18+
- `containsHeader()`
19+
20+
<h2 align="center" style="font-weight: bold;">Bug Fixes 🐛</h2>
21+
22+
1. [#27](https://github.com/KinsonDigital/Velaptor/issues/27) - Fixed a bug where attempting to update a file in a repository where the file already exists throws an error saying that it already exists.

core/GitHubClient.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ export abstract class GitHubClient extends WebApiClient {
1111
private headerParser: LinkHeaderParser = new LinkHeaderParser();
1212
private _repoName = "";
1313
private _ownerName = "";
14-
protected readonly headers: Headers = new Headers();
1514

1615
/**
1716
* Initializes a new instance of the {@link WebAPIClient} class.
@@ -27,11 +26,11 @@ export abstract class GitHubClient extends WebApiClient {
2726
this.repoName = Utils.isNothing(repoName) ? "" : repoName.trim();
2827

2928
this.baseUrl = "https://api.github.com";
30-
this.headers.append("Accept", "application/vnd.github+json");
31-
this.headers.append("X-GitHub-Api-Version", "2022-11-28");
29+
this.updateOrAdd("Accept", "application/vnd.github+json");
30+
this.updateOrAdd("X-GitHub-Api-Version", "2022-11-28");
3231

3332
if (!Utils.isNothing(token)) {
34-
this.headers.append("Authorization", `Bearer ${token}`);
33+
this.updateOrAdd("Authorization", `Bearer ${token}`);
3534
}
3635
}
3736

@@ -70,7 +69,7 @@ export abstract class GitHubClient extends WebApiClient {
7069
* @returns True if a token was provided; otherwise, false.
7170
*/
7271
protected containsToken(): boolean {
73-
return this.headers.has("Authorization");
72+
return this.containsHeader("Authorization");
7473
}
7574

7675
/**

core/WebApiClient.ts

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Guard } from "./Guard.ts";
44
* Provides a base class for HTTP clients.
55
*/
66
export abstract class WebApiClient {
7-
protected readonly headers: Headers = new Headers();
7+
private readonly headers: Headers = new Headers();
88
protected baseUrl = "";
99

1010
/**
@@ -15,9 +15,13 @@ export abstract class WebApiClient {
1515
protected async getResponseData<T>(response: Response): Promise<T> {
1616
const responseText: string = await response.text();
1717

18-
return this.headers.has("Accept") && this.headers.get("Accept") === "application/vnd.github.v3.raw"
19-
? responseText
20-
: await JSON.parse(responseText);
18+
const acceptHeaderValue = this.getHeader("Accept") ?? "";
19+
20+
return acceptHeaderValue.length > 0 &&
21+
acceptHeaderValue?.startsWith("application/vnd") &&
22+
acceptHeaderValue.includes("json")
23+
? await JSON.parse(responseText)
24+
: responseText;
2125
}
2226

2327
/**
@@ -111,19 +115,45 @@ export abstract class WebApiClient {
111115
}
112116

113117
/**
114-
* Sets an HTTP header with a name that matches the given {@link name}
115-
* to the given {@link value}.
118+
* Gets the value of an HTTP header with a name that matches the given {@link name}.
119+
* @param name The name of the header to get.
120+
* @returns The value of the header.
121+
*/
122+
public getHeader(name: string): string | null {
123+
return this.headers.get(name);
124+
}
125+
126+
/**
127+
* Updates or adds an HTTP header with the given {@link name} and {@link value}.
116128
* @param name The name of the header to set.
117129
* @param value The value of the header to set.
118130
*/
119-
protected setHeader(name: string, value: string): void {
131+
public updateOrAdd(name: string, value: string): void {
120132
if (this.headers.has(name)) {
121133
this.headers.set(name, value);
122134
} else {
123135
this.headers.append(name, value);
124136
}
125137
}
126138

139+
/**
140+
* Clears all of the HTTP headers.
141+
*/
142+
public clearHeaders(): void {
143+
for (const headerName of this.headers.keys()) {
144+
this.headers.delete(headerName);
145+
}
146+
}
147+
148+
/**
149+
* Returns a value indicating whether or not an HTTP header with the given {@link name} exists.
150+
* @param name The name of the header to check.
151+
* @returns True if the header exists, otherwise false.
152+
*/
153+
public containsHeader(name: string): boolean {
154+
return this.headers.has(name);
155+
}
156+
127157
/**
128158
* Builds an error message from the given error message and response.
129159
* @param errorMsg The error message to use.

deno.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"version": "v1.0.0-preview.5",
2+
"version": "v1.0.0-preview.6",
33
"tasks": {
44
"clear": "deno run -A ./.github/deno-tasks/clear-screen.ts",
55
"build": "deno task clear && deno run -A ./.github/cicd/scripts/deno-check.ts",

deps.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// ----IMPORTS----
22

33
// Official Deno Modules
4-
import { exists, existsSync } from "https://deno.land/std@0.203.0/fs/exists.ts";
5-
import { extname, basename } from "https://deno.land/std@0.203.0/path/mod.ts";
4+
import { exists, existsSync, walkSync } from "https://deno.land/std@0.203.0/fs/mod.ts";
5+
import { extname, basename, isAbsolute } from "https://deno.land/std@0.203.0/path/mod.ts";
66
import { decodeBase64, encodeBase64 } from "https://deno.land/std@0.203.0/encoding/base64.ts";
77
import { assert, assertEquals, assertThrows, assertRejects, equal } from "https://deno.land/std@0.204.0/assert/mod.ts";
88
import { assertSpyCall, assertSpyCalls, spy, stub, returnsNext, returnsArg } from "https://deno.land/std@0.204.0/testing/mock.ts";
@@ -14,8 +14,8 @@ import { TwitterApi, TweetV2PostTweetResult } from "npm:twitter-api-v2@1.15.0";
1414
// ----EXPORTS----
1515

1616
// Official Deno Modules
17-
export { exists, existsSync };
18-
export { extname, basename };
17+
export { exists, existsSync, walkSync };
18+
export { extname, basename, isAbsolute };
1919
export { decodeBase64, encodeBase64 };
2020
export { assert, assertEquals, assertThrows, assertRejects, equal };
2121
export { assertSpyCall, assertSpyCalls, spy, stub, returnsNext, returnsArg }

0 commit comments

Comments
 (0)