@jam-comments/server-utilities 4.1.0 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.js +8 -1
- package/dist/cjs/injectSchema.js +19 -0
- package/dist/cjs/injectSchema.test.js +63 -0
- package/dist/cjs/markupFetcher.js +100 -34
- package/dist/cjs/markupFetcher.test.js +272 -26
- package/dist/cjs/utils.js +79 -1
- package/dist/cjs/utils.test.js +43 -0
- package/dist/esm/index.js +4 -2
- package/dist/esm/injectSchema.js +15 -0
- package/dist/esm/injectSchema.test.js +61 -0
- package/dist/esm/markupFetcher.js +96 -34
- package/dist/esm/markupFetcher.test.js +239 -16
- package/dist/esm/utils.js +71 -0
- package/dist/esm/utils.test.js +44 -1
- package/dist/types/index.d.ts +3 -2
- package/dist/types/injectSchema.d.ts +1 -0
- package/dist/types/injectSchema.test.d.ts +1 -0
- package/dist/types/markupFetcher.d.ts +24 -1
- package/dist/types/utils.d.ts +9 -0
- package/package.json +1 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { expect, it } from "vitest";
|
|
2
|
+
import { injectSchema } from "./injectSchema";
|
|
3
|
+
it("injects schema into markup", () => {
|
|
4
|
+
const markup = `
|
|
5
|
+
<div id="jcComments">
|
|
6
|
+
<div jc-data="jcSchema" data-schema="[{"@context":"https:\/\/schema.org","@type":"Comment","name":"Alexis","dateCreated":"2022-10-28T10:41:49+00:00","url":"https:\/\/macarthur.me\/posts\/dynamic-routing#comment-133"}]"></div>
|
|
7
|
+
|
|
8
|
+
<!-- JC:SCHEMA -->
|
|
9
|
+
</div>`;
|
|
10
|
+
const blogPostSchema = {
|
|
11
|
+
"@context": "http://schema.org",
|
|
12
|
+
"@type": "BlogPosting",
|
|
13
|
+
};
|
|
14
|
+
const result = injectSchema(markup, blogPostSchema);
|
|
15
|
+
expect(result).toEqual(`
|
|
16
|
+
<div id="jcComments">
|
|
17
|
+
<script type="application/ld+json">{"@context":"http://schema.org","@type":"BlogPosting","comment":[{"@context":"https://schema.org","@type":"Comment","name":"Alexis","dateCreated":"2022-10-28T10:41:49+00:00","url":"https://macarthur.me/posts/dynamic-routing#comment-133"}]}</script>
|
|
18
|
+
</div>`);
|
|
19
|
+
});
|
|
20
|
+
it("returns markup if schema is not found", () => {
|
|
21
|
+
const markup = `
|
|
22
|
+
<div id="jcComments">
|
|
23
|
+
<!-- JC:SCHEMA -->
|
|
24
|
+
</div>`;
|
|
25
|
+
const blogPostSchema = {
|
|
26
|
+
"@context": "http://schema.org",
|
|
27
|
+
"@type": "BlogPosting",
|
|
28
|
+
};
|
|
29
|
+
const result = injectSchema(markup, blogPostSchema);
|
|
30
|
+
expect(result).toEqual(markup);
|
|
31
|
+
});
|
|
32
|
+
it("returns markup if schema is not valid JSON", () => {
|
|
33
|
+
const markup = `
|
|
34
|
+
<div id="jcComments">
|
|
35
|
+
<div jc-data="jcSchema" data-schema="not-valid-json"></div>
|
|
36
|
+
<!-- JC:SCHEMA -->
|
|
37
|
+
</div>`;
|
|
38
|
+
const blogPostSchema = {
|
|
39
|
+
"@context": "http://schema.org",
|
|
40
|
+
"@type": "BlogPosting",
|
|
41
|
+
};
|
|
42
|
+
const result = injectSchema(markup, blogPostSchema);
|
|
43
|
+
expect(result).toEqual(markup);
|
|
44
|
+
});
|
|
45
|
+
it("injects even when JSON is not HTML-encoded", () => {
|
|
46
|
+
const markup = `
|
|
47
|
+
<div id="jcComments">
|
|
48
|
+
<div jc-data="jcSchema" data-schema="[{"@context":"https://schema.org","@type":"Comment","name":"Alexis","dateCreated":"2022-10-28T10:41:49+00:00","url":"https://macarthur.me/posts/dynamic-routing#comment-133"}]"></div>
|
|
49
|
+
|
|
50
|
+
<!-- JC:SCHEMA -->
|
|
51
|
+
</div>`;
|
|
52
|
+
const blogPostSchema = {
|
|
53
|
+
"@context": "http://schema.org",
|
|
54
|
+
"@type": "BlogPosting",
|
|
55
|
+
};
|
|
56
|
+
const result = injectSchema(markup, blogPostSchema);
|
|
57
|
+
expect(result).toEqual(`
|
|
58
|
+
<div id="jcComments">
|
|
59
|
+
<script type="application/ld+json">{"@context":"http://schema.org","@type":"BlogPosting","comment":[{"@context":"https://schema.org","@type":"Comment","name":"Alexis","dateCreated":"2022-10-28T10:41:49+00:00","url":"https://macarthur.me/posts/dynamic-routing#comment-133"}]}</script>
|
|
60
|
+
</div>`);
|
|
61
|
+
});
|
|
@@ -1,39 +1,101 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { injectSchema } from "./injectSchema";
|
|
2
|
+
import { createTempDirectory, deleteTempDirectory, getEnvironment, isValidTimezone, parseJson, readFile, saveFile, } from "./utils";
|
|
3
|
+
export async function fetchAll({ tz = undefined, domain, apiKey, baseUrl = "https://go.jamcomments.com", environment = getEnvironment(), }, platform, fetchImplementation, batchMarkupFetcherImpl = batchMarkupFetcher) {
|
|
4
|
+
const fetchBatchMarkup = batchMarkupFetcherImpl(platform, fetchImplementation);
|
|
5
|
+
createTempDirectory();
|
|
6
|
+
let shouldKeepFetching = true;
|
|
7
|
+
let page = 1;
|
|
8
|
+
console.log("Fetching comments from JamComments! This might take a sec.");
|
|
9
|
+
try {
|
|
10
|
+
while (shouldKeepFetching) {
|
|
11
|
+
const { data: items, meta: { current_page, last_page }, } = await fetchBatchMarkup({
|
|
12
|
+
domain,
|
|
13
|
+
apiKey,
|
|
14
|
+
baseUrl,
|
|
15
|
+
environment,
|
|
16
|
+
page,
|
|
17
|
+
tz,
|
|
18
|
+
});
|
|
19
|
+
console.log(`Checking for comment data. Batch: ${current_page}/${last_page}`);
|
|
20
|
+
const saveMarkupPromises = items.map((item) => {
|
|
21
|
+
return saveFile(item.path, item.markup);
|
|
22
|
+
});
|
|
23
|
+
await Promise.all(saveMarkupPromises);
|
|
24
|
+
if (current_page === last_page) {
|
|
25
|
+
shouldKeepFetching = false;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
page++;
|
|
29
|
+
}
|
|
7
30
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
deleteTempDirectory();
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function batchMarkupFetcher(platform, fetchImplementation = fetch) {
|
|
38
|
+
return async ({ tz = undefined, domain, apiKey, baseUrl = "https://go.jamcomments.com", environment = getEnvironment(), page = 1, }) => {
|
|
39
|
+
const response = await makeMarkupRequest({ tz, domain, apiKey, baseUrl, environment, page }, "/api/v3/markup/all", fetchImplementation, platform);
|
|
40
|
+
return response.json();
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export async function fetchFreshMarkup({ tz = undefined, path, domain, apiKey, baseUrl = "https://go.jamcomments.com", environment = getEnvironment(), }, fetchImplementation = fetch, platform) {
|
|
44
|
+
const response = await makeMarkupRequest({ tz, path, domain, apiKey, baseUrl, environment }, "/api/v3/markup", fetchImplementation, platform);
|
|
45
|
+
return response.text();
|
|
46
|
+
}
|
|
47
|
+
export async function makeMarkupRequest({ tz = undefined, path = undefined, domain, apiKey, baseUrl = "https://go.jamcomments.com", environment = getEnvironment(), page = undefined, }, baseServicePath, fetchImplementation = fetch, platform) {
|
|
48
|
+
const trimmedTimezone = tz?.trim();
|
|
49
|
+
if (trimmedTimezone && !isValidTimezone(trimmedTimezone)) {
|
|
50
|
+
throw new Error(`The timezone passed to JamComments is invalid: ${trimmedTimezone}`);
|
|
51
|
+
}
|
|
52
|
+
const params = new URLSearchParams({
|
|
53
|
+
domain,
|
|
54
|
+
});
|
|
55
|
+
if (path) {
|
|
56
|
+
params.set("path", path);
|
|
57
|
+
}
|
|
58
|
+
if (page) {
|
|
59
|
+
params.set("page", page.toString());
|
|
60
|
+
}
|
|
61
|
+
if (trimmedTimezone) {
|
|
62
|
+
params.set("tz", trimmedTimezone);
|
|
63
|
+
}
|
|
64
|
+
if (environment !== "production") {
|
|
65
|
+
params.set("stub", "true");
|
|
66
|
+
}
|
|
67
|
+
const requestUrl = `${baseUrl}${baseServicePath}?${params}`;
|
|
68
|
+
const response = await fetchImplementation(requestUrl, {
|
|
69
|
+
method: "GET",
|
|
70
|
+
headers: {
|
|
71
|
+
Authorization: `Bearer ${apiKey}`,
|
|
72
|
+
Accept: "application/json",
|
|
73
|
+
"X-Platform": platform,
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
if (response.status === 401) {
|
|
77
|
+
throw new Error(`Unauthorized! Are your domain and JamComments API key set correctly?`);
|
|
78
|
+
}
|
|
79
|
+
if (!response.ok) {
|
|
80
|
+
throw new Error(`JamComments request failed! Status code: ${response.status}, message: ${response.statusText}, request URL: ${requestUrl}`);
|
|
81
|
+
}
|
|
82
|
+
return response;
|
|
83
|
+
}
|
|
84
|
+
export function markupFetcher(platform, fetchImplementation = fetch) {
|
|
85
|
+
return async ({ tz = undefined, path, domain, apiKey, schema, baseUrl = "https://go.jamcomments.com", environment = getEnvironment(), }) => {
|
|
86
|
+
path = path || "/";
|
|
87
|
+
const savedFile = readFile(path);
|
|
88
|
+
const markup = savedFile
|
|
89
|
+
? savedFile
|
|
90
|
+
: await fetchFreshMarkup({ tz, path, domain, apiKey, baseUrl, environment }, fetchImplementation, platform);
|
|
12
91
|
if (schema) {
|
|
13
92
|
const preparedSchema = typeof schema !== "string" ? JSON.stringify(schema) : schema;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (environment !== "production") {
|
|
20
|
-
params.set("stub", "true");
|
|
21
|
-
}
|
|
22
|
-
const requestUrl = `${baseUrl}/api/v3/markup?${params}`;
|
|
23
|
-
const response = await fetchImplementation(requestUrl, {
|
|
24
|
-
method: "GET",
|
|
25
|
-
headers: {
|
|
26
|
-
Authorization: `Bearer ${apiKey}`,
|
|
27
|
-
Accept: "application/json",
|
|
28
|
-
"X-Platform": platform,
|
|
29
|
-
},
|
|
30
|
-
});
|
|
31
|
-
if (response.status === 401) {
|
|
32
|
-
throw new Error(`Unauthorized! Are your domain and JamComments API key set correctly?`);
|
|
33
|
-
}
|
|
34
|
-
if (!response.ok) {
|
|
35
|
-
throw new Error(`JamComments request failed! Status code: ${response.status}, message: ${response.statusText}, request URL: ${requestUrl}`);
|
|
93
|
+
const parsedSchema = parseJson(preparedSchema);
|
|
94
|
+
if (!parsedSchema) {
|
|
95
|
+
return markup;
|
|
96
|
+
}
|
|
97
|
+
return injectSchema(markup, parsedSchema);
|
|
36
98
|
}
|
|
37
|
-
return
|
|
99
|
+
return markup;
|
|
38
100
|
};
|
|
39
|
-
}
|
|
101
|
+
}
|
|
@@ -1,7 +1,204 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
import
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import * as injectSchema from "./injectSchema";
|
|
3
|
+
import * as fetcherExports from "./markupFetcher";
|
|
4
|
+
import { afterEach } from "node:test";
|
|
5
|
+
import * as utilsExports from "./utils";
|
|
6
|
+
const { deleteTempDirectory } = utilsExports;
|
|
7
|
+
const { markupFetcher, batchMarkupFetcher } = fetcherExports;
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
deleteTempDirectory();
|
|
10
|
+
});
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
vi.resetAllMocks();
|
|
13
|
+
});
|
|
14
|
+
describe("fetchAll()", () => {
|
|
15
|
+
it("fetches all comments in a single request", async () => {
|
|
16
|
+
const saveFileSpy = vi.spyOn(utilsExports, "saveFile");
|
|
17
|
+
const mockBatchFetcher = vi.fn().mockReturnValue({
|
|
18
|
+
data: [
|
|
19
|
+
{ path: "/test", markup: "markup1" },
|
|
20
|
+
{ path: "/test2", markup: "markup2" },
|
|
21
|
+
],
|
|
22
|
+
meta: {
|
|
23
|
+
current_page: 1,
|
|
24
|
+
from: 1,
|
|
25
|
+
last_page: 1,
|
|
26
|
+
path: "/test",
|
|
27
|
+
per_page: 10,
|
|
28
|
+
to: 2,
|
|
29
|
+
total: 2,
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
const batchMarkupFetcherMock = vi.fn().mockImplementation((a, b) => {
|
|
33
|
+
return mockBatchFetcher;
|
|
34
|
+
});
|
|
35
|
+
await fetcherExports.fetchAll({
|
|
36
|
+
domain: "test.com",
|
|
37
|
+
apiKey: "123abc",
|
|
38
|
+
environment: "production",
|
|
39
|
+
}, "test_platform", vi.fn(), batchMarkupFetcherMock);
|
|
40
|
+
expect(batchMarkupFetcherMock).toHaveBeenCalledWith("test_platform", expect.anything());
|
|
41
|
+
expect(mockBatchFetcher).toHaveBeenCalledWith({
|
|
42
|
+
domain: "test.com",
|
|
43
|
+
apiKey: "123abc",
|
|
44
|
+
baseUrl: "https://go.jamcomments.com",
|
|
45
|
+
environment: "production",
|
|
46
|
+
page: 1,
|
|
47
|
+
tz: undefined,
|
|
48
|
+
});
|
|
49
|
+
expect(saveFileSpy).toHaveBeenCalledTimes(2);
|
|
50
|
+
expect(saveFileSpy).toHaveBeenCalledWith("/test", "markup1");
|
|
51
|
+
expect(saveFileSpy).toHaveBeenCalledWith("/test2", "markup2");
|
|
52
|
+
});
|
|
53
|
+
it("fetches all comments in multiple requests", async () => {
|
|
54
|
+
const saveFileSpy = vi.spyOn(utilsExports, "saveFile");
|
|
55
|
+
const mockBatchFetcher = vi
|
|
56
|
+
.fn()
|
|
57
|
+
.mockReturnValueOnce({
|
|
58
|
+
data: [
|
|
59
|
+
{ path: "/test", markup: "markup1" },
|
|
60
|
+
{ path: "/test2", markup: "markup2" },
|
|
61
|
+
],
|
|
62
|
+
meta: {
|
|
63
|
+
current_page: 1,
|
|
64
|
+
from: 1,
|
|
65
|
+
last_page: 2,
|
|
66
|
+
path: "/test",
|
|
67
|
+
per_page: 2,
|
|
68
|
+
to: 2,
|
|
69
|
+
},
|
|
70
|
+
})
|
|
71
|
+
.mockReturnValueOnce({
|
|
72
|
+
data: [
|
|
73
|
+
{ path: "/test3", markup: "markup3" },
|
|
74
|
+
{ path: "/test4", markup: "markup4" },
|
|
75
|
+
],
|
|
76
|
+
meta: {
|
|
77
|
+
current_page: 2,
|
|
78
|
+
from: 3,
|
|
79
|
+
last_page: 2,
|
|
80
|
+
path: "/test",
|
|
81
|
+
per_page: 2,
|
|
82
|
+
to: 4,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
const batchMarkupFetcherMock = vi.fn().mockImplementation((a, b) => {
|
|
86
|
+
return mockBatchFetcher;
|
|
87
|
+
});
|
|
88
|
+
await fetcherExports.fetchAll({
|
|
89
|
+
domain: "test.com",
|
|
90
|
+
apiKey: "123abc",
|
|
91
|
+
environment: "production",
|
|
92
|
+
}, "test_platform", vi.fn(), batchMarkupFetcherMock);
|
|
93
|
+
expect(batchMarkupFetcherMock).toHaveBeenCalledWith("test_platform", expect.anything());
|
|
94
|
+
expect(mockBatchFetcher).toHaveBeenCalledWith({
|
|
95
|
+
domain: "test.com",
|
|
96
|
+
apiKey: "123abc",
|
|
97
|
+
baseUrl: "https://go.jamcomments.com",
|
|
98
|
+
environment: "production",
|
|
99
|
+
page: 1,
|
|
100
|
+
tz: undefined,
|
|
101
|
+
});
|
|
102
|
+
expect(mockBatchFetcher).toHaveBeenCalledTimes(2);
|
|
103
|
+
expect(mockBatchFetcher).toHaveBeenCalledWith({
|
|
104
|
+
domain: "test.com",
|
|
105
|
+
apiKey: "123abc",
|
|
106
|
+
baseUrl: "https://go.jamcomments.com",
|
|
107
|
+
environment: "production",
|
|
108
|
+
page: 2,
|
|
109
|
+
tz: undefined,
|
|
110
|
+
});
|
|
111
|
+
expect(saveFileSpy).toHaveBeenCalledTimes(4);
|
|
112
|
+
expect(saveFileSpy).toHaveBeenCalledWith("/test", "markup1");
|
|
113
|
+
expect(saveFileSpy).toHaveBeenCalledWith("/test2", "markup2");
|
|
114
|
+
});
|
|
115
|
+
it("deletes the temp directory of anything fails", async () => {
|
|
116
|
+
const deleteTempDirectorySpy = vi.spyOn(utilsExports, "deleteTempDirectory");
|
|
117
|
+
const mockBatchFetcher = vi.fn().mockImplementation(() => {
|
|
118
|
+
throw new Error("test error");
|
|
119
|
+
});
|
|
120
|
+
const batchMarkupFetcherMock = vi.fn().mockImplementation((a, b) => {
|
|
121
|
+
return mockBatchFetcher;
|
|
122
|
+
});
|
|
123
|
+
try {
|
|
124
|
+
await fetcherExports.fetchAll({
|
|
125
|
+
domain: "test.com",
|
|
126
|
+
apiKey: "123abc",
|
|
127
|
+
environment: "production",
|
|
128
|
+
}, "test_platform", vi.fn(), batchMarkupFetcherMock);
|
|
129
|
+
}
|
|
130
|
+
catch (e) {
|
|
131
|
+
expect(e.message).toEqual("test error");
|
|
132
|
+
}
|
|
133
|
+
expect(deleteTempDirectorySpy).toHaveBeenCalledOnce();
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
describe("batchMarkupFetcher", () => {
|
|
137
|
+
it("constructs fetch request correctly", async () => {
|
|
138
|
+
const injectSchemaSpy = vi.spyOn(injectSchema, "injectSchema");
|
|
139
|
+
const fetchMock = vi.fn().mockImplementation(() => {
|
|
140
|
+
return {
|
|
141
|
+
status: 200,
|
|
142
|
+
ok: true,
|
|
143
|
+
json: () => "results!",
|
|
144
|
+
};
|
|
145
|
+
});
|
|
146
|
+
const fetcher = batchMarkupFetcher("test", fetchMock);
|
|
147
|
+
const result = await fetcher({
|
|
148
|
+
domain: "test.com",
|
|
149
|
+
apiKey: "123abc",
|
|
150
|
+
environment: "production",
|
|
151
|
+
});
|
|
152
|
+
expect(injectSchemaSpy).not.toHaveBeenCalled();
|
|
153
|
+
expect(fetchMock).toHaveBeenCalledWith("https://go.jamcomments.com/api/v3/markup/all?domain=test.com&page=1", expect.objectContaining({
|
|
154
|
+
headers: expect.objectContaining({
|
|
155
|
+
Accept: "application/json",
|
|
156
|
+
Authorization: "Bearer 123abc",
|
|
157
|
+
"X-Platform": "test",
|
|
158
|
+
}),
|
|
159
|
+
method: "GET",
|
|
160
|
+
}));
|
|
161
|
+
expect(result).toEqual("results!");
|
|
162
|
+
});
|
|
163
|
+
it("stubs comments", async () => {
|
|
164
|
+
const fetchMock = vi.fn().mockImplementation(() => {
|
|
165
|
+
return {
|
|
166
|
+
status: 200,
|
|
167
|
+
ok: true,
|
|
168
|
+
json: () => "results!",
|
|
169
|
+
};
|
|
170
|
+
});
|
|
171
|
+
const fetcher = batchMarkupFetcher("test", fetchMock);
|
|
172
|
+
const result = await fetcher({
|
|
173
|
+
domain: "test.com",
|
|
174
|
+
apiKey: "123abc",
|
|
175
|
+
environment: "development",
|
|
176
|
+
});
|
|
177
|
+
expect(fetchMock).toHaveBeenCalledWith("https://go.jamcomments.com/api/v3/markup/all?domain=test.com&page=1&stub=true", expect.anything());
|
|
178
|
+
expect(result).toEqual("results!");
|
|
179
|
+
});
|
|
180
|
+
it("uses different base URL", async () => {
|
|
181
|
+
const fetchMock = vi.fn().mockImplementation(() => {
|
|
182
|
+
return {
|
|
183
|
+
status: 200,
|
|
184
|
+
ok: true,
|
|
185
|
+
json: () => "results!",
|
|
186
|
+
};
|
|
187
|
+
});
|
|
188
|
+
const fetcher = batchMarkupFetcher("test", fetchMock);
|
|
189
|
+
const result = await fetcher({
|
|
190
|
+
domain: "test.com",
|
|
191
|
+
apiKey: "123abc",
|
|
192
|
+
baseUrl: "http://ur-mom.com",
|
|
193
|
+
environment: "production",
|
|
194
|
+
});
|
|
195
|
+
expect(fetchMock).toHaveBeenCalledWith("http://ur-mom.com/api/v3/markup/all?domain=test.com&page=1", expect.anything());
|
|
196
|
+
expect(result).toEqual("results!");
|
|
197
|
+
});
|
|
198
|
+
});
|
|
3
199
|
describe("markupFetcher", () => {
|
|
4
200
|
it("constructs fetch request correctly", async () => {
|
|
201
|
+
const injectSchemaSpy = vi.spyOn(injectSchema, "injectSchema");
|
|
5
202
|
const fetchMock = vi.fn().mockImplementation(() => {
|
|
6
203
|
return {
|
|
7
204
|
status: 200,
|
|
@@ -16,7 +213,8 @@ describe("markupFetcher", () => {
|
|
|
16
213
|
apiKey: "123abc",
|
|
17
214
|
environment: "production",
|
|
18
215
|
});
|
|
19
|
-
expect(
|
|
216
|
+
expect(injectSchemaSpy).not.toHaveBeenCalled();
|
|
217
|
+
expect(fetchMock).toHaveBeenCalledWith("https://go.jamcomments.com/api/v3/markup?domain=test.com&path=%2Ftest", expect.objectContaining({
|
|
20
218
|
headers: expect.objectContaining({
|
|
21
219
|
Accept: "application/json",
|
|
22
220
|
Authorization: "Bearer 123abc",
|
|
@@ -41,7 +239,7 @@ describe("markupFetcher", () => {
|
|
|
41
239
|
apiKey: "123abc",
|
|
42
240
|
environment: "development",
|
|
43
241
|
});
|
|
44
|
-
expect(fetchMock).toHaveBeenCalledWith("https://go.jamcomments.com/api/v3/markup?
|
|
242
|
+
expect(fetchMock).toHaveBeenCalledWith("https://go.jamcomments.com/api/v3/markup?domain=test.com&path=%2Ftest&stub=true", expect.anything());
|
|
45
243
|
expect(result).toEqual("results!");
|
|
46
244
|
});
|
|
47
245
|
it("uses different base URL", async () => {
|
|
@@ -60,7 +258,7 @@ describe("markupFetcher", () => {
|
|
|
60
258
|
baseUrl: "http://ur-mom.com",
|
|
61
259
|
environment: "production",
|
|
62
260
|
});
|
|
63
|
-
expect(fetchMock).toHaveBeenCalledWith("http://ur-mom.com/api/v3/markup?
|
|
261
|
+
expect(fetchMock).toHaveBeenCalledWith("http://ur-mom.com/api/v3/markup?domain=test.com&path=%2Ftest", expect.anything());
|
|
64
262
|
expect(result).toEqual("results!");
|
|
65
263
|
});
|
|
66
264
|
it("respects production!!!", async () => {
|
|
@@ -79,7 +277,7 @@ describe("markupFetcher", () => {
|
|
|
79
277
|
baseUrl: "http://ur-mom.com",
|
|
80
278
|
environment: "production",
|
|
81
279
|
});
|
|
82
|
-
expect(fetchMock).toHaveBeenCalledWith("http://ur-mom.com/api/v3/markup?
|
|
280
|
+
expect(fetchMock).toHaveBeenCalledWith("http://ur-mom.com/api/v3/markup?domain=test.com&path=%2Ftest", expect.anything());
|
|
83
281
|
expect(result).toEqual("results!");
|
|
84
282
|
});
|
|
85
283
|
it("falls back to root path", async () => {
|
|
@@ -97,7 +295,7 @@ describe("markupFetcher", () => {
|
|
|
97
295
|
apiKey: "123abc",
|
|
98
296
|
environment: "production",
|
|
99
297
|
});
|
|
100
|
-
expect(fetchMock).toHaveBeenCalledWith("https://go.jamcomments.com/api/v3/markup?
|
|
298
|
+
expect(fetchMock).toHaveBeenCalledWith("https://go.jamcomments.com/api/v3/markup?domain=test.com&path=%2F", expect.anything());
|
|
101
299
|
expect(result).toEqual("results!");
|
|
102
300
|
});
|
|
103
301
|
it("credentials are invalid", async () => {
|
|
@@ -132,9 +330,9 @@ describe("markupFetcher", () => {
|
|
|
132
330
|
});
|
|
133
331
|
describe("timezone validation", () => {
|
|
134
332
|
it("throws error when invalid timezone is provided", async () => {
|
|
135
|
-
const
|
|
136
|
-
const fetcher = markupFetcher("test",
|
|
137
|
-
expect(
|
|
333
|
+
const fetchImplMock = vi.fn();
|
|
334
|
+
const fetcher = markupFetcher("test", fetchImplMock);
|
|
335
|
+
expect(fetchImplMock).not.toHaveBeenCalled();
|
|
138
336
|
expect(fetcher({
|
|
139
337
|
path: "/test",
|
|
140
338
|
domain: "test.com",
|
|
@@ -153,13 +351,13 @@ describe("markupFetcher", () => {
|
|
|
153
351
|
});
|
|
154
352
|
const fetcher = markupFetcher("test", fetchMock);
|
|
155
353
|
const result = await fetcher({
|
|
156
|
-
path:
|
|
354
|
+
path: "/some/path",
|
|
157
355
|
domain: "test.com",
|
|
158
356
|
apiKey: "123abc",
|
|
159
357
|
tz: "America/New_York",
|
|
160
358
|
environment: "production",
|
|
161
359
|
});
|
|
162
|
-
expect(fetchMock).toHaveBeenCalledWith("https://go.jamcomments.com/api/v3/markup?
|
|
360
|
+
expect(fetchMock).toHaveBeenCalledWith("https://go.jamcomments.com/api/v3/markup?domain=test.com&path=%2Fsome%2Fpath&tz=America%2FNew_York", expect.anything());
|
|
163
361
|
expect(result).toEqual("results!");
|
|
164
362
|
});
|
|
165
363
|
it("trims a valid timezone", async () => {
|
|
@@ -172,19 +370,20 @@ describe("markupFetcher", () => {
|
|
|
172
370
|
});
|
|
173
371
|
const fetcher = markupFetcher("test", fetchMock);
|
|
174
372
|
const result = await fetcher({
|
|
175
|
-
path:
|
|
373
|
+
path: "/some/path",
|
|
176
374
|
domain: "test.com",
|
|
177
375
|
apiKey: "123abc",
|
|
178
376
|
tz: " America/Chicago ",
|
|
179
377
|
environment: "production",
|
|
180
378
|
});
|
|
181
|
-
expect(fetchMock).toHaveBeenCalledWith("https://go.jamcomments.com/api/v3/markup?
|
|
379
|
+
expect(fetchMock).toHaveBeenCalledWith("https://go.jamcomments.com/api/v3/markup?domain=test.com&path=%2Fsome%2Fpath&tz=America%2FChicago", expect.anything());
|
|
182
380
|
expect(result).toEqual("results!");
|
|
183
381
|
});
|
|
184
382
|
});
|
|
185
383
|
});
|
|
186
384
|
describe("passing schema", function () {
|
|
187
385
|
it("first stringifies schema if given an object", async () => {
|
|
386
|
+
const injectSchemaSpy = vi.spyOn(injectSchema, "injectSchema");
|
|
188
387
|
const fetchMock = vi.fn().mockImplementation(() => {
|
|
189
388
|
return {
|
|
190
389
|
status: 200,
|
|
@@ -200,10 +399,12 @@ describe("passing schema", function () {
|
|
|
200
399
|
schema: { foo: "bar" },
|
|
201
400
|
environment: "production",
|
|
202
401
|
});
|
|
203
|
-
expect(
|
|
402
|
+
expect(injectSchemaSpy).toHaveBeenCalledWith("results!", { foo: "bar" });
|
|
403
|
+
expect(fetchMock).toHaveBeenCalledWith("https://go.jamcomments.com/api/v3/markup?domain=test.com&path=%2Ftest", expect.anything());
|
|
204
404
|
expect(result).toEqual("results!");
|
|
205
405
|
});
|
|
206
406
|
it("does not stringify schema if given a string", async () => {
|
|
407
|
+
const injectSchemaSpy = vi.spyOn(injectSchema, "injectSchema");
|
|
207
408
|
const fetchMock = vi.fn().mockImplementation(() => {
|
|
208
409
|
return {
|
|
209
410
|
status: 200,
|
|
@@ -219,7 +420,29 @@ describe("passing schema", function () {
|
|
|
219
420
|
schema: '{"foo":"bar"}',
|
|
220
421
|
environment: "production",
|
|
221
422
|
});
|
|
222
|
-
expect(
|
|
423
|
+
expect(injectSchemaSpy).toHaveBeenCalledWith("results!", { foo: "bar" });
|
|
424
|
+
expect(fetchMock).toHaveBeenCalledWith("https://go.jamcomments.com/api/v3/markup?domain=test.com&path=%2Ftest", expect.anything());
|
|
425
|
+
expect(result).toEqual("results!");
|
|
426
|
+
});
|
|
427
|
+
it("returns markup if invalid json is provided", async () => {
|
|
428
|
+
const injectSchemaSpy = vi.spyOn(injectSchema, "injectSchema");
|
|
429
|
+
const fetchMock = vi.fn().mockImplementation(() => {
|
|
430
|
+
return {
|
|
431
|
+
status: 200,
|
|
432
|
+
ok: true,
|
|
433
|
+
text: () => "results!",
|
|
434
|
+
};
|
|
435
|
+
});
|
|
436
|
+
const fetcher = markupFetcher("test", fetchMock);
|
|
437
|
+
const result = await fetcher({
|
|
438
|
+
path: "/test",
|
|
439
|
+
domain: "test.com",
|
|
440
|
+
apiKey: "123abc",
|
|
441
|
+
schema: "not-valid-json",
|
|
442
|
+
environment: "production",
|
|
443
|
+
});
|
|
444
|
+
expect(injectSchemaSpy).not.toHaveBeenCalled();
|
|
445
|
+
expect(fetchMock).toHaveBeenCalledWith("https://go.jamcomments.com/api/v3/markup?domain=test.com&path=%2Ftest", expect.anything());
|
|
223
446
|
expect(result).toEqual("results!");
|
|
224
447
|
});
|
|
225
448
|
});
|
package/dist/esm/utils.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, rmdirSync, writeFile } from "fs";
|
|
2
|
+
import { TEMP_DIRECTORY } from ".";
|
|
1
3
|
export function isValidTimezone(tz) {
|
|
2
4
|
try {
|
|
3
5
|
Intl.DateTimeFormat(undefined, { timeZone: tz });
|
|
@@ -22,3 +24,72 @@ export function reAppendMarkup(element, markup) {
|
|
|
22
24
|
element.innerHTML = "";
|
|
23
25
|
element.append(documentFragment);
|
|
24
26
|
}
|
|
27
|
+
export function parseJson(json) {
|
|
28
|
+
try {
|
|
29
|
+
return JSON.parse(json);
|
|
30
|
+
}
|
|
31
|
+
catch (ex) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function unescapeHTML(str) {
|
|
36
|
+
var htmlEntities = {
|
|
37
|
+
nbsp: " ",
|
|
38
|
+
cent: "¢",
|
|
39
|
+
pound: "£",
|
|
40
|
+
yen: "¥",
|
|
41
|
+
euro: "€",
|
|
42
|
+
copy: "©",
|
|
43
|
+
reg: "®",
|
|
44
|
+
lt: "<",
|
|
45
|
+
gt: ">",
|
|
46
|
+
quot: '"',
|
|
47
|
+
amp: "&",
|
|
48
|
+
apos: "'",
|
|
49
|
+
};
|
|
50
|
+
return str.replace(/\&([^;]+);/g, function (entity, entityCode) {
|
|
51
|
+
var match;
|
|
52
|
+
if (entityCode in htmlEntities) {
|
|
53
|
+
return htmlEntities[entityCode];
|
|
54
|
+
}
|
|
55
|
+
else if ((match = entityCode.match(/^#x([\da-fA-F]+)$/))) {
|
|
56
|
+
return String.fromCharCode(parseInt(match[1], 16));
|
|
57
|
+
}
|
|
58
|
+
else if ((match = entityCode.match(/^#(\d+)$/))) {
|
|
59
|
+
return String.fromCharCode(~~match[1]);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
return entity;
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
export function deleteTempDirectory() {
|
|
67
|
+
if (existsSync(TEMP_DIRECTORY)) {
|
|
68
|
+
rmdirSync(TEMP_DIRECTORY, { recursive: true });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
export function createTempDirectory(mkdirSyncImpl = mkdirSync, existsSyncImpl = existsSync) {
|
|
72
|
+
if (!existsSyncImpl(TEMP_DIRECTORY)) {
|
|
73
|
+
mkdirSyncImpl(TEMP_DIRECTORY);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export function toSavedFileName(name) {
|
|
77
|
+
return name.replace(/^\//, "").replace(/\//g, "::");
|
|
78
|
+
}
|
|
79
|
+
export function readFile(name, readFileSyncImpl = readFileSync) {
|
|
80
|
+
const fileName = toSavedFileName(name);
|
|
81
|
+
try {
|
|
82
|
+
return readFileSyncImpl(`${TEMP_DIRECTORY}/${fileName}`, "utf8");
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export function saveFile(name, data, writeFileImpl = writeFile) {
|
|
89
|
+
const fileName = toSavedFileName(name);
|
|
90
|
+
return new Promise((resolve) => {
|
|
91
|
+
writeFileImpl(`${TEMP_DIRECTORY}/${fileName}`, data, () => {
|
|
92
|
+
resolve();
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}
|
package/dist/esm/utils.test.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
-
import { getEnvironment } from "./utils";
|
|
2
|
+
import { getEnvironment, createTempDirectory, toSavedFileName, readFile, saveFile, } from "./utils";
|
|
3
|
+
import { TEMP_DIRECTORY } from ".";
|
|
3
4
|
const env = process.env;
|
|
4
5
|
beforeEach(() => {
|
|
5
6
|
vi.resetModules();
|
|
@@ -42,3 +43,45 @@ describe("getEnvironment()", () => {
|
|
|
42
43
|
});
|
|
43
44
|
});
|
|
44
45
|
});
|
|
46
|
+
describe("createTempDirectory()", () => {
|
|
47
|
+
it("creates temp directory fresh", () => {
|
|
48
|
+
const mkdirSyncMock = vi.fn();
|
|
49
|
+
const existsSyncMock = vi.fn().mockReturnValue(false);
|
|
50
|
+
expect(() => createTempDirectory(mkdirSyncMock, existsSyncMock)).not.toThrow();
|
|
51
|
+
expect(mkdirSyncMock).toHaveBeenCalledWith(TEMP_DIRECTORY);
|
|
52
|
+
});
|
|
53
|
+
it("does not throw error when it already exists", () => {
|
|
54
|
+
const mkdirSyncMock = vi.fn().mockImplementation(() => {
|
|
55
|
+
throw new Error("Directory already exists");
|
|
56
|
+
});
|
|
57
|
+
const existsSyncMock = vi.fn().mockReturnValue(true);
|
|
58
|
+
expect(() => createTempDirectory(mkdirSyncMock, existsSyncMock)).not.toThrow();
|
|
59
|
+
expect(mkdirSyncMock).not.toHaveBeenCalledWith();
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
describe("toSavedFileName()", () => {
|
|
63
|
+
it("replaces slashes with double colons", () => {
|
|
64
|
+
expect(toSavedFileName("/hey/there")).toEqual("hey::there");
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
describe("readFile()", () => {
|
|
68
|
+
it("reads the file", () => {
|
|
69
|
+
const readFileSyncMock = vi.fn().mockReturnValue("file contents");
|
|
70
|
+
expect(readFile("hey/there", readFileSyncMock)).toEqual("file contents");
|
|
71
|
+
expect(readFileSyncMock).toHaveBeenCalledWith(`${TEMP_DIRECTORY}/hey::there`, "utf8");
|
|
72
|
+
});
|
|
73
|
+
it("returns null when file doesn't exist", () => {
|
|
74
|
+
const readFileSyncMock = vi.fn().mockImplementation(() => {
|
|
75
|
+
throw new Error("File not found");
|
|
76
|
+
});
|
|
77
|
+
expect(readFile("hey/there", readFileSyncMock)).toEqual(null);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
describe("saveFile()", () => {
|
|
81
|
+
it("saves the file", async () => {
|
|
82
|
+
const writeFileMock = vi.fn();
|
|
83
|
+
const result = saveFile("hey/there", "file contents", writeFileMock);
|
|
84
|
+
expect(writeFileMock).toHaveBeenCalledWith(`${TEMP_DIRECTORY}/hey::there`, "file contents", expect.any(Function));
|
|
85
|
+
expect(result).toBeInstanceOf(Promise);
|
|
86
|
+
});
|
|
87
|
+
});
|