Skip to content

Commit d1d1b69

Browse files
committed
feat: stricter error handling will now also throw on "2XX" codes but with error body.
1 parent 7b4515e commit d1d1b69

File tree

2 files changed

+55
-32
lines changed

2 files changed

+55
-32
lines changed

src/request-factory.ts

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import axios, { AxiosError, AxiosResponse } from 'axios';
2-
import { MailcowErrorResponse, MailcowException } from './types';
1+
import axios from 'axios';
2+
import { BaseResponse, MailcowException, MailcowResponse } from './types';
33
import MailcowClient from './index';
44

55
/**
@@ -24,6 +24,24 @@ export function wrapPromiseToArray<T>(promise: Promise<T | T[]>): Promise<T[]> {
2424
});
2525
}
2626

27+
function isErrorType(type: string | undefined): boolean {
28+
return type === 'danger' || type === 'error';
29+
}
30+
31+
/**
32+
* Checks if a Mailcow API response indicates an error.
33+
* Throws a MailcowException if a definite error is detected.
34+
*/
35+
function checkMailcowResponse(res: MailcowResponse | BaseResponse): void {
36+
// Accepts either a single object or an array
37+
const arr = Array.isArray(res) ? res : [res];
38+
for (const item of arr) {
39+
if (isErrorType(item.type)) {
40+
throw new MailcowException(Array.isArray(item.msg) ? item.msg.join(', ') : item.msg);
41+
}
42+
}
43+
}
44+
2745
/**
2846
* Factory method patterns for creating Axios Requests.
2947
* @internal
@@ -41,38 +59,37 @@ export default class RequestFactory {
4159
* @param payload - The payload to send with the request.
4260
*/
4361
async post<T, P extends object>(route: string, payload: P): Promise<T> {
44-
return new Promise((resolve, reject) => {
45-
axios
46-
.post(this.ctx.BASE_URL + route, payload, this.ctx.AXIOS_CONFIG)
47-
// On succes
48-
.then((res: AxiosResponse<T>) => {
49-
resolve(res.data);
50-
})
51-
// On error
52-
.catch((e: AxiosError<MailcowErrorResponse>) => {
53-
const { msg } = e.response.data;
54-
reject(new MailcowException(msg));
55-
});
56-
});
62+
try {
63+
const res = await axios.post<T>(this.ctx.BASE_URL + route, payload, this.ctx.AXIOS_CONFIG);
64+
// Only throws if response has danger or error type
65+
checkMailcowResponse(res.data as unknown as MailcowResponse);
66+
return res.data;
67+
} catch (e) {
68+
if (axios.isAxiosError(e) && e.response?.data) {
69+
// Check if the response *itself* is an error type
70+
checkMailcowResponse(e.response.data as unknown as MailcowResponse);
71+
// If no definite error, throw generic
72+
throw new MailcowException('Unknown Mailcow error');
73+
}
74+
throw e;
75+
}
5776
}
5877

5978
/**
6079
* GET Request Factory
6180
* @param route - The route to which to send the request.
6281
*/
6382
async get<T>(route: string): Promise<T> {
64-
return new Promise((resolve, reject) => {
65-
axios
66-
.get(this.ctx.BASE_URL + route, this.ctx.AXIOS_CONFIG)
67-
// On succes
68-
.then((res: AxiosResponse<T>) => {
69-
resolve(res.data);
70-
})
71-
// On error
72-
.catch((e: AxiosError<MailcowErrorResponse>) => {
73-
const { msg } = e.response.data;
74-
reject(new MailcowException(msg));
75-
});
76-
});
83+
try {
84+
const res = await axios.get<T>(this.ctx.BASE_URL + route, this.ctx.AXIOS_CONFIG);
85+
checkMailcowResponse(res.data as unknown as MailcowResponse);
86+
return res.data;
87+
} catch (e) {
88+
if (axios.isAxiosError(e) && e.response?.data) {
89+
checkMailcowResponse(e.response.data as unknown as MailcowResponse);
90+
throw new MailcowException('Unknown Mailcow error');
91+
}
92+
throw e;
93+
}
7794
}
7895
}

test/index.test.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import {
77
MailcowResponse,
88
Syncjob,
99
SyncjobAttributes, SyncjobEditAttributes
10-
} from "../src/types";
11-
import { AliasEditAttributes } from "../src/types";
10+
} from "../src";
11+
import { AliasEditAttributes } from "../src";
1212
import * as https from "node:https";
1313

1414
const mcc: MailcowClient = new MailcowClient(
@@ -27,7 +27,6 @@ function isSucces(res: MailcowResponse) {
2727
async function thenTestOrFail(promise: Promise<any>, test: Function, fatal = false): Promise<void> {
2828
await promise
2929
.then((res: any) => {
30-
console.log(JSON.stringify(res, null, 4));
3130
test(res);
3231
})
3332
.catch((err) => {
@@ -44,7 +43,14 @@ describe("Alias Endpoint tests", (): void => {
4443
await thenTestOrFail(mcc.aliases.get(), (res: Alias[]) => expect(res).to.be.length.least(1), true)
4544
});
4645
it('should get a single alias', async () => {
47-
await thenTestOrFail(mcc.aliases.get(8), (res: Alias[]) => expect(res).to.be.length.least(1), true)
46+
let id: number;
47+
await mcc.aliases.get().then((res: Alias[]) => {
48+
id = res[0].id
49+
})
50+
51+
await thenTestOrFail(mcc.aliases.get(id), (res: Alias[]) => {
52+
expect(res).to.be.length.least(1)
53+
}, true)
4854
});
4955
describe("Modify aliases", (): void => {
5056
const attr: AliasPostRequest = {

0 commit comments

Comments
 (0)