Skip to content

Commit 35c629a

Browse files
authored
feat: Work on Upload Functions (#10)
Get initial upload functions cleaned up and sorted.
1 parent 1f00454 commit 35c629a

29 files changed

+850
-36
lines changed

.eslintrc.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@ const config = {
88
"plugin:@typescript-eslint/stylistic-type-checked",
99
"plugin:prettier/recommended",
1010
],
11-
ignorePatterns: ["**/bundlers/**"],
11+
ignorePatterns: [
12+
"**/bundlers/**",
13+
"**/coverage/**",
14+
"**/dist/**",
15+
"**/node_modules/**",
16+
],
1217
parserOptions: {
1318
ecmaVersion: "latest",
1419
sourceType: "module",

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,5 @@ dist
128128
.yarn/build-state.yml
129129
.yarn/install-state.gz
130130
.pnp.*
131+
132+
.DS_Store

bundlers/vite/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,8 @@
2929
},
3030
"volta": {
3131
"extends": "../../package.json"
32+
},
33+
"engines": {
34+
"node": ">=18.0.0"
3235
}
33-
}
36+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export class FailedFetchError extends Error {
2+
constructor(msg: string) {
3+
super(msg);
4+
}
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export class FailedUploadError extends Error {
2+
constructor(msg: string) {
3+
super(msg);
4+
}
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export class NoUploadTokenError extends Error {
2+
constructor(msg: string) {
3+
super(msg);
4+
}
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export class UploadLimitReachedError extends Error {
2+
constructor(msg: string) {
3+
super(msg);
4+
}
5+
}

packages/bundler-plugin-core/src/index.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { type UnpluginOptions, createUnplugin } from "unplugin";
22
import { satisfies } from "semver";
3-
import { z } from "zod";
43

54
import {
65
type BundleAnalysisUploadPlugin,
@@ -34,12 +33,6 @@ export function codecovUnpluginFactory({
3433
}
3534

3635
if (userOptions?.enableBundleAnalysis) {
37-
const statsFileName = z
38-
.string()
39-
.endsWith(".json")
40-
.optional()
41-
.parse(userOptions?.statsFileName);
42-
4336
const output: Output = {
4437
version: "1",
4538
};
@@ -48,7 +41,6 @@ export function codecovUnpluginFactory({
4841
const { pluginVersion, version, ...pluginOpts } =
4942
bundleAnalysisUploadPlugin({
5043
output,
51-
statsFileName,
5244
uploaderOverrides: userOptions?.uploaderOverrides,
5345
});
5446

packages/bundler-plugin-core/src/types.ts

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,59 @@ export interface Output {
4646

4747
export interface BundleAnalysisUploadPluginArgs {
4848
output: Output;
49-
statsFileName?: string;
5049
uploaderOverrides?: UploadOverrides;
5150
}
5251

5352
export interface Options {
54-
statsFileName?: string;
53+
/**
54+
* The upload token to use for uploading the bundle analysis information.
55+
*
56+
* `globalUploadToken` and `repoName` must be set if this is not set.
57+
*/
58+
globalUploadToken?: string;
59+
60+
/**
61+
* The name of the repository to upload the bundle analysis information to.
62+
*
63+
* `globalUploadToken` and `repoName` must be set if this is not set.
64+
*/
65+
repoName?: string;
66+
67+
/**
68+
* The upload token to use for uploading the bundle analysis information.
69+
*
70+
* Mutually exclusive to using `globalUploadToken` and `repoName`.
71+
*/
72+
repoToken?: string;
73+
74+
/**
75+
* The commit hash to use for uploading the bundle analysis information.
76+
*
77+
* Defaults package.json name field.
78+
*/
79+
namespace?: string;
80+
81+
// TODO: Update the default value here
82+
/**
83+
* The api url used to fetch the upload url.
84+
*
85+
* Only required if self-hosting codecov.
86+
*
87+
* Defaults to `https://api.codecov.io`.
88+
*/
89+
apiUrl?: string;
90+
91+
/**
92+
* The amount of times the upload function will retry.
93+
*
94+
* Defaults to 3
95+
*/
96+
retryCount?: number;
97+
98+
/** Whether you would like bundle analysis to be enabled. */
5599
enableBundleAnalysis?: boolean;
100+
101+
/** Override values for passing custom information to API. */
56102
uploaderOverrides?: UploadOverrides;
57103
}
58104

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import { HttpResponse, http } from "msw";
2+
import { setupServer } from "msw/node";
3+
4+
import { fetchWithRetry } from "../fetchWithRetry";
5+
6+
jest.mock("@/utils/delay");
7+
8+
const server = setupServer();
9+
10+
beforeAll(() => {
11+
server.listen();
12+
});
13+
14+
afterEach(() => {
15+
server.resetHandlers();
16+
});
17+
18+
afterAll(() => {
19+
server.close();
20+
});
21+
22+
interface SetupArgs {
23+
status?: number;
24+
data?: object;
25+
retryCount?: number;
26+
sendError?: boolean;
27+
}
28+
29+
describe("fetchWithRetry", () => {
30+
let consoleSpy: jest.SpyInstance;
31+
32+
afterEach(() => {
33+
consoleSpy.mockReset();
34+
});
35+
36+
function setup({
37+
status = 200,
38+
data = {},
39+
sendError = false,
40+
retryCount = 0,
41+
}: SetupArgs) {
42+
consoleSpy = jest.spyOn(console, "log").mockImplementation(() => null);
43+
44+
server.use(
45+
http.all("http://localhost", ({}) => {
46+
if (retryCount === 0 && !sendError) {
47+
return HttpResponse.json(data, { status });
48+
}
49+
retryCount -= 1;
50+
return HttpResponse.error();
51+
}),
52+
);
53+
}
54+
55+
describe('when the initial response is "success"', () => {
56+
it("returns the pre-signed URL", async () => {
57+
setup({
58+
data: { url: "http://example.com" },
59+
retryCount: 0,
60+
});
61+
62+
const urlPromise = await fetchWithRetry({
63+
url: "http://localhost",
64+
requestData: {},
65+
retryCount: 3,
66+
});
67+
const data = (await urlPromise.json()) as { url: string };
68+
69+
expect(data?.url).toBe("http://example.com");
70+
});
71+
});
72+
73+
describe('when the initial response is "retryable"', () => {
74+
it("returns the pre-signed URL after retrying", async () => {
75+
setup({
76+
data: { url: "http://example.com" },
77+
retryCount: 2,
78+
});
79+
80+
const urlPromise = await fetchWithRetry({
81+
url: "http://localhost",
82+
requestData: {},
83+
retryCount: 3,
84+
});
85+
const data = (await urlPromise.json()) as { url: string };
86+
87+
expect(data?.url).toBe("http://example.com");
88+
});
89+
});
90+
91+
describe("retry count exceeds limit", () => {
92+
it("throws an error", async () => {
93+
setup({
94+
data: { url: "http://example.com" },
95+
retryCount: 2,
96+
sendError: true,
97+
});
98+
99+
let error;
100+
try {
101+
await fetchWithRetry({
102+
url: "http://localhost",
103+
requestData: {},
104+
retryCount: 1,
105+
});
106+
} catch (e) {
107+
error = e;
108+
}
109+
expect(error).toBeInstanceOf(TypeError);
110+
});
111+
});
112+
});

0 commit comments

Comments
 (0)