Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions __tests__/commands/pages/__snapshots__/upload.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,20 @@ exports[`rdme docs upload > given that the file path is a single file > should e
}
`;

exports[`rdme docs upload > given that the file path is a single file > should gracefully handle errors that claim they are JSON but are not 1`] = `
{
"error": [Error: The ReadMe API responded with an unexpected error. Please try again and if this issue persists, get in touch with us at support@readme.io.],
"stderr": "- 🔬 Validating frontmatter data...
✔ 🔬 Validating frontmatter data... no issues found!
- 🚀 Uploading files to ReadMe...
✖ 🚀 Uploading files to ReadMe... 1 file(s) failed.
",
"stdout": "🚨 Received errors when attempting to upload 1 page(s):
- __tests__/__fixtures__/docs/new-docs/new-doc.md: The ReadMe API responded with an unexpected error. Please try again and if this issue persists, get in touch with us at support@readme.io.
",
}
`;

exports[`rdme docs upload > given that the file path is a single file > should not throw an error if \`max-errors\` flag is set to -1 1`] = `
{
"result": {
Expand Down Expand Up @@ -1672,6 +1686,20 @@ exports[`rdme reference upload > given that the file path is a single file > sho
}
`;

exports[`rdme reference upload > given that the file path is a single file > should gracefully handle errors that claim they are JSON but are not 1`] = `
{
"error": [Error: The ReadMe API responded with an unexpected error. Please try again and if this issue persists, get in touch with us at support@readme.io.],
"stderr": "- 🔬 Validating frontmatter data...
✔ 🔬 Validating frontmatter data... no issues found!
- 🚀 Uploading files to ReadMe...
✖ 🚀 Uploading files to ReadMe... 1 file(s) failed.
",
"stdout": "🚨 Received errors when attempting to upload 1 page(s):
- __tests__/__fixtures__/docs/new-docs/new-doc.md: The ReadMe API responded with an unexpected error. Please try again and if this issue persists, get in touch with us at support@readme.io.
",
}
`;

exports[`rdme reference upload > given that the file path is a single file > should not throw an error if \`max-errors\` flag is set to -1 1`] = `
{
"result": {
Expand Down
20 changes: 20 additions & 0 deletions __tests__/commands/pages/upload.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,26 @@ describe.each([
mock.done();
});

it('should gracefully handle errors that claim they are JSON but are not', async () => {
const mock = getAPIv2Mock({ authorization })
.get(`/branches/stable/${route}/new-doc`)
.reply(404)
.post(`/branches/stable/${route}`, {
category: { uri: `/branches/stable/categories/${route}/category-slug` },
slug: 'new-doc',
title: 'This is the document title',
content: { body: '\nBody\n' },
})
.reply(201, '<>yikes</>', { 'content-type': 'application/json' });

const result = await run(['__tests__/__fixtures__/docs/new-docs/new-doc.md', '--key', key]);

expect(result).toMatchSnapshot();
expect(fs.writeFileSync).not.toHaveBeenCalled();

mock.done();
});

describe('given that the --dry-run flag is passed', () => {
it('should not create anything in ReadMe', async () => {
const mock = getAPIv2Mock({ authorization }).get(`/branches/stable/${route}/new-doc`).reply(404);
Expand Down
49 changes: 37 additions & 12 deletions src/lib/readmeAPIFetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,15 +348,27 @@ export async function handleAPIv1Res(
) {
const contentType = res.headers.get('content-type') || '';
const extension = mime.extension(contentType);
debug(`received status code ${res.status} from ${res.url} with content type: ${contentType}`);
if (extension === 'json') {
// TODO: type this better
// biome-ignore lint/suspicious/noExplicitAny: We do not have TS types for APIv1 responses.
const body = (await res.json()) as any;
debug(`received status code ${res.status} from ${res.url} with JSON response: ${JSON.stringify(body)}`);
if (body.error && rejectOnJsonError) {
return Promise.reject(new APIv1Error(body));
// Just to be safe, we parse the response as text first in case the response is not valid JSON.
const text = await res.text();
debug(`received status code ${res.status} from ${res.url} with JSON response: ${text}`);
try {
// biome-ignore lint/suspicious/noExplicitAny: We do not have TS types for APIv1 responses.
const body = JSON.parse(text) as any;
if (body.error && rejectOnJsonError) {
throw new APIv1Error(body);
}
return body;
} catch (e) {
if (e instanceof APIv1Error) {
throw e;
}
debug(`received the following error when attempting to parse JSON response: ${e.message}`);
throw new Error(
'The ReadMe API responded with an unexpected error. Please try again and if this issue persists, get in touch with us at support@readme.io.',
);
}
return body;
}
if (res.status === SUCCESS_NO_CONTENT) {
debug(`received status code ${res.status} from ${res.url} with no content`);
Expand Down Expand Up @@ -386,19 +398,32 @@ export async function handleAPIv2Res<R extends ResponseBody = ResponseBody, T ex
): Promise<R> {
const contentType = res.headers.get('content-type') || '';
const extension = mime.extension(contentType) || contentType.includes('json') ? 'json' : false;
this.debug(`received status code ${res.status} from ${res.url} with content type: ${contentType}`);
if (res.status === SUCCESS_NO_CONTENT) {
// to prevent a memory leak, we should still consume the response body? even though we don't use it?
// https://x.com/cramforce/status/1762142087930433999
const body = await res.text();
this.debug(`received status code ${res.status} from ${res.url} with no content: ${body}`);
return {} as R;
} else if (extension === 'json') {
const body = (await res.json()) as R;
this.debug(`received status code ${res.status} from ${res.url} with JSON response: ${JSON.stringify(body)}`);
if (!res.ok) {
throw new APIv2Error(body as APIv2ErrorResponse);
// Just to be safe, we parse the response as text first in case the response is not valid JSON.
const text = await res.text();
this.debug(`received status code ${res.status} from ${res.url} with JSON response: ${text}`);
try {
const body = JSON.parse(text) as R;
if (!res.ok) {
throw new APIv2Error(body as APIv2ErrorResponse);
}
return body;
} catch (e) {
if (e instanceof APIv2Error) {
throw e;
}
this.debug(`received the following error when attempting to parse JSON response: ${e.message}`);
throw new Error(
'The ReadMe API responded with an unexpected error. Please try again and if this issue persists, get in touch with us at support@readme.io.',
);
}
return body;
}

// If we receive a non-JSON response, it's likely an error.
Expand Down
Loading